From dd47fa53d80580014e5aeb70d07c8ae2ba4a36fd Mon Sep 17 00:00:00 2001 From: Fabian Orccon Date: Tue, 18 Jul 2023 10:08:54 +0200 Subject: [PATCH] h265parse: Parse SEI unregistered user data Part-of: --- .../flow-expectations/log-parse-src-expected | 4 +- .../gst-libs/gst/codecparsers/gsth264parser.c | 2 +- .../gst-libs/gst/codecparsers/gsth265parser.c | 59 +++++++++++++++++++ .../gst-libs/gst/codecparsers/gsth265parser.h | 33 +++++++++++ .../gst/videoparsers/gsth265parse.c | 26 ++++++++ .../gst/videoparsers/gsth265parse.h | 1 + .../tests/check/elements/h265parse.c | 55 +++++++++++++++++ 7 files changed, 177 insertions(+), 3 deletions(-) diff --git a/subprojects/gst-integration-testsuites/testsuites/validate/h265parse/trickmode_predicted.seek_trickmode_predicted/flow-expectations/log-parse-src-expected b/subprojects/gst-integration-testsuites/testsuites/validate/h265parse/trickmode_predicted.seek_trickmode_predicted/flow-expectations/log-parse-src-expected index dd270b9c9f..44611ad02d 100644 --- a/subprojects/gst-integration-testsuites/testsuites/validate/h265parse/trickmode_predicted.seek_trickmode_predicted/flow-expectations/log-parse-src-expected +++ b/subprojects/gst-integration-testsuites/testsuites/validate/h265parse/trickmode_predicted.seek_trickmode_predicted/flow-expectations/log-parse-src-expected @@ -4,14 +4,14 @@ event segment: format=TIME, start=0:00:00.066666666, offset=0:00:00.000000000, s event tag: GstTagList-stream, taglist=(taglist)"taglist\,\ video-codec\=\(string\)\"H.265\\\ /\\\ HEVC\"\,\ encoder\=\(string\)x265\,\ bitrate\=\(uint\)1547698\;"; event tag: GstTagList-global, taglist=(taglist)"taglist\,\ datetime\=\(datetime\)2019-07-15T17:28:39Z\,\ container-format\=\(string\)\"ISO\\\ MP4/M4A\"\;"; event tag: GstTagList-stream, taglist=(taglist)"taglist\,\ video-codec\=\(string\)\"H.265\\\ \\\(Main\\\ 10\\\ Profile\\\)\"\,\ encoder\=\(string\)x265\,\ bitrate\=\(uint\)1547698\;"; -buffer: dts=0:00:00.000000000, pts=0:00:00.066666666, dur=0:00:00.033333333, flags=discont marker header +buffer: dts=0:00:00.000000000, pts=0:00:00.066666666, dur=0:00:00.033333333, flags=discont marker header, meta=GstVideoSEIUserDataUnregisteredMeta buffer: dts=0:00:00.033333333, pts=0:00:00.166666666, dur=0:00:00.033333333, flags=marker delta-unit buffer: dts=0:00:00.066666666, pts=0:00:00.133333333, dur=0:00:00.033333333, flags=marker delta-unit event flush-start: (no structure) event flush-stop: GstEventFlushStop, reset-time=(boolean)true; event segment: format=TIME, start=0:00:00.066666666, offset=0:00:00.000000000, stop=0:00:10.066666666, flags=0x201, time=0:00:00.000000000, base=0:00:00.000000000, position=0:00:00.066666666 event tag: GstTagList-stream, taglist=(taglist)"taglist\,\ video-codec\=\(string\)\"H.265\\\ \\\(Main\\\ 10\\\ Profile\\\)\"\,\ encoder\=\(string\)x265\,\ bitrate\=\(uint\)1547698\;"; -buffer: dts=0:00:00.000000000, pts=0:00:00.066666666, dur=0:00:00.033333333, flags=discont marker header +buffer: dts=0:00:00.000000000, pts=0:00:00.066666666, dur=0:00:00.033333333, flags=discont marker header, meta=GstVideoSEIUserDataUnregisteredMeta buffer: dts=0:00:00.033333333, pts=0:00:00.166666666, dur=0:00:00.033333333, flags=marker delta-unit buffer: dts=0:00:00.133333333, pts=0:00:00.266666666, dur=0:00:00.033333333, flags=marker delta-unit buffer: dts=0:00:00.233333333, pts=0:00:00.366666666, dur=0:00:00.033333333, flags=marker delta-unit diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth264parser.c b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth264parser.c index 1aa9e719c0..287124dc81 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth264parser.c +++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth264parser.c @@ -1102,8 +1102,8 @@ gst_h264_parser_parse_user_data_unregistered (GstH264NalParser * nalparser, for (int i = 0; i < 16; i++) { READ_UINT8 (nr, urud->uuid[i], 8); - --payload_size; } + payload_size -= 16; urud->size = payload_size; diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.c b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.c index 5f8a393b7f..4a73c6a49d 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.c +++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.c @@ -1242,6 +1242,47 @@ error: } } +static GstH265ParserResult +gst_h265_parser_parse_user_data_unregistered (GstH265Parser * parser, + GstH265UserDataUnregistered * urud, NalReader * nr, guint payload_size) +{ + guint8 *data = NULL; + gint i; + + if (payload_size < 16) { + GST_WARNING ("Too small payload size %d", payload_size); + return GST_H265_PARSER_BROKEN_DATA; + } + + for (int i = 0; i < 16; i++) { + READ_UINT8 (nr, urud->uuid[i], 8); + } + payload_size -= 16; + + urud->size = payload_size; + + data = g_malloc0 (payload_size); + for (i = 0; i < payload_size; ++i) { + READ_UINT8 (nr, data[i], 8); + } + + if (payload_size < 1) { + GST_WARNING ("No more remaining payload data to store"); + g_clear_pointer (&data, g_free); + return GST_H265_PARSER_BROKEN_DATA; + } + + urud->data = data; + GST_MEMDUMP ("SEI user data unregistered", data, payload_size); + return GST_H265_PARSER_OK; + +error: + { + GST_WARNING ("error parsing \"User Data Unregistered\""); + g_clear_pointer (&data, g_free); + return GST_H265_PARSER_ERROR; + } +} static GstH265ParserResult gst_h265_parser_parse_time_code (GstH265Parser * parser, @@ -3067,6 +3108,10 @@ gst_h265_parser_parse_sei_message (GstH265Parser * parser, res = gst_h265_parser_parse_registered_user_data (parser, &sei->payload.registered_user_data, nr, payload_size >> 3); break; + case GST_H265_SEI_USER_DATA_UNREGISTERED: + res = gst_h265_parser_parse_user_data_unregistered (parser, + &sei->payload.user_data_unregistered, nr, payload_size >> 3); + break; case GST_H265_SEI_RECOVERY_POINT: res = gst_h265_parser_parse_recovery_point (parser, &sei->payload.recovery_point, nr); @@ -3228,6 +3273,16 @@ gst_h265_sei_copy (GstH265SEIMessage * dst_sei, dst_rud->data = g_malloc (src_rud->size); memcpy ((guint8 *) dst_rud->data, src_rud->data, src_rud->size); } + } else if (dst_sei->payloadType == GST_H265_SEI_USER_DATA_UNREGISTERED) { + GstH265UserDataUnregistered *dst_udu = + &dst_sei->payload.user_data_unregistered; + const GstH265UserDataUnregistered *src_udu = + &src_sei->payload.user_data_unregistered; + + if (src_udu->size) { + dst_udu->data = g_malloc (src_udu->size); + memcpy ((guint8 *) dst_udu->data, src_udu->data, src_udu->size); + } } return TRUE; @@ -3256,6 +3311,10 @@ gst_h265_sei_free (GstH265SEIMessage * sei) GstH265RegisteredUserData *rud = &sei->payload.registered_user_data; g_free ((guint8 *) rud->data); rud->data = NULL; + } else if (sei->payloadType == GST_H265_SEI_USER_DATA_UNREGISTERED) { + GstH265UserDataUnregistered *udu = &sei->payload.user_data_unregistered; + g_free ((guint8 *) udu->data); + udu->data = NULL; } } diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.h b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.h index ff01c23711..38890b6181 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.h @@ -348,11 +348,19 @@ typedef enum * * The type of SEI message. */ +/** + * GST_H265_SEI_USER_DATA_UNREGISTERED + * + * User data unregistered (D.2.7) + * + * Since: 1.24 + */ typedef enum { GST_H265_SEI_BUF_PERIOD = 0, GST_H265_SEI_PIC_TIMING = 1, GST_H265_SEI_REGISTERED_USER_DATA = 4, + GST_H265_SEI_USER_DATA_UNREGISTERED = 5, GST_H265_SEI_RECOVERY_POINT = 6, GST_H265_SEI_TIME_CODE = 136, GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME = 137, @@ -446,6 +454,7 @@ typedef struct _GstH265SliceHdr GstH265SliceHdr; typedef struct _GstH265PicTiming GstH265PicTiming; typedef struct _GstH265RegisteredUserData GstH265RegisteredUserData; +typedef struct _GstH265UserDataUnregistered GstH265UserDataUnregistered; typedef struct _GstH265BufferingPeriod GstH265BufferingPeriod; typedef struct _GstH265RecoveryPoint GstH265RecoveryPoint; typedef struct _GstH265TimeCode GstH265TimeCode; @@ -1577,6 +1586,22 @@ struct _GstH265RegisteredUserData guint size; }; +/** + * GstH265UserDataUnregistered: + * @uuid: an uuid_iso_iec_11578. + * @data: the data of user_data_payload_byte + * @size: the size of @data in bytes + * + * The User data unregistered SEI message syntax. + * + * Since: 1.24 + */ +struct _GstH265UserDataUnregistered +{ + guint8 uuid[16]; + const guint8 *data; + guint size; +}; /** * GstH265TimeCode: @@ -1642,6 +1667,13 @@ struct _GstH265ContentLightLevel guint16 max_pic_average_light_level; }; +/** + * _GstH265SEIMessage.payload.user_data_unregistered: + * + * User Data Unregistered + * + * Since: 1.24 + */ struct _GstH265SEIMessage { GstH265SEIPayloadType payloadType; @@ -1654,6 +1686,7 @@ struct _GstH265SEIMessage GstH265TimeCode time_code; GstH265MasteringDisplayColourVolume mastering_display_colour_volume; GstH265ContentLightLevel content_light_level; + GstH265UserDataUnregistered user_data_unregistered; /* ... could implement more */ } payload; }; diff --git a/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.c b/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.c index 69eb77178f..2af4070725 100644 --- a/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.c +++ b/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.c @@ -121,6 +121,9 @@ static gboolean gst_h265_parse_src_event (GstBaseParse * parse, static void gst_h265_parse_process_sei_user_data (GstH265Parse * h265parse, GstH265RegisteredUserData * rud); +static void +gst_h265_parse_process_sei_user_data_unregistered (GstH265Parse * h265parse, + GstH265UserDataUnregistered * urud); static void gst_h265_parse_class_init (GstH265ParseClass * klass) @@ -179,6 +182,7 @@ gst_h265_parse_finalize (GObject * object) { GstH265Parse *h265parse = GST_H265_PARSE (object); + gst_video_user_data_unregistered_clear (&h265parse->user_data_unregistered); g_object_unref (h265parse->frame_out); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -581,6 +585,10 @@ gst_h265_parse_process_sei (GstH265Parse * h265parse, GstH265NalUnit * nalu) gst_h265_parse_process_sei_user_data (h265parse, &sei.payload.registered_user_data); break; + case GST_H265_SEI_USER_DATA_UNREGISTERED: + gst_h265_parse_process_sei_user_data_unregistered (h265parse, + &sei.payload.user_data_unregistered); + break; case GST_H265_SEI_BUF_PERIOD: /* FIXME */ break; @@ -709,6 +717,21 @@ gst_h265_parse_process_sei_user_data (GstH265Parse * h265parse, } +static void +gst_h265_parse_process_sei_user_data_unregistered (GstH265Parse * h265parse, + GstH265UserDataUnregistered * urud) +{ + GstByteReader br; + + if (urud->data == NULL || urud->size < 1) + return; + + gst_byte_reader_init (&br, urud->data, urud->size); + + gst_video_parse_user_data_unregistered ((GstElement *) h265parse, + &h265parse->user_data_unregistered, &br, urud->uuid); +} + /* caller guarantees 2 bytes of nal payload */ static gboolean gst_h265_parse_process_nal (GstH265Parse * h265parse, GstH265NalUnit * nalu) @@ -3098,6 +3121,9 @@ gst_h265_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_video_push_user_data ((GstElement *) h265parse, &h265parse->user_data, parse_buffer); + gst_video_push_user_data_unregistered ((GstElement *) h265parse, + &h265parse->user_data_unregistered, parse_buffer); + gst_h265_parse_reset_frame (h265parse); return GST_FLOW_OK; diff --git a/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.h b/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.h index e49d22cdbc..d71f03535f 100644 --- a/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.h +++ b/subprojects/gst-plugins-bad/gst/videoparsers/gsth265parse.h @@ -115,6 +115,7 @@ struct _GstH265Parse gboolean picture_start; GstVideoParseUserData user_data; + GstVideoParseUserDataUnregistered user_data_unregistered; /* props */ gint interval; diff --git a/subprojects/gst-plugins-bad/tests/check/elements/h265parse.c b/subprojects/gst-plugins-bad/tests/check/elements/h265parse.c index a2c4f3de80..afdeafde58 100644 --- a/subprojects/gst-plugins-bad/tests/check/elements/h265parse.c +++ b/subprojects/gst-plugins-bad/tests/check/elements/h265parse.c @@ -24,6 +24,7 @@ */ #include +#include #include "parser.h" #define SRC_CAPS_TMPL "video/x-h265, parsed=(boolean)false" @@ -1116,6 +1117,58 @@ GST_START_TEST (test_drain) GST_END_TEST; +GST_START_TEST (test_parse_sei_userdefinedunregistered) +{ + GstVideoSEIUserDataUnregisteredMeta *meta; + GstHarness *h; + GstBuffer *buf; + + const guint8 bytestream[] = { + 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x04, 0x08, 0x00, 0x00, 0x03, + 0x00, 0x9e, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1e, 0x90, 0x11, 0x08, + 0xb2, 0xca, 0xcd, 0x57, 0x95, 0xcd, 0xc0, 0x80, 0x80, 0x01, 0x00, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x19, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x44, 0x01, 0xc1, 0x73, 0x18, 0x31, 0x08, 0x90, + // SEI + 0x00, 0x00, 0x01, 0x4e, 0x01, + 0x05, // SEI Type. + 0x18, // SEI Payload size (16 UUID size + 8 payload size = 24). + // SEI User Data Unregistered UUID. + 0xee, 0x2c, 0xa2, 0xde, 0x09, 0xb5, 0x17, 0x47, 0xdb, 0xbb, 0x55, 0xa4, + 0xfe, 0x7f, 0xc2, 0xfc, + // SEI User Data Unregistered Payload. + 0x4e, 0x78, 0x32, 0x36, 0x35, 0x20, 0x28, 0x62, + }; + const gsize bytestream_size = sizeof (bytestream); + const guint8 uuid[] = { + 0xee, 0x2c, 0xa2, 0xde, 0x09, 0xb5, 0x17, 0x47, 0xdb, 0xbb, 0x55, 0xa4, + 0xfe, 0x7f, 0xc2, 0xfc + }; + const guint8 payload[] = { 0x4e, 0x78, 0x32, 0x36, 0x35, 0x20, 0x28, 0x62 }; + + h = gst_harness_new ("h265parse"); + gst_harness_set_src_caps_str (h, "video/x-h265, stream-format=byte-stream"); + + buf = gst_buffer_new_and_alloc (bytestream_size); + gst_buffer_fill (buf, 0, bytestream, bytestream_size); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + gst_harness_push_event (h, gst_event_new_eos ()); + + buf = gst_harness_pull (h); + meta = gst_buffer_get_video_sei_user_data_unregistered_meta (buf); + fail_unless (meta != NULL); + + fail_unless (memcmp (meta->uuid, uuid, 16) == 0); + fail_unless_equals_int (meta->size, G_N_ELEMENTS (payload)); + fail_unless (memcmp (meta->data, payload, meta->size) == 0); + + gst_buffer_unref (buf); + + gst_harness_teardown (h); +} + +GST_END_TEST; static Suite * h265parse_harnessed_suite (void) @@ -1152,6 +1205,8 @@ h265parse_harnessed_suite (void) tcase_add_test (tc_chain, test_drain); + tcase_add_test (tc_chain, test_parse_sei_userdefinedunregistered); + return s; }