mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 06:46:38 +00:00
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.
This commit is contained in:
parent
ef7b89f382
commit
49f830d457
4 changed files with 491 additions and 227 deletions
14
ChangeLog
14
ChangeLog
|
@ -1,3 +1,17 @@
|
||||||
|
2006-02-13 Edward Hervey <edward@fluendo.com>
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
2006-02-13 Edgard Lima <edgard.lima@indt.org.br>
|
2006-02-13 Edgard Lima <edgard.lima@indt.org.br>
|
||||||
|
|
||||||
* configure.ac:
|
* configure.ac:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
plugin_LTLIBRARIES = libgstqtdemux.la
|
plugin_LTLIBRARIES = libgstqtdemux.la
|
||||||
|
|
||||||
libgstqtdemux_la_CFLAGS = ${GST_CFLAGS}
|
libgstqtdemux_la_CFLAGS = ${GST_CFLAGS}
|
||||||
libgstqtdemux_la_LIBADD =
|
libgstqtdemux_la_LIBADD = $(GST_BASE_LIBS)
|
||||||
libgstqtdemux_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
|
libgstqtdemux_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
|
||||||
libgstqtdemux_la_SOURCES = qtdemux.c
|
libgstqtdemux_la_SOURCES = qtdemux.c
|
||||||
|
|
||||||
|
|
|
@ -101,12 +101,9 @@ struct _QtDemuxStream
|
||||||
|
|
||||||
enum QtDemuxState
|
enum QtDemuxState
|
||||||
{
|
{
|
||||||
QTDEMUX_STATE_NULL,
|
QTDEMUX_STATE_INITIAL, /* Initial state (haven't got the header yet) */
|
||||||
QTDEMUX_STATE_HEADER,
|
QTDEMUX_STATE_HEADER, /* Parsing the header */
|
||||||
QTDEMUX_STATE_HEADER_SEEKING,
|
QTDEMUX_STATE_MOVIE, /* Parsing/Playing the media data */
|
||||||
QTDEMUX_STATE_SEEKING,
|
|
||||||
QTDEMUX_STATE_MOVIE,
|
|
||||||
QTDEMUX_STATE_SEEKING_EOS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
|
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,
|
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
static void gst_qtdemux_loop_header (GstPad * pad);
|
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 (GstPad * sinkpad);
|
||||||
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
|
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_moov (GstQTDemux * qtdemux, void *buffer, int length);
|
||||||
static void qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer,
|
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_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
|
||||||
gst_pad_set_activatepull_function (qtdemux->sinkpad,
|
gst_pad_set_activatepull_function (qtdemux->sinkpad,
|
||||||
qtdemux_sink_activate_pull);
|
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);
|
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->last_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
qtdemux->pullbased = FALSE;
|
||||||
|
qtdemux->neededbytes = 16;
|
||||||
|
qtdemux->todrop = 0;
|
||||||
|
qtdemux->adapter = gst_adapter_new ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -454,44 +462,23 @@ GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||||
"Quicktime stream demuxer",
|
"Quicktime stream demuxer",
|
||||||
plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);
|
plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* not needed, we don't work in streaming mode yet */
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_qtdemux_handle_sink_event (GstQTDemux * qtdemux)
|
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
|
||||||
{
|
{
|
||||||
gboolean res = TRUE;
|
GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
|
||||||
guint32 remaining;
|
gboolean res = FALSE;
|
||||||
GstEvent *event;
|
|
||||||
GstEventType type;
|
|
||||||
|
|
||||||
gst_bytestream_get_status (qtdemux->bs, &remaining, &event);
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_NEWSEGMENT:
|
||||||
type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
|
/* We need to convert it to a GST_FORMAT_TIME new segment */
|
||||||
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;
|
|
||||||
default:
|
default:
|
||||||
gst_pad_event_default (qtdemux->sinkpad, event);
|
gst_pad_event_default (demux->sinkpad, event);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_event_unref (event);
|
gst_event_unref (event);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
|
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:{
|
case GST_STATE_CHANGE_PAUSED_TO_READY:{
|
||||||
gint n;
|
gint n;
|
||||||
|
|
||||||
qtdemux->state = QTDEMUX_STATE_HEADER;
|
qtdemux->state = QTDEMUX_STATE_INITIAL;
|
||||||
qtdemux->last_ts = GST_CLOCK_TIME_NONE;
|
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++) {
|
for (n = 0; n < qtdemux->n_streams; n++) {
|
||||||
gst_element_remove_pad (element, qtdemux->streams[n]->pad);
|
gst_element_remove_pad (element, qtdemux->streams[n]->pad);
|
||||||
g_free (qtdemux->streams[n]->samples);
|
g_free (qtdemux->streams[n]->samples);
|
||||||
|
@ -525,37 +517,19 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_qtdemux_loop_header (GstPad * pad)
|
extract_initial_length_and_fourcc (guint8 * data, guint32 * plength,
|
||||||
|
guint32 * pfourcc)
|
||||||
{
|
{
|
||||||
GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
|
|
||||||
guint8 *data;
|
|
||||||
guint32 length;
|
guint32 length;
|
||||||
guint32 fourcc;
|
guint32 fourcc;
|
||||||
GstBuffer *buf = NULL;
|
|
||||||
guint64 offset;
|
|
||||||
guint64 cur_offset;
|
|
||||||
int size;
|
|
||||||
GstFlowReturn ret;
|
|
||||||
|
|
||||||
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);
|
length = GST_READ_UINT32_BE (data);
|
||||||
GST_DEBUG ("length %08x", length);
|
GST_DEBUG ("length %08x", length);
|
||||||
fourcc = GST_READ_UINT32_LE (data + 4);
|
fourcc = GST_READ_UINT32_LE (data + 4);
|
||||||
GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
|
GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
|
||||||
|
|
||||||
gst_buffer_unref (buf);
|
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
length = G_MAXUINT32; //gst_bytestream_length (qtdemux->bs) - cur_offset;
|
length = G_MAXUINT32;
|
||||||
}
|
}
|
||||||
if (length == 1) {
|
if (length == 1) {
|
||||||
/* this means we have an extended size, which is the 64 bit value of
|
/* this means we have an extended size, which is the 64 bit value of
|
||||||
|
@ -571,6 +545,28 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
length = length2;
|
length = length2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (plength)
|
||||||
|
*plength = length;
|
||||||
|
if (pfourcc)
|
||||||
|
*pfourcc = fourcc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
|
||||||
|
{
|
||||||
|
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) {
|
switch (fourcc) {
|
||||||
case GST_MAKE_FOURCC ('m', 'd', 'a', 't'):
|
case GST_MAKE_FOURCC ('m', 'd', 'a', 't'):
|
||||||
case GST_MAKE_FOURCC ('f', 'r', 'e', 'e'):
|
case GST_MAKE_FOURCC ('f', 'r', 'e', 'e'):
|
||||||
|
@ -581,10 +577,9 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
case GST_MAKE_FOURCC ('m', 'o', 'o', 'v'):{
|
case GST_MAKE_FOURCC ('m', 'o', 'o', 'v'):{
|
||||||
GstBuffer *moov;
|
GstBuffer *moov;
|
||||||
|
|
||||||
ret =
|
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
|
||||||
gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
|
|
||||||
if (ret != GST_FLOW_OK)
|
if (ret != GST_FLOW_OK)
|
||||||
goto error;
|
goto beach;
|
||||||
cur_offset += length;
|
cur_offset += length;
|
||||||
qtdemux->offset += length;
|
qtdemux->offset += length;
|
||||||
|
|
||||||
|
@ -599,8 +594,8 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
qtdemux->state = QTDEMUX_STATE_MOVIE;
|
qtdemux->state = QTDEMUX_STATE_MOVIE;
|
||||||
GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
|
GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
|
||||||
qtdemux->state);
|
qtdemux->state);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
ed_edd_and_eddy:
|
ed_edd_and_eddy:
|
||||||
default:{
|
default:{
|
||||||
GST_LOG ("unknown %08x '%" GST_FOURCC_FORMAT "' at %d",
|
GST_LOG ("unknown %08x '%" GST_FOURCC_FORMAT "' at %d",
|
||||||
|
@ -610,30 +605,24 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
ret = gst_bytestream_seek (qtdemux->bs, cur_offset + length,
|
beach:
|
||||||
GST_SEEK_METHOD_SET);
|
return ret;
|
||||||
GST_DEBUG ("seek returned %d", ret);
|
}
|
||||||
if (ret == FALSE) {
|
|
||||||
length = cur_offset + length;
|
static GstFlowReturn
|
||||||
cur_offset = qtdemux->offset;
|
gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
|
||||||
length -= cur_offset;
|
{
|
||||||
if (gst_bytestream_flush (qtdemux->bs, length) == FALSE) {
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
if (!gst_qtdemux_handle_sink_event (qtdemux)) {
|
GstBuffer *buf = NULL;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
//qtdemux->offset = cur_offset + length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QTDEMUX_STATE_MOVIE:{
|
|
||||||
QtDemuxStream *stream;
|
QtDemuxStream *stream;
|
||||||
guint64 min_time;
|
guint64 min_time;
|
||||||
|
guint64 offset;
|
||||||
|
int size;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* Figure out the next stream sample to output */
|
||||||
min_time = G_MAXUINT64;
|
min_time = G_MAXUINT64;
|
||||||
for (i = 0; i < qtdemux->n_streams; i++) {
|
for (i = 0; i < qtdemux->n_streams; i++) {
|
||||||
stream = qtdemux->streams[i];
|
stream = qtdemux->streams[i];
|
||||||
|
@ -650,7 +639,7 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
|
GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
|
||||||
gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
|
gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
|
||||||
break;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream = qtdemux->streams[index];
|
stream = qtdemux->streams[index];
|
||||||
|
@ -671,7 +660,7 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
|
|
||||||
ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf);
|
ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf);
|
||||||
if (ret != GST_FLOW_OK)
|
if (ret != GST_FLOW_OK)
|
||||||
goto error;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf) {
|
if (buf) {
|
||||||
|
@ -716,21 +705,39 @@ gst_qtdemux_loop_header (GstPad * pad)
|
||||||
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), stream->pad);
|
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), stream->pad);
|
||||||
gst_buffer_set_caps (buf, stream->caps);
|
gst_buffer_set_caps (buf, stream->caps);
|
||||||
ret = gst_pad_push (stream->pad, buf);
|
ret = gst_pad_push (stream->pad, buf);
|
||||||
if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED)
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->sample_index++;
|
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_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;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
/* oh crap */
|
/* oh crap */
|
||||||
g_error ("State=%d", qtdemux->state);
|
g_error ("State=%d", qtdemux->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
if (ret != GST_FLOW_OK) {
|
||||||
|
|
||||||
error:
|
|
||||||
{
|
|
||||||
GST_LOG_OBJECT (qtdemux, "pausing task, reason %s",
|
GST_LOG_OBJECT (qtdemux, "pausing task, reason %s",
|
||||||
gst_flow_get_name (ret));
|
gst_flow_get_name (ret));
|
||||||
gst_pad_pause_task (qtdemux->sinkpad);
|
gst_pad_pause_task (qtdemux->sinkpad);
|
||||||
|
@ -739,24 +746,244 @@ error:
|
||||||
GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
|
GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
|
||||||
(NULL), ("stream stopped, reason %s", gst_flow_get_name (ret)));
|
(NULL), ("stream stopped, reason %s", gst_flow_get_name (ret)));
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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).
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QTDEMUX_STATE_HEADER:{
|
||||||
|
guint8 *data;
|
||||||
|
guint32 fourcc;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (demux, "In header");
|
||||||
|
|
||||||
|
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 (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? */
|
||||||
|
/* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 (outbuf) =
|
||||||
|
stream->samples[stream->sample_index].timestamp;
|
||||||
|
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 (outbuf) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send buffer */
|
||||||
|
GST_LOG_OBJECT (demux,
|
||||||
|
"Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warning ("This line should never be reached\n");
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
qtdemux_sink_activate (GstPad * sinkpad)
|
qtdemux_sink_activate (GstPad * sinkpad)
|
||||||
{
|
{
|
||||||
if (gst_pad_check_pull_range (sinkpad))
|
if (gst_pad_check_pull_range (sinkpad))
|
||||||
return gst_pad_activate_pull (sinkpad, TRUE);
|
return gst_pad_activate_pull (sinkpad, TRUE);
|
||||||
|
else
|
||||||
return FALSE;
|
return gst_pad_activate_push (sinkpad, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
||||||
{
|
{
|
||||||
|
GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
|
||||||
|
|
||||||
if (active) {
|
if (active) {
|
||||||
/* if we have a scheduler we can start the task */
|
/* if we have a scheduler we can start the task */
|
||||||
|
demux->pullbased = TRUE;
|
||||||
gst_pad_start_task (sinkpad,
|
gst_pad_start_task (sinkpad,
|
||||||
(GstTaskFunction) gst_qtdemux_loop_header, sinkpad);
|
(GstTaskFunction) gst_qtdemux_loop_header, sinkpad);
|
||||||
} else {
|
} else {
|
||||||
|
@ -766,6 +993,16 @@ qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
||||||
return TRUE;
|
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
|
void
|
||||||
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
|
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
|
||||||
QtDemuxStream * stream, GstTagList * list)
|
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);
|
chunk_offset = QTDEMUX_GUINT64_GET (co64->data + 16 + j * 8);
|
||||||
}
|
}
|
||||||
for (k = 0; k < samples_per_chunk; k++) {
|
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].chunk = j;
|
||||||
samples[index].offset = chunk_offset;
|
samples[index].offset = chunk_offset;
|
||||||
chunk_offset += samples[index].size;
|
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,
|
GST_LOG_OBJECT (qtdemux, "co64 chunk %d offset %" G_GUINT64_FORMAT, j,
|
||||||
chunk_offset);
|
chunk_offset);
|
||||||
}
|
}
|
||||||
|
GST_LOG_OBJECT (qtdemux, "Creating entry %d with offset %lld",
|
||||||
|
j, chunk_offset);
|
||||||
samples[j].chunk = j;
|
samples[j].chunk = j;
|
||||||
samples[j].offset = chunk_offset;
|
samples[j].offset = chunk_offset;
|
||||||
if (stream->samples_per_packet * stream->compression != 0)
|
if (stream->samples_per_packet * stream->compression != 0)
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#define __GST_QTDEMUX_H__
|
#define __GST_QTDEMUX_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -61,6 +62,14 @@ struct _GstQTDemux {
|
||||||
|
|
||||||
int state;
|
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;
|
guint64 offset;
|
||||||
|
|
||||||
GstTagList *tag_list;
|
GstTagList *tag_list;
|
||||||
|
|
Loading…
Reference in a new issue