videoencoder: pass upstream HDR information through codec state
authorJakub Adam <jakub.adam@collabora.com>
Tue, 25 May 2021 19:16:48 +0000 (21:16 +0200)
committerNicolas Dufresne <nicolas@ndufresne.ca>
Tue, 17 Aug 2021 14:54:06 +0000 (14:54 +0000)
Don't copy HDR metadata from sink pad, because its caps may not have
been set yet if GstVideoEncoder::negotiate is called from
GstVideoEncoder::set_format, as e.g. vpx encoder does.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/1175>

gst-libs/gst/video/gstvideoencoder.c
tests/check/libs/videoencoder.c

index 0d85ba2329a8a7c59f70fb9dcce82a0707ed752e..ae859a139cd3e00cf651c35993bc49d0588e498d 100644 (file)
@@ -674,6 +674,15 @@ _new_output_state (GstCaps * caps, GstVideoCodecState * reference)
 
     GST_VIDEO_INFO_MULTIVIEW_MODE (tgt) = GST_VIDEO_INFO_MULTIVIEW_MODE (ref);
     GST_VIDEO_INFO_MULTIVIEW_FLAGS (tgt) = GST_VIDEO_INFO_MULTIVIEW_FLAGS (ref);
+
+    if (reference->mastering_display_info) {
+      state->mastering_display_info = g_slice_dup (GstVideoMasteringDisplayInfo,
+          reference->mastering_display_info);
+    }
+    if (reference->content_light_level) {
+      state->content_light_level = g_slice_dup (GstVideoContentLightLevel,
+          reference->content_light_level);
+    }
   }
 
   return state;
@@ -683,6 +692,8 @@ static GstVideoCodecState *
 _new_input_state (GstCaps * caps)
 {
   GstVideoCodecState *state;
+  GstStructure *c_struct;
+  const gchar *s;
 
   state = g_slice_new0 (GstVideoCodecState);
   state->ref_count = 1;
@@ -691,6 +702,18 @@ _new_input_state (GstCaps * caps)
     goto parse_fail;
   state->caps = gst_caps_ref (caps);
 
+  c_struct = gst_caps_get_structure (caps, 0);
+
+  if ((s = gst_structure_get_string (c_struct, "mastering-display-info"))) {
+    state->mastering_display_info = g_slice_new (GstVideoMasteringDisplayInfo);
+    gst_video_mastering_display_info_from_string (state->mastering_display_info,
+        s);
+  }
+  if ((s = gst_structure_get_string (c_struct, "content-light-level"))) {
+    state->content_light_level = g_slice_new (GstVideoContentLightLevel);
+    gst_video_content_light_level_from_string (state->content_light_level, s);
+  }
+
   return state;
 
 parse_fail:
@@ -1815,7 +1838,7 @@ gst_video_encoder_negotiate_default (GstVideoEncoder * encoder)
   g_return_val_if_fail (state->caps != NULL, FALSE);
 
   if (encoder->priv->output_state_changed) {
-    GstCaps *incaps;
+    GstStructure *out_struct;
 
     state->caps = gst_caps_make_writable (state->caps);
 
@@ -1874,30 +1897,20 @@ gst_video_encoder_negotiate_default (GstVideoEncoder * encoder)
           GST_VIDEO_INFO_MULTIVIEW_FLAGS (info), GST_FLAG_SET_MASK_EXACT, NULL);
     }
 
-    incaps = gst_pad_get_current_caps (GST_VIDEO_ENCODER_SINK_PAD (encoder));
-    if (incaps) {
-      GstStructure *in_struct;
-      GstStructure *out_struct;
-      const gchar *s;
-
-      in_struct = gst_caps_get_structure (incaps, 0);
-      out_struct = gst_caps_get_structure (state->caps, 0);
-
-      /* forward upstream mastering display info and content light level
-       * if subclass didn't set */
-      if ((s = gst_structure_get_string (in_struct, "mastering-display-info"))
-          && !gst_structure_has_field (out_struct, "mastering-display-info")) {
-        gst_caps_set_simple (state->caps, "mastering-display-info",
-            G_TYPE_STRING, s, NULL);
-      }
+    out_struct = gst_caps_get_structure (state->caps, 0);
 
-      if ((s = gst_structure_get_string (in_struct, "content-light-level")) &&
-          !gst_structure_has_field (out_struct, "content-light-level")) {
-        gst_caps_set_simple (state->caps,
-            "content-light-level", G_TYPE_STRING, s, NULL);
-      }
+    /* forward upstream mastering display info and content light level
+     * if subclass didn't set */
+    if (state->mastering_display_info &&
+        !gst_structure_has_field (out_struct, "mastering-display-info")) {
+      gst_video_mastering_display_info_add_to_caps
+          (state->mastering_display_info, state->caps);
+    }
 
-      gst_caps_unref (incaps);
+    if (state->content_light_level &&
+        !gst_structure_has_field (out_struct, "content-light-level")) {
+      gst_video_content_light_level_add_to_caps (state->content_light_level,
+          state->caps);
     }
 
     encoder->priv->output_state_changed = FALSE;
index a481fc24b9225f3cd69538abf2910263fc6f0d07..ee13b0fb387766c5a757d3148bf39f098928ec90 100644 (file)
@@ -54,6 +54,7 @@ struct _GstVideoEncoderTester
   gboolean send_headers;
   gboolean key_frame_sent;
   gboolean enable_step_by_step;
+  gboolean negotiate_in_set_format;
   GstVideoCodecFrame *last_frame;
 };
 
@@ -83,12 +84,19 @@ static gboolean
 gst_video_encoder_tester_set_format (GstVideoEncoder * enc,
     GstVideoCodecState * state)
 {
+  GstVideoEncoderTester *enc_tester = GST_VIDEO_ENCODER_TESTER (enc);
+
   GstVideoCodecState *res = gst_video_encoder_set_output_state (enc,
       gst_caps_new_simple ("video/x-test-custom", "width", G_TYPE_INT,
           480, "height", G_TYPE_INT, 360, NULL),
-      NULL);
+      state);
 
   gst_video_codec_state_unref (res);
+
+  if (enc_tester->negotiate_in_set_format) {
+    gst_video_encoder_negotiate (enc);
+  }
+
   return TRUE;
 }
 
@@ -1194,6 +1202,67 @@ GST_START_TEST (videoencoder_force_keyunit_min_interval)
 
 GST_END_TEST;
 
+GST_START_TEST (videoencoder_hdr_metadata)
+{
+  const gchar *mdi_str =
+      "35399:14599:8500:39850:6550:2300:15634:16450:10000000:1";
+  const gchar *cll_str = "1000:50";
+  gint i;
+
+  /* Check that HDR metadata get passed to src pad no matter if negotiate gets
+   * called from gst_video_encoder_finish_frame() or GstVideoEncoder::set_format
+   */
+  for (i = 1; i >= 0; --i) {
+    GstVideoMasteringDisplayInfo mdi;
+    GstVideoContentLightLevel cll;
+    GstSegment segment;
+    GstCaps *caps;
+    GstStructure *s;
+    const gchar *str;
+
+    setup_videoencodertester ();
+    GST_VIDEO_ENCODER_TESTER (enc)->negotiate_in_set_format = i;
+
+    gst_pad_set_active (mysrcpad, TRUE);
+    gst_element_set_state (enc, GST_STATE_PLAYING);
+    gst_pad_set_active (mysinkpad, TRUE);
+
+    fail_unless (gst_pad_push_event (mysrcpad,
+            gst_event_new_stream_start ("id")));
+
+    gst_video_mastering_display_info_from_string (&mdi, mdi_str);
+    gst_video_content_light_level_from_string (&cll, cll_str);
+
+    caps = create_test_caps ();
+    gst_video_mastering_display_info_add_to_caps (&mdi, caps);
+    gst_video_content_light_level_add_to_caps (&cll, caps);
+
+    fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps)));
+    gst_caps_unref (caps);
+
+    gst_segment_init (&segment, GST_FORMAT_TIME);
+    fail_unless (gst_pad_push_event (mysrcpad,
+            gst_event_new_segment (&segment)));
+
+    gst_pad_push (mysrcpad, create_test_buffer (0));
+
+    caps = gst_pad_get_current_caps (mysinkpad);
+
+    s = gst_caps_get_structure (caps, 0);
+    fail_unless (str = gst_structure_get_string (s, "mastering-display-info"));
+    fail_unless_equals_string (str, mdi_str);
+
+    fail_unless (str = gst_structure_get_string (s, "content-light-level"));
+    fail_unless_equals_string (str, cll_str);
+
+    gst_caps_unref (caps);
+
+    cleanup_videoencodertest ();
+  }
+}
+
+GST_END_TEST;
+
 static Suite *
 gst_videoencoder_suite (void)
 {
@@ -1212,6 +1281,7 @@ gst_videoencoder_suite (void)
   tcase_add_test (tc, videoencoder_playback_events_subframes);
   tcase_add_test (tc, videoencoder_force_keyunit_handling);
   tcase_add_test (tc, videoencoder_force_keyunit_min_interval);
+  tcase_add_test (tc, videoencoder_hdr_metadata);
 
   return s;
 }