mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
h264parser: Add support for creating picture timing SEI
This new method can make it possible to inject timecode meta into h264 bitstream
This commit is contained in:
parent
72854261bb
commit
db1ea18276
2 changed files with 255 additions and 1 deletions
|
@ -2729,6 +2729,70 @@ error:
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_h264_write_sei_pic_timing (NalWriter * nw, GstH264PicTiming * tim)
|
||||
{
|
||||
if (tim->CpbDpbDelaysPresentFlag) {
|
||||
WRITE_UINT32 (nw, tim->cpb_removal_delay,
|
||||
tim->cpb_removal_delay_length_minus1 + 1);
|
||||
WRITE_UINT32 (nw, tim->dpb_output_delay,
|
||||
tim->dpb_output_delay_length_minus1 + 1);
|
||||
}
|
||||
|
||||
if (tim->pic_struct_present_flag) {
|
||||
const guint8 num_clock_ts_table[9] = {
|
||||
1, 1, 1, 2, 2, 3, 3, 2, 3
|
||||
};
|
||||
guint8 num_clock_num_ts;
|
||||
guint i;
|
||||
|
||||
WRITE_UINT8 (nw, tim->pic_struct, 4);
|
||||
|
||||
num_clock_num_ts = num_clock_ts_table[tim->pic_struct];
|
||||
for (i = 0; i < num_clock_num_ts; i++) {
|
||||
WRITE_UINT8 (nw, tim->clock_timestamp_flag[i], 1);
|
||||
if (tim->clock_timestamp_flag[i]) {
|
||||
GstH264ClockTimestamp *timestamp = &tim->clock_timestamp[i];
|
||||
|
||||
WRITE_UINT8 (nw, timestamp->ct_type, 2);
|
||||
WRITE_UINT8 (nw, timestamp->nuit_field_based_flag, 1);
|
||||
WRITE_UINT8 (nw, timestamp->counting_type, 5);
|
||||
WRITE_UINT8 (nw, timestamp->full_timestamp_flag, 1);
|
||||
WRITE_UINT8 (nw, timestamp->discontinuity_flag, 1);
|
||||
WRITE_UINT8 (nw, timestamp->cnt_dropped_flag, 1);
|
||||
WRITE_UINT8 (nw, timestamp->n_frames, 8);
|
||||
|
||||
if (timestamp->full_timestamp_flag) {
|
||||
WRITE_UINT8 (nw, timestamp->seconds_value, 6);
|
||||
WRITE_UINT8 (nw, timestamp->minutes_value, 6);
|
||||
WRITE_UINT8 (nw, timestamp->hours_value, 5);
|
||||
} else {
|
||||
WRITE_UINT8 (nw, timestamp->seconds_flag, 1);
|
||||
if (timestamp->seconds_flag) {
|
||||
WRITE_UINT8 (nw, timestamp->seconds_value, 6);
|
||||
WRITE_UINT8 (nw, timestamp->minutes_flag, 1);
|
||||
if (timestamp->minutes_flag) {
|
||||
WRITE_UINT8 (nw, timestamp->minutes_value, 6);
|
||||
WRITE_UINT8 (nw, timestamp->hours_flag, 1);
|
||||
if (timestamp->hours_flag)
|
||||
WRITE_UINT8 (nw, timestamp->hours_value, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tim->time_offset_length > 0) {
|
||||
WRITE_UINT32 (nw, timestamp->time_offset, tim->time_offset_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GstMemory *
|
||||
gst_h264_create_sei_memory_internal (guint8 nal_prefix_size,
|
||||
gboolean packetized, GArray * messages)
|
||||
|
@ -2844,6 +2908,89 @@ gst_h264_create_sei_memory_internal (guint8 nal_prefix_size,
|
|||
*/
|
||||
payload_size_data = 4;
|
||||
break;
|
||||
case GST_H264_SEI_PIC_TIMING:{
|
||||
GstH264PicTiming *tim = &msg->payload.pic_timing;
|
||||
const guint8 num_clock_ts_table[9] = {
|
||||
1, 1, 1, 2, 2, 3, 3, 2, 3
|
||||
};
|
||||
guint8 num_clock_num_ts;
|
||||
guint i;
|
||||
|
||||
if (!tim->CpbDpbDelaysPresentFlag && !tim->pic_struct_present_flag) {
|
||||
GST_WARNING
|
||||
("Both CpbDpbDelaysPresentFlag and pic_struct_present_flag are zero");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tim->CpbDpbDelaysPresentFlag) {
|
||||
payload_size_in_bits = tim->cpb_removal_delay_length_minus1 + 1;
|
||||
payload_size_in_bits += tim->dpb_output_delay_length_minus1 + 1;
|
||||
}
|
||||
|
||||
if (tim->pic_struct_present_flag) {
|
||||
/* pic_struct: 4bits */
|
||||
payload_size_in_bits += 4;
|
||||
|
||||
num_clock_num_ts = num_clock_ts_table[tim->pic_struct];
|
||||
for (i = 0; i < num_clock_num_ts; i++) {
|
||||
/* clock_timestamp_flag: 1bit */
|
||||
payload_size_in_bits++;
|
||||
|
||||
if (tim->clock_timestamp_flag[i]) {
|
||||
GstH264ClockTimestamp *timestamp = &tim->clock_timestamp[i];
|
||||
|
||||
/* ct_type: 2bits
|
||||
* nuit_field_based_flag: 1bit
|
||||
* counting_type: 5bits
|
||||
* full_timestamp_flag: 1bit
|
||||
* discontinuity_flag: 1bit
|
||||
* cnt_dropped_flag: 1bit
|
||||
* n_frames: 8bits
|
||||
*/
|
||||
payload_size_in_bits += 19;
|
||||
if (timestamp->full_timestamp_flag) {
|
||||
/* seconds_value: 6bits
|
||||
* minutes_value: 6bits
|
||||
* hours_value: 5bits
|
||||
*/
|
||||
payload_size_in_bits += 17;
|
||||
} else {
|
||||
/* seconds_flag: 1bit */
|
||||
payload_size_in_bits++;
|
||||
|
||||
if (timestamp->seconds_flag) {
|
||||
/* seconds_value: 6bits
|
||||
* minutes_flag: 1bit
|
||||
*/
|
||||
payload_size_in_bits += 7;
|
||||
if (timestamp->minutes_flag) {
|
||||
/* minutes_value: 6bits
|
||||
* hours_flag: 1bits
|
||||
*/
|
||||
payload_size_in_bits += 7;
|
||||
if (timestamp->hours_flag) {
|
||||
/* hours_value: 5bits */
|
||||
payload_size_in_bits += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* time_offset_length bits */
|
||||
payload_size_in_bits += tim->time_offset_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payload_size_data = payload_size_in_bits >> 3;
|
||||
|
||||
if ((payload_size_in_bits & 0x7) != 0) {
|
||||
GST_INFO ("Bits for Picture Timing SEI is not byte aligned");
|
||||
payload_size_data++;
|
||||
need_align = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2904,6 +3051,14 @@ gst_h264_create_sei_memory_internal (guint8 nal_prefix_size,
|
|||
}
|
||||
have_written_data = TRUE;
|
||||
break;
|
||||
case GST_H264_SEI_PIC_TIMING:
|
||||
GST_DEBUG ("Writing \"Picture timing\"");
|
||||
if (!gst_h264_write_sei_pic_timing (&nw, &msg->payload.pic_timing)) {
|
||||
GST_WARNING ("Failed to write \"Picture timing\"");
|
||||
goto error;
|
||||
}
|
||||
have_written_data = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@ static guint8 nalu_sps_with_vui[] = {
|
|||
};
|
||||
|
||||
static guint8 nalu_sei_pic_timing[] = {
|
||||
0x00, 0x00, 0x01, 0x06, 0x01, 0x01, 0x32, 0x80
|
||||
0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x01, 0x32, 0x80
|
||||
};
|
||||
|
||||
static guint8 nalu_chained_sei[] = {
|
||||
|
@ -429,6 +429,91 @@ check_sei_cll (const GstH264ContentLightLevel * a,
|
|||
(a->max_pic_average_light_level == b->max_pic_average_light_level);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_sei_pic_timing (const GstH264PicTiming * a, const GstH264PicTiming * b)
|
||||
{
|
||||
if (a->CpbDpbDelaysPresentFlag != b->CpbDpbDelaysPresentFlag)
|
||||
return FALSE;
|
||||
|
||||
if (a->CpbDpbDelaysPresentFlag) {
|
||||
if (a->cpb_removal_delay != b->cpb_removal_delay ||
|
||||
a->cpb_removal_delay_length_minus1 != b->cpb_removal_delay_length_minus1
|
||||
|| a->dpb_output_delay != b->dpb_output_delay
|
||||
|| a->dpb_output_delay_length_minus1 !=
|
||||
b->dpb_output_delay_length_minus1)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (a->pic_struct_present_flag != b->pic_struct_present_flag)
|
||||
return FALSE;
|
||||
|
||||
if (a->pic_struct_present_flag) {
|
||||
const guint8 num_clock_ts_table[9] = {
|
||||
1, 1, 1, 2, 2, 3, 3, 2, 3
|
||||
};
|
||||
guint8 num_clock_num_ts;
|
||||
guint i;
|
||||
|
||||
if (a->pic_struct != b->pic_struct)
|
||||
return FALSE;
|
||||
|
||||
if (a->time_offset_length != b->time_offset_length)
|
||||
return FALSE;
|
||||
|
||||
num_clock_num_ts = num_clock_ts_table[a->pic_struct];
|
||||
|
||||
for (i = 0; i < num_clock_num_ts; i++) {
|
||||
if (a->clock_timestamp_flag[i] != b->clock_timestamp_flag[i])
|
||||
return FALSE;
|
||||
|
||||
if (a->clock_timestamp_flag[i]) {
|
||||
const GstH264ClockTimestamp *ta = &a->clock_timestamp[i];
|
||||
const GstH264ClockTimestamp *tb = &b->clock_timestamp[i];
|
||||
|
||||
if (ta->ct_type != tb->ct_type ||
|
||||
ta->nuit_field_based_flag != tb->nuit_field_based_flag ||
|
||||
ta->counting_type != tb->counting_type ||
|
||||
ta->discontinuity_flag != tb->discontinuity_flag ||
|
||||
ta->cnt_dropped_flag != tb->cnt_dropped_flag ||
|
||||
ta->n_frames != tb->n_frames)
|
||||
return FALSE;
|
||||
|
||||
if (ta->full_timestamp_flag) {
|
||||
if (ta->seconds_value != tb->seconds_value ||
|
||||
ta->minutes_value != tb->minutes_value ||
|
||||
ta->hours_value != tb->hours_value)
|
||||
return FALSE;
|
||||
} else {
|
||||
if (ta->seconds_flag != tb->seconds_flag)
|
||||
return FALSE;
|
||||
|
||||
if (ta->seconds_flag) {
|
||||
if (ta->seconds_value != tb->seconds_value ||
|
||||
ta->minutes_flag != tb->minutes_flag)
|
||||
return FALSE;
|
||||
|
||||
if (ta->minutes_flag) {
|
||||
if (ta->minutes_value != tb->minutes_value ||
|
||||
ta->hours_flag != tb->hours_flag)
|
||||
return FALSE;
|
||||
|
||||
if (ta->hours_flag) {
|
||||
if (ta->hours_value != tb->hours_value)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ta->time_offset != tb->time_offset)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_START_TEST (test_h264_create_sei)
|
||||
{
|
||||
GstH264NalParser *parser;
|
||||
|
@ -459,11 +544,25 @@ GST_START_TEST (test_h264_create_sei)
|
|||
{h264_sei_cll, G_N_ELEMENTS (h264_sei_cll),
|
||||
GST_H264_SEI_CONTENT_LIGHT_LEVEL, {0,},
|
||||
(SEICheckFunc) check_sei_cll},
|
||||
{nalu_sei_pic_timing, G_N_ELEMENTS (nalu_sei_pic_timing),
|
||||
GST_H264_SEI_PIC_TIMING, {0,},
|
||||
(SEICheckFunc) check_sei_pic_timing},
|
||||
/* *INDENT-ON* */
|
||||
};
|
||||
|
||||
parser = gst_h264_nal_parser_new ();
|
||||
|
||||
/* inject SPS for picture timing sei */
|
||||
parse_ret =
|
||||
gst_h264_parser_identify_nalu_unchecked (parser, nalu_sps_with_vui, 0,
|
||||
sizeof (nalu_sps_with_vui), &nalu);
|
||||
assert_equals_int (parse_ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (nalu.type, GST_H264_NAL_SPS);
|
||||
assert_equals_int (nalu.size, 28);
|
||||
|
||||
parse_ret = gst_h264_parser_parse_nal (parser, &nalu);
|
||||
assert_equals_int (parse_ret, GST_H264_PARSER_OK);
|
||||
|
||||
/* test single sei message per sei nal unit */
|
||||
for (i = 0; i < G_N_ELEMENTS (test_list); i++) {
|
||||
gsize nal_size;
|
||||
|
|
Loading…
Reference in a new issue