summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@gmail.com>2008-01-16 18:28:00 +0000
committerWim Taymans <wim.taymans@gmail.com>2008-01-16 18:28:00 +0000
commit631b6a1766f76ed7075e03d5a94683f6a9c10fc2 (patch)
tree788cad0ccd9d64e2561edb480dc670d92c5f0699 /gst
parentae0feba9ec333fc281df4b3ab2ae1119da35f8b6 (diff)
downloadgst-plugins-bad-631b6a1766f76ed7075e03d5a94683f6a9c10fc2.tar.gz
gst-plugins-bad-631b6a1766f76ed7075e03d5a94683f6a9c10fc2.tar.bz2
gst-plugins-bad-631b6a1766f76ed7075e03d5a94683f6a9c10fc2.zip
gst/h264parse/gsth264parse.*: Add reverse playback support for containers that don't know how to properly send data b...
Original commit message from CVS: * gst/h264parse/gsth264parse.c: (gst_nal_list_new), (gst_nal_list_prepend_link), (gst_nal_list_delete_head), (gst_nal_bs_init), (gst_nal_bs_read), (gst_nal_bs_eos), (gst_nal_bs_read_ue), (gst_h264_parse_class_init), (gst_h264_parse_init), (gst_h264_parse_set_property), (gst_h264_parse_get_property), (gst_h264_parse_sink_setcaps), (gst_h264_parse_clear_queues), (gst_h264_parse_chain_forward), (gst_h264_parse_flush_decode), (gst_h264_parse_queue_buffer), (gst_h264_parse_find_start_reverse), (gst_h264_parse_chain_reverse), (gst_h264_parse_chain), (gst_h264_parse_sink_event), (gst_h264_parse_change_state): * gst/h264parse/gsth264parse.h: Add reverse playback support for containers that don't know how to properly send data between I-frames.
Diffstat (limited to 'gst')
-rw-r--r--gst/h264parse/gsth264parse.c717
-rw-r--r--gst/h264parse/gsth264parse.h19
2 files changed, 684 insertions, 52 deletions
diff --git a/gst/h264parse/gsth264parse.c b/gst/h264parse/gsth264parse.c
index fef7ddfc..05e1b7f4 100644
--- a/gst/h264parse/gsth264parse.c
+++ b/gst/h264parse/gsth264parse.c
@@ -1,5 +1,6 @@
/* GStreamer h264 parser
* Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+ * (C) 2008 Wim Taymans <wim.taymans@gmail.com>
*
* gsth264parse.c:
*
@@ -46,13 +47,184 @@ static const GstElementDetails gst_h264_parse_details =
GST_ELEMENT_DETAILS ("H264Parse",
"Codec/Parser",
"Parses raw h264 stream",
- "Michal Benes <michal.benes@itonis.tv>");
+ "Michal Benes <michal.benes@itonis.tv>,"
+ "Wim Taymans <wim.taymans@gmail.com");
+
+#define DEFAULT_SPLIT_PACKETIZED FALSE
+
+enum
+{
+ PROP_0,
+ PROP_SPLIT_PACKETIZED
+};
+
+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
+} GstNalUnitType;
+
+/* small linked list implementation to allocate the list entry and the data in
+ * one go */
+struct _GstNalList
+{
+ GstNalList *next;
+
+ gint nal_type;
+ gint nal_ref_idc;
+ gint first_mb_in_slice;
+ gint slice_type;
+ gboolean slice;
+ gboolean i_frame;
+
+ GstBuffer *buffer;
+};
+
+static GstNalList *
+gst_nal_list_new (GstBuffer * buffer)
+{
+ GstNalList *new_list;
+
+ new_list = g_slice_new0 (GstNalList);
+ new_list->buffer = buffer;
+
+ return new_list;
+}
+
+static GstNalList *
+gst_nal_list_prepend_link (GstNalList * list, GstNalList * link)
+{
+ link->next = list;
+
+ return link;
+}
+
+static GstNalList *
+gst_nal_list_delete_head (GstNalList * list)
+{
+ if (list) {
+ GstNalList *old = list;
+
+ list = list->next;
+
+ g_slice_free (GstNalList, old);
+ }
+ return list;
+}
+
+/* simple bitstream parser, automatically skips over
+ * emulation_prevention_three_bytes. */
+typedef struct
+{
+ guint8 *data;
+ guint8 *end;
+ gint head; /* bitpos in the cache of next bit */
+ guint64 cache; /* cached bytes */
+} GstNalBs;
+
+static void
+gst_nal_bs_init (GstNalBs * bs, guint8 * data, guint size)
+{
+ bs->data = data;
+ bs->end = data + size;
+ bs->head = 0;
+ /* fill with something other than 0 to detect emulation prevention bytes */
+ bs->cache = 0xffffffff;
+}
+
+static guint32
+gst_nal_bs_read (GstNalBs * bs, guint n)
+{
+ guint32 res = 0;
+ gint shift;
+
+ if (n == 0)
+ return res;
+
+ /* fill up the cache if we need to */
+ while (bs->head < n) {
+ guint8 byte;
+ gboolean check_three_byte;
+
+ check_three_byte = TRUE;
+ next_byte:
+ if (bs->data >= bs->end) {
+ /* we're at the end, can't produce more than head number of bits */
+ n = bs->head;
+ break;
+ }
+ /* get the byte, this can be an emulation_prevention_three_byte that we need
+ * to ignore. */
+ byte = *bs->data++;
+ if (check_three_byte && byte == 0x03 && ((bs->cache & 0xffff) == 0)) {
+ /* next byte goes unconditionally to the cache, even if it's 0x03 */
+ check_three_byte = FALSE;
+ goto next_byte;
+ }
+ /* shift bytes in cache, moving the head bits of the cache left */
+ bs->cache = (bs->cache << 8) | byte;
+ bs->head += 8;
+ }
+
+ /* bring the required bits down and truncate */
+ if ((shift = bs->head - n) > 0)
+ res = bs->cache >> shift;
+ else
+ res = bs->cache;
+
+ /* mask out required bits */
+ if (n < 32)
+ res &= (1 << n) - 1;
+
+ bs->head = shift;
+
+ return res;
+}
+
+static gboolean
+gst_nal_bs_eos (GstNalBs * bs)
+{
+ return (bs->data >= bs->end) && (bs->head == 0);
+}
+
+/* read unsigned Exp-Golomb code */
+static gint
+gst_nal_bs_read_ue (GstNalBs * bs)
+{
+ gint i = 0;
+
+ while (gst_nal_bs_read (bs, 1) == 0 && !gst_nal_bs_eos (bs) && i < 32)
+ i++;
+
+ return ((1 << i) - 1 + gst_nal_bs_read (bs, i));
+}
+
GST_BOILERPLATE (GstH264Parse, gst_h264_parse, GstElement, GST_TYPE_ELEMENT);
static void gst_h264_parse_finalize (GObject * object);
+static void gst_h264_parse_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_h264_parse_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buf);
-static gboolean gst_h264_parse_handle_event (GstPad * pad, GstEvent * event);
+static gboolean gst_h264_parse_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps);
+
+static GstStateChangeReturn gst_h264_parse_change_state (GstElement * element,
+ GstStateChange transition);
static void
gst_h264_parse_base_init (gpointer g_class)
@@ -69,6 +241,46 @@ gst_h264_parse_base_init (gpointer g_class)
}
static void
+gst_h264_parse_class_init (GstH264ParseClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_h264_parse_finalize);
+ gobject_class->set_property = gst_h264_parse_set_property;
+ gobject_class->get_property = gst_h264_parse_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_SPLIT_PACKETIZED,
+ g_param_spec_boolean ("split-packetized", "Split packetized",
+ "Split NAL units of packetized streams", DEFAULT_SPLIT_PACKETIZED,
+ G_PARAM_READWRITE));
+
+ gstelement_class->change_state = gst_h264_parse_change_state;
+}
+
+static void
+gst_h264_parse_init (GstH264Parse * h264parse, GstH264ParseClass * g_class)
+{
+ h264parse->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ gst_pad_set_chain_function (h264parse->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_h264_parse_chain));
+ gst_pad_set_event_function (h264parse->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_h264_parse_sink_event));
+ gst_pad_set_setcaps_function (h264parse->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_h264_parse_sink_setcaps));
+ gst_element_add_pad (GST_ELEMENT (h264parse), h264parse->sinkpad);
+
+ h264parse->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+ gst_element_add_pad (GST_ELEMENT (h264parse), h264parse->srcpad);
+
+ h264parse->split_packetized = DEFAULT_SPLIT_PACKETIZED;
+ h264parse->adapter = gst_adapter_new ();
+}
+
+static void
gst_h264_parse_finalize (GObject * object)
{
GstH264Parse *h264parse;
@@ -81,59 +293,94 @@ gst_h264_parse_finalize (GObject * object)
}
static void
-gst_h264_parse_class_init (GstH264ParseClass * klass)
+gst_h264_parse_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
{
- GObjectClass *gobject_class;
+ GstH264Parse *parse;
- gobject_class = G_OBJECT_CLASS (klass);
+ parse = GST_H264PARSE (object);
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_h264_parse_finalize);
+ switch (prop_id) {
+ case PROP_SPLIT_PACKETIZED:
+ parse->split_packetized = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
-gst_h264_parse_init (GstH264Parse * h264parse, GstH264ParseClass * g_class)
+gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
{
- h264parse->sink = gst_pad_new_from_static_template (&sinktemplate, "sink");
- gst_pad_set_chain_function (h264parse->sink,
- GST_DEBUG_FUNCPTR (gst_h264_parse_chain));
- gst_pad_set_event_function (h264parse->sink,
- GST_DEBUG_FUNCPTR (gst_h264_parse_handle_event));
- gst_element_add_pad (GST_ELEMENT (h264parse), h264parse->sink);
+ GstH264Parse *parse;
- h264parse->src = gst_pad_new_from_static_template (&srctemplate, "src");
- gst_element_add_pad (GST_ELEMENT (h264parse), h264parse->src);
+ parse = GST_H264PARSE (object);
- h264parse->adapter = gst_adapter_new ();
+ switch (prop_id) {
+ case PROP_SPLIT_PACKETIZED:
+ g_value_set_boolean (value, parse->split_packetized);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
-static GstFlowReturn
-gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
+static gboolean
+gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps)
{
- GstFlowReturn res = GST_FLOW_OK;
+ gboolean res;
GstH264Parse *h264parse;
+ GstStructure *str;
+ const GValue *value;
h264parse = GST_H264PARSE (GST_PAD_PARENT (pad));
- if (!GST_PAD_CAPS (h264parse->src)) {
- GstCaps *caps;
+ str = gst_caps_get_structure (caps, 0);
+
+ /* packetized video has a codec_data */
+ if ((value = gst_structure_get_value (str, "codec_data"))) {
+ GST_DEBUG_OBJECT (h264parse, "have packetized h264");
+ h264parse->packetized = TRUE;
+ /* FIXME, PPS, SPS have vital info for detecting new I-frames */
+ } else {
+ GST_DEBUG_OBJECT (h264parse, "have bytestream h264");
+ h264parse->packetized = FALSE;
+ }
- /* Forward sink caps if possible */
- caps = GST_PAD_CAPS (h264parse->sink);
- if (caps)
- gst_caps_ref (caps);
+ /* forward the caps */
+ res = gst_pad_set_caps (h264parse->srcpad, caps);
- /* Set default caps if the sink caps were not negotiated */
- if (!caps)
- caps = gst_caps_new_simple ("video/x-h264", NULL);
+ return res;
+}
- /* Set source caps */
- if (!gst_pad_set_caps (h264parse->src, caps)) {
- GST_ELEMENT_ERROR (GST_ELEMENT (h264parse),
- CORE, NEGOTIATION, (NULL), ("failed to set caps"));
- gst_caps_unref (caps);
- return GST_FLOW_ERROR;
- }
- gst_caps_unref (caps);
+static void
+gst_h264_parse_clear_queues (GstH264Parse * h264parse)
+{
+ g_list_foreach (h264parse->gather, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (h264parse->gather);
+ h264parse->gather = NULL;
+ while (h264parse->decode) {
+ gst_buffer_unref (h264parse->decode->buffer);
+ h264parse->decode = gst_nal_list_delete_head (h264parse->decode);
+ }
+ h264parse->decode = NULL;
+ gst_adapter_clear (h264parse->adapter);
+ h264parse->have_i_frame = FALSE;
+}
+
+static GstFlowReturn
+gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
+ GstBuffer * buffer)
+{
+ GstFlowReturn res = GST_FLOW_OK;
+ const guint8 *data;
+
+ if (discont) {
+ gst_adapter_clear (h264parse->adapter);
+ h264parse->discont = TRUE;
}
gst_adapter_push (h264parse->adapter, buffer);
@@ -143,7 +390,6 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
gint next_nalu_pos = -1;
guint32 nalu_size;
gint avail;
- const guint8 *data;
avail = gst_adapter_available (h264parse->adapter);
if (avail < 5)
@@ -164,45 +410,416 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
}
}
} else {
- /* Packetized format */
- if (nalu_size + 4 <= avail)
- next_nalu_pos = nalu_size + 4;
+ /* Packetized format, see if we have to split it, usually splitting is not
+ * a good idea as decoders have no way of handling it. */
+ if (h264parse->split_packetized) {
+ if (nalu_size + 4 <= avail)
+ next_nalu_pos = nalu_size + 4;
+ } else {
+ next_nalu_pos = avail;
+ }
}
-
- /* Send packet */
+ /* we have a packet */
if (next_nalu_pos > 0) {
GstBuffer *outbuf;
- outbuf = gst_buffer_new_and_alloc (next_nalu_pos);
- memcpy (GST_BUFFER_DATA (outbuf), data, next_nalu_pos);
- gst_adapter_flush (h264parse->adapter, next_nalu_pos);
+ outbuf = gst_adapter_take_buffer (h264parse->adapter, next_nalu_pos);
+
+ GST_DEBUG_OBJECT (h264parse,
+ "pushing buffer %p, size %u, ts %" GST_TIME_FORMAT, outbuf,
+ next_nalu_pos, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
- gst_buffer_set_caps (outbuf, GST_PAD_CAPS (h264parse->src));
+ if (h264parse->discont) {
+ GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+ h264parse->discont = FALSE;
+ }
+
+ gst_buffer_set_caps (outbuf, GST_PAD_CAPS (h264parse->srcpad));
GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
- res = gst_pad_push (h264parse->src, outbuf);
+ res = gst_pad_push (h264parse->srcpad, outbuf);
+ } else {
+ /* NALU can not be parsed yet, we wait for more data in the adapter. */
+ break;
+ }
+ }
+ return res;
+}
+
+static GstFlowReturn
+gst_h264_parse_flush_decode (GstH264Parse * h264parse)
+{
+ GstFlowReturn res = GST_FLOW_OK;
+ gboolean first = TRUE;
+
+ while (h264parse->decode) {
+ GstNalList *link;
+ GstBuffer *buf;
+
+ link = h264parse->decode;
+ buf = link->buffer;
+
+ GST_DEBUG_OBJECT (h264parse, "have type: %d, I frame: %d", link->nal_type,
+ link->i_frame);
+
+ if (first) {
+ /* first buffer has discont */
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+ first = FALSE;
} else {
- break; /* NALU can not be parsed yet */
+ /* next buffers are not discont */
+ GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
}
+
+ GST_DEBUG_OBJECT (h264parse, "pushing buffer %p, ts %" GST_TIME_FORMAT,
+ buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+ res = gst_pad_push (h264parse->srcpad, buf);
+
+ h264parse->decode = gst_nal_list_delete_head (h264parse->decode);
}
+ /* the i frame is gone now */
+ h264parse->have_i_frame = FALSE;
+
+ return res;
+}
+
+/* check that the decode queue contains a valid sync code that should be pushed
+ * out before adding @buffer to the decode queue */
+static GstFlowReturn
+gst_h264_parse_queue_buffer (GstH264Parse * parse, GstBuffer * buffer)
+{
+ guint8 *data;
+ guint size;
+ guint32 nalu_size;
+ GstNalBs bs;
+ GstNalList *link;
+ GstFlowReturn res = GST_FLOW_OK;
+
+ /* create new NALU link */
+ link = gst_nal_list_new (buffer);
+
+ /* first parse the buffer */
+ data = GST_BUFFER_DATA (buffer);
+ size = GST_BUFFER_SIZE (buffer);
+
+ link->slice = FALSE;
+ link->i_frame = FALSE;
+
+ GST_DEBUG_OBJECT (parse, "analyse buffer of size %u", size);
+
+ /* now parse all the NAL units in this buffer, for bytestream we only have one
+ * NAL unit but for packetized streams we can have multiple ones */
+ while (size >= 5) {
+ nalu_size = (data[0] << 24)
+ + (data[1] << 16) + (data[2] << 8) + data[3];
+
+ link->nal_ref_idc = (data[4] & 0x60) >> 5;
+ link->nal_type = (data[4] & 0x1f);
+
+ GST_DEBUG_OBJECT (parse, "size: %u, NAL type: %d, ref_idc: %d",
+ nalu_size, link->nal_type, link->nal_ref_idc);
+
+ /* first parse some things needed to get to the frame type */
+ if (link->nal_type >= NAL_SLICE && link->nal_type <= NAL_SLICE_IDR) {
+ gst_nal_bs_init (&bs, data + 5, size - 5);
+
+ link->first_mb_in_slice = gst_nal_bs_read_ue (&bs);
+ link->slice_type = gst_nal_bs_read_ue (&bs);
+ link->slice = TRUE;
+
+ GST_DEBUG_OBJECT (parse, "first MB: %d, slice type: %d",
+ link->first_mb_in_slice, link->slice_type);
+
+ switch (link->slice_type) {
+ case 0:
+ case 5:
+ case 3:
+ case 8: /* SP */
+ /* P frames */
+ GST_DEBUG_OBJECT (parse, "we have a P slice");
+ break;
+ case 1:
+ case 6:
+ /* B frames */
+ GST_DEBUG_OBJECT (parse, "we have a B slice");
+ break;
+ case 2:
+ case 7:
+ case 4:
+ case 9:
+ /* I frames */
+ GST_DEBUG_OBJECT (parse, "we have an I slice");
+ link->i_frame = TRUE;
+ break;
+ }
+ }
+ /* bytestream, we can exit now */
+ if (nalu_size == 1)
+ break;
+
+ /* packetized format, continue parsing all packets, skip size */
+ nalu_size += 4;
+ data += nalu_size;
+ size -= nalu_size;
+ }
+
+ /* we have an I frame in the queue, this new NAL unit is a slice but not
+ * an I frame, output the decode queue */
+ GST_DEBUG_OBJECT (parse, "have_I_frame: %d, I_frame: %d, slice: %d",
+ parse->have_i_frame, link->i_frame, link->slice);
+ if (parse->have_i_frame && !link->i_frame && link->slice) {
+ GST_DEBUG_OBJECT (parse, "flushing decode queue");
+ res = gst_h264_parse_flush_decode (parse);
+ }
+ if (link->i_frame)
+ /* we're going to add a new I-frame in the queue */
+ parse->have_i_frame = TRUE;
+
+ GST_DEBUG_OBJECT (parse, "copy %d bytes of NAL to decode queue", size);
+ parse->decode = gst_nal_list_prepend_link (parse->decode, link);
+
+ return res;
+}
+
+static guint
+gst_h264_parse_find_start_reverse (GstH264Parse * parse, guint8 * data,
+ guint size, guint32 * code)
+{
+ guint32 search = *code;
+
+ while (size > 0) {
+ /* the sync code is kept in reverse */
+ search = (search << 8) | (data[size - 1]);
+ GST_DEBUG_OBJECT (parse, "start code 0x%08x", search);
+ if (search == 0x01000000)
+ break;
+
+ size--;
+ }
+ *code = search;
+
+ return size - 1;
+}
+
+static GstFlowReturn
+gst_h264_parse_chain_reverse (GstH264Parse * h264parse, gboolean discont,
+ GstBuffer * buffer)
+{
+ GstFlowReturn res = GST_FLOW_OK;
+
+ /* if we have a discont, move buffers to the decode list */
+ if (G_UNLIKELY (discont)) {
+ guint start, stop, last;
+ guint32 code;
+ GstBuffer *prev;
+
+ GST_DEBUG_OBJECT (h264parse,
+ "received discont, copy gathered buffers for decoding");
+
+ /* init start code accumulator */
+ stop = -1;
+ prev = NULL;
+
+ while (h264parse->gather) {
+ GstBuffer *gbuf;
+ guint8 *data;
+
+ /* get new buffer and init the start code search to the end position */
+ gbuf = GST_BUFFER_CAST (h264parse->gather->data);
+
+ /* remove from the gather list, they are in reverse order */
+ h264parse->gather =
+ g_list_delete_link (h264parse->gather, h264parse->gather);
+
+ if (h264parse->packetized) {
+ /* packetized the packets are already split, we can just parse and
+ * store them */
+ GST_DEBUG_OBJECT (h264parse, "copied packetized buffer");
+ res = gst_h264_parse_queue_buffer (h264parse, gbuf);
+ } else {
+ /* bytestream, we have to split the NALUs on the sync markers */
+ code = 0xffffffff;
+ if (prev) {
+ /* if we have a previous buffer or a leftover, merge them together
+ * now */
+ gbuf = gst_buffer_join (gbuf, prev);
+ prev = NULL;
+ }
+ last = GST_BUFFER_SIZE (gbuf);
+ data = GST_BUFFER_DATA (gbuf);
+
+ while (last > 0) {
+ /* find a start code searching backwards in this buffer */
+ start =
+ gst_h264_parse_find_start_reverse (h264parse, data, last, &code);
+ GST_DEBUG_OBJECT (h264parse, "found start %d", start);
+
+ if (start != -1) {
+ GstBuffer *decode;
+
+ /* we found a start code, copy everything starting from it to the
+ * decode queue. */
+ if (start != 0)
+ decode = gst_buffer_create_sub (gbuf, start, last - start);
+ else
+ decode = gbuf;
+
+ /* see what we have here */
+ res = gst_h264_parse_queue_buffer (h264parse, decode);
+ } else {
+ /* no start code found, keep the buffer and merge with potential next
+ * buffer. */
+ GST_DEBUG_OBJECT (h264parse, "keeping buffer");
+ prev = gst_buffer_create_sub (gbuf, 0, last);
+ gst_buffer_unref (gbuf);
+ }
+ last = start;
+ }
+ }
+ }
+ }
+ if (buffer) {
+ /* add buffer to gather queue */
+ GST_DEBUG_OBJECT (h264parse, "gathering buffer %p, size %u", buffer,
+ GST_BUFFER_SIZE (buffer));
+ h264parse->gather = g_list_prepend (h264parse->gather, buffer);
+ }
return res;
}
+static GstFlowReturn
+gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstFlowReturn res;
+ GstH264Parse *h264parse;
+ gboolean discont;
+ GstCaps *caps;
+
+ h264parse = GST_H264PARSE (GST_PAD_PARENT (pad));
+
+ if (!GST_PAD_CAPS (h264parse->srcpad)) {
+ /* Set default caps if the sink caps were not negotiated, this is when we
+ * are reading from a file or so */
+ caps = gst_caps_new_simple ("video/x-h264", NULL);
+
+ /* Set source caps */
+ if (!gst_pad_set_caps (h264parse->srcpad, caps))
+ goto caps_failed;
+
+ /* we assume the bytestream format but won't really fail otherwise if the
+ * data turns out to be a nicely aligned packetized format (except we don't
+ * do the codec_data caps with the PPS and SPS). */
+ h264parse->packetized = FALSE;
+
+ gst_caps_unref (caps);
+ }
+
+ discont = GST_BUFFER_IS_DISCONT (buffer);
+
+ GST_DEBUG_OBJECT (h264parse, "received buffer");
+
+ if (h264parse->segment.rate > 0.0)
+ res = gst_h264_parse_chain_forward (h264parse, discont, buffer);
+ else
+ res = gst_h264_parse_chain_reverse (h264parse, discont, buffer);
+
+ return res;
+
+ /* ERRORS */
+caps_failed:
+ {
+ GST_ELEMENT_ERROR (GST_ELEMENT (h264parse),
+ CORE, NEGOTIATION, (NULL), ("failed to set caps"));
+ gst_caps_unref (caps);
+ return GST_FLOW_ERROR;
+ }
+}
static gboolean
-gst_h264_parse_handle_event (GstPad * pad, GstEvent * event)
+gst_h264_parse_sink_event (GstPad * pad, GstEvent * event)
{
GstH264Parse *h264parse;
gboolean res;
h264parse = GST_H264PARSE (gst_pad_get_parent (pad));
- res = gst_pad_event_default (pad, event);
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_STOP:
+ GST_DEBUG_OBJECT (h264parse, "received FLUSH stop");
+ gst_segment_init (&h264parse->segment, GST_FORMAT_UNDEFINED);
+ gst_h264_parse_clear_queues (h264parse);
+ res = gst_pad_push_event (h264parse->srcpad, event);
+ break;
+ case GST_EVENT_EOS:
+ GST_DEBUG_OBJECT (h264parse, "received EOS");
+ if (h264parse->segment.rate < 0.0) {
+ gst_h264_parse_chain_reverse (h264parse, TRUE, NULL);
+ gst_h264_parse_flush_decode (h264parse);
+ }
+ res = gst_pad_push_event (h264parse->srcpad, event);
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ {
+ gdouble rate, applied_rate;
+ GstFormat format;
+ gint64 start, stop, pos;
+ gboolean update;
+
+ gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
+ &format, &start, &stop, &pos);
+
+ /* now configure the values */
+ gst_segment_set_newsegment_full (&h264parse->segment, update,
+ rate, applied_rate, format, start, stop, pos);
+
+ GST_DEBUG_OBJECT (h264parse,
+ "Pushing newseg rate %g, applied rate %g, format %d, start %"
+ G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT ", pos %" G_GINT64_FORMAT,
+ rate, applied_rate, format, start, stop, pos);
+
+ res = gst_pad_push_event (h264parse->srcpad, event);
+ break;
+ }
+ default:
+ res = gst_pad_push_event (h264parse->srcpad, event);
+ break;
+ }
gst_object_unref (h264parse);
+
return res;
}
+static GstStateChangeReturn
+gst_h264_parse_change_state (GstElement * element, GstStateChange transition)
+{
+ GstH264Parse *h264parse;
+ GstStateChangeReturn ret;
+
+ h264parse = GST_H264PARSE (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_segment_init (&h264parse->segment, GST_FORMAT_UNDEFINED);
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_h264_parse_clear_queues (h264parse);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static gboolean
plugin_init (GstPlugin * plugin)
diff --git a/gst/h264parse/gsth264parse.h b/gst/h264parse/gsth264parse.h
index 8f283be9..8aed514c 100644
--- a/gst/h264parse/gsth264parse.h
+++ b/gst/h264parse/gsth264parse.h
@@ -42,12 +42,27 @@ G_BEGIN_DECLS
typedef struct _GstH264Parse GstH264Parse;
typedef struct _GstH264ParseClass GstH264ParseClass;
+typedef struct _GstNalList GstNalList;
+
struct _GstH264Parse
{
GstElement element;
- GstPad *sink;
- GstPad *src;
+ GstPad *sinkpad;
+ GstPad *srcpad;
+
+ gboolean split_packetized;
+
+ GstSegment segment;
+ gboolean packetized;
+ gboolean discont;
+
+ /* gather/decode queues for reverse playback */
+ GList *gather;
+ GstNalList *decode;
+ gboolean have_sps;
+ gboolean have_pps;
+ gboolean have_i_frame;
GstAdapter *adapter;
};