[FFmpeg-devel] [PATCH 2/2] [avformat/mxf] Add support for ST 2084 / ST 2067-21 mastering metadata.
Steven Robertson
steven at strobe.cc
Sat Sep 24 08:19:04 EEST 2016
Signed-off-by: Steven Robertson <steven at strobe.cc>
---
libavformat/mxfdec.c | 132 ++++++++++++++++++++++++++++++++++++++++-----------
libavformat/mxfenc.c | 43 ++++++++++++++++-
2 files changed, 145 insertions(+), 30 deletions(-)
diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c
index d012e41..761aa1f 100644
--- a/libavformat/mxfdec.c
+++ b/libavformat/mxfdec.c
@@ -52,6 +52,7 @@
#include "libavutil/intreadwrite.h"
#include "libavutil/parseutils.h"
#include "libavutil/timecode.h"
+#include "libavutil/mastering_display_metadata.h"
#include "avformat.h"
#include "internal.h"
#include "mxf.h"
@@ -191,6 +192,10 @@ typedef struct MXFDescriptor {
UID color_trc_ul;
UID color_primaries_ul;
UID color_space_ul;
+ uint16_t mastering_primaries[3][2];
+ uint16_t mastering_white_point[2];
+ uint32_t mastering_max_luminance;
+ uint32_t mastering_min_luminance;
} MXFDescriptor;
typedef struct MXFIndexTableSegment {
@@ -284,22 +289,26 @@ typedef struct MXFMetadataReadTableEntry {
static int mxf_read_close(AVFormatContext *s);
/* partial keys to match */
-static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 };
-static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 };
-static const uint8_t mxf_avid_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0e,0x04,0x03,0x01 };
-static const uint8_t mxf_canopus_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x0a,0x0e,0x0f,0x03,0x01 };
-static const uint8_t mxf_system_item_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x03,0x01,0x04 };
-static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 };
+static const uint8_t mxf_header_partition_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 };
+static const uint8_t mxf_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 };
+static const uint8_t mxf_avid_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0e,0x04,0x03,0x01 };
+static const uint8_t mxf_canopus_essence_element_key[] = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x0a,0x0e,0x0f,0x03,0x01 };
+static const uint8_t mxf_system_item_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x03,0x01,0x04 };
+static const uint8_t mxf_klv_key[] = { 0x06,0x0e,0x2b,0x34 };
/* complete keys to match */
-static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 };
-static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
-static const uint8_t mxf_encrypted_essence_container[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 };
-static const uint8_t mxf_random_index_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x11,0x01,0x00 };
-static const uint8_t mxf_sony_mpeg4_extradata[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0e,0x06,0x06,0x02,0x02,0x01,0x00,0x00 };
-static const uint8_t mxf_avid_project_name[] = { 0xa5,0xfb,0x7b,0x25,0xf6,0x15,0x94,0xb9,0x62,0xfc,0x37,0x17,0x49,0x2d,0x42,0xbf };
-static const uint8_t mxf_jp2k_rsiz[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 };
-static const uint8_t mxf_indirect_value_utf16le[] = { 0x4c,0x00,0x02,0x10,0x01,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
-static const uint8_t mxf_indirect_value_utf16be[] = { 0x42,0x01,0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
+static const uint8_t mxf_crypto_source_container_ul[] = { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x02,0x02,0x00,0x00,0x00 };
+static const uint8_t mxf_encrypted_triplet_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
+static const uint8_t mxf_encrypted_essence_container[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 };
+static const uint8_t mxf_random_index_pack_key[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x11,0x01,0x00 };
+static const uint8_t mxf_sony_mpeg4_extradata[] = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0e,0x06,0x06,0x02,0x02,0x01,0x00,0x00 };
+static const uint8_t mxf_avid_project_name[] = { 0xa5,0xfb,0x7b,0x25,0xf6,0x15,0x94,0xb9,0x62,0xfc,0x37,0x17,0x49,0x2d,0x42,0xbf };
+static const uint8_t mxf_jp2k_rsiz[] = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02,0x01,0x00 };
+static const uint8_t mxf_indirect_value_utf16le[] = { 0x4c,0x00,0x02,0x10,0x01,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
+static const uint8_t mxf_indirect_value_utf16be[] = { 0x42,0x01,0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x2b,0x34,0x01,0x04,0x01,0x01 };
+static const uint8_t mxf_mastering_display_primaries_ul[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x01,0x00,0x00 };
+static const uint8_t mxf_mastering_display_white_point_ul[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x02,0x00,0x00 };
+static const uint8_t mxf_mastering_display_max_luminance_ul[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x03,0x00,0x00 };
+static const uint8_t mxf_mastering_display_min_luminance_ul[] = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x04,0x00,0x00 };
#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
@@ -968,6 +977,20 @@ static void mxf_read_pixel_layout(AVIOContext *pb, MXFDescriptor *descriptor)
ff_mxf_decode_pixel_layout(layout, &descriptor->pix_fmt);
}
+/*
+ * Match an uid independently of the version byte and up to len common bytes
+ * Returns: boolean
+ */
+static int mxf_match_uid(const UID key, const UID uid, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (i != 7 && key[i] != uid[i])
+ return 0;
+ }
+ return 1;
+}
+
static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
{
MXFDescriptor *descriptor = arg;
@@ -1059,6 +1082,24 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int
rsiz == FF_PROFILE_JPEG2000_DCINEMA_4K)
descriptor->pix_fmt = AV_PIX_FMT_XYZ12;
}
+ if (mxf_match_uid(uid, mxf_mastering_display_primaries_ul, 16)) {
+ descriptor->mastering_primaries[0][0] = avio_rb16(pb);
+ descriptor->mastering_primaries[0][1] = avio_rb16(pb);
+ descriptor->mastering_primaries[1][0] = avio_rb16(pb);
+ descriptor->mastering_primaries[1][1] = avio_rb16(pb);
+ descriptor->mastering_primaries[2][0] = avio_rb16(pb);
+ descriptor->mastering_primaries[2][1] = avio_rb16(pb);
+ }
+ if (mxf_match_uid(uid, mxf_mastering_display_white_point_ul, 16)) {
+ descriptor->mastering_white_point[0] = avio_rb16(pb);
+ descriptor->mastering_white_point[1] = avio_rb16(pb);
+ }
+ if (mxf_match_uid(uid, mxf_mastering_display_max_luminance_ul, 16)) {
+ descriptor->mastering_max_luminance = avio_rb32(pb);
+ }
+ if (mxf_match_uid(uid, mxf_mastering_display_min_luminance_ul, 16)) {
+ descriptor->mastering_min_luminance = avio_rb32(pb);
+ }
break;
}
return 0;
@@ -1094,20 +1135,6 @@ static int mxf_read_tagged_value(void *arg, AVIOContext *pb, int tag, int size,
return 0;
}
-/*
- * Match an uid independently of the version byte and up to len common bytes
- * Returns: boolean
- */
-static int mxf_match_uid(const UID key, const UID uid, int len)
-{
- int i;
- for (i = 0; i < len; i++) {
- if (i != 7 && key[i] != uid[i])
- return 0;
- }
- return 1;
-}
-
static const MXFCodecUL *mxf_get_codec_ul(const MXFCodecUL *uls, UID *uid)
{
while (uls->uid[0]) {
@@ -1868,6 +1895,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
const MXFCodecUL *color_trc_ul = NULL;
const MXFCodecUL *color_primaries_ul = NULL;
const MXFCodecUL *color_space_ul = NULL;
+ int has_mastering_primaries;
+ int has_mastering_luminance;
AVStream *st;
AVTimecode tc;
int flags;
@@ -2114,6 +2143,51 @@ static int mxf_parse_structural_metadata(MXFContext *mxf)
if (color_trc_ul->uid[0])
st->codecpar->color_trc = color_trc_ul->id;
+ has_mastering_primaries =
+ descriptor->mastering_primaries[0][0] > 0 && descriptor->mastering_primaries[0][1] > 0 &&
+ descriptor->mastering_primaries[1][0] > 0 && descriptor->mastering_primaries[1][1] > 0 &&
+ descriptor->mastering_primaries[2][0] > 0 && descriptor->mastering_primaries[2][1] > 0 &&
+ descriptor->mastering_white_point[0] > 0 && descriptor->mastering_white_point[1] > 0;
+ has_mastering_luminance = descriptor->mastering_max_luminance > 0;
+
+ if (has_mastering_primaries || has_mastering_luminance) {
+ AVMasteringDisplayMetadata *metadata =
+ (AVMasteringDisplayMetadata*) av_stream_new_side_data(
+ st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ sizeof(AVMasteringDisplayMetadata));
+ if (!metadata)
+ return AVERROR(ENOMEM);
+ memset(metadata, 0, sizeof(AVMasteringDisplayMetadata));
+ if (has_mastering_primaries) {
+ const int chroma_den = 50000;
+ metadata->display_primaries[0][0] = av_make_q(
+ descriptor->mastering_primaries[0][0], chroma_den);
+ metadata->display_primaries[0][1] = av_make_q(
+ descriptor->mastering_primaries[0][1], chroma_den);
+ metadata->display_primaries[1][0] = av_make_q(
+ descriptor->mastering_primaries[1][0], chroma_den);
+ metadata->display_primaries[1][1] = av_make_q(
+ descriptor->mastering_primaries[1][1], chroma_den);
+ metadata->display_primaries[2][0] = av_make_q(
+ descriptor->mastering_primaries[2][0], chroma_den);
+ metadata->display_primaries[2][1] = av_make_q(
+ descriptor->mastering_primaries[2][1], chroma_den);
+ metadata->white_point[0] = av_make_q(
+ descriptor->mastering_white_point[0], chroma_den);
+ metadata->white_point[1] = av_make_q(
+ descriptor->mastering_white_point[1], chroma_den);
+ metadata->has_primaries = 1;
+ }
+ if (has_mastering_luminance) {
+ const int luma_den = 10000;
+ metadata->max_luminance = av_make_q(
+ descriptor->mastering_max_luminance, luma_den);
+ metadata->min_luminance = av_make_q(
+ descriptor->mastering_min_luminance, luma_den);
+ metadata->has_luminance = 1;
+ }
+ }
+
st->need_parsing = AVSTREAM_PARSE_HEADERS;
if (material_track->sequence->origin) {
av_dict_set_int(&st->metadata, "material_track_origin", material_track->sequence->origin, 0);
diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index eb77f3a..8222b58 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -34,6 +34,7 @@
* SMPTE 422M Mapping JPEG 2000 Codestreams into the MXF Generic Container
* SMPTE RP210: SMPTE Metadata Dictionary
* SMPTE RP224: Registry of SMPTE Universal Labels
+ * SMPTE 2067-21: Interoperable Master Format - Application #2E
*/
#include <inttypes.h>
@@ -46,6 +47,7 @@
#include "libavutil/avassert.h"
#include "libavutil/pixdesc.h"
#include "libavutil/time_internal.h"
+#include "libavutil/mastering_display_metadata.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/dnxhddata.h"
#include "libavcodec/h264.h"
@@ -448,6 +450,11 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
// Wave Audio Essence Descriptor
{ 0x3D09, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x03,0x05,0x00,0x00,0x00}}, /* Average Bytes Per Second */
{ 0x3D0A, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x02,0x01,0x00,0x00,0x00}}, /* Block Align */
+ // Dynamic Tags
+ { 0xFF01, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x01,0x00,0x00}}, /* Mastering Display Primaries */
+ { 0xFF02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x02,0x00,0x00}}, /* Mastering Display White Point Chromaticity */
+ { 0xFF03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x03,0x00,0x00}}, /* Mastering Display Maximum Luminance */
+ { 0xFF04, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x20,0x04,0x01,0x01,0x04,0x00,0x00}}, /* Mastering Display Minimum Luminance */
};
static const MXFLocalTagPair mxf_user_comments_local_tag[] = {
@@ -1018,7 +1025,9 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
const MXFCodecUL* color_space_ul;
int stored_height = (st->codecpar->height+15)/16*16;
int display_height;
- int f1, f2;
+ int f1, f2, side_data_size;
+ const uint8_t *side_data;
+ const AVMasteringDisplayMetadata *mastering_metadata = NULL;
unsigned desc_size = size+8+8+8+8+8+8+8+5+16+4+12+20+5;
if (sc->interlaced && sc->field_dominance)
desc_size += 5;
@@ -1035,6 +1044,16 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
if (color_space_ul->uid[0])
desc_size += 20;
+ side_data = av_stream_get_side_data(
+ st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, &side_data_size);
+ if (side_data_size == sizeof(AVMasteringDisplayMetadata)) {
+ mastering_metadata = (const AVMasteringDisplayMetadata*)side_data;
+ if (mastering_metadata->has_primaries)
+ desc_size += 16 + 8;
+ if (mastering_metadata->has_luminance)
+ desc_size += 8 + 8;
+ }
+
mxf_write_generic_desc(s, st, key, desc_size);
mxf_write_local_tag(pb, 4, 0x3203);
@@ -1127,6 +1146,28 @@ static void mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID ke
mxf_write_local_tag(pb, 16, 0x321A);
avio_write(pb, color_space_ul->uid, 16);
}
+ if (mastering_metadata) {
+ if (mastering_metadata->has_primaries) {
+ AVRational chroma_factor = av_make_q(50000, 1);
+ mxf_write_local_tag(pb, 12, 0xFF01);
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->display_primaries[0][0], chroma_factor)));
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->display_primaries[0][1], chroma_factor)));
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->display_primaries[1][0], chroma_factor)));
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->display_primaries[1][1], chroma_factor)));
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->display_primaries[2][0], chroma_factor)));
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->display_primaries[2][1], chroma_factor)));
+ mxf_write_local_tag(pb, 4, 0xFF02);
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->white_point[0], chroma_factor)));
+ avio_wb16(pb, (uint16_t) av_q2d(av_mul_q(mastering_metadata->white_point[1], chroma_factor)));
+ }
+ if (mastering_metadata->has_luminance) {
+ AVRational luma_factor = av_make_q(10000, 1);
+ mxf_write_local_tag(pb, 4, 0xFF03);
+ avio_wb32(pb, (uint32_t) av_q2d(av_mul_q(mastering_metadata->max_luminance, luma_factor)));
+ mxf_write_local_tag(pb, 4, 0xFF04);
+ avio_wb32(pb, (uint32_t) av_q2d(av_mul_q(mastering_metadata->min_luminance, luma_factor)));
+ }
+ }
}
static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
--
2.8.0.rc3.226.g39d4020
More information about the ffmpeg-devel
mailing list