X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Favi%2Fgstavidemux.c;h=6bcdffd73c5159058bfab0d2f2f1899f2001ad42;hb=86933b40e928d1697a1eb72ded294e8714f00b9b;hp=ac6914343acfd7d4702328754ee7c2b8abb7e8e8;hpb=01f0a5ce3271cc133dbb26a9dc5553ddd1c51726;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c index ac69143..6bcdffd 100644 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2006> Nokia Corporation (contact ) + * Copyright (C) <2009-2010> STEricsson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -56,6 +57,12 @@ #define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v)) +#define GST_AVI_KEYFRAME 1 +#define ENTRY_IS_KEYFRAME(e) ((e)->flags == GST_AVI_KEYFRAME) +#define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME) +#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0) + + GST_DEBUG_CATEGORY_STATIC (avidemux_debug); #define GST_CAT_DEFAULT avidemux_debug @@ -104,6 +111,10 @@ static void gst_avi_demux_set_index (GstElement * element, GstIndex * index); static GstIndex *gst_avi_demux_get_index (GstElement * element); static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element, GstStateChange transition); +static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi); +static void gst_avi_demux_get_buffer_info (GstAviDemux * avi, + GstAviStream * stream, guint entry_n, GstClockTime * timestamp, + GstClockTime * ts_end, guint64 * offset, guint64 * offset_end); static void gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf); @@ -140,13 +151,6 @@ gst_avi_demux_get_type (void) static void gst_avi_demux_base_init (GstAviDemuxClass * klass) { - static const GstElementDetails gst_avi_demux_details = - GST_ELEMENT_DETAILS ("Avi demuxer", - "Codec/Demuxer", - "Demultiplex an avi file into audio and video", - "Erik Walthinsen \n" - "Wim Taymans \n" - "Thijs Vermeir "); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl; GstCaps *audcaps, *vidcaps, *subcaps; @@ -170,7 +174,12 @@ gst_avi_demux_base_init (GstAviDemuxClass * klass) gst_element_class_add_pad_template (element_class, subsrctempl); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_templ)); - gst_element_class_set_details (element_class, &gst_avi_demux_details); + gst_element_class_set_details_simple (element_class, "Avi demuxer", + "Codec/Demuxer", + "Demultiplex an avi file into audio and video", + "Erik Walthinsen , " + "Wim Taymans , " + "Thijs Vermeir "); } static void @@ -270,6 +279,7 @@ gst_avi_demux_reset (GstAviDemux * avi) avi->state = GST_AVI_DEMUX_START; avi->offset = 0; + avi->building_index = FALSE; avi->index_offset = 0; g_free (avi->avih); @@ -279,10 +289,18 @@ gst_avi_demux_reset (GstAviDemux * avi) gst_object_unref (avi->element_index); avi->element_index = NULL; + if (avi->close_seg_event) { + gst_event_unref (avi->close_seg_event); + avi->close_seg_event = NULL; + } if (avi->seg_event) { gst_event_unref (avi->seg_event); avi->seg_event = NULL; } + if (avi->seek_event) { + gst_event_unref (avi->seek_event); + avi->seek_event = NULL; + } if (avi->globaltags) gst_tag_list_free (avi->globaltags); @@ -630,9 +648,9 @@ gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before) if (before) { if (entry) { + gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val); GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val); - gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val); if (val < min) min = val; } @@ -659,6 +677,71 @@ gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before) return min; } +static guint +gst_avi_demux_index_entry_offset_search (GstAviIndexEntry * entry, + guint64 * offset) +{ + if (entry->offset < *offset) + return -1; + else if (entry->offset > *offset) + return 1; + return 0; +} + +static guint64 +gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset, + gboolean before) +{ + GstAviStream *stream; + GstAviIndexEntry *entry; + gint i; + gint64 val, min = offset; + guint index; + + for (i = 0; i < avi->num_streams; i++) { + stream = &avi->stream[i]; + + /* compensate for chunk header */ + offset += 8; + entry = + gst_util_array_binary_search (stream->index, stream->idx_n, + sizeof (GstAviIndexEntry), + (GCompareDataFunc) gst_avi_demux_index_entry_offset_search, + before ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER, &offset, NULL); + offset -= 8; + + if (entry) + index = entry - stream->index; + + if (before) { + if (entry) { + val = stream->index[index].offset; + GST_DEBUG_OBJECT (avi, + "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val); + if (val < min) + min = val; + } + continue; + } + + if (!entry) { + GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i); + stream->current_entry = 0; + stream->current_total = 0; + continue; + } + + val = stream->index[index].offset - 8; + GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, i, + val); + + stream->current_total = stream->index[index].total; + stream->current_entry = index; + } + + return min; +} + #define GST_AVI_SEEK_PUSH_DISPLACE (4 * GST_SECOND) static gboolean @@ -678,7 +761,6 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event) gint64 start, stop, time, offset = 0; gboolean update; GstSegment segment; - GstIndexEntry *entry; /* some debug output */ gst_segment_init (&segment, GST_FORMAT_UNDEFINED); @@ -702,31 +784,72 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event) goto exit; } - if (!avi->element_index) { - GST_WARNING_OBJECT (avi, "no index data, forcing EOS"); - goto eos; - } + if (avi->have_index) { + GstAviIndexEntry *entry; + guint i = 0, index = 0, k = 0; + GstAviStream *stream; - /* Let's check if we have an index entry for this position */ - entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id, - GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE, - GST_FORMAT_BYTES, start); + /* compensate chunk header, stored index offset points after header */ + start += 8; + /* find which stream we're on */ + do { + stream = &avi->stream[i]; - /* we can not go where we have not yet been before ... */ - if (!entry) { - GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS"); + /* find the index for start bytes offset */ + entry = gst_util_array_binary_search (stream->index, + stream->idx_n, sizeof (GstAviIndexEntry), + (GCompareDataFunc) gst_avi_demux_index_entry_offset_search, + GST_SEARCH_MODE_AFTER, &start, NULL); + + if (entry == NULL) + continue; + index = entry - stream->index; + + /* we are on the stream with a chunk start offset closest to start */ + if (!offset || stream->index[index].offset < offset) { + offset = stream->index[index].offset; + k = i; + } + /* exact match needs no further searching */ + if (stream->index[index].offset == start) + break; + } while (++i < avi->num_streams); + start -= 8; + offset -= 8; + stream = &avi->stream[k]; + + /* so we have no idea what is to come, or where we are */ + if (!offset) { + GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS"); + goto eos; + } + + /* get the ts corresponding to start offset bytes for the stream */ + gst_avi_demux_get_buffer_info (avi, stream, index, + (GstClockTime *) & time, NULL, NULL, NULL); + } else if (avi->element_index) { + GstIndexEntry *entry; + + /* Let's check if we have an index entry for this position */ + entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id, + GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE, + GST_FORMAT_BYTES, start); + + /* we can not go where we have not yet been before ... */ + if (!entry) { + GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS"); + goto eos; + } + + gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time); + gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &offset); + } else { + GST_WARNING_OBJECT (avi, "no index data, forcing EOS"); goto eos; } - offset = start; - gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time); - gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &start); stop = GST_CLOCK_TIME_NONE; - /* compensate for slack */ - if (time) - time += GST_AVI_SEEK_PUSH_DISPLACE; - /* set up segment and send downstream */ gst_segment_set_newsegment_full (&avi->segment, update, rate, arate, GST_FORMAT_TIME, time, stop, time); @@ -741,16 +864,17 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event) GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT, start); /* adjust state for streaming thread accordingly */ - avi->offset = offset; - gst_avi_demux_seek_streams (avi, offset, FALSE); + if (avi->have_index) + gst_avi_demux_seek_streams_index (avi, offset, FALSE); + else + gst_avi_demux_seek_streams (avi, offset, FALSE); /* set up streaming thread */ - avi->offset = offset; - avi->todrop = start - offset; + g_assert (offset >= start); + avi->offset = start; + avi->todrop = offset - start; exit: - /* in any case, clear leftover in current segment, if any */ - gst_adapter_clear (avi->adapter); gst_event_unref (event); res = TRUE; break; @@ -804,7 +928,6 @@ gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: - /* handle seeking only in pull mode */ if (!avi->streaming) { res = gst_avi_demux_handle_seek (avi, pad, event); } else { @@ -1010,7 +1133,6 @@ wrong_header: } /* AVI header handling */ - /* * gst_avi_demux_parse_avih: * @avi: caller element (used for errors/debug). @@ -1471,51 +1593,90 @@ out_of_mem: } } -#if 0 +/* + * Create and push a flushing seek event upstream + */ +static gboolean +perform_seek_to_offset (GstAviDemux * demux, guint64 offset) +{ + GstEvent *event; + gboolean res = 0; + + GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset); + + event = + gst_event_new_seek (1.0, GST_FORMAT_BYTES, + GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset, + GST_SEEK_TYPE_NONE, -1); + + res = gst_pad_push_event (demux->sinkpad, event); + + if (res) + demux->offset = offset; + return res; +} + /* * Read AVI index when streaming */ -static void +static gboolean gst_avi_demux_read_subindexes_push (GstAviDemux * avi) { guint32 tag = 0, size; GstBuffer *buf = NULL; - gint i, n; + guint odml_stream; GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams); - for (n = 0; n < avi->num_streams; n++) { - GstAviStream *stream = &avi->stream[n]; + if (avi->odml_subidxs[avi->odml_subidx] != avi->offset) + return FALSE; - for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) { - if (!gst_avi_demux_peek_chunk (avi, &tag, &size)) - continue; - else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10, - '0' + stream->num % 10)) && - (tag != GST_MAKE_FOURCC ('0' + stream->num / 10, - '0' + stream->num % 10, 'i', 'x'))) { - GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")", - GST_FOURCC_ARGS (tag)); - continue; - } + if (!gst_avi_demux_peek_chunk (avi, &tag, &size)) + return TRUE; - avi->offset += 8 + GST_ROUND_UP_2 (size); + /* this is the ODML chunk we expect */ + odml_stream = avi->odml_stream; - buf = gst_buffer_new (); - GST_BUFFER_DATA (buf) = gst_adapter_take (avi->adapter, size); - GST_BUFFER_SIZE (buf) = size; + if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + odml_stream / 10, + '0' + odml_stream % 10)) && + (tag != GST_MAKE_FOURCC ('0' + odml_stream / 10, + '0' + odml_stream % 10, 'i', 'x'))) { + GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")", + GST_FOURCC_ARGS (tag)); + return FALSE; + } - if (!gst_avi_demux_parse_subindex (avi, stream, buf)) - continue; - } + avi->offset += 8 + GST_ROUND_UP_2 (size); + /* flush chunk header so we get just the 'size' payload data */ + gst_adapter_flush (avi->adapter, 8); + buf = gst_adapter_take_buffer (avi->adapter, size); - g_free (stream->indexes); - stream->indexes = NULL; + if (!gst_avi_demux_parse_subindex (avi, &avi->stream[odml_stream], buf)) + return FALSE; + + /* we parsed the index, go to next subindex */ + avi->odml_subidx++; + + if (avi->odml_subidxs[avi->odml_subidx] == GST_BUFFER_OFFSET_NONE) { + /* we reached the end of the indexes for this stream, move to the next + * stream to handle the first index */ + avi->odml_stream++; + avi->odml_subidx = 0; + + if (avi->odml_stream < avi->num_streams) { + /* there are more indexes */ + avi->odml_subidxs = avi->stream[avi->odml_stream].indexes; + } else { + /* we're done, get stream stats now */ + avi->have_index = gst_avi_demux_do_index_stats (avi); + + return TRUE; + } } - /* get stream stats now */ - avi->have_index = gst_avi_demux_do_index_stats (avi); + + /* seek to next index */ + return perform_seek_to_offset (avi, avi->odml_subidxs[avi->odml_subidx]); } -#endif /* * Read AVI index @@ -1831,14 +1992,36 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res); break; case GST_RIFF_FCC_auds: - stream->is_vbr = (stream->strh->samplesize == 0) - && stream->strh->scale > 1; res = gst_riff_parse_strf_auds (element, sub, &stream->strf.auds, &stream->extradata); + stream->is_vbr = (stream->strh->samplesize == 0) + && stream->strh->scale > 1 + && stream->strf.auds->blockalign != 1; sub = NULL; GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d", stream->is_vbr, res); + /* we need these or we have no way to come up with timestamps */ + if ((!stream->is_vbr && !stream->strf.auds->av_bps) || + (stream->is_vbr && (!stream->strh->scale || + !stream->strh->rate))) { + GST_WARNING_OBJECT (element, + "invalid audio header, ignoring stream"); + goto fail; + } + /* some more sanity checks */ + if (stream->is_vbr) { + if (stream->strf.auds->blockalign <= 4) { + /* that would mean (too) many frames per chunk, + * so not likely set as expected */ + GST_DEBUG_OBJECT (element, + "suspicious blockalign %d for VBR audio; " + "overriding to 1 frame per chunk", + stream->strf.auds->blockalign); + /* this should top any likely value */ + stream->strf.auds->blockalign = (1 << 12); + } + } break; case GST_RIFF_FCC_iavs: stream->is_vbr = TRUE; @@ -1912,6 +2095,9 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) } GST_DEBUG_OBJECT (avi, "stream name: %s", stream->name); break; + case GST_RIFF_IDIT: + gst_avi_demux_parse_idit (avi, sub); + break; default: if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') || tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10, @@ -1926,6 +2112,7 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring", GST_FOURCC_ARGS (tag)); /* fall-through */ + case GST_RIFF_TAG_JUNQ: case GST_RIFF_TAG_JUNK: break; } @@ -2167,6 +2354,7 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf) "Unknown tag %" GST_FOURCC_FORMAT " in ODML header", GST_FOURCC_ARGS (tag)); /* fall-through */ + case GST_RIFF_TAG_JUNQ: case GST_RIFF_TAG_JUNK: next: /* skip and move to next chunk */ @@ -2185,7 +2373,7 @@ gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf) static guint gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream) { - return stream->idx_n - 1; + return stream->idx_n; } /* find a previous entry in the index with the given flags */ @@ -2541,6 +2729,89 @@ zero_index: } /* + * gst_avi_demux_stream_index_push: + * @avi: avi demuxer object. + * + * Read index. + */ +static void +gst_avi_demux_stream_index_push (GstAviDemux * avi) +{ + guint64 offset = avi->idx1_offset; + GstBuffer *buf; + guint32 tag; + guint32 size; + + GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset); + + /* get chunk information */ + if (!gst_avi_demux_peek_chunk (avi, &tag, &size)) + return; + + /* check tag first before blindly trying to read 'size' bytes */ + if (tag == GST_RIFF_TAG_LIST) { + /* this is the movi tag */ + GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT, + (8 + GST_ROUND_UP_2 (size))); + avi->idx1_offset = offset + 8 + GST_ROUND_UP_2 (size); + /* issue seek to allow chain function to handle it and return! */ + perform_seek_to_offset (avi, avi->idx1_offset); + return; + } + + if (tag != GST_RIFF_TAG_idx1) + goto no_index; + + GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset); + + /* flush chunk header */ + gst_adapter_flush (avi->adapter, 8); + /* read chunk payload */ + buf = gst_adapter_take_buffer (avi->adapter, size); + if (!buf) + goto pull_failed; + /* advance offset */ + offset += 8 + GST_ROUND_UP_2 (size); + + GST_DEBUG ("will parse index chunk size %u for tag %" + GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag)); + + avi->offset = avi->first_movi_offset; + gst_avi_demux_parse_index (avi, buf); + +#ifndef GST_DISABLE_GST_DEBUG + /* debug our indexes */ + { + gint i; + GstAviStream *stream; + + for (i = 0; i < avi->num_streams; i++) { + stream = &avi->stream[i]; + GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes", + i, stream->idx_n, stream->total_bytes); + } + } +#endif + return; + + /* ERRORS */ +pull_failed: + { + GST_DEBUG_OBJECT (avi, + "taking data from adapter failed: pos=%" G_GUINT64_FORMAT " size=%u", + offset, size); + return; + } +no_index: + { + GST_WARNING_OBJECT (avi, + "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT, + GST_FOURCC_ARGS (tag)); + return; + } +} + +/* * gst_avi_demux_peek_tag: * * Returns the tag and size of the next chunk @@ -2689,7 +2960,6 @@ gst_avi_demux_stream_scan (GstAviDemux * avi) /* collect stats */ avi->have_index = gst_avi_demux_do_index_stats (avi); - return TRUE; /* ERRORS */ @@ -2822,11 +3092,6 @@ gst_avi_demux_check_seekability (GstAviDemux * avi) seekable = FALSE; } - if (!avi->element_index) { - GST_DEBUG_OBJECT (avi, "no index"); - seekable = FALSE; - } - done: GST_INFO_OBJECT (avi, "seekable: %d (%" G_GUINT64_FORMAT " - %" G_GUINT64_FORMAT ")", seekable, start, stop); @@ -2920,6 +3185,7 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi) GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub)))); /* fall-through */ + case GST_RIFF_TAG_JUNQ: case GST_RIFF_TAG_JUNK: goto next; } @@ -2932,6 +3198,7 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi) "Unknown off %d tag %" GST_FOURCC_FORMAT " in AVI header", offset, GST_FOURCC_ARGS (tag)); /* fall-through */ + case GST_RIFF_TAG_JUNQ: case GST_RIFF_TAG_JUNK: next: /* move to next chunk */ @@ -2974,7 +3241,10 @@ gst_avi_demux_stream_header_push (GstAviDemux * avi) switch (ltag) { case GST_RIFF_LIST_movi: gst_adapter_flush (avi->adapter, 12); + if (!avi->first_movi_offset) + avi->first_movi_offset = avi->offset; avi->offset += 12; + avi->idx1_offset = avi->offset + size - 4; goto skipping_done; case GST_RIFF_LIST_INFO: GST_DEBUG ("Found INFO chunk"); @@ -3113,9 +3383,12 @@ header_wrong_avih: } static void -gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d) +gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d, + gint h, gint min, gint s) { GDate *date; + GstDateTime *dt; + date = g_date_new_dmy (d, m, y); if (!g_date_valid (date)) { /* bogus date */ @@ -3124,12 +3397,19 @@ gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d) return; } + dt = gst_date_time_new_local_time (y, m, d, h, min, s); + if (avi->globaltags == NULL) avi->globaltags = gst_tag_list_new (); gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); g_date_free (date); + if (dt) { + gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE_TIME, + dt, NULL); + gst_date_time_unref (dt); + } } static void @@ -3143,7 +3423,7 @@ gst_avi_demux_parse_idit_nums_only (GstAviDemux * avi, gchar * data) GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag"); return; } - gst_avi_demux_add_date_tag (avi, y, m, d); + gst_avi_demux_add_date_tag (avi, y, m, d, 0, 0, 0); } static gint @@ -3194,7 +3474,7 @@ gst_avi_demux_parse_idit_text (GstAviDemux * avi, gchar * data) return; } month = get_month_num (monthstr, strlen (monthstr)); - gst_avi_demux_add_date_tag (avi, year, month, day); + gst_avi_demux_add_date_tag (avi, year, month, day, hour, min, sec); } static void @@ -3356,6 +3636,7 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi) GST_MEMDUMP_OBJECT (avi, "Unknown list", GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub)); /* fall-through */ + case GST_RIFF_TAG_JUNQ: case GST_RIFF_TAG_JUNK: goto next; } @@ -3371,6 +3652,7 @@ gst_avi_demux_stream_header_pull (GstAviDemux * avi) GST_MEMDUMP_OBJECT (avi, "Unknown tag", GST_BUFFER_DATA (sub), GST_BUFFER_SIZE (sub)); /* fall-through */ + case GST_RIFF_TAG_JUNQ: case GST_RIFF_TAG_JUNK: next: if (sub) @@ -3650,6 +3932,9 @@ gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream, GST_TIME_ARGS (stream->current_timestamp), GST_TIME_ARGS (stream->current_ts_end), stream->current_offset, stream->current_offset_end); + + GST_DEBUG_OBJECT (avi, "Seeking to offset %" G_GUINT64_FORMAT, + stream->index[index].offset); } /* @@ -3664,7 +3949,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment) GstAviStream *stream; seek_time = segment->last_stop; - keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT); + keyframe = ! !(segment->flags & GST_SEEK_FLAG_KEY_UNIT); GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe); @@ -3727,7 +4012,7 @@ gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment) } /* - * Handle seek event. + * Handle seek event in pull mode. */ static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event) @@ -3809,30 +4094,21 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event) * actually never fails. */ gst_avi_demux_do_seek (avi, &seeksegment); + gst_event_replace (&avi->close_seg_event, NULL); if (flush) { GstEvent *fevent = gst_event_new_flush_stop (); GST_DEBUG_OBJECT (avi, "sending flush stop"); gst_avi_demux_push_event (avi, gst_event_ref (fevent)); gst_pad_push_event (avi->sinkpad, fevent); - - /* reset the last flow and mark discont, FLUSH is always DISCONT */ - for (i = 0; i < avi->num_streams; i++) { - GST_DEBUG_OBJECT (avi, "marking DISCONT"); - avi->stream[i].last_flow = GST_FLOW_OK; - avi->stream[i].discont = TRUE; - } } 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_full (TRUE, + avi->close_seg_event = gst_event_new_new_segment_full (TRUE, avi->segment.rate, avi->segment.applied_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 */ @@ -3869,6 +4145,12 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event) gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop, avi->sinkpad); } + /* reset the last flow and mark discont, seek is always DISCONT */ + for (i = 0; i < avi->num_streams; i++) { + GST_DEBUG_OBJECT (avi, "marking DISCONT"); + avi->stream[i].last_flow = GST_FLOW_OK; + avi->stream[i].discont = TRUE; + } GST_PAD_STREAM_UNLOCK (avi->sinkpad); return TRUE; @@ -3881,102 +4163,214 @@ no_format: } } +/* + * Handle seek event in push mode. + */ static gboolean -gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, - GstEvent * event) +avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event) { gdouble rate; GstFormat format; GstSeekFlags flags; - GstSeekType cur_type, stop_type; + GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type; gint64 cur, stop; - gboolean res; - gint64 byte_cur; - gint64 time = 0; - GstIndexEntry *entry; + gboolean keyframe; + GstAviStream *stream; + guint index; + guint n, str_num; + guint64 min_offset; + GstSegment seeksegment; + gboolean update; - GST_DEBUG_OBJECT (avi, "doing push-based seek"); + /* check we have the index */ + if (!avi->have_index) { + GST_DEBUG_OBJECT (avi, "no seek index built, seek aborted."); + return FALSE; + } else { + GST_DEBUG_OBJECT (avi, "doing push-based seek with event"); + } gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); - if (stop_type != GST_SEEK_TYPE_NONE) - goto unsupported_seek; - stop = -1; + if (format != GST_FORMAT_TIME) { + GstFormat fmt = GST_FORMAT_TIME; + gboolean res = TRUE; - /* only forward streaming and seeking is possible */ - if (rate <= 0) - goto unsupported_seek; + 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) { + GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted."); + return FALSE; + } - /* only TIME */ - if (format != GST_FORMAT_TIME) - goto unsupported_format; + format = fmt; + } - /* not really advisable otherwise */ - if ((flags & GST_SEEK_FLAG_FLUSH) == 0) - goto unsupported_seek; + /* let gst_segment handle any tricky stuff */ + GST_DEBUG_OBJECT (avi, "configuring seek"); + memcpy (&seeksegment, &avi->segment, sizeof (GstSegment)); + gst_segment_set_seek (&seeksegment, rate, format, flags, + cur_type, cur, stop_type, stop, &update); - /* should have index, let's check anyway */ - if (!avi->element_index) - goto abort_seek; + keyframe = ! !(flags & GST_SEEK_FLAG_KEY_UNIT); + cur = seeksegment.last_stop; - /* find reasonable corresponding BYTE position - * note that we have no keyframe info in push mode whatsoever, - * so we can not cater for related stuff in any way */ + GST_DEBUG_OBJECT (avi, + "Seek requested: ts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT + ", kf %u, rate %lf", GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), keyframe, + rate); - /* some slack aiming for a keyframe */ - if (cur < GST_AVI_SEEK_PUSH_DISPLACE) - cur = 0; - else - cur -= GST_AVI_SEEK_PUSH_DISPLACE; + if (rate < 0) { + GST_DEBUG_OBJECT (avi, "negative rate seek not supported in push mode"); + return FALSE; + } - entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id, - GST_INDEX_LOOKUP_BEFORE, GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_TIME, cur); + /* FIXME, this code assumes the main stream with keyframes is stream 0, + * which is mostly correct... */ + str_num = avi->main_stream; + stream = &avi->stream[str_num]; - if (!entry) - goto abort_seek; + /* get the entry index for the requested position */ + index = gst_avi_demux_index_for_time (avi, stream, cur); + GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT, + str_num, index, GST_TIME_ARGS (cur)); - gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &byte_cur); - gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time); + /* check if we are already on a keyframe */ + if (!ENTRY_IS_KEYFRAME (&stream->index[index])) { + GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back"); + /* now go to the previous keyframe, this is where we should start + * decoding from. */ + index = gst_avi_demux_index_prev (avi, stream, index, TRUE); + GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", index); + } - GST_DEBUG_OBJECT (avi, "found index entry for %" GST_TIME_FORMAT - " at %" GST_TIME_FORMAT ", located at offset %" G_GINT64_FORMAT, - GST_TIME_ARGS (cur), GST_TIME_ARGS (time), byte_cur); + gst_avi_demux_get_buffer_info (avi, stream, index, + &stream->current_timestamp, &stream->current_ts_end, + &stream->current_offset, &stream->current_offset_end); - /* adjust offset to be in each stream's region */ - byte_cur = gst_avi_demux_seek_streams (avi, byte_cur, TRUE); + /* re-use cur to be the timestamp of the seek as it _will_ be */ + cur = stream->current_timestamp; - /* let's not try push seeking if target and entry are too far apart */ - if (cur - time > 30 * GST_SECOND) - goto abort_seek; + min_offset = stream->index[index].offset; + avi->seek_kf_offset = min_offset - 8; - GST_DEBUG_OBJECT (avi, "Pushing BYTE seek rate %g, " - "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur, - stop); - /* BYTE seek event */ - event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur, - stop_type, stop); - res = gst_pad_push_event (avi->sinkpad, event); + GST_DEBUG_OBJECT (avi, + "Seek to: ts %" GST_TIME_FORMAT " (on str %u, idx %u, offset %" + G_GUINT64_FORMAT ")", GST_TIME_ARGS (stream->current_timestamp), str_num, + index, min_offset); - return res; + for (n = 0; n < avi->num_streams; n++) { + GstAviStream *str = &avi->stream[n]; + guint idx; - /* ERRORS */ -abort_seek: - { - GST_DEBUG_OBJECT (avi, "could not determine byte position to seek to, " - "seek aborted."); - return FALSE; + if (n == avi->main_stream) + continue; + + /* get the entry index for the requested position */ + idx = gst_avi_demux_index_for_time (avi, str, cur); + GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT, n, + idx, GST_TIME_ARGS (cur)); + + /* check if we are already on a keyframe */ + if (!ENTRY_IS_KEYFRAME (&str->index[idx])) { + GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back"); + /* now go to the previous keyframe, this is where we should start + * decoding from. */ + idx = gst_avi_demux_index_prev (avi, str, idx, TRUE); + GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", idx); + } + + gst_avi_demux_get_buffer_info (avi, str, idx, + &str->current_timestamp, &str->current_ts_end, + &str->current_offset, &str->current_offset_end); + + if (str->index[idx].offset < min_offset) { + min_offset = str->index[idx].offset; + GST_DEBUG_OBJECT (avi, + "Found an earlier offset at %" G_GUINT64_FORMAT ", str %u", + min_offset, n); + str_num = n; + stream = str; + index = idx; + } } -unsupported_seek: - { - GST_DEBUG_OBJECT (avi, "unsupported seek, seek aborted."); + + GST_DEBUG_OBJECT (avi, + "Seek performed: str %u, offset %" G_GUINT64_FORMAT ", idx %u, ts %" + GST_TIME_FORMAT ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT + ", off_end %" G_GUINT64_FORMAT, str_num, min_offset, index, + GST_TIME_ARGS (stream->current_timestamp), + GST_TIME_ARGS (stream->current_ts_end), stream->current_offset, + stream->current_offset_end); + + /* index data refers to data, not chunk header (for pull mode convenience) */ + min_offset -= 8; + GST_DEBUG_OBJECT (avi, "seeking to chunk at offset %" G_GUINT64_FORMAT, + min_offset); + + if (!perform_seek_to_offset (avi, min_offset)) { + GST_DEBUG_OBJECT (avi, "seek event failed!"); return FALSE; } -unsupported_format: - { - GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted."); - return FALSE; + + return TRUE; +} + +/* + * Handle whether we can perform the seek event or if we have to let the chain + * function handle seeks to build the seek indexes first. + */ +static gboolean +gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, + GstEvent * event) +{ + /* check for having parsed index already */ + if (!avi->have_index) { + guint64 offset = 0; + gboolean building_index; + + GST_OBJECT_LOCK (avi); + /* handle the seek event in the chain function */ + avi->state = GST_AVI_DEMUX_SEEK; + + /* copy the event */ + if (avi->seek_event) + gst_event_unref (avi->seek_event); + avi->seek_event = gst_event_ref (event); + + /* set the building_index flag so that only one thread can setup the + * structures for index seeking. */ + building_index = avi->building_index; + if (!building_index) { + avi->building_index = TRUE; + if (avi->stream[0].indexes) { + avi->odml_stream = 0; + avi->odml_subidxs = avi->stream[avi->odml_stream].indexes; + offset = avi->odml_subidxs[0]; + } else { + offset = avi->idx1_offset; + } + } + GST_OBJECT_UNLOCK (avi); + + if (!building_index) { + /* seek to the first subindex or legacy index */ + GST_INFO_OBJECT (avi, + "Seeking to legacy index/first subindex at %" G_GUINT64_FORMAT, + offset); + return perform_seek_to_offset (avi, offset); + } + + /* FIXME: we have to always return true so that we don't block the seek + * thread. + * Note: maybe it is OK to return true if we're still building the index */ + return TRUE; } + + return avi_demux_handle_seek_push (avi, pad, event); } /* @@ -4122,7 +4516,7 @@ gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream, new_entry = old_entry + 1; /* see if we reached the end */ - if (new_entry > stream->stop_entry) { + if (new_entry >= stream->stop_entry) { if (avi->segment.rate < 0.0) { if (stream->step_entry == stream->start_entry) { /* we stepped all the way to the start, eos */ @@ -4472,7 +4866,7 @@ gst_avi_demux_stream_data (GstAviDemux * avi) continue; } return GST_FLOW_OK; - } else if (tag == GST_RIFF_TAG_JUNK) { + } else if (tag == GST_RIFF_TAG_JUNK || tag == GST_RIFF_TAG_JUNQ) { /* rec list might contain JUNK chunks */ GST_DEBUG ("Found JUNK tag"); if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) { @@ -4494,9 +4888,13 @@ gst_avi_demux_stream_data (GstAviDemux * avi) * through the whole file */ if (avi->abort_buffering) { avi->abort_buffering = FALSE; - gst_adapter_flush (avi->adapter, 8); + if (size) { + gst_adapter_flush (avi->adapter, 8); + return GST_FLOW_OK; + } + } else { + return GST_FLOW_OK; } - return GST_FLOW_OK; } GST_DEBUG ("chunk ID %" GST_FOURCC_FORMAT ", size %u", GST_FOURCC_ARGS (tag), size); @@ -4512,15 +4910,32 @@ gst_avi_demux_stream_data (GstAviDemux * avi) } else { GstAviStream *stream; GstClockTime next_ts = 0; - GstBuffer *buf; + GstBuffer *buf = NULL; guint64 offset; + gboolean saw_desired_kf = stream_nr != avi->main_stream + || avi->offset >= avi->seek_kf_offset; - gst_adapter_flush (avi->adapter, 8); + if (stream_nr == avi->main_stream && avi->offset == avi->seek_kf_offset) { + GST_DEBUG_OBJECT (avi, "Desired keyframe reached"); + avi->seek_kf_offset = 0; + } + + if (saw_desired_kf) { + gst_adapter_flush (avi->adapter, 8); + /* get buffer */ + if (size) { + buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size)); + /* patch the size */ + GST_BUFFER_SIZE (buf) = size; + } else { + buf = NULL; + } + } else { + GST_DEBUG_OBJECT (avi, + "Desired keyframe not yet reached, flushing chunk"); + gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size)); + } - /* get buffer */ - buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size)); - /* patch the size */ - GST_BUFFER_SIZE (buf) = size; offset = avi->offset; avi->offset += 8 + GST_ROUND_UP_2 (size); @@ -4536,10 +4951,9 @@ gst_avi_demux_stream_data (GstAviDemux * avi) if (G_UNLIKELY (!stream->pad)) { GST_WARNING_OBJECT (avi, "no pad for stream ID %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)); - gst_buffer_unref (buf); + if (buf) + gst_buffer_unref (buf); } else { - GstClockTime dur_ts = 0; - /* get time of this buffer */ gst_pad_query_position (stream->pad, &format, (gint64 *) & next_ts); if (G_UNLIKELY (format != GST_FORMAT_TIME)) @@ -4551,47 +4965,52 @@ gst_avi_demux_stream_data (GstAviDemux * avi) stream->current_entry++; stream->current_total += size; - /* invert the picture if needed */ - buf = gst_avi_demux_invert (stream, buf); - - gst_pad_query_position (stream->pad, &format, (gint64 *) & dur_ts); - if (G_UNLIKELY (format != GST_FORMAT_TIME)) - goto wrong_format; + /* update current position in the segment */ + gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, next_ts); - GST_BUFFER_TIMESTAMP (buf) = next_ts; - GST_BUFFER_DURATION (buf) = dur_ts - next_ts; - if (stream->strh->type == GST_RIFF_FCC_vids) { - GST_BUFFER_OFFSET (buf) = stream->current_entry - 1; - GST_BUFFER_OFFSET_END (buf) = stream->current_entry; - } else { - GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE; - GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; - } + if (saw_desired_kf && buf) { + GstClockTime dur_ts = 0; - gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad)); - GST_DEBUG_OBJECT (avi, - "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %" - GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT - " and size %d over pad %s", GST_TIME_ARGS (next_ts), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf), - size, GST_PAD_NAME (stream->pad)); + /* invert the picture if needed */ + buf = gst_avi_demux_invert (stream, buf); - /* update current position in the segment */ - gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, next_ts); + gst_pad_query_position (stream->pad, &format, (gint64 *) & dur_ts); + if (G_UNLIKELY (format != GST_FORMAT_TIME)) + goto wrong_format; - /* mark discont when pending */ - if (G_UNLIKELY (stream->discont)) { - GST_DEBUG_OBJECT (avi, "Setting DISCONT"); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - stream->discont = FALSE; - } - res = gst_pad_push (stream->pad, buf); + GST_BUFFER_TIMESTAMP (buf) = next_ts; + GST_BUFFER_DURATION (buf) = dur_ts - next_ts; + if (stream->strh->type == GST_RIFF_FCC_vids) { + GST_BUFFER_OFFSET (buf) = stream->current_entry - 1; + GST_BUFFER_OFFSET_END (buf) = stream->current_entry; + } else { + GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE; + GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE; + } - /* combine flows */ - res = gst_avi_demux_combine_flows (avi, stream, res); - if (G_UNLIKELY (res != GST_FLOW_OK)) { - GST_DEBUG ("Push failed; %s", gst_flow_get_name (res)); - return res; + gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad)); + GST_DEBUG_OBJECT (avi, + "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %" + GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT + " and size %d over pad %s", GST_TIME_ARGS (next_ts), + GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), + GST_BUFFER_OFFSET (buf), size, GST_PAD_NAME (stream->pad)); + + /* mark discont when pending */ + if (G_UNLIKELY (stream->discont)) { + GST_DEBUG_OBJECT (avi, "Setting DISCONT"); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; + } + res = gst_pad_push (stream->pad, buf); + buf = NULL; + + /* combine flows */ + res = gst_avi_demux_combine_flows (avi, stream, res); + if (G_UNLIKELY (res != GST_FLOW_OK)) { + GST_DEBUG ("Push failed; %s", gst_flow_get_name (res)); + return res; + } } } } @@ -4674,6 +5093,10 @@ gst_avi_demux_loop (GstPad * pad) avi->state = GST_AVI_DEMUX_MOVI; break; case GST_AVI_DEMUX_MOVI: + if (G_UNLIKELY (avi->close_seg_event)) { + gst_avi_demux_push_event (avi, avi->close_seg_event); + avi->close_seg_event = NULL; + } if (G_UNLIKELY (avi->seg_event)) { gst_avi_demux_push_event (avi, avi->seg_event); avi->seg_event = NULL; @@ -4699,13 +5122,13 @@ gst_avi_demux_loop (GstPad * pad) return; /* ERRORS */ -pause: - GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res)); - avi->segment_running = FALSE; - gst_pad_pause_task (avi->sinkpad); +pause:{ + + gboolean push_eos = FALSE; + GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res)); + avi->segment_running = FALSE; + gst_pad_pause_task (avi->sinkpad); - if (GST_FLOW_IS_FATAL (res) || (res == GST_FLOW_NOT_LINKED)) { - gboolean push_eos = TRUE; if (res == GST_FLOW_UNEXPECTED) { /* handle end-of-stream/segment */ @@ -4721,13 +5144,17 @@ pause: (GST_ELEMENT_CAST (avi), gst_message_new_segment_done (GST_OBJECT_CAST (avi), GST_FORMAT_TIME, stop)); - push_eos = FALSE; + } else { + push_eos = TRUE; } - } else { - /* for fatal errors we post an error message */ + } else if (res == GST_FLOW_NOT_LINKED || res < GST_FLOW_UNEXPECTED) { + /* for fatal errors we post an error message, wrong-state is + * not fatal because it happens due to flushes and only means + * that we should stop now. */ GST_ELEMENT_ERROR (avi, STREAM, FAILED, (_("Internal data stream error.")), ("streaming stopped, reason %s", gst_flow_get_name (res))); + push_eos = TRUE; } if (push_eos) { GST_INFO_OBJECT (avi, "sending eos"); @@ -4773,6 +5200,10 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf) } break; case GST_AVI_DEMUX_MOVI: + if (G_UNLIKELY (avi->close_seg_event)) { + gst_avi_demux_push_event (avi, avi->close_seg_event); + avi->close_seg_event = NULL; + } if (G_UNLIKELY (avi->seg_event)) { gst_avi_demux_push_event (avi, avi->seg_event); avi->seg_event = NULL; @@ -4782,6 +5213,42 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf) } res = gst_avi_demux_stream_data (avi); break; + case GST_AVI_DEMUX_SEEK: + { + GstEvent *event; + + res = GST_FLOW_OK; + + /* obtain and parse indexes */ + if (avi->stream[0].indexes && !gst_avi_demux_read_subindexes_push (avi)) + /* seek in subindex read function failed */ + goto index_failed; + + if (!avi->stream[0].indexes && !avi->have_index + && avi->avih->flags & GST_RIFF_AVIH_HASINDEX) + gst_avi_demux_stream_index_push (avi); + + if (avi->have_index) { + /* use the indexes now to construct nice durations */ + gst_avi_demux_calculate_durations_from_index (avi); + } else { + /* still parsing indexes */ + break; + } + + GST_OBJECT_LOCK (avi); + event = avi->seek_event; + avi->seek_event = NULL; + GST_OBJECT_UNLOCK (avi); + + /* calculate and perform seek */ + if (!avi_demux_handle_seek_push (avi, avi->sinkpad, event)) + goto seek_failed; + + gst_event_unref (event); + avi->state = GST_AVI_DEMUX_MOVI; + break; + } default: GST_ELEMENT_ERROR (avi, STREAM, FAILED, (NULL), ("Illegal internal state")); @@ -4792,13 +5259,28 @@ gst_avi_demux_chain (GstPad * pad, GstBuffer * buf) GST_DEBUG_OBJECT (avi, "state: %d res:%s", avi->state, gst_flow_get_name (res)); - if (G_UNLIKELY (avi->abort_buffering)) { + if (G_UNLIKELY (avi->abort_buffering)) + goto abort_buffering; + + return res; + + /* ERRORS */ +index_failed: + { + GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("failed to read indexes")); + return GST_FLOW_ERROR; + } +seek_failed: + { + GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("push mode seek failed")); + return GST_FLOW_ERROR; + } +abort_buffering: + { avi->abort_buffering = FALSE; - res = GST_FLOW_ERROR; GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("unhandled buffer size")); + return GST_FLOW_ERROR; } - - return res; } static gboolean @@ -4913,6 +5395,7 @@ gst_avi_demux_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: + avi->have_index = FALSE; gst_avi_demux_reset (avi); break; default: