Browse Source

Transcoder updates for bitrate validator warnings (#106)

* Transcoder updates for validator warnings (gop, max-bitrates) for #50

* Make the variant settings easier to set/read in code

* Update test to reflect update to transcoder

* Reduce the buffer to give it a little more room to improve the quality. It feels a bit crunched right now.

* Fix test due to changed bitrate buffer ratios

* Tweak buffer size one more time to get errors in the validator to zero

* Saw errors w/ different bitrates, so tweaking it again
pull/98/head v0.0.1
Gabe Kangas 5 years ago committed by GitHub
parent
commit
d21f136120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      config/config.go
  2. 1
      config/defaults.go
  3. 60
      core/ffmpeg/transcoder.go
  4. 4
      core/ffmpeg/transcoder_test.go

9
config/config.go

@ -197,6 +197,15 @@ func (c *config) GetVideoStreamQualities() []StreamQuality { @@ -197,6 +197,15 @@ func (c *config) GetVideoStreamQualities() []StreamQuality {
return _default.VideoSettings.StreamQualities
}
// GetFramerate returns the framerate or default
func (q *StreamQuality) GetFramerate() int {
if q.Framerate > 0 {
return q.Framerate
}
return _default.VideoSettings.StreamQualities[0].Framerate
}
//Load tries to load the configuration file
func Load(filePath string, versionInfo string) error {
Config = new(config)

1
config/defaults.go

@ -21,6 +21,7 @@ func getDefaults() config { @@ -21,6 +21,7 @@ func getDefaults() config {
IsAudioPassthrough: true,
VideoBitrate: 1200,
EncoderPreset: "veryfast",
Framerate: 24,
}
defaults.VideoSettings.StreamQualities = []StreamQuality{defaultQuality}

60
core/ffmpeg/transcoder.go

@ -33,7 +33,7 @@ type HLSVariant struct { @@ -33,7 +33,7 @@ type HLSVariant struct {
videoSize VideoSize // Resizes the video via scaling
framerate int // The output framerate
videoBitrate string // The output bitrate
videoBitrate int // The output bitrate
isVideoPassthrough bool // Override all settings and just copy the video stream
audioBitrate string // The audio bitrate
@ -164,11 +164,11 @@ func getVariantFromConfigQuality(quality config.StreamQuality, index int) HLSVar @@ -164,11 +164,11 @@ func getVariantFromConfigQuality(quality config.StreamQuality, index int) HLSVar
variant.encoderPreset = "veryfast"
}
variant.SetVideoBitrate(strconv.Itoa(quality.VideoBitrate) + "k")
variant.SetVideoBitrate(quality.VideoBitrate)
variant.SetAudioBitrate(strconv.Itoa(quality.AudioBitrate) + "k")
variant.SetVideoScalingWidth(quality.ScaledWidth)
variant.SetVideoScalingHeight(quality.ScaledHeight)
variant.SetVideoFramerate(quality.Framerate)
variant.SetVideoFramerate(quality.GetFramerate())
return variant
}
@ -196,13 +196,6 @@ func NewTranscoder() Transcoder { @@ -196,13 +196,6 @@ func NewTranscoder() Transcoder {
transcoder.segmentLengthSeconds = config.Config.GetVideoSegmentSecondsLength()
qualities := config.Config.VideoSettings.StreamQualities
if len(qualities) == 0 {
defaultQuality := config.StreamQuality{}
defaultQuality.VideoBitrate = 1000
defaultQuality.EncoderPreset = "superfast"
qualities = append(qualities, defaultQuality)
}
for index, quality := range qualities {
variant := getVariantFromConfigQuality(quality, index)
transcoder.AddVariant(variant)
@ -212,9 +205,9 @@ func NewTranscoder() Transcoder { @@ -212,9 +205,9 @@ func NewTranscoder() Transcoder {
}
// Uses `map` https://www.ffmpeg.org/ffmpeg-all.html#Stream-specifiers-1 https://www.ffmpeg.org/ffmpeg-all.html#Advanced-options
func (v *HLSVariant) getVariantString() string {
func (v *HLSVariant) getVariantString(t *Transcoder) string {
variantEncoderCommands := []string{
v.getVideoQualityString(),
v.getVideoQualityString(t),
v.getAudioQualityString(),
}
@ -222,17 +215,7 @@ func (v *HLSVariant) getVariantString() string { @@ -222,17 +215,7 @@ func (v *HLSVariant) getVariantString() string {
variantEncoderCommands = append(variantEncoderCommands, v.getScalingString())
}
if v.framerate == 0 {
v.framerate = 30
}
if v.framerate != 0 {
variantEncoderCommands = append(variantEncoderCommands, fmt.Sprintf("-r %d", v.framerate))
// Insert a keyframe every 2 seconds.
// Multiply your output frame rate * 2. For example, if your input is -framerate 30, then use -g 60
variantEncoderCommands = append(variantEncoderCommands, "-g "+strconv.Itoa(v.framerate*2))
variantEncoderCommands = append(variantEncoderCommands, "-keyint_min "+strconv.Itoa(v.framerate*2))
}
variantEncoderCommands = append(variantEncoderCommands, fmt.Sprintf("-r %d", v.framerate))
if v.encoderPreset != "" {
variantEncoderCommands = append(variantEncoderCommands, fmt.Sprintf("-preset %s", v.encoderPreset))
@ -247,7 +230,7 @@ func (t *Transcoder) getVariantsString() string { @@ -247,7 +230,7 @@ func (t *Transcoder) getVariantsString() string {
var variantsStreamMaps = " -var_stream_map \""
for _, variant := range t.variants {
variantsCommandFlags = variantsCommandFlags + " " + variant.getVariantString()
variantsCommandFlags = variantsCommandFlags + " " + variant.getVariantString(t)
variantsStreamMaps = variantsStreamMaps + fmt.Sprintf("v:%d,a:%d ", variant.index, variant.index)
}
variantsCommandFlags = variantsCommandFlags + " " + variantsStreamMaps + "\""
@ -278,17 +261,40 @@ func (v *HLSVariant) getScalingString() string { @@ -278,17 +261,40 @@ func (v *HLSVariant) getScalingString() string {
// Video Quality
// SetVideoBitrate will set the output bitrate of this variant's video
func (v *HLSVariant) SetVideoBitrate(bitrate string) {
func (v *HLSVariant) SetVideoBitrate(bitrate int) {
v.videoBitrate = bitrate
}
func (v *HLSVariant) getVideoQualityString() string {
func (v *HLSVariant) getVideoQualityString(t *Transcoder) string {
if v.isVideoPassthrough {
return fmt.Sprintf("-map v:0 -c:v:%d copy", v.index)
}
encoderCodec := "libx264"
return fmt.Sprintf("-map v:0 -c:v:%d %s -b:v:%d %s", v.index, encoderCodec, v.index, v.videoBitrate)
// -1 to work around segments being generated slightly larger than expected.
// https://trac.ffmpeg.org/ticket/6915?replyto=58#comment:57
gop := (t.segmentLengthSeconds * v.framerate) - 1
// For limiting the output bitrate
// https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
// https://developer.apple.com/documentation/http_live_streaming/about_apple_s_http_live_streaming_tools
// Adjust the max & buffer size until the output bitrate doesn't exceed the ~+10% that Apple's media validator
// complains about.
maxBitrate := int(float64(v.videoBitrate) * 1.06) // Max is a ~+10% over specified bitrate.
bufferSize := int(float64(v.videoBitrate) * 1.2) // How often it checks the bitrate of encoded segments to see if it's too high/low.
cmd := []string{
"-map v:0",
fmt.Sprintf("-c:v:%d %s", v.index, encoderCodec), // Video codec used for this variant
fmt.Sprintf("-b:v:%d %dk", v.index, v.videoBitrate), // The average bitrate for this variant
fmt.Sprintf("-maxrate:v:%d %dk", v.index, maxBitrate), // The max bitrate allowed for this variant
fmt.Sprintf("-bufsize:v:%d %dk", v.index, bufferSize), // How often the encoder checks the bitrate in order to meet average/max values
fmt.Sprintf("-g:v:%d %d", v.index, gop), // How often i-frames are encoded into the segments
fmt.Sprintf("-x264-params:v:%d \"scenecut=0:open_gop=0:min-keyint=%d:keyint=%d\"", v.index, gop, gop), // How often i-frames are encoded into the segments
}
return strings.Join(cmd, " ")
}
// SetVideoFramerate will set the output framerate of this variant's video

4
core/ffmpeg/transcoder_test.go

@ -13,7 +13,7 @@ func TestFFmpegCommand(t *testing.T) { @@ -13,7 +13,7 @@ func TestFFmpegCommand(t *testing.T) {
transcoder.SetHLSPlaylistLength(10)
variant := HLSVariant{}
variant.videoBitrate = "1200k"
variant.videoBitrate = 1200
variant.isAudioPassthrough = true
variant.encoderPreset = "veryfast"
variant.SetVideoFramerate(30)
@ -22,7 +22,7 @@ func TestFFmpegCommand(t *testing.T) { @@ -22,7 +22,7 @@ func TestFFmpegCommand(t *testing.T) {
cmd := transcoder.getString()
expected := "cat fakecontent.flv | /fake/path/ffmpeg -hide_banner -i pipe: -map v:0 -c:v:0 libx264 -b:v:0 1200k -map a:0 -c:a:0 copy -r 30 -g 60 -keyint_min 60 -preset veryfast -var_stream_map \"v:0,a:0 \" -f hls -hls_time 4 -hls_list_size 10 -hls_delete_threshold 10 -hls_flags delete_segments+program_date_time+temp_file -tune zerolatency -sc_threshold 0 -master_pl_name stream.m3u8 -strftime 1 -hls_segment_filename fakeOutput/%v/stream-%s.ts -max_muxing_queue_size 400 fakeOutput/%v/stream.m3u8 2> transcoder.log"
expected := `cat fakecontent.flv | /fake/path/ffmpeg -hide_banner -i pipe: -map v:0 -c:v:0 libx264 -b:v:0 1200k -maxrate:v:0 1272k -bufsize:v:0 1440k -g:v:0 119 -x264-params:v:0 "scenecut=0:open_gop=0:min-keyint=119:keyint=119" -map a:0 -c:a:0 copy -r 30 -preset veryfast -var_stream_map "v:0,a:0 " -f hls -hls_time 4 -hls_list_size 10 -hls_delete_threshold 10 -hls_flags delete_segments+program_date_time+temp_file -tune zerolatency -sc_threshold 0 -master_pl_name stream.m3u8 -strftime 1 -hls_segment_filename fakeOutput/%v/stream-%s.ts -max_muxing_queue_size 400 fakeOutput/%v/stream.m3u8 2> transcoder.log`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected. Got %s, want: %s", cmd, expected)

Loading…
Cancel
Save