aws-sdk-go-v2 icon indicating copy to clipboard operation
aws-sdk-go-v2 copied to clipboard

MediaConvert CreateJobTemplate error for Baseline profiles

Open tigrato opened this issue 3 years ago • 3 comments

Documentation

Describe the bug

When creating a simple encoding template job for H264 video output with Baseline profile the number NumberBFramesBetweenReferenceFrames has to be sent as 0 (Baseline does not support Bframes between reference frames). The field NumberBFramesBetweenReferenceFrames: int32 is only serialized if the provided value is not zero so it is never sent here

Expected behavior

The system allow me to create the JobTemplate without errors

Current behavior

MediaConvert API returns an error mentioning that NumberBFramesBetweenReferenceFrames must be <=0 OR that the Profile must be changed to something different than Baseline (If I sent the NumberBFramesBetweenReferenceFrames <0 the media convert api returns other error)

MediaConvert: CreateJobTemplate, https response error StatusCode: 400, RequestID: 083e9ebb-36b3-414b-8dd3-cbc50d2edbf8, BadRequestException: /outputGroups/0/outputs/3/videoDescription/codecSettings/h264Settings/codecProfile: Should match all dependencies: See other errors for more details | /outputGroups/0/outputs/3/videoDescription/codecSettings/h264Settings: Should match exactly one schema defined in "oneOf" | /outputGroups/0/outputs/3/videoDescription/codecSettings/h264Settings: numberBFramesBetweenReferenceFrames is a required property | /outputGroups/0/outputs/3/videoDescription/codecSettings/h264Settings/codecProfile: Should be equal to one of the allowed values in ["HIGH","HIGH_10BIT","HIGH_422","HIGH_422_10BIT","MAIN"]

Steps to Reproduce

Create via GO API a template with the Following H264 config

H264Settings: &types.H264Settings{
                AdaptiveQuantization: "HIGH",
                Bitrate: 1768615,
                CodecLevel: "LEVEL_3_1",
                CodecProfile: "BASELINE",
                DynamicSubGop: "STATIC",
                EntropyEncoding: "CAVLC",
                FieldEncoding: "PAFF",
                FlickerAdaptiveQuantization: "DISABLED",
                FramerateControl: "SPECIFIED",
                FramerateConversionAlgorithm: "DUPLICATE_DROP",
                FramerateDenominator: 1,
                FramerateNumerator: 25,
                GopBReference: "DISABLED",
                GopClosedCadence: 1,
                GopSize: 50.0,
                GopSizeUnits: "FRAMES",
                HrdBufferInitialFillPercentage: 0,
                HrdBufferSize: 0,
                InterlaceMode: "PROGRESSIVE",
                MaxBitrate: 0,
                MinIInterval: 0,
                NumberBFramesBetweenReferenceFrames: 0,
                NumberReferenceFrames: 2,
                ParControl: "SPECIFIED",
                ParDenominator: 1,
                ParNumerator: 1,
                QualityTuningLevel: "SINGLE_PASS",
                QvbrSettings: nil,
                RateControlMode: "CBR",
                RepeatPps: "DISABLED",
                ScanTypeConversionMode: "",
                SceneChangeDetect: "ENABLED",
                Slices: 1,
                SlowPal: "DISABLED",
                Softness: 0,
                SpatialAdaptiveQuantization: "ENABLED",
                Syntax: "DEFAULT",
                Telecine: "NONE",
                TemporalAdaptiveQuantization: "ENABLED",
                UnregisteredSeiTimecode: "DISABLED",
              },


Possible Solution

Mark NumberBFramesBetweenReferenceFrames to be serialized if the Baseline profile is selected even if it is zero or change the media convert behaviour to set it as 0 if baseline profile is found.

AWS Go SDK version used

v1.16

Compiler and Version used

1.17

Operating System and version

MacOS 11.6

tigrato avatar Feb 07 '22 15:02 tigrato

Hi, is this still persisting with the latest version of SDK?

vudh1 avatar Apr 29 '22 21:04 vudh1

This issue has not received a response in 1 week. If you want to keep this issue open, please just leave a comment below and auto-close will be canceled.

github-actions[bot] avatar May 02 '22 00:05 github-actions[bot]

@vudh1 yes, it is still present

tigrato avatar May 06 '22 19:05 tigrato

Hi @tigrato ,

Sorry for the really long wait time. This issue took time to investigate.

The problem is that the default value 0, is being ignored even though you are trying to set it explicitly. This is because of the way the mediaconvert service team exports their model does not work correctly with Smithy based SDKs (Kotlin, Rust, Go, Swift..) In specific, those exports lack the "Default" trait which indicates default values that should be serialized.

In your case NumberBFramesBetweenReferenceFrames is set to 0, but 0 is also the default value for it so the field is ignored in serialization. Unfortunately right now there is no solution for this and these individual parameters will have to be patched by hand. A broad solution will be addressed in the future as part of a cross-SDK effort.

In the meantime, you can check out my workaround code. In short, what the code does is:

  • Create a custom middleware
  • In that middleware we get the request JSON body, and hard-insert the missing field and value and re-write the body.
  • Lastly we append the custom middleware after the Serialize step.

⚠️ Please note that this code works with the parameters I have made up, and might look a bit different for you.

func main() {

	customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
		if service == mediaconvert.ServiceID && region == "us-east-1" {
			return aws.Endpoint{
				PartitionID:   "aws",
				URL:           "https://q25wbt2lc.mediaconvert.us-east-1.amazonaws.com",
				SigningRegion: "us-east-1",
			}, nil
		}
		return aws.Endpoint{}, &aws.EndpointNotFoundError{}
	})

	myMiddleware := middleware.SerializeMiddlewareFunc("MyTestMiddleware",
		func(ctx context.Context, input middleware.SerializeInput, next middleware.SerializeHandler,
		) (
			output middleware.SerializeOutput, metadata middleware.Metadata, err error,
		) {
			req, ok := input.Request.(*smithyhttp.Request)
			if !ok {
				return output, metadata, fmt.Errorf("unexpected transport: %T", input.Request)
			}

			// get the body from the request
			bodyStream := req.GetStream()
			buf := new(strings.Builder)
			_, err = io.Copy(buf, bodyStream)
			actualBodyString := buf.String()

			// the location after which we want to insert the parameter.
			substring := `h264Settings":{`
			index := strings.Index(actualBodyString, substring)

			// re-constructing the body
			newBodyString := actualBodyString[:index+len(substring)] + `"NumberBFramesBetweenReferenceFrames":0,` + actualBodyString[len(substring)+index:]
			newStream := strings.NewReader(newBodyString)

			// matching the content length
			req.ContentLength = int64(len(newBodyString))
			input.Request = req

			// overriding the body
			input.Request, err = req.SetStream(newStream)
			if err != nil {
				panic(err)
			}

			return next.HandleSerialize(ctx, input)
		})

	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolverWithOptions(customResolver), config.WithClientLogMode(aws.LogRequestWithBody|aws.LogResponseWithBody))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}

	client := mediaconvert.NewFromConfig(cfg, func(options *mediaconvert.Options) {
		options.APIOptions = append(options.APIOptions, func(stack *middleware.Stack) error {
			return stack.Serialize.Add(myMiddleware, middleware.After)
		})

	})

	// this is a fake template, your input fields might look different
	out, err := client.CreateJobTemplate(context.Background(), &mediaconvert.CreateJobTemplateInput{
		Name: aws.String("1582_template"),
		Settings: &types.JobTemplateSettings{
			OutputGroups: []types.OutputGroup{
				{
					OutputGroupSettings: &types.OutputGroupSettings{
						Type: "CMAF_GROUP_SETTINGS",

						CmafGroupSettings: &types.CmafGroupSettings{
							SegmentLength:  1,
							FragmentLength: 1,
						},
					},
					Outputs: []types.Output{
						{
							ContainerSettings: &types.ContainerSettings{
								Container: "CMFC",
							},
							VideoDescription: &types.VideoDescription{
								CodecSettings: &types.VideoCodecSettings{
									Codec: types.VideoCodecH264,
									H264Settings: &types.H264Settings{
										AdaptiveQuantization:                "HIGH",
										Bitrate:                             1768615,
										CodecLevel:                          "LEVEL_3_1",
										CodecProfile:                        "BASELINE",
										DynamicSubGop:                       "STATIC",
										EntropyEncoding:                     "CAVLC",
										FieldEncoding:                       "PAFF",
										FlickerAdaptiveQuantization:         "DISABLED",
										FramerateControl:                    "SPECIFIED",
										FramerateConversionAlgorithm:        "DUPLICATE_DROP",
										FramerateDenominator:                1,
										FramerateNumerator:                  25,
										GopBReference:                       "DISABLED",
										GopClosedCadence:                    1,
										GopSize:                             50.0,
										GopSizeUnits:                        "FRAMES",
										HrdBufferInitialFillPercentage:      0,
										HrdBufferSize:                       0,
										InterlaceMode:                       "PROGRESSIVE",
										MaxBitrate:                          0,
										MinIInterval:                        0,
										NumberBFramesBetweenReferenceFrames: 0,
										NumberReferenceFrames:               2,
										ParControl:                          "SPECIFIED",
										ParDenominator:                      1,
										ParNumerator:                        1,
										QualityTuningLevel:                  "SINGLE_PASS",
										QvbrSettings:                        nil,
										RateControlMode:                     "CBR",
										RepeatPps:                           "DISABLED",
										ScanTypeConversionMode:              "",
										SceneChangeDetect:                   "ENABLED",
										Slices:                              1,
										SlowPal:                             "DISABLED",
										Softness:                            0,
										SpatialAdaptiveQuantization:         "ENABLED",
										Syntax:                              "DEFAULT",
										Telecine:                            "NONE",
										TemporalAdaptiveQuantization:        "ENABLED",
										UnregisteredSeiTimecode:             "DISABLED",
									},
								},
							},
						},
					},
				},
			},
		},
	})

	if err != nil {
		panic(err)
	}
}

Let me know if this helps at all. I'm going to leave this issue open so we can address it down the line.

Thanks again for your patience, Ran~

RanVaknin avatar Jan 09 '23 05:01 RanVaknin

Hey, @RanVaknin thanks for the response. I ended up with something similar to your workaround to force the inclusion of the missing values. Hope this gets fixed soon so I can get rid of this code.

tigrato avatar Jan 09 '23 15:01 tigrato

Hi @tigrato ,

We are tracking this in a separate ticket https://github.com/aws/aws-sdk/issues/577

Closing this in favor of tracking it there. Thanks, Ran~

RanVaknin avatar Aug 10 '23 18:08 RanVaknin

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

github-actions[bot] avatar Aug 10 '23 18:08 github-actions[bot]