mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
h265parse: forward time codes
This transforms time code SEIs into GstVideoTimeCodeMeta
This commit is contained in:
parent
7c425cf339
commit
55bb8966e1
4 changed files with 186 additions and 4 deletions
|
@ -1124,6 +1124,64 @@ error:
|
||||||
return GST_H265_PARSER_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 *************/
|
/******** API *************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2312,6 +2370,10 @@ gst_h265_parser_parse_sei_message (GstH265Parser * parser,
|
||||||
res = gst_h265_parser_parse_recovery_point (parser,
|
res = gst_h265_parser_parse_recovery_point (parser,
|
||||||
&sei->payload.recovery_point, nr);
|
&sei->payload.recovery_point, nr);
|
||||||
break;
|
break;
|
||||||
|
case GST_H265_SEI_TIME_CODE:
|
||||||
|
res = gst_h265_parser_parse_time_code (parser,
|
||||||
|
&sei->payload.time_code, nr);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* Just consume payloadSize bytes, which does not account for
|
/* Just consume payloadSize bytes, which does not account for
|
||||||
emulation prevention bytes */
|
emulation prevention bytes */
|
||||||
|
|
|
@ -221,6 +221,7 @@ typedef enum
|
||||||
* @GST_H265_SEI_BUF_PERIOD: Buffering Period SEI Message
|
* @GST_H265_SEI_BUF_PERIOD: Buffering Period SEI Message
|
||||||
* @GST_H265_SEI_PIC_TIMING: Picture Timing 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_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.
|
* The type of SEI message.
|
||||||
|
@ -230,6 +231,7 @@ typedef enum
|
||||||
GST_H265_SEI_BUF_PERIOD = 0,
|
GST_H265_SEI_BUF_PERIOD = 0,
|
||||||
GST_H265_SEI_PIC_TIMING = 1,
|
GST_H265_SEI_PIC_TIMING = 1,
|
||||||
GST_H265_SEI_RECOVERY_POINT = 6,
|
GST_H265_SEI_RECOVERY_POINT = 6,
|
||||||
|
GST_H265_SEI_TIME_CODE = 136,
|
||||||
/* and more... */
|
/* and more... */
|
||||||
} GstH265SEIPayloadType;
|
} GstH265SEIPayloadType;
|
||||||
|
|
||||||
|
@ -316,6 +318,7 @@ typedef struct _GstH265SliceHdr GstH265SliceHdr;
|
||||||
typedef struct _GstH265PicTiming GstH265PicTiming;
|
typedef struct _GstH265PicTiming GstH265PicTiming;
|
||||||
typedef struct _GstH265BufferingPeriod GstH265BufferingPeriod;
|
typedef struct _GstH265BufferingPeriod GstH265BufferingPeriod;
|
||||||
typedef struct _GstH265RecoveryPoint GstH265RecoveryPoint;
|
typedef struct _GstH265RecoveryPoint GstH265RecoveryPoint;
|
||||||
|
typedef struct _GstH265TimeCode GstH265TimeCode;
|
||||||
typedef struct _GstH265SEIMessage GstH265SEIMessage;
|
typedef struct _GstH265SEIMessage GstH265SEIMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1073,6 +1076,36 @@ struct _GstH265RecoveryPoint
|
||||||
guint8 broken_link_flag;
|
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
|
struct _GstH265SEIMessage
|
||||||
{
|
{
|
||||||
GstH265SEIPayloadType payloadType;
|
GstH265SEIPayloadType payloadType;
|
||||||
|
@ -1081,6 +1114,7 @@ struct _GstH265SEIMessage
|
||||||
GstH265BufferingPeriod buffering_period;
|
GstH265BufferingPeriod buffering_period;
|
||||||
GstH265PicTiming pic_timing;
|
GstH265PicTiming pic_timing;
|
||||||
GstH265RecoveryPoint recovery_point;
|
GstH265RecoveryPoint recovery_point;
|
||||||
|
GstH265TimeCode time_code;
|
||||||
/* ... could implement more */
|
/* ... could implement more */
|
||||||
} payload;
|
} payload;
|
||||||
};
|
};
|
||||||
|
|
|
@ -544,9 +544,14 @@ gst_h265_parse_process_sei (GstH265Parse * h265parse, GstH265NalUnit * nalu)
|
||||||
sei.payload.recovery_point.broken_link_flag);
|
sei.payload.recovery_point.broken_link_flag);
|
||||||
h265parse->keyframe = TRUE;
|
h265parse->keyframe = TRUE;
|
||||||
break;
|
break;
|
||||||
|
case GST_H265_SEI_TIME_CODE:
|
||||||
case GST_H265_SEI_BUF_PERIOD:
|
memcpy (&h265parse->time_code, &sei.payload.time_code,
|
||||||
|
sizeof (GstH265TimeCode));
|
||||||
|
break;
|
||||||
case GST_H265_SEI_PIC_TIMING:
|
case GST_H265_SEI_PIC_TIMING:
|
||||||
|
h265parse->sei_pic_struct = sei.payload.pic_timing.pic_struct;
|
||||||
|
break;
|
||||||
|
case GST_H265_SEI_BUF_PERIOD:
|
||||||
/* FIXME */
|
/* FIXME */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1245,8 +1250,8 @@ gst_h265_parse_make_codec_data (GstH265Parse * h265parse)
|
||||||
}
|
}
|
||||||
data[6] |=
|
data[6] |=
|
||||||
(pft->progressive_source_flag << 7) | (pft->interlaced_source_flag << 6) |
|
(pft->progressive_source_flag << 7) | (pft->interlaced_source_flag << 6) |
|
||||||
(pft->non_packed_constraint_flag << 5) | (pft->
|
(pft->
|
||||||
frame_only_constraint_flag << 4);
|
non_packed_constraint_flag << 5) | (pft->frame_only_constraint_flag << 4);
|
||||||
data[12] = pft->level_idc;
|
data[12] = pft->level_idc;
|
||||||
/* min_spatial_segmentation_idc */
|
/* min_spatial_segmentation_idc */
|
||||||
GST_WRITE_UINT16_BE (data + 13, 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 */
|
/* but not necessarily or reliably this */
|
||||||
if (fps_num > 0 && fps_den > 0) {
|
if (fps_num > 0 && fps_den > 0) {
|
||||||
|
GstStructure *s2;
|
||||||
GST_INFO_OBJECT (h265parse, "setting framerate in caps");
|
GST_INFO_OBJECT (h265parse, "setting framerate in caps");
|
||||||
gst_caps_set_simple (caps, "framerate",
|
gst_caps_set_simple (caps, "framerate",
|
||||||
GST_TYPE_FRACTION, fps_num, fps_den, NULL);
|
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),
|
gst_base_parse_set_frame_rate (GST_BASE_PARSE (h265parse),
|
||||||
fps_num, fps_den, 0, 0);
|
fps_num, fps_den, 0, 0);
|
||||||
latency = gst_util_uint64_scale (GST_SECOND, fps_den, fps_num);
|
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);
|
gst_h265_parse_reset_frame (h265parse);
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
|
@ -52,6 +52,7 @@ struct _GstH265Parse
|
||||||
gint fps_num, fps_den;
|
gint fps_num, fps_den;
|
||||||
gint upstream_par_n, upstream_par_d;
|
gint upstream_par_n, upstream_par_d;
|
||||||
gint parsed_par_n, parsed_par_d;
|
gint parsed_par_n, parsed_par_d;
|
||||||
|
gint parsed_fps_n, parsed_fps_d;
|
||||||
/* current codec_data in output caps, if any */
|
/* current codec_data in output caps, if any */
|
||||||
GstBuffer *codec_data;
|
GstBuffer *codec_data;
|
||||||
/* input codec_data, if any */
|
/* input codec_data, if any */
|
||||||
|
@ -86,6 +87,12 @@ struct _GstH265Parse
|
||||||
GstBuffer *sps_nals[GST_H265_MAX_SPS_COUNT];
|
GstBuffer *sps_nals[GST_H265_MAX_SPS_COUNT];
|
||||||
GstBuffer *pps_nals[GST_H265_MAX_PPS_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;
|
gboolean discont;
|
||||||
|
|
||||||
/* frame parsing */
|
/* frame parsing */
|
||||||
|
|
Loading…
Reference in a new issue