rtpvp9pay: fix profile parsing

Incorrect parsing of these bits meant that we were incorrectly parsing
the VP9 uncompressed bitstream header for some profiles, as the header
is of variable length and format depending on the profile. Amongst
various unintended effects, this caused the width and height from the SS
to be incorrectly parsed and set in the caps.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8275>
This commit is contained in:
Will Miller 2025-01-09 17:12:54 +00:00
parent bc285dbbbe
commit 05158769c3
2 changed files with 132 additions and 1 deletions

View file

@ -278,8 +278,11 @@ gst_rtp_vp9_pay_parse_frame (GstRtpVP9Pay * self, GstBuffer * buffer,
goto error;
/* profile, variable length */
if (!gst_bit_reader_get_bits_uint32 (&reader, &profile, 2))
guint8 version, high;
if (!(gst_bit_reader_get_bits_uint8 (&reader, &version, 1) &&
gst_bit_reader_get_bits_uint8 (&reader, &high, 1)))
goto error;
profile = (high << 1) + version;
if (profile > 2) {
if (!gst_bit_reader_get_bits_uint32 (&reader, &tmp, 1))
goto error;

View file

@ -24,6 +24,7 @@
#include <gst/check/check.h>
#include <gst/check/gstharness.h>
#include <gst/rtp/gstrtpbuffer.h>
#define RTP_VP9_CAPS_STR \
"application/x-rtp,media=video,encoding-name=VP9,clock-rate=90000,payload=96"
@ -478,6 +479,126 @@ GST_START_TEST (test_pay_delta_unit_flag)
GST_END_TEST;
static void
fail_unless_vp9_ss (GstBuffer * buf, gint width, gint height)
{
GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
guint8 *payload;
/* check the SS, on the payloaded buffer */
gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
payload = gst_rtp_buffer_get_payload (&rtp);
/* Assume: buffer should be the start of the frame as well as a keyframe */
fail_unless_equals_int (8, (gint) (payload[0] & 0x08));
fail_unless_equals_int (0, (gint) (payload[0] & 0x40));
/* it also should have set its V bit as we always hard code a SS */
fail_unless_equals_int (2, (gint) (payload[0] & 0x02));
/* similarly, as we don't set the picture ID mode on the payloader,
* it should not have its I set */
fail_unless_equals_int (0, (gint) (payload[0] & 0x80));
/* Now assuming no picture ID signaled, we should find the SS right after */
/* *INDENT-OFF* */
fail_unless_equals_int (0x18, (gint) payload[1]); /* N_S=0 Y=1 G=1 */
fail_unless_equals_int (width >> 8, (gint) payload[2]);
fail_unless_equals_int (width & 0xFF, (gint) payload[3]);
fail_unless_equals_int (height >> 8, (gint) payload[4]);
fail_unless_equals_int (height & 0xFF, (gint) payload[5]);
/* *INDENT-ON* */
fail_unless_equals_int (0x01, (gint) payload[6]); /* N_G=1 */
fail_unless_equals_int (0x04, (gint) payload[7]); /* T=0 U=0 R=1 */
fail_unless_equals_int (0x01, (gint) payload[8]); /* P_DIFF=1 */
gst_rtp_buffer_unmap (&rtp);
}
static void
test_pay_ss_resolution_profiles (guint8 payload[], gint payload_len)
{
GstHarness *h = gst_harness_new_parse ("rtpvp9pay");
GstFlowReturn ret;
GstBuffer *buffer, *out;
gst_harness_set_src_caps_str (h, "video/x-vp9");
buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
payload, payload_len, 0, payload_len, NULL, NULL);
ret = gst_harness_push (h, buffer);
fail_unless_equals_int (ret, GST_FLOW_OK);
out = gst_harness_pull (h);
fail_if (out == NULL);
fail_unless_vp9_ss (out, 16, 16);
gst_buffer_unref (out);
gst_harness_teardown (h);
}
GST_START_TEST (test_pay_ss_resolution_profile_0)
{
// keyframe bitstream for profile 0, with 16x16 frame size
guint8 payload[] =
{ 0x82, 0x49, 0x83, 0x42, 0x20, 0x0, 0xf0, 0x0, 0xf6, 0x0, 0x38, 0x24,
0x1c, 0x18, 0x42, 0x0, 0x0, 0x33, 0x0, 0x0, 0x0, 0x85, 0xff, 0xfa, 0xd0,
0x7f, 0xff, 0xff, 0xff, 0xde, 0x4c, 0x1f, 0xff, 0xff, 0xff, 0xf9, 0xa6,
0x50, 0x0
};
test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
}
GST_END_TEST;
GST_START_TEST (test_pay_ss_resolution_profile_1)
{
// keyframe bitstream for profile 1, with 16x16 frame size
guint8 payload[] = {
0xa2, 0x49, 0x83, 0x42, 0x20, 0x00, 0x1e, 0x00,
0x1e, 0xc0, 0x07, 0x04, 0x83, 0x83, 0x08, 0x40,
0x00, 0x06, 0x60, 0x00, 0x00, 0x10, 0xbf, 0xff,
0x5a, 0x0f, 0xff, 0xff, 0xff, 0xfb, 0xc9, 0x83,
0xff, 0xff, 0xff, 0xff, 0x34, 0xca, 0x00
};
test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
}
GST_END_TEST;
GST_START_TEST (test_pay_ss_resolution_profile_2)
{
// keyframe bitstream for profile 2, with 16x16 frame size
guint8 payload[] = {
0x92, 0x49, 0x83, 0x42, 0x10, 0x00, 0x78, 0x00,
0x7b, 0x00, 0x1c, 0x12, 0x0e, 0x0c, 0x21, 0x00,
0x00, 0x19, 0x80, 0x00, 0x00, 0x42, 0xff, 0xfd,
0x68, 0x3f, 0xff, 0xff, 0xff, 0xef, 0x26, 0x0f,
0xff, 0xff, 0xff, 0xfc, 0xd3, 0x28, 0x00
};
test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
}
GST_END_TEST;
GST_START_TEST (test_pay_ss_resolution_profile_3)
{
// keyframe bitstream for profile 3, with 16x16 frame size
guint8 payload[] =
{ 0xb1, 0x24, 0xc1, 0xa1, 0x8, 0x0, 0x7, 0x80, 0x7, 0xb0, 0x1, 0xc1, 0x20,
0xe0, 0xc2, 0x10, 0x0, 0x1, 0x98, 0x0, 0x0, 0x4, 0x2f, 0xff, 0xd6, 0x83,
0xff, 0xff, 0xff, 0xfe, 0xf2, 0x60, 0xff, 0xff, 0xff, 0xff, 0xcd, 0x32,
0x80, 0x0
};
test_pay_ss_resolution_profiles (payload, G_N_ELEMENTS (payload));
}
GST_END_TEST;
static Suite *
rtpvp9_suite (void)
{
@ -487,6 +608,13 @@ rtpvp9_suite (void)
suite_add_tcase (s, (tc_chain = tcase_create ("vp9pay")));
tcase_add_test (tc_chain, test_pay_delta_unit_flag);
suite_add_tcase (s, (tc_chain =
tcase_create ("vp9pay-ss-resolution-profile")));
tcase_add_test (tc_chain, test_pay_ss_resolution_profile_0);
tcase_add_test (tc_chain, test_pay_ss_resolution_profile_1);
tcase_add_test (tc_chain, test_pay_ss_resolution_profile_2);
tcase_add_test (tc_chain, test_pay_ss_resolution_profile_3);
suite_add_tcase (s, (tc_chain = tcase_create ("vp9depay")));
tcase_add_test (tc_chain, test_depay_flexible_mode);
tcase_add_test (tc_chain, test_depay_non_flexible_mode);