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": " 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://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://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:
Question:
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.