h264parse: Port to the new h.264 parsing library

This commit is contained in:
Thibault Saunier 2011-08-02 12:37:02 +02:00 committed by Edward Hervey
parent a1cfba43f6
commit b968f82b18
5 changed files with 459 additions and 1421 deletions

View file

@ -2,9 +2,8 @@ plugin_LTLIBRARIES = libgstvideoparsersbad.la
libgstvideoparsersbad_la_SOURCES = plugin.c \
h263parse.c gsth263parse.c \
gsth264parse.c h264parse.c \
gstdiracparse.c dirac_parse.c \
gstmpegvideoparse.c
gsth264parse.c gstmpegvideoparse.c
libgstvideoparsersbad_la_CFLAGS = \
$(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
@ -16,9 +15,8 @@ libgstvideoparsersbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gsth263parse.h h263parse.h \
gsth264parse.h h264parse.h \
gstdiracparse.h dirac_parse.h \
gstmpegvideoparse.h
gsth264parse.h gstmpegvideoparse.h
Android.mk: Makefile.am $(BUILT_SOURCES)
androgenizer \

View file

@ -1,7 +1,10 @@
/* GStreamer H.264 Parser
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* Copyright (C) <2010> Collabora Multimedia
* Copyright (C) <2010> Collabora ltd
* Copyright (C) <2010> Nokia Corporation
* Copyright (C) <2011> Intel Corporation
*
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* Copyright (C) <2011> Thibault Saunier <thibault.saunier@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -163,9 +166,14 @@ gst_h264_parse_finalize (GObject * object)
static void
gst_h264_parse_reset_frame (GstH264Parse * h264parse)
{
GST_DEBUG_OBJECT (h264parse, "reset frame");
/* done parsing; reset state */
h264parse->last_nal_pos = 0;
h264parse->next_sc_pos = 0;
h264parse->nalu.valid = FALSE;
h264parse->nalu.offset = 0;
h264parse->nalu.size = 0;
h264parse->current_off = 0;
h264parse->picture_start = FALSE;
h264parse->update_caps = FALSE;
h264parse->idr_pos = -1;
@ -202,7 +210,13 @@ gst_h264_parse_start (GstBaseParse * parse)
GST_DEBUG_OBJECT (parse, "start");
gst_h264_parse_reset (h264parse);
gst_h264_params_create (&h264parse->params, GST_ELEMENT (h264parse));
h264parse->nalparser = gst_h264_nal_parser_new ();
h264parse->dts = GST_CLOCK_TIME_NONE;
h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE;
h264parse->sei_pic_struct_pres_flag = FALSE;
h264parse->sei_pic_struct = 0;
h264parse->field_pic_flag = 0;
gst_base_parse_set_min_frame_size (parse, 6);
@ -212,13 +226,19 @@ gst_h264_parse_start (GstBaseParse * parse)
static gboolean
gst_h264_parse_stop (GstBaseParse * parse)
{
guint i;
GstH264Parse *h264parse = GST_H264_PARSE (parse);
GST_DEBUG_OBJECT (parse, "stop");
gst_h264_parse_reset (h264parse);
gst_h264_params_free (h264parse->params);
h264parse->params = NULL;
for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++)
gst_buffer_replace (&h264parse->sps_nals[i], NULL);
for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++)
gst_buffer_replace (&h264parse->pps_nals[i], NULL);
g_free (h264parse->nalparser);
h264parse->nalparser = NULL;
return TRUE;
}
@ -318,6 +338,8 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data,
GstBuffer *buf;
const guint nl = h264parse->nal_length_size;
GST_DEBUG ("Nal length %d %d", size, h264parse->nal_length_size);
buf = gst_buffer_new_and_alloc (size + nl + 4);
if (format == GST_H264_PARSE_FORMAT_AVC) {
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl));
@ -332,65 +354,158 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data,
return buf;
}
static void
gst_h264_parser_store_nal (GstH264Parse * h264parse, guint id,
GstH264NalUnitType naltype, GstH264NalUnit * nalu)
{
GstBuffer *buf, **store;
guint size = nalu->size, store_size;
if (naltype == GST_H264_NAL_SPS) {
store_size = GST_H264_MAX_SPS_COUNT;
store = h264parse->sps_nals;
GST_DEBUG ("Storing sps %u", id);
} else if (naltype == GST_H264_NAL_PPS) {
store_size = GST_H264_MAX_PPS_COUNT;
store = h264parse->pps_nals;
GST_DEBUG ("Storing pps %u", id);
} else
return;
if (id >= store_size) {
GST_DEBUG_OBJECT (h264parse, "unable to store nal, id out-of-range %d", id);
return;
}
buf = gst_buffer_new_and_alloc (size);
memcpy (GST_BUFFER_DATA (buf), nalu->data + nalu->offset, size);
if (store[id])
gst_buffer_unref (store[id]);
store[id] = buf;
}
/* SPS/PPS/IDR considered key, all others DELTA;
* so downstream waiting for keyframe can pick up at SPS/PPS/IDR */
#define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8))
/* caller guarantees 2 bytes of nal payload */
static void
gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data,
gint sc_pos, gint nal_pos, guint nal_size)
gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu)
{
guint nal_type;
GstH264SliceHdr slice;
GstH264PPS pps;
GstH264SPS sps;
GstH264SEIMessage sei;
gboolean slcparsed = FALSE;
GstH264NalParser *nalparser = h264parse->nalparser;
g_return_if_fail (nal_pos - sc_pos > 0 && nal_pos - sc_pos <= 4);
/* nothing to do for broken input */
if (G_UNLIKELY (nal_size < 2)) {
GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nal_size);
if (G_UNLIKELY (nalu->size < 2)) {
GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nalu->size);
return;
}
/* lower layer collects params */
gst_h264_params_parse_nal (h264parse->params, data + nal_pos, nal_size);
/* we have a peek as well */
nal_type = data[nal_pos] & 0x1f;
nal_type = nalu->type;
h264parse->keyframe |= NAL_TYPE_IS_KEY (nal_type);
GST_DEBUG_OBJECT (h264parse, "processing nal of type %u, size %u",
nal_type, nalu->size);
switch (nal_type) {
case NAL_SPS:
case NAL_PPS:
case GST_H264_NAL_SPS:
gst_h264_parser_parse_sps (nalparser, nalu, &sps, TRUE);
GST_DEBUG_OBJECT (h264parse, "triggering src caps check");
h264parse->update_caps = TRUE;
/* found in stream, no need to forcibly push at start */
h264parse->push_codec = FALSE;
gst_h264_parser_store_nal (h264parse, sps.id, nal_type, nalu);
break;
case GST_H264_NAL_PPS:
gst_h264_parser_parse_pps (nalparser, nalu, &pps);
/* parameters might have changed, force caps check */
GST_DEBUG_OBJECT (h264parse, "triggering src caps check");
h264parse->update_caps = TRUE;
/* found in stream, no need to forcibly push at start */
h264parse->push_codec = FALSE;
gst_h264_parser_store_nal (h264parse, pps.id, nal_type, nalu);
break;
case NAL_SLICE:
case NAL_SLICE_DPA:
case NAL_SLICE_DPB:
case NAL_SLICE_DPC:
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.pic_timing.pic_struct_present_flag;
h264parse->sei_cpb_removal_delay = sei.pic_timing.cpb_removal_delay;
if (h264parse->sei_pic_struct_pres_flag)
h264parse->sei_pic_struct = sei.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;
}
break;
case GST_H264_NAL_SLICE:
case GST_H264_NAL_SLICE_DPA:
case GST_H264_NAL_SLICE_DPB:
case GST_H264_NAL_SLICE_DPC:
slcparsed = TRUE;
if (gst_h264_parser_parse_slice_hdr (nalparser, nalu,
&slice, FALSE, FALSE) == GST_H264_PARSER_ERROR)
return;
/* real frame data */
h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0);
h264parse->frame_start |= (slice.first_mb_in_slice == 0);
/* if we need to sneak codec NALs into the stream,
* this is a good place, so fake it as IDR
* (which should be at start anyway) */
GST_DEBUG ("Frame start: %i first_mb_in_slice %i", h264parse->frame_start,
slice.first_mb_in_slice);
if (G_LIKELY (!h264parse->push_codec))
break;
/* fall-through */
case NAL_SLICE_IDR:
case GST_H264_NAL_SLICE_IDR:
if (!slcparsed) {
if (gst_h264_parser_parse_slice_hdr (nalparser, nalu,
&slice, FALSE, FALSE) == GST_H264_PARSER_ERROR)
return;
GST_DEBUG ("Frame start: %i first_mb_in_slice %i",
h264parse->frame_start, slice.first_mb_in_slice);
}
/* real frame data */
h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0);
h264parse->frame_start |= (slice.first_mb_in_slice == 0);
/* mark where config needs to go if interval expired */
/* mind replacement buffer if applicable */
if (h264parse->format == GST_H264_PARSE_FORMAT_AVC)
h264parse->idr_pos = gst_adapter_available (h264parse->frame_out);
else
h264parse->idr_pos = sc_pos;
h264parse->idr_pos = nalu->offset - 4;
GST_DEBUG_OBJECT (h264parse, "marking IDR in frame at offset %d",
h264parse->idr_pos);
GST_DEBUG ("first MB: %u, slice type: %u", slice.first_mb_in_slice,
slice.type);
break;
default:
gst_h264_parser_parse_nal (nalparser, nalu);
}
/* if AVC output needed, collect properly prefixed nal in adapter,
@ -400,7 +515,7 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data,
GST_LOG_OBJECT (h264parse, "collecting NAL in AVC frame");
buf = gst_h264_parse_wrap_nal (h264parse, h264parse->format,
data + nal_pos, nal_size);
nalu->data + nalu->offset, nalu->size);
gst_adapter_push (h264parse->frame_out, buf);
}
}
@ -408,21 +523,31 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data,
/* caller guarantees at least 2 bytes of nal payload for each nal
* returns TRUE if next_nal indicates that nal terminates an AU */
static inline gboolean
gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal,
guint8 * next_nal)
gst_h264_parse_collect_nal (GstH264Parse * h264parse, const guint8 * data,
guint size, GstH264NalUnit * nalu)
{
gint nal_type;
gboolean complete;
GstH264ParserResult parse_res;
GstH264NalUnitType nal_type = nalu->type;
GstH264NalUnit nnalu;
if (h264parse->align == GST_H264_PARSE_ALIGN_NAL)
GST_DEBUG ("Parsing collecte nal");
parse_res = gst_h264_parser_identify_nalu (h264parse->nalparser, data,
nalu->offset + nalu->size, size, &nnalu);
if (parse_res == GST_H264_PARSER_ERROR)
return FALSE;
if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) {
return TRUE;
}
/* determine if AU complete */
nal_type = nal[0] & 0x1f;
GST_LOG_OBJECT (h264parse, "nal type: %d", nal_type);
/* coded slice NAL starts a picture,
* i.e. other types become aggregated in front of it */
h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5);
h264parse->picture_start |= (nal_type == GST_H264_NAL_SLICE ||
nal_type == GST_H264_NAL_SLICE_DPA || nal_type == GST_H264_NAL_SLICE_IDR);
/* consider a coded slices (IDR or not) to start a picture,
* (so ending the previous one) if first_mb_in_slice == 0
@ -431,35 +556,23 @@ gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal,
* but in practice it works in sane cases, needs not much parsing,
* and also works with broken frame_num in NAL
* (where spec-wise would fail) */
nal_type = next_nal[0] & 0x1f;
nal_type = nnalu.type;
complete = h264parse->picture_start && (nal_type >= GST_H264_NAL_SEI &&
nal_type <= GST_H264_NAL_AU_DELIMITER);
GST_LOG_OBJECT (h264parse, "next nal type: %d", nal_type);
complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9);
complete |= h264parse->picture_start &&
(nal_type == 1 || nal_type == 2 || nal_type == 5) &&
(nal_type == GST_H264_NAL_SLICE ||
nal_type == GST_H264_NAL_SLICE_DPA ||
nal_type == GST_H264_NAL_SLICE_IDR) &&
/* first_mb_in_slice == 0 considered start of frame */
(next_nal[1] & 0x80);
(nnalu.data[nnalu.offset + 1] & 0x80);
GST_LOG_OBJECT (h264parse, "au complete: %d", complete);
return complete;
}
/* finds next startcode == 00 00 01, along with a subsequent byte */
static guint
gst_h264_parse_find_sc (GstBuffer * buffer, guint skip)
{
GstByteReader br;
guint sc_pos = -1;
gst_byte_reader_init_from_buffer (&br, buffer);
/* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */
sc_pos = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100,
skip, gst_byte_reader_get_remaining (&br) - skip);
return sc_pos;
}
/* FIXME move into baseparse, or anything equivalent;
* see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */
#define GST_BASE_PARSE_FRAME_FLAG_PARSING 0x10000
@ -470,10 +583,11 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse,
{
GstH264Parse *h264parse = GST_H264_PARSE (parse);
GstBuffer *buffer = frame->buffer;
gint sc_pos, nal_pos, next_sc_pos, next_nal_pos;
guint8 *data;
guint size;
guint size, current_off = 0;
gboolean drain;
GstH264NalParser *nalparser = h264parse->nalparser;
GstH264NalUnit nalu = h264parse->nalu;
/* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */
if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5))
@ -495,105 +609,108 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse,
data = GST_BUFFER_DATA (buffer);
size = GST_BUFFER_SIZE (buffer);
GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d",
h264parse->last_nal_pos, h264parse->next_sc_pos);
nal_pos = h264parse->last_nal_pos;
next_sc_pos = h264parse->next_sc_pos;
if (!next_sc_pos) {
sc_pos = gst_h264_parse_find_sc (buffer, 0);
if (sc_pos == -1) {
/* SC not found, need more data */
sc_pos = GST_BUFFER_SIZE (buffer) - 3;
/* avoid going < 0 later on */
nal_pos = next_sc_pos = sc_pos;
goto more;
}
nal_pos = sc_pos + 3;
next_sc_pos = nal_pos;
/* sc might have 2 or 3 0-bytes */
if (sc_pos > 0 && data[sc_pos - 1] == 00)
sc_pos--;
GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos);
} else {
/* previous checks already arrange sc at start */
sc_pos = 0;
}
drain = GST_BASE_PARSE_DRAINING (parse);
current_off = h264parse->current_off;
GST_DEBUG ("Last parse position %u", current_off);
while (TRUE) {
gint prev_sc_pos;
switch (gst_h264_parser_identify_nalu (nalparser, data, current_off,
size, &nalu)) {
case GST_H264_PARSER_OK:
GST_DEBUG ("Complete nal found. %u Off: %u, Size: %u",
current_off, nalu.offset, nalu.size);
next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos);
if (next_sc_pos == -1) {
GST_LOG_OBJECT (h264parse, "no next sc");
if (drain) {
/* FLUSH/EOS, it's okay if we can't find the next frame */
next_sc_pos = size;
next_nal_pos = size;
} else {
next_sc_pos = size - 3;
goto more;
}
} else {
next_nal_pos = next_sc_pos + 3;
if (data[next_sc_pos - 1] == 00)
next_sc_pos--;
GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos);
/* need at least 1 more byte of next NAL */
if (!drain && (next_nal_pos == size - 1))
goto more;
}
/* determine nal's sc position */
prev_sc_pos = nal_pos - 3;
g_assert (prev_sc_pos >= 0);
if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0)
prev_sc_pos--;
/* already consume and gather info from NAL */
if (G_UNLIKELY (next_sc_pos - nal_pos < 2)) {
GST_WARNING_OBJECT (h264parse, "input stream is corrupt; "
"it contains a NAL unit of length %d", next_sc_pos - nal_pos);
/* broken nal at start -> arrange to skip it,
* otherwise have it terminate current au
* (and so it will be skippd on next frame round) */
if (prev_sc_pos == sc_pos) {
*skipsize = sc_pos + 2;
return FALSE;
} else {
next_sc_pos = prev_sc_pos;
current_off = nalu.offset + nalu.size;
GST_DEBUG ("CURENT OFF. %u, %u", current_off, nalu.offset + nalu.size);
if (!h264parse->nalu.size && !h264parse->nalu.valid)
h264parse->nalu = nalu;
break;
}
}
gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos,
next_sc_pos - nal_pos);
if (next_nal_pos >= size - 1 ||
gst_h264_parse_collect_nal (h264parse, data + nal_pos,
data + next_nal_pos))
break;
case GST_H264_PARSER_BROKEN_LINK:
return FALSE;
case GST_H264_PARSER_ERROR:
current_off = size - 3;
goto parsing_error;
case GST_H264_PARSER_NO_NAL:
current_off = size - 3;
goto more;
case GST_H264_PARSER_BROKEN_DATA:
GST_WARNING_OBJECT (h264parse, "input stream is corrupt; "
"it contains a NAL unit of length %d", nalu.size);
/* move along */
next_sc_pos = nal_pos = next_nal_pos;
/* broken nal at start -> arrange to skip it,
* otherwise have it terminate current au
* (and so it will be skipped on next frame round) */
if (nalu.sc_offset == h264parse->nalu.sc_offset) {
*skipsize = nalu.offset;
GST_DEBUG ("Skiping broken nal");
return FALSE;
} else {
nalu.size = 0;
goto end;
}
case GST_H264_PARSER_NO_NAL_END:
GST_DEBUG ("Not a complete nal found at offset %u", nalu.offset);
current_off = nalu.sc_offset;
/* We keep the reference to this nal so we start over the parsing
* here */
if (!h264parse->nalu.size && !h264parse->nalu.valid)
h264parse->nalu = nalu;
if (drain) {
GST_DEBUG ("Drainning NAL %u %u %u", size, h264parse->nalu.offset,
h264parse->nalu.size);
/* Can't parse the nalu */
if (size - h264parse->nalu.offset < 2) {
*skipsize = nalu.offset;
return FALSE;
}
/* We parse it anyway */
nalu.size = size - nalu.offset;
break;
}
goto more;
}
current_off = nalu.offset + nalu.size;
GST_DEBUG ("%p Complete nal found. Off: %u, Size: %u", data, nalu.offset,
nalu.size);
gst_h264_parse_process_nal (h264parse, &nalu);
if (gst_h264_parse_collect_nal (h264parse, data, size, &nalu) || drain)
break;
}
*skipsize = sc_pos;
*framesize = next_sc_pos - sc_pos;
end:
/* FIXME this shouldnt be needed */
if (h264parse->nalu.sc_offset > 0 && data[h264parse->nalu.sc_offset - 1] == 0)
h264parse->nalu.sc_offset--;
*skipsize = h264parse->nalu.sc_offset;
*framesize = nalu.offset + nalu.size - h264parse->nalu.sc_offset; /* CHECKME */
h264parse->current_off = current_off;
return TRUE;
parsing_error:
GST_DEBUG ("Error parsing Nal Unit");
more:
/* ask for best next available */
*framesize = G_MAXUINT;
if (!h264parse->nalu.size) {
/* skip up to initial startcode */
*skipsize = h264parse->nalu.sc_offset;
} else {
*skipsize = 0;
}
/* skip up to initial startcode */
*skipsize = sc_pos;
/* resume scanning here next time */
h264parse->last_nal_pos = nal_pos - sc_pos;
h264parse->next_sc_pos = next_sc_pos - sc_pos;
/* Restart parsing from here next time */
h264parse->current_off = current_off;
return FALSE;
}
@ -610,8 +727,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
/* only nal payload in stored nals */
for (i = 0; i < MAX_SPS_COUNT; i++) {
if ((nal = h264parse->params->sps_nals[i])) {
for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) {
if ((nal = h264parse->sps_nals[i])) {
num_sps++;
/* size bytes also count */
sps_size += GST_BUFFER_SIZE (nal) + 2;
@ -623,8 +740,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
}
}
}
for (i = 0; i < MAX_PPS_COUNT; i++) {
if ((nal = h264parse->params->pps_nals[i])) {
for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) {
if ((nal = h264parse->pps_nals[i])) {
num_pps++;
/* size bytes also count */
pps_size += GST_BUFFER_SIZE (nal) + 2;
@ -648,8 +765,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
data[5] = 0xe0 | num_sps; /* number of SPSs */
data += 6;
for (i = 0; i < MAX_SPS_COUNT; i++) {
if ((nal = h264parse->params->sps_nals[i])) {
for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) {
if ((nal = h264parse->sps_nals[i])) {
GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal));
memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal));
data += 2 + GST_BUFFER_SIZE (nal);
@ -658,8 +775,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
data[0] = num_pps;
data++;
for (i = 0; i < MAX_PPS_COUNT; i++) {
if ((nal = h264parse->params->pps_nals[i])) {
for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) {
if ((nal = h264parse->pps_nals[i])) {
GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal));
memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal));
data += 2 + GST_BUFFER_SIZE (nal);
@ -672,7 +789,7 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
static void
gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
{
GstH264ParamsSPS *sps;
GstH264SPS *sps;
GstCaps *sink_caps;
gboolean modified = FALSE;
GstBuffer *buf = NULL;
@ -695,7 +812,7 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
else
sink_caps = gst_caps_new_simple ("video/x-h264", NULL);
sps = h264parse->params->sps;
sps = h264parse->nalparser->last_sps;
GST_DEBUG_OBJECT (h264parse, "sps: %p", sps);
/* only codec-data for nice-and-clean au aligned packetized avc format */
@ -764,6 +881,134 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
gst_buffer_unref (buf);
}
static void
gst_h264_parse_get_timestamp (GstH264Parse * h264parse,
GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame)
{
GstH264SPS *sps = h264parse->nalparser->last_sps;
GstClockTime upstream;
gint duration = 1;
g_return_if_fail (out_dur != NULL);
g_return_if_fail (out_ts != NULL);
upstream = *out_ts;
if (!frame) {
GST_LOG_OBJECT (h264parse, "no frame data -> 0 duration");
*out_dur = 0;
goto exit;
} else {
*out_ts = upstream;
}
if (!sps) {
GST_DEBUG_OBJECT (h264parse, "referred SPS invalid");
goto exit;
} else if (!sps->vui_parameters.timing_info_present_flag) {
GST_DEBUG_OBJECT (h264parse,
"unable to compute timestamp: timing info not present");
goto exit;
} else if (sps->vui_parameters.time_scale == 0) {
GST_DEBUG_OBJECT (h264parse,
"unable to compute timestamp: time_scale = 0 "
"(this is forbidden in spec; bitstream probably contains error)");
goto exit;
}
if (h264parse->sei_pic_struct_pres_flag &&
h264parse->sei_pic_struct != (guint8) - 1) {
/* Note that when h264parse->sei_pic_struct == -1 (unspecified), there
* are ways to infer its value. This is related to computing the
* TopFieldOrderCnt and BottomFieldOrderCnt, which looks
* complicated and thus not implemented for the time being. Yet
* the value we have here is correct for many applications
*/
switch (h264parse->sei_pic_struct) {
case GST_H264_SEI_PIC_STRUCT_TOP_FIELD:
case GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD:
duration = 1;
break;
case GST_H264_SEI_PIC_STRUCT_FRAME:
case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM:
case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP:
duration = 2;
break;
case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP:
case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM:
duration = 3;
break;
case GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING:
duration = 4;
break;
case GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING:
duration = 6;
break;
default:
GST_DEBUG_OBJECT (h264parse,
"h264parse->sei_pic_struct of unknown value %d. Not parsed",
h264parse->sei_pic_struct);
break;
}
} else {
duration = h264parse->field_pic_flag ? 1 : 2;
}
GST_LOG_OBJECT (h264parse, "frame tick duration %d", duration);
/*
* h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS):
* Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000
* Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n)
* where
* Tc = num_units_in_tick / time_scale
*/
if (h264parse->ts_trn_nb != GST_CLOCK_TIME_NONE) {
GST_LOG_OBJECT (h264parse, "buffering based ts");
/* buffering period is present */
if (upstream != GST_CLOCK_TIME_NONE) {
/* If upstream timestamp is valid, we respect it and adjust current
* reference point */
h264parse->ts_trn_nb = upstream -
(GstClockTime) gst_util_uint64_scale_int
(h264parse->sei_cpb_removal_delay * GST_SECOND,
sps->vui_parameters.num_units_in_tick,
sps->vui_parameters.time_scale);
} else {
/* If no upstream timestamp is given, we write in new timestamp */
upstream = h264parse->dts = h264parse->ts_trn_nb +
(GstClockTime) gst_util_uint64_scale_int
(h264parse->sei_cpb_removal_delay * GST_SECOND,
sps->vui_parameters.num_units_in_tick,
sps->vui_parameters.time_scale);
}
} else {
GstClockTime dur;
GST_LOG_OBJECT (h264parse, "duration based ts");
/* naive method: no removal delay specified
* track upstream timestamp and provide best guess frame duration */
dur = gst_util_uint64_scale_int (duration * GST_SECOND,
sps->vui_parameters.num_units_in_tick, sps->vui_parameters.time_scale);
/* sanity check */
if (dur < GST_MSECOND) {
GST_DEBUG_OBJECT (h264parse, "discarding dur %" GST_TIME_FORMAT,
GST_TIME_ARGS (dur));
} else {
*out_dur = dur;
}
}
exit:
if (GST_CLOCK_TIME_IS_VALID (upstream))
*out_ts = h264parse->dts = upstream;
if (GST_CLOCK_TIME_IS_VALID (*out_dur) &&
GST_CLOCK_TIME_IS_VALID (h264parse->dts))
h264parse->dts += *out_dur;
}
static GstFlowReturn
gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
{
@ -776,7 +1021,7 @@ gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
gst_h264_parse_update_src_caps (h264parse, NULL);
gst_h264_params_get_timestamp (h264parse->params,
gst_h264_parse_get_timestamp (h264parse,
&GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer),
h264parse->frame_start);
@ -864,16 +1109,16 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) {
/* send separate config NAL buffers */
GST_DEBUG_OBJECT (h264parse, "- sending SPS/PPS");
for (i = 0; i < MAX_SPS_COUNT; i++) {
if ((codec_nal = h264parse->params->sps_nals[i])) {
for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) {
if ((codec_nal = h264parse->sps_nals[i])) {
GST_DEBUG_OBJECT (h264parse, "sending SPS nal");
gst_h264_parse_push_codec_buffer (h264parse, codec_nal,
timestamp);
h264parse->last_report = new_ts;
}
}
for (i = 0; i < MAX_PPS_COUNT; i++) {
if ((codec_nal = h264parse->params->pps_nals[i])) {
for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) {
if ((codec_nal = h264parse->pps_nals[i])) {
GST_DEBUG_OBJECT (h264parse, "sending PPS nal");
gst_h264_parse_push_codec_buffer (h264parse, codec_nal,
timestamp);
@ -890,8 +1135,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer),
h264parse->idr_pos);
GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS");
for (i = 0; i < MAX_SPS_COUNT; i++) {
if ((codec_nal = h264parse->params->sps_nals[i])) {
for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) {
if ((codec_nal = h264parse->sps_nals[i])) {
GST_DEBUG_OBJECT (h264parse, "inserting SPS nal");
gst_byte_writer_put_uint32_be (&bw,
bs ? 1 : GST_BUFFER_SIZE (codec_nal));
@ -900,8 +1145,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
h264parse->last_report = new_ts;
}
}
for (i = 0; i < MAX_PPS_COUNT; i++) {
if ((codec_nal = h264parse->params->pps_nals[i])) {
for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) {
if ((codec_nal = h264parse->pps_nals[i])) {
GST_DEBUG_OBJECT (h264parse, "inserting PPS nal");
gst_byte_writer_put_uint32_be (&bw,
bs ? 1 : GST_BUFFER_SIZE (codec_nal));
@ -937,7 +1182,9 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
GstStructure *str;
const GValue *value;
GstBuffer *codec_data = NULL;
guint size, format, align;
guint size, format, align, off;
GstH264NalUnit nalu;
GstH264ParserResult parseres;
h264parse = GST_H264_PARSE (parse);
@ -959,7 +1206,7 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
if (format != GST_H264_PARSE_FORMAT_BYTE &&
(value = gst_structure_get_value (str, "codec_data"))) {
guint8 *data;
guint num_sps, num_pps, profile, len;
guint num_sps, num_pps, profile;
gint i;
GST_DEBUG_OBJECT (h264parse, "have packetized h264");
@ -992,28 +1239,29 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
GST_DEBUG_OBJECT (h264parse, "nal length %u", h264parse->nal_length_size);
num_sps = data[5] & 0x1f;
data += 6;
size -= 6;
off = 6;
for (i = 0; i < num_sps; i++) {
len = GST_READ_UINT16_BE (data);
if (size < len + 2 || len < 2)
parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser,
data, off, size - off, 2, &nalu);
if (parseres != GST_H264_PARSER_OK)
goto avcc_too_small;
/* digest for later reference */
gst_h264_parse_process_nal (h264parse, data, 0, 2, len);
data += len + 2;
size -= len + 2;
gst_h264_parse_process_nal (h264parse, &nalu);
off = nalu.offset + nalu.size;
}
num_pps = data[0];
data++;
size++;
for (i = 0; i < num_pps; i++) {
len = GST_READ_UINT16_BE (data);
if (size < len + 2 || len < 2)
parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser,
data, off, size - off, 2, &nalu);
if (parseres != GST_H264_PARSER_OK) {
goto avcc_too_small;
/* digest for later reference */
gst_h264_parse_process_nal (h264parse, data, 0, 2, len);
data += len + 2;
size -= len + 2;
}
gst_h264_parse_process_nal (h264parse, &nalu);
off = nalu.offset + nalu.size;
}
h264parse->codec_data = gst_buffer_ref (codec_data);
@ -1089,10 +1337,12 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
GstBuffer *sub;
GstFlowReturn ret = GST_FLOW_OK;
guint32 len;
GstH264NalUnit nalu;
const guint nl = h264parse->nal_length_size;
GST_LOG_OBJECT (h264parse, "processing packet buffer of size %d",
GST_BUFFER_SIZE (buffer));
gst_byte_reader_init_from_buffer (&br, buffer);
while (ret == GST_FLOW_OK && gst_byte_reader_get_remaining (&br)) {
GST_DEBUG_OBJECT (h264parse, "AVC nal offset %d",
@ -1114,9 +1364,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
break;
default:
goto not_negotiated;
break;
}
GST_DEBUG_OBJECT (h264parse, "AVC nal size %d", len);
if (gst_byte_reader_get_remaining (&br) < len)
goto parse_failed;
if (h264parse->split_packetized) {
@ -1133,9 +1384,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
/* NOTE: so if it is really configured to do so,
* pre_push can/will still insert codec-data at intervals,
* which is not really pure pass-through, but anyway ... */
gst_h264_parse_process_nal (h264parse,
gst_h264_parser_identify_nalu (h264parse->nalparser,
GST_BUFFER_DATA (buffer), gst_byte_reader_get_pos (&br) - nl,
gst_byte_reader_get_pos (&br), len);
GST_BUFFER_SIZE (buffer), &nalu);
gst_h264_parse_process_nal (h264parse, &nalu);
gst_byte_reader_skip_unchecked (&br, len);
}
}

View file

@ -1,7 +1,10 @@
/* GStreamer H.264 Parser
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* Copyright (C) <2010> Collabora Multimedia
* Copyright (C) <2010> Collabora ltd
* Copyright (C) <2010> Nokia Corporation
* Copyright (C) <2011> Intel Corporation
*
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* Copyright (C) <2011> Thibault Saunier <thibault.saunier@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -24,8 +27,7 @@
#include <gst/gst.h>
#include <gst/base/gstbaseparse.h>
#include "h264parse.h"
#include <gst/codecparsers/gsth264parser.h>
G_BEGIN_DECLS
@ -61,16 +63,34 @@ struct _GstH264Parse
gboolean packetized;
/* state */
GstH264Params *params;
GstH264NalParser *nalparser;
GstH264NalUnit nalu;
guint align;
guint format;
guint current_off;
GstClockTime last_report;
gboolean push_codec;
/* collected SPS and PPS NALUs */
GstBuffer *sps_nals[GST_H264_MAX_SPS_COUNT];
GstBuffer *pps_nals[GST_H264_MAX_PPS_COUNT];
/* Infos we need to keep track of */
guint32 sei_cpb_removal_delay;
guint8 sei_pic_struct;
guint8 sei_pic_struct_pres_flag;
guint field_pic_flag;
/* cached timestamps */
/* (trying to) track upstream dts and interpolate */
GstClockTime dts;
/* dts at start of last buffering period */
GstClockTime ts_trn_nb;
/* frame parsing */
guint last_nal_pos;
guint next_sc_pos;
/*guint last_nal_pos;*/
/*guint next_sc_pos;*/
gint idr_pos;
gboolean update_caps;
GstAdapter *frame_out;

File diff suppressed because it is too large Load diff

View file

@ -1,186 +0,0 @@
/* GStreamer H.264 Parser
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
* Copyright (C) <2010> Collabora Multimedia
* Copyright (C) <2010> Nokia Corporation
*
* Some bits C-c,C-v'ed and s/4/3 from h264parse:
* (C) 2005 Michal Benes <michal.benes@itonis.tv>
* (C) 2008 Wim Taymans <wim.taymans@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_H264_PARAMS_H__
#define __GST_H264_PARAMS_H__
#include <gst/gst.h>
G_BEGIN_DECLS
typedef enum
{
NAL_UNKNOWN = 0,
NAL_SLICE = 1,
NAL_SLICE_DPA = 2,
NAL_SLICE_DPB = 3,
NAL_SLICE_DPC = 4,
NAL_SLICE_IDR = 5,
NAL_SEI = 6,
NAL_SPS = 7,
NAL_PPS = 8,
NAL_AU_DELIMITER = 9,
NAL_SEQ_END = 10,
NAL_STREAM_END = 11,
NAL_FILTER_DATA = 12
} GstH264ParamsNalUnitType;
/* SEI type */
typedef enum
{
SEI_BUF_PERIOD = 0,
SEI_PIC_TIMING = 1
/* and more... */
} GstH264ParamsSEIPayloadType;
/* SEI pic_struct type */
typedef enum
{
SEI_PIC_STRUCT_FRAME = 0,
SEI_PIC_STRUCT_TOP_FIELD = 1,
SEI_PIC_STRUCT_BOTTOM_FIELD = 2,
SEI_PIC_STRUCT_TOP_BOTTOM = 3,
SEI_PIC_STRUCT_BOTTOM_TOP = 4,
SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5,
SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6,
SEI_PIC_STRUCT_FRAME_DOUBLING = 7,
SEI_PIC_STRUCT_FRAME_TRIPLING = 8
} GstH264ParamsSEIPicStructType;
typedef struct _GstH264Params GstH264Params;
typedef struct _GstH264ParamsSPS GstH264ParamsSPS;
typedef struct _GstH264ParamsPPS GstH264ParamsPPS;
#define MAX_SPS_COUNT 32
#define MAX_PPS_COUNT 256
/* SPS: sequential parameter sets */
struct _GstH264ParamsSPS
{
gboolean valid;
/* raw values */
guint8 profile_idc;
guint8 level_idc;
guint8 sps_id;
guint8 pic_order_cnt_type;
guint8 log2_max_frame_num_minus4;
gboolean frame_mbs_only_flag;
guint8 log2_max_pic_order_cnt_lsb_minus4;
gboolean frame_cropping_flag;
gboolean scp_flag;
/* VUI parameters */
gboolean vui_parameters_present_flag;
gboolean timing_info_present_flag;
guint32 num_units_in_tick;
guint32 time_scale;
gboolean fixed_frame_rate_flag;
gboolean nal_hrd_parameters_present_flag;
gboolean vcl_hrd_parameters_present_flag;
/* hrd parameters */
guint8 cpb_cnt_minus1;
gint initial_cpb_removal_delay_length_minus1;
gint cpb_removal_delay_length_minus1;
gint dpb_output_delay_length_minus1;
gboolean time_offset_length_minus1;
gboolean pic_struct_present_flag;
/* ... and probably more ... */
/* derived values */
gint width, height;
gint fps_num, fps_den;
};
/* PPS: pic parameter sets */
struct _GstH264ParamsPPS
{
gboolean valid;
/* raw values */
guint8 pps_id;
guint8 sps_id;
};
struct _GstH264Params
{
/* debug purposes */
GstElement *el;
/* SPS: sequential parameter set */
GstH264ParamsSPS sps_buffers[MAX_SPS_COUNT];
/* current SPS; most recent one in stream or referenced by PPS */
GstH264ParamsSPS *sps;
/* PPS: sequential parameter set */
GstH264ParamsPPS pps_buffers[MAX_PPS_COUNT];
/* current PPS; most recent one in stream */
GstH264ParamsPPS *pps;
/* extracted from slice header or otherwise relevant nal */
guint8 first_mb_in_slice;
guint8 slice_type;
gboolean field_pic_flag;
gboolean bottom_field_flag;
/* SEI: supplemental enhancement messages */
#ifdef EXTRA_PARSE
/* buffering period */
guint32 initial_cpb_removal_delay[32];
#endif
/* picture timing */
guint32 sei_cpb_removal_delay;
guint8 sei_pic_struct;
/* And more... */
/* cached timestamps */
/* (trying to) track upstream dts and interpolate */
GstClockTime dts;
/* dts at start of last buffering period */
GstClockTime ts_trn_nb;
/* collected SPS and PPS NALUs */
GstBuffer *sps_nals[MAX_SPS_COUNT];
GstBuffer *pps_nals[MAX_PPS_COUNT];
};
gboolean gst_h264_params_parse_nal (GstH264Params * params, guint8 * nal, gint size);
void gst_h264_params_get_timestamp (GstH264Params * params,
GstClockTime * out_ts, GstClockTime * out_dur,
gboolean frame);
void gst_h264_params_create (GstH264Params ** _params, GstElement * element);
void gst_h264_params_free (GstH264Params * params);
G_END_DECLS
#endif