Search code examples
amazon-web-servicesboto3aws-media-convert

AWS MediaConvert: HLS Output Video Plays Without Audio, Despite .aac and .ts Files in S3


I'm using AWS MediaConvert to transcode a video into both DASH and HLS formats. While the DASH output works fine, the HLS output plays the video without audio. However, the output directory in the S3 bucket contains both the .aac (audio) and .ts (video) files, as well as the .m3u8 manifest files.

Here is the relevant part of my MediaConvert service class:

class MediaConvertService:
    aws_region = settings.AWS_REGION_NAME
    role_arn = settings.MEDIA_CONVERT_ROLE_ARN
    job_template = settings.MEDIA_CONVERT_JOB_TEMPLATE

    @classmethod
    def get_client(cls):
        return boto3.client("mediaconvert", region_name=cls.aws_region)

    @classmethod
    def prepare_media_convert_job_settings(cls, s3_url, bucket_name, object_key):
        job_settings = {
            "Role": cls.role_arn,
            "JobTemplate": cls.job_template,
            "Settings": {
                "Inputs": [
                    {
                        "FileInput": s3_url,
                        "VideoSelector": {
                            "ColorSpace": "FOLLOW",
                        },
                        "AudioSelectors": {
                            "Audio Selector 1": {"DefaultSelection": "DEFAULT"}
                        },
                        "TimecodeSource": "ZEROBASED",
                    }
                ],
                "OutputGroups": [
                    {
                        "CustomName": "CF-AEMC-GRP",
                        "Name": "DASH ISO",
                        "OutputGroupSettings": {
                            "Type": "DASH_ISO_GROUP_SETTINGS",
                            "DashIsoGroupSettings": {
                                "Destination": f"s3://{bucket_name}/output/DASH/{object_key}/",
                                "FragmentLength": 2,
                                "SegmentLength": 30,
                                "AudioChannelConfigSchemeIdUri": "MPEG_CHANNEL_CONFIGURATION"
                            }
                        },
                        "Outputs": [
                            {
                                "ContainerSettings": {"Container": "MPD"},
                                "VideoDescription": {
                                    "CodecSettings": {
                                        "Codec": "H_264",
                                        "H264Settings": {
                                            "MaxBitrate": 20000000,
                                            "RateControlMode": "QVBR",
                                            "SceneChangeDetect": "TRANSITION_DETECTION",
                                            "QvbrSettings": {
                                                "QvbrQualityLevel": 7
                                            }
                                        }
                                    }
                                },
                                "NameModifier": "_dash_video"
                            },
                            {
                                "ContainerSettings": {"Container": "MPD"},
                                "AudioDescriptions": [
                                    {
                                        "CodecSettings": {
                                            "Codec": "AAC",
                                            "AacSettings": {
                                                "Bitrate": 96000,
                                                "CodingMode": "CODING_MODE_2_0",
                                                "SampleRate": 48000
                                            }
                                        }
                                    }
                                ],
                                "NameModifier": "_dash_audio"
                            }
                        ]
                    },
                    {
                        "CustomName": "CF-AEMC-GRP-HLS",
                        "Name": "Apple HLS",
                        "OutputGroupSettings": {
                            "Type": "HLS_GROUP_SETTINGS",
                            "HlsGroupSettings": {
                                "Destination": f"s3://{bucket_name}/output/HLS/{object_key}/",
                                "SegmentLength": 6,
                                "MinSegmentLength": 2,
                                "OutputSelection": "MANIFESTS_AND_SEGMENTS",
                                "DirectoryStructure": "SINGLE_DIRECTORY",
                                "ManifestCompression": "NONE",
                                "StreamInfResolution": "INCLUDE"
                            }
                        },
                        "Outputs": [
                            {
                                "ContainerSettings": {"Container": "M3U8"},
                                "VideoDescription": {
                                    "CodecSettings": {
                                        "Codec": "H_264",
                                        "H264Settings": {
                                            "MaxBitrate": 20000000,
                                            "RateControlMode": "QVBR",
                                            "SceneChangeDetect": "TRANSITION_DETECTION",
                                            "QvbrSettings": {
                                                "QvbrQualityLevel": 7
                                            }
                                        }
                                    }
                                },
                                "NameModifier": "_hls_video"
                            },
                            {
                                "ContainerSettings": {"Container": "M3U8"},
                                "AudioDescriptions": [
                                    {
                                        "AudioSourceName": "Audio Selector 1",
                                        "CodecSettings": {
                                            "Codec": "AAC",
                                            "AacSettings": {
                                                "Bitrate": 96000,
                                                "CodingMode": "CODING_MODE_2_0",
                                                "SampleRate": 48000
                                            }
                                        }
                                    }
                                ],
                                "NameModifier": "_hls_audio"
                            }
                        ]
                    }
                ]
            },
        }
        return job_settings

    @classmethod
    def create_job(cls, s3_url, bucket_name, object_key):
        job_settings = cls.prepare_media_convert_job_settings(
            s3_url=s3_url, bucket_name=bucket_name, object_key=object_key
        )
        mediaconvert_client = cls.get_client()
        response = mediaconvert_client.create_job(**job_settings)
        assert response.get("ResponseMetadata", {}).get("HTTPStatusCode") == 201
        # remove extension from object key
        key = object_key.split(".")[0]
        dash_url = settings.DASH_FILE_PATH.format(
            bucket_name=bucket_name,
            region=cls.aws_region,
            content_dir=f"DASH/{object_key}",
            dash_file_name=key,
        )
        hls_url = settings.HLS_FILE_PATH.format(
            bucket_name=bucket_name,
            region=cls.aws_region,
            content_dir=f"HLS/{object_key}",
            hls_file_name=key,
        )
        return dash_url, hls_url

This is the Job template JSON (Create job request body)

{
  "Description": "DASH ISO media convert for videos",
  "Category": "video",
  "Name": "Cineflare Prod AEMC Template",
  "Settings": {
    "TimecodeConfig": {
      "Source": "ZEROBASED"
    },
    "OutputGroups": [
      {
        "CustomName": "CF-AEMC-GRP",
        "Name": "DASH ISO",
        "Outputs": [
          {
            "ContainerSettings": {
              "Container": "MPD"
            },
            "VideoDescription": {
              "CodecSettings": {
                "Codec": "H_264",
                "H264Settings": {
                  "MaxBitrate": 10000000,
                  "RateControlMode": "QVBR",
                  "SceneChangeDetect": "TRANSITION_DETECTION"
                }
              }
            },
            "NameModifier": "_dash_video"
          },
          {
            "ContainerSettings": {
              "Container": "MPD"
            },
            "AudioDescriptions": [
              {
                "AudioSourceName": "Audio Selector 1",
                "CodecSettings": {
                  "Codec": "AAC",
                  "AacSettings": {
                    "Bitrate": 96000,
                    "CodingMode": "CODING_MODE_2_0",
                    "SampleRate": 48000
                  }
                }
              }
            ],
            "NameModifier": "_dash_audio"
          }
        ],
        "OutputGroupSettings": {
          "Type": "DASH_ISO_GROUP_SETTINGS",
          "DashIsoGroupSettings": {
            "AudioChannelConfigSchemeIdUri": "MPEG_CHANNEL_CONFIGURATION",
            "SegmentLength": 30,
            "Destination": "s3://cineflare-content/output/DASH/",
            "DestinationSettings": {
              "S3Settings": {
                "StorageClass": "STANDARD"
              }
            },
            "FragmentLength": 2
          }
        }
      },
      {
        "CustomName": "CF-AEMC-GRP-HLS",
        "Name": "Apple HLS",
        "Outputs": [
          {
            "ContainerSettings": {
              "Container": "M3U8",
              "M3u8Settings": {}
            },
            "VideoDescription": {
              "CodecSettings": {
                "Codec": "H_264",
                "H264Settings": {
                  "MaxBitrate": 20000000,
                  "RateControlMode": "QVBR",
                  "SceneChangeDetect": "TRANSITION_DETECTION"
                }
              }
            },
            "OutputSettings": {
              "HlsSettings": {}
            },
            "NameModifier": "_hls_video"
          },
          {
            "ContainerSettings": {
              "Container": "M3U8",
              "M3u8Settings": {}
            },
            "AudioDescriptions": [
              {
                "AudioSourceName": "Audio Selector 1",
                "CodecSettings": {
                  "Codec": "AAC",
                  "AacSettings": {
                    "Bitrate": 96000,
                    "CodingMode": "CODING_MODE_2_0",
                    "SampleRate": 48000
                  }
                }
              }
            ],
            "OutputSettings": {
              "HlsSettings": {}
            },
            "NameModifier": "_hls_audio"
          }
        ],
        "OutputGroupSettings": {
          "Type": "HLS_GROUP_SETTINGS",
          "HlsGroupSettings": {
            "SegmentLength": 10,
            "Destination": "s3://cineflare-content/output/HLS/",
            "DestinationSettings": {
              "S3Settings": {
                "StorageClass": "STANDARD"
              }
            },
            "MinSegmentLength": 0
          }
        }
      }
    ],
    "Inputs": [
      {
        "AudioSelectors": {
          "Audio Selector 1": {
            "DefaultSelection": "DEFAULT"
          }
        },
        "VideoSelector": {
          "ColorSpace": "FOLLOW"
        },
        "TimecodeSource": "ZEROBASED"
      }
    ]
  },
  "AccelerationSettings": {
    "Mode": "DISABLED"
  },
  "StatusUpdateInterval": "SECONDS_60",
  "Priority": 0,
  "HopDestinations": []
}

This is the text content in the root manifest file:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:BANDWIDTH=1128250,AVERAGE-BANDWIDTH=537898,CODECS="avc1.640029",RESOLUTION=640x360,FRAME-RATE=24.000
Padmaavat_Official_Trailer_Ranveer_Singh_Shahid_Kapoor_Deepika_Padukone_hls_video.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=96510,AVERAGE-BANDWIDTH=96210,CODECS="mp4a.40.2"
Padmaavat_Official_Trailer_Ranveer_Singh_Shahid_Kapoor_Deepika_Padukone_hls_audio.m3u8

This is the text content in audio manifest file: [I've trimmed it]

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:6,
Padmaavat_Official_Trailer_Ranveer_Singh_Shahid_Kapoor_Deepika_Padukone_hls_audio_00001.aac
#EXTINF:6,
Padmaavat_Official_Trailer_Ranveer_Singh_Shahid_Kapoor_Deepika_Padukone_hls_audio_00002.aac
#EXTINF:6,
#EXT-X-ENDLIST

Issues:

  1. The HLS output video plays without audio, even though the .aac and .ts files, as well as the .m3u8 manifest files, are present in the output directory on S3.
  2. The DASH output works perfectly with both audio and video

Question:

  1. Is there any configuration I'm missing in my HLS output settings that could be causing the audio not to play?
  2. Are there any additional steps I need to take to ensure that the HLS stream correctly combines the video and audio tracks?

Solution

  • The parent manifest is not formatted to associate the video with any audio.

    • It is missing the 'AUDIO="program_audio" parameter in the video line

    • The audio rendition should be declared with a tag like: #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="program_audio"

    • List item

    To fix this, try:

    a. For each audio rendition, specify a name in the 'Audio group ID' field

    b. On each video rendition, specify 'Audio rendition sets' field to name each 'Audio group ID' you want associated to that video rendition

    c. Optional : you can change the default selections by altering the values of 'Audio track type' in the console.