summaryrefslogtreecommitdiffstats
path: root/gst/qtdemux
diff options
context:
space:
mode:
authorEdward Hervey <bilboed@bilboed.com>2006-02-13 22:04:42 +0000
committerEdward Hervey <bilboed@bilboed.com>2006-02-13 22:04:42 +0000
commit49f830d457fbc69dcd9e3e23b44e3d1f5f53499a (patch)
tree2c7b9c10638333d4f350b94a4d17f995ec795a93 /gst/qtdemux
parentef7b89f382dc536bae955a250d0dc85762fb4a74 (diff)
downloadgst-plugins-bad-49f830d457fbc69dcd9e3e23b44e3d1f5f53499a.tar.gz
gst-plugins-bad-49f830d457fbc69dcd9e3e23b44e3d1f5f53499a.tar.bz2
gst-plugins-bad-49f830d457fbc69dcd9e3e23b44e3d1f5f53499a.zip
gst/qtdemux/: QtDemux can now work push-based.
Original commit message from CVS: * gst/qtdemux/Makefile.am: * gst/qtdemux/qtdemux.c: (gst_qtdemux_init), (gst_qtdemux_handle_sink_event), (gst_qtdemux_change_state), (extract_initial_length_and_fourcc), (gst_qtdemux_loop_state_header), (gst_qtdemux_loop_state_movie), (gst_qtdemux_loop_header), (next_entry_size), (gst_qtdemux_chain), (qtdemux_sink_activate), (qtdemux_sink_activate_pull), (qtdemux_sink_activate_push), (qtdemux_parse_trak): * gst/qtdemux/qtdemux.h: QtDemux can now work push-based. It still needs some love for seeking.
Diffstat (limited to 'gst/qtdemux')
-rw-r--r--gst/qtdemux/Makefile.am2
-rw-r--r--gst/qtdemux/qtdemux.c663
-rw-r--r--gst/qtdemux/qtdemux.h9
3 files changed, 462 insertions, 212 deletions
diff --git a/gst/qtdemux/Makefile.am b/gst/qtdemux/Makefile.am
index 9c6a9659..f65f337e 100644
--- a/gst/qtdemux/Makefile.am
+++ b/gst/qtdemux/Makefile.am
@@ -2,7 +2,7 @@
plugin_LTLIBRARIES = libgstqtdemux.la
libgstqtdemux_la_CFLAGS = ${GST_CFLAGS}
-libgstqtdemux_la_LIBADD =
+libgstqtdemux_la_LIBADD = $(GST_BASE_LIBS)
libgstqtdemux_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
libgstqtdemux_la_SOURCES = qtdemux.c
diff --git a/gst/qtdemux/qtdemux.c b/gst/qtdemux/qtdemux.c
index b22a103f..77458aec 100644
--- a/gst/qtdemux/qtdemux.c
+++ b/gst/qtdemux/qtdemux.c
@@ -101,12 +101,9 @@ struct _QtDemuxStream
enum QtDemuxState
{
- QTDEMUX_STATE_NULL,
- QTDEMUX_STATE_HEADER,
- QTDEMUX_STATE_HEADER_SEEKING,
- QTDEMUX_STATE_SEEKING,
- QTDEMUX_STATE_MOVIE,
- QTDEMUX_STATE_SEEKING_EOS,
+ QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
+ QTDEMUX_STATE_HEADER, /* Parsing the header */
+ QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
};
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
@@ -146,8 +143,11 @@ static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
GstStateChange transition);
static void gst_qtdemux_loop_header (GstPad * pad);
+static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
+static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
+static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event);
static void qtdemux_parse_moov (GstQTDemux * qtdemux, void *buffer, int length);
static void qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer,
@@ -230,10 +230,18 @@ gst_qtdemux_init (GstQTDemux * qtdemux)
gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
gst_pad_set_activatepull_function (qtdemux->sinkpad,
qtdemux_sink_activate_pull);
+ gst_pad_set_activatepush_function (qtdemux->sinkpad,
+ qtdemux_sink_activate_push);
+ gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
+ gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
gst_element_add_pad (GST_ELEMENT (qtdemux), qtdemux->sinkpad);
- qtdemux->state = QTDEMUX_STATE_HEADER;
+ qtdemux->state = QTDEMUX_STATE_INITIAL;
qtdemux->last_ts = GST_CLOCK_TIME_NONE;
+ qtdemux->pullbased = FALSE;
+ qtdemux->neededbytes = 16;
+ qtdemux->todrop = 0;
+ qtdemux->adapter = gst_adapter_new ();
}
#if 0
@@ -454,44 +462,23 @@ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
"Quicktime stream demuxer",
plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);
-#if 0
-/* not needed, we don't work in streaming mode yet */
static gboolean
-gst_qtdemux_handle_sink_event (GstQTDemux * qtdemux)
+gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
{
- gboolean res = TRUE;
- guint32 remaining;
- GstEvent *event;
- GstEventType type;
-
- gst_bytestream_get_status (qtdemux->bs, &remaining, &event);
-
- type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
- GST_DEBUG ("qtdemux: event %p %d", event, type);
-
- switch (type) {
- case GST_EVENT_INTERRUPT:
- gst_event_unref (event);
- return FALSE;
- case GST_EVENT_EOS:
- gst_bytestream_flush (qtdemux->bs, remaining);
- gst_pad_event_default (qtdemux->sinkpad, event);
- return FALSE;
- case GST_EVENT_FLUSH:
- break;
- case GST_EVENT_DISCONTINUOUS:
- GST_DEBUG ("discontinuous event");
- //gst_bytestream_flush_fast(qtdemux->bs, remaining);
- break;
+ GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
+ gboolean res = FALSE;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NEWSEGMENT:
+ /* We need to convert it to a GST_FORMAT_TIME new segment */
default:
- gst_pad_event_default (qtdemux->sinkpad, event);
+ gst_pad_event_default (demux->sinkpad, event);
return TRUE;
}
gst_event_unref (event);
return res;
}
-#endif
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
@@ -505,8 +492,13 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_PAUSED_TO_READY:{
gint n;
- qtdemux->state = QTDEMUX_STATE_HEADER;
+ qtdemux->state = QTDEMUX_STATE_INITIAL;
qtdemux->last_ts = GST_CLOCK_TIME_NONE;
+ qtdemux->neededbytes = 16;
+ qtdemux->todrop = 0;
+ qtdemux->pullbased = FALSE;
+ qtdemux->offset = 0;
+ gst_adapter_clear (qtdemux->adapter);
for (n = 0; n < qtdemux->n_streams; n++) {
gst_element_remove_pad (element, qtdemux->streams[n]->pad);
g_free (qtdemux->streams[n]->samples);
@@ -525,222 +517,454 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
}
static void
-gst_qtdemux_loop_header (GstPad * pad)
+extract_initial_length_and_fourcc (guint8 * data, guint32 * plength,
+ guint32 * pfourcc)
+{
+ guint32 length;
+ guint32 fourcc;
+
+ length = GST_READ_UINT32_BE (data);
+ GST_DEBUG ("length %08x", length);
+ fourcc = GST_READ_UINT32_LE (data + 4);
+ GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+
+ if (length == 0) {
+ length = G_MAXUINT32;
+ }
+ if (length == 1) {
+ /* this means we have an extended size, which is the 64 bit value of
+ * the next 8 bytes */
+ guint32 length1, length2;
+
+ length1 = GST_READ_UINT32_BE (data + 8);
+ GST_DEBUG ("length1 %08x", length1);
+ length2 = GST_READ_UINT32_BE (data + 12);
+ GST_DEBUG ("length2 %08x", length2);
+
+ /* FIXME: I guess someone didn't want to make 64 bit size work :) */
+ length = length2;
+ }
+
+ if (plength)
+ *plength = length;
+ if (pfourcc)
+ *pfourcc = fourcc;
+}
+
+static GstFlowReturn
+gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
{
- GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
- guint8 *data;
guint32 length;
guint32 fourcc;
GstBuffer *buf = NULL;
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint64 cur_offset = qtdemux->offset;
+
+ ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
+ if (ret != GST_FLOW_OK)
+ goto beach;
+ extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), &length, &fourcc);
+ gst_buffer_unref (buf);
+
+
+ switch (fourcc) {
+ case GST_MAKE_FOURCC ('m', 'd', 'a', 't'):
+ case GST_MAKE_FOURCC ('f', 'r', 'e', 'e'):
+ case GST_MAKE_FOURCC ('w', 'i', 'd', 'e'):
+ case GST_MAKE_FOURCC ('P', 'I', 'C', 'T'):
+ case GST_MAKE_FOURCC ('p', 'n', 'o', 't'):
+ goto ed_edd_and_eddy;
+ case GST_MAKE_FOURCC ('m', 'o', 'o', 'v'):{
+ GstBuffer *moov;
+
+ ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
+ if (ret != GST_FLOW_OK)
+ goto beach;
+ cur_offset += length;
+ qtdemux->offset += length;
+
+ qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
+ if (1) {
+ qtdemux_node_dump (qtdemux, qtdemux->moov_node);
+ }
+ qtdemux_parse_tree (qtdemux);
+ g_node_destroy (qtdemux->moov_node);
+ gst_buffer_unref (moov);
+ qtdemux->moov_node = NULL;
+ qtdemux->state = QTDEMUX_STATE_MOVIE;
+ GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
+ qtdemux->state);
+ }
+ break;
+ ed_edd_and_eddy:
+ default:{
+ GST_LOG ("unknown %08x '%" GST_FOURCC_FORMAT "' at %d",
+ fourcc, GST_FOURCC_ARGS (fourcc), cur_offset);
+ cur_offset += length;
+ qtdemux->offset += length;
+ break;
+ }
+ }
+
+beach:
+ return ret;
+}
+
+static GstFlowReturn
+gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstBuffer *buf = NULL;
+ QtDemuxStream *stream;
+ guint64 min_time;
guint64 offset;
- guint64 cur_offset;
int size;
- GstFlowReturn ret;
+ int index = -1;
+ int i;
+
+ /* Figure out the next stream sample to output */
+ min_time = G_MAXUINT64;
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ stream = qtdemux->streams[i];
+ GST_LOG_OBJECT (qtdemux,
+ "stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i,
+ stream->sample_index,
+ GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
+ if (stream->sample_index < stream->n_samples
+ && stream->samples[stream->sample_index].timestamp < min_time) {
+ min_time = stream->samples[stream->sample_index].timestamp;
+ index = i;
+ }
+ }
+ if (index == -1) {
+ GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
+ gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
+ goto beach;
+ }
+
+ stream = qtdemux->streams[index];
+
+ offset = stream->samples[stream->sample_index].offset;
+ size = stream->samples[stream->sample_index].size;
+
+ GST_LOG_OBJECT (qtdemux,
+ "pushing from stream %d, sample_index=%d offset=%" G_GUINT64_FORMAT
+ ",size=%d timestamp=%" GST_TIME_FORMAT,
+ index, stream->sample_index, offset, size,
+ GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
+
+ buf = NULL;
+ if (size > 0) {
+ GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
+ offset);
+
+ ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf);
+ if (ret != GST_FLOW_OK)
+ goto beach;
+ }
+
+ if (buf) {
+ /* hum... FIXME changing framerate breaks horribly, better set
+ * an average framerate, or get rid of the framerate property. */
+ if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
+ //float fps =
+ // 1. * GST_SECOND / stream->samples[stream->sample_index].duration;
+ /*
+ if (fps != stream->fps) {
+ gst_caps_set_simple (stream->caps, "framerate", G_TYPE_DOUBLE, fps,
+ NULL);
+ stream->fps = fps;
+ gst_pad_set_explicit_caps (stream->pad, stream->caps);
+ }
+ */
+ }
+
+ /* first buffer? */
+ if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
+ gst_qtdemux_send_event (qtdemux,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ 0, GST_CLOCK_TIME_NONE, 0));
+ }
+
+ /* timestamps of AMR aren't known... */
+ if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
+ GST_BUFFER_TIMESTAMP (buf) =
+ stream->samples[stream->sample_index].timestamp;
+ qtdemux->last_ts = GST_BUFFER_TIMESTAMP (buf);
+ GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int
+ (stream->samples[stream->sample_index].duration, GST_SECOND,
+ stream->timescale);
+ } else {
+ if (stream->sample_index == 0) {
+ GST_BUFFER_TIMESTAMP (buf) = 0;
+ }
+ }
+
+ GST_LOG_OBJECT (qtdemux,
+ "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), stream->pad);
+ gst_buffer_set_caps (buf, stream->caps);
+ ret = gst_pad_push (stream->pad, buf);
+ }
+
+ stream->sample_index++;
+
+beach:
+ return ret;
+}
+
+static void
+gst_qtdemux_loop_header (GstPad * pad)
+{
+ GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
+ guint64 cur_offset;
+ GstFlowReturn ret = GST_FLOW_ERROR;
cur_offset = qtdemux->offset;
GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
cur_offset, qtdemux->state);
switch (qtdemux->state) {
- case QTDEMUX_STATE_HEADER:{
- ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
- if (ret != GST_FLOW_OK)
- goto error;
- data = GST_BUFFER_DATA (buf);
- length = GST_READ_UINT32_BE (data);
- GST_DEBUG ("length %08x", length);
- fourcc = GST_READ_UINT32_LE (data + 4);
- GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
+ case QTDEMUX_STATE_INITIAL:
+ case QTDEMUX_STATE_HEADER:
+ ret = gst_qtdemux_loop_state_header (qtdemux);
+ break;
+ case QTDEMUX_STATE_MOVIE:
+ ret = gst_qtdemux_loop_state_movie (qtdemux);
+ break;
+ default:
+ /* oh crap */
+ g_error ("State=%d", qtdemux->state);
+ }
- gst_buffer_unref (buf);
+ if (ret != GST_FLOW_OK) {
+ GST_LOG_OBJECT (qtdemux, "pausing task, reason %s",
+ gst_flow_get_name (ret));
+ gst_pad_pause_task (qtdemux->sinkpad);
+ if (GST_FLOW_IS_FATAL (ret)) {
+ gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
+ GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
+ (NULL), ("stream stopped, reason %s", gst_flow_get_name (ret)));
+ }
+ }
+}
- if (length == 0) {
- length = G_MAXUINT32; //gst_bytestream_length (qtdemux->bs) - cur_offset;
- }
- if (length == 1) {
- /* this means we have an extended size, which is the 64 bit value of
- * the next 8 bytes */
- guint32 length1, length2;
-
- length1 = GST_READ_UINT32_BE (data + 8);
- GST_DEBUG ("length1 %08x", length1);
- length2 = GST_READ_UINT32_BE (data + 12);
- GST_DEBUG ("length2 %08x", length2);
-
- /* FIXME: I guess someone didn't want to make 64 bit size work :) */
- length = length2;
- }
+/*
+ next_entry_size
+
+ Returns the size of the first entry at the current offset.
+ If -1, there are none (which means EOS or empty file).
+*/
- switch (fourcc) {
- case GST_MAKE_FOURCC ('m', 'd', 'a', 't'):
- case GST_MAKE_FOURCC ('f', 'r', 'e', 'e'):
- case GST_MAKE_FOURCC ('w', 'i', 'd', 'e'):
- case GST_MAKE_FOURCC ('P', 'I', 'C', 'T'):
- case GST_MAKE_FOURCC ('p', 'n', 'o', 't'):
- goto ed_edd_and_eddy;
- case GST_MAKE_FOURCC ('m', 'o', 'o', 'v'):{
- GstBuffer *moov;
-
- ret =
- gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
- if (ret != GST_FLOW_OK)
- goto error;
- cur_offset += length;
- qtdemux->offset += length;
-
- qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
- if (1) {
- qtdemux_node_dump (qtdemux, qtdemux->moov_node);
- }
- qtdemux_parse_tree (qtdemux);
- g_node_destroy (qtdemux->moov_node);
- gst_buffer_unref (moov);
- qtdemux->moov_node = NULL;
- qtdemux->state = QTDEMUX_STATE_MOVIE;
- GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
- qtdemux->state);
- break;
- }
- ed_edd_and_eddy:
- default:{
- GST_LOG ("unknown %08x '%" GST_FOURCC_FORMAT "' at %d",
- fourcc, GST_FOURCC_ARGS (fourcc), cur_offset);
- cur_offset += length;
- qtdemux->offset += length;
- break;
- }
- }
-#if 0
- ret = gst_bytestream_seek (qtdemux->bs, cur_offset + length,
- GST_SEEK_METHOD_SET);
- GST_DEBUG ("seek returned %d", ret);
- if (ret == FALSE) {
- length = cur_offset + length;
- cur_offset = qtdemux->offset;
- length -= cur_offset;
- if (gst_bytestream_flush (qtdemux->bs, length) == FALSE) {
- if (!gst_qtdemux_handle_sink_event (qtdemux)) {
- return;
- }
- }
- }
-#endif
- //qtdemux->offset = cur_offset + length;
- break;
+static guint64
+next_entry_size (GstQTDemux * demux)
+{
+ QtDemuxStream *stream;
+ int i;
+ int smallidx = 0;
+ guint64 smalloffs = -1;
+
+ GST_LOG_OBJECT (demux, "Finding entry at offset %lld", demux->offset);
+
+ for (i = 0; i < demux->n_streams; i++) {
+ stream = demux->streams[i];
+
+ GST_LOG_OBJECT (demux,
+ "Checking Stream %d (sample_index:%d / offset:%lld / size:%d / chunk:%d)",
+ i, stream->sample_index, stream->samples[stream->sample_index].offset,
+ stream->samples[stream->sample_index].size,
+ stream->samples[stream->sample_index].chunk);
+
+ if ((smalloffs == -1)
+ || (stream->samples[stream->sample_index].offset < smalloffs)) {
+ smallidx = i;
+ smalloffs = stream->samples[stream->sample_index].offset;
}
- case QTDEMUX_STATE_MOVIE:{
- QtDemuxStream *stream;
- guint64 min_time;
- int index = -1;
- int i;
-
- min_time = G_MAXUINT64;
- for (i = 0; i < qtdemux->n_streams; i++) {
- stream = qtdemux->streams[i];
- GST_LOG_OBJECT (qtdemux,
- "stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i,
- stream->sample_index,
- GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
- if (stream->sample_index < stream->n_samples
- && stream->samples[stream->sample_index].timestamp < min_time) {
- min_time = stream->samples[stream->sample_index].timestamp;
- index = i;
+ }
+
+ GST_LOG_OBJECT (demux, "stream %d offset %lld demux->offset :%lld",
+ smallidx, smalloffs, demux->offset);
+
+ stream = demux->streams[smallidx];
+
+ if (stream->samples[stream->sample_index].offset >= demux->offset) {
+ demux->todrop =
+ stream->samples[stream->sample_index].offset - demux->offset;
+ return stream->samples[stream->sample_index].size + demux->todrop;
+ }
+
+ GST_DEBUG_OBJECT (demux, "There wasn't any entry at offset %lld",
+ demux->offset);
+ return -1;
+}
+
+static GstFlowReturn
+gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
+{
+ GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ gst_adapter_push (demux->adapter, inbuf);
+
+ GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
+ inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));
+
+ while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
+ ret == GST_FLOW_OK) {
+
+ GST_DEBUG_OBJECT (demux,
+ "state:%d , demux->neededbytes:%d, demux->offset:%lld", demux->state,
+ demux->neededbytes, demux->offset);
+
+ switch (demux->state) {
+ case QTDEMUX_STATE_INITIAL:{
+ const guint8 *data;
+ guint32 fourcc;
+ guint32 size;
+
+ data = gst_adapter_peek (demux->adapter, demux->neededbytes);
+
+ /* get fourcc/length, set neededbytes */
+ extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc);
+ if (fourcc == GST_MAKE_FOURCC ('m', 'd', 'a', 't')) {
+ demux->state = QTDEMUX_STATE_MOVIE;
+ demux->offset += 24;
+ gst_adapter_flush (demux->adapter, 24);
+ demux->neededbytes = next_entry_size (demux);
+ } else {
+ demux->neededbytes = size;
+ demux->state = QTDEMUX_STATE_HEADER;
}
}
- if (index == -1) {
- GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
- gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
break;
- }
- stream = qtdemux->streams[index];
+ case QTDEMUX_STATE_HEADER:{
+ guint8 *data;
+ guint32 fourcc;
- offset = stream->samples[stream->sample_index].offset;
- size = stream->samples[stream->sample_index].size;
+ GST_DEBUG_OBJECT (demux, "In header");
- GST_LOG_OBJECT (qtdemux,
- "pushing from stream %d, sample_index=%d offset=%" G_GUINT64_FORMAT
- ",size=%d timestamp=%" GST_TIME_FORMAT,
- index, stream->sample_index, offset, size,
- GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
-
- buf = NULL;
- if (size > 0) {
- GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
- offset);
-
- ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf);
- if (ret != GST_FLOW_OK)
- goto error;
+ data = gst_adapter_take (demux->adapter, demux->neededbytes);
+
+ /* parse the header */
+ extract_initial_length_and_fourcc (data, NULL, &fourcc);
+ if (fourcc == GST_MAKE_FOURCC ('m', 'o', 'o', 'v')) {
+ GST_DEBUG_OBJECT (demux, "Parsing [moov]");
+
+ demux->offset += demux->neededbytes;
+ qtdemux_parse_moov (demux, data, demux->neededbytes);
+ qtdemux_node_dump (demux, demux->moov_node);
+ qtdemux_parse_tree (demux);
+
+ g_node_destroy (demux->moov_node);
+ g_free (data);
+ demux->moov_node = NULL;
+
+ demux->neededbytes = 16;
+ demux->state = QTDEMUX_STATE_INITIAL;
+ } else {
+ GST_WARNING_OBJECT (demux,
+ "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (fourcc));
+ /* Let's jump that one and go back to initial state */
+ demux->offset += demux->neededbytes;
+ demux->neededbytes = 16;
+ demux->state = QTDEMUX_STATE_INITIAL;
+ }
}
+ break;
+
+ case QTDEMUX_STATE_MOVIE:{
+ guint8 *data;
+ GstBuffer *outbuf;
+ QtDemuxStream *stream = NULL;
+ int i = -1;
+
+ GST_DEBUG_OBJECT (demux, "BEGIN // in MOVIE for offset %lld",
+ demux->offset);
- if (buf) {
- /* hum... FIXME changing framerate breaks horribly, better set
- * an average framerate, or get rid of the framerate property. */
- if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
- //float fps =
- // 1. * GST_SECOND / stream->samples[stream->sample_index].duration;
- /*
- if (fps != stream->fps) {
- gst_caps_set_simple (stream->caps, "framerate", G_TYPE_DOUBLE, fps,
- NULL);
- stream->fps = fps;
- gst_pad_set_explicit_caps (stream->pad, stream->caps);
- }
- */
+ if (demux->todrop) {
+ gst_adapter_flush (demux->adapter, demux->todrop);
+ demux->neededbytes -= demux->todrop;
+ demux->offset += demux->todrop;
+ }
+
+ /* Figure out which stream this is packet belongs to */
+ for (i = 0; i < demux->n_streams; i++) {
+ stream = demux->streams[i];
+ GST_LOG_OBJECT (demux,
+ "Checking stream %d (sample_index:%d / offset:%lld / size:%d / chunk:%d)",
+ i, stream->sample_index,
+ stream->samples[stream->sample_index].offset,
+ stream->samples[stream->sample_index].size,
+ stream->samples[stream->sample_index].chunk);
+ if (stream->samples[stream->sample_index].offset == demux->offset)
+ break;
+ }
+
+ if (stream == NULL) {
+ GST_WARNING_OBJECT (demux, "WHAT THE FUCK ?");
+ ret = GST_FLOW_ERROR;
+ break;
}
/* first buffer? */
- if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
- gst_qtdemux_send_event (qtdemux,
+ /* FIXME : this should be handled in sink_event */
+ if (demux->last_ts == GST_CLOCK_TIME_NONE) {
+ gst_qtdemux_send_event (demux,
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
0, GST_CLOCK_TIME_NONE, 0));
}
- /* timestamps of AMR aren't known... */
+ /* get data */
+ data = gst_adapter_take (demux->adapter, demux->neededbytes);
+
+ /* Put data in a buffer, set timestamps, caps, ... */
+ outbuf = gst_buffer_new ();
+ gst_buffer_set_data (outbuf, data, demux->neededbytes);
+ GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
+ GST_FOURCC_ARGS (stream->fourcc));
+
if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
- GST_BUFFER_TIMESTAMP (buf) =
+ GST_BUFFER_TIMESTAMP (outbuf) =
stream->samples[stream->sample_index].timestamp;
- qtdemux->last_ts = GST_BUFFER_TIMESTAMP (buf);
- GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int
+ demux->last_ts = GST_BUFFER_TIMESTAMP (outbuf);
+ GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int
(stream->samples[stream->sample_index].duration, GST_SECOND,
stream->timescale);
+
} else {
- if (stream->sample_index == 0) {
- GST_BUFFER_TIMESTAMP (buf) = 0;
- }
+ if (stream->sample_index == 0)
+ GST_BUFFER_TIMESTAMP (outbuf) = 0;
}
- GST_LOG_OBJECT (qtdemux,
+ /* send buffer */
+ GST_LOG_OBJECT (demux,
"Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), stream->pad);
- gst_buffer_set_caps (buf, stream->caps);
- ret = gst_pad_push (stream->pad, buf);
- if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
- goto error;
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), stream->pad);
+ gst_buffer_set_caps (outbuf, stream->caps);
+ ret = gst_pad_push (stream->pad, outbuf);
+
+ stream->sample_index++;
+
+ /* update current offset and figure out size of next buffer */
+ GST_LOG_OBJECT (demux, "bumping offset:%lld up by %lld",
+ demux->offset, demux->neededbytes);
+ demux->offset += demux->neededbytes;
+ GST_LOG_OBJECT (demux, "offset is now %lld", demux->offset);
+
+ if ((demux->neededbytes = next_entry_size (demux)) == -1)
+ ret = GST_FLOW_ERROR;
}
- stream->sample_index++;
- break;
+ break;
+ default:
+ g_warning ("This line should never be reached\n");
+ ret = GST_FLOW_ERROR;
}
- default:
- /* oh crap */
- g_error ("State=%d", qtdemux->state);
}
- return;
-error:
- {
- GST_LOG_OBJECT (qtdemux, "pausing task, reason %s",
- gst_flow_get_name (ret));
- gst_pad_pause_task (qtdemux->sinkpad);
- if (GST_FLOW_IS_FATAL (ret)) {
- gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
- GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
- (NULL), ("stream stopped, reason %s", gst_flow_get_name (ret)));
- }
- return;
- }
+ return ret;
}
static gboolean
@@ -748,15 +972,18 @@ qtdemux_sink_activate (GstPad * sinkpad)
{
if (gst_pad_check_pull_range (sinkpad))
return gst_pad_activate_pull (sinkpad, TRUE);
-
- return FALSE;
+ else
+ return gst_pad_activate_push (sinkpad, TRUE);
}
static gboolean
qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
{
+ GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
+
if (active) {
/* if we have a scheduler we can start the task */
+ demux->pullbased = TRUE;
gst_pad_start_task (sinkpad,
(GstTaskFunction) gst_qtdemux_loop_header, sinkpad);
} else {
@@ -766,6 +993,16 @@ qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
return TRUE;
}
+static gboolean
+qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
+{
+ GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
+
+ demux->pullbased = FALSE;
+
+ return TRUE;
+}
+
void
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
QtDemuxStream * stream, GstTagList * list)
@@ -2296,6 +2533,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
chunk_offset = QTDEMUX_GUINT64_GET (co64->data + 16 + j * 8);
}
for (k = 0; k < samples_per_chunk; k++) {
+ GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %lld",
+ index, chunk_offset);
samples[index].chunk = j;
samples[index].offset = chunk_offset;
chunk_offset += samples[index].size;
@@ -2380,6 +2619,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
GST_LOG_OBJECT (qtdemux, "co64 chunk %d offset %" G_GUINT64_FORMAT, j,
chunk_offset);
}
+ GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %lld",
+ j, chunk_offset);
samples[j].chunk = j;
samples[j].offset = chunk_offset;
if (stream->samples_per_packet * stream->compression != 0)
diff --git a/gst/qtdemux/qtdemux.h b/gst/qtdemux/qtdemux.h
index 21584f58..42aa1571 100644
--- a/gst/qtdemux/qtdemux.h
+++ b/gst/qtdemux/qtdemux.h
@@ -22,6 +22,7 @@
#define __GST_QTDEMUX_H__
#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
@@ -61,6 +62,14 @@ struct _GstQTDemux {
int state;
+ gboolean pullbased;
+
+ /* push based variables */
+ guint neededbytes;
+ guint todrop;
+ GstAdapter *adapter;
+
+ /* offset of the media data (i.e.: Size of header) */
guint64 offset;
GstTagList *tag_list;