diff --git a/db/types.go b/db/types.go index 441cc7dc..546137a0 100644 --- a/db/types.go +++ b/db/types.go @@ -119,6 +119,7 @@ type VideoPreset struct { GopSize string `json:"gopSize,omitempty" redis-hash:"gopsize,omitempty"` GopMode string `json:"gopMode,omitempty" redis-hash:"gopmode,omitempty"` InterlaceMode string `json:"interlaceMode,omitempty" redis-hash:"interlacemode,omitempty"` + BFrames string `json:"bframes,omitempty" redis-hash:"bframes,omitempty"` } // AudioPreset defines the set of parameters for audio on a given preset diff --git a/go.mod b/go.mod index c28d8bb6..87c924e2 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/onsi/gomega v1.5.0 // indirect github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.2 - github.com/video-dev/go-elementalconductor v1.0.0 + github.com/video-dev/go-elementalconductor v1.1.0 github.com/video-dev/go-encodingcom v1.0.0 github.com/video-dev/zencoder v0.0.0-20161215190743-745874544382 ) diff --git a/go.sum b/go.sum index d2f4b996..774b5c89 100644 --- a/go.sum +++ b/go.sum @@ -168,6 +168,8 @@ github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/video-dev/go-elementalconductor v1.0.0 h1:vnWf1sAXgdTKhsHhAWOwKMBCPA+p0Rr8+ZotVUEes/4= github.com/video-dev/go-elementalconductor v1.0.0/go.mod h1:DwQxx5JUDRY0+/6OcAROOaOEUW6/FnOCDBkTVnWuJTM= +github.com/video-dev/go-elementalconductor v1.1.0 h1:T+voFHpYQKrjH+72KywQO7v+qnzcFYD+YZFpRCLQYUU= +github.com/video-dev/go-elementalconductor v1.1.0/go.mod h1:DwQxx5JUDRY0+/6OcAROOaOEUW6/FnOCDBkTVnWuJTM= github.com/video-dev/go-encodingcom v1.0.0 h1:OiVfdy9qUrKGfOID5TYwoBzvObNx9EQNI041ZA0kZ38= github.com/video-dev/go-encodingcom v1.0.0/go.mod h1:7O86jGr922w65EzPw83u2w36Mm+S90zdy4stfYFxelg= github.com/video-dev/zencoder v0.0.0-20161215190743-745874544382 h1:n83HJM7e1ECRGC6Q9Wb28Jmh/GwT973/r7x71QGyJrc= diff --git a/internal/provider/bitmovin/bitmovin.go b/internal/provider/bitmovin/bitmovin.go index bf71215f..03a846cf 100644 --- a/internal/provider/bitmovin/bitmovin.go +++ b/internal/provider/bitmovin/bitmovin.go @@ -228,6 +228,13 @@ func (p *bitmovinProvider) createH264VideoPreset(preset db.Preset, customData ma } h264.MaxGOP = intToPtr(int64(gopSize)) } + if preset.Video.BFrames != "" { + bFrames, err := strconv.Atoi(preset.Video.BFrames) + if err != nil { + return nil, err + } + h264.BFrames = intToPtr(int64(bFrames)) + } return h264, nil } diff --git a/internal/provider/elementalconductor/elementalconductor.go b/internal/provider/elementalconductor/elementalconductor.go index c7feb3c5..483ac371 100644 --- a/internal/provider/elementalconductor/elementalconductor.go +++ b/internal/provider/elementalconductor/elementalconductor.go @@ -70,6 +70,10 @@ func (p *elementalConductorProvider) CreatePreset(preset db.Preset) (string, err elementalConductorPreset.AudioCodec = preset.Audio.Codec elementalConductorPreset.AudioBitrate = preset.Audio.Bitrate + if preset.Video.BFrames != "" { + elementalConductorPreset.GopNumBFrames = preset.Video.BFrames + } + result, err := p.client.CreatePreset(&elementalConductorPreset) if err != nil { return "", err diff --git a/internal/provider/encodingcom/encodingcom.go b/internal/provider/encodingcom/encodingcom.go index 3691962e..2448aaaf 100644 --- a/internal/provider/encodingcom/encodingcom.go +++ b/internal/provider/encodingcom/encodingcom.go @@ -72,7 +72,12 @@ func (e *encodingComProvider) Transcode(job *db.Job) (*provider.JobStatus, error } func (e *encodingComProvider) CreatePreset(preset db.Preset) (string, error) { - resp, err := e.client.SavePreset(preset.Name, e.presetToFormat(preset)) + format, err := e.presetToFormat(preset) + if err != nil { + return "", err + } + + resp, err := e.client.SavePreset(preset.Name, format) if err != nil { return "", err } @@ -91,7 +96,7 @@ func (e *encodingComProvider) sourceMedia(original string) string { return original } -func (e *encodingComProvider) presetToFormat(preset db.Preset) encodingcom.Format { +func (e *encodingComProvider) presetToFormat(preset db.Preset) (encodingcom.Format, error) { falseYesNoBoolean := encodingcom.YesNoBoolean(false) format := encodingcom.Format{ Output: []string{preset.Container}, @@ -112,8 +117,18 @@ func (e *encodingComProvider) presetToFormat(preset db.Preset) encodingcom.Forma format.AudioCodec = e.getNormalizedCodec(preset.Audio.Codec) format.VideoCodec = e.getNormalizedCodec(preset.Video.Codec) format.Size = e.getSize(preset.Video.Width, preset.Video.Height) + + if preset.Video.BFrames != "" { + bframes, err := strconv.Atoi(preset.Video.BFrames) + if err != nil { + return format, err + } + + format.Bframes = bframes + } } - return format + + return format, nil } func (e *encodingComProvider) buildStream(preset db.Preset) []encodingcom.Stream { diff --git a/internal/provider/encodingcom/encodingcom_test.go b/internal/provider/encodingcom/encodingcom_test.go index 6d12e1e2..50319e8f 100644 --- a/internal/provider/encodingcom/encodingcom_test.go +++ b/internal/provider/encodingcom/encodingcom_test.go @@ -1216,7 +1216,12 @@ func TestPresetToFormat(t *testing.T) { } var p encodingComProvider for _, test := range tests { - resultingFormat := p.presetToFormat(test.givenPreset) + resultingFormat, err := p.presetToFormat(test.givenPreset) + + if err != nil { + t.Fatalf("Failed to convert preset to format: %#v", test.givenPreset) + } + if !reflect.DeepEqual(resultingFormat, test.expectedFormat) { t.Errorf("%s: presetToFormat: wrong value. Want %#v. Got %#v", test.givenTestCase, test.expectedFormat, resultingFormat) pretty.Fdiff(os.Stderr, resultingFormat, test.expectedFormat) diff --git a/internal/provider/mediaconvert/mediaconvert_test.go b/internal/provider/mediaconvert/mediaconvert_test.go index edf2682e..10fc4721 100644 --- a/internal/provider/mediaconvert/mediaconvert_test.go +++ b/internal/provider/mediaconvert/mediaconvert_test.go @@ -28,6 +28,7 @@ var ( Bitrate: "400000", GopSize: "120", InterlaceMode: "progressive", + BFrames: "3", }, Audio: db.AudioPreset{ Codec: "aac", @@ -92,13 +93,14 @@ func Test_mcProvider_CreatePreset(t *testing.T) { CodecSettings: &mediaconvert.VideoCodecSettings{ Codec: mediaconvert.VideoCodecH264, H264Settings: &mediaconvert.H264Settings{ - Bitrate: aws.Int64(400000), - CodecLevel: mediaconvert.H264CodecLevelLevel41, - CodecProfile: mediaconvert.H264CodecProfileHigh, - InterlaceMode: mediaconvert.H264InterlaceModeProgressive, - QualityTuningLevel: mediaconvert.H264QualityTuningLevelMultiPassHq, - RateControlMode: mediaconvert.H264RateControlModeVbr, - GopSize: aws.Float64(120), + Bitrate: aws.Int64(400000), + CodecLevel: mediaconvert.H264CodecLevelLevel41, + CodecProfile: mediaconvert.H264CodecProfileHigh, + InterlaceMode: mediaconvert.H264InterlaceModeProgressive, + QualityTuningLevel: mediaconvert.H264QualityTuningLevelMultiPassHq, + RateControlMode: mediaconvert.H264RateControlModeVbr, + GopSize: aws.Float64(120), + NumberBFramesBetweenReferenceFrames: aws.Int64(3), }, }, }, diff --git a/internal/provider/mediaconvert/preset_mapping.go b/internal/provider/mediaconvert/preset_mapping.go index 23c40009..215cb910 100644 --- a/internal/provider/mediaconvert/preset_mapping.go +++ b/internal/provider/mediaconvert/preset_mapping.go @@ -182,16 +182,27 @@ func videoPresetFrom(preset db.Preset) (*mediaconvert.VideoDescription, error) { tuning = mediaconvert.H264QualityTuningLevelMultiPassHq } + var bframes *int64 + if preset.Video.BFrames != "" { + b, err := strconv.ParseInt(preset.Video.BFrames, 10, 64) + + if err != nil { + return nil, errors.Wrapf(err, "parsing bframes %q to int64", preset.Video.BFrames) + } + bframes = &b + } + videoPreset.CodecSettings = &mediaconvert.VideoCodecSettings{ Codec: mediaconvert.VideoCodecH264, H264Settings: &mediaconvert.H264Settings{ - Bitrate: aws.Int64(bitrate), - GopSize: aws.Float64(gopSize), - RateControlMode: rateControl, - CodecProfile: profile, - CodecLevel: level, - InterlaceMode: interlaceMode, - QualityTuningLevel: tuning, + Bitrate: aws.Int64(bitrate), + GopSize: aws.Float64(gopSize), + RateControlMode: rateControl, + CodecProfile: profile, + CodecLevel: level, + InterlaceMode: interlaceMode, + QualityTuningLevel: tuning, + NumberBFramesBetweenReferenceFrames: bframes, }, } default: diff --git a/internal/provider/zencoder/zencoder.go b/internal/provider/zencoder/zencoder.go index 6b188264..755fe511 100644 --- a/internal/provider/zencoder/zencoder.go +++ b/internal/provider/zencoder/zencoder.go @@ -236,6 +236,14 @@ func (z *zencoderProvider) buildOutput(job *db.Job, preset db.Preset, filename s if preset.Video.Codec == "h264" { zencoderOutput.H264Profile = strings.ToLower(preset.Video.Profile) zencoderOutput.H264Level = strings.ToLower(preset.Video.ProfileLevel) + + if preset.Video.BFrames != "" { + bframes, err := strconv.ParseInt(preset.Video.BFrames, 10, 32) + if err != nil { + return zencoder.OutputSettings{}, fmt.Errorf("error converting preset bframes (%q): %s ", preset.Video.BFrames, err) + } + zencoderOutput.H264Bframes = int32(bframes) + } } if preset.RateControl == "CBR" { zencoderOutput.ConstantBitrate = true diff --git a/internal/provider/zencoder/zencoder_test.go b/internal/provider/zencoder/zencoder_test.go index 624ca478..b0c18a25 100644 --- a/internal/provider/zencoder/zencoder_test.go +++ b/internal/provider/zencoder/zencoder_test.go @@ -130,6 +130,7 @@ func TestGetPreset(t *testing.T) { GopMode: "fixed", GopSize: "90", Height: "1080", + BFrames: "4", }, Audio: db.AudioPreset{ Bitrate: "128000", @@ -163,6 +164,7 @@ func TestZencoderDeletePreset(t *testing.T) { GopMode: "fixed", GopSize: "90", Height: "1080", + BFrames: "3", }, Audio: db.AudioPreset{ Bitrate: "128000",