diff --git a/gst-libs/gst/codecparsers/gsth265parser.c b/gst-libs/gst/codecparsers/gsth265parser.c index d44a045f78..16fce006b9 100644 --- a/gst-libs/gst/codecparsers/gsth265parser.c +++ b/gst-libs/gst/codecparsers/gsth265parser.c @@ -1124,6 +1124,64 @@ error: return GST_H265_PARSER_ERROR; } +static GstH265ParserResult +gst_h265_parser_parse_time_code (GstH265Parser * parser, + GstH265TimeCode * tc, NalReader * nr) +{ + guint i; + + GST_DEBUG ("parsing \"Time code\""); + + READ_UINT8 (nr, tc->num_clock_ts, 2); + + for (i = 0; i < tc->num_clock_ts; i++) { + READ_UINT8 (nr, tc->clock_timestamp_flag[i], 1); + if (tc->clock_timestamp_flag[i]) { + READ_UINT8 (nr, tc->units_field_based_flag[i], 1); + READ_UINT8 (nr, tc->counting_type[i], 5); + READ_UINT8 (nr, tc->full_timestamp_flag[i], 1); + READ_UINT8 (nr, tc->discontinuity_flag[i], 1); + READ_UINT8 (nr, tc->cnt_dropped_flag[i], 1); + READ_UINT16 (nr, tc->n_frames[i], 9); + + if (tc->full_timestamp_flag[i]) { + tc->seconds_flag[i] = TRUE; + READ_UINT8 (nr, tc->seconds_value[i], 6); + + tc->minutes_flag[i] = TRUE; + READ_UINT8 (nr, tc->minutes_value[i], 6); + + tc->hours_flag[i] = TRUE; + READ_UINT8 (nr, tc->hours_value[i], 5); + } else { + READ_UINT8 (nr, tc->seconds_flag[i], 1); + if (tc->seconds_flag[i]) { + READ_UINT8 (nr, tc->seconds_value[i], 6); + READ_UINT8 (nr, tc->minutes_flag[i], 1); + if (tc->minutes_flag[i]) { + READ_UINT8 (nr, tc->minutes_value[i], 6); + READ_UINT8 (nr, tc->hours_flag[i], 1); + if (tc->hours_flag[i]) { + READ_UINT8 (nr, tc->hours_value[i], 5); + } + } + } + } + } + + READ_UINT8 (nr, tc->time_offset_length[i], 5); + + if (tc->time_offset_length[i] > 0) + READ_UINT32 (nr, tc->time_offset_value[i], tc->time_offset_length[i]); + } + + return GST_H265_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Time code\""); + return GST_H265_PARSER_ERROR; +} + /******** API *************/ /** @@ -2312,6 +2370,10 @@ gst_h265_parser_parse_sei_message (GstH265Parser * parser, res = gst_h265_parser_parse_recovery_point (parser, &sei->payload.recovery_point, nr); break; + case GST_H265_SEI_TIME_CODE: + res = gst_h265_parser_parse_time_code (parser, + &sei->payload.time_code, nr); + break; default: /* Just consume payloadSize bytes, which does not account for emulation prevention bytes */ diff --git a/gst-libs/gst/codecparsers/gsth265parser.h b/gst-libs/gst/codecparsers/gsth265parser.h index b4d1901c01..db996fc990 100644 --- a/gst-libs/gst/codecparsers/gsth265parser.h +++ b/gst-libs/gst/codecparsers/gsth265parser.h @@ -221,6 +221,7 @@ typedef enum * @GST_H265_SEI_BUF_PERIOD: Buffering Period SEI Message * @GST_H265_SEI_PIC_TIMING: Picture Timing SEI Message * @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) * ... * * The type of SEI message. @@ -230,6 +231,7 @@ typedef enum GST_H265_SEI_BUF_PERIOD = 0, GST_H265_SEI_PIC_TIMING = 1, GST_H265_SEI_RECOVERY_POINT = 6, + GST_H265_SEI_TIME_CODE = 136, /* and more... */ } GstH265SEIPayloadType; @@ -316,6 +318,7 @@ typedef struct _GstH265SliceHdr GstH265SliceHdr; typedef struct _GstH265PicTiming GstH265PicTiming; typedef struct _GstH265BufferingPeriod GstH265BufferingPeriod; typedef struct _GstH265RecoveryPoint GstH265RecoveryPoint; +typedef struct _GstH265TimeCode GstH265TimeCode; typedef struct _GstH265SEIMessage GstH265SEIMessage; /** @@ -1073,6 +1076,36 @@ struct _GstH265RecoveryPoint guint8 broken_link_flag; }; +/** + * GstH265TimeCode: + * The time code SEI message provides time code information similar to that + * defined by SMPTE ST 12-1 (2014) for field(s) or frame(s) of the current + * picture. + * + * D.2.27 + * + * Since: 1.16 + */ +struct _GstH265TimeCode +{ + guint8 num_clock_ts; + guint8 clock_timestamp_flag[3]; + guint8 units_field_based_flag[3]; + guint8 counting_type[3]; + guint8 full_timestamp_flag[3]; + guint8 discontinuity_flag[3]; + guint8 cnt_dropped_flag[3]; + guint16 n_frames[3]; + guint8 seconds_flag[3]; + guint8 seconds_value[3]; + guint8 minutes_flag[3]; + guint8 minutes_value[3]; + guint8 hours_flag[3]; + guint8 hours_value[3]; + guint8 time_offset_length[3]; + guint32 time_offset_value[3]; +}; + struct _GstH265SEIMessage { GstH265SEIPayloadType payloadType; @@ -1081,6 +1114,7 @@ struct _GstH265SEIMessage GstH265BufferingPeriod buffering_period; GstH265PicTiming pic_timing; GstH265RecoveryPoint recovery_point; + GstH265TimeCode time_code; /* ... could implement more */ } payload; }; diff --git a/gst/videoparsers/gsth265parse.c b/gst/videoparsers/gsth265parse.c index c7fd8bf5b0..b16f20d09f 100644 --- a/gst/videoparsers/gsth265parse.c +++ b/gst/videoparsers/gsth265parse.c @@ -544,9 +544,14 @@ gst_h265_parse_process_sei (GstH265Parse * h265parse, GstH265NalUnit * nalu) sei.payload.recovery_point.broken_link_flag); h265parse->keyframe = TRUE; break; - - case GST_H265_SEI_BUF_PERIOD: + case GST_H265_SEI_TIME_CODE: + memcpy (&h265parse->time_code, &sei.payload.time_code, + sizeof (GstH265TimeCode)); + break; case GST_H265_SEI_PIC_TIMING: + h265parse->sei_pic_struct = sei.payload.pic_timing.pic_struct; + break; + case GST_H265_SEI_BUF_PERIOD: /* FIXME */ break; } @@ -1245,8 +1250,8 @@ gst_h265_parse_make_codec_data (GstH265Parse * h265parse) } data[6] |= (pft->progressive_source_flag << 7) | (pft->interlaced_source_flag << 6) | - (pft->non_packed_constraint_flag << 5) | (pft-> - frame_only_constraint_flag << 4); + (pft-> + non_packed_constraint_flag << 5) | (pft->frame_only_constraint_flag << 4); data[12] = pft->level_idc; /* min_spatial_segmentation_idc */ GST_WRITE_UINT16_BE (data + 13, min_spatial_segmentation_idc); @@ -1701,9 +1706,13 @@ gst_h265_parse_update_src_caps (GstH265Parse * h265parse, GstCaps * caps) /* but not necessarily or reliably this */ if (fps_num > 0 && fps_den > 0) { + GstStructure *s2; GST_INFO_OBJECT (h265parse, "setting framerate in caps"); gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, fps_num, fps_den, NULL); + s2 = gst_caps_get_structure (caps, 0); + gst_structure_get_fraction (s2, "framerate", &h265parse->parsed_fps_n, + &h265parse->parsed_fps_d); gst_base_parse_set_frame_rate (GST_BASE_PARSE (h265parse), fps_num, fps_den, 0, 0); latency = gst_util_uint64_scale (GST_SECOND, fps_den, fps_num); @@ -2213,6 +2222,76 @@ 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; + + if (!h265parse->time_code.clock_timestamp_flag[i]) + break; + + h265parse->time_code.clock_timestamp_flag[i] = 0; + + /* Table D.2 */ + switch (h265parse->sei_pic_struct) { + case GST_H265_SEI_PIC_STRUCT_FRAME: + case GST_H265_SEI_PIC_STRUCT_TOP_FIELD: + case GST_H265_SEI_PIC_STRUCT_BOTTOM_FIELD: + field_count = h265parse->sei_pic_struct; + break; + case GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM: + case GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_PREVIOUS_BOTTOM: + case GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_NEXT_BOTTOM: + field_count = i + 1; + break; + case GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP: + case GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_PREVIOUS_TOP: + case GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_NEXT_TOP: + field_count = 2 - i; + break; + case GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: + field_count = i % 2 ? 2 : 1; + break; + case GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: + field_count = i % 2 ? 1 : 2; + break; + case GST_H265_SEI_PIC_STRUCT_FRAME_DOUBLING: + case GST_H265_SEI_PIC_STRUCT_FRAME_TRIPLING: + field_count = 0; + break; + } + + if (field_count == -1) { + GST_WARNING_OBJECT (parse, + "failed to determine field count for timecode"); + field_count = 0; + } + + /* Dropping of the two lowest (value 0 and 1) n_frames[ i ] counts when + * seconds_value[ i ] is equal to 0 and minutes_value[ i ] is not an integer + * multiple of 10 */ + if (h265parse->time_code.counting_type[i] == 4) + flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME; + + if (h265parse->sei_pic_struct != GST_H265_SEI_PIC_STRUCT_FRAME) + flags |= GST_VIDEO_TIME_CODE_FLAGS_INTERLACED; + + gst_buffer_add_video_time_code_meta_full (buffer, + h265parse->parsed_fps_n, + h265parse->parsed_fps_d, + NULL, + flags, + h265parse->time_code.hours_flag[i] ? h265parse->time_code. + hours_value[i] : 0, + h265parse->time_code.minutes_flag[i] ? h265parse->time_code. + minutes_value[i] : 0, + h265parse->time_code.seconds_flag[i] ? h265parse->time_code. + seconds_value[i] : 0, h265parse->time_code.n_frames[i], field_count); + } + } + gst_h265_parse_reset_frame (h265parse); return GST_FLOW_OK; diff --git a/gst/videoparsers/gsth265parse.h b/gst/videoparsers/gsth265parse.h index d27aac3586..f52364984e 100644 --- a/gst/videoparsers/gsth265parse.h +++ b/gst/videoparsers/gsth265parse.h @@ -52,6 +52,7 @@ struct _GstH265Parse gint fps_num, fps_den; gint upstream_par_n, upstream_par_d; gint parsed_par_n, parsed_par_d; + gint parsed_fps_n, parsed_fps_d; /* current codec_data in output caps, if any */ GstBuffer *codec_data; /* input codec_data, if any */ @@ -86,6 +87,12 @@ struct _GstH265Parse GstBuffer *sps_nals[GST_H265_MAX_SPS_COUNT]; GstBuffer *pps_nals[GST_H265_MAX_PPS_COUNT]; + /* Infos we need to keep track of */ + guint8 sei_pic_struct; + + /* Collected TimeCode SEI */ + GstH265TimeCode time_code; + gboolean discont; /* frame parsing */