diff --git a/transcoder/src/filestream.go b/transcoder/src/filestream.go index 570d049f..6be157af 100644 --- a/transcoder/src/filestream.go +++ b/transcoder/src/filestream.go @@ -91,8 +91,8 @@ func (fs *FileStream) GetMaster() string { master += "\n" // codec is the prefix + the level, the level is not part of the codec we want to compare for the same_codec check bellow - transmux_prefix := "avc1.6400" - transmux_codec := transmux_prefix + "28" + transcode_prefix := "avc1.6400" + transcode_codec := transcode_prefix + "28" audio_codec := "mp4a.40.2" var def_video *Video @@ -108,10 +108,15 @@ func (fs *FileStream) GetMaster() string { if def_video != nil { qualities := Filter(Qualities, func(quality Quality) bool { - same_codec := def_video.MimeCodec != nil && strings.HasPrefix(*def_video.MimeCodec, transmux_prefix) - return quality.Height() < def_video.Height || - (quality.Height() == def_video.Height && !same_codec) + return quality.Height() < def_video.Height }) + transcode_count := len(qualities) + + // NoResize is the same idea as Original but we change the codec. + // This is only needed when the original's codec is different from what we would transcode it to. + if def_video.MimeCodec == nil || !strings.HasPrefix(*def_video.MimeCodec, transcode_prefix) { + qualities = append(qualities, NoResize) + } qualities = append(qualities, Original) for _, quality := range qualities { @@ -137,26 +142,23 @@ func (fs *FileStream) GetMaster() string { } master += "\n" - // original stream - { - bitrate := float64(def_video.Bitrate) - master += "#EXT-X-STREAM-INF:" - master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", int(math.Min(bitrate*0.8, float64(def_video.Quality().AverageBitrate())))) - master += fmt.Sprintf("BANDWIDTH=%d,", int(math.Min(bitrate, float64(def_video.Quality().MaxBitrate())))) - master += fmt.Sprintf("RESOLUTION=%dx%d,", def_video.Width, def_video.Height) - if def_video.MimeCodec != nil { - master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{*def_video.MimeCodec, audio_codec}, ",")) - } - master += "AUDIO=\"audio\"," - master += "CLOSED-CAPTIONS=NONE\n" - master += fmt.Sprintf("%d/%s/index.m3u8\n", def_video.Index, Original) - } - aspectRatio := float32(def_video.Width) / float32(def_video.Height) - for i, quality := range qualities { - if i == len(qualities)-1 { - // skip the original stream that already got handled + if i >= transcode_count { + // original & noresize streams + bitrate := float64(def_video.Bitrate) + master += "#EXT-X-STREAM-INF:" + master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", int(math.Min(bitrate*0.8, float64(def_video.Quality().AverageBitrate())))) + master += fmt.Sprintf("BANDWIDTH=%d,", int(math.Min(bitrate, float64(def_video.Quality().MaxBitrate())))) + master += fmt.Sprintf("RESOLUTION=%dx%d,", def_video.Width, def_video.Height) + if quality != Original { + master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, audio_codec}, ",")) + } else if def_video.MimeCodec != nil { + master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{*def_video.MimeCodec, audio_codec}, ",")) + } + master += "AUDIO=\"audio\"," + master += "CLOSED-CAPTIONS=NONE\n" + master += fmt.Sprintf("%d/%s/index.m3u8\n", def_video.Index, quality) continue } @@ -164,7 +166,7 @@ func (fs *FileStream) GetMaster() string { master += fmt.Sprintf("AVERAGE-BANDWIDTH=%d,", quality.AverageBitrate()) master += fmt.Sprintf("BANDWIDTH=%d,", quality.MaxBitrate()) master += fmt.Sprintf("RESOLUTION=%dx%d,", int(aspectRatio*float32(quality.Height())+0.5), quality.Height()) - master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transmux_codec, audio_codec}, ",")) + master += fmt.Sprintf("CODECS=\"%s\",", strings.Join([]string{transcode_codec, audio_codec}, ",")) master += "AUDIO=\"audio\"," master += "CLOSED-CAPTIONS=NONE\n" master += fmt.Sprintf("%d/%s/index.m3u8\n", def_video.Index, quality) diff --git a/transcoder/src/quality.go b/transcoder/src/quality.go index e64a1768..3b0f98be 100644 --- a/transcoder/src/quality.go +++ b/transcoder/src/quality.go @@ -17,6 +17,7 @@ const ( P1440 Quality = "1440p" P4k Quality = "4k" P8k Quality = "8k" + NoResize Quality = "transcode" Original Quality = "original" ) @@ -27,6 +28,9 @@ func QualityFromString(str string) (Quality, error) { if str == string(Original) { return Original, nil } + if str == string(NoResize) { + return NoResize, nil + } for _, quality := range Qualities { if string(quality) == str { diff --git a/transcoder/src/videostream.go b/transcoder/src/videostream.go index 95c69a56..42832e93 100644 --- a/transcoder/src/videostream.go +++ b/transcoder/src/videostream.go @@ -71,16 +71,30 @@ func (vs *VideoStream) getTranscodeArgs(segments string) []string { } args = append(args, Settings.HwAccel.EncodeFlags...) - width := int32(float64(vs.quality.Height()) / float64(vs.video.Height) * float64(vs.video.Width)) - // force a width that is a multiple of two else some apps behave badly. - width = closestMultiple(width, 2) + + quality := vs.quality + if vs.quality != NoResize { + width := int32(float64(vs.quality.Height()) / float64(vs.video.Height) * float64(vs.video.Width)) + // force a width that is a multiple of two else some apps behave badly. + width = closestMultiple(width, 2) + args = append(args, + "-vf", fmt.Sprintf(Settings.HwAccel.ScaleFilter, width, vs.quality.Height()), + ) + } else { + // NoResize doesn't have bitrate info, fallback to a know quality higher or equal. + for _, q := range Qualities { + if q.Height() >= vs.video.Height { + quality = q + break + } + } + } args = append(args, - "-vf", fmt.Sprintf(Settings.HwAccel.ScaleFilter, width, vs.quality.Height()), // Even less sure but bufsize are 5x the avergae bitrate since the average bitrate is only // useful for hls segments. - "-bufsize", fmt.Sprint(vs.quality.MaxBitrate()*5), - "-b:v", fmt.Sprint(vs.quality.AverageBitrate()), - "-maxrate", fmt.Sprint(vs.quality.MaxBitrate()), + "-bufsize", fmt.Sprint(quality.MaxBitrate()*5), + "-b:v", fmt.Sprint(quality.AverageBitrate()), + "-maxrate", fmt.Sprint(quality.MaxBitrate()), // Force segments to be split exactly on keyframes (only works when transcoding) // forced-idr is needed to force keyframes to be an idr-frame (by default it can be any i frames) // without this option, some hardware encoders uses others i-frames and the -f segment can't cut at them.