[FFmpeg-cvslog] avcodec: support for CUVA HDR Vivid metadata

Limin Wang git at videolan.org
Tue Mar 1 03:26:07 EET 2022


ffmpeg | branch: master | Limin Wang <lance.lmwang at gmail.com> | Mon Feb 14 23:17:11 2022 +0800| [5cd3c83a86ea65144c3f59cf07bad4e36ee511ba] | committer: Limin Wang

avcodec: support for CUVA HDR Vivid metadata

Signed-off-by: Limin Wang <lance.lmwang at gmail.com>

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=5cd3c83a86ea65144c3f59cf07bad4e36ee511ba
---

 libavcodec/Makefile            |   2 +-
 libavcodec/dynamic_hdr_vivid.c | 139 +++++++++++++++++++++++++++++++++++++++++
 libavcodec/dynamic_hdr_vivid.h |  35 +++++++++++
 libavcodec/hevc_sei.c          |  48 +++++++++++++-
 libavcodec/hevc_sei.h          |   5 ++
 libavcodec/hevcdec.c           |  15 +++++
 6 files changed, 241 insertions(+), 3 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 6076b4ad80..24effa807b 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -96,7 +96,7 @@ OBJS-$(CONFIG_H264PARSE)               += h264_parse.o h2645_parse.o h264_ps.o
 OBJS-$(CONFIG_H264PRED)                += h264pred.o
 OBJS-$(CONFIG_H264QPEL)                += h264qpel.o
 OBJS-$(CONFIG_HEVCPARSE)               += hevc_parse.o h2645_parse.o hevc_ps.o hevc_sei.o hevc_data.o \
-                                          dynamic_hdr10_plus.o
+                                          dynamic_hdr10_plus.o dynamic_hdr_vivid.o
 OBJS-$(CONFIG_HPELDSP)                 += hpeldsp.o
 OBJS-$(CONFIG_HUFFMAN)                 += huffman.o
 OBJS-$(CONFIG_HUFFYUVDSP)              += huffyuvdsp.o
diff --git a/libavcodec/dynamic_hdr_vivid.c b/libavcodec/dynamic_hdr_vivid.c
new file mode 100644
index 0000000000..d689669dec
--- /dev/null
+++ b/libavcodec/dynamic_hdr_vivid.c
@@ -0,0 +1,139 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "dynamic_hdr_vivid.h"
+#include "get_bits.h"
+
+static const int32_t maxrgb_den = 4095;
+static const int32_t color_saturation_gain_den = 128;
+static const int32_t maximum_luminance_den = 4095;
+static const int32_t base_param_m_p_den = 16383;
+static const int32_t base_param_m_m_den = 10;
+static const int32_t base_param_m_a_den = 1023;
+static const int32_t base_param_m_b_den = 1023;
+static const int32_t base_param_m_n_den = 10;
+static const int32_t base_param_Delta_den = 127;
+
+int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t *data,
+                                             int size)
+{
+    GetBitContext gbc, *gb = &gbc;
+    int ret;
+
+    if (!s)
+        return AVERROR(ENOMEM);
+
+    ret = init_get_bits8(gb, data, size);
+    if (ret < 0)
+        return ret;
+
+    if (get_bits_left(gb) < 8)
+        return AVERROR_INVALIDDATA;
+
+    s->system_start_code = get_bits(gb, 8);
+    if (s->system_start_code == 0x01) {
+        s->num_windows = 1;
+
+        if (get_bits_left(gb) < 12 * 4 * s->num_windows)
+            return AVERROR_INVALIDDATA;
+        for (int w = 0; w < s->num_windows; w++) {
+            AVHDRVividColorTransformParams *params = &s->params[w];
+
+            params->minimum_maxrgb  = (AVRational){get_bits(gb, 12), maxrgb_den};
+            params->average_maxrgb  = (AVRational){get_bits(gb, 12), maxrgb_den};
+            params->variance_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den};
+            params->maximum_maxrgb  = (AVRational){get_bits(gb, 12), maxrgb_den};
+        }
+
+        if (get_bits_left(gb) < 2 * s->num_windows)
+            return AVERROR_INVALIDDATA;
+        for (int w = 0; w < s->num_windows; w++) {
+            AVHDRVividColorTransformParams *params = &s->params[w];
+
+            params->tone_mapping_mode_flag = get_bits(gb, 1);
+            if (params->tone_mapping_mode_flag) {
+                if (get_bits_left(gb) < 1 )
+                    return AVERROR_INVALIDDATA;
+                params->tone_mapping_param_num = get_bits(gb, 1) + 1;
+                for (int i = 0; i < params->tone_mapping_param_num; i++) {
+                    AVHDRVividColorToneMappingParams *tm_params = &params->tm_params[i];
+
+                    if (get_bits_left(gb) < 13)
+                        return AVERROR_INVALIDDATA;
+                    tm_params->targeted_system_display_maximum_luminance = (AVRational){get_bits(gb, 12), maximum_luminance_den};
+                    tm_params->base_enable_flag = get_bits(gb, 1);
+                    if (tm_params->base_enable_flag) {
+                        if (get_bits_left(gb) < (14 + 6 + 10 + 10 + 6 + 8 + 10))
+                            return AVERROR_INVALIDDATA;
+                        tm_params->base_param_m_p = (AVRational){get_bits(gb, 14), base_param_m_p_den};
+                        tm_params->base_param_m_m = (AVRational){get_bits(gb,  6), base_param_m_m_den};
+                        tm_params->base_param_m_a = (AVRational){get_bits(gb, 10), base_param_m_a_den};
+                        tm_params->base_param_m_b = (AVRational){get_bits(gb, 10), base_param_m_b_den};
+                        tm_params->base_param_m_n = (AVRational){get_bits(gb,  6), base_param_m_n_den};
+                        tm_params->base_param_k1 = get_bits(gb, 2);
+                        tm_params->base_param_k2 = get_bits(gb, 2);
+                        tm_params->base_param_k3 = get_bits(gb, 4);
+                        tm_params->base_param_Delta_enable_mode = get_bits(gb, 3);
+                        if (tm_params->base_param_Delta_enable_mode == 2 || tm_params->base_param_Delta_enable_mode == 6)
+                            tm_params->base_param_Delta = (AVRational){get_bits(gb, 7) * -1, base_param_Delta_den};
+                        else
+                            tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den};
+
+                        if (get_bits_left(gb) < 1)
+                            return AVERROR_INVALIDDATA;
+                        tm_params->three_Spline_enable_flag = get_bits(gb, 1);
+                        if (tm_params->three_Spline_enable_flag) {
+                            if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1))
+                                return AVERROR_INVALIDDATA;
+                            tm_params->three_Spline_num = get_bits(gb, 1) + 1;
+                            for (int j = 0; j < tm_params->three_Spline_num; j++) {
+                                tm_params->three_Spline_TH_mode = get_bits(gb, 2);
+                                if (tm_params->three_Spline_TH_mode == 0 || tm_params->three_Spline_TH_mode == 2) {
+                                    if (get_bits_left(gb) < 8)
+                                        return AVERROR_INVALIDDATA;
+                                    tm_params->three_Spline_TH_enable_MB = (AVRational){get_bits(gb, 8),  255};
+                                }
+                                tm_params->three_Spline_TH_enable = (AVRational){get_bits(gb, 12),  4095};
+                                tm_params->three_Spline_TH_Delta1 = (AVRational){get_bits(gb, 10),  1023};
+                                tm_params->three_Spline_TH_Delta2 = (AVRational){get_bits(gb, 10),  1023};
+                                tm_params->three_Spline_enable_Strength = (AVRational){get_bits(gb,  8),  255};
+                            }
+                        } else {
+                            tm_params->three_Spline_num     = 1;
+                            tm_params->three_Spline_TH_mode = 0;
+                        }
+
+                    }
+                }
+            }
+
+            params->color_saturation_mapping_flag = get_bits(gb, 1);
+            if (params->color_saturation_mapping_flag) {
+                if (get_bits_left(gb) < 3 + params->color_saturation_num * 8)
+                    return AVERROR_INVALIDDATA;
+
+                params->color_saturation_num = get_bits(gb, 3);
+                for (int i = 0; i < params->color_saturation_num; i++) {
+                    params->color_saturation_gain[i] = (AVRational){get_bits(gb, 8), color_saturation_gain_den};
+                }
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/libavcodec/dynamic_hdr_vivid.h b/libavcodec/dynamic_hdr_vivid.h
new file mode 100644
index 0000000000..d521b3d263
--- /dev/null
+++ b/libavcodec/dynamic_hdr_vivid.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_DYNAMIC_HDR_VIVID_H
+#define AVCODEC_DYNAMIC_HDR_VIVID_H
+
+#include "libavutil/hdr_dynamic_vivid_metadata.h"
+
+/**
+ * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRVivid).
+ * @param s A pointer containing the decoded AVDynamicHDRVivid structure.
+ * @param data The byte array containing the raw ITU-T T.35 data.
+ * @param size Size of the data array in bytes.
+ *
+ * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
+ */
+int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t *data,
+                                             int size);
+
+#endif /* AVCODEC_DYNAMIC_HDR_VIVID_H */
diff --git a/libavcodec/hevc_sei.c b/libavcodec/hevc_sei.c
index 7fd822652e..ec3036f932 100644
--- a/libavcodec/hevc_sei.c
+++ b/libavcodec/hevc_sei.c
@@ -24,6 +24,7 @@
 
 #include "atsc_a53.h"
 #include "dynamic_hdr10_plus.h"
+#include "dynamic_hdr_vivid.h"
 #include "golomb.h"
 #include "hevc_ps.h"
 #include "hevc_sei.h"
@@ -245,6 +246,34 @@ static int decode_registered_user_data_dynamic_hdr_plus(HEVCSEIDynamicHDRPlus *s
     return 0;
 }
 
+static int decode_registered_user_data_dynamic_hdr_vivid(HEVCSEIDynamicHDRVivid *s,
+                                                        GetBitContext *gb, int size)
+{
+    size_t meta_size;
+    int err;
+    AVDynamicHDRVivid *metadata = av_dynamic_hdr_vivid_alloc(&meta_size);
+    if (!metadata)
+        return AVERROR(ENOMEM);
+
+    err = ff_parse_itu_t_t35_to_dynamic_hdr_vivid(metadata,
+                                                  gb->buffer + get_bits_count(gb) / 8, size);
+    if (err < 0) {
+        av_free(metadata);
+        return err;
+    }
+
+    av_buffer_unref(&s->info);
+    s->info = av_buffer_create((uint8_t *)metadata, meta_size, NULL, NULL, 0);
+    if (!s->info) {
+        av_free(metadata);
+        return AVERROR(ENOMEM);
+    }
+
+    skip_bits_long(gb, size * 8);
+
+    return 0;
+}
+
 static int decode_nal_sei_user_data_registered_itu_t_t35(HEVCSEI *s, GetBitContext *gb,
                                                          void *logctx, int size)
 {
@@ -263,9 +292,9 @@ static int decode_nal_sei_user_data_registered_itu_t_t35(HEVCSEI *s, GetBitConte
         size--;
     }
 
-    if (country_code != 0xB5) { // usa_country_code
+    if (country_code != 0xB5 && country_code != 0x26) { // usa_country_code and cn_country_code
         av_log(logctx, AV_LOG_VERBOSE,
-               "Unsupported User Data Registered ITU-T T35 SEI message (country_code = %d)\n",
+               "Unsupported User Data Registered ITU-T T35 SEI message (country_code = 0x%x)\n",
                country_code);
         goto end;
     }
@@ -273,6 +302,20 @@ static int decode_nal_sei_user_data_registered_itu_t_t35(HEVCSEI *s, GetBitConte
     provider_code = get_bits(gb, 16);
 
     switch (provider_code) {
+    case 0x04: { // cuva_provider_code
+        const uint16_t cuva_provider_oriented_code = 0x0005;
+        uint16_t provider_oriented_code;
+
+        if (size < 2)
+            return AVERROR_INVALIDDATA;
+        size -= 2;
+
+        provider_oriented_code = get_bits(gb, 16);
+        if (provider_oriented_code == cuva_provider_oriented_code) {
+            return decode_registered_user_data_dynamic_hdr_vivid(&s->dynamic_hdr_vivid, gb, size);
+        }
+        break;
+    }
     case 0x3C: { // smpte_provider_code
         // A/341 Amendment - 2094-40
         const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
@@ -559,4 +602,5 @@ void ff_hevc_reset_sei(HEVCSEI *s)
     s->unregistered.nb_buf_ref = 0;
     av_freep(&s->unregistered.buf_ref);
     av_buffer_unref(&s->dynamic_hdr_plus.info);
+    av_buffer_unref(&s->dynamic_hdr_vivid.info);
 }
diff --git a/libavcodec/hevc_sei.h b/libavcodec/hevc_sei.h
index ac8c61c1f1..f198402333 100644
--- a/libavcodec/hevc_sei.h
+++ b/libavcodec/hevc_sei.h
@@ -78,6 +78,10 @@ typedef struct HEVCSEIDynamicHDRPlus {
     AVBufferRef *info;
 } HEVCSEIDynamicHDRPlus;
 
+typedef struct HEVCSEIDynamicHDRVivid {
+    AVBufferRef *info;
+} HEVCSEIDynamicHDRVivid;
+
 typedef struct HEVCSEIContentLight {
     int present;
     uint16_t max_content_light_level;
@@ -139,6 +143,7 @@ typedef struct HEVCSEI {
     HEVCSEIUnregistered unregistered;
     HEVCSEIMasteringDisplay mastering_display;
     HEVCSEIDynamicHDRPlus dynamic_hdr_plus;
+    HEVCSEIDynamicHDRVivid dynamic_hdr_vivid;
     HEVCSEIContentLight content_light;
     int active_seq_parameter_set_id;
     HEVCSEIAlternativeTransfer alternative_transfer;
diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
index 1d4ad42278..068750ddb6 100644
--- a/libavcodec/hevcdec.c
+++ b/libavcodec/hevcdec.c
@@ -2984,6 +2984,17 @@ static int set_side_data(HEVCContext *s)
     if ((ret = ff_dovi_attach_side_data(&s->dovi_ctx, out)) < 0)
         return ret;
 
+    if (s->sei.dynamic_hdr_vivid.info) {
+        AVBufferRef *info_ref = av_buffer_ref(s->sei.dynamic_hdr_vivid.info);
+        if (!info_ref)
+            return AVERROR(ENOMEM);
+
+        if (!av_frame_new_side_data_from_buf(out, AV_FRAME_DATA_DYNAMIC_HDR_VIVID, info_ref)) {
+            av_buffer_unref(&info_ref);
+            return AVERROR(ENOMEM);
+        }
+    }
+
     return 0;
 }
 
@@ -3777,6 +3788,10 @@ static int hevc_update_thread_context(AVCodecContext *dst,
     if (ret < 0)
         return ret;
 
+    ret = av_buffer_replace(&s->sei.dynamic_hdr_vivid.info, s0->sei.dynamic_hdr_vivid.info);
+    if (ret < 0)
+        return ret;
+
     s->sei.frame_packing        = s0->sei.frame_packing;
     s->sei.display_orientation  = s0->sei.display_orientation;
     s->sei.mastering_display    = s0->sei.mastering_display;



More information about the ffmpeg-cvslog mailing list