[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