diff options
author | Wim Taymans <wim.taymans@gmail.com> | 2008-01-16 18:28:00 +0000 |
---|---|---|
committer | Wim Taymans <wim.taymans@gmail.com> | 2008-01-16 18:28:00 +0000 |
commit | 631b6a1766f76ed7075e03d5a94683f6a9c10fc2 (patch) | |
tree | 788cad0ccd9d64e2561edb480dc670d92c5f0699 /gst | |
parent | ae0feba9ec333fc281df4b3ab2ae1119da35f8b6 (diff) | |
download | gst-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.c | 717 | ||||
-rw-r--r-- | gst/h264parse/gsth264parse.h | 19 |
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; }; |