rtph264pay: Don't insert SPS/PPS before the second image slice

Only the first slice, for which fist_mb_in_slice is set to 0,
should trigger insertion of SPS and PPS buffers.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3402>
This commit is contained in:
Patricia Muscalu 2022-11-14 10:49:02 +01:00 committed by GStreamer Marge Bot
parent 4067bbdd91
commit c3e52d5c4f
2 changed files with 86 additions and 2 deletions

View file

@ -924,6 +924,7 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload,
{ {
GstRtpH264Pay *rtph264pay; GstRtpH264Pay *rtph264pay;
guint8 nal_header, nal_type; guint8 nal_header, nal_type;
gboolean first_slice = FALSE;
gboolean send_spspps; gboolean send_spspps;
guint size; guint size;
@ -960,8 +961,17 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload,
send_spspps = FALSE; send_spspps = FALSE;
if (nal_type == IDR_TYPE_ID) {
guint8 first_mb_in_slice;
gst_buffer_extract (paybuf, 1, &first_mb_in_slice, 1);
/* 'first_mb_in_slice' specifies the address of the first macroblock
* in the slice. if 'first_mb_in_slice' is 0 (note that it's exp golomb
* code), the current slice is the first slice of the frame */
first_slice = ((first_mb_in_slice >> 7) & 0x01) == 1;
}
/* check if we need to emit an SPS/PPS now */ /* check if we need to emit an SPS/PPS now */
if (nal_type == IDR_TYPE_ID && rtph264pay->spspps_interval > 0) { if (first_slice && nal_type == IDR_TYPE_ID && rtph264pay->spspps_interval > 0) {
if (rtph264pay->last_spspps != -1) { if (rtph264pay->last_spspps != -1) {
guint64 diff; guint64 diff;
GstClockTime running_time = GstClockTime running_time =
@ -993,7 +1003,8 @@ gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload,
GST_DEBUG_OBJECT (rtph264pay, "no previous SPS/PPS time, send now"); GST_DEBUG_OBJECT (rtph264pay, "no previous SPS/PPS time, send now");
send_spspps = TRUE; send_spspps = TRUE;
} }
} else if (nal_type == IDR_TYPE_ID && rtph264pay->spspps_interval == -1) { } else if (first_slice && nal_type == IDR_TYPE_ID
&& rtph264pay->spspps_interval == -1) {
GST_DEBUG_OBJECT (rtph264pay, "sending SPS/PPS before current IDR frame"); GST_DEBUG_OBJECT (rtph264pay, "sending SPS/PPS before current IDR frame");
/* send SPS/PPS before every IDR frame */ /* send SPS/PPS before every IDR frame */
send_spspps = TRUE; send_spspps = TRUE;

View file

@ -1455,6 +1455,77 @@ GST_START_TEST (test_rtph264pay_avc_incomplete_nal)
GST_END_TEST; GST_END_TEST;
/* A buffer consists of two memory chunks: a primary slice with
* fist_mb_in_slice set to 0 and a secondary one. Only the first slice
* should trigger generation of SPS and PPS buffers */
GST_START_TEST (test_rtph264pay_avc_two_slices_per_buffer_config_interval)
{
GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123"
" name=p config-interval=-1");
GstFlowReturn ret;
GstBuffer *slice1;
GstBuffer *slice2;
GstBuffer *buffer;
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
guint8 *rest_of_image;
gsize rest_of_slice_1_size;
gsize rest_of_image_size;
guint num_buffers;
guint8 *payload = NULL;
guint8 nal_type;
gst_harness_set_src_caps_str (h,
"video/x-h264,alignment=au,stream-format=avc,"
"codec_data=(buffer)01f4000dffe1001c67f4000d919b2884d80b50606064000003"
"000400000300f23c50a65801000668ebec448440");
/* first slice */
slice1 = wrap_static_buffer (h264_idr_slice_1_avc, 1);
rest_of_slice_1_size = sizeof (h264_idr_slice_1_avc) - 1;
rest_of_image_size = rest_of_slice_1_size + sizeof (h264_idr_slice_2_avc);
rest_of_image = g_malloc (rest_of_image_size);
memcpy (rest_of_image, h264_idr_slice_1_avc + 1, rest_of_slice_1_size);
memcpy (rest_of_image + rest_of_slice_1_size, h264_idr_slice_2_avc,
sizeof (h264_idr_slice_2_avc));
/* second slice */
slice2 =
wrap_static_buffer_full (rest_of_image, rest_of_image_size,
rest_of_image, g_free);
buffer = gst_buffer_append (slice1, slice2);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
/* SPS PPS IDR (Slice1) IDR (Slice2) */
num_buffers = gst_harness_buffers_in_queue (h);
fail_unless_equals_int (num_buffers, 4);
for (guint i = 0; i < num_buffers; i++) {
buffer = gst_harness_pull (h);
fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
payload = gst_rtp_buffer_get_payload (&rtp);
gst_rtp_buffer_unmap (&rtp);
nal_type = (GST_READ_UINT8 (payload)) & 0x1f;
GST_INFO ("nal_type=%d", nal_type);
if (i == 0) {
fail_unless_equals_int (nal_type, 7);
} else if (i == 1) {
fail_unless_equals_int (nal_type, 8);
} else if (i == 2) {
fail_unless_equals_int (nal_type, 5);
} else if (i == 3) {
fail_unless_equals_int (nal_type, 5);
}
gst_buffer_unref (buffer);
}
gst_harness_teardown (h);
}
GST_END_TEST;
static Suite * static Suite *
rtph264_suite (void) rtph264_suite (void)
{ {
@ -1486,6 +1557,8 @@ rtph264_suite (void)
tcase_add_test (tc_chain, test_rtph264pay_avc); tcase_add_test (tc_chain, test_rtph264pay_avc);
tcase_add_test (tc_chain, test_rtph264pay_avc_two_slices_per_buffer); tcase_add_test (tc_chain, test_rtph264pay_avc_two_slices_per_buffer);
tcase_add_test (tc_chain, test_rtph264pay_avc_incomplete_nal); tcase_add_test (tc_chain, test_rtph264pay_avc_incomplete_nal);
tcase_add_test (tc_chain,
test_rtph264pay_avc_two_slices_per_buffer_config_interval);
return s; return s;
} }