diff --git a/Source/C++/Apps/Mp4Info/Mp4Info.cpp b/Source/C++/Apps/Mp4Info/Mp4Info.cpp index 9968483d..205cd0bc 100644 --- a/Source/C++/Apps/Mp4Info/Mp4Info.cpp +++ b/Source/C++/Apps/Mp4Info/Mp4Info.cpp @@ -588,9 +588,17 @@ ShowSampleDescription_Text(AP4_SampleDescription& description, bool verbose) // Dolby AC-4 specifics AP4_Dac4Atom* dac4 = AP4_DYNAMIC_CAST(AP4_Dac4Atom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DAC4)); if (dac4) { + printf(" Codecs String: "); + AP4_String codec; + dac4->GetCodecString(codec); + printf("%s", codec.GetChars()); + printf("\n"); + const AP4_Dac4Atom::Ac4Dsi& dsi = dac4->GetDsi(); + printf(" AC-4 dsi version: %d\n", dsi.ac4_dsi_version); unsigned short self_contained = 0; if (dsi.ac4_dsi_version == 1) { + printf(" AC-4 bitstream version: %d\n", dsi.d.v1.bitstream_version); for (unsigned int i = 0; i < dsi.d.v1.n_presentations; i++) { AP4_Dac4Atom::Ac4Dsi::PresentationV1& presentation = dsi.d.v1.presentations[i]; if (presentation.presentation_version == 1 || presentation.presentation_version == 2) { @@ -929,11 +937,12 @@ ShowSampleDescription_Json(AP4_SampleDescription& description, bool verbose) if (dac4) { printf(",\n"); printf("\"dolby_ac4_info\": {\n"); - - printf(" \"presentations\": [\n"); const AP4_Dac4Atom::Ac4Dsi& dsi = dac4->GetDsi(); + printf(" \"dsi version\": %d,\n", dsi.ac4_dsi_version); unsigned short self_contained = 0; if (dsi.ac4_dsi_version == 1) { + printf(" \"bitstream version\": %d,\n", dsi.d.v1.bitstream_version); + printf(" \"presentations\": [\n"); const char* separator = ""; for (unsigned int i = 0; i < dsi.d.v1.n_presentations; i++) { AP4_Dac4Atom::Ac4Dsi::PresentationV1& presentation = dsi.d.v1.presentations[i]; @@ -1014,7 +1023,48 @@ ShowSampleDescription_Json(AP4_SampleDescription& description, bool verbose) // Dolby Vision specifics AP4_DvccAtom* dvcc = AP4_DYNAMIC_CAST(AP4_DvccAtom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DVCC)); + if(!dvcc) { + dvcc = AP4_DYNAMIC_CAST(AP4_DvccAtom, desc->GetDetails().GetChild(AP4_ATOM_TYPE_DVVC)); + } if (dvcc) { + /* Codec String */ + char workspace[64]; + char coding[5]; + strncpy(coding, codec.GetChars(), 4); + coding[4] = '\0'; + /* Non back-compatible */ + if (strcmp(coding, "dvav") == 0 || strcmp(coding, "dva1") == 0 || + strcmp(coding, "dvhe") == 0 || strcmp(coding, "dvh1") == 0){ + AP4_FormatString(workspace, + sizeof(workspace), + "%s.%02d.%02d", + coding, + dvcc->GetDvProfile(), + dvcc->GetDvLevel()); + codec = workspace; + } else { + if (strcmp(coding, "avc1") == 0){ + strcpy(coding, "dva1"); + }else if (strcmp(coding, "avc3") == 0){ + strcpy(coding, "dvav"); + }else if (strcmp(coding, "hev1") == 0){ + strcpy(coding, "dvhe"); + }else if (strcmp(coding, "hvc1") == 0){ + strcpy(coding, "dvh1"); + } + AP4_FormatString(workspace, + sizeof(workspace), + "%s,%s.%02d.%02d", + codec.GetChars(), + coding, + dvcc->GetDvProfile(), + dvcc->GetDvLevel()); + codec = workspace; + } + printf(",\n"); + printf("\"dv_codecs_string\":\""); + printf("%s", codec.GetChars()); + printf("\""); /* Dolby Vision */ printf(",\n"); printf("\"dolby_vision\": {\n"); diff --git a/Source/C++/Apps/Mp4Mux/Mp4Mux.cpp b/Source/C++/Apps/Mp4Mux/Mp4Mux.cpp index c00975d1..8412e711 100644 --- a/Source/C++/Apps/Mp4Mux/Mp4Mux.cpp +++ b/Source/C++/Apps/Mp4Mux/Mp4Mux.cpp @@ -91,10 +91,15 @@ PrintUsageAndExit() "\n" "Supported types:\n" " h264: H264/AVC NAL units\n" + " optional params:\n" + " dv_profile: integer number for Dolby vision profile ID (valid value: 9)\n" + " dv_bc: integer number for Dolby vision BL signal cross-compatibility ID (must be 2 if dv_profile is set to 9)\n" + " frame_rate: floating point number in frames per second (default=24.0)\n" + " format: avc1 (default) or avc3 for AVC tracks and Dolby Vision back-compatible tracks\n" " h265: H265/HEVC NAL units\n" " optional params:\n" - " dv_profile: integer number for Dolby vision profile ID (valid value: 5,8,9)\n" - " dv_bc: integer number for Dolby vision BL signal cross-compatibility ID (mandatory if dv_profile is set to 8/9)\n" + " dv_profile: integer number for Dolby vision profile ID (valid value: 5,8)\n" + " dv_bc: integer number for Dolby vision BL signal cross-compatibility ID (mandatory if dv_profile is set to 8)\n" " frame_rate: floating point number in frames per second (default=24.0)\n" " format: hev1 or hvc1 (default) for HEVC tracks and Dolby Vision backward-compatible tracks\n" " dvhe or dvh1 (default) for Dolby vision tracks\n" @@ -237,42 +242,40 @@ CheckDoviInputParameters(AP4_Array& parameters) format = AP4_SAMPLE_FORMAT_DVH1; } else if (parameters[i].m_Value == "dvhe") { format = AP4_SAMPLE_FORMAT_DVHE; + } else if (parameters[i].m_Value == "avc1") { + format = AP4_SAMPLE_FORMAT_AVC1; + } else if (parameters[i].m_Value == "avc3") { + format = AP4_SAMPLE_FORMAT_AVC3; } else if (parameters[i].m_Value == "dvav") { format = AP4_SAMPLE_FORMAT_DVAV; } else if (parameters[i].m_Value == "dva1") { format = AP4_SAMPLE_FORMAT_DVA1; + } else { + fprintf(stderr, "ERROR: format name is invalid\n"); + return AP4_ERROR_INVALID_PARAMETERS; } } } //check: sample entry box name is set correctly - if (format) { - if ((format == AP4_SAMPLE_FORMAT_DVAV) || (format == AP4_SAMPLE_FORMAT_DVA1)) { - if (profile != 9) { - fprintf(stderr, "ERROR: sample entry name is mismatch with profile\n"); - return AP4_ERROR_INVALID_PARAMETERS; - } - } else { - if ((profile != 8) && (profile != 5)) { - fprintf(stderr, "ERROR: sample entry name is mismatch with profile\n"); - return AP4_ERROR_INVALID_PARAMETERS; - } - } - } if (format) { if (profile == 5) { + // profile 5 does not compatible with other profile, only use dvhe or dvh1 if ((format != AP4_SAMPLE_FORMAT_DVHE) && (format != AP4_SAMPLE_FORMAT_DVH1)) { fprintf(stderr, "ERROR: sample entry name is not correct for profile 5\n"); return AP4_ERROR_INVALID_PARAMETERS; } } else if (profile == 8) { + // profile 8 has CCID with 1, 2, 4, may be compatible with HDR10, SDR or HLG, should + // not use dvhe or dvh1 if ((format != AP4_SAMPLE_FORMAT_HVC1) && (format != AP4_SAMPLE_FORMAT_HEV1)) { fprintf(stderr, "ERROR: sample entry name is not correct for profile 8\n"); return AP4_ERROR_INVALID_PARAMETERS; } } else if (profile == 9) { - if (format != AP4_SAMPLE_FORMAT_AVC1) { + // profile 9 only has CCID with 2, which is SDR compliant, should not use dvav or dva1 + if ((format != AP4_SAMPLE_FORMAT_AVC1) && (format != AP4_SAMPLE_FORMAT_AVC3)) { fprintf(stderr, "ERROR: sample entry name is not correct for profile 9\n"); return AP4_ERROR_INVALID_PARAMETERS; } @@ -1228,6 +1231,8 @@ AddH264DoviTrack(AP4_Movie& movie, AP4_UI32 dv_bl_signal_comp_id = 0; AP4_UI32 dv_level = 0; + AP4_UI32 format = 0; + AP4_ByteStream* input; AP4_Result result = AP4_FileByteStream::Create(input_name, AP4_FileByteStream::STREAM_MODE_READ, input); if (AP4_FAILED(result)) { @@ -1250,6 +1255,16 @@ AddH264DoviTrack(AP4_Movie& movie, return; } video_frame_rate = (unsigned int)(1000.0*frame_rate); + } else if (parameters[i].m_Name == "format") { + if (parameters[i].m_Value == "avc1") { + format = AP4_SAMPLE_FORMAT_AVC1; + } else if (parameters[i].m_Value == "avc3") { + format = AP4_SAMPLE_FORMAT_AVC3; + } else if (parameters[i].m_Value == "dva1") { + format = AP4_SAMPLE_FORMAT_DVA1; + } else if (parameters[i].m_Value == "dvav") { + format = AP4_SAMPLE_FORMAT_DVAV; + } } else if (parameters[i].m_Name == "dv_profile") { dv_profile = atoi(parameters[i].m_Value.GetChars()); } else if (parameters[i].m_Name == "dv_bc") { @@ -1270,6 +1285,13 @@ AddH264DoviTrack(AP4_Movie& movie, // parse the input AP4_AvcFrameParser parser; + + if (format == AP4_SAMPLE_FORMAT_AVC3 || format == AP4_SAMPLE_FORMAT_DVAV) { + parser.SetParameterControl(true); + } else if (format == AP4_SAMPLE_FORMAT_AVC1 || format == AP4_SAMPLE_FORMAT_DVA1) { + parser.SetParameterControl(false); + } + for (;;) { bool eos; unsigned char input_buffer[AP4_MUX_READ_BUFFER_SIZE]; @@ -1402,7 +1424,7 @@ AddH264DoviTrack(AP4_Movie& movie, // setup the video the sample descripton AP4_AvcDoviSampleDescription* sample_description = - new AP4_AvcDoviSampleDescription(AP4_SAMPLE_FORMAT_AVC1, + new AP4_AvcDoviSampleDescription(format, video_width, video_height, 24, @@ -1463,7 +1485,7 @@ AddH264DoviTrack(AP4_Movie& movie, track->UseTrakAtom()->AddChild(new_edts, 1); } // update the brands list - brands.Append(AP4_FILE_BRAND_AVC1); + brands.Append(format); // cleanup input->Release(); @@ -1836,6 +1858,11 @@ AddH265DoviTrack(AP4_Movie& movie, // parse the input AP4_HevcFrameParser parser; + if (format == AP4_SAMPLE_FORMAT_HEV1 || format == AP4_SAMPLE_FORMAT_DVHE) { + parser.SetParameterControl(true); + } else if (format == AP4_SAMPLE_FORMAT_HVC1 || format == AP4_SAMPLE_FORMAT_DVH1) { + parser.SetParameterControl(false); + } for (;;) { bool eos; unsigned char input_buffer[AP4_MUX_READ_BUFFER_SIZE]; @@ -1959,6 +1986,7 @@ AddH265DoviTrack(AP4_Movie& movie, AP4_UI08 num_temporal_layers = 0; // unknown AP4_UI08 temporal_id_nested = 0; // unknown AP4_UI08 nalu_length_size = 4; + AP4_UI08 transfer_characteristics = sps->vui_parameters.transfer_characteristics; sps->GetInfo(video_width, video_height); if (Options.verbose) { @@ -2073,6 +2101,13 @@ AddH265DoviTrack(AP4_Movie& movie, } // update the brands list brands.Append(format); + if (dv_profile == 8 && dv_bl_signal_comp_id == 4) { + if (transfer_characteristics == 18) { + brands.Append(AP4_FILE_BRAND_DB4H); + } else if (transfer_characteristics == 14) { + brands.Append(AP4_FILE_BRAND_DB4G); + } + } // cleanup input->Release(); @@ -2227,6 +2262,7 @@ main(int argc, char** argv) // add all the tracks bool hasDovi = false; + AP4_UI08 dolby_vision_ccid = 0; for (unsigned int i=0; iGetMvhdAtom()->SetNextTrackId(movie->GetTracks().ItemCount() + 1); diff --git a/Source/C++/Codecs/Ap4AvcParser.cpp b/Source/C++/Codecs/Ap4AvcParser.cpp index b95398b3..66e814f7 100644 --- a/Source/C++/Codecs/Ap4AvcParser.cpp +++ b/Source/C++/Codecs/Ap4AvcParser.cpp @@ -144,7 +144,8 @@ AP4_AvcFrameParser::AP4_AvcFrameParser() : m_PrevFrameNum(0), m_PrevFrameNumOffset(0), m_PrevPicOrderCntMsb(0), - m_PrevPicOrderCntLsb(0) + m_PrevPicOrderCntLsb(0), + m_keepParameterSets(true) { for (unsigned int i=0; i<256; i++) { m_PPS[i] = NULL; diff --git a/Source/C++/Codecs/Ap4AvcParser.h b/Source/C++/Codecs/Ap4AvcParser.h index 8f9cd6c3..18d7002b 100644 --- a/Source/C++/Codecs/Ap4AvcParser.h +++ b/Source/C++/Codecs/Ap4AvcParser.h @@ -293,6 +293,8 @@ class AP4_AvcFrameParser { unsigned int nal_ref_idc, AP4_AvcSliceHeader& slice_header); + void SetParameterControl(bool isKeep) { m_keepParameterSets = isKeep; } + private: // methods bool SameFrame(unsigned int nal_unit_type_1, unsigned int nal_ref_idc_1, AP4_AvcSliceHeader& sh1, @@ -322,6 +324,9 @@ class AP4_AvcFrameParser { unsigned int m_PrevFrameNumOffset; int m_PrevPicOrderCntMsb; unsigned int m_PrevPicOrderCntLsb; + + // control if the parameter sets(SPS, PPS) need to be stored in stream('mdat') + bool m_keepParameterSets; }; #endif // _AP4_AVC_PARSER_H_ diff --git a/Source/C++/Codecs/Ap4HevcParser.cpp b/Source/C++/Codecs/Ap4HevcParser.cpp index 306a7c1a..f9d1c1e0 100644 --- a/Source/C++/Codecs/Ap4HevcParser.cpp +++ b/Source/C++/Codecs/Ap4HevcParser.cpp @@ -664,6 +664,57 @@ AP4_HevcProfileTierLevel::Parse(AP4_BitReader& bits, unsigned int max_num_sub_la return AP4_SUCCESS; } +/*---------------------------------------------------------------------- +| AP4_HevcVuiParameters::AP4_HevcVuiParameters ++---------------------------------------------------------------------*/ +AP4_HevcVuiParameters::AP4_HevcVuiParameters() : + aspect_ratio_info_present_flag(0), + aspect_ratio_idc(0), + sar_width(0), + sar_height(0), + overscan_info_present_flag(0), + overscan_appropriate_flag(0), + video_signal_type_present_flag(0), + video_format(0), + video_full_range_flag(0), + colour_description_present_flag(0), + colour_primaries(0), + transfer_characteristics(0), + matrix_coeffs(0) +{ +} + +/*---------------------------------------------------------------------- +| AP4_HevcVuiParameters::Parse ++---------------------------------------------------------------------*/ +AP4_Result +AP4_HevcVuiParameters::Parse(AP4_BitReader& bits, unsigned int& transfer_characteristics) +{ + // vui_parameters + aspect_ratio_info_present_flag = bits.ReadBit(); + if (aspect_ratio_info_present_flag) { + aspect_ratio_idc = bits.ReadBits(8); + if (aspect_ratio_idc == 255) { + sar_width = bits.ReadBits(16); + sar_height = bits.ReadBits(16); + } + } + overscan_info_present_flag = bits.ReadBit(); + if (overscan_info_present_flag) overscan_appropriate_flag = bits.ReadBit(); + video_signal_type_present_flag = bits.ReadBit(); + if (video_signal_type_present_flag) { + video_format = bits.ReadBits(3); + video_full_range_flag = bits.ReadBit(); + colour_description_present_flag = bits.ReadBit(); + if (colour_description_present_flag) { + colour_primaries = bits.ReadBits(8); + transfer_characteristics = bits.ReadBits(8); + matrix_coeffs = bits.ReadBits(8); + } + } + return AP4_SUCCESS; +} + /*---------------------------------------------------------------------- | AP4_HevcPictureParameterSet::AP4_HevcPictureParameterSet +---------------------------------------------------------------------*/ @@ -827,9 +878,11 @@ AP4_HevcSequenceParameterSet::AP4_HevcSequenceParameterSet() : long_term_ref_pics_present_flag(0), num_long_term_ref_pics_sps(0), sps_temporal_mvp_enabled_flag(0), - strong_intra_smoothing_enabled_flag(0) + strong_intra_smoothing_enabled_flag(0), + vui_parameters_present_flag(0) { AP4_SetMemory(&profile_tier_level, 0, sizeof(profile_tier_level)); + AP4_SetMemory(&vui_parameters, 0, sizeof(vui_parameters)); for (unsigned int i=0; i<8; i++) { sps_max_dec_pic_buffering_minus1[i] = 0; sps_max_num_reorder_pics[i] = 0; @@ -935,6 +988,13 @@ AP4_HevcSequenceParameterSet::Parse(const unsigned char* data, unsigned int data } sps_temporal_mvp_enabled_flag = bits.ReadBit(); strong_intra_smoothing_enabled_flag = bits.ReadBit(); + vui_parameters_present_flag = bits.ReadBit(); + if (vui_parameters_present_flag) { + AP4_Result result = vui_parameters.Parse(bits, vui_parameters.transfer_characteristics); + if (AP4_FAILED(result)) { + return result; + } + } return AP4_SUCCESS; } @@ -1045,7 +1105,8 @@ AP4_HevcFrameParser::AP4_HevcFrameParser() : m_AccessUnitFlags(0), m_VclNalUnitsInAccessUnit(0), m_PrevTid0Pic_PicOrderCntMsb(0), - m_PrevTid0Pic_PicOrderCntLsb(0) + m_PrevTid0Pic_PicOrderCntLsb(0), + m_keepParameterSets(true) { for (unsigned int i=0; i<=AP4_HEVC_PPS_MAX_ID; i++) { m_PPS[i] = NULL; diff --git a/Source/C++/Codecs/Ap4HevcParser.h b/Source/C++/Codecs/Ap4HevcParser.h index 5e30662e..f71b25b9 100644 --- a/Source/C++/Codecs/Ap4HevcParser.h +++ b/Source/C++/Codecs/Ap4HevcParser.h @@ -153,6 +153,59 @@ struct AP4_HevcProfileTierLevel { } sub_layer_info[8]; }; +/*---------------------------------------------------------------------- +| AP4_HevcVuiParameters ++---------------------------------------------------------------------*/ +struct AP4_HevcVuiParameters { + AP4_HevcVuiParameters(); + + // methods + AP4_Result Parse(AP4_BitReader& bits, unsigned int &transfer_characteristics); + + unsigned int aspect_ratio_info_present_flag; + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coeffs; + //unsigned int chroma_loc_info_present_flag; + //unsigned int chroma_sample_loc_type_top_field; + //unsigned int chroma_sample_loc_type_bottom_field; + //unsigned int neutral_chroma_indication_flag; + //unsigned int field_seq_flag; + //unsigned int frame_field_info_present_flag; + //unsigned int default_display_window_flag; + //unsigned int def_disp_win_left_offset; + //unsigned int def_disp_win_right_offset; + //unsigned int def_disp_win_top_offset; + //unsigned int def_disp_win_bottom_offset; + //unsigned int vui_timing_info_present_flag; + //unsigned int vui_num_units_in_tick; + //unsigned int vui_time_scale; + //unsigned int vui_poc_proportional_to_timing_flag; + //unsigned int vui_num_ticks_poc_diff_one_minus1; + //unsigned int vui_hrd_parameters_present_flag; + + //// skip hrd_parameters + //unsigned int bitstream_restriction_flag; + //unsigned int tiles_fixed_structure_flag; + //unsigned int motion_vectors_over_pic_boundaries_flag; + //unsigned int restricted_ref_pic_lists_flag; + //unsigned int min_spatial_segmentation_idc; + //unsigned int max_bytes_per_pic_denom; + //unsigned int max_bits_per_min_cu_denom; + //unsigned int log2_max_mv_length_horizontal; + //unsigned int log2_max_mv_length_vertical; +}; + + /*---------------------------------------------------------------------- | AP4_HevcShortTermRefPicSet +---------------------------------------------------------------------*/ @@ -268,7 +321,8 @@ struct AP4_HevcSequenceParameterSet { unsigned int num_long_term_ref_pics_sps; unsigned int sps_temporal_mvp_enabled_flag; unsigned int strong_intra_smoothing_enabled_flag; - + unsigned int vui_parameters_present_flag; + AP4_HevcVuiParameters vui_parameters; AP4_HevcShortTermRefPicSet short_term_ref_pic_sets[AP4_HEVC_SPS_MAX_RPS]; }; @@ -416,6 +470,8 @@ class AP4_HevcFrameParser { AP4_HevcVideoParameterSet** GetVideoParameterSets() { return &m_VPS[0]; } AP4_HevcSequenceParameterSet** GetSequenceParameterSets() { return &m_SPS[0]; } AP4_HevcPictureParameterSet** GetPictureParameterSets() { return &m_PPS[0]; } + + void SetParameterControl(bool isKeep) { m_keepParameterSets = isKeep; } private: // methods @@ -441,6 +497,9 @@ class AP4_HevcFrameParser { // picture order counting unsigned int m_PrevTid0Pic_PicOrderCntMsb; unsigned int m_PrevTid0Pic_PicOrderCntLsb; + + // control if the parameter sets(VPS, SPS, PPS) need to be stored in stream('mdat') + bool m_keepParameterSets; }; #endif // _AP4_HEVC_PARSER_H_ diff --git a/Source/C++/Core/Ap4File.h b/Source/C++/Core/Ap4File.h index 9375258d..b916c685 100644 --- a/Source/C++/Core/Ap4File.h +++ b/Source/C++/Core/Ap4File.h @@ -73,6 +73,10 @@ const AP4_UI32 AP4_FILE_BRAND_OPF2 = AP4_ATOM_TYPE('o','p','f','2'); const AP4_UI32 AP4_FILE_BRAND_AVC1 = AP4_ATOM_TYPE('a','v','c','1'); const AP4_UI32 AP4_FILE_BRAND_HVC1 = AP4_ATOM_TYPE('h','v','c','1'); const AP4_UI32 AP4_FILE_BRAND_DBY1 = AP4_ATOM_TYPE('d','b','y','1'); +const AP4_UI32 AP4_FILE_BRAND_DB1P = AP4_ATOM_TYPE('d','b','1','p'); +const AP4_UI32 AP4_FILE_BRAND_DB2G = AP4_ATOM_TYPE('d','b','2','g'); +const AP4_UI32 AP4_FILE_BRAND_DB4H = AP4_ATOM_TYPE('d','b','4','h'); +const AP4_UI32 AP4_FILE_BRAND_DB4G = AP4_ATOM_TYPE('d','b','4','g'); /*---------------------------------------------------------------------- | AP4_File diff --git a/Source/Python/utils/mp4-dash.py b/Source/Python/utils/mp4-dash.py index 07c037d3..e226cae3 100755 --- a/Source/Python/utils/mp4-dash.py +++ b/Source/Python/utils/mp4-dash.py @@ -979,45 +979,61 @@ def OutputHls(options, set_attributes, audio_sets, video_sets, subtitles_sets, s iframes_playlist_name = options.hls_iframes_playlist_name iframes_playlist_path = media_subdir+'/'+iframes_playlist_name + supplemental_codec_string = '' + if hasattr(video_track, 'supplemental_codec'): + if hasattr(video_track, 'dv_brand'): + supplemental_codec_string = video_track.supplemental_codec+'/'+video_track.dv_brand + else: + supplemental_codec_string = video_track.supplemental_codec + if audio_groups: # one entry per matching audio group for audio_group_name in audio_groups: if '*' not in video_track.hls_group_match and audio_group_name not in video_track.hls_group_match: continue audio_codecs = ','.join(audio_groups[audio_group_name]['codecs']) - master_playlist_file.write('#EXT-X-STREAM-INF:{}AUDIO="{}",AVERAGE-BANDWIDTH={:.0f},BANDWIDTH={:.0f},CODECS="{}",RESOLUTION={:.0f}x{:.0f},FRAME-RATE={:.3f}\n'.format( + master_playlist_file.write('#EXT-X-STREAM-INF:{}AUDIO="{}",AVERAGE-BANDWIDTH={:.0f},BANDWIDTH={:.0f},VIDEO-RANGE={},CODECS="{}",RESOLUTION={:.0f}x{:.0f},FRAME-RATE={:.3f}\n'.format( subtitles_group, audio_group_name, video_track.average_segment_bitrate + audio_groups[audio_group_name]['average_segment_bitrate'], video_track.max_segment_bitrate + audio_groups[audio_group_name]['max_segment_bitrate'], + video_track.video_range, video_track.codec+','+audio_codecs, video_track.width, video_track.height, video_track.frame_rate)) + if supplemental_codec_string != '': + master_playlist_file.write(',SUPPLEMENTAL-CODECS="{}"\n'.format(supplemental_codec_string)) master_playlist_file.write(media_playlist_path+'\n') else: # no audio - master_playlist_file.write('#EXT-X-STREAM-INF:{}AVERAGE-BANDWIDTH={:.0f},BANDWIDTH={:.0f},CODECS="{}",RESOLUTION={:.0f}x{:.0f},FRAME-RATE={:.3f}\n'.format( + master_playlist_file.write('#EXT-X-STREAM-INF:{}AVERAGE-BANDWIDTH={:.0f},BANDWIDTH={:.0f},VIDEO-RANGE={},CODECS="{}",RESOLUTION={:.0f}x{:.0f},FRAME-RATE={:.3f}'.format( subtitles_group, video_track.average_segment_bitrate, video_track.max_segment_bitrate, + video_track.video_range, video_track.codec, video_track.width, video_track.height, video_track.frame_rate)) + if supplemental_codec_string != '': + master_playlist_file.write(',SUPPLEMENTAL-CODECS="{}"\n'.format(supplemental_codec_string)) master_playlist_file.write(media_playlist_path+'\n') OutputHlsTrack(options, video_track, all_audio_tracks + all_video_tracks, media_subdir, media_playlist_name, media_file_name) iframe_average_segment_bitrate,iframe_max_bitrate = OutputHlsIframeIndex(options, video_track, all_audio_tracks + all_video_tracks, media_subdir, iframes_playlist_name, media_file_name) # this will be written later - iframe_playlist_lines.append('#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH={:.0f},BANDWIDTH={:.0f},CODECS="{}",RESOLUTION={:.0f}x{:.0f},URI="{}"\n'.format( + iframe_playlist_lines.append('#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH={:.0f},BANDWIDTH={:.0f},VIDEO-RANGE={},CODECS="{}",RESOLUTION={:.0f}x{:.0f},URI="{}"'.format( iframe_average_segment_bitrate, iframe_max_bitrate, + video_track.video_range, video_track.codec, video_track.width, video_track.height, iframes_playlist_path)) + if supplemental_codec_string != '': + iframe_playlist_lines.append(',SUPPLEMENTAL-CODECS="{}"\n'.format(supplemental_codec_string)) master_playlist_file.write('\n# I-Frame Playlists\n') master_playlist_file.write(''.join(iframe_playlist_lines)) diff --git a/Source/Python/utils/mp4utils.py b/Source/Python/utils/mp4utils.py index afed8405..58f3c103 100644 --- a/Source/Python/utils/mp4utils.py +++ b/Source/Python/utils/mp4utils.py @@ -407,6 +407,7 @@ def __init__(self, parent, info): if self.type == 'video': # set the scan type (hardcoded for now) self.scan_type = 'progressive' + self.video_range = 'SDR' # set the width and height self.width = sample_desc['width'] @@ -415,21 +416,32 @@ def __init__(self, parent, info): # add dolby vision signaling if present if 'dolby_vision' in sample_desc: dv_info = sample_desc['dolby_vision'] - if sample_desc['coding'] in ['dvav', 'dva1', 'dvhe', 'dvh1']: - # non-backward-compatible - self.codec = sample_desc['coding'] + ('.%02d.%02d' % (dv_info['profile'], dv_info['level'])) + if dv_info['profile'] == 5: + self.video_range = 'PQ' + elif dv_info['profile'] in [8, 9]: + self.supplemental_codec = sample_desc['dv_codecs_string'].split(",")[1].split(".")[0] + \ + "." + sample_desc['dv_codecs_string'].split(",")[1].split(".")[1] + str('.%02d' % dv_info['level']) + bl_compatibility_id = dv_info['dv_bl_signal_compatibility_id'] + if bl_compatibility_id == 1: + self.video_range = 'PQ' + brand = 'db1p' + if brand in self.parent.info['file']['compatible_brands']: + self.dv_brand = brand + elif bl_compatibility_id == 2: + self.video_range = 'SDR' + brand = 'db2g' + if brand in self.parent.info['file']['compatible_brands']: + self.dv_brand = brand + elif bl_compatibility_id == 4: + self.video_range = 'HLG' + if 'db4g' in self.parent.info['file']['compatible_brands']: + self.dv_brand = 'db4g' + elif 'db4h' in self.parent.info['file']['compatible_brands']: + self.dv_brand = 'db4h' + else: + PrintErrorAndExit('ERROR: unsupported ccid for Dolby Vision profile 8/9.') else: - # backward-compatible - coding_map = { - 'avc1': 'dva1', - 'avc3': 'dvav', - 'hev1': 'dvhe', - 'hvc1': 'dvh1' - } - dv_coding = coding_map.get(sample_desc['coding']) - if dv_coding: - dv_string = dv_coding + ('.%02d.%02d' % (dv_info['profile'], dv_info['level'])) - self.codec += ','+dv_string + PrintErrorAndExit('ERROR: unsupported Dolby Vision profile.') if self.type == 'audio': self.sample_rate = sample_desc['sample_rate'] @@ -978,7 +990,7 @@ def ReGroupEC3Sets(audio_sets): for name, audio_tracks in audio_sets.items(): if audio_tracks[0].codec_family == 'ec-3': for track in audio_tracks: - if track.info['sample_descriptions'][0]['dolby_digital_plus_info']['atmos'] == 'Yes': + if track.info['sample_descriptions'][0]['dolby_digital_plus_info']['Dolby_Atmos'] == 'Yes': adaptation_set_name = ('audio', track.language, track.codec_family, track.channels, 'ATMOS') else: adaptation_set_name = ('audio', track.language, track.codec_family, track.channels)