From b4a00f78bc438b9a2b0121cc47ae7b6817ea3060 Mon Sep 17 00:00:00 2001 From: Jakub Adam Date: Tue, 25 May 2021 21:16:48 +0200 Subject: videoencoder: pass upstream HDR information through codec state 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: --- gst-libs/gst/video/gstvideoencoder.c | 59 +++++++++++++++++------------ tests/check/libs/videoencoder.c | 72 +++++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 24 deletions(-) diff --git a/gst-libs/gst/video/gstvideoencoder.c b/gst-libs/gst/video/gstvideoencoder.c index 0d85ba232..ae859a139 100644 --- a/gst-libs/gst/video/gstvideoencoder.c +++ b/gst-libs/gst/video/gstvideoencoder.c @@ -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; diff --git a/tests/check/libs/videoencoder.c b/tests/check/libs/videoencoder.c index a481fc24b..ee13b0fb3 100644 --- a/tests/check/libs/videoencoder.c +++ b/tests/check/libs/videoencoder.c @@ -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; } -- cgit v1.2.1