summaryrefslogtreecommitdiffstats
path: root/gst/avi/gstavidemux.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/avi/gstavidemux.c')
-rw-r--r--gst/avi/gstavidemux.c693
1 files changed, 340 insertions, 353 deletions
diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c
index 01b0de1b..8164847b 100644
--- a/gst/avi/gstavidemux.c
+++ b/gst/avi/gstavidemux.c
@@ -83,7 +83,9 @@ static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
-static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update);
+static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
+static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
+ GstEvent * event);
static void gst_avi_demux_loop (GstPad * pad);
static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
@@ -226,8 +228,6 @@ gst_avi_demux_reset (GstAviDemux * avi)
avi->globaltags = NULL;
avi->got_tags = FALSE;
-
- gst_segment_init (&avi->segment, GST_FORMAT_TIME);
}
static gst_avi_index_entry *
@@ -257,6 +257,7 @@ gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
GST_LOG_OBJECT (avi, "stream_nr:%d , time:%" GST_TIME_FORMAT " flags:%d",
stream_nr, GST_TIME_ARGS (time), flags);
+
i = -1;
do {
entry = gst_avi_demux_index_next (avi, stream_nr, i + 1);
@@ -269,6 +270,7 @@ gst_avi_demux_index_entry_for_time (GstAviDemux * avi,
"looking at entry %d / ts:%" GST_TIME_FORMAT " / dur:%" GST_TIME_FORMAT
" flags:%d", i, GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (entry->dur),
entry->flags);
+
if (entry->ts <= time && (entry->flags & flags) == flags)
last_entry = entry;
} while (entry->ts < time);
@@ -429,6 +431,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
pos = gst_util_uint64_scale_int (xlen, stream->current_frame,
stream->total_frames);
} else {
+ /* we don't know */
res = FALSE;
}
} else {
@@ -451,20 +454,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
res = FALSE;
break;
}
-
- /* use duration from the index if we have an
- * index instead of trusting the stream header */
- if (GST_CLOCK_TIME_IS_VALID (stream->idx_duration)) {
- gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration);
- } else {
- gint64 len;
-
- len =
- gst_util_uint64_scale ((guint64) stream->strh->length *
- stream->strh->scale, GST_SECOND, stream->strh->rate);
-
- gst_query_set_duration (query, GST_FORMAT_TIME, len);
- }
+ gst_query_set_duration (query, GST_FORMAT_TIME, stream->idx_duration);
break;
}
default:
@@ -493,104 +483,26 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
- avi_stream_context *stream;
GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi,
"have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
- if (!avi->index_entries) {
- GST_CAT_DEBUG_OBJECT (GST_CAT_EVENT, avi, "no index entries, returning");
- return FALSE;
- }
-
- stream = gst_pad_get_element_private (pad);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
- {
- /* FIXME, this seeking code is not correct, look at wavparse for
- * a better example */
- GstFormat format;
- GstSeekFlags flags;
- gdouble rate;
- gint64 start, stop;
- gint64 tstart, tstop;
- gint64 duration;
- GstFormat tformat = GST_FORMAT_TIME;
- GstSeekType start_type, stop_type;
- gboolean update_start = TRUE;
- gboolean update_stop = TRUE;
-
- gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
- &stop_type, &stop);
-
- GST_DEBUG_OBJECT (avi,
- "seek format %d, flags:%d, %08x, start:%lld, stop:%lld", format,
- flags, stream->strh->type, start, stop);
-
- if (format != GST_FORMAT_TIME) {
- res &=
- gst_avi_demux_src_convert (pad, format, start, &tformat, &tstart);
- res &= gst_avi_demux_src_convert (pad, format, stop, &tformat, &tstop);
- if (!res)
- goto done;
- } else {
- tstart = start;
- tstop = stop;
- }
-
- if (!gst_pad_query_duration (stream->pad, &tformat, &duration)) {
- res = FALSE;
- goto done;
- }
-
- switch (start_type) {
- case GST_SEEK_TYPE_CUR:
- tstart = avi->segment.start + tstart;
- break;
- case GST_SEEK_TYPE_END:
- tstart = duration + tstart;
- break;
- case GST_SEEK_TYPE_NONE:
- tstart = avi->segment.start;
- update_start = FALSE;
- break;
- case GST_SEEK_TYPE_SET:
- break;
- }
- tstart = CLAMP (tstart, 0, duration);
-
- switch (stop_type) {
- case GST_SEEK_TYPE_CUR:
- tstop = avi->segment.stop + tstop;
- break;
- case GST_SEEK_TYPE_END:
- tstop = duration + tstop;
- break;
- case GST_SEEK_TYPE_NONE:
- tstop = avi->segment.stop;
- update_stop = FALSE;
- break;
- case GST_SEEK_TYPE_SET:
- break;
- }
- tstop = CLAMP (tstop, 0, duration);
-
- /* now store the values */
- avi->segment.rate = rate;
- avi->segment.flags = flags;
- avi->segment.start = tstart;
- avi->segment.stop = tstop;
-
- gst_avi_demux_handle_seek (avi, update_start || update_stop);
+ /* handle seeking */
+ res = gst_avi_demux_handle_seek (avi, pad, event);
+ break;
+ case GST_EVENT_QOS:
+ /* FIXME, we can do something clever here like skip to the next keyframe
+ * based on the QoS values. */
+ res = FALSE;
break;
-
- }
default:
+ /* most other events are not very usefull */
res = FALSE;
break;
}
-done:
gst_event_unref (event);
GST_DEBUG_OBJECT (avi, "returning %d", res);
@@ -603,7 +515,7 @@ done:
* @buf: input data to be used for parsing.
*
* "Open" a RIFF/AVI file. The buffer should be at least 12
- * bytes long. Discards buffer after use.
+ * bytes long. Takes ownership of @buf.
*
* Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
* Throws an error, caller should error out (fatal).
@@ -638,10 +550,10 @@ gst_avi_demux_stream_init (GstAviDemux * avi)
GstFlowReturn res;
GstBuffer *buf = NULL;
- if ((res = gst_pad_pull_range (avi->sinkpad,
- avi->offset, 12, &buf)) != GST_FLOW_OK)
+ res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
+ if (res != GST_FLOW_OK)
return res;
- else if (!gst_avi_demux_parse_file_header (GST_ELEMENT (avi), buf))
+ else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
goto wrong_header;
avi->offset += 12;
@@ -652,7 +564,6 @@ gst_avi_demux_stream_init (GstAviDemux * avi)
wrong_header:
{
GST_DEBUG_OBJECT (avi, "error parsing file header");
- gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
}
@@ -1001,7 +912,6 @@ gst_avi_demux_read_subindexes (GstAviDemux * avi,
* Errors are not fatal. It does indicate the stream
* was skipped.
*/
-
static gboolean
gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
{
@@ -1020,11 +930,11 @@ gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
GST_DEBUG_OBJECT (element, "Parsing stream");
/* read strh */
- if (!buf || !gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
+ if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub) ||
tag != GST_RIFF_TAG_strh) {
GST_ERROR_OBJECT (element,
"Failed to find strh chunk (tag: %" GST_FOURCC_FORMAT ")",
- buf ? GST_BUFFER_SIZE (buf) : 0, GST_FOURCC_ARGS (tag));
+ GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
goto fail;
} else if (!gst_riff_parse_strh (element, sub, &stream->strh))
goto fail;
@@ -1034,8 +944,7 @@ gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
tag != GST_RIFF_TAG_strf) {
GST_ERROR_OBJECT (element,
"Failed to find strh chunk (size: %d, tag: %"
- GST_FOURCC_FORMAT ")", buf ? GST_BUFFER_SIZE (buf) : 0,
- GST_FOURCC_ARGS (tag));
+ GST_FOURCC_FORMAT ")", GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
goto fail;
} else {
gboolean res = FALSE;
@@ -1081,9 +990,9 @@ gst_avi_demux_parse_stream (GstElement * element, GstBuffer * buf)
case GST_RIFF_TAG_strn:
g_free (stream->name);
if (sub != NULL) {
- stream->name = g_new (gchar, GST_BUFFER_SIZE (sub) + 1);
- memcpy (stream->name, GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub));
- stream->name[GST_BUFFER_SIZE (sub)] = '\0';
+ stream->name =
+ g_strndup ((gchar *) GST_BUFFER_DATA (sub),
+ (gsize) GST_BUFFER_SIZE (sub));
gst_buffer_unref (sub);
sub = NULL;
} else {
@@ -1233,7 +1142,6 @@ fail:
gst_buffer_unref (stream->extradata);
memset (stream, 0, sizeof (avi_stream_context));
avi->num_streams++;
-
return FALSE;
}
}
@@ -1246,7 +1154,6 @@ fail:
* Read an openDML-2.0 extension header. Fills in the frame number
* in the avi demuxer object when reading succeeds.
*/
-
static void
gst_avi_demux_parse_odml (GstElement * element, GstBuffer * buf)
{
@@ -1441,9 +1348,8 @@ gst_avi_demux_stream_index (GstAviDemux * avi,
/* get position */
if (gst_pad_pull_range (avi->sinkpad, offset, 8, &buf) != GST_FLOW_OK)
return;
- else if (!buf || GST_BUFFER_SIZE (buf) < 8) {
- if (buf)
- gst_buffer_unref (buf);
+ else if (GST_BUFFER_SIZE (buf) < 8) {
+ gst_buffer_unref (buf);
return;
}
offset += 8 + GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
@@ -1641,10 +1547,15 @@ gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
GstFlowReturn res = GST_FLOW_OK;
GstBuffer *buf = NULL;
- if ((res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf)) != GST_FLOW_OK)
+ res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
+ if (res != GST_FLOW_OK)
goto beach;
- if (GST_BUFFER_SIZE (buf) != 8)
+
+ if (GST_BUFFER_SIZE (buf) != 8) {
+ gst_buffer_unref (buf);
return GST_FLOW_ERROR;
+ }
+
*tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
*size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
gst_buffer_unref (buf);
@@ -1663,7 +1574,6 @@ beach:
* Returns the offset and size of the next buffer
* Position is the position of the buffer (after tag and size)
*/
-
static GstFlowReturn
gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
guint32 * tag, guint * size)
@@ -1673,7 +1583,8 @@ gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
GstFlowReturn res = GST_FLOW_OK;
while (1) {
- if ((res = gst_avi_demux_peek_tag (avi, off, tag, &siz)) != GST_FLOW_OK)
+ res = gst_avi_demux_peek_tag (avi, off, tag, &siz);
+ if (res != GST_FLOW_OK)
break;
if (*tag == GST_RIFF_TAG_LIST)
off += 12;
@@ -1693,7 +1604,6 @@ gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
*
* pull-range based
*/
-
static gboolean
gst_avi_demux_stream_scan (GstAviDemux * avi,
GList ** index, GList ** alloc_list)
@@ -1883,7 +1793,6 @@ gst_avi_demux_stream_scan (GstAviDemux * avi,
* smaller pieces. In the second case, we re-order chunk reading
* order. The end result should be a smoother playing AVI.
*/
-
static gint
sort (gst_avi_index_entry * a, gst_avi_index_entry * b)
{
@@ -2056,23 +1965,50 @@ static void
gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
{
gst_avi_index_entry *entry;
- GstClockTime end_time;
gint stream, i;
+ GstClockTime total;
+
+ total = GST_CLOCK_TIME_NONE;
- /* we assume all streams start at a timestamp of 0 for now */
+ /* all streams start at a timestamp 0 */
for (stream = 0; stream < avi->num_streams; stream++) {
- end_time = GST_CLOCK_TIME_NONE;
+ GstClockTime duration = GST_CLOCK_TIME_NONE;
+ GstClockTime hduration;
+ gst_riff_strh *strh = avi->stream[stream].strh;
+
+ /* get header duration */
+ hduration = gst_util_uint64_scale ((guint64) strh->length *
+ strh->scale, GST_SECOND, strh->rate);
+ GST_INFO ("Stream %d duration according to header: %" GST_TIME_FORMAT,
+ stream, GST_TIME_ARGS (hduration));
+
i = 0;
+ /* run over index to get last duration */
while ((entry = gst_avi_demux_index_next (avi, stream, i))) {
- end_time = entry->ts + entry->dur;
+ duration = entry->ts + entry->dur;
i++;
}
- if (GST_CLOCK_TIME_IS_VALID (end_time)) {
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ /* index gave valid duration, use that */
GST_INFO ("Stream %d duration according to index: %" GST_TIME_FORMAT,
- stream, GST_TIME_ARGS (end_time));
- avi->stream[stream].idx_duration = end_time;
+ stream, GST_TIME_ARGS (duration));
+ } else {
+ /* fall back to header info to calculate a duration */
+ duration = hduration;
}
+ /* set duration for the stream */
+ avi->stream[stream].idx_duration = duration;
+
+ /* find total duration */
+ if (total == GST_CLOCK_TIME_NONE || duration > total)
+ total = duration;
}
+
+ /* and set the total duration in the segment. */
+ GST_INFO ("Setting total duration to: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (total));
+
+ gst_segment_set_duration (&avi->segment, GST_FORMAT_TIME, total);
}
static gboolean
@@ -2095,7 +2031,6 @@ gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
/*
* Read full AVI headers.
*/
-
static GstFlowReturn
gst_avi_demux_stream_header (GstAviDemux * avi)
{
@@ -2104,6 +2039,7 @@ gst_avi_demux_stream_header (GstAviDemux * avi)
guint32 tag;
GList *index = NULL, *alloc = NULL;
guint offset = 4;
+ gint64 stop;
/* the header consists of a 'hdrl' LIST tag */
if ((res = gst_riff_read_chunk (GST_ELEMENT (avi), avi->sinkpad,
@@ -2203,8 +2139,10 @@ gst_avi_demux_stream_header (GstAviDemux * avi)
if ((res = gst_pad_pull_range (avi->sinkpad, avi->offset,
12, &buf)) != GST_FLOW_OK)
return res;
- else if (!buf || GST_BUFFER_SIZE (buf) < 12)
+ else if (GST_BUFFER_SIZE (buf) < 12) {
+ gst_buffer_unref (buf);
return GST_FLOW_ERROR;
+ }
tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
@@ -2260,22 +2198,22 @@ done:
gst_avi_demux_massage_index (avi, index, alloc);
gst_avi_demux_calculate_durations_from_index (avi);
- /* send initial discont */
- avi->segment.start = 0;
- avi->segment.stop =
- gst_util_uint64_scale_int ((gint64) avi->stream[0].strh->scale *
- avi->stream[0].strh->length, GST_SECOND, avi->stream[0].strh->rate);
+ /* create initial NEWSEGMENT event */
+ if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
+ stop = avi->segment.duration;
- GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, avi->segment.stop);
+ GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
+ if (avi->seek_event)
+ gst_event_unref (avi->seek_event);
avi->seek_event = gst_event_new_new_segment
(FALSE, avi->segment.rate, GST_FORMAT_TIME,
- avi->segment.start, avi->segment.stop, avi->segment.start);
+ avi->segment.start, stop, avi->segment.start);
/* at this point we know all the streams and we can signal the no more
* pads signal */
GST_DEBUG_OBJECT (avi, "signaling no more pads");
- gst_element_no_more_pads (GST_ELEMENT (avi));
+ gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
return GST_FLOW_OK;
@@ -2301,10 +2239,8 @@ no_avih:
GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
("Invalid AVI header (no avih at start): %"
GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
- if (sub) {
+ if (sub)
gst_buffer_unref (sub);
- sub = NULL;
- }
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
@@ -2312,10 +2248,8 @@ invalid_avih:
{
GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
("Invalid AVI header (cannot parse avih at start)"));
- if (sub) {
+ if (sub)
gst_buffer_unref (sub);
- sub = NULL;
- }
gst_buffer_unref (buf);
return GST_FLOW_ERROR;
}
@@ -2336,86 +2270,190 @@ no_index:
}
}
-/*
- * Handle seek.
+/* Do the actual seeking.
*/
-
static gboolean
-gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update)
+gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
{
- GstClockTime start_time;
- gboolean flush, keyframe;
+ GstClockTime seek_time;
+ gboolean keyframe;
gst_avi_index_entry *entry;
- guint i;
+
+ seek_time = segment->last_stop;
+ keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
/* FIXME: if we seek in an openDML file, we will have multiple
* primary levels. Seeking in between those will cause havoc. */
- flush = avi->segment.flags & GST_SEEK_FLAG_FLUSH;
- keyframe = avi->segment.flags & GST_SEEK_FLAG_KEY_UNIT;
+ /* get the entry for the requested position, which is always in last_stop.
+ * we search the index intry for stream 0, since all entries are sorted by
+ * time and stream we automagically are positioned for the other streams as
+ * well. FIXME, this code assumes the main stream with keyframes is stream 0,
+ * which is mostly correct... */
+ entry = gst_avi_demux_index_entry_for_time (avi, 0, seek_time,
+ (guint32) GST_RIFF_IF_KEYFRAME);
+ if (entry) {
+ GST_DEBUG_OBJECT (avi,
+ "Got keyframe entry %d [stream:%d / ts:%" GST_TIME_FORMAT
+ " / duration:%" GST_TIME_FORMAT "]", entry->index_nr,
+ entry->stream_nr, GST_TIME_ARGS (entry->ts),
+ GST_TIME_ARGS (entry->dur));
+ avi->current_entry = entry->index_nr;
+ } else {
+ GST_WARNING_OBJECT (avi,
+ "Couldn't find AviIndexEntry for time:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (seek_time));
+ if (avi->current_entry >= avi->index_size && avi->index_size > 0)
+ avi->current_entry = avi->index_size - 1;
+ }
+
+ GST_DEBUG_OBJECT (avi, "seek: %" GST_TIME_FORMAT
+ " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
+
+ if (keyframe) {
+ /* when seeking to a keyframe, we update the result seek time
+ * to the time of the keyframe. */
+ seek_time = avi->index_entries[avi->current_entry].ts;
+ }
+
+ segment->last_stop = seek_time;
+
+ return TRUE;
+}
+
+static gboolean
+gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
+{
+ gboolean res;
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
+ gint64 cur, stop;
+ gboolean flush;
+ gboolean update;
+ GstSegment seeksegment = { 0, };
+
+ if (event) {
+ GST_DEBUG_OBJECT (avi, "doing seek with event");
+
+ gst_event_parse_seek (event, &rate, &format, &flags,
+ &cur_type, &cur, &stop_type, &stop);
+
+ /* we have to have a format as the segment format. Try to convert
+ * if not. */
+ if (format != GST_FORMAT_TIME) {
+ GstFormat fmt;
+
+ fmt = GST_FORMAT_TIME;
+ res = TRUE;
+ if (cur_type != GST_SEEK_TYPE_NONE)
+ res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
+ if (res && stop_type != GST_SEEK_TYPE_NONE)
+ res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
+ if (!res)
+ goto no_format;
+
+ format = fmt;
+ }
+ } else {
+ GST_DEBUG_OBJECT (avi, "doing seek without event");
+ flags = 0;
+ }
+
+ /* save flush flag */
+ flush = flags & GST_SEEK_FLAG_FLUSH;
if (flush) {
+ /* for a flushing seek, we send a flush_start on all pads. This will
+ * eventually stop streaming with a WRONG_STATE. We can thus eventually
+ * take the STREAM_LOCK. */
+ GST_DEBUG_OBJECT (avi, "sending flush start");
gst_avi_demux_push_event (avi, gst_event_new_flush_start ());
gst_pad_push_event (avi->sinkpad, gst_event_new_flush_start ());
- } else
+ } else {
+ /* a non-flushing seek, we PAUSE the task so that we can take the
+ * STREAM_LOCK */
+ GST_DEBUG_OBJECT (avi, "non flushing seek, pausing task");
gst_pad_pause_task (avi->sinkpad);
+ }
+ /* wait for streaming to stop */
GST_PAD_STREAM_LOCK (avi->sinkpad);
- /* fill current_entry according to flags and update */
- if (update) {
- entry = gst_avi_demux_index_entry_for_time (avi, 0, avi->segment.start,
- (guint32) GST_RIFF_IF_KEYFRAME);
- if (entry) {
- GST_DEBUG_OBJECT (avi,
- "Got keyframe entry %d [stream:%d / ts:%" GST_TIME_FORMAT
- " / duration:%" GST_TIME_FORMAT "]", entry->index_nr,
- entry->stream_nr, GST_TIME_ARGS (entry->ts),
- GST_TIME_ARGS (entry->dur));
- avi->current_entry = entry->index_nr;
- } else {
- GST_WARNING_OBJECT (avi,
- "Couldn't find AviIndexEntry for time:%" GST_TIME_FORMAT,
- GST_TIME_ARGS (avi->segment.start));
- if (avi->current_entry >= avi->index_size && avi->index_size > 0)
- avi->current_entry = avi->index_size - 1;
- }
- }
-
- GST_DEBUG_OBJECT (avi, "seek: %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
- " keyframe seeking:%d update:%d", GST_TIME_ARGS (avi->segment.start),
- GST_TIME_ARGS (avi->segment.stop), keyframe, update);
+ /* copy segment, we need this because we still need the old
+ * segment when we close the current segment. */
+ memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
- if (keyframe)
- start_time = avi->index_entries[avi->current_entry].ts;
- else
- start_time = avi->segment.start;
+ if (event) {
+ GST_DEBUG_OBJECT (avi, "configuring seek");
+ gst_segment_set_seek (&seeksegment, rate, format, flags,
+ cur_type, cur, stop_type, stop, &update);
+ }
- avi->seek_event = gst_event_new_new_segment
- (!update, avi->segment.rate, GST_FORMAT_TIME,
- start_time, avi->segment.stop, start_time);
+ /* do the seek, seeksegment.last_stop contains the new position, this
+ * actually never fails. */
+ res = gst_avi_demux_do_seek (avi, &seeksegment);
if (flush) {
+ gint i;
+
+ GST_DEBUG_OBJECT (avi, "sending flush stop");
gst_avi_demux_push_event (avi, gst_event_new_flush_stop ());
gst_pad_push_event (avi->sinkpad, gst_event_new_flush_stop ());
+ /* reset the last flow */
for (i = 0; i < avi->num_streams; i++) {
avi->stream[i].last_flow = GST_FLOW_OK;
}
+ } else if (avi->segment_running) {
+ GstEvent *seg;
+
+ /* we are running the current segment and doing a non-flushing seek,
+ * close the segment first based on the last_stop. */
+ GST_DEBUG_OBJECT (avi, "closing running segment %" G_GINT64_FORMAT
+ " to %" G_GINT64_FORMAT, avi->segment.start, avi->segment.last_stop);
+ seg = gst_event_new_new_segment (TRUE,
+ avi->segment.rate, avi->segment.format,
+ avi->segment.start, avi->segment.last_stop, avi->segment.time);
+ gst_avi_demux_push_event (avi, seg);
}
+ /* now update the real segment info */
+ memcpy (&avi->segment, &seeksegment, sizeof (GstSegment));
+
+ /* post the SEGMENT_START message when we do segmented playback */
if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT (avi),
- gst_message_new_segment_start (GST_OBJECT (avi), GST_FORMAT_TIME,
- start_time));
+ gst_message_new_segment_start (GST_OBJECT (avi),
+ avi->segment.format, avi->segment.last_stop));
}
- gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop,
- avi->sinkpad);
+ /* prepare for streaming again */
+ if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
+ stop = avi->segment.duration;
+ /* queue the segment event for the streaming thread. */
+ if (avi->seek_event)
+ gst_event_unref (avi->seek_event);
+ avi->seek_event = gst_event_new_new_segment (FALSE,
+ avi->segment.rate, avi->segment.format,
+ avi->segment.last_stop, stop, avi->segment.time);
+
+ if (!avi->streaming) {
+ avi->segment_running = TRUE;
+ gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop,
+ avi->sinkpad);
+ }
GST_PAD_STREAM_UNLOCK (avi->sinkpad);
return TRUE;
+ /* ERRORS */
+no_format:
+ {
+ GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
+ return FALSE;
+ }
}
/*
@@ -2423,7 +2461,6 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, gboolean update)
* returns either the buffer or a new one (with old
* one dereferenced).
*/
-
static inline void
swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes)
{
@@ -2469,30 +2506,29 @@ gst_avi_demux_invert (avi_stream_context * stream, GstBuffer * buf)
/*
Returns the aggregated GstFlowReturn.
*/
-
static GstFlowReturn
gst_avi_demux_aggregated_flow (GstAviDemux * avi)
{
gint i;
GstFlowReturn res = GST_FLOW_OK;
- gboolean haveok = FALSE;
for (i = 0; i < avi->num_streams; i++) {
- GstFlowReturn last = avi->stream[i].last_flow;
+ res = avi->stream[i].last_flow;
- GST_LOG_OBJECT (avi, "stream %d , flow : %s", i, gst_flow_get_name (last));
- if (last == GST_FLOW_OK)
- haveok = TRUE;
- else if (last < res) {
- res = last;
- }
- }
+ GST_LOG_OBJECT (avi, "stream %d , flow : %s", i, gst_flow_get_name (res));
- if (!GST_FLOW_IS_FATAL (res) && res != GST_FLOW_WRONG_STATE && haveok)
- res = GST_FLOW_OK;
+ /* at least one flow is success, return that value */
+ if (GST_FLOW_IS_SUCCESS (res))
+ break;
+
+ /* any other error that is not-linked can be returned right away */
+ if (res != GST_FLOW_NOT_LINKED)
+ break;
+ }
GST_DEBUG_OBJECT (avi, "Returning aggregated value of %s",
gst_flow_get_name (res));
+
return res;
}
@@ -2501,94 +2537,112 @@ gst_avi_demux_process_next_entry (GstAviDemux * avi)
{
GstFlowReturn res = GST_FLOW_OK;
gboolean processed = FALSE;
+ avi_stream_context *stream;
+ gst_avi_index_entry *entry;
do {
+ GstBuffer *buf;
- if (avi->current_entry >= avi->index_size) {
- GST_LOG_OBJECT (avi, "Handled last index entry, setting EOS (%d > %d)",
- avi->current_entry, avi->index_size);
- avi->stream[0].last_flow = GST_FLOW_UNEXPECTED;
+ /* see if we are at the end */
+ if (avi->current_entry >= avi->index_size)
goto eos;
- } else {
- GstBuffer *buf;
- gst_avi_index_entry *entry = &avi->index_entries[avi->current_entry++];
- avi_stream_context *stream;
- if (entry->stream_nr >= avi->num_streams) {
- GST_DEBUG_OBJECT (avi,
- "Entry has non-existing stream nr %d", entry->stream_nr);
- continue;
- }
+ /* get next entry, this will work as we checked for the size above */
+ entry = &avi->index_entries[avi->current_entry++];
- stream = &avi->stream[entry->stream_nr];
-
- if ((entry->flags & GST_RIFF_IF_KEYFRAME)
- && GST_CLOCK_TIME_IS_VALID (entry->ts)
- && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
- && (entry->ts > avi->segment.stop)) {
- GST_LOG_OBJECT (avi, "Found keyframe after segment,"
- " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")",
- GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (avi->segment.stop));
- res = stream->last_flow = GST_FLOW_UNEXPECTED;
- goto beach;
- }
+ /* see if we have a valid stream, ignore if not */
+ if (entry->stream_nr >= avi->num_streams) {
+ GST_DEBUG_OBJECT (avi,
+ "Entry has non-existing stream nr %d", entry->stream_nr);
+ continue;
+ }
- if (entry->size == 0 || !stream->pad) {
- GST_DEBUG_OBJECT (avi, "Skipping entry %d (%d, %p)",
- avi->current_entry - 1, entry->size, stream->pad);
- goto next;
- }
+ /* get stream now */
+ stream = &avi->stream[entry->stream_nr];
- if ((res = gst_pad_pull_range (avi->sinkpad, entry->offset +
- avi->index_offset, entry->size, &buf)) != GST_FLOW_OK) {
- stream->last_flow = res;
- goto beach;
- }
+ if ((entry->flags & GST_RIFF_IF_KEYFRAME)
+ && GST_CLOCK_TIME_IS_VALID (entry->ts)
+ && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
+ && (entry->ts > avi->segment.stop)) {
+ goto eos_stop;
+ }
- if (GST_BUFFER_SIZE (buf) < entry->size) {
- GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT
- ", only got %d/%d bytes (truncated file?)", entry->offset +
- avi->index_offset, GST_BUFFER_SIZE (buf), entry->size);
- gst_buffer_unref (buf);
- res = stream->last_flow = GST_FLOW_UNEXPECTED;
- goto beach;
- }
+ if (entry->size == 0 || !stream->pad) {
+ GST_DEBUG_OBJECT (avi, "Skipping entry %d (%d, %p)",
+ avi->current_entry - 1, entry->size, stream->pad);
+ goto next;
+ }
- if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) {
- buf = gst_avi_demux_invert (stream, buf);
- }
- if (!(entry->flags & GST_RIFF_IF_KEYFRAME))
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
-
- GST_BUFFER_TIMESTAMP (buf) = entry->ts;
- GST_BUFFER_DURATION (buf) = entry->dur;
- gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
- GST_DEBUG_OBJECT (avi, "Processing buffer of size %d and time %"
- GST_TIME_FORMAT " on pad %s",
- GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
- GST_PAD_NAME (stream->pad));
- res = gst_pad_push (stream->pad, buf);
+ /* pull in the data */
+ res = gst_pad_pull_range (avi->sinkpad, entry->offset +
+ avi->index_offset, entry->size, &buf);
+ if (res != GST_FLOW_OK) {
stream->last_flow = res;
- if (res != GST_FLOW_OK && res != GST_FLOW_NOT_LINKED) {
- GST_DEBUG_OBJECT (avi, "Flow on pad %s: %s",
- GST_PAD_NAME (stream->pad), gst_flow_get_name (res));
- goto beach;
- }
- processed = TRUE;
+ goto beach;
+ }
- next:
- stream->current_frame = entry->frames_before + 1;
- stream->current_byte = entry->bytes_before + entry->size;
+ /* check for short buffers, this is EOS as well */
+ if (GST_BUFFER_SIZE (buf) < entry->size) {
+ GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT
+ ", only got %d/%d bytes (truncated file?)", entry->offset +
+ avi->index_offset, GST_BUFFER_SIZE (buf), entry->size);
+ gst_buffer_unref (buf);
+ res = stream->last_flow = GST_FLOW_UNEXPECTED;
+ goto beach;
+ }
+
+ /* invert the picture if needed */
+ if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) {
+ buf = gst_avi_demux_invert (stream, buf);
}
+
+ /* mark non-keyframes */
+ if (!(entry->flags & GST_RIFF_IF_KEYFRAME))
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
+
+ GST_BUFFER_TIMESTAMP (buf) = entry->ts;
+ GST_BUFFER_DURATION (buf) = entry->dur;
+ gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
+
+ GST_DEBUG_OBJECT (avi, "Processing buffer of size %d and time %"
+ GST_TIME_FORMAT " on pad %s",
+ GST_BUFFER_SIZE (buf), GST_TIME_ARGS (entry->ts),
+ GST_PAD_NAME (stream->pad));
+
+ /* update current position in the segment */
+ gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, entry->ts);
+
+ res = stream->last_flow = gst_pad_push (stream->pad, buf);
+ /* mark as processed, we increment the frame and byte counters then
+ * return the GstFlowReturn */
+ processed = TRUE;
+
+ next:
+ stream->current_frame = entry->frames_before + 1;
+ stream->current_byte = entry->bytes_before + entry->size;
} while (!processed);
beach:
GST_DEBUG_OBJECT (avi, "returning %s", gst_flow_get_name (res));
+
return res;
+ /* ERRORS */
eos:
{
- return GST_FLOW_UNEXPECTED;
+ GST_LOG_OBJECT (avi, "Handled last index entry, setting EOS (%d > %d)",
+ avi->current_entry, avi->index_size);
+ /* we mark the first stream as EOS */
+ res = avi->stream[0].last_flow = GST_FLOW_UNEXPECTED;
+ goto beach;
+ }
+eos_stop:
+ {
+ GST_LOG_OBJECT (avi, "Found keyframe after segment,"
+ " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")",
+ GST_TIME_ARGS (entry->ts), GST_TIME_ARGS (avi->segment.stop));
+ res = stream->last_flow = GST_FLOW_UNEXPECTED;
+ goto beach;
}
}
@@ -2615,81 +2669,6 @@ push_tag_lists (GstAviDemux * avi)
avi->got_tags = FALSE;
}
-/*
- * Read data.
- */
-
-static GstFlowReturn
-gst_avi_demux_stream_data (GstAviDemux * avi)
-{
- /* if we have a avi->index_entries[], we don't want to read
- * the stream linearly, but seek to the next ts/index_entry. */
- //if (avi->index_entries != NULL) {
- return gst_avi_demux_process_next_entry (avi);
- //}
-#if 0
- if (!gst_avi_demux_sync (avi, &tag, FALSE))
- return FALSE;
- stream_nr = CHUNKID_TO_STREAMNR (tag);
-
- if (stream_nr < 0 || stream_nr >= avi->num_streams) {
- /* recoverable */
- GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ")",
- stream_nr, GST_FOURCC_ARGS (tag));
- if (!gst_riff_read_skip (riff))
- return FALSE;
- } else {
- avi_stream_context *stream;
- GstClockTime next_ts;
- GstFormat format;
- GstBuffer *buf;
-
- /* get buffer */
- if (!gst_riff_read_data (riff, &tag, &buf))
- return FALSE;
-
- /* get time of this buffer */
- stream = &avi->stream[stream_nr];
- format = GST_FORMAT_TIME;
- gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &next_ts);
-
- /* set delay (if any) */
- if (stream->strh->init_frames == stream->current_frame &&
- stream->delay == 0)
- stream->delay = next_ts;
-
- stream->current_frame++;
- stream->current_byte += GST_BUFFER_SIZE (buf);
-
- /* should we skip this data? */
- if (stream->skip) {
- stream->skip--;
- gst_buffer_unref (buf);
- } else {
- if (!stream->pad || !GST_PAD_IS_USABLE (stream->pad)) {
- gst_buffer_unref (buf);
- } else {
- GstClockTime dur_ts;
-
- if (stream->strh->fcc_handler == GST_MAKE_FOURCC ('D', 'I', 'B', ' ')) {
- buf = gst_avi_demux_invert (stream, buf);
- }
-
- GST_BUFFER_TIMESTAMP (buf) = next_ts;
- gst_pad_query (stream->pad, GST_QUERY_POSITION, &format, &dur_ts);
- GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
- GST_DEBUG_OBJECT (avi,
- "Pushing buffer with time=%" GST_TIME_FORMAT " over pad %s",
- GST_TIME_ARGS (next_ts), GST_PAD_NAME (stream->pad));
- gst_pad_push (stream->pad, GST_DATA (buf));
- }
- }
- }
-
- return TRUE;
-#endif
-}
-
static void
gst_avi_demux_loop (GstPad * pad)
{
@@ -2714,7 +2693,8 @@ gst_avi_demux_loop (GstPad * pad)
}
if (G_UNLIKELY (avi->got_tags))
push_tag_lists (avi);
- res = gst_avi_demux_stream_data (avi);
+ /* process each index entry in turn */
+ res = gst_avi_demux_process_next_entry (avi);
break;
default:
g_assert_not_reached ();
@@ -2723,13 +2703,13 @@ gst_avi_demux_loop (GstPad * pad)
GST_DEBUG_OBJECT (avi, "res:%s", gst_flow_get_name (res));
/* Get Aggregated flow return */
-
if ((res != GST_FLOW_OK)
&& ((res = gst_avi_demux_aggregated_flow (avi)) != GST_FLOW_OK))
goto pause;
return;
+ /* ERRORS */
pause:
{
GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
@@ -2738,6 +2718,8 @@ pause:
gboolean push_eos = TRUE;
if (res == GST_FLOW_UNEXPECTED) {
+ /* we completed the segment on EOS. */
+ avi->segment_running = FALSE;
/* handle end-of-stream/segment */
if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message
@@ -2771,12 +2753,16 @@ gst_avi_demux_sink_activate (GstPad * sinkpad)
static gboolean
gst_avi_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
{
+ GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (sinkpad));
+
if (active) {
- /* if we have a scheduler we can start the task */
+ avi->segment_running = TRUE;
gst_pad_start_task (sinkpad, (GstTaskFunction) gst_avi_demux_loop, sinkpad);
} else {
gst_pad_stop_task (sinkpad);
+ avi->segment_running = FALSE;
}
+ gst_object_unref (avi);
return TRUE;
}
@@ -2789,12 +2775,13 @@ gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
+ avi->streaming = FALSE;
+ gst_segment_init (&avi->segment, GST_FORMAT_TIME);
break;
default:
break;
}
-
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
goto done;