h265parse: Parse SEI unregistered user data

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5070>
This commit is contained in:
Fabian Orccon 2023-07-18 10:08:54 +02:00 committed by GStreamer Marge Bot
parent 6b339b5d39
commit dd47fa53d8
7 changed files with 177 additions and 3 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
};

View file

@ -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;

View file

@ -115,6 +115,7 @@ struct _GstH265Parse
gboolean picture_start;
GstVideoParseUserData user_data;
GstVideoParseUserDataUnregistered user_data_unregistered;
/* props */
gint interval;

View file

@ -24,6 +24,7 @@
*/
#include <gst/check/check.h>
#include <gst/video/video-sei.h>
#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;
}