gst/qtdemux/qtdemux.c: More cleanups, added comments.

Original commit message from CVS:
* gst/qtdemux/qtdemux.c: (gst_qtdemux_go_back),
(gst_qtdemux_perform_seek), (gst_qtdemux_do_seek),
(gst_qtdemux_loop_state_movie), (gst_qtdemux_loop),
(gst_qtdemux_chain), (qtdemux_parse_tree), (qtdemux_parse_trak):
More cleanups, added comments.
Mark discontinuities on outgoing buffers.
Post better errors when something goes wrong.
Handle EOS and segment end properly.
This commit is contained in:
Wim Taymans 2006-04-04 11:20:58 +00:00
parent 075303cf8e
commit 378bcc051d

View file

@ -1,6 +1,7 @@
/* GStreamer /* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2003> David A. Schleef <ds@schleef.org> * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
* Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -53,22 +54,22 @@ struct _QtNode
{ {
guint32 type; guint32 type;
gpointer data; gpointer data;
int len; gint len;
}; };
struct _QtNodeType struct _QtNodeType
{ {
guint32 fourcc; guint32 fourcc;
char *name; gchar *name;
int flags; gint flags;
void (*dump) (GstQTDemux * qtdemux, void *buffer, int depth); void (*dump) (GstQTDemux * qtdemux, void *buffer, int depth);
}; };
struct _QtDemuxSample struct _QtDemuxSample
{ {
int sample_index; gint sample_index;
int chunk; gint chunk;
int size; gint size;
guint64 offset; guint64 offset;
guint64 timestamp; /* In GstClockTime */ guint64 timestamp; /* In GstClockTime */
guint32 duration; /* in stream->timescale units */ guint32 duration; /* in stream->timescale units */
@ -81,26 +82,29 @@ struct _QtDemuxStream
GstCaps *caps; GstCaps *caps;
guint32 fourcc; guint32 fourcc;
GstPad *pad; GstPad *pad;
int n_samples; gint n_samples;
QtDemuxSample *samples; QtDemuxSample *samples;
int timescale; gint timescale;
gboolean all_keyframe; /* TRUE when all packets are keyframes (no stss) */ gboolean all_keyframe; /* TRUE when all packets are keyframes (no stss) */
int sample_index; gint sample_index;
int width; gint width;
int height; gint height;
/* Numerator/denominator framerate */ /* Numerator/denominator framerate */
gint fps_n; gint fps_n;
gint fps_d; gint fps_d;
double rate; gdouble rate;
int n_channels; gint n_channels;
guint bytes_per_frame; guint bytes_per_frame;
guint compression; guint compression;
guint samples_per_packet; guint samples_per_packet;
guint16 bits_per_sample; guint16 bits_per_sample;
guint16 color_table_id; guint16 color_table_id;
/* when a discontinuity is pending */
gboolean discont;
}; };
enum QtDemuxState enum QtDemuxState
@ -472,14 +476,16 @@ gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
gst_event_unref (event); gst_event_unref (event);
} }
/* move all streams back to the given position, This function will /* move all streams back on the keyframe before @offset.
* always position the streams on a keyframe before offset.
* *
* Start to search back from the stream sample_index position or * If @end is FALSE, the search is started from the current
* when end is TRUE, from the last sample of the stream. * sample_index position of each stream.
* If @end is TRUE, the search is started from the last sample
* of each stream.
* *
* Returns the minimum of the timestamps of the positions of all streams. * Returns: the minimum of the timestamps of the positions of all streams.
*/ */
/* FIXME, binary search would be nice here */
static guint64 static guint64
gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset) gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset)
{ {
@ -490,34 +496,35 @@ gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset)
for (n = 0; n < qtdemux->n_streams; n++) { for (n = 0; n < qtdemux->n_streams; n++) {
QtDemuxStream *str; QtDemuxStream *str;
gboolean keyframe; gboolean keyframe;
gint i; gint search;
str = qtdemux->streams[n]; str = qtdemux->streams[n];
keyframe = str->all_keyframe; keyframe = str->all_keyframe;
if (end) /* start from the last sample if @end == TRUE */
str->sample_index = str->n_samples - 1; if (end) {
if (str->n_samples == 0)
search = 0;
else
search = str->n_samples - 1;
} else
search = str->sample_index;
if (str->sample_index == 0) { for (; search > 0; search--) {
/* can't go back, we're at the beginning */
min_time = 0;
continue;
}
for (i = str->sample_index; i >= 0; i--) {
guint64 timestamp; guint64 timestamp;
timestamp = str->samples[i].timestamp; timestamp = str->samples[search].timestamp;
/* Seek to the sample just before the desired offset and /* Seek to the sample just before the desired offset and
* let downstream throw away bits outside of the segment */ * let downstream throw away bits outside of the segment */
if (timestamp <= offset) { if (timestamp <= offset) {
/* update the keyframe flag */ /* update the keyframe flag */
keyframe = keyframe | str->samples[i].keyframe; keyframe = keyframe | str->samples[search].keyframe;
if (keyframe) { if (keyframe) {
GST_DEBUG_OBJECT (qtdemux, GST_DEBUG_OBJECT (qtdemux,
"found keyframe at sample %d, %" GST_TIME_FORMAT, i, "found keyframe at sample %d, %" GST_TIME_FORMAT, search,
GST_TIME_ARGS (timestamp)); GST_TIME_ARGS (timestamp));
/* update min_time */
if (timestamp < min_time) if (timestamp < min_time)
min_time = timestamp; min_time = timestamp;
break; break;
@ -525,13 +532,17 @@ gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset)
} }
} }
/* did not find anything, position to beginning */ /* did not find anything or we're at the beginning, position to beginning */
if (i <= 0) { if (search <= 0) {
i = 0; search = 0;
min_time = 0; min_time = 0;
} }
/* and set stream to the index */
str->sample_index = i; if (search != str->sample_index) {
str->sample_index = search;
/* position changed, we have a discont */
str->discont = TRUE;
}
} }
return min_time; return min_time;
} }
@ -539,9 +550,10 @@ gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset)
/* perform the seek. /* perform the seek.
* *
* We always go to the keyframe before the desired seek position. If * We always go to the keyframe before the desired seek position. If
* the seek was to a keyframe, we update the last_stop with the position * the seek was to a keyframe, we update the last_stop and time with
* of the keyframe, else we leve the event as-is and it will be clipped * the position of the keyframe, else we leve the event as-is and it
* automatically to the right segment boundaries by downstream elements. * will be clipped automatically to the right segment boundaries by
* downstream elements.
*/ */
static gboolean static gboolean
gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment) gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
@ -565,13 +577,14 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
* position. We start from our current position. */ * position. We start from our current position. */
gst_qtdemux_go_back (qtdemux, FALSE, min); gst_qtdemux_go_back (qtdemux, FALSE, min);
/* update the segment to the position of the keyframes */ /* update the segment values to the position of the keyframes */
segment->last_stop = min; segment->last_stop = min;
segment->time = min; segment->time = min;
} }
return TRUE; return TRUE;
} }
/* do a seek in pull based mode */
static gboolean static gboolean
gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
{ {
@ -617,12 +630,14 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
GST_DEBUG_OBJECT (qtdemux, "seek format %d", format); GST_DEBUG_OBJECT (qtdemux, "seek format %d", format);
/* stop streaming, either by flushing or by pausing the task */
if (flush) { if (flush) {
/* unlock upstream pull_range */ /* unlock upstream pull_range */
gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ()); gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
/* make sure out loop function exits */ /* make sure out loop function exits */
gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ()); gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ());
} else { } else {
/* non flushing seek, pause the task */
qtdemux->segment_running = FALSE; qtdemux->segment_running = FALSE;
gst_pad_pause_task (qtdemux->sinkpad); gst_pad_pause_task (qtdemux->sinkpad);
} }
@ -641,7 +656,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
cur_type, cur, stop_type, stop, &update); cur_type, cur, stop_type, stop, &update);
} }
/* now do the seek */ /* now do the seek, this actually never returns FALSE */
res = gst_qtdemux_perform_seek (qtdemux, &seeksegment); res = gst_qtdemux_perform_seek (qtdemux, &seeksegment);
/* prepare for streaming again */ /* prepare for streaming again */
@ -662,6 +677,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
qtdemux->segment.time)); qtdemux->segment.time));
} }
/* commit the new segment */
memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment)); memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));
if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) { if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
@ -670,7 +686,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
qtdemux->segment.format, qtdemux->segment.last_stop)); qtdemux->segment.format, qtdemux->segment.last_stop));
} }
/* now send the newsegment */ /* send the newsegment */
GST_DEBUG_OBJECT (qtdemux, "Sending newsegment from %" G_GINT64_FORMAT GST_DEBUG_OBJECT (qtdemux, "Sending newsegment from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, qtdemux->segment.start, stop); " to %" G_GINT64_FORMAT, qtdemux->segment.start, stop);
@ -681,11 +697,11 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
gst_qtdemux_push_event (qtdemux, newsegment); gst_qtdemux_push_event (qtdemux, newsegment);
/* restart streaming */
qtdemux->segment_running = TRUE; qtdemux->segment_running = TRUE;
gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop, gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
qtdemux->sinkpad); qtdemux->sinkpad);
/* and restart */
GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad); GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
return TRUE; return TRUE;
@ -912,22 +928,26 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
QtDemuxStream *stream; QtDemuxStream *stream;
guint64 min_time; guint64 min_time;
guint64 offset; guint64 offset;
int size; guint64 timestamp;
int index = -1; gint size;
int i; gint index = -1;
gint i;
/* Figure out the next stream sample to output */ /* 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];
if (stream->sample_index < stream->n_samples) { if (stream->sample_index < stream->n_samples) {
timestamp = stream->samples[stream->sample_index].timestamp;
GST_LOG_OBJECT (qtdemux, GST_LOG_OBJECT (qtdemux,
"stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i, "stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i,
stream->sample_index, stream->sample_index, GST_TIME_ARGS (timestamp));
GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
if (stream->samples[stream->sample_index].timestamp < min_time) { if (timestamp < min_time) {
min_time = stream->samples[stream->sample_index].timestamp; min_time = timestamp;
index = i; index = i;
} }
} }
@ -935,6 +955,7 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
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 ());
ret = GST_FLOW_UNEXPECTED;
goto beach; goto beach;
} }
@ -942,75 +963,75 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
offset = stream->samples[stream->sample_index].offset; offset = stream->samples[stream->sample_index].offset;
size = stream->samples[stream->sample_index].size; size = stream->samples[stream->sample_index].size;
timestamp = stream->samples[stream->sample_index].timestamp;
GST_LOG_OBJECT (qtdemux, GST_LOG_OBJECT (qtdemux,
"pushing from stream %d, sample_index=%d offset=%" G_GUINT64_FORMAT "pushing from stream %d, sample_index=%d offset=%" G_GUINT64_FORMAT
",size=%d timestamp=%" GST_TIME_FORMAT, ",size=%d timestamp=%" GST_TIME_FORMAT,
index, stream->sample_index, offset, size, index, stream->sample_index, offset, size, GST_TIME_ARGS (timestamp));
GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
buf = NULL; if (G_UNLIKELY (size <= 0))
if (size > 0) { goto beach;
GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
offset);
ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf); GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
if (ret != GST_FLOW_OK) offset);
goto beach;
ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf);
if (ret != GST_FLOW_OK)
goto beach;
buf = gst_buffer_make_metadata_writable (buf);
#if 0
/* 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);
}
}
#endif
/* first buffer? */
if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
gst_qtdemux_push_event (qtdemux,
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
0, GST_CLOCK_TIME_NONE, 0));
} }
if (buf) { if (stream->discont) {
buf = gst_buffer_make_metadata_writable (buf); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
/* hum... FIXME changing framerate breaks horribly, better set stream->discont = FALSE;
* 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_push_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')) {
if (stream->sample_index == 0)
GST_BUFFER_TIMESTAMP (buf) = 0;
} else {
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);
}
gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME,
qtdemux->last_ts);
if (!(stream->all_keyframe
|| stream->samples[stream->sample_index].keyframe)) {
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
}
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);
} }
/* timestamps of AMR aren't known... */
if (stream->fourcc == GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
if (stream->sample_index == 0)
GST_BUFFER_TIMESTAMP (buf) = 0;
} else {
GST_BUFFER_TIMESTAMP (buf) = 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);
}
gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME,
qtdemux->last_ts);
if (!(stream->all_keyframe || stream->samples[stream->sample_index].keyframe))
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
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++; stream->sample_index++;
beach: beach:
@ -1020,9 +1041,11 @@ beach:
static void static void
gst_qtdemux_loop (GstPad * pad) gst_qtdemux_loop (GstPad * pad)
{ {
GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad)); GstQTDemux *qtdemux;
guint64 cur_offset; guint64 cur_offset;
GstFlowReturn ret = GST_FLOW_ERROR; GstFlowReturn ret;
qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
cur_offset = qtdemux->offset; cur_offset = qtdemux->offset;
GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d", GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
@ -1037,21 +1060,58 @@ gst_qtdemux_loop (GstPad * pad)
ret = gst_qtdemux_loop_state_movie (qtdemux); ret = gst_qtdemux_loop_state_movie (qtdemux);
break; break;
default: default:
/* oh crap */ /* ouch */
g_error ("State=%d", qtdemux->state); goto invalid_state;
} }
if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_NOT_LINKED)) { /* if all is fine, continue */
GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", if (G_LIKELY (ret == GST_FLOW_OK))
gst_flow_get_name (ret)); goto done;
qtdemux->segment_running = FALSE;
gst_pad_pause_task (qtdemux->sinkpad); /* we don't care about unlinked pads */
if (GST_FLOW_IS_FATAL (ret)) { if (ret == GST_FLOW_NOT_LINKED)
goto done;
/* other errors make us stop */
GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", gst_flow_get_name (ret));
qtdemux->segment_running = FALSE;
gst_pad_pause_task (qtdemux->sinkpad);
/* fatal errors need special actions */
if (GST_FLOW_IS_FATAL (ret)) {
/* check EOS */
if (ret == GST_FLOW_UNEXPECTED) {
if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
GST_FORMAT_TIME, qtdemux->last_ts));
} else {
GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
}
} else {
gst_qtdemux_push_event (qtdemux, gst_event_new_eos ()); gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED, GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
(NULL), ("stream stopped, reason %s", gst_flow_get_name (ret))); (NULL), ("streaming stopped, reason %s", gst_flow_get_name (ret)));
} }
} }
done:
gst_object_unref (qtdemux);
return;
/* ERRORS */
invalid_state:
{
GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
(NULL), ("streaming stopped, invalid state"));
qtdemux->segment_running = FALSE;
gst_pad_pause_task (qtdemux->sinkpad);
gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
goto done;
}
} }
/* /*
@ -1121,9 +1181,11 @@ gst_qtdemux_post_buffering (GstQTDemux * demux, gint num, gint denom)
static GstFlowReturn static GstFlowReturn
gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
{ {
GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad)); GstQTDemux *demux;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad));
gst_adapter_push (demux->adapter, inbuf); gst_adapter_push (demux->adapter, inbuf);
GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u", GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
@ -1162,9 +1224,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
demux->neededbytes = size; demux->neededbytes = size;
demux->state = QTDEMUX_STATE_HEADER; demux->state = QTDEMUX_STATE_HEADER;
} }
}
break; break;
}
case QTDEMUX_STATE_HEADER:{ case QTDEMUX_STATE_HEADER:{
guint8 *data; guint8 *data;
guint32 fourcc; guint32 fourcc;
@ -1211,9 +1272,9 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
demux->neededbytes = 16; demux->neededbytes = 16;
demux->state = QTDEMUX_STATE_INITIAL; demux->state = QTDEMUX_STATE_INITIAL;
} }
}
break;
break;
}
case QTDEMUX_STATE_BUFFER_MDAT:{ case QTDEMUX_STATE_BUFFER_MDAT:{
GST_DEBUG_OBJECT (demux, "Got our buffer at offset %lld", GST_DEBUG_OBJECT (demux, "Got our buffer at offset %lld",
demux->mdatoffset); demux->mdatoffset);
@ -1229,9 +1290,9 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
demux->neededbytes = 16; demux->neededbytes = 16;
demux->state = QTDEMUX_STATE_INITIAL; demux->state = QTDEMUX_STATE_INITIAL;
gst_qtdemux_post_buffering (demux, 1, 1); gst_qtdemux_post_buffering (demux, 1, 1);
}
break;
break;
}
case QTDEMUX_STATE_MOVIE:{ case QTDEMUX_STATE_MOVIE:{
guint8 *data; guint8 *data;
GstBuffer *outbuf; GstBuffer *outbuf;
@ -1256,15 +1317,13 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
stream->samples[stream->sample_index].offset, stream->samples[stream->sample_index].offset,
stream->samples[stream->sample_index].size, stream->samples[stream->sample_index].size,
stream->samples[stream->sample_index].chunk); stream->samples[stream->sample_index].chunk);
if (stream->samples[stream->sample_index].offset == demux->offset) if (stream->samples[stream->sample_index].offset == demux->offset)
break; break;
} }
if (stream == NULL) { if (stream == NULL)
GST_WARNING_OBJECT (demux, "No stream found."); goto unknown_stream;
ret = GST_FLOW_ERROR;
break;
}
/* first buffer? */ /* first buffer? */
/* FIXME : this should be handled in sink_event */ /* FIXME : this should be handled in sink_event */
@ -1312,12 +1371,11 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
GST_LOG_OBJECT (demux, "offset is now %lld", demux->offset); GST_LOG_OBJECT (demux, "offset is now %lld", demux->offset);
if ((demux->neededbytes = next_entry_size (demux)) == -1) if ((demux->neededbytes = next_entry_size (demux)) == -1)
ret = GST_FLOW_ERROR; goto eos;
}
break; break;
}
default: default:
g_warning ("This line should never be reached\n"); goto invalid_state;
ret = GST_FLOW_ERROR;
} }
} }
@ -1328,7 +1386,31 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
demux->neededbytes); demux->neededbytes);
} }
done:
gst_object_unref (demux);
return ret; return ret;
/* ERRORS */
unknown_stream:
{
GST_ELEMENT_ERROR (demux, STREAM, FAILED, (NULL), ("unknown stream found"));
ret = GST_FLOW_ERROR;
goto done;
}
eos:
{
GST_DEBUG_OBJECT (demux, "no next entry, EOS");
ret = GST_FLOW_UNEXPECTED;
goto done;
}
invalid_state:
{
GST_ELEMENT_ERROR (demux, STREAM, FAILED,
(NULL), ("qtdemuxer invalid state %d", demux->state));
ret = GST_FLOW_ERROR;
goto done;
}
} }
static gboolean static gboolean
@ -2508,8 +2590,17 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
qtdemux->timescale = QTDEMUX_GUINT32_GET (mvhd->data + 20); qtdemux->timescale = QTDEMUX_GUINT32_GET (mvhd->data + 20);
qtdemux->duration = QTDEMUX_GUINT32_GET (mvhd->data + 24); qtdemux->duration = QTDEMUX_GUINT32_GET (mvhd->data + 24);
GST_INFO ("timescale: %d", qtdemux->timescale); GST_INFO_OBJECT (qtdemux, "timescale: %d", qtdemux->timescale);
GST_INFO ("duration: %d", qtdemux->duration); GST_INFO_OBJECT (qtdemux, "duration: %d", qtdemux->duration);
if (qtdemux->timescale != 0 && qtdemux->duration != 0) {
gint64 duration;
duration = gst_util_uint64_scale_int (qtdemux->duration,
GST_SECOND, qtdemux->timescale);
gst_segment_set_duration (&qtdemux->segment, GST_FORMAT_TIME, duration);
}
trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak); trak = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_trak);
qtdemux_parse_trak (qtdemux, trak); qtdemux_parse_trak (qtdemux, trak);
@ -2587,7 +2678,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd); mdhd = qtdemux_tree_get_child_by_type (mdia, FOURCC_mdhd);
g_assert (mdhd); g_assert (mdhd);
/* new streams always need a discont */
stream = g_new0 (QtDemuxStream, 1); stream = g_new0 (QtDemuxStream, 1);
stream->discont = TRUE;
stream->timescale = QTDEMUX_GUINT32_GET (mdhd->data + 20); stream->timescale = QTDEMUX_GUINT32_GET (mdhd->data + 20);
GST_LOG ("track timescale: %d", stream->timescale); GST_LOG ("track timescale: %d", stream->timescale);
@ -2720,7 +2813,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
} }
} }
GST_INFO ("type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, GST_INFO_OBJECT (qtdemux,
"type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
GST_FOURCC_ARGS (QTDEMUX_FOURCC_GET (stsd->data + offset + 4)), GST_FOURCC_ARGS (QTDEMUX_FOURCC_GET (stsd->data + offset + 4)),
stream->caps); stream->caps);
} else if (stream->subtype == FOURCC_soun) { } else if (stream->subtype == FOURCC_soun) {
@ -2860,11 +2954,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
"samplesize", G_TYPE_INT, samplesize, NULL); "samplesize", G_TYPE_INT, samplesize, NULL);
} }
} }
GST_INFO ("type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT, GST_INFO_OBJECT (qtdemux,
"type %" GST_FOURCC_FORMAT " caps %" GST_PTR_FORMAT,
GST_FOURCC_ARGS (QTDEMUX_FOURCC_GET (stsd->data + 16 + 4)), GST_FOURCC_ARGS (QTDEMUX_FOURCC_GET (stsd->data + 16 + 4)),
stream->caps); stream->caps);
} else { } else {
GST_INFO ("unknown subtype %" GST_FOURCC_FORMAT, GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (stream->subtype)); GST_FOURCC_ARGS (stream->subtype));
g_free (stream); g_free (stream);
return; return;
@ -3053,9 +3148,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
GST_SECOND, stream->rate); GST_SECOND, stream->rate);
} }
#if 0 #if 0
GST_INFO ("moo samples_per_chunk=%d rate=%d dur=%lld %lld", GST_INFO_OBJECT (qtdemux,
(int) samples_per_chunk, "moo samples_per_chunk=%d rate=%d dur=%lld %lld",
(int) stream->rate, (int) samples_per_chunk, (int) stream->rate,
(long long) ((samples_per_chunk * GST_SECOND) / stream->rate), (long long) ((samples_per_chunk * GST_SECOND) / stream->rate),
(long long) timestamp); (long long) timestamp);
#endif #endif