Skip to content

Commit

Permalink
Merge pull request axiomatic-systems#798 from DolbyLaboratories/dolby…
Browse files Browse the repository at this point in the history
…/dovi_hls

Add Dolby Vision signaling for HLS.
  • Loading branch information
barbibulle authored Nov 21, 2022
2 parents 5e7bb34 + 7fe0287 commit c3b666f
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 42 deletions.
54 changes: 52 additions & 2 deletions Source/C++/Apps/Mp4Info/Mp4Info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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");
Expand Down
88 changes: 70 additions & 18 deletions Source/C++/Apps/Mp4Mux/Mp4Mux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -237,42 +242,40 @@ CheckDoviInputParameters(AP4_Array<Parameter>& 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;
}
Expand Down Expand Up @@ -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)) {
Expand All @@ -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") {
Expand All @@ -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];
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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; i<input_names.ItemCount(); i++) {
char* input_name = input_names[i];
const char* input_type = NULL;
Expand Down Expand Up @@ -2285,6 +2321,15 @@ main(int argc, char** argv)
if (parameters[i].m_Name == "dv_profile") {
isDovi = true;
}
if (parameters[i].m_Name == "dv_bc") {
if (parameters[i].m_Value == "1") {
dolby_vision_ccid |= 0x1;
} else if (parameters[i].m_Value == "2") {
dolby_vision_ccid |= 0x2;
} else if (parameters[i].m_Value == "4") {
dolby_vision_ccid |= 0x4;
}
}
}

if (!strcmp(input_type, "h264")) {
Expand Down Expand Up @@ -2334,6 +2379,13 @@ main(int argc, char** argv)
if (hasDovi) {
brands.Append(AP4_FILE_BRAND_DBY1);
}
// brand of Dolby Vision with CCID == 4 needs transfer characteristic to be decided later
if (dolby_vision_ccid & 0x1) {
brands.Append(AP4_FILE_BRAND_DB1P);
}
if (dolby_vision_ccid & 0x2) {
brands.Append(AP4_FILE_BRAND_DB2G);
}

movie->GetMvhdAtom()->SetNextTrackId(movie->GetTracks().ItemCount() + 1);

Expand Down
3 changes: 2 additions & 1 deletion Source/C++/Codecs/Ap4AvcParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions Source/C++/Codecs/Ap4AvcParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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_
Loading

0 comments on commit c3b666f

Please sign in to comment.