h265parser: Allow partially broken hvcC data

Ignores parsing error on the last nalu of the array if the nalu type
is not VPS/SPS/PPS

Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2905
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5188>
This commit is contained in:
Seungha Yang 2023-08-15 19:55:34 +09:00 committed by GStreamer Marge Bot
parent f058eda798
commit d84c13ab0d
2 changed files with 77 additions and 1 deletions

View file

@ -5072,13 +5072,15 @@ gst_h265_parser_parse_decoder_config_record (GstH265Parser * parser,
g_assert (gst_bit_reader_get_pos (&br) == 23 * 8);
for (i = 0; i < num_of_arrays; i++) {
GstH265DecoderConfigRecordNalUnitArray array;
guint8 nalu_type;
GstH265NalUnit nalu;
guint16 num_nalu, j;
guint offset;
READ_CONFIG_UINT8 (array.array_completeness, 1);
SKIP_CONFIG_BITS (1);
READ_CONFIG_UINT8 (array.nal_unit_type, 6);
READ_CONFIG_UINT8 (nalu_type, 6);
array.nal_unit_type = nalu_type;
READ_CONFIG_UINT16 (num_nalu, 16);
@ -5090,6 +5092,15 @@ gst_h265_parser_parse_decoder_config_record (GstH265Parser * parser,
2, &nalu);
if (result != GST_H265_PARSER_OK) {
g_array_unref (array.nalu);
/* Ignores parsing error if this is the last nalu and not an essential
* nalu for decoding */
if (i + 1 == num_of_arrays && j + 1 == num_nalu &&
nalu_type != GST_H265_NAL_VPS && nalu_type != GST_H265_NAL_SPS &&
nalu_type != GST_H265_NAL_PPS) {
GST_WARNING ("Couldn't parse the last nalu, type %d at array %d / %d",
nalu_type, i, j);
goto out;
}
goto error;
}
@ -5106,6 +5117,7 @@ gst_h265_parser_parse_decoder_config_record (GstH265Parser * parser,
}
}
out:
*config = ret;
return GST_H265_PARSER_OK;

View file

@ -1170,6 +1170,69 @@ GST_START_TEST (test_parse_sei_userdefinedunregistered)
GST_END_TEST;
GST_START_TEST (test_invalid_sei_in_hvcc)
{
GstHarness *h;
GstCaps *caps;
GstBuffer *codec_data;
/* Consists of 4 arrays (VPS, SPS, PPS, SEI -> broken) and each array contains
* single nalu
* Captured from the log at
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2905
*/
static const guint8 hvcc_data[] = {
0x01, 0x01, 0x01, 0x01, 0x60, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf0, 0x00, 0xfc, 0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f, 0x04, 0x20,
0x00, 0x01, 0x00, 0x17, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
0x7b, 0xac, 0x09, 0x21, 0x00, 0x01, 0x00, 0x42, 0x42, 0x01, 0x01, 0x01,
0x60, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x7b, 0xa0, 0x02, 0x80, 0x80, 0x2d, 0x1f, 0xe3, 0x6b, 0xbb, 0x53,
0x77, 0x72, 0x5d, 0x60, 0x2d, 0xc0, 0x40, 0x40, 0x41, 0x00, 0x00, 0x03,
0x03, 0xe8, 0x00, 0x00, 0x4e, 0x20, 0x72, 0x1d, 0xee, 0x51, 0x00, 0x05,
0xdc, 0x00, 0x00, 0x1a, 0x5e, 0x00, 0x00, 0x2e, 0xe0, 0x00, 0x00, 0xd2,
0xf0, 0x08, 0x22, 0x00, 0x01, 0x00, 0x0b, 0x44, 0x01, 0xc1, 0x72, 0xb0,
0x9c, 0x38, 0x77, 0x06, 0x0c, 0x24, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00
};
caps = gst_caps_new_simple ("video/x-h265", "stream-format", G_TYPE_STRING,
"hvc1", "alignment", G_TYPE_STRING, "au", NULL);
codec_data = gst_buffer_new_memdup (hvcc_data, sizeof (hvcc_data));
gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
gst_buffer_unref (codec_data);
h = gst_harness_new ("h265parse");
gst_harness_set_src_caps (h, caps);
gst_harness_push_event (h, gst_event_new_eos ());
while (TRUE) {
GstEvent *event = gst_harness_pull_event (h);
fail_unless (event);
if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
GstStructure *s;
gint width, height;
gst_event_parse_caps (event, &caps);
s = gst_caps_get_structure (caps, 0);
fail_unless (gst_structure_get_int (s, "width", &width));
fail_unless_equals_int (width, 1280);
fail_unless (gst_structure_get_int (s, "height", &height));
fail_unless_equals_int (height, 720);
gst_event_unref (event);
break;
}
gst_event_unref (event);
}
gst_harness_teardown (h);
}
GST_END_TEST;
static Suite *
h265parse_harnessed_suite (void)
{
@ -1206,6 +1269,7 @@ h265parse_harnessed_suite (void)
tcase_add_test (tc_chain, test_drain);
tcase_add_test (tc_chain, test_parse_sei_userdefinedunregistered);
tcase_add_test (tc_chain, test_invalid_sei_in_hvcc);
return s;
}