diff --git a/apps/bmxtranswrap/bmxtranswrap.cpp b/apps/bmxtranswrap/bmxtranswrap.cpp index 88c74029..560fdc0a 100644 --- a/apps/bmxtranswrap/bmxtranswrap.cpp +++ b/apps/bmxtranswrap/bmxtranswrap.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -4325,6 +4326,15 @@ int main(int argc, const char** argv) if (afd) clip_track->SetAFD(afd); break; + case VC3_DNXHR_444: + case VC3_DNXHR_HQX: + case VC3_DNXHR_HQ: + case VC3_DNXHR_SQ: + case VC3_DNXHR_LB: + if (afd) + clip_track->SetAFD(afd); + clip_track->SetComponentDepth(input_picture_info->component_depth); + break; case WAVE_PCM: clip_track->SetSamplingRate(output_sound_info->sampling_rate); clip_track->SetQuantizationBits(output_sound_info->bits_per_sample); @@ -4460,6 +4470,14 @@ int main(int argc, const char** argv) if (BMX_OPT_PROP_IS_SET(user_rdd36_opaque)) rdd36_helper->SetIsOpaque(user_rdd36_opaque); } + + VC3MXFDescriptorHelper *vc3_helper = dynamic_cast(pict_helper); + if (vc3_helper) { + if (input_picture_info->display_width > 0) + vc3_helper->SetFrameWidth(input_picture_info->display_width); + if (input_picture_info->display_height > 0) + vc3_helper->SetFrameHeight(input_picture_info->display_height); + } } else if (sound_helper) { if (BMX_OPT_PROP_IS_SET(user_ref_image_edit_rate)) sound_helper->SetReferenceImageEditRate(user_ref_image_edit_rate); diff --git a/apps/raw2bmx/raw2bmx.cpp b/apps/raw2bmx/raw2bmx.cpp index d786035b..601a4c5e 100644 --- a/apps/raw2bmx/raw2bmx.cpp +++ b/apps/raw2bmx/raw2bmx.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,7 @@ struct RawInput BMX_OPT_PROP_DECL(uint8_t, afd); BMX_OPT_PROP_DECL(uint32_t, component_depth); uint32_t input_height; + uint32_t input_width; bool have_avci_header; bool d10_fixed_frame_size; BMX_OPT_PROP_DECL(MXFSignalStandard, signal_standard); @@ -853,6 +855,11 @@ static void usage(const char *cmd) printf(" --vc3_720p_1258 Raw VC3/DNxHD 1280x720p 45 Mbps input file\n"); printf(" --vc3_1080p_1259 Raw VC3/DNxHD 1920x1080p 85 Mbps input file\n"); printf(" --vc3_1080i_1260 Raw VC3/DNxHD 1920x1080i 85 Mbps input file\n"); + printf(" --vc3_dnxhr_444 Raw VC3/DNxHR 4:4:4 12-bit input file\n"); + printf(" --vc3_dnxhr_hqx Raw VC3/DNxHR High Quality 12-bit input file\n"); + printf(" --vc3_dnxhr_hq Raw VC3/DNxHR High Quality input file\n"); + printf(" --vc3_dnxhr_sq Raw VC3/DNxHR Standard Quality input file\n"); + printf(" --vc3_dnxhr_lb Raw VC3/DNxHR Low Bandwidth input file\n"); printf(" --pcm Raw PCM audio input file\n"); printf(" --wave Wave PCM audio input file\n"); printf(" --anc Raw ST 436 Ancillary data. Requires the --anc-const option or frame wrapped in KLV and the --klv option\n"); @@ -2095,6 +2102,23 @@ int main(int argc, const char** argv) cmdln_index++; continue; // skip input reset at the end } + else if (strcmp(argv[cmdln_index], "--width") == 0) + { + if (cmdln_index + 1 >= argc) + { + usage_ref(argv[0]); + fprintf(stderr, "Missing argument for Option '%s'\n", argv[cmdln_index]); + return 1; + } + if (!parse_int(argv[cmdln_index + 1], &value) || value == 0) { + usage_ref(argv[0]); + fprintf(stderr, "Invalid value '%s' for Option '%s'\n", argv[cmdln_index + 1], argv[cmdln_index]); + return 1; + } + input.input_width = value; + cmdln_index++; + continue; // skip input reset at the end + } else if (strcmp(argv[cmdln_index], "--height") == 0) { if (cmdln_index + 1 >= argc) @@ -4035,6 +4059,71 @@ int main(int argc, const char** argv) inputs.push_back(input); cmdln_index++; } + else if (strcmp(argv[cmdln_index], "--vc3_dnxhr_444") == 0) + { + if (cmdln_index + 1 >= argc) + { + usage_ref(argv[0]); + fprintf(stderr, "Missing argument for input '%s'\n", argv[cmdln_index]); + return 1; + } + input.essence_type = VC3_DNXHR_444; + input.filename = argv[cmdln_index + 1]; + inputs.push_back(input); + cmdln_index++; + } + else if (strcmp(argv[cmdln_index], "--vc3_dnxhr_hqx") == 0) + { + if (cmdln_index + 1 >= argc) + { + usage_ref(argv[0]); + fprintf(stderr, "Missing argument for input '%s'\n", argv[cmdln_index]); + return 1; + } + input.essence_type = VC3_DNXHR_HQX; + input.filename = argv[cmdln_index + 1]; + inputs.push_back(input); + cmdln_index++; + } + else if (strcmp(argv[cmdln_index], "--vc3_dnxhr_hq") == 0) + { + if (cmdln_index + 1 >= argc) + { + usage_ref(argv[0]); + fprintf(stderr, "Missing argument for input '%s'\n", argv[cmdln_index]); + return 1; + } + input.essence_type = VC3_DNXHR_HQ; + input.filename = argv[cmdln_index + 1]; + inputs.push_back(input); + cmdln_index++; + } + else if (strcmp(argv[cmdln_index], "--vc3_dnxhr_sq") == 0) + { + if (cmdln_index + 1 >= argc) + { + usage_ref(argv[0]); + fprintf(stderr, "Missing argument for input '%s'\n", argv[cmdln_index]); + return 1; + } + input.essence_type = VC3_DNXHR_SQ; + input.filename = argv[cmdln_index + 1]; + inputs.push_back(input); + cmdln_index++; + } + else if (strcmp(argv[cmdln_index], "--vc3_dnxhr_lb") == 0) + { + if (cmdln_index + 1 >= argc) + { + usage_ref(argv[0]); + fprintf(stderr, "Missing argument for input '%s'\n", argv[cmdln_index]); + return 1; + } + input.essence_type = VC3_DNXHR_LB; + input.filename = argv[cmdln_index + 1]; + inputs.push_back(input); + cmdln_index++; + } else if (strcmp(argv[cmdln_index], "--pcm") == 0) { if (cmdln_index + 1 >= argc) @@ -4274,6 +4363,16 @@ int main(int argc, const char** argv) } } + // change default component depth for VC-3/DNxHR 12-bit + for (i = 0; i < inputs.size(); i++) { + RawInput *input = &inputs[i]; + if ((input->essence_type == VC3_DNXHR_444 || + input->essence_type == VC3_DNXHR_HQX) && + !BMX_OPT_PROP_IS_SET(input->component_depth)) + { + BMX_OPT_PROP_SET(input->component_depth, 12); + } + } // extract essence info for (i = 0; i < inputs.size(); i++) { @@ -4426,6 +4525,13 @@ int main(int argc, const char** argv) } else { vc3_parser->ParseFrameInfo(input->raw_reader->GetSampleData(), input->raw_reader->GetSampleDataSize()); + if (input->input_width == 0) + input->input_width = vc3_parser->GetFrameWidth(); + if (input->input_height == 0) + input->input_height = vc3_parser->GetFrameHeight(); + + input->raw_reader->SetFixedSampleSize(vc3_parser->GetFrameSize()); + switch (vc3_parser->GetCompressionId()) { case 1235: @@ -4470,6 +4576,22 @@ int main(int argc, const char** argv) case 1260: input->essence_type = VC3_1080I_1260; break; + // DNxHR + case 1270: + input->essence_type = VC3_DNXHR_444; + break; + case 1271: + input->essence_type = VC3_DNXHR_HQX; + break; + case 1272: + input->essence_type = VC3_DNXHR_HQ; + break; + case 1273: + input->essence_type = VC3_DNXHR_SQ; + break; + case 1274: + input->essence_type = VC3_DNXHR_LB; + break; default: log_error("Unknown VC3 essence type\n"); throw false; @@ -5609,6 +5731,11 @@ int main(int argc, const char** argv) case VC3_720P_1258: case VC3_1080P_1259: case VC3_1080I_1260: + case VC3_DNXHR_444: + case VC3_DNXHR_HQX: + case VC3_DNXHR_HQ: + case VC3_DNXHR_SQ: + case VC3_DNXHR_LB: if (BMX_OPT_PROP_IS_SET(input->afd)) clip_track->SetAFD(input->afd); break; @@ -5712,6 +5839,14 @@ int main(int argc, const char** argv) if (BMX_OPT_PROP_IS_SET(input->rdd36_opaque)) rdd36_helper->SetIsOpaque(input->rdd36_opaque); } + + VC3MXFDescriptorHelper *vc3_helper = dynamic_cast(pict_helper); + if (vc3_helper) { + if (input->input_width > 0) + vc3_helper->SetFrameWidth(input->input_width); + if (input->input_height > 0) + vc3_helper->SetFrameHeight(input->input_height); + } } else if (sound_helper) { if (BMX_OPT_PROP_IS_SET(output_sound_info->ref_image_edit_rate)) sound_helper->SetReferenceImageEditRate(output_sound_info->ref_image_edit_rate); @@ -5762,6 +5897,11 @@ int main(int argc, const char** argv) case VC3_720P_1258: case VC3_1080P_1259: case VC3_1080I_1260: + case VC3_DNXHR_444: + case VC3_DNXHR_HQX: + case VC3_DNXHR_HQ: + case VC3_DNXHR_SQ: + case VC3_DNXHR_LB: case UNC_SD: case UNC_HD_1080I: case UNC_HD_1080P: diff --git a/include/bmx/EssenceType.h b/include/bmx/EssenceType.h index bddab831..4b55a489 100644 --- a/include/bmx/EssenceType.h +++ b/include/bmx/EssenceType.h @@ -136,6 +136,12 @@ typedef enum VC3_720P_1258, VC3_1080P_1259, VC3_1080I_1260, + // VC-3, DNxHR + VC3_DNXHR_444, // 1270, RGB 4:4:4, 12-bit + VC3_DNXHR_HQX, // 1271, YCbCr 4:2:2, 12-bit + VC3_DNXHR_HQ, // 1272, YCbCr 4:2:2, 8-bit + VC3_DNXHR_SQ, // 1273, YCbCr 4:2:2, 8-bit + VC3_DNXHR_LB, // 1274, YCbCr 4:2:2, 8-bit // Avid MJPEG MJPEG_2_1, MJPEG_3_1, diff --git a/include/bmx/essence_parser/VC3EssenceParser.h b/include/bmx/essence_parser/VC3EssenceParser.h index 676425b0..ca5ff9e4 100644 --- a/include/bmx/essence_parser/VC3EssenceParser.h +++ b/include/bmx/essence_parser/VC3EssenceParser.h @@ -36,9 +36,6 @@ #include -#define VC3_PARSER_MIN_DATA_SIZE 44 - - namespace bmx { diff --git a/include/bmx/mxf_helper/VC3MXFDescriptorHelper.h b/include/bmx/mxf_helper/VC3MXFDescriptorHelper.h index 69c9ee39..741a5bb3 100644 --- a/include/bmx/mxf_helper/VC3MXFDescriptorHelper.h +++ b/include/bmx/mxf_helper/VC3MXFDescriptorHelper.h @@ -61,6 +61,8 @@ class VC3MXFDescriptorHelper : public PictureMXFDescriptorHelper public: // configure and create new descriptor virtual void SetEssenceType(EssenceType essence_type); + virtual void SetFrameWidth(uint32_t frame_width); + virtual void SetFrameHeight(uint32_t frame_height); virtual mxfpp::FileDescriptor* CreateFileDescriptor(mxfpp::HeaderMetadata *header_metadata); virtual void UpdateFileDescriptor(); @@ -73,6 +75,8 @@ class VC3MXFDescriptorHelper : public PictureMXFDescriptorHelper private: size_t mEssenceIndex; + BMX_OPT_PROP_DECL(uint32_t, mFrameWidth); + BMX_OPT_PROP_DECL(uint32_t, mFrameHeight); }; diff --git a/src/avid_mxf/AvidTrack.cpp b/src/avid_mxf/AvidTrack.cpp index 31d8ab89..64146138 100644 --- a/src/avid_mxf/AvidTrack.cpp +++ b/src/avid_mxf/AvidTrack.cpp @@ -142,6 +142,11 @@ static const AvidSampleRateSupport AVID_SAMPLE_RATE_SUPPORT[] = {VC3_720P_1258, {{-1, -1}, {0, 0}}}, {VC3_1080P_1259, {{-1, -1}, {0, 0}}}, {VC3_1080I_1260, {{-1, -1}, {0, 0}}}, + {VC3_DNXHR_444, {{-1, -1}, {0, 0}}}, + {VC3_DNXHR_HQX, {{-1, -1}, {0, 0}}}, + {VC3_DNXHR_HQ, {{-1, -1}, {0, 0}}}, + {VC3_DNXHR_SQ, {{-1, -1}, {0, 0}}}, + {VC3_DNXHR_LB, {{-1, -1}, {0, 0}}}, {UNC_SD, {{25, 1}, {30000, 1001}, {0, 0}}}, {UNC_HD_1080I, {{25, 1}, {30000, 1001}, {0, 0}}}, {UNC_HD_1080P, {{25, 1}, {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, {60, 1}, {0, 0}}}, @@ -253,6 +258,11 @@ AvidTrack* AvidTrack::OpenNew(AvidClip *clip, File *file, uint32_t track_index, case VC3_720P_1258: case VC3_1080P_1259: case VC3_1080I_1260: + case VC3_DNXHR_444: + case VC3_DNXHR_HQX: + case VC3_DNXHR_HQ: + case VC3_DNXHR_SQ: + case VC3_DNXHR_LB: return new AvidVC3Track(clip, track_index, essence_type, file); case UNC_SD: case UNC_HD_1080I: diff --git a/src/common/EssenceType.cpp b/src/common/EssenceType.cpp index 8bb4b591..4fd76918 100644 --- a/src/common/EssenceType.cpp +++ b/src/common/EssenceType.cpp @@ -134,6 +134,11 @@ static const EssenceTypeInfo ESSENCE_TYPE_INFO[] = {VC3_720P_1258, PICTURE_ESSENCE, "VC3 720p 1258", "VC3_720p_1258"}, {VC3_1080P_1259, PICTURE_ESSENCE, "VC3 1080p 1259", "VC3_1080p_1259"}, {VC3_1080I_1260, PICTURE_ESSENCE, "VC3 1080i 1260", "VC3_1080i_1260"}, + {VC3_DNXHR_444, PICTURE_ESSENCE, "VC3 DNxHR 444", "VC3_DNXHR_444"}, + {VC3_DNXHR_HQX, PICTURE_ESSENCE, "VC3 DNxHR HQX", "VC3_DNXHR_HQX"}, + {VC3_DNXHR_HQ, PICTURE_ESSENCE, "VC3 DNxHR HQ", "VC3_DNXHR_HQ"}, + {VC3_DNXHR_SQ, PICTURE_ESSENCE, "VC3 DNxHR SQ", "VC3_DNXHR_SQ"}, + {VC3_DNXHR_LB, PICTURE_ESSENCE, "VC3 DNxHR LB", "VC3_DNXHR_LB"}, {MJPEG_2_1, PICTURE_ESSENCE, "MJPEG 2:1", "MJPEG_2_1"}, {MJPEG_3_1, PICTURE_ESSENCE, "MJPEG 3:1", "MJPEG_3_1"}, {MJPEG_10_1, PICTURE_ESSENCE, "MJPEG 10:1", "MJPEG_10_1"}, diff --git a/src/essence_parser/VC3EssenceParser.cpp b/src/essence_parser/VC3EssenceParser.cpp index ee0af8b0..4d69ad90 100644 --- a/src/essence_parser/VC3EssenceParser.cpp +++ b/src/essence_parser/VC3EssenceParser.cpp @@ -33,6 +33,8 @@ #include "config.h" #endif +#include + #include #include "EssenceParserUtils.h" #include @@ -43,9 +45,9 @@ using namespace std; using namespace bmx; -#define HEADER_PREFIX_HVN0 0x000000028000LL -#define HEADER_PREFIX_S 0x000002800000LL - +#define VC3_MIN_HEADER_SIZE 0x280 +#define VC3_OFFSETS_START 0x168 +#define VARIABLE 0 typedef struct { @@ -55,38 +57,41 @@ typedef struct uint16_t frame_height; uint8_t bit_depth; uint32_t frame_size; + Rational packet_scale; } CompressionParameters; static const CompressionParameters COMPRESSION_PARAMETERS[] = { - {1235, true, 1920, 1080, 10, 917504}, - {1237, true, 1920, 1080, 8, 606208}, - {1238, true, 1920, 1080, 8, 917504}, - {1241, false, 1920, 1080, 10, 917504}, - {1242, false, 1920, 1080, 8, 606208}, - {1243, false, 1920, 1080, 8, 917504}, - {1244, false, 1920, 1080, 8, 606208}, - {1250, true, 1280, 720, 10, 458752}, - {1251, true, 1280, 720, 8, 458752}, - {1252, true, 1280, 720, 8, 303104}, - {1253, true, 1920, 1080, 8, 188416}, - {1258, true, 1280, 720, 8, 212992}, - {1259, true, 1920, 1080, 8, 417792}, - {1260, false, 1920, 1080, 8, 417792}, + {1235, true, 1920, 1080, 10, 917504, ZERO_RATIONAL}, + {1237, true, 1920, 1080, 8, 606208, ZERO_RATIONAL}, + {1238, true, 1920, 1080, 8, 917504, ZERO_RATIONAL}, + {1241, false, 1920, 1080, 10, 917504, ZERO_RATIONAL}, + {1242, false, 1920, 1080, 8, 606208, ZERO_RATIONAL}, + {1243, false, 1920, 1080, 8, 917504, ZERO_RATIONAL}, + {1244, false, 1920, 1080, 8, 606208, ZERO_RATIONAL}, + {1250, true, 1280, 720, 10, 458752, ZERO_RATIONAL}, + {1251, true, 1280, 720, 8, 458752, ZERO_RATIONAL}, + {1252, true, 1280, 720, 8, 303104, ZERO_RATIONAL}, + {1253, true, 1920, 1080, 8, 188416, ZERO_RATIONAL}, + {1258, true, 1280, 720, 8, 212992, ZERO_RATIONAL}, + {1259, true, 1920, 1080, 8, 417792, ZERO_RATIONAL}, + {1260, false, 1920, 1080, 8, 417792, ZERO_RATIONAL}, + {1270, false, VARIABLE, VARIABLE, 12, VARIABLE, {0xe000, 0xff}}, // DNxHR 444 12-bit + {1271, false, VARIABLE, VARIABLE, 12, VARIABLE, {0x7000, 0xff}}, // DNxHR HQX 12-bit + {1272, false, VARIABLE, VARIABLE, 8, VARIABLE, {0x7000, 0xff}}, // DNxHR HQ + {1273, false, VARIABLE, VARIABLE, 8, VARIABLE, {0x4a00, 0xff}}, // DNxHR SQ + {1274, false, VARIABLE, VARIABLE, 8, VARIABLE, {0x1700, 0xff}}, // DNxHR LB }; +static uint32_t get_hr_frame_size(CompressionParameters const& cp, uint32_t w, uint32_t h) +{ + BMX_CHECK(cp.frame_size == VARIABLE); + BMX_CHECK(w > 0 && h > 0); + uint32_t result = ((w + 15) / 16) * ((h + 15) / 16) * cp.packet_scale.numerator / cp.packet_scale.denominator; + result = (result + 2048) & ~0xFFF; -static uint64_t get_uint64(const unsigned char *data) -{ - return (((uint64_t)data[0]) << 56) | - (((uint64_t)data[1]) << 48) | - (((uint64_t)data[2]) << 40) | - (((uint64_t)data[3]) << 32) | - (((uint64_t)data[4]) << 24) | - (((uint64_t)data[5]) << 16) | - (((uint64_t)data[6]) << 8) | - (uint64_t)data[7]; + return max(result, (uint32_t)8192); } static uint32_t get_uint32(const unsigned char *data) @@ -103,6 +108,49 @@ static uint16_t get_uint16(const unsigned char *data) (uint16_t)data[1]; } +static bool vc3_is_header(const unsigned char *data, uint32_t data_size) +{ + if (data_size < VC3_MIN_HEADER_SIZE) return false; + + uint8_t version = data[4]; // 1 or 2 up to full HD, 3 for larger resolutions + uint8_t interlaced = data[5]; // 1, 2 or 3 + + if (version >= 1 && version <= 3 && interlaced >= 1 && interlaced <= 3) + { + uint32_t header_size = get_uint32(data); + uint32_t offsets_size = get_uint32(data + VC3_OFFSETS_START); + uint32_t nb_offsets = get_uint16(data + VC3_OFFSETS_START + 4); + + return (((version < 3 && header_size == VC3_MIN_HEADER_SIZE) + || (version == 3 && header_size >= VC3_MIN_HEADER_SIZE)) + && header_size == VC3_OFFSETS_START + 4 + offsets_size + && offsets_size == nb_offsets * 4 + 4); + } + + return false; +} + +static uint8_t vc3_get_interlaced(const unsigned char *data) +{ + return data[5] & 0x03; +} + +static uint16_t vc3_get_width(const unsigned char *data) +{ + return get_uint16(data + 0x1a); +} + +static uint16_t vc3_get_height(const unsigned char *data) +{ + bool interlaced = vc3_get_interlaced(data) != 1; + return get_uint16(data + 0x18) << interlaced; +} + +static uint32_t vc3_get_compression_id(const unsigned char *data) +{ + return get_uint32(data + 0x28); +} + VC3EssenceParser::VC3EssenceParser() @@ -123,14 +171,11 @@ uint32_t VC3EssenceParser::ParseFrameStart(const unsigned char *data, uint32_t d { BMX_CHECK(data_size != ESSENCE_PARSER_NULL_OFFSET); - uint64_t state = 0; - uint32_t i; - for (i = 0; i < data_size; i++) { - state = (state << 8) | data[i]; - if ((state & 0xffffffff0000LL) == HEADER_PREFIX_S && - (state & 0x000000000003LL) < 3) // coding unit is progressive frame or field 1 + for (uint32_t i = 0; i < data_size; i++) { + if (vc3_is_header(data + i, data_size - i) && + vc3_get_interlaced(data + i) < 3) // coding unit is progressive frame or field 1 { - return i - 5; + return i; } } @@ -145,21 +190,31 @@ uint32_t VC3EssenceParser::ParseFrameSize(const unsigned char *data, uint32_t da { BMX_CHECK(data_size != ESSENCE_PARSER_NULL_OFFSET); - if (data_size < VC3_PARSER_MIN_DATA_SIZE) + if (data_size < VC3_MIN_HEADER_SIZE) return ESSENCE_PARSER_NULL_OFFSET; - // check header prefix - uint64_t prefix = get_uint64(data) >> 24; - BMX_CHECK( (prefix & 0xffffffff00LL) == HEADER_PREFIX_HVN0 && - ((prefix & 0x00000000ffLL) == 1 || (prefix & 0x00000000ffLL) == 2)); + // check header + BMX_CHECK(vc3_is_header(data, data_size)); - uint32_t compression_id = get_uint32(data + 40); + uint32_t compression_id = vc3_get_compression_id(data); - size_t i; - for (i = 0; i < BMX_ARRAY_SIZE(COMPRESSION_PARAMETERS); i++) + for (size_t i = 0; i < BMX_ARRAY_SIZE(COMPRESSION_PARAMETERS); i++) { - if (compression_id == COMPRESSION_PARAMETERS[i].compression_id) - return COMPRESSION_PARAMETERS[i].frame_size; + if (compression_id == COMPRESSION_PARAMETERS[i].compression_id) { + uint32_t frame_size = COMPRESSION_PARAMETERS[i].frame_size; + + if (frame_size == VARIABLE) + { + uint32_t w = vc3_get_width(data); + uint32_t h = vc3_get_height(data); + frame_size = get_hr_frame_size(COMPRESSION_PARAMETERS[i], w, h); + } + + if (data_size >= frame_size) + return frame_size; + else + return ESSENCE_PARSER_NULL_OFFSET; + } } return ESSENCE_PARSER_NULL_FRAME_SIZE; @@ -168,15 +223,11 @@ uint32_t VC3EssenceParser::ParseFrameSize(const unsigned char *data, uint32_t da void VC3EssenceParser::ParseFrameInfo(const unsigned char *data, uint32_t data_size) { BMX_CHECK(data_size != ESSENCE_PARSER_NULL_OFFSET); - BMX_CHECK(data_size >= VC3_PARSER_MIN_DATA_SIZE); - - // check header prefix - uint64_t prefix = get_uint64(data) >> 24; - BMX_CHECK( (prefix & 0xffffffff00LL) == HEADER_PREFIX_HVN0 && - ((prefix & 0x00000000ffLL) == 1 || (prefix & 0x00000000ffLL) == 2)); + BMX_CHECK(data_size >= VC3_MIN_HEADER_SIZE); + BMX_CHECK(vc3_is_header(data, data_size)); // compression id - mCompressionId = get_uint32(data + 40); + mCompressionId = vc3_get_compression_id(data); size_t param_index; for (param_index = 0; param_index < BMX_ARRAY_SIZE(COMPRESSION_PARAMETERS); param_index++) { @@ -185,25 +236,36 @@ void VC3EssenceParser::ParseFrameInfo(const unsigned char *data, uint32_t data_s } BMX_CHECK(param_index < BMX_ARRAY_SIZE(COMPRESSION_PARAMETERS)); + CompressionParameters const& cp = COMPRESSION_PARAMETERS[param_index]; + // Note: found that an Avid MC v3.0 file containing 1252 720p50 had FFC=01h and SST=1; // SST should be 0 for progressive scan. DNxHD_Compliance_Issue_To_Licensees-1.doc // states that some Avid bitstreams may have SST incorrectly set to 1080i // Ignore the bitstream information and use the scan type associated with the compression id - mIsProgressive = COMPRESSION_PARAMETERS[param_index].is_progressive; + mIsProgressive = cp.is_progressive; // image geometry + mFrameWidth = vc3_get_width(data); + mFrameHeight = vc3_get_height(data); + // Note: DNxHD_Compliance_Issue_To_Licensees-1.doc states that some Avid bitstreams, // e.g. produced by Avid Media Composer 3.0, may have ALPF incorrectly set to 1080 for // 1080i sources. Ignore the bitstream information and use the frame height associated // with the compression id - mFrameHeight = COMPRESSION_PARAMETERS[param_index].frame_height; - mFrameWidth = get_uint16(data + 26); - BMX_CHECK(mFrameWidth == COMPRESSION_PARAMETERS[param_index].frame_width); + if (cp.frame_height != VARIABLE) { + mFrameHeight = cp.frame_height; + } + uint32_t sbd_bits = get_bits(data, data_size, 33 * 8, 3); BMX_CHECK(sbd_bits == 2 || sbd_bits == 1); mBitDepth = (sbd_bits == 2 ? 10 : 8); - BMX_CHECK(mBitDepth == COMPRESSION_PARAMETERS[param_index].bit_depth); - mFrameSize = COMPRESSION_PARAMETERS[param_index].frame_size; -} + if (cp.frame_size == VARIABLE) { + mFrameSize = get_hr_frame_size(cp, mFrameWidth, mFrameHeight); + } else { + mFrameSize = cp.frame_size; + } + BMX_CHECK(cp.frame_width == VARIABLE || mFrameWidth == cp.frame_width); + BMX_CHECK(mBitDepth >= 8 && mBitDepth <= cp.bit_depth); +} diff --git a/src/mxf_helper/VC3MXFDescriptorHelper.cpp b/src/mxf_helper/VC3MXFDescriptorHelper.cpp index 55053cd4..dd793344 100644 --- a/src/mxf_helper/VC3MXFDescriptorHelper.cpp +++ b/src/mxf_helper/VC3MXFDescriptorHelper.cpp @@ -33,6 +33,7 @@ #include "config.h" #endif +#include #include #include @@ -46,6 +47,20 @@ using namespace std; using namespace bmx; using namespace mxfpp; +/* + * MXF AVID labels for DNxHR + */ +#define MXF_AVID_BASE_L(b8, b9, b10, b11, b12, b13, b14, b15, b16) \ + {0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, b8, b9, b10, b11, b12, b13, b14, b15, b16} + +#define MXF_AVID_DNXHR_EC_L MXF_AVID_BASE_L(0x0a, 0x0d, 0x01, 0x03, 0x01, 0x02, 0x11, 0x02, 0x00) +#define MXF_DNXHR_CMDEF_L(byte14) MXF_AVID_BASE_L(0x0d, 0x04, 0x01, 0x02, 0x02, 0x71, byte14, 0x00, 0x00) + +static const mxfUL MXF_CMDEF_L(VC3_DNXHR_444) = MXF_DNXHR_CMDEF_L(0x24); +static const mxfUL MXF_CMDEF_L(VC3_DNXHR_HQX) = MXF_DNXHR_CMDEF_L(0x25); +static const mxfUL MXF_CMDEF_L(VC3_DNXHR_HQ) = MXF_DNXHR_CMDEF_L(0x26); +static const mxfUL MXF_CMDEF_L(VC3_DNXHR_SQ) = MXF_DNXHR_CMDEF_L(0x27); +static const mxfUL MXF_CMDEF_L(VC3_DNXHR_LB) = MXF_DNXHR_CMDEF_L(0x28); typedef struct @@ -67,6 +82,8 @@ typedef struct mxfUL avid_ec_label; } SupportedEssence; +enum { VARI = 0 }; + static const SupportedEssence SUPPORTED_ESSENCE[] = { {MXF_CMDEF_L(VC3_1080P_1235), VC3_1080P_1235, 1235, 10, 2, 917504, 1920, 1080, 1920, 1080, {42, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(DNxHD), MXF_EC_L(DNxHD1080p1235ClipWrapped)}, @@ -83,9 +100,45 @@ static const SupportedEssence SUPPORTED_ESSENCE[] = {MXF_CMDEF_L(VC3_720P_1258), VC3_720P_1258, 1258, 8, 2, 212992, 960, 720, 1280, 720, {26, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE296M, MXF_CMDEF_L(DNxHD), MXF_EC_L(DNxHD720p1258ClipWrapped)}, {MXF_CMDEF_L(VC3_1080P_1259), VC3_1080P_1259, 1259, 8, 2, 417792, 1440, 1080, 1920, 1080, {42, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(DNxHD), MXF_EC_L(DNxHD1080p1259ClipWrapped)}, {MXF_CMDEF_L(VC3_1080I_1260), VC3_1080I_1260, 1260, 8, 2, 417792, 1440, 540, 1920, 540, {21, 584}, MXF_SEPARATE_FIELDS, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(DNxHD), MXF_EC_L(DNxHD1080i1260ClipWrapped)}, + {MXF_CMDEF_L(VC3_DNXHR_444), VC3_DNXHR_444, 1270, 12, 1, VARI, VARI, VARI, VARI, VARI, {1, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(VC3_DNXHR_444), MXF_AVID_DNXHR_EC_L}, + {MXF_CMDEF_L(VC3_DNXHR_HQX), VC3_DNXHR_HQX, 1271, 12, 2, VARI, VARI, VARI, VARI, VARI, {1, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(VC3_DNXHR_HQX), MXF_AVID_DNXHR_EC_L}, + {MXF_CMDEF_L(VC3_DNXHR_HQ), VC3_DNXHR_HQ, 1272, 8, 2, VARI, VARI, VARI, VARI, VARI, {1, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(VC3_DNXHR_HQ), MXF_AVID_DNXHR_EC_L}, + {MXF_CMDEF_L(VC3_DNXHR_SQ), VC3_DNXHR_SQ, 1273, 8, 2, VARI, VARI, VARI, VARI, VARI, {1, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(VC3_DNXHR_SQ), MXF_AVID_DNXHR_EC_L}, + {MXF_CMDEF_L(VC3_DNXHR_LB), VC3_DNXHR_LB, 1274, 8, 2, VARI, VARI, VARI, VARI, VARI, {1, 0}, MXF_FULL_FRAME, MXF_SIGNAL_STANDARD_SMPTE274M, MXF_CMDEF_L(VC3_DNXHR_LB), MXF_AVID_DNXHR_EC_L}, +}; + +typedef struct +{ + int32_t resolution_id; + Rational packet_scale; +} CompressionParams; + +static const CompressionParams COMPRESSION_PARAMS[] = +{ + {1270, {0xe000, 0xff}}, // DNxHR 444 12-bit + {1271, {0x7000, 0xff}}, // DNxHR HQX 12-bit + {1272, {0x7000, 0xff}}, // DNxHR HQ + {1273, {0x4a00, 0xff}}, // DNxHR SQ + {1274, {0x1700, 0xff}}, // DNxHR LB }; +static uint32_t get_hr_frame_size(int32_t resolution_id, uint32_t w, uint32_t h) +{ + size_t param_index; + for (param_index = 0; param_index < BMX_ARRAY_SIZE(COMPRESSION_PARAMS); param_index++) + { + if (COMPRESSION_PARAMS[param_index].resolution_id == resolution_id) + break; + } + BMX_CHECK(param_index < BMX_ARRAY_SIZE(COMPRESSION_PARAMS)); + + Rational packet_scale = COMPRESSION_PARAMS[param_index].packet_scale; + uint32_t result = ((w + 15) / 16) * ((h + 15) / 16) * packet_scale.numerator / packet_scale.denominator; + result = (result + 2048) & ~0xFFF; + return max(result, (uint32_t)8192); +} + EssenceType VC3MXFDescriptorHelper::IsSupported(FileDescriptor *file_descriptor, mxfUL alternative_ec_label) { @@ -152,6 +205,8 @@ VC3MXFDescriptorHelper::VC3MXFDescriptorHelper() { mEssenceIndex = 0; mEssenceType = SUPPORTED_ESSENCE[0].essence_type; + BMX_OPT_PROP_DEFAULT(mFrameWidth, 0); + BMX_OPT_PROP_DEFAULT(mFrameHeight, 0); } VC3MXFDescriptorHelper::~VC3MXFDescriptorHelper() @@ -185,6 +240,15 @@ void VC3MXFDescriptorHelper::Initialize(FileDescriptor *file_descriptor, uint16_ } } } + + if (SUPPORTED_ESSENCE[mEssenceIndex].stored_width == VARI) { + GenericPictureEssenceDescriptor *picture_descriptor = dynamic_cast(file_descriptor); + BMX_ASSERT(picture_descriptor); + BMX_CHECK(picture_descriptor->haveDisplayWidth() && picture_descriptor->haveDisplayHeight()); + + BMX_OPT_PROP_SET(mFrameWidth, picture_descriptor->getDisplayWidth()); + BMX_OPT_PROP_SET(mFrameHeight, picture_descriptor->getDisplayHeight()); + } } void VC3MXFDescriptorHelper::SetEssenceType(EssenceType essence_type) @@ -195,7 +259,11 @@ void VC3MXFDescriptorHelper::SetEssenceType(EssenceType essence_type) for (i = 0; i < BMX_ARRAY_SIZE(SUPPORTED_ESSENCE); i++) { if (SUPPORTED_ESSENCE[i].essence_type == essence_type) { mEssenceIndex = i; - mAvidResolutionId = SUPPORTED_ESSENCE[i].resolution_id; + // DNxHR: do NOT write Avid ResolutionID (golden doesn't have it) + if (SUPPORTED_ESSENCE[i].frame_size == VARI) + mAvidResolutionId = 0; + else + mAvidResolutionId = SUPPORTED_ESSENCE[i].resolution_id; break; } } @@ -204,6 +272,16 @@ void VC3MXFDescriptorHelper::SetEssenceType(EssenceType essence_type) PictureMXFDescriptorHelper::SetEssenceType(essence_type); } +void VC3MXFDescriptorHelper::SetFrameWidth(uint32_t frame_width) +{ + BMX_OPT_PROP_SET(mFrameWidth, frame_width); +} + +void VC3MXFDescriptorHelper::SetFrameHeight(uint32_t frame_height) +{ + BMX_OPT_PROP_SET(mFrameHeight, frame_height); +} + FileDescriptor* VC3MXFDescriptorHelper::CreateFileDescriptor(mxfpp::HeaderMetadata *header_metadata) { mFileDescriptor = new CDCIEssenceDescriptor(header_metadata); @@ -218,15 +296,33 @@ void VC3MXFDescriptorHelper::UpdateFileDescriptor() CDCIEssenceDescriptor *cdci_descriptor = dynamic_cast(mFileDescriptor); BMX_ASSERT(cdci_descriptor); + // DNxHR: override the generic AAF-KLV EC label with the actual DNxHR EC label + // (parent sets AvidAAFKLVEssenceContainer for all Avid flavour, but DNxHR needs its own) + if ((mFlavour & MXFDESC_AVID_FLAVOUR) && SUPPORTED_ESSENCE[mEssenceIndex].frame_size == VARI) { + mFileDescriptor->setEssenceContainer(SUPPORTED_ESSENCE[mEssenceIndex].avid_ec_label); + } + if ((mFlavour & MXFDESC_AVID_FLAVOUR)) cdci_descriptor->setPictureEssenceCoding(SUPPORTED_ESSENCE[mEssenceIndex].avid_pc_label); else cdci_descriptor->setPictureEssenceCoding(SUPPORTED_ESSENCE[mEssenceIndex].pc_label); - cdci_descriptor->setSignalStandard(SUPPORTED_ESSENCE[mEssenceIndex].signal_standard); + // DNxHR: do NOT write SignalStandard (golden and Resolve don't have it) + if (SUPPORTED_ESSENCE[mEssenceIndex].frame_size != VARI) + cdci_descriptor->setSignalStandard(SUPPORTED_ESSENCE[mEssenceIndex].signal_standard); cdci_descriptor->setFrameLayout(SUPPORTED_ESSENCE[mEssenceIndex].frame_layout); SetColorSitingMod(MXF_COLOR_SITING_REC601); cdci_descriptor->setComponentDepth(SUPPORTED_ESSENCE[mEssenceIndex].component_depth); - if (SUPPORTED_ESSENCE[mEssenceIndex].component_depth == 10) { + if (SUPPORTED_ESSENCE[mEssenceIndex].component_depth == 12) { + // DNxHR 12-bit + cdci_descriptor->setBlackRefLevel(64); + cdci_descriptor->setWhiteReflevel(940); + cdci_descriptor->setColorRange(897); + } else if (SUPPORTED_ESSENCE[mEssenceIndex].frame_size == VARI) { + // DNxHR 8-bit (LB/SQ/HQ) — Avid uses non-standard levels + cdci_descriptor->setBlackRefLevel(23); + cdci_descriptor->setWhiteReflevel(149); + cdci_descriptor->setColorRange(225); + } else if (SUPPORTED_ESSENCE[mEssenceIndex].component_depth == 10) { cdci_descriptor->setBlackRefLevel(64); cdci_descriptor->setWhiteReflevel(940); cdci_descriptor->setColorRange(897); @@ -235,11 +331,29 @@ void VC3MXFDescriptorHelper::UpdateFileDescriptor() cdci_descriptor->setWhiteReflevel(235); cdci_descriptor->setColorRange(225); } - SetCodingEquationsMod(ITUR_BT709_CODING_EQ); - cdci_descriptor->setStoredWidth(SUPPORTED_ESSENCE[mEssenceIndex].stored_width); - cdci_descriptor->setStoredHeight(SUPPORTED_ESSENCE[mEssenceIndex].stored_height); - cdci_descriptor->setDisplayWidth(SUPPORTED_ESSENCE[mEssenceIndex].display_width); - cdci_descriptor->setDisplayHeight(SUPPORTED_ESSENCE[mEssenceIndex].display_height); + if (SUPPORTED_ESSENCE[mEssenceIndex].frame_size == VARI) { + // DNxHR: write CodingEquations as straight UL, not AUID half-swapped + // (Avid DNxHR uses standard UL format, unlike DNxHD which uses AAF AUID) + GenericPictureEssenceDescriptor *pic_desc = dynamic_cast(mFileDescriptor); + pic_desc->setCodingEquations(ITUR_BT709_CODING_EQ); + // DNxHR: add TransferCharacteristic and ColorPrimaries (both working files have them) + pic_desc->setCaptureGamma(ITUR_BT709_TRANSFER_CH); + pic_desc->setColorPrimaries(ITU709_COLOR_PRIM); + } else { + SetCodingEquationsMod(ITUR_BT709_CODING_EQ); + } + if (SUPPORTED_ESSENCE[mEssenceIndex].stored_width == VARI) { + BMX_CHECK(BMX_OPT_PROP_IS_SET(mFrameWidth) && BMX_OPT_PROP_IS_SET(mFrameHeight)); + cdci_descriptor->setStoredWidth(mFrameWidth); + cdci_descriptor->setStoredHeight(mFrameHeight); + cdci_descriptor->setDisplayWidth(mFrameWidth); + cdci_descriptor->setDisplayHeight(mFrameHeight); + } else { + cdci_descriptor->setStoredWidth(SUPPORTED_ESSENCE[mEssenceIndex].stored_width); + cdci_descriptor->setStoredHeight(SUPPORTED_ESSENCE[mEssenceIndex].stored_height); + cdci_descriptor->setDisplayWidth(SUPPORTED_ESSENCE[mEssenceIndex].display_width); + cdci_descriptor->setDisplayHeight(SUPPORTED_ESSENCE[mEssenceIndex].display_height); + } cdci_descriptor->setSampledWidth(cdci_descriptor->getDisplayWidth()); cdci_descriptor->setSampledHeight(cdci_descriptor->getDisplayHeight()); if ((mFlavour & MXFDESC_AVID_FLAVOUR)) { @@ -251,13 +365,32 @@ void VC3MXFDescriptorHelper::UpdateFileDescriptor() cdci_descriptor->setVideoLineMap(SUPPORTED_ESSENCE[mEssenceIndex].video_line_map); cdci_descriptor->setHorizontalSubsampling(SUPPORTED_ESSENCE[mEssenceIndex].horiz_subsampling); cdci_descriptor->setVerticalSubsampling(1); - if ((mFlavour & MXFDESC_AVID_FLAVOUR)) - cdci_descriptor->setImageAlignmentOffset(8192); + if ((mFlavour & MXFDESC_AVID_FLAVOUR)) { + if (SUPPORTED_ESSENCE[mEssenceIndex].frame_size == VARI) { + cdci_descriptor->setImageAlignmentOffset(4096); // Avid aligns DNxHR to 4096 bytes + // DNxHR: set aspect ratio based on frame dimensions + if (BMX_OPT_PROP_IS_SET(mFrameWidth) && BMX_OPT_PROP_IS_SET(mFrameHeight)) { + mxfRational aspect_ratio; + aspect_ratio.numerator = 256; + aspect_ratio.denominator = 135; + cdci_descriptor->setAspectRatio(aspect_ratio); + } + } else { + cdci_descriptor->setImageAlignmentOffset(8192); + } + } } uint32_t VC3MXFDescriptorHelper::GetSampleSize() { - return SUPPORTED_ESSENCE[mEssenceIndex].frame_size; + uint32_t frame_size = SUPPORTED_ESSENCE[mEssenceIndex].frame_size; + + if (frame_size == VARI) { + BMX_CHECK(BMX_OPT_PROP_IS_SET(mFrameWidth) && BMX_OPT_PROP_IS_SET(mFrameHeight)); + frame_size = get_hr_frame_size(SUPPORTED_ESSENCE[mEssenceIndex].resolution_id, mFrameWidth, mFrameHeight); + } + + return frame_size; } mxfUL VC3MXFDescriptorHelper::ChooseEssenceContainerUL() const @@ -272,4 +405,3 @@ mxfUL VC3MXFDescriptorHelper::ChooseEssenceContainerUL() const return MXF_EC_L(VC3ClipWrapped); } } -