From 40212aaf00a0eb109bd00df2424f891ed6e49b53 Mon Sep 17 00:00:00 2001 From: Aaron Boxer Date: Wed, 8 May 2019 11:06:40 -0400 Subject: [PATCH] h265parse: add support for SEI registered user data --- gst-libs/gst/codecparsers/gsth265parser.c | 51 ++++++++++++++++++++ gst-libs/gst/codecparsers/gsth265parser.h | 13 +++++ gst/videoparsers/gsth265parse.c | 59 ++++++++++++++++++++++- gst/videoparsers/gsth265parse.h | 3 ++ 4 files changed, 125 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/codecparsers/gsth265parser.c b/gst-libs/gst/codecparsers/gsth265parser.c index df8e99dbab..f0bd1084af 100644 --- a/gst-libs/gst/codecparsers/gsth265parser.c +++ b/gst-libs/gst/codecparsers/gsth265parser.c @@ -1132,6 +1132,53 @@ error: return GST_H265_PARSER_ERROR; } + +static GstH265ParserResult +gst_h265_parser_parse_registered_user_data (GstH265Parser * parser, + GstH265RegisteredUserData * rud, NalReader * nr, guint payload_size) +{ + guint8 *data = NULL; + guint i; + + rud->data = NULL; + rud->size = 0; + + if (payload_size < 2) + return GST_H265_PARSER_ERROR; + + READ_UINT8 (nr, rud->country_code, 8); + --payload_size; + + if (rud->country_code == 0xFF) { + READ_UINT8 (nr, rud->country_code_extension, 8); + --payload_size; + } else { + rud->country_code_extension = 0; + } + + if (payload_size < 8) + return GST_H265_PARSER_ERROR; + + data = g_malloc (payload_size); + for (i = 0; i < payload_size / 8; ++i) { + READ_UINT8 (nr, data[i], 8); + } + + GST_MEMDUMP ("SEI user data", data, payload_size / 8); + + rud->data = data; + rud->size = payload_size; + return GST_H265_PARSER_OK; + +error: + { + GST_WARNING ("error parsing \"Registered User Data\""); + g_free (data); + return GST_H265_PARSER_ERROR; + } +} + + static GstH265ParserResult gst_h265_parser_parse_time_code (GstH265Parser * parser, GstH265TimeCode * tc, NalReader * nr) @@ -2472,6 +2519,10 @@ gst_h265_parser_parse_sei_message (GstH265Parser * parser, res = gst_h265_parser_parse_pic_timing (parser, &sei->payload.pic_timing, nr); break; + case GST_H265_SEI_REGISTERED_USER_DATA: + res = gst_h265_parser_parse_registered_user_data (parser, + &sei->payload.registered_user_data, nr, payloadSizeBytes); + break; case GST_H265_SEI_RECOVERY_POINT: res = gst_h265_parser_parse_recovery_point (parser, &sei->payload.recovery_point, nr); diff --git a/gst-libs/gst/codecparsers/gsth265parser.h b/gst-libs/gst/codecparsers/gsth265parser.h index dab28ebdf5..27b529b54d 100644 --- a/gst-libs/gst/codecparsers/gsth265parser.h +++ b/gst-libs/gst/codecparsers/gsth265parser.h @@ -271,6 +271,7 @@ typedef enum * GstH265SEIPayloadType: * @GST_H265_SEI_BUF_PERIOD: Buffering Period SEI Message * @GST_H265_SEI_PIC_TIMING: Picture Timing SEI Message + * @GST_H265_SEI_REGISTERED_USER_DATA: Registered user data (D.2.5) * @GST_H265_SEI_RECOVERY_POINT: Recovery Point SEI Message (D.3.8) * @GST_H265_SEI_TIME_CODE: Time code SEI message (D.2.27) (Since: 1.16) * @GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME: Mastering display colour volume information SEI message (D.2.28) (Since: 1.18) @@ -283,6 +284,7 @@ typedef enum { GST_H265_SEI_BUF_PERIOD = 0, GST_H265_SEI_PIC_TIMING = 1, + GST_H265_SEI_REGISTERED_USER_DATA = 4, GST_H265_SEI_RECOVERY_POINT = 6, GST_H265_SEI_TIME_CODE = 136, GST_H265_SEI_MASTERING_DISPLAY_COLOUR_VOLUME = 137, @@ -373,6 +375,7 @@ typedef struct _GstH265ShortTermRefPicSet GstH265ShortTermRefPicSet; typedef struct _GstH265SliceHdr GstH265SliceHdr; typedef struct _GstH265PicTiming GstH265PicTiming; +typedef struct _GstH265RegisteredUserData GstH265RegisteredUserData; typedef struct _GstH265BufferingPeriod GstH265BufferingPeriod; typedef struct _GstH265RecoveryPoint GstH265RecoveryPoint; typedef struct _GstH265TimeCode GstH265TimeCode; @@ -1226,6 +1229,15 @@ struct _GstH265RecoveryPoint guint8 broken_link_flag; }; +struct _GstH265RegisteredUserData +{ + guint8 country_code; + guint8 country_code_extension; + const guint8 *data; + guint size; +}; + + /** * GstH265TimeCode: * The time code SEI message provides time code information similar to that @@ -1297,6 +1309,7 @@ struct _GstH265SEIMessage union { GstH265BufferingPeriod buffering_period; GstH265PicTiming pic_timing; + GstH265RegisteredUserData registered_user_data; GstH265RecoveryPoint recovery_point; GstH265TimeCode time_code; GstH265MasteringDisplayColourVolume mastering_display_colour_volume; diff --git a/gst/videoparsers/gsth265parse.c b/gst/videoparsers/gsth265parse.c index 909955c4b8..75c256799b 100644 --- a/gst/videoparsers/gsth265parse.c +++ b/gst/videoparsers/gsth265parse.c @@ -114,6 +114,9 @@ static GstCaps *gst_h265_parse_get_caps (GstBaseParse * parse, static gboolean gst_h265_parse_event (GstBaseParse * parse, GstEvent * event); static gboolean gst_h265_parse_src_event (GstBaseParse * parse, GstEvent * event); +static void +gst_h265_parse_process_sei_user_data (GstH265Parse * h265parse, + GstH265RegisteredUserData * rud); static void gst_h265_parse_class_init (GstH265ParseClass * klass) @@ -570,6 +573,10 @@ gst_h265_parse_process_sei (GstH265Parse * h265parse, GstH265NalUnit * nalu) case GST_H265_SEI_PIC_TIMING: h265parse->sei_pic_struct = sei.payload.pic_timing.pic_struct; break; + case GST_H265_SEI_REGISTERED_USER_DATA: + gst_h265_parse_process_sei_user_data (h265parse, + &sei.payload.registered_user_data); + break; case GST_H265_SEI_BUF_PERIOD: /* FIXME */ break; @@ -672,6 +679,39 @@ gst_h265_parse_process_sei (GstH265Parse * h265parse, GstH265NalUnit * nalu) g_array_free (messages, TRUE); } +static void +gst_h265_parse_process_sei_user_data (GstH265Parse * h265parse, + GstH265RegisteredUserData * rud) +{ + guint16 provider_code; + GstByteReader br; + GstVideoParseUtilsField field = GST_VIDEO_PARSE_UTILS_FIELD_1; + + /* only US country code is currently supported */ + switch (rud->country_code) { + case ITU_T_T35_COUNTRY_CODE_US: + break; + default: + GST_LOG_OBJECT (h265parse, "Unsupported country code %d", + rud->country_code); + return; + } + + if (rud->data == NULL || rud->size < 2) + return; + + gst_byte_reader_init (&br, rud->data, rud->size); + + provider_code = gst_byte_reader_get_uint16_be_unchecked (&br); + + if (h265parse->sei_pic_struct == + (guint8) GST_H265_SEI_PIC_STRUCT_BOTTOM_FIELD) + field = GST_VIDEO_PARSE_UTILS_FIELD_1; + gst_video_parse_user_data ((GstElement *) h265parse, &h265parse->user_data, + &br, field, provider_code); + +} + /* caller guarantees 2 bytes of nal payload */ static gboolean gst_h265_parse_process_nal (GstH265Parse * h265parse, GstH265NalUnit * nalu) @@ -2565,6 +2605,7 @@ gst_h265_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) GstH265Parse *h265parse; GstBuffer *buffer; GstEvent *event; + GstBuffer *parse_buffer = NULL; h265parse = GST_H265_PARSE (parse); @@ -2671,9 +2712,9 @@ gst_h265_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) guint i = 0; for (i = 0; i < h265parse->time_code.num_clock_ts; i++) { - GstVideoTimeCodeFlags flags = 0; gint field_count = -1; guint n_frames; + GstVideoTimeCodeFlags flags = 0; if (!h265parse->time_code.clock_timestamp_flag[i]) break; @@ -2742,6 +2783,22 @@ gst_h265_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) } } + if (frame->out_buffer) { + parse_buffer = frame->out_buffer = + gst_buffer_make_writable (frame->out_buffer); + } else { + parse_buffer = frame->buffer = gst_buffer_make_writable (frame->buffer); + } + + if (h265parse->sei_pic_struct != GST_H265_SEI_PIC_STRUCT_FRAME) { + GST_BUFFER_FLAG_SET (parse_buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED); + if (h265parse->sei_pic_struct == GST_H265_SEI_PIC_STRUCT_TOP_FIELD) + GST_BUFFER_FLAG_SET (parse_buffer, GST_VIDEO_BUFFER_FLAG_TFF); + } + + gst_video_push_user_data ((GstElement *) h265parse, &h265parse->user_data, + parse_buffer); + gst_h265_parse_reset_frame (h265parse); return GST_FLOW_OK; diff --git a/gst/videoparsers/gsth265parse.h b/gst/videoparsers/gsth265parse.h index 0af1528d70..40f0682096 100644 --- a/gst/videoparsers/gsth265parse.h +++ b/gst/videoparsers/gsth265parse.h @@ -25,6 +25,7 @@ #include #include #include +#include "gstvideoparseutils.h" G_BEGIN_DECLS @@ -108,6 +109,8 @@ struct _GstH265Parse /* AU state */ gboolean picture_start; + GstVideoParseUserData user_data; + /* props */ gint interval;