h264parse: Fix multiple SEI messages in one SEI RBSP parsing.

An SEI RBSP could contains more than one SEI message as specified in
7.4.2.3.1.

This commit change the parser API: the gst_h264_parser_parse_sei()
function now create and fill a GArray containing GstH264SEIMessage.

https://bugzilla.gnome.org/show_bug.cgi?id=721715
This commit is contained in:
Aurélien Zanelli 2014-01-03 09:44:28 +01:00 committed by Sebastian Dröge
parent 24c87b18c2
commit af78b45979
3 changed files with 155 additions and 72 deletions

View file

@ -269,6 +269,21 @@ nal_reader_skip (NalReader * nr, guint nbits)
return TRUE;
}
static inline gboolean
nal_reader_skip_to_next_byte (NalReader * nr)
{
if (nr->bits_in_cache == 0) {
if (G_LIKELY ((nr->size - nr->byte) > 0))
nr->byte++;
else
return FALSE;
}
nr->bits_in_cache = 0;
return TRUE;
}
static inline guint
nal_reader_get_pos (const NalReader * nr)
{
@ -490,6 +505,14 @@ scan_for_start_codes (const guint8 * data, guint size)
0, size);
}
static gboolean
gst_h264_parser_byte_aligned (NalReader * nr)
{
if (nr->bits_in_cache != 0)
return FALSE;
return TRUE;
}
static gboolean
gst_h264_parser_more_data (NalReader * nr)
{
@ -1145,6 +1168,80 @@ error:
return GST_H264_PARSER_ERROR;
}
static GstH264ParserResult
gst_h264_parser_parse_sei_message (GstH264NalParser * nalparser,
NalReader * nr, GstH264SEIMessage * sei)
{
guint32 payloadSize;
guint8 payload_type_byte, payload_size_byte;
#ifndef GST_DISABLE_GST_DEBUG
guint remaining, payload_size;
#endif
GstH264ParserResult res;
GST_DEBUG ("parsing \"Sei message\"");
sei->payloadType = 0;
do {
READ_UINT8 (nr, payload_type_byte, 8);
sei->payloadType += payload_type_byte;
} while (payload_type_byte == 0xff);
payloadSize = 0;
do {
READ_UINT8 (nr, payload_size_byte, 8);
payloadSize += payload_size_byte;
}
while (payload_size_byte == 0xff);
#ifndef GST_DISABLE_GST_DEBUG
remaining = nal_reader_get_remaining (nr);
payload_size = payloadSize * 8 < remaining ? payloadSize * 8 : remaining;
GST_DEBUG ("SEI message received: payloadType %u, payloadSize = %u bits",
sei->payloadType, payload_size);
#endif
if (sei->payloadType == GST_H264_SEI_BUF_PERIOD) {
/* size not set; might depend on emulation_prevention_three_byte */
res = gst_h264_parser_parse_buffering_period (nalparser,
&sei->payload.buffering_period, nr);
} else if (sei->payloadType == GST_H264_SEI_PIC_TIMING) {
/* size not set; might depend on emulation_prevention_three_byte */
res = gst_h264_parser_parse_pic_timing (nalparser,
&sei->payload.pic_timing, nr);
} else {
/* Just consume payloadSize */
guint32 i;
for (i = 0; i < payloadSize; i++)
nal_reader_skip_to_next_byte (nr);
res = GST_H264_PARSER_OK;
}
/* When SEI message doesn't end at byte boundary,
* check remaining bits fit the specification.
*/
if (!gst_h264_parser_byte_aligned (nr)) {
guint8 bit_equal_to_one;
READ_UINT8 (nr, bit_equal_to_one, 1);
if (!bit_equal_to_one)
GST_WARNING ("Bit non equal to one.");
while (!gst_h264_parser_byte_aligned (nr)) {
guint8 bit_equal_to_zero;
READ_UINT8 (nr, bit_equal_to_zero, 1);
if (bit_equal_to_zero)
GST_WARNING ("Bit non equal to zero.");
}
}
return res;
error:
GST_WARNING ("error parsing \"Sei message\"");
return GST_H264_PARSER_ERROR;
}
/******** API *************/
/**
@ -1942,69 +2039,33 @@ error:
* gst_h264_parser_parse_sei:
* @nalparser: a #GstH264NalParser
* @nalu: The #GST_H264_NAL_SEI #GstH264NalUnit to parse
* @sei: The #GstH264SEIMessage to fill.
* @messages: The GArray of #GstH264SEIMessage to fill. The caller must free it when done.
*
* Parses @data, and fills the @sei structures.
* Parses @data, create and fills the @messages array.
*
* Returns: a #GstH264ParserResult
*/
GstH264ParserResult
gst_h264_parser_parse_sei (GstH264NalParser * nalparser, GstH264NalUnit * nalu,
GstH264SEIMessage * sei)
GArray ** messages)
{
NalReader nr;
guint32 payloadSize;
guint8 payload_type_byte, payload_size_byte;
#ifndef GST_DISABLE_GST_DEBUG
guint remaining, payload_size;
#endif
GstH264SEIMessage sei;
GstH264ParserResult res;
GST_DEBUG ("parsing \"Sei message\"");
GST_DEBUG ("parsing SEI nal");
nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1);
*messages = g_array_new (FALSE, FALSE, sizeof (GstH264SEIMessage));
/* init */
memset (sei, 0, sizeof (*sei));
sei->payloadType = 0;
do {
READ_UINT8 (&nr, payload_type_byte, 8);
sei->payloadType += payload_type_byte;
} while (payload_type_byte == 0xff);
payloadSize = 0;
do {
READ_UINT8 (&nr, payload_size_byte, 8);
payloadSize += payload_size_byte;
}
while (payload_size_byte == 0xff);
#ifndef GST_DISABLE_GST_DEBUG
remaining = nal_reader_get_remaining (&nr) * 8;
payload_size = payloadSize < remaining ? payloadSize : remaining;
GST_DEBUG ("SEI message received: payloadType %u, payloadSize = %u bytes",
sei->payloadType, payload_size);
#endif
if (sei->payloadType == GST_H264_SEI_BUF_PERIOD) {
/* size not set; might depend on emulation_prevention_three_byte */
res = gst_h264_parser_parse_buffering_period (nalparser,
&sei->payload.buffering_period, &nr);
} else if (sei->payloadType == GST_H264_SEI_PIC_TIMING) {
/* size not set; might depend on emulation_prevention_three_byte */
res = gst_h264_parser_parse_pic_timing (nalparser,
&sei->payload.pic_timing, &nr);
} else
res = GST_H264_PARSER_OK;
res = gst_h264_parser_parse_sei_message (nalparser, &nr, &sei);
if (res == GST_H264_PARSER_OK)
g_array_append_val (*messages, sei);
else
break;
} while (gst_h264_parser_more_data (&nr));
return res;
error:
GST_WARNING ("error parsing \"Sei message\"");
return GST_H264_PARSER_ERROR;
}
/**

View file

@ -749,7 +749,7 @@ GstH264ParserResult gst_h264_parser_parse_pps (GstH264NalParser *nalpars
GstH264NalUnit *nalu, GstH264PPS *pps);
GstH264ParserResult gst_h264_parser_parse_sei (GstH264NalParser *nalparser,
GstH264NalUnit *nalu, GstH264SEIMessage *sei);
GstH264NalUnit *nalu, GArray ** messages);
void gst_h264_nal_parser_free (GstH264NalParser *nalparser);

View file

@ -460,6 +460,50 @@ _nal_name (GstH264NalUnitType nal_type)
}
#endif
static void
gst_h264_parse_process_sei (GstH264Parse * h264parse, GstH264NalUnit * nalu)
{
GstH264SEIMessage sei;
GstH264NalParser *nalparser = h264parse->nalparser;
GstH264ParserResult pres;
GArray *messages;
guint i;
pres = gst_h264_parser_parse_sei (nalparser, nalu, &messages);
if (pres != GST_H264_PARSER_OK)
GST_WARNING_OBJECT (h264parse, "failed to parse one ore more SEI message");
/* Even if pres != GST_H264_PARSER_OK, some message could have been parsed and
* stored in messages.
*/
for (i = 0; i < messages->len; i++) {
sei = g_array_index (messages, GstH264SEIMessage, i);
switch (sei.payloadType) {
case GST_H264_SEI_PIC_TIMING:
h264parse->sei_pic_struct_pres_flag =
sei.payload.pic_timing.pic_struct_present_flag;
h264parse->sei_cpb_removal_delay =
sei.payload.pic_timing.cpb_removal_delay;
if (h264parse->sei_pic_struct_pres_flag)
h264parse->sei_pic_struct = sei.payload.pic_timing.pic_struct;
GST_LOG_OBJECT (h264parse, "pic timing updated");
break;
case GST_H264_SEI_BUF_PERIOD:
if (h264parse->ts_trn_nb == GST_CLOCK_TIME_NONE ||
h264parse->dts == GST_CLOCK_TIME_NONE)
h264parse->ts_trn_nb = 0;
else
h264parse->ts_trn_nb = h264parse->dts;
GST_LOG_OBJECT (h264parse,
"new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT,
GST_TIME_ARGS (h264parse->ts_trn_nb));
break;
}
}
g_array_free (messages, TRUE);
}
/* caller guarantees 2 bytes of nal payload */
static void
gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu)
@ -467,7 +511,6 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu)
guint nal_type;
GstH264PPS pps = { 0, };
GstH264SPS sps = { 0, };
GstH264SEIMessage sei;
GstH264NalParser *nalparser = h264parse->nalparser;
GstH264ParserResult pres;
@ -528,28 +571,7 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu)
gst_h264_parser_store_nal (h264parse, pps.id, nal_type, nalu);
break;
case GST_H264_NAL_SEI:
gst_h264_parser_parse_sei (nalparser, nalu, &sei);
switch (sei.payloadType) {
case GST_H264_SEI_PIC_TIMING:
h264parse->sei_pic_struct_pres_flag =
sei.payload.pic_timing.pic_struct_present_flag;
h264parse->sei_cpb_removal_delay =
sei.payload.pic_timing.cpb_removal_delay;
if (h264parse->sei_pic_struct_pres_flag)
h264parse->sei_pic_struct = sei.payload.pic_timing.pic_struct;
break;
case GST_H264_SEI_BUF_PERIOD:
if (h264parse->ts_trn_nb == GST_CLOCK_TIME_NONE ||
h264parse->dts == GST_CLOCK_TIME_NONE)
h264parse->ts_trn_nb = 0;
else
h264parse->ts_trn_nb = h264parse->dts;
GST_LOG_OBJECT (h264parse,
"new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT,
GST_TIME_ARGS (h264parse->ts_trn_nb));
break;
}
gst_h264_parse_process_sei (h264parse, nalu);
/* mark SEI pos */
if (h264parse->sei_pos == -1) {
if (h264parse->transform)