diff mbox series

[FFmpeg-devel,v1] avcodec: add hdr sei support for hevc_nvenc

Message ID 20201229035649.5612-1-kloblic@gmail.com
State New
Headers show
Series [FFmpeg-devel,v1] avcodec: add hdr sei support for hevc_nvenc | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

klobliu Dec. 29, 2020, 3:56 a.m. UTC
From: klobliu <klobliu@tencent.com>

Signed-off-by: klobliu <kloblic@gmail.com>
---
 libavcodec/cbs.c               |  1 +
 libavcodec/cbs_h2645.c         | 60 +++++++++++++++++++++++++++++++++++++++
 libavcodec/cbs_h265.h          |  2 ++
 libavcodec/h265_metadata_bsf.c | 64 +++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 126 insertions(+), 1 deletion(-)

Comments

Lynne Dec. 29, 2020, 2:44 p.m. UTC | #1
Dec 29, 2020, 04:56 by kloblic@gmail.com:

> From: klobliu <klobliu@tencent.com>
>
> Signed-off-by: klobliu <kloblic@gmail.com>
> ---
>  libavcodec/cbs.c               |  1 +
>  libavcodec/cbs_h2645.c         | 60 +++++++++++++++++++++++++++++++++++++++
>  libavcodec/cbs_h265.h          |  2 ++
>  libavcodec/h265_metadata_bsf.c | 64 +++++++++++++++++++++++++++++++++++++++++-
>  4 files changed, 126 insertions(+), 1 deletion(-)
>

One of us is working on exactly this at the moment. We want to
use the same syntax for every codec, as well as have a generic
CLI option to automatically insert a bsf if needed.
So this is kind of redundant.
diff mbox series

Patch

diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index f98531e131..b3db4749bf 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -26,6 +26,7 @@ 
 
 #include "cbs.h"
 #include "cbs_internal.h"
+#include "cbs_h265.h"
 
 
 static const CodedBitstreamType *cbs_type_table[] = {
diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c
index 434322492c..ca55c4f298 100644
--- a/libavcodec/cbs_h2645.c
+++ b/libavcodec/cbs_h2645.c
@@ -1538,6 +1538,66 @@  const CodedBitstreamType ff_cbs_type_h265 = {
     .close             = &cbs_h265_close,
 };
 
+int ff_cbs_h265_add_sei_message(CodedBitstreamFragment *au,
+  H265RawSEIPayload *payload)
+{
+  H265RawSEI *sei = NULL;
+  int err, i, k;
+
+  // Use single SEI NAL unit to store HDR info.
+  for (i = 0; i < au->nb_units; i++) {
+    if (au->units[i].type == HEVC_NAL_SEI_PREFIX || au->units[i].type == HEVC_NAL_SEI_SUFFIX) {
+      H265RawSEI * raw_sei = (H265RawSEI *)au->units[i].content;
+      if (raw_sei) {
+        for (k = 0; k < raw_sei->payload_count; k++) {
+          if (raw_sei->payload[k].payload_type == payload->payload_type) {
+            sei = au->units[i].content;
+            if (sei->payload_count < H265_MAX_SEI_PAYLOADS)
+              break;
+            sei = NULL;
+          }
+        }
+      }
+    }
+  }
+
+  if (!sei) {
+    AVBufferRef *sei_ref;
+
+    sei = av_mallocz(sizeof(*sei));
+    if (!sei) {
+      err = AVERROR(ENOMEM);
+      goto fail;
+    }
+
+    sei->nal_unit_header.nal_unit_type = HEVC_NAL_SEI_PREFIX;
+    sei->nal_unit_header.nuh_layer_id = 0;
+    sei->nal_unit_header.nuh_temporal_id_plus1 = 1;
+    sei_ref = av_buffer_create((uint8_t*)sei, sizeof(*sei),
+      &cbs_h265_free_sei, NULL, 0);
+    if (!sei_ref) {
+      av_freep(&sei);
+      err = AVERROR(ENOMEM);
+      goto fail;
+    }
+
+    err = ff_cbs_insert_unit_content(au, i, HEVC_NAL_SEI_PREFIX,
+      sei, sei_ref);
+    av_buffer_unref(&sei_ref);
+    if (err < 0)
+      goto fail;
+  }
+
+  memcpy(&sei->payload[sei->payload_count], payload, sizeof(*payload));
+  ++sei->payload_count;
+
+  return 0;
+fail:
+  cbs_h265_free_sei_payload(payload);
+  return err;
+}
+
+
 int ff_cbs_h264_add_sei_message(CodedBitstreamFragment *au,
                                 H264RawSEIPayload *payload)
 {
diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h
index 15b22bbfd4..cd3824c7fa 100644
--- a/libavcodec/cbs_h265.h
+++ b/libavcodec/cbs_h265.h
@@ -24,6 +24,7 @@ 
 
 #include "cbs_h2645.h"
 #include "hevc.h"
+#include "cbs.h"
 
 enum {
     // This limit is arbitrary - it is sufficient for one message of each
@@ -746,5 +747,6 @@  typedef struct CodedBitstreamH265Context {
     const H265RawPPS *active_pps;
 } CodedBitstreamH265Context;
 
+int ff_cbs_h265_add_sei_message(CodedBitstreamFragment *au, H265RawSEIPayload *payload);
 
 #endif /* AVCODEC_CBS_H265_H */
diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/h265_metadata_bsf.c
index 504a75dac2..91e5df05d2 100644
--- a/libavcodec/h265_metadata_bsf.c
+++ b/libavcodec/h265_metadata_bsf.c
@@ -25,6 +25,7 @@ 
 #include "cbs_h265.h"
 #include "hevc.h"
 #include "h265_profile_level.h"
+#include "hevc_sei.h"
 
 enum {
     PASS,
@@ -70,6 +71,11 @@  typedef struct H265MetadataContext {
     int level;
     int level_guess;
     int level_warned;
+
+    char * max_cll;
+    char * mastering_display_color_volume;
+    int done_first_au;
+
 } H265MetadataContext;
 
 
@@ -393,7 +399,7 @@  static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
 {
     H265MetadataContext *ctx = bsf->priv_data;
     CodedBitstreamFragment *au = &ctx->access_unit;
-    int err, i;
+    int err, i, has_sps;
 
     err = ff_bsf_get_packet_ref(bsf, pkt);
     if (err < 0)
@@ -471,15 +477,64 @@  static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
             err = h265_metadata_update_sps(bsf, au->units[i].content);
             if (err < 0)
                 goto fail;
+            has_sps = 1;
         }
     }
 
+     if (has_sps || !ctx->done_first_au) {
+      if (ctx->max_cll) {
+        H265RawSEIPayload payload = {
+             .payload_type = HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO,
+        };
+        payload.payload_size = 4;
+
+        if (sscanf(ctx->max_cll, "%hu|%hu",
+          &payload.payload.content_light_level.max_content_light_level,
+          &payload.payload.content_light_level.max_pic_average_light_level) == 2) {
+          err = ff_cbs_h265_add_sei_message(au, &payload);
+          if (err < 0) {
+            av_log(bsf, AV_LOG_ERROR, "Failed to add HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO SEI "
+              "message to access unit.\n");
+            goto fail;
+          }
+        }
+      }
+
+      if (ctx->mastering_display_color_volume) {
+        H265RawSEIPayload payload = {
+            .payload_type = HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO,
+        };
+        payload.payload_size = 24;
+
+        if (sscanf(ctx->mastering_display_color_volume,
+          "G(%hu|%hu)B(%hu|%hu)R(%hu|%hu)WP(%hu|%hu)L(%u|%u)",
+          &payload.payload.mastering_display.display_primaries_x[0],
+          &payload.payload.mastering_display.display_primaries_y[0],
+          &payload.payload.mastering_display.display_primaries_x[1],
+          &payload.payload.mastering_display.display_primaries_y[1],
+          &payload.payload.mastering_display.display_primaries_x[2],
+          &payload.payload.mastering_display.display_primaries_y[2],
+          &payload.payload.mastering_display.white_point_x,
+          &payload.payload.mastering_display.white_point_y,
+          &payload.payload.mastering_display.max_display_mastering_luminance,
+          &payload.payload.mastering_display.min_display_mastering_luminance) == 10) {
+          err = ff_cbs_h265_add_sei_message(au, &payload);
+          if (err < 0) {
+            av_log(bsf, AV_LOG_ERROR, "Failed to add HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO SEI "
+              "message to access unit.\n");
+            goto fail;
+          }
+        }
+      }
+    }
+
     err = ff_cbs_write_packet(ctx->output, pkt, au);
     if (err < 0) {
         av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n");
         goto fail;
     }
 
+    ctx->done_first_au = 1;
     err = 0;
 fail:
     ff_cbs_fragment_reset(au);
@@ -607,6 +662,13 @@  static const AVOption h265_metadata_options[] = {
         OFFSET(crop_bottom), AV_OPT_TYPE_INT,
         { .i64 = -1 }, -1, HEVC_MAX_HEIGHT, FLAGS },
 
+    { "max_cll", "Content light level for hdr. example 1000|800",
+        OFFSET(max_cll), AV_OPT_TYPE_STRING,
+        { .str = NULL }, .flags = FLAGS },
+    { "master_display", "Mastering display color volume for hdr.",
+        OFFSET(mastering_display_color_volume), AV_OPT_TYPE_STRING,
+        { .str = NULL }, .flags = FLAGS },
+
     { "level", "Set level (tables A.6 and A.7)",
         OFFSET(level), AV_OPT_TYPE_INT,
         { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" },