From e83d6ca87c7da2d6a9ff09dcec339cadc054610f Mon Sep 17 00:00:00 2001
From: Neil Birkbeck <neil.birkbeck@gmail.com>
Date: Thu, 3 Nov 2016 17:13:03 -0700
Subject: [PATCH] lavf/matroskaenc.c: use metadata key/value to set mastering
 metadata

Add key/value metadata interface to allow command line setting of AVMasteringDisplayMetadata. The formatting is the same as the option in libx265.

Signed-off-by: Neil Birkbeck <neil.birkbeck@gmail.com>
---
 libavformat/matroskaenc.c | 49 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 46 insertions(+), 3 deletions(-)

diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 2cfeee9..4287171 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -804,17 +804,61 @@ static int mkv_write_codecprivate(AVFormatContext *s, AVIOContext *pb,
 }
 
 static int mkv_write_video_color(AVIOContext *pb, AVCodecParameters *par, AVStream *st) {
+    AVDictionaryEntry *tag;
     AVIOContext *dyn_cp;
     uint8_t *colorinfo_ptr;
     int side_data_size = 0;
     int ret, colorinfo_size;
     const uint8_t *side_data = av_stream_get_side_data(
         st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, &side_data_size);
+    AVMasteringDisplayMetadata *metadata =
+        (side_data_size == sizeof(AVMasteringDisplayMetadata)) ?
+        (AVMasteringDisplayMetadata*)side_data : NULL;
 
     ret = avio_open_dyn_buf(&dyn_cp);
     if (ret < 0)
         return ret;
 
+    // If key-value pair metadata is specified, override the packet side data
+    // Accept a format similar to the command line argument in x265:
+    //    G(x,y)B(x,y)R(x,y)WP(x,y),L(max,min)
+    if (tag = av_dict_get(st->metadata, "master_display", NULL, 0)) {
+        int primaries[4][2];
+        int luma[2];
+        int num_read = sscanf(tag->value,
+                              "G(%d,%d)B(%d,%d)R(%d,%d)WP(%d,%d)L(%d,%d)",
+                              primaries[1], primaries[1] + 1,
+                              primaries[2], primaries[2] + 1,
+                              primaries[0], primaries[0] + 1,
+                              primaries[3], primaries[3] + 1,
+                              luma + 1, luma + 0);
+        if (num_read >= 8) {
+            const float chroma_denom = 50000.f;
+            const float luma_denom = 10000.f;
+            int i, j;
+            if (!metadata) {
+                metadata = (AVMasteringDisplayMetadata*) av_stream_new_side_data(
+                    st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+                    sizeof(AVMasteringDisplayMetadata));
+                memset(metadata, 0, sizeof(AVMasteringDisplayMetadata));
+            }
+            for (i = 0; i < 3; ++i) {
+                for (j = 0; j < 2; ++j) {
+                    metadata->display_primaries[i][j] =
+                        av_make_q(primaries[i][j], chroma_denom);
+                }
+            }
+            metadata->white_point[0] = av_make_q(primaries[3][0], chroma_denom);
+            metadata->white_point[1] = av_make_q(primaries[3][1], chroma_denom);
+            metadata->has_primaries = 1;
+            if (num_read == 10) {
+                metadata->min_luminance = av_make_q(luma[0], luma_denom);
+                metadata->max_luminance = av_make_q(luma[1], luma_denom);
+                metadata->has_luminance = 1;
+            }
+        }
+    }
+
     if (par->color_trc != AVCOL_TRC_UNSPECIFIED &&
         par->color_trc < AVCOL_TRC_NB) {
         put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORTRANSFERCHARACTERISTICS,
@@ -840,11 +884,9 @@ static int mkv_write_video_color(AVIOContext *pb, AVCodecParameters *par, AVStre
         put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORCHROMASITINGHORZ, (xpos >> 7) + 1);
         put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT, (ypos >> 7) + 1);
     }
-    if (side_data_size == sizeof(AVMasteringDisplayMetadata)) {
+    if (metadata) {
         ebml_master meta_element = start_ebml_master(
             dyn_cp, MATROSKA_ID_VIDEOCOLORMASTERINGMETA, 0);
-        const AVMasteringDisplayMetadata *metadata =
-            (const AVMasteringDisplayMetadata*)side_data;
         if (metadata->has_primaries) {
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RX,
                            av_q2d(metadata->display_primaries[0][0]));
@@ -1425,6 +1467,7 @@ static int mkv_check_tag_name(const char *name, unsigned int elementid)
 {
     return av_strcasecmp(name, "title") &&
            av_strcasecmp(name, "stereo_mode") &&
+           av_strcasecmp(name, "master_display") &&
            av_strcasecmp(name, "creation_time") &&
            av_strcasecmp(name, "encoding_tool") &&
            av_strcasecmp(name, "duration") &&
-- 
2.8.0.rc3.226.g39d4020

