mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
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:
parent
075303cf8e
commit
378bcc051d
1 changed files with 239 additions and 144 deletions
|
@ -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,39 +963,38 @@ 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));
|
|
||||||
|
if (G_UNLIKELY (size <= 0))
|
||||||
|
goto beach;
|
||||||
|
|
||||||
buf = NULL;
|
|
||||||
if (size > 0) {
|
|
||||||
GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
|
GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
|
||||||
offset);
|
offset);
|
||||||
|
|
||||||
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 beach;
|
goto beach;
|
||||||
}
|
|
||||||
|
|
||||||
if (buf) {
|
|
||||||
buf = gst_buffer_make_metadata_writable (buf);
|
buf = gst_buffer_make_metadata_writable (buf);
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* hum... FIXME changing framerate breaks horribly, better set
|
/* hum... FIXME changing framerate breaks horribly, better set
|
||||||
* an average framerate, or get rid of the framerate property. */
|
* an average framerate, or get rid of the framerate property. */
|
||||||
if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
|
if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
|
||||||
//float fps =
|
float fps =
|
||||||
// 1. * GST_SECOND / stream->samples[stream->sample_index].duration;
|
1. * GST_SECOND / stream->samples[stream->sample_index].duration;
|
||||||
/*
|
|
||||||
if (fps != stream->fps) {
|
if (fps != stream->fps) {
|
||||||
gst_caps_set_simple (stream->caps, "framerate", G_TYPE_DOUBLE, fps,
|
gst_caps_set_simple (stream->caps, "framerate", G_TYPE_DOUBLE, fps, NULL);
|
||||||
NULL);
|
|
||||||
stream->fps = fps;
|
stream->fps = fps;
|
||||||
gst_pad_set_explicit_caps (stream->pad, stream->caps);
|
gst_pad_set_explicit_caps (stream->pad, stream->caps);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* first buffer? */
|
/* first buffer? */
|
||||||
if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
|
if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
|
||||||
|
@ -983,13 +1003,17 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
|
||||||
0, GST_CLOCK_TIME_NONE, 0));
|
0, GST_CLOCK_TIME_NONE, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stream->discont) {
|
||||||
|
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
|
||||||
|
stream->discont = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* timestamps of AMR aren't known... */
|
/* timestamps of AMR aren't known... */
|
||||||
if (stream->fourcc == GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
|
if (stream->fourcc == GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
|
||||||
if (stream->sample_index == 0)
|
if (stream->sample_index == 0)
|
||||||
GST_BUFFER_TIMESTAMP (buf) = 0;
|
GST_BUFFER_TIMESTAMP (buf) = 0;
|
||||||
} else {
|
} else {
|
||||||
GST_BUFFER_TIMESTAMP (buf) =
|
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
||||||
stream->samples[stream->sample_index].timestamp;
|
|
||||||
qtdemux->last_ts = GST_BUFFER_TIMESTAMP (buf);
|
qtdemux->last_ts = GST_BUFFER_TIMESTAMP (buf);
|
||||||
GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int
|
GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int
|
||||||
(stream->samples[stream->sample_index].duration, GST_SECOND,
|
(stream->samples[stream->sample_index].duration, GST_SECOND,
|
||||||
|
@ -998,10 +1022,8 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
|
||||||
gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME,
|
gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME,
|
||||||
qtdemux->last_ts);
|
qtdemux->last_ts);
|
||||||
|
|
||||||
if (!(stream->all_keyframe
|
if (!(stream->all_keyframe || stream->samples[stream->sample_index].keyframe))
|
||||||
|| stream->samples[stream->sample_index].keyframe)) {
|
|
||||||
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
|
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||||
}
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (qtdemux,
|
GST_LOG_OBJECT (qtdemux,
|
||||||
"Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
|
"Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
|
||||||
|
@ -1009,7 +1031,6 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
|
||||||
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);
|
||||||
}
|
|
||||||
|
|
||||||
stream->sample_index++;
|
stream->sample_index++;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
/* we don't care about unlinked pads */
|
||||||
|
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;
|
qtdemux->segment_running = FALSE;
|
||||||
gst_pad_pause_task (qtdemux->sinkpad);
|
gst_pad_pause_task (qtdemux->sinkpad);
|
||||||
|
|
||||||
|
/* fatal errors need special actions */
|
||||||
if (GST_FLOW_IS_FATAL (ret)) {
|
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
|
||||||
|
|
Loading…
Reference in a new issue