avidemux: more DISCONT handling
[platform/upstream/gst-plugins-good.git] / gst / avi / gstavidemux.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@temple-baptist.com>
3  * Copyright (C) <2006> Nokia Corporation (contact <stefan.kost@nokia.com>)
4  * Copyright (C) <2009-2010> STEricsson <benjamin.gaignard@stericsson.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /* Element-Checklist-Version: 5 */
22
23 /**
24  * SECTION:element-avidemux
25  *
26  * Demuxes an .avi file into raw or compressed audio and/or video streams.
27  *
28  * This element supports both push and pull-based scheduling, depending on the
29  * capabilities of the upstream elements.
30  *
31  * <refsect2>
32  * <title>Example launch line</title>
33  * |[
34  * gst-launch filesrc location=test.avi ! avidemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
35  * ]| Play (parse and decode) an .avi file and try to output it to
36  * an automatically detected soundcard and videosink. If the AVI file contains
37  * compressed audio or video data, this will only work if you have the
38  * right decoder elements/plugins installed.
39  * </refsect2>
40  *
41  * Last reviewed on 2006-12-29 (0.10.6)
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #include <string.h>
49 #include <stdio.h>
50
51 #include "gst/riff/riff-media.h"
52 #include "gstavidemux.h"
53 #include "avi-ids.h"
54 #include <gst/gst-i18n-plugin.h>
55 #include <gst/base/gstadapter.h>
56
57
58 #define DIV_ROUND_UP(s,v) (((s) + ((v)-1)) / (v))
59
60 GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
61 #define GST_CAT_DEFAULT avidemux_debug
62
63 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
64     GST_PAD_SINK,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS ("video/x-msvideo")
67     );
68
69 static void gst_avi_demux_base_init (GstAviDemuxClass * klass);
70 static void gst_avi_demux_class_init (GstAviDemuxClass * klass);
71 static void gst_avi_demux_init (GstAviDemux * avi);
72 static void gst_avi_demux_finalize (GObject * object);
73
74 static void gst_avi_demux_reset (GstAviDemux * avi);
75
76 #if 0
77 static const GstEventMask *gst_avi_demux_get_event_mask (GstPad * pad);
78 #endif
79 static gboolean gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event);
80 static gboolean gst_avi_demux_handle_sink_event (GstPad * pad,
81     GstEvent * event);
82 static gboolean gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event);
83
84 #if 0
85 static const GstFormat *gst_avi_demux_get_src_formats (GstPad * pad);
86 #endif
87 static const GstQueryType *gst_avi_demux_get_src_query_types (GstPad * pad);
88 static gboolean gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query);
89 static gboolean gst_avi_demux_src_convert (GstPad * pad, GstFormat src_format,
90     gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
91
92 static gboolean gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment);
93 static gboolean gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad,
94     GstEvent * event);
95 static gboolean gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
96     GstEvent * event);
97 static void gst_avi_demux_loop (GstPad * pad);
98 static gboolean gst_avi_demux_sink_activate (GstPad * sinkpad);
99 static gboolean gst_avi_demux_sink_activate_pull (GstPad * sinkpad,
100     gboolean active);
101 static gboolean gst_avi_demux_activate_push (GstPad * pad, gboolean active);
102 static GstFlowReturn gst_avi_demux_chain (GstPad * pad, GstBuffer * buf);
103
104 static void gst_avi_demux_set_index (GstElement * element, GstIndex * index);
105 static GstIndex *gst_avi_demux_get_index (GstElement * element);
106 static GstStateChangeReturn gst_avi_demux_change_state (GstElement * element,
107     GstStateChange transition);
108 static void gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi);
109 static void gst_avi_demux_get_buffer_info (GstAviDemux * avi,
110     GstAviStream * stream, guint entry_n, GstClockTime * timestamp,
111     GstClockTime * ts_end, guint64 * offset, guint64 * offset_end);
112
113 static void gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf);
114
115 static GstElementClass *parent_class = NULL;
116
117 /* GObject methods */
118
119 GType
120 gst_avi_demux_get_type (void)
121 {
122   static GType avi_demux_type = 0;
123
124   if (!avi_demux_type) {
125     static const GTypeInfo avi_demux_info = {
126       sizeof (GstAviDemuxClass),
127       (GBaseInitFunc) gst_avi_demux_base_init,
128       NULL,
129       (GClassInitFunc) gst_avi_demux_class_init,
130       NULL,
131       NULL,
132       sizeof (GstAviDemux),
133       0,
134       (GInstanceInitFunc) gst_avi_demux_init,
135     };
136
137     avi_demux_type =
138         g_type_register_static (GST_TYPE_ELEMENT,
139         "GstAviDemux", &avi_demux_info, 0);
140   }
141
142   return avi_demux_type;
143 }
144
145 static void
146 gst_avi_demux_base_init (GstAviDemuxClass * klass)
147 {
148   static const GstElementDetails gst_avi_demux_details =
149       GST_ELEMENT_DETAILS ("Avi demuxer",
150       "Codec/Demuxer",
151       "Demultiplex an avi file into audio and video",
152       "Erik Walthinsen <omega@cse.ogi.edu>\n"
153       "Wim Taymans <wim.taymans@chello.be>\n"
154       "Thijs Vermeir <thijsvermeir@gmail.com>");
155   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
156   GstPadTemplate *videosrctempl, *audiosrctempl, *subsrctempl;
157   GstCaps *audcaps, *vidcaps, *subcaps;
158
159   audcaps = gst_riff_create_audio_template_caps ();
160   gst_caps_append (audcaps, gst_caps_new_simple ("audio/x-avi-unknown", NULL));
161   audiosrctempl = gst_pad_template_new ("audio_%02d",
162       GST_PAD_SRC, GST_PAD_SOMETIMES, audcaps);
163
164   vidcaps = gst_riff_create_video_template_caps ();
165   gst_caps_append (vidcaps, gst_riff_create_iavs_template_caps ());
166   gst_caps_append (vidcaps, gst_caps_new_simple ("video/x-avi-unknown", NULL));
167   videosrctempl = gst_pad_template_new ("video_%02d",
168       GST_PAD_SRC, GST_PAD_SOMETIMES, vidcaps);
169
170   subcaps = gst_caps_new_simple ("application/x-subtitle-avi", NULL);
171   subsrctempl = gst_pad_template_new ("subtitle_%02d",
172       GST_PAD_SRC, GST_PAD_SOMETIMES, subcaps);
173   gst_element_class_add_pad_template (element_class, audiosrctempl);
174   gst_element_class_add_pad_template (element_class, videosrctempl);
175   gst_element_class_add_pad_template (element_class, subsrctempl);
176   gst_element_class_add_pad_template (element_class,
177       gst_static_pad_template_get (&sink_templ));
178   gst_element_class_set_details (element_class, &gst_avi_demux_details);
179 }
180
181 static void
182 gst_avi_demux_class_init (GstAviDemuxClass * klass)
183 {
184   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
185   GObjectClass *gobject_class = (GObjectClass *) klass;
186
187   GST_DEBUG_CATEGORY_INIT (avidemux_debug, "avidemux",
188       0, "Demuxer for AVI streams");
189
190   parent_class = g_type_class_peek_parent (klass);
191
192   gobject_class->finalize = gst_avi_demux_finalize;
193   gstelement_class->change_state =
194       GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
195
196   gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_avi_demux_set_index);
197   gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_avi_demux_get_index);
198 }
199
200 static void
201 gst_avi_demux_init (GstAviDemux * avi)
202 {
203   avi->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
204   gst_pad_set_activate_function (avi->sinkpad,
205       GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate));
206   gst_pad_set_activatepull_function (avi->sinkpad,
207       GST_DEBUG_FUNCPTR (gst_avi_demux_sink_activate_pull));
208   gst_pad_set_activatepush_function (avi->sinkpad,
209       GST_DEBUG_FUNCPTR (gst_avi_demux_activate_push));
210   gst_pad_set_chain_function (avi->sinkpad,
211       GST_DEBUG_FUNCPTR (gst_avi_demux_chain));
212   gst_pad_set_event_function (avi->sinkpad,
213       GST_DEBUG_FUNCPTR (gst_avi_demux_handle_sink_event));
214   gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad);
215
216   avi->adapter = gst_adapter_new ();
217
218   gst_avi_demux_reset (avi);
219 }
220
221 static void
222 gst_avi_demux_finalize (GObject * object)
223 {
224   GstAviDemux *avi = GST_AVI_DEMUX (object);
225
226   GST_DEBUG ("AVI: finalize");
227
228   g_object_unref (avi->adapter);
229
230   G_OBJECT_CLASS (parent_class)->finalize (object);
231 }
232
233 static void
234 gst_avi_demux_reset_stream (GstAviDemux * avi, GstAviStream * stream)
235 {
236   g_free (stream->strh);
237   g_free (stream->strf.data);
238   g_free (stream->name);
239   g_free (stream->index);
240   g_free (stream->indexes);
241   if (stream->initdata)
242     gst_buffer_unref (stream->initdata);
243   if (stream->extradata)
244     gst_buffer_unref (stream->extradata);
245   if (stream->pad) {
246     if (stream->exposed) {
247       gst_pad_set_active (stream->pad, FALSE);
248       gst_element_remove_pad (GST_ELEMENT_CAST (avi), stream->pad);
249     } else
250       gst_object_unref (stream->pad);
251   }
252   if (stream->taglist) {
253     gst_tag_list_free (stream->taglist);
254     stream->taglist = NULL;
255   }
256   memset (stream, 0, sizeof (GstAviStream));
257 }
258
259 static void
260 gst_avi_demux_reset (GstAviDemux * avi)
261 {
262   gint i;
263
264   GST_DEBUG ("AVI: reset");
265
266   for (i = 0; i < avi->num_streams; i++)
267     gst_avi_demux_reset_stream (avi, &avi->stream[i]);
268
269   avi->header_state = GST_AVI_DEMUX_HEADER_TAG_LIST;
270   avi->num_streams = 0;
271   avi->num_v_streams = 0;
272   avi->num_a_streams = 0;
273   avi->num_t_streams = 0;
274   avi->main_stream = -1;
275
276   avi->state = GST_AVI_DEMUX_START;
277   avi->offset = 0;
278   avi->building_index = FALSE;
279
280   avi->index_offset = 0;
281   g_free (avi->avih);
282   avi->avih = NULL;
283
284   if (avi->element_index)
285     gst_object_unref (avi->element_index);
286   avi->element_index = NULL;
287
288   if (avi->seg_event) {
289     gst_event_unref (avi->seg_event);
290     avi->seg_event = NULL;
291   }
292   if (avi->seek_event) {
293     gst_event_unref (avi->seek_event);
294     avi->seek_event = NULL;
295   }
296
297   if (avi->globaltags)
298     gst_tag_list_free (avi->globaltags);
299   avi->globaltags = NULL;
300
301   avi->got_tags = TRUE;         /* we always want to push global tags */
302   avi->have_eos = FALSE;
303   avi->seekable = TRUE;
304
305   gst_adapter_clear (avi->adapter);
306
307   gst_segment_init (&avi->segment, GST_FORMAT_TIME);
308 }
309
310
311 /* GstElement methods */
312
313 #if 0
314 static const GstFormat *
315 gst_avi_demux_get_src_formats (GstPad * pad)
316 {
317   GstAviStream *stream = gst_pad_get_element_private (pad);
318
319   static const GstFormat src_a_formats[] = {
320     GST_FORMAT_TIME,
321     GST_FORMAT_BYTES,
322     GST_FORMAT_DEFAULT,
323     0
324   };
325   static const GstFormat src_v_formats[] = {
326     GST_FORMAT_TIME,
327     GST_FORMAT_DEFAULT,
328     0
329   };
330
331   return (stream->strh->type == GST_RIFF_FCC_auds ?
332       src_a_formats : src_v_formats);
333 }
334 #endif
335
336 /* assumes stream->strf.auds->av_bps != 0 */
337 static inline GstClockTime
338 avi_stream_convert_bytes_to_time_unchecked (GstAviStream * stream,
339     guint64 bytes)
340 {
341   return gst_util_uint64_scale_int (bytes, GST_SECOND,
342       stream->strf.auds->av_bps);
343 }
344
345 static inline guint64
346 avi_stream_convert_time_to_bytes_unchecked (GstAviStream * stream,
347     GstClockTime time)
348 {
349   return gst_util_uint64_scale_int (time, stream->strf.auds->av_bps,
350       GST_SECOND);
351 }
352
353 /* assumes stream->strh->rate != 0 */
354 static inline GstClockTime
355 avi_stream_convert_frames_to_time_unchecked (GstAviStream * stream,
356     guint64 frames)
357 {
358   return gst_util_uint64_scale (frames, stream->strh->scale * GST_SECOND,
359       stream->strh->rate);
360 }
361
362 static inline guint64
363 avi_stream_convert_time_to_frames_unchecked (GstAviStream * stream,
364     GstClockTime time)
365 {
366   return gst_util_uint64_scale (time, stream->strh->rate,
367       stream->strh->scale * GST_SECOND);
368 }
369
370 static gboolean
371 gst_avi_demux_src_convert (GstPad * pad,
372     GstFormat src_format,
373     gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
374 {
375   GstAviStream *stream = gst_pad_get_element_private (pad);
376   gboolean res = TRUE;
377
378   GST_LOG_OBJECT (pad,
379       "Received  src_format:%s, src_value:%" G_GUINT64_FORMAT
380       ", dest_format:%s", gst_format_get_name (src_format), src_value,
381       gst_format_get_name (*dest_format));
382
383   if (G_UNLIKELY (src_format == *dest_format)) {
384     *dest_value = src_value;
385     goto done;
386   }
387   if (G_UNLIKELY (!stream->strh || !stream->strf.data)) {
388     res = FALSE;
389     goto done;
390   }
391   if (G_UNLIKELY (stream->strh->type == GST_RIFF_FCC_vids &&
392           (src_format == GST_FORMAT_BYTES
393               || *dest_format == GST_FORMAT_BYTES))) {
394     res = FALSE;
395     goto done;
396   }
397
398   switch (src_format) {
399     case GST_FORMAT_TIME:
400       switch (*dest_format) {
401         case GST_FORMAT_BYTES:
402           *dest_value = gst_util_uint64_scale_int (src_value,
403               stream->strf.auds->av_bps, GST_SECOND);
404           break;
405         case GST_FORMAT_DEFAULT:
406           *dest_value =
407               gst_util_uint64_scale_round (src_value, stream->strh->rate,
408               stream->strh->scale * GST_SECOND);
409           break;
410         default:
411           res = FALSE;
412           break;
413       }
414       break;
415     case GST_FORMAT_BYTES:
416       switch (*dest_format) {
417         case GST_FORMAT_TIME:
418           if (stream->strf.auds->av_bps != 0) {
419             *dest_value = avi_stream_convert_bytes_to_time_unchecked (stream,
420                 src_value);
421           } else
422             res = FALSE;
423           break;
424         default:
425           res = FALSE;
426           break;
427       }
428       break;
429     case GST_FORMAT_DEFAULT:
430       switch (*dest_format) {
431         case GST_FORMAT_TIME:
432           *dest_value =
433               avi_stream_convert_frames_to_time_unchecked (stream, src_value);
434           break;
435         default:
436           res = FALSE;
437           break;
438       }
439       break;
440     default:
441       res = FALSE;
442   }
443
444 done:
445   GST_LOG_OBJECT (pad,
446       "Returning res:%d dest_format:%s dest_value:%" G_GUINT64_FORMAT, res,
447       gst_format_get_name (*dest_format), *dest_value);
448   return res;
449 }
450
451 static const GstQueryType *
452 gst_avi_demux_get_src_query_types (GstPad * pad)
453 {
454   static const GstQueryType src_types[] = {
455     GST_QUERY_POSITION,
456     GST_QUERY_DURATION,
457     GST_QUERY_SEEKING,
458     GST_QUERY_CONVERT,
459     0
460   };
461
462   return src_types;
463 }
464
465 static gboolean
466 gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query)
467 {
468   gboolean res = TRUE;
469   GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
470
471   GstAviStream *stream = gst_pad_get_element_private (pad);
472
473   if (!stream->strh || !stream->strf.data)
474     return gst_pad_query_default (pad, query);
475
476   switch (GST_QUERY_TYPE (query)) {
477     case GST_QUERY_POSITION:{
478       gint64 pos = 0;
479
480       GST_DEBUG ("pos query for stream %u: frames %u, bytes %u",
481           stream->num, stream->current_entry, stream->current_total);
482
483       /* FIXME, this looks clumsy */
484       if (stream->strh->type == GST_RIFF_FCC_auds) {
485         if (stream->is_vbr) {
486           /* VBR */
487           pos = gst_util_uint64_scale ((gint64) stream->current_entry *
488               stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
489           GST_DEBUG_OBJECT (avi, "VBR convert frame %u, time %"
490               GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
491         } else if (stream->strf.auds->av_bps != 0) {
492           /* CBR */
493           pos = gst_util_uint64_scale (stream->current_total, GST_SECOND,
494               (guint64) stream->strf.auds->av_bps);
495           GST_DEBUG_OBJECT (avi,
496               "CBR convert bytes %u, time %" GST_TIME_FORMAT,
497               stream->current_total, GST_TIME_ARGS (pos));
498         } else if (stream->idx_n != 0 && stream->total_bytes != 0) {
499           /* calculate timestamps based on percentage of length */
500           guint64 xlen = avi->avih->us_frame *
501               avi->avih->tot_frames * GST_USECOND;
502
503           if (stream->is_vbr) {
504             pos = gst_util_uint64_scale (xlen, stream->current_entry,
505                 stream->idx_n);
506             GST_DEBUG_OBJECT (avi, "VBR perc convert frame %u, time %"
507                 GST_TIME_FORMAT, stream->current_entry, GST_TIME_ARGS (pos));
508           } else {
509             pos = gst_util_uint64_scale (xlen, stream->current_total,
510                 stream->total_bytes);
511             GST_DEBUG_OBJECT (avi,
512                 "CBR perc convert bytes %u, time %" GST_TIME_FORMAT,
513                 stream->current_total, GST_TIME_ARGS (pos));
514           }
515         } else {
516           /* we don't know */
517           res = FALSE;
518         }
519       } else {
520         if (stream->strh->rate != 0) {
521           pos = gst_util_uint64_scale ((guint64) stream->current_entry *
522               stream->strh->scale, GST_SECOND, (guint64) stream->strh->rate);
523         } else {
524           pos = stream->current_entry * avi->avih->us_frame * GST_USECOND;
525         }
526       }
527       if (res) {
528         GST_DEBUG ("pos query : %" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
529         gst_query_set_position (query, GST_FORMAT_TIME, pos);
530       } else
531         GST_WARNING ("pos query failed");
532       break;
533     }
534     case GST_QUERY_DURATION:
535     {
536       GstFormat fmt;
537       GstClockTime duration;
538
539       /* only act on audio or video streams */
540       if (stream->strh->type != GST_RIFF_FCC_auds &&
541           stream->strh->type != GST_RIFF_FCC_vids) {
542         res = FALSE;
543         break;
544       }
545
546       /* take stream duration, fall back to avih duration */
547       if ((duration = stream->duration) == -1)
548         duration = avi->duration;
549
550       gst_query_parse_duration (query, &fmt, NULL);
551
552       switch (fmt) {
553         case GST_FORMAT_TIME:
554           gst_query_set_duration (query, fmt, duration);
555           break;
556         case GST_FORMAT_DEFAULT:
557         {
558           gint64 dur;
559           GST_DEBUG_OBJECT (query, "total frames is %" G_GUINT32_FORMAT,
560               stream->idx_n);
561
562           if (stream->idx_n >= 0)
563             gst_query_set_duration (query, fmt, stream->idx_n);
564           else if (gst_pad_query_convert (pad, GST_FORMAT_TIME,
565                   duration, &fmt, &dur))
566             gst_query_set_duration (query, fmt, dur);
567           break;
568         }
569         default:
570           res = FALSE;
571           break;
572       }
573       break;
574     }
575     case GST_QUERY_SEEKING:{
576       GstFormat fmt;
577
578       gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
579       if (fmt == GST_FORMAT_TIME) {
580         gboolean seekable = TRUE;
581
582         if (avi->streaming) {
583           seekable = avi->seekable;
584         }
585
586         gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
587             0, stream->duration);
588         res = TRUE;
589       }
590       break;
591     }
592     case GST_QUERY_CONVERT:{
593       GstFormat src_fmt, dest_fmt;
594       gint64 src_val, dest_val;
595
596       gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
597       if ((res = gst_avi_demux_src_convert (pad, src_fmt, src_val, &dest_fmt,
598                   &dest_val)))
599         gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
600       else
601         res = gst_pad_query_default (pad, query);
602       break;
603     }
604     default:
605       res = gst_pad_query_default (pad, query);
606       break;
607   }
608
609   gst_object_unref (avi);
610   return res;
611 }
612
613 #if 0
614 static const GstEventMask *
615 gst_avi_demux_get_event_mask (GstPad * pad)
616 {
617   static const GstEventMask masks[] = {
618     {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
619     {0,}
620   };
621
622   return masks;
623 }
624 #endif
625
626 static guint64
627 gst_avi_demux_seek_streams (GstAviDemux * avi, guint64 offset, gboolean before)
628 {
629   GstAviStream *stream;
630   GstIndexEntry *entry;
631   gint i;
632   gint64 val, min = offset;
633
634   for (i = 0; i < avi->num_streams; i++) {
635     stream = &avi->stream[i];
636
637     entry = gst_index_get_assoc_entry (avi->element_index, stream->index_id,
638         before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER,
639         GST_ASSOCIATION_FLAG_NONE, GST_FORMAT_BYTES, offset);
640
641     if (before) {
642       if (entry) {
643         GST_DEBUG_OBJECT (avi, "stream %d, previous entry at %"
644             G_GUINT64_FORMAT, i, val);
645         gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
646         if (val < min)
647           min = val;
648       }
649       continue;
650     }
651
652     if (!entry) {
653       GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
654       stream->current_entry = 0;
655       stream->current_total = 0;
656       continue;
657     }
658
659     gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &val);
660     GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT,
661         i, val);
662
663     gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &val);
664     stream->current_total = val;
665     gst_index_entry_assoc_map (entry, GST_FORMAT_DEFAULT, &val);
666     stream->current_entry = val;
667   }
668
669   return min;
670 }
671
672 static guint
673 gst_avi_demux_index_entry_offset_search (GstAviIndexEntry * entry,
674     guint64 * offset)
675 {
676   if (entry->offset < *offset)
677     return -1;
678   else if (entry->offset > *offset)
679     return 1;
680   return 0;
681 }
682
683 static guint64
684 gst_avi_demux_seek_streams_index (GstAviDemux * avi, guint64 offset,
685     gboolean before)
686 {
687   GstAviStream *stream;
688   GstAviIndexEntry *entry;
689   gint i;
690   gint64 val, min = offset;
691   guint index;
692
693   for (i = 0; i < avi->num_streams; i++) {
694     stream = &avi->stream[i];
695
696     entry =
697         gst_util_array_binary_search (stream->index, stream->idx_n,
698         sizeof (GstAviIndexEntry),
699         (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
700         before ? GST_SEARCH_MODE_BEFORE : GST_SEARCH_MODE_AFTER, &offset, NULL);
701
702     if (entry)
703       index = entry - stream->index;
704
705     if (before) {
706       if (entry) {
707         GST_DEBUG_OBJECT (avi,
708             "stream %d, previous entry at %" G_GUINT64_FORMAT, i, val);
709         val = stream->index[index].offset;
710         if (val < min)
711           min = val;
712       }
713       continue;
714     }
715
716     if (!entry) {
717       GST_DEBUG_OBJECT (avi, "no position for stream %d, assuming at start", i);
718       stream->current_entry = 0;
719       stream->current_total = 0;
720       continue;
721     }
722
723     val = stream->index[index].offset;
724     GST_DEBUG_OBJECT (avi, "stream %d, next entry at %" G_GUINT64_FORMAT, i,
725         val);
726
727     gst_avi_demux_get_buffer_info (avi, stream, index, (GstClockTime *) & val,
728         NULL, NULL, NULL);
729     stream->current_total = val;
730     stream->current_entry = index;
731   }
732
733   return min;
734 }
735
736 #define GST_AVI_SEEK_PUSH_DISPLACE     (4 * GST_SECOND)
737
738 static gboolean
739 gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event)
740 {
741   gboolean res = TRUE;
742   GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
743
744   GST_DEBUG_OBJECT (avi,
745       "have event type %s: %p on sink pad", GST_EVENT_TYPE_NAME (event), event);
746
747   switch (GST_EVENT_TYPE (event)) {
748     case GST_EVENT_NEWSEGMENT:
749     {
750       GstFormat format;
751       gdouble rate, arate;
752       gint64 start, stop, time, offset = 0;
753       gboolean update;
754       GstSegment segment;
755
756       /* some debug output */
757       gst_segment_init (&segment, GST_FORMAT_UNDEFINED);
758       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
759           &start, &stop, &time);
760       gst_segment_set_newsegment_full (&segment, update, rate, arate, format,
761           start, stop, time);
762       GST_DEBUG_OBJECT (avi,
763           "received format %d newsegment %" GST_SEGMENT_FORMAT, format,
764           &segment);
765
766       /* chain will send initial newsegment after pads have been added */
767       if (avi->state != GST_AVI_DEMUX_MOVI) {
768         GST_DEBUG_OBJECT (avi, "still starting, eating event");
769         goto exit;
770       }
771
772       /* we only expect a BYTE segment, e.g. following a seek */
773       if (format != GST_FORMAT_BYTES) {
774         GST_DEBUG_OBJECT (avi, "unsupported segment format, ignoring");
775         goto exit;
776       }
777
778       if (avi->have_index) {
779         GstAviIndexEntry *entry;
780         guint i = 0, index;
781         GstAviStream *stream;
782
783         /* find which stream we're on */
784         do {
785           stream = &avi->stream[i];
786
787           /* find the index for start bytes offset */
788           entry = gst_util_array_binary_search (stream->index,
789               stream->idx_n, sizeof (GstAviIndexEntry),
790               (GCompareDataFunc) gst_avi_demux_index_entry_offset_search,
791               GST_SEARCH_MODE_BEFORE, &start, NULL);
792
793           if (entry == NULL) {
794             index = 0;
795           } else {
796             index = entry - stream->index;
797           }
798
799           if (stream->index[index].offset == start)
800             break;
801         } while (++i < avi->num_streams);
802
803         /* get the ts corresponding to start offset bytes for the stream */
804         gst_avi_demux_get_buffer_info (avi, stream, index,
805             (GstClockTime *) & time, NULL, NULL, NULL);
806       } else if (avi->element_index) {
807         GstIndexEntry *entry;
808
809         /* Let's check if we have an index entry for this position */
810         entry = gst_index_get_assoc_entry (avi->element_index, avi->index_id,
811             GST_INDEX_LOOKUP_AFTER, GST_ASSOCIATION_FLAG_NONE,
812             GST_FORMAT_BYTES, start);
813
814         /* we can not go where we have not yet been before ... */
815         if (!entry) {
816           GST_WARNING_OBJECT (avi, "insufficient index data, forcing EOS");
817           goto eos;
818         }
819
820         gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time);
821         gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &start);
822       } else {
823         GST_WARNING_OBJECT (avi, "no index data, forcing EOS");
824         goto eos;
825       }
826
827       offset = start;
828       stop = GST_CLOCK_TIME_NONE;
829
830       /* set up segment and send downstream */
831       gst_segment_set_newsegment_full (&avi->segment, update, rate, arate,
832           GST_FORMAT_TIME, time, stop, time);
833       GST_DEBUG_OBJECT (avi, "Pushing newseg update %d, rate %g, "
834           "applied rate %g, format %d, start %" G_GINT64_FORMAT ", "
835           "stop %" G_GINT64_FORMAT, update, rate, arate, GST_FORMAT_TIME,
836           time, stop);
837       gst_avi_demux_push_event (avi,
838           gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
839               time, stop, time));
840
841       GST_DEBUG_OBJECT (avi, "next chunk expected at %" G_GINT64_FORMAT, start);
842
843       /* adjust state for streaming thread accordingly */
844       avi->offset = offset;
845       if (avi->have_index)
846         gst_avi_demux_seek_streams_index (avi, offset, FALSE);
847       else
848         gst_avi_demux_seek_streams (avi, offset, FALSE);
849
850       /* set up streaming thread */
851       avi->offset = offset;
852       avi->todrop = start - offset;
853
854     exit:
855       gst_event_unref (event);
856       res = TRUE;
857       break;
858     eos:
859       /* set up for EOS */
860       avi->have_eos = TRUE;
861       goto exit;
862     }
863     case GST_EVENT_EOS:
864     {
865       if (avi->state != GST_AVI_DEMUX_MOVI) {
866         gst_event_unref (event);
867         GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
868             (NULL), ("got eos and didn't receive a complete header object"));
869       } else if (!gst_avi_demux_push_event (avi, event)) {
870         GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
871             (NULL), ("got eos but no streams (yet)"));
872       }
873       break;
874     }
875     case GST_EVENT_FLUSH_STOP:
876     {
877       gint i;
878
879       gst_adapter_clear (avi->adapter);
880       avi->have_eos = FALSE;
881       for (i = 0; i < avi->num_streams; i++) {
882         avi->stream[i].last_flow = GST_FLOW_OK;
883         avi->stream[i].discont = TRUE;
884       }
885       /* fall through to default case so that the event gets passed downstream */
886     }
887     default:
888       res = gst_pad_event_default (pad, event);
889       break;
890   }
891
892   gst_object_unref (avi);
893
894   return res;
895 }
896
897 static gboolean
898 gst_avi_demux_handle_src_event (GstPad * pad, GstEvent * event)
899 {
900   gboolean res = TRUE;
901   GstAviDemux *avi = GST_AVI_DEMUX (gst_pad_get_parent (pad));
902
903   GST_DEBUG_OBJECT (avi,
904       "have event type %s: %p on src pad", GST_EVENT_TYPE_NAME (event), event);
905
906   switch (GST_EVENT_TYPE (event)) {
907     case GST_EVENT_SEEK:
908       if (!avi->streaming) {
909         res = gst_avi_demux_handle_seek (avi, pad, event);
910       } else {
911         res = gst_avi_demux_handle_seek_push (avi, pad, event);
912       }
913       gst_event_unref (event);
914       break;
915     case GST_EVENT_QOS:
916     case GST_EVENT_NAVIGATION:
917       res = FALSE;
918       gst_event_unref (event);
919       break;
920     default:
921       res = gst_pad_event_default (pad, event);
922       break;
923   }
924
925   gst_object_unref (avi);
926
927   return res;
928 }
929
930 /* streaming helper (push) */
931
932 /*
933  * gst_avi_demux_peek_chunk_info:
934  * @avi: Avi object
935  * @tag: holder for tag
936  * @size: holder for tag size
937  *
938  * Peek next chunk info (tag and size)
939  *
940  * Returns: TRUE when one chunk info has been got
941  */
942 static gboolean
943 gst_avi_demux_peek_chunk_info (GstAviDemux * avi, guint32 * tag, guint32 * size)
944 {
945   const guint8 *data = NULL;
946
947   if (gst_adapter_available (avi->adapter) < 8)
948     return FALSE;
949
950   data = gst_adapter_peek (avi->adapter, 8);
951   *tag = GST_READ_UINT32_LE (data);
952   *size = GST_READ_UINT32_LE (data + 4);
953
954   return TRUE;
955 }
956
957 /*
958  * gst_avi_demux_peek_chunk:
959  * @avi: Avi object
960  * @tag: holder for tag
961  * @size: holder for tag size
962  *
963  * Peek enough data for one full chunk
964  *
965  * Returns: %TRUE when one chunk has been got
966  */
967 static gboolean
968 gst_avi_demux_peek_chunk (GstAviDemux * avi, guint32 * tag, guint32 * size)
969 {
970   guint32 peek_size = 0;
971   gint available;
972
973   if (!gst_avi_demux_peek_chunk_info (avi, tag, size))
974     goto peek_failed;
975
976   /* size 0 -> empty data buffer would surprise most callers,
977    * large size -> do not bother trying to squeeze that into adapter,
978    * so we throw poor man's exception, which can be caught if caller really
979    * wants to handle 0 size chunk */
980   if (!(*size) || (*size) >= (1 << 30))
981     goto strange_size;
982
983   peek_size = (*size + 1) & ~1;
984   available = gst_adapter_available (avi->adapter);
985
986   GST_DEBUG_OBJECT (avi,
987       "Need to peek chunk of %d bytes to read chunk %" GST_FOURCC_FORMAT
988       ", %d bytes available", *size, GST_FOURCC_ARGS (*tag), available);
989
990   if (available < (8 + peek_size))
991     goto need_more;
992
993   return TRUE;
994
995   /* ERRORS */
996 peek_failed:
997   {
998     GST_INFO_OBJECT (avi, "Failed to peek");
999     return FALSE;
1000   }
1001 strange_size:
1002   {
1003     GST_INFO_OBJECT (avi,
1004         "Invalid/unexpected chunk size %d for tag %" GST_FOURCC_FORMAT, *size,
1005         GST_FOURCC_ARGS (*tag));
1006     /* chain should give up */
1007     avi->abort_buffering = TRUE;
1008     return FALSE;
1009   }
1010 need_more:
1011   {
1012     GST_INFO_OBJECT (avi, "need more %d < %" G_GUINT32_FORMAT,
1013         available, 8 + peek_size);
1014     return FALSE;
1015   }
1016 }
1017
1018 /* AVI init */
1019
1020 /*
1021  * gst_avi_demux_parse_file_header:
1022  * @element: caller element (used for errors/debug).
1023  * @buf: input data to be used for parsing.
1024  *
1025  * "Open" a RIFF/AVI file. The buffer should be at least 12
1026  * bytes long. Takes ownership of @buf.
1027  *
1028  * Returns: TRUE if the file is a RIFF/AVI file, FALSE otherwise.
1029  *          Throws an error, caller should error out (fatal).
1030  */
1031 static gboolean
1032 gst_avi_demux_parse_file_header (GstElement * element, GstBuffer * buf)
1033 {
1034   guint32 doctype;
1035   GstClockTime stamp;
1036
1037   stamp = gst_util_get_timestamp ();
1038
1039   /* riff_parse posts an error */
1040   if (!gst_riff_parse_file_header (element, buf, &doctype))
1041     return FALSE;
1042
1043   if (doctype != GST_RIFF_RIFF_AVI)
1044     goto not_avi;
1045
1046   stamp = gst_util_get_timestamp () - stamp;
1047   GST_DEBUG_OBJECT (element, "header parsing took %" GST_TIME_FORMAT,
1048       GST_TIME_ARGS (stamp));
1049
1050   return TRUE;
1051
1052   /* ERRORS */
1053 not_avi:
1054   {
1055     GST_ELEMENT_ERROR (element, STREAM, WRONG_TYPE, (NULL),
1056         ("File is not an AVI file: %" GST_FOURCC_FORMAT,
1057             GST_FOURCC_ARGS (doctype)));
1058     return FALSE;
1059   }
1060 }
1061
1062 /*
1063  * Read AVI file tag when streaming
1064  */
1065 static GstFlowReturn
1066 gst_avi_demux_stream_init_push (GstAviDemux * avi)
1067 {
1068   if (gst_adapter_available (avi->adapter) >= 12) {
1069     GstBuffer *tmp;
1070
1071     tmp = gst_adapter_take_buffer (avi->adapter, 12);
1072
1073     GST_DEBUG ("Parsing avi header");
1074     if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), tmp)) {
1075       return GST_FLOW_ERROR;
1076     }
1077     GST_DEBUG ("header ok");
1078     avi->offset += 12;
1079
1080     avi->state = GST_AVI_DEMUX_HEADER;
1081   }
1082   return GST_FLOW_OK;
1083 }
1084
1085 /*
1086  * Read AVI file tag
1087  */
1088 static GstFlowReturn
1089 gst_avi_demux_stream_init_pull (GstAviDemux * avi)
1090 {
1091   GstFlowReturn res;
1092   GstBuffer *buf = NULL;
1093
1094   res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
1095   if (res != GST_FLOW_OK)
1096     return res;
1097   else if (!gst_avi_demux_parse_file_header (GST_ELEMENT_CAST (avi), buf))
1098     goto wrong_header;
1099
1100   avi->offset += 12;
1101
1102   return GST_FLOW_OK;
1103
1104   /* ERRORS */
1105 wrong_header:
1106   {
1107     GST_DEBUG_OBJECT (avi, "error parsing file header");
1108     return GST_FLOW_ERROR;
1109   }
1110 }
1111
1112 /* AVI header handling */
1113 /*
1114  * gst_avi_demux_parse_avih:
1115  * @avi: caller element (used for errors/debug).
1116  * @buf: input data to be used for parsing.
1117  * @avih: pointer to structure (filled in by function) containing
1118  *        stream information (such as flags, number of streams, etc.).
1119  *
1120  * Read 'avih' header. Discards buffer after use.
1121  *
1122  * Returns: TRUE on success, FALSE otherwise. Throws an error if
1123  *          the header is invalid. The caller should error out
1124  *          (fatal).
1125  */
1126 static gboolean
1127 gst_avi_demux_parse_avih (GstAviDemux * avi,
1128     GstBuffer * buf, gst_riff_avih ** _avih)
1129 {
1130   gst_riff_avih *avih;
1131
1132   if (buf == NULL)
1133     goto no_buffer;
1134
1135   if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih))
1136     goto avih_too_small;
1137
1138   avih = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1139
1140 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1141   avih->us_frame = GUINT32_FROM_LE (avih->us_frame);
1142   avih->max_bps = GUINT32_FROM_LE (avih->max_bps);
1143   avih->pad_gran = GUINT32_FROM_LE (avih->pad_gran);
1144   avih->flags = GUINT32_FROM_LE (avih->flags);
1145   avih->tot_frames = GUINT32_FROM_LE (avih->tot_frames);
1146   avih->init_frames = GUINT32_FROM_LE (avih->init_frames);
1147   avih->streams = GUINT32_FROM_LE (avih->streams);
1148   avih->bufsize = GUINT32_FROM_LE (avih->bufsize);
1149   avih->width = GUINT32_FROM_LE (avih->width);
1150   avih->height = GUINT32_FROM_LE (avih->height);
1151   avih->scale = GUINT32_FROM_LE (avih->scale);
1152   avih->rate = GUINT32_FROM_LE (avih->rate);
1153   avih->start = GUINT32_FROM_LE (avih->start);
1154   avih->length = GUINT32_FROM_LE (avih->length);
1155 #endif
1156
1157   /* debug stuff */
1158   GST_INFO_OBJECT (avi, "avih tag found:");
1159   GST_INFO_OBJECT (avi, " us_frame    %u", avih->us_frame);
1160   GST_INFO_OBJECT (avi, " max_bps     %u", avih->max_bps);
1161   GST_INFO_OBJECT (avi, " pad_gran    %u", avih->pad_gran);
1162   GST_INFO_OBJECT (avi, " flags       0x%08x", avih->flags);
1163   GST_INFO_OBJECT (avi, " tot_frames  %u", avih->tot_frames);
1164   GST_INFO_OBJECT (avi, " init_frames %u", avih->init_frames);
1165   GST_INFO_OBJECT (avi, " streams     %u", avih->streams);
1166   GST_INFO_OBJECT (avi, " bufsize     %u", avih->bufsize);
1167   GST_INFO_OBJECT (avi, " width       %u", avih->width);
1168   GST_INFO_OBJECT (avi, " height      %u", avih->height);
1169   GST_INFO_OBJECT (avi, " scale       %u", avih->scale);
1170   GST_INFO_OBJECT (avi, " rate        %u", avih->rate);
1171   GST_INFO_OBJECT (avi, " start       %u", avih->start);
1172   GST_INFO_OBJECT (avi, " length      %u", avih->length);
1173
1174   *_avih = avih;
1175   gst_buffer_unref (buf);
1176
1177   if (avih->us_frame != 0 && avih->tot_frames != 0)
1178     avi->duration =
1179         (guint64) avih->us_frame * (guint64) avih->tot_frames * 1000;
1180   else
1181     avi->duration = GST_CLOCK_TIME_NONE;
1182
1183   GST_INFO_OBJECT (avi, " header duration  %" GST_TIME_FORMAT,
1184       GST_TIME_ARGS (avi->duration));
1185
1186   return TRUE;
1187
1188   /* ERRORS */
1189 no_buffer:
1190   {
1191     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No buffer"));
1192     return FALSE;
1193   }
1194 avih_too_small:
1195   {
1196     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
1197         ("Too small avih (%d available, %d needed)",
1198             GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
1199     gst_buffer_unref (buf);
1200     return FALSE;
1201   }
1202 }
1203
1204 /*
1205  * gst_avi_demux_parse_superindex:
1206  * @avi: caller element (used for debugging/errors).
1207  * @buf: input data to use for parsing.
1208  * @locations: locations in the file (byte-offsets) that contain
1209  *             the actual indexes (see get_avi_demux_parse_subindex()).
1210  *             The array ends with GST_BUFFER_OFFSET_NONE.
1211  *
1212  * Reads superindex (openDML-2 spec stuff) from the provided data.
1213  *
1214  * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
1215  *          on error, but they are not fatal.
1216  */
1217 static gboolean
1218 gst_avi_demux_parse_superindex (GstAviDemux * avi,
1219     GstBuffer * buf, guint64 ** _indexes)
1220 {
1221   guint8 *data;
1222   guint16 bpe = 16;
1223   guint32 num, i;
1224   guint64 *indexes;
1225   guint size;
1226
1227   *_indexes = NULL;
1228
1229   size = buf ? GST_BUFFER_SIZE (buf) : 0;
1230   if (size < 24)
1231     goto too_small;
1232
1233   data = GST_BUFFER_DATA (buf);
1234
1235   /* check type of index. The opendml2 specs state that
1236    * there should be 4 dwords per array entry. Type can be
1237    * either frame or field (and we don't care). */
1238   if (GST_READ_UINT16_LE (data) != 4 ||
1239       (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
1240     GST_WARNING_OBJECT (avi,
1241         "Superindex for stream has unexpected "
1242         "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1243         GST_READ_UINT16_LE (data), data[2], data[3]);
1244     bpe = GST_READ_UINT16_LE (data) * 4;
1245   }
1246   num = GST_READ_UINT32_LE (&data[4]);
1247
1248   GST_DEBUG_OBJECT (avi, "got %d indexes", num);
1249
1250   indexes = g_new (guint64, num + 1);
1251   for (i = 0; i < num; i++) {
1252     if (size < 24 + bpe * (i + 1))
1253       break;
1254     indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
1255     GST_DEBUG_OBJECT (avi, "index %d at %" G_GUINT64_FORMAT, i, indexes[i]);
1256   }
1257   indexes[i] = GST_BUFFER_OFFSET_NONE;
1258   *_indexes = indexes;
1259
1260   gst_buffer_unref (buf);
1261
1262   return TRUE;
1263
1264   /* ERRORS */
1265 too_small:
1266   {
1267     GST_ERROR_OBJECT (avi,
1268         "Not enough data to parse superindex (%d available, 24 needed)", size);
1269     if (buf)
1270       gst_buffer_unref (buf);
1271     return FALSE;
1272   }
1273 }
1274
1275 /* add an entry to the index of a stream. @num should be an estimate of the
1276  * total amount of index entries for all streams and is used to dynamically
1277  * allocate memory for the index entries. */
1278 static inline gboolean
1279 gst_avi_demux_add_index (GstAviDemux * avi, GstAviStream * stream,
1280     guint num, GstAviIndexEntry * entry)
1281 {
1282   /* ensure index memory */
1283   if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) {
1284     guint idx_max = stream->idx_max;
1285     GstAviIndexEntry *new_idx;
1286
1287     /* we need to make some more room */
1288     if (idx_max == 0) {
1289       /* initial size guess, assume each stream has an equal amount of entries,
1290        * overshoot with at least 8K */
1291       idx_max = (num / avi->num_streams) + (8192 / sizeof (GstAviIndexEntry));
1292     } else {
1293       idx_max += 8192 / sizeof (GstAviIndexEntry);
1294       GST_DEBUG_OBJECT (avi, "expanded index from %u to %u",
1295           stream->idx_max, idx_max);
1296     }
1297     new_idx = g_try_renew (GstAviIndexEntry, stream->index, idx_max);
1298     /* out of memory, if this fails stream->index is untouched. */
1299     if (G_UNLIKELY (!new_idx))
1300       return FALSE;
1301     /* use new index */
1302     stream->index = new_idx;
1303     stream->idx_max = idx_max;
1304   }
1305
1306   /* update entry total and stream stats. The entry total can be converted to
1307    * the timestamp of the entry easily. */
1308   if (stream->strh->type == GST_RIFF_FCC_auds) {
1309     gint blockalign;
1310
1311     if (stream->is_vbr) {
1312       entry->total = stream->total_blocks;
1313     } else {
1314       entry->total = stream->total_bytes;
1315     }
1316     blockalign = stream->strf.auds->blockalign;
1317     if (blockalign > 0)
1318       stream->total_blocks += DIV_ROUND_UP (entry->size, blockalign);
1319     else
1320       stream->total_blocks++;
1321   } else {
1322     if (stream->is_vbr) {
1323       entry->total = stream->idx_n;
1324     } else {
1325       entry->total = stream->total_bytes;
1326     }
1327   }
1328   stream->total_bytes += entry->size;
1329   if (ENTRY_IS_KEYFRAME (entry))
1330     stream->n_keyframes++;
1331
1332   /* and add */
1333   GST_LOG_OBJECT (avi,
1334       "Adding stream %u, index entry %d, kf %d, size %u "
1335       ", offset %" G_GUINT64_FORMAT ", total %" G_GUINT64_FORMAT, stream->num,
1336       stream->idx_n, ENTRY_IS_KEYFRAME (entry), entry->size, entry->offset,
1337       entry->total);
1338   stream->index[stream->idx_n++] = *entry;
1339
1340   return TRUE;
1341 }
1342
1343 /* given @entry_n in @stream, calculate info such as timestamps and
1344  * offsets for the entry. */
1345 static void
1346 gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
1347     guint entry_n, GstClockTime * timestamp, GstClockTime * ts_end,
1348     guint64 * offset, guint64 * offset_end)
1349 {
1350   GstAviIndexEntry *entry;
1351
1352   entry = &stream->index[entry_n];
1353
1354   if (stream->is_vbr) {
1355     /* VBR stream next timestamp */
1356     if (stream->strh->type == GST_RIFF_FCC_auds) {
1357       if (timestamp)
1358         *timestamp =
1359             avi_stream_convert_frames_to_time_unchecked (stream, entry->total);
1360       if (ts_end)
1361         *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1362             entry->total + 1);
1363     } else {
1364       if (timestamp)
1365         *timestamp =
1366             avi_stream_convert_frames_to_time_unchecked (stream, entry_n);
1367       if (ts_end)
1368         *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1369             entry_n + 1);
1370     }
1371   } else if (stream->strh->type == GST_RIFF_FCC_auds) {
1372     /* constant rate stream */
1373     if (timestamp)
1374       *timestamp =
1375           avi_stream_convert_bytes_to_time_unchecked (stream, entry->total);
1376     if (ts_end)
1377       *ts_end = avi_stream_convert_bytes_to_time_unchecked (stream,
1378           entry->total + entry->size);
1379   }
1380   if (stream->strh->type == GST_RIFF_FCC_vids) {
1381     /* video offsets are the frame number */
1382     if (offset)
1383       *offset = entry_n;
1384     if (offset_end)
1385       *offset_end = entry_n + 1;
1386   } else {
1387     /* no offsets for audio */
1388     if (offset)
1389       *offset = -1;
1390     if (offset_end)
1391       *offset_end = -1;
1392   }
1393 }
1394
1395 /* collect and debug stats about the indexes for all streams.
1396  * This method is also responsible for filling in the stream duration
1397  * as measured by the amount of index entries.
1398  *
1399  * Returns TRUE if the index is not empty, else FALSE */
1400 static gboolean
1401 gst_avi_demux_do_index_stats (GstAviDemux * avi)
1402 {
1403   guint total_idx = 0;
1404   guint i;
1405 #ifndef GST_DISABLE_GST_DEBUG
1406   guint total_max = 0;
1407 #endif
1408
1409   /* get stream stats now */
1410   for (i = 0; i < avi->num_streams; i++) {
1411     GstAviStream *stream;
1412
1413     if (G_UNLIKELY (!(stream = &avi->stream[i])))
1414       continue;
1415     if (G_UNLIKELY (!stream->strh))
1416       continue;
1417     if (G_UNLIKELY (!stream->index || stream->idx_n == 0))
1418       continue;
1419
1420     /* we interested in the end_ts of the last entry, which is the total
1421      * duration of this stream */
1422     gst_avi_demux_get_buffer_info (avi, stream, stream->idx_n - 1,
1423         NULL, &stream->idx_duration, NULL, NULL);
1424
1425     total_idx += stream->idx_n;
1426 #ifndef GST_DISABLE_GST_DEBUG
1427     total_max += stream->idx_max;
1428 #endif
1429     GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
1430         "%5u keyframes, entry size = %2u, total size = %10u, allocated %10u",
1431         i, GST_TIME_ARGS (stream->idx_duration), stream->idx_n,
1432         stream->n_keyframes, (guint) sizeof (GstAviIndexEntry),
1433         (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
1434         (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
1435   }
1436   total_idx *= sizeof (GstAviIndexEntry);
1437 #ifndef GST_DISABLE_GST_DEBUG
1438   total_max *= sizeof (GstAviIndexEntry);
1439 #endif
1440   GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
1441       total_max, total_idx, total_max - total_idx);
1442
1443   if (total_idx == 0) {
1444     GST_WARNING_OBJECT (avi, "Index is empty !");
1445     return FALSE;
1446   }
1447   return TRUE;
1448 }
1449
1450 /*
1451  * gst_avi_demux_parse_subindex:
1452  * @avi: Avi object
1453  * @buf: input data to use for parsing.
1454  * @stream: stream context.
1455  * @entries_list: a list (returned by the function) containing all the
1456  *           indexes parsed in this specific subindex. The first
1457  *           entry is also a pointer to allocated memory that needs
1458  *           to be free´ed. May be NULL if no supported indexes were
1459  *           found.
1460  *
1461  * Reads superindex (openDML-2 spec stuff) from the provided data.
1462  * The buffer should contain a GST_RIFF_TAG_ix?? chunk.
1463  *
1464  * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
1465  *          throw an error, caller should bail out asap.
1466  */
1467 static gboolean
1468 gst_avi_demux_parse_subindex (GstAviDemux * avi, GstAviStream * stream,
1469     GstBuffer * buf)
1470 {
1471   guint8 *data;
1472   guint16 bpe;
1473   guint32 num, i;
1474   guint64 baseoff;
1475   guint size;
1476
1477   if (!buf)
1478     return TRUE;
1479
1480   size = GST_BUFFER_SIZE (buf);
1481
1482   /* check size */
1483   if (size < 24)
1484     goto too_small;
1485
1486   data = GST_BUFFER_DATA (buf);
1487
1488   /* We don't support index-data yet */
1489   if (data[3] & 0x80)
1490     goto not_implemented;
1491
1492   /* check type of index. The opendml2 specs state that
1493    * there should be 4 dwords per array entry. Type can be
1494    * either frame or field (and we don't care). */
1495   bpe = (data[2] & 0x01) ? 12 : 8;
1496   if (GST_READ_UINT16_LE (data) != bpe / 4 ||
1497       (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
1498     GST_WARNING_OBJECT (avi,
1499         "Superindex for stream %d has unexpected "
1500         "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1501         stream->num, GST_READ_UINT16_LE (data), data[2], data[3]);
1502     bpe = GST_READ_UINT16_LE (data) * 4;
1503   }
1504   num = GST_READ_UINT32_LE (&data[4]);
1505   baseoff = GST_READ_UINT64_LE (&data[12]);
1506
1507   /* If there's nothing, just return ! */
1508   if (num == 0)
1509     goto empty_index;
1510
1511   GST_INFO_OBJECT (avi, "Parsing subindex, nr_entries = %6d", num);
1512
1513   for (i = 0; i < num; i++) {
1514     GstAviIndexEntry entry;
1515
1516     if (size < 24 + bpe * (i + 1))
1517       break;
1518
1519     /* fill in offset and size. offset contains the keyframe flag in the
1520      * upper bit*/
1521     entry.offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * i]);
1522     entry.size = GST_READ_UINT32_LE (&data[24 + bpe * i + 4]);
1523     /* handle flags */
1524     if (stream->strh->type == GST_RIFF_FCC_auds) {
1525       /* all audio frames are keyframes */
1526       ENTRY_SET_KEYFRAME (&entry);
1527     } else {
1528       /* else read flags */
1529       entry.flags = (entry.size & 0x80000000) ? 0 : GST_AVI_KEYFRAME;
1530     }
1531     entry.size &= ~0x80000000;
1532
1533     /* and add */
1534     if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
1535       goto out_of_mem;
1536   }
1537   gst_buffer_unref (buf);
1538
1539   return TRUE;
1540
1541   /* ERRORS */
1542 too_small:
1543   {
1544     GST_ERROR_OBJECT (avi,
1545         "Not enough data to parse subindex (%d available, 24 needed)", size);
1546     gst_buffer_unref (buf);
1547     return TRUE;                /* continue */
1548   }
1549 not_implemented:
1550   {
1551     GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
1552         ("Subindex-is-data is not implemented"));
1553     gst_buffer_unref (buf);
1554     return FALSE;
1555   }
1556 empty_index:
1557   {
1558     GST_DEBUG_OBJECT (avi, "the index is empty");
1559     gst_buffer_unref (buf);
1560     return TRUE;
1561   }
1562 out_of_mem:
1563   {
1564     GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
1565         ("Cannot allocate memory for %u*%u=%u bytes",
1566             (guint) sizeof (GstAviIndexEntry), num,
1567             (guint) sizeof (GstAviIndexEntry) * num));
1568     gst_buffer_unref (buf);
1569     return FALSE;
1570   }
1571 }
1572
1573 /*
1574  * Create and push a flushing seek event upstream
1575  */
1576 static gboolean
1577 perform_seek_to_offset (GstAviDemux * demux, guint64 offset)
1578 {
1579   GstEvent *event;
1580   gboolean res = 0;
1581
1582   GST_DEBUG_OBJECT (demux, "Seeking to %" G_GUINT64_FORMAT, offset);
1583
1584   event =
1585       gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1586       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, offset,
1587       GST_SEEK_TYPE_NONE, -1);
1588
1589   res = gst_pad_push_event (demux->sinkpad, event);
1590
1591   if (res)
1592     demux->offset = offset;
1593   return res;
1594 }
1595
1596 /*
1597  * Read AVI index when streaming
1598  */
1599 static gboolean
1600 gst_avi_demux_read_subindexes_push (GstAviDemux * avi)
1601 {
1602   guint32 tag = 0, size;
1603   GstBuffer *buf = NULL;
1604   guint odml_stream;
1605
1606   GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1607
1608   if (avi->odml_subidxs[avi->odml_subidx] != avi->offset)
1609     return FALSE;
1610
1611   if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
1612     return TRUE;
1613
1614   /* this is the ODML chunk we expect */
1615   odml_stream = avi->odml_stream;
1616
1617   if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + odml_stream / 10,
1618               '0' + odml_stream % 10)) &&
1619       (tag != GST_MAKE_FOURCC ('0' + odml_stream / 10,
1620               '0' + odml_stream % 10, 'i', 'x'))) {
1621     GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1622         GST_FOURCC_ARGS (tag));
1623     return FALSE;
1624   }
1625
1626   avi->offset += 8 + GST_ROUND_UP_2 (size);
1627   /* flush chunk header so we get just the 'size' payload data */
1628   gst_adapter_flush (avi->adapter, 8);
1629   buf = gst_adapter_take_buffer (avi->adapter, size);
1630
1631   if (!gst_avi_demux_parse_subindex (avi, &avi->stream[odml_stream], buf))
1632     return FALSE;
1633
1634   /* we parsed the index, go to next subindex */
1635   avi->odml_subidx++;
1636
1637   if (avi->odml_subidxs[avi->odml_subidx] == GST_BUFFER_OFFSET_NONE) {
1638     /* we reached the end of the indexes for this stream, move to the next
1639      * stream to handle the first index */
1640     avi->odml_stream++;
1641     avi->odml_subidx = 0;
1642
1643     if (avi->odml_stream < avi->num_streams) {
1644       /* there are more indexes */
1645       avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
1646     } else {
1647       /* we're done, get stream stats now */
1648       avi->have_index = gst_avi_demux_do_index_stats (avi);
1649
1650       return TRUE;
1651     }
1652   }
1653
1654   /* seek to next index */
1655   return perform_seek_to_offset (avi, avi->odml_subidxs[avi->odml_subidx]);
1656 }
1657
1658 /*
1659  * Read AVI index
1660  */
1661 static void
1662 gst_avi_demux_read_subindexes_pull (GstAviDemux * avi)
1663 {
1664   guint32 tag;
1665   GstBuffer *buf;
1666   gint i, n;
1667
1668   GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1669
1670   for (n = 0; n < avi->num_streams; n++) {
1671     GstAviStream *stream = &avi->stream[n];
1672
1673     if (stream->indexes == NULL)
1674       continue;
1675
1676     for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
1677       if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi), avi->sinkpad,
1678               &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
1679         continue;
1680       else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
1681                   '0' + stream->num % 10)) &&
1682           (tag != GST_MAKE_FOURCC ('0' + stream->num / 10,
1683                   '0' + stream->num % 10, 'i', 'x'))) {
1684         /* Some ODML files (created by god knows what muxer) have a ##ix format
1685          * instead of the 'official' ix##. They are still valid though. */
1686         GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1687             GST_FOURCC_ARGS (tag));
1688         gst_buffer_unref (buf);
1689         continue;
1690       }
1691
1692       if (!gst_avi_demux_parse_subindex (avi, stream, buf))
1693         continue;
1694     }
1695
1696     g_free (stream->indexes);
1697     stream->indexes = NULL;
1698   }
1699   /* get stream stats now */
1700   avi->have_index = gst_avi_demux_do_index_stats (avi);
1701 }
1702
1703 /*
1704  * gst_avi_demux_riff_parse_vprp:
1705  * @element: caller element (used for debugging/error).
1706  * @buf: input data to be used for parsing, stripped from header.
1707  * @vprp: a pointer (returned by this function) to a filled-in vprp
1708  *        structure. Caller should free it.
1709  *
1710  * Parses a video stream´s vprp. This function takes ownership of @buf.
1711  *
1712  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
1713  *          should be skipped on error, but it is not fatal.
1714  */
1715 static gboolean
1716 gst_avi_demux_riff_parse_vprp (GstElement * element,
1717     GstBuffer * buf, gst_riff_vprp ** _vprp)
1718 {
1719   gst_riff_vprp *vprp;
1720   gint k;
1721
1722   g_return_val_if_fail (buf != NULL, FALSE);
1723   g_return_val_if_fail (_vprp != NULL, FALSE);
1724
1725   if (GST_BUFFER_SIZE (buf) < G_STRUCT_OFFSET (gst_riff_vprp, field_info))
1726     goto too_small;
1727
1728   vprp = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1729
1730 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1731   vprp->format_token = GUINT32_FROM_LE (vprp->format_token);
1732   vprp->standard = GUINT32_FROM_LE (vprp->standard);
1733   vprp->vert_rate = GUINT32_FROM_LE (vprp->vert_rate);
1734   vprp->hor_t_total = GUINT32_FROM_LE (vprp->hor_t_total);
1735   vprp->vert_lines = GUINT32_FROM_LE (vprp->vert_lines);
1736   vprp->aspect = GUINT32_FROM_LE (vprp->aspect);
1737   vprp->width = GUINT32_FROM_LE (vprp->width);
1738   vprp->height = GUINT32_FROM_LE (vprp->height);
1739   vprp->fields = GUINT32_FROM_LE (vprp->fields);
1740 #endif
1741
1742   /* size checking */
1743   /* calculate fields based on size */
1744   k = (GST_BUFFER_SIZE (buf) - G_STRUCT_OFFSET (gst_riff_vprp, field_info)) /
1745       vprp->fields;
1746   if (vprp->fields > k) {
1747     GST_WARNING_OBJECT (element,
1748         "vprp header indicated %d fields, only %d available", vprp->fields, k);
1749     vprp->fields = k;
1750   }
1751   if (vprp->fields > GST_RIFF_VPRP_VIDEO_FIELDS) {
1752     GST_WARNING_OBJECT (element,
1753         "vprp header indicated %d fields, at most %d supported", vprp->fields,
1754         GST_RIFF_VPRP_VIDEO_FIELDS);
1755     vprp->fields = GST_RIFF_VPRP_VIDEO_FIELDS;
1756   }
1757 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1758   for (k = 0; k < vprp->fields; k++) {
1759     gst_riff_vprp_video_field_desc *fd;
1760
1761     fd = &vprp->field_info[k];
1762     fd->compressed_bm_height = GUINT32_FROM_LE (fd->compressed_bm_height);
1763     fd->compressed_bm_width = GUINT32_FROM_LE (fd->compressed_bm_width);
1764     fd->valid_bm_height = GUINT32_FROM_LE (fd->valid_bm_height);
1765     fd->valid_bm_width = GUINT16_FROM_LE (fd->valid_bm_width);
1766     fd->valid_bm_x_offset = GUINT16_FROM_LE (fd->valid_bm_x_offset);
1767     fd->valid_bm_y_offset = GUINT32_FROM_LE (fd->valid_bm_y_offset);
1768     fd->video_x_t_offset = GUINT32_FROM_LE (fd->video_x_t_offset);
1769     fd->video_y_start = GUINT32_FROM_LE (fd->video_y_start);
1770   }
1771 #endif
1772
1773   /* debug */
1774   GST_INFO_OBJECT (element, "vprp tag found in context vids:");
1775   GST_INFO_OBJECT (element, " format_token  %d", vprp->format_token);
1776   GST_INFO_OBJECT (element, " standard      %d", vprp->standard);
1777   GST_INFO_OBJECT (element, " vert_rate     %d", vprp->vert_rate);
1778   GST_INFO_OBJECT (element, " hor_t_total   %d", vprp->hor_t_total);
1779   GST_INFO_OBJECT (element, " vert_lines    %d", vprp->vert_lines);
1780   GST_INFO_OBJECT (element, " aspect        %d:%d", vprp->aspect >> 16,
1781       vprp->aspect & 0xffff);
1782   GST_INFO_OBJECT (element, " width         %d", vprp->width);
1783   GST_INFO_OBJECT (element, " height        %d", vprp->height);
1784   GST_INFO_OBJECT (element, " fields        %d", vprp->fields);
1785   for (k = 0; k < vprp->fields; k++) {
1786     gst_riff_vprp_video_field_desc *fd;
1787
1788     fd = &(vprp->field_info[k]);
1789     GST_INFO_OBJECT (element, " field %u description:", k);
1790     GST_INFO_OBJECT (element, "  compressed_bm_height  %d",
1791         fd->compressed_bm_height);
1792     GST_INFO_OBJECT (element, "  compressed_bm_width  %d",
1793         fd->compressed_bm_width);
1794     GST_INFO_OBJECT (element, "  valid_bm_height       %d",
1795         fd->valid_bm_height);
1796     GST_INFO_OBJECT (element, "  valid_bm_width        %d", fd->valid_bm_width);
1797     GST_INFO_OBJECT (element, "  valid_bm_x_offset     %d",
1798         fd->valid_bm_x_offset);
1799     GST_INFO_OBJECT (element, "  valid_bm_y_offset     %d",
1800         fd->valid_bm_y_offset);
1801     GST_INFO_OBJECT (element, "  video_x_t_offset      %d",
1802         fd->video_x_t_offset);
1803     GST_INFO_OBJECT (element, "  video_y_start         %d", fd->video_y_start);
1804   }
1805
1806   gst_buffer_unref (buf);
1807
1808   *_vprp = vprp;
1809
1810   return TRUE;
1811
1812   /* ERRORS */
1813 too_small:
1814   {
1815     GST_ERROR_OBJECT (element,
1816         "Too small vprp (%d available, at least %d needed)",
1817         GST_BUFFER_SIZE (buf),
1818         (int) G_STRUCT_OFFSET (gst_riff_vprp, field_info));
1819     gst_buffer_unref (buf);
1820     return FALSE;
1821   }
1822 }
1823
1824 static void
1825 gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force)
1826 {
1827   guint i;
1828
1829   GST_DEBUG_OBJECT (avi, "force : %d", force);
1830
1831   for (i = 0; i < avi->num_streams; i++) {
1832     GstAviStream *stream = &avi->stream[i];
1833
1834     if (force || stream->idx_n != 0) {
1835       GST_LOG_OBJECT (avi, "Added pad %s with caps %" GST_PTR_FORMAT,
1836           GST_PAD_NAME (stream->pad), GST_PAD_CAPS (stream->pad));
1837       gst_element_add_pad ((GstElement *) avi, stream->pad);
1838       stream->exposed = TRUE;
1839       if (avi->main_stream == -1)
1840         avi->main_stream = i;
1841     } else {
1842       GST_WARNING_OBJECT (avi, "Stream #%d doesn't have any entry, removing it",
1843           i);
1844       gst_avi_demux_reset_stream (avi, stream);
1845     }
1846   }
1847 }
1848
1849 /*
1850  * gst_avi_demux_parse_stream:
1851  * @avi: calling element (used for debugging/errors).
1852  * @buf: input buffer used to parse the stream.
1853  *
1854  * Parses all subchunks in a strl chunk (which defines a single
1855  * stream). Discards the buffer after use. This function will
1856  * increment the stream counter internally.
1857  *
1858  * Returns: whether the stream was identified successfully.
1859  *          Errors are not fatal. It does indicate the stream
1860  *          was skipped.
1861  */
1862 static gboolean
1863 gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
1864 {
1865   GstAviStream *stream;
1866   GstElementClass *klass;
1867   GstPadTemplate *templ;
1868   GstBuffer *sub = NULL;
1869   guint offset = 4;
1870   guint32 tag = 0;
1871   gchar *codec_name = NULL, *padname = NULL;
1872   const gchar *tag_name;
1873   GstCaps *caps = NULL;
1874   GstPad *pad;
1875   GstElement *element;
1876   gboolean got_strh = FALSE, got_strf = FALSE, got_vprp = FALSE;
1877   gst_riff_vprp *vprp = NULL;
1878
1879   element = GST_ELEMENT_CAST (avi);
1880
1881   GST_DEBUG_OBJECT (avi, "Parsing stream");
1882
1883   if (avi->num_streams >= GST_AVI_DEMUX_MAX_STREAMS) {
1884     GST_WARNING_OBJECT (avi,
1885         "maximum no of streams (%d) exceeded, ignoring stream",
1886         GST_AVI_DEMUX_MAX_STREAMS);
1887     gst_buffer_unref (buf);
1888     /* not a fatal error, let's say */
1889     return TRUE;
1890   }
1891
1892   stream = &avi->stream[avi->num_streams];
1893
1894   /* initial settings */
1895   stream->idx_duration = GST_CLOCK_TIME_NONE;
1896   stream->hdr_duration = GST_CLOCK_TIME_NONE;
1897   stream->duration = GST_CLOCK_TIME_NONE;
1898
1899   while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
1900     /* sub can be NULL if the chunk is empty */
1901     if (sub == NULL) {
1902       GST_DEBUG_OBJECT (avi, "ignoring empty chunk %" GST_FOURCC_FORMAT,
1903           GST_FOURCC_ARGS (tag));
1904       continue;
1905     }
1906     switch (tag) {
1907       case GST_RIFF_TAG_strh:
1908       {
1909         gst_riff_strh *strh;
1910
1911         if (got_strh) {
1912           GST_WARNING_OBJECT (avi, "Ignoring additional strh chunk");
1913           break;
1914         }
1915         if (!gst_riff_parse_strh (element, sub, &stream->strh)) {
1916           /* ownership given away */
1917           sub = NULL;
1918           GST_WARNING_OBJECT (avi, "Failed to parse strh chunk");
1919           goto fail;
1920         }
1921         sub = NULL;
1922         strh = stream->strh;
1923         /* sanity check; stream header frame rate matches global header
1924          * frame duration */
1925         if (stream->strh->type == GST_RIFF_FCC_vids) {
1926           GstClockTime s_dur;
1927           GstClockTime h_dur = avi->avih->us_frame * GST_USECOND;
1928
1929           s_dur = gst_util_uint64_scale (GST_SECOND, strh->scale, strh->rate);
1930           GST_DEBUG_OBJECT (avi, "verifying stream framerate %d/%d, "
1931               "frame duration = %d ms", strh->rate, strh->scale,
1932               (gint) (s_dur / GST_MSECOND));
1933           if (h_dur > (10 * GST_MSECOND) && (s_dur > 10 * h_dur)) {
1934             strh->rate = GST_SECOND / GST_USECOND;
1935             strh->scale = h_dur / GST_USECOND;
1936             GST_DEBUG_OBJECT (avi, "correcting stream framerate to %d/%d",
1937                 strh->rate, strh->scale);
1938           }
1939         }
1940         /* determine duration as indicated by header */
1941         stream->hdr_duration = gst_util_uint64_scale ((guint64) strh->length *
1942             strh->scale, GST_SECOND, (guint64) strh->rate);
1943         GST_INFO ("Stream duration according to header: %" GST_TIME_FORMAT,
1944             GST_TIME_ARGS (stream->hdr_duration));
1945         if (stream->hdr_duration == 0)
1946           stream->hdr_duration = GST_CLOCK_TIME_NONE;
1947
1948         got_strh = TRUE;
1949         break;
1950       }
1951       case GST_RIFF_TAG_strf:
1952       {
1953         gboolean res = FALSE;
1954
1955         if (got_strf) {
1956           GST_WARNING_OBJECT (avi, "Ignoring additional strf chunk");
1957           break;
1958         }
1959         if (!got_strh) {
1960           GST_ERROR_OBJECT (avi, "Found strf chunk before strh chunk");
1961           goto fail;
1962         }
1963         switch (stream->strh->type) {
1964           case GST_RIFF_FCC_vids:
1965             stream->is_vbr = TRUE;
1966             res = gst_riff_parse_strf_vids (element, sub,
1967                 &stream->strf.vids, &stream->extradata);
1968             sub = NULL;
1969             GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res);
1970             break;
1971           case GST_RIFF_FCC_auds:
1972             stream->is_vbr = (stream->strh->samplesize == 0)
1973                 && stream->strh->scale > 1;
1974             res =
1975                 gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
1976                 &stream->extradata);
1977             sub = NULL;
1978             GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
1979                 stream->is_vbr, res);
1980             break;
1981           case GST_RIFF_FCC_iavs:
1982             stream->is_vbr = TRUE;
1983             res = gst_riff_parse_strf_iavs (element, sub,
1984                 &stream->strf.iavs, &stream->extradata);
1985             sub = NULL;
1986             GST_DEBUG_OBJECT (element, "marking iavs as VBR, res %d", res);
1987             break;
1988           case GST_RIFF_FCC_txts:
1989             /* nothing to parse here */
1990             stream->is_vbr = (stream->strh->samplesize == 0)
1991                 && (stream->strh->scale > 1);
1992             res = TRUE;
1993             break;
1994           default:
1995             GST_ERROR_OBJECT (avi,
1996                 "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
1997                 GST_FOURCC_ARGS (stream->strh->type));
1998             break;
1999         }
2000         if (sub) {
2001           gst_buffer_unref (sub);
2002           sub = NULL;
2003         }
2004         if (!res)
2005           goto fail;
2006         got_strf = TRUE;
2007         break;
2008       }
2009       case GST_RIFF_TAG_vprp:
2010       {
2011         if (got_vprp) {
2012           GST_WARNING_OBJECT (avi, "Ignoring additional vprp chunk");
2013           break;
2014         }
2015         if (!got_strh) {
2016           GST_ERROR_OBJECT (avi, "Found vprp chunk before strh chunk");
2017           goto fail;
2018         }
2019         if (!got_strf) {
2020           GST_ERROR_OBJECT (avi, "Found vprp chunk before strf chunk");
2021           goto fail;
2022         }
2023
2024         if (!gst_avi_demux_riff_parse_vprp (element, sub, &vprp)) {
2025           GST_WARNING_OBJECT (avi, "Failed to parse vprp chunk");
2026           /* not considered fatal */
2027           g_free (vprp);
2028           vprp = NULL;
2029         } else
2030           got_vprp = TRUE;
2031         sub = NULL;
2032         break;
2033       }
2034       case GST_RIFF_TAG_strd:
2035         if (stream->initdata)
2036           gst_buffer_unref (stream->initdata);
2037         stream->initdata = sub;
2038         sub = NULL;
2039         break;
2040       case GST_RIFF_TAG_strn:
2041         g_free (stream->name);
2042         if (sub != NULL) {
2043           stream->name =
2044               g_strndup ((gchar *) GST_BUFFER_DATA (sub),
2045               (gsize) GST_BUFFER_SIZE (sub));
2046           gst_buffer_unref (sub);
2047           sub = NULL;
2048         } else {
2049           stream->name = g_strdup ("");
2050         }
2051         GST_DEBUG_OBJECT (avi, "stream name: %s", stream->name);
2052         break;
2053       default:
2054         if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
2055             tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
2056                 '0' + avi->num_streams % 10)) {
2057           g_free (stream->indexes);
2058           gst_avi_demux_parse_superindex (avi, sub, &stream->indexes);
2059           stream->superindex = TRUE;
2060           sub = NULL;
2061           break;
2062         }
2063         GST_WARNING_OBJECT (avi,
2064             "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
2065             GST_FOURCC_ARGS (tag));
2066         /* fall-through */
2067       case GST_RIFF_TAG_JUNQ:
2068       case GST_RIFF_TAG_JUNK:
2069         break;
2070     }
2071     if (sub != NULL) {
2072       gst_buffer_unref (sub);
2073       sub = NULL;
2074     }
2075   }
2076
2077   if (!got_strh) {
2078     GST_WARNING_OBJECT (avi, "Failed to find strh chunk");
2079     goto fail;
2080   }
2081
2082   if (!got_strf) {
2083     GST_WARNING_OBJECT (avi, "Failed to find strf chunk");
2084     goto fail;
2085   }
2086
2087   /* get class to figure out the template */
2088   klass = GST_ELEMENT_GET_CLASS (avi);
2089
2090   /* we now have all info, let´s set up a pad and a caps and be done */
2091   /* create stream name + pad */
2092   switch (stream->strh->type) {
2093     case GST_RIFF_FCC_vids:{
2094       guint32 fourcc;
2095
2096       fourcc = (stream->strf.vids->compression) ?
2097           stream->strf.vids->compression : stream->strh->fcc_handler;
2098       padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
2099       templ = gst_element_class_get_pad_template (klass, "video_%02d");
2100       caps = gst_riff_create_video_caps (fourcc, stream->strh,
2101           stream->strf.vids, stream->extradata, stream->initdata, &codec_name);
2102       if (!caps) {
2103         caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
2104             GST_TYPE_FOURCC, fourcc, NULL);
2105       } else if (got_vprp && vprp) {
2106         guint32 aspect_n, aspect_d;
2107         gint n, d;
2108
2109         aspect_n = vprp->aspect >> 16;
2110         aspect_d = vprp->aspect & 0xffff;
2111         /* calculate the pixel aspect ratio using w/h and aspect ratio */
2112         n = aspect_n * stream->strf.vids->height;
2113         d = aspect_d * stream->strf.vids->width;
2114         if (n && d)
2115           gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
2116               n, d, NULL);
2117         /* very local, not needed elsewhere */
2118         g_free (vprp);
2119         vprp = NULL;
2120       }
2121       tag_name = GST_TAG_VIDEO_CODEC;
2122       avi->num_v_streams++;
2123       break;
2124     }
2125     case GST_RIFF_FCC_auds:{
2126       padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
2127       templ = gst_element_class_get_pad_template (klass, "audio_%02d");
2128       caps = gst_riff_create_audio_caps (stream->strf.auds->format,
2129           stream->strh, stream->strf.auds, stream->extradata,
2130           stream->initdata, &codec_name);
2131       if (!caps) {
2132         caps = gst_caps_new_simple ("audio/x-avi-unknown", "codec_id",
2133             G_TYPE_INT, stream->strf.auds->format, NULL);
2134       }
2135       tag_name = GST_TAG_AUDIO_CODEC;
2136       avi->num_a_streams++;
2137       break;
2138     }
2139     case GST_RIFF_FCC_iavs:{
2140       guint32 fourcc = stream->strh->fcc_handler;
2141
2142       padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
2143       templ = gst_element_class_get_pad_template (klass, "video_%02d");
2144       caps = gst_riff_create_iavs_caps (fourcc, stream->strh,
2145           stream->strf.iavs, stream->extradata, stream->initdata, &codec_name);
2146       if (!caps) {
2147         caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
2148             GST_TYPE_FOURCC, fourcc, NULL);
2149       }
2150       tag_name = GST_TAG_VIDEO_CODEC;
2151       avi->num_v_streams++;
2152       break;
2153     }
2154     case GST_RIFF_FCC_txts:{
2155       padname = g_strdup_printf ("subtitle_%02d", avi->num_t_streams);
2156       templ = gst_element_class_get_pad_template (klass, "subtitle_%02d");
2157       caps = gst_caps_new_simple ("application/x-subtitle-avi", NULL);
2158       tag_name = NULL;
2159       avi->num_t_streams++;
2160       break;
2161     }
2162     default:
2163       g_assert_not_reached ();
2164   }
2165
2166   /* no caps means no stream */
2167   if (!caps) {
2168     GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname);
2169     goto fail;
2170   }
2171
2172   GST_DEBUG_OBJECT (element, "codec-name=%s",
2173       (codec_name ? codec_name : "NULL"));
2174   GST_DEBUG_OBJECT (element, "caps=%" GST_PTR_FORMAT, caps);
2175
2176   /* set proper settings and add it */
2177   if (stream->pad)
2178     gst_object_unref (stream->pad);
2179   pad = stream->pad = gst_pad_new_from_template (templ, padname);
2180   g_free (padname);
2181
2182   gst_pad_use_fixed_caps (pad);
2183 #if 0
2184   gst_pad_set_formats_function (pad,
2185       GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_formats));
2186   gst_pad_set_event_mask_function (pad,
2187       GST_DEBUG_FUNCPTR (gst_avi_demux_get_event_mask));
2188 #endif
2189   gst_pad_set_event_function (pad,
2190       GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_event));
2191   gst_pad_set_query_type_function (pad,
2192       GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_query_types));
2193   gst_pad_set_query_function (pad,
2194       GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_query));
2195 #if 0
2196   gst_pad_set_convert_function (pad,
2197       GST_DEBUG_FUNCPTR (gst_avi_demux_src_convert));
2198 #endif
2199
2200   if (avi->element_index)
2201     gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (stream->pad),
2202         &stream->index_id);
2203
2204   stream->num = avi->num_streams;
2205
2206   stream->start_entry = 0;
2207   stream->step_entry = 0;
2208   stream->stop_entry = 0;
2209
2210   stream->current_entry = -1;
2211   stream->current_total = 0;
2212
2213   stream->last_flow = GST_FLOW_OK;
2214   stream->discont = TRUE;
2215
2216   stream->total_bytes = 0;
2217   stream->total_blocks = 0;
2218   stream->n_keyframes = 0;
2219
2220   stream->idx_n = 0;
2221   stream->idx_max = 0;
2222
2223   gst_pad_set_element_private (pad, stream);
2224   avi->num_streams++;
2225
2226   gst_pad_set_caps (pad, caps);
2227   gst_pad_set_active (pad, TRUE);
2228   gst_caps_unref (caps);
2229
2230   /* make tags */
2231   if (codec_name) {
2232     if (!stream->taglist)
2233       stream->taglist = gst_tag_list_new ();
2234
2235     avi->got_tags = TRUE;
2236
2237     gst_tag_list_add (stream->taglist, GST_TAG_MERGE_APPEND, tag_name,
2238         codec_name, NULL);
2239     g_free (codec_name);
2240   }
2241
2242   gst_buffer_unref (buf);
2243
2244   return TRUE;
2245
2246   /* ERRORS */
2247 fail:
2248   {
2249     /* unref any mem that may be in use */
2250     if (buf)
2251       gst_buffer_unref (buf);
2252     if (sub)
2253       gst_buffer_unref (sub);
2254     g_free (vprp);
2255     g_free (codec_name);
2256     gst_avi_demux_reset_stream (avi, stream);
2257     avi->num_streams++;
2258     return FALSE;
2259   }
2260 }
2261
2262 /*
2263  * gst_avi_demux_parse_odml:
2264  * @avi: calling element (used for debug/error).
2265  * @buf: input buffer to be used for parsing.
2266  *
2267  * Read an openDML-2.0 extension header. Fills in the frame number
2268  * in the avi demuxer object when reading succeeds.
2269  */
2270 static void
2271 gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
2272 {
2273   guint32 tag = 0;
2274   guint offset = 4;
2275   GstBuffer *sub = NULL;
2276
2277   while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
2278           &sub)) {
2279     switch (tag) {
2280       case GST_RIFF_TAG_dmlh:{
2281         gst_riff_dmlh dmlh, *_dmlh;
2282         guint size;
2283
2284         /* sub == NULL is possible and means an empty buffer */
2285         size = sub ? GST_BUFFER_SIZE (sub) : 0;
2286
2287         /* check size */
2288         if (size < sizeof (gst_riff_dmlh)) {
2289           GST_ERROR_OBJECT (avi,
2290               "DMLH entry is too small (%d bytes, %d needed)",
2291               size, (int) sizeof (gst_riff_dmlh));
2292           goto next;
2293         }
2294         _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (sub);
2295         dmlh.totalframes = GST_READ_UINT32_LE (&_dmlh->totalframes);
2296
2297         GST_INFO_OBJECT (avi, "dmlh tag found: totalframes: %u",
2298             dmlh.totalframes);
2299
2300         avi->avih->tot_frames = dmlh.totalframes;
2301         goto next;
2302       }
2303
2304       default:
2305         GST_WARNING_OBJECT (avi,
2306             "Unknown tag %" GST_FOURCC_FORMAT " in ODML header",
2307             GST_FOURCC_ARGS (tag));
2308         /* fall-through */
2309       case GST_RIFF_TAG_JUNQ:
2310       case GST_RIFF_TAG_JUNK:
2311       next:
2312         /* skip and move to next chunk */
2313         if (sub) {
2314           gst_buffer_unref (sub);
2315           sub = NULL;
2316         }
2317         break;
2318     }
2319   }
2320   if (buf)
2321     gst_buffer_unref (buf);
2322 }
2323
2324 /* Index helper */
2325 static guint
2326 gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream)
2327 {
2328   return stream->idx_n - 1;
2329 }
2330
2331 /* find a previous entry in the index with the given flags */
2332 static guint
2333 gst_avi_demux_index_prev (GstAviDemux * avi, GstAviStream * stream,
2334     guint last, gboolean keyframe)
2335 {
2336   GstAviIndexEntry *entry;
2337   guint i;
2338
2339   for (i = last; i > 0; i--) {
2340     entry = &stream->index[i - 1];
2341     if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2342       return i - 1;
2343     }
2344   }
2345   return 0;
2346 }
2347
2348 static guint
2349 gst_avi_demux_index_next (GstAviDemux * avi, GstAviStream * stream,
2350     guint last, gboolean keyframe)
2351 {
2352   GstAviIndexEntry *entry;
2353   gint i;
2354
2355   for (i = last + 1; i < stream->idx_n; i++) {
2356     entry = &stream->index[i];
2357     if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2358       return i;
2359     }
2360   }
2361   return stream->idx_n - 1;
2362 }
2363
2364 static guint
2365 gst_avi_demux_index_entry_search (GstAviIndexEntry * entry, guint64 * total)
2366 {
2367   if (entry->total < *total)
2368     return -1;
2369   else if (entry->total > *total)
2370     return 1;
2371   return 0;
2372 }
2373
2374 /*
2375  * gst_avi_demux_index_for_time:
2376  * @avi: Avi object
2377  * @stream: the stream
2378  * @time: a time position
2379  *
2380  * Finds the index entry which time is less or equal than the requested time.
2381  * Try to avoid binary search when we can convert the time to an index
2382  * position directly (for example for video frames with a fixed duration).
2383  *
2384  * Returns: the found position in the index.
2385  */
2386 static guint
2387 gst_avi_demux_index_for_time (GstAviDemux * avi,
2388     GstAviStream * stream, guint64 time)
2389 {
2390   guint index = -1;
2391   guint64 total;
2392
2393   GST_LOG_OBJECT (avi, "search time:%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
2394
2395   /* easy (and common) cases */
2396   if (time == 0 || stream->idx_n == 0)
2397     return 0;
2398   if (time >= stream->idx_duration)
2399     return stream->idx_n - 1;
2400
2401   /* figure out where we need to go. For that we convert the time to an
2402    * index entry or we convert it to a total and then do a binary search. */
2403   if (stream->is_vbr) {
2404     /* VBR stream next timestamp */
2405     if (stream->strh->type == GST_RIFF_FCC_auds) {
2406       total = avi_stream_convert_time_to_frames_unchecked (stream, time);
2407     } else {
2408       index = avi_stream_convert_time_to_frames_unchecked (stream, time);
2409     }
2410   } else {
2411     /* constant rate stream */
2412     total = avi_stream_convert_time_to_bytes_unchecked (stream, time);
2413   }
2414
2415   if (index == -1) {
2416     GstAviIndexEntry *entry;
2417
2418     /* no index, find index with binary search on total */
2419     GST_LOG_OBJECT (avi, "binary search for entry with total %"
2420         G_GUINT64_FORMAT, total);
2421
2422     entry = gst_util_array_binary_search (stream->index,
2423         stream->idx_n, sizeof (GstAviIndexEntry),
2424         (GCompareDataFunc) gst_avi_demux_index_entry_search,
2425         GST_SEARCH_MODE_BEFORE, &total, NULL);
2426
2427     if (entry == NULL) {
2428       GST_LOG_OBJECT (avi, "not found, assume index 0");
2429       index = 0;
2430     } else {
2431       index = entry - stream->index;
2432       GST_LOG_OBJECT (avi, "found at %u", index);
2433     }
2434   } else {
2435     GST_LOG_OBJECT (avi, "converted time to index %u", index);
2436   }
2437
2438   return index;
2439 }
2440
2441 static inline GstAviStream *
2442 gst_avi_demux_stream_for_id (GstAviDemux * avi, guint32 id)
2443 {
2444   guint stream_nr;
2445   GstAviStream *stream;
2446
2447   /* get the stream for this entry */
2448   stream_nr = CHUNKID_TO_STREAMNR (id);
2449   if (G_UNLIKELY (stream_nr >= avi->num_streams)) {
2450     GST_WARNING_OBJECT (avi, "invalid stream nr %d", stream_nr);
2451     return NULL;
2452   }
2453   stream = &avi->stream[stream_nr];
2454   if (G_UNLIKELY (!stream->strh)) {
2455     GST_WARNING_OBJECT (avi, "Unhandled stream %d, skipping", stream_nr);
2456     return NULL;
2457   }
2458   return stream;
2459 }
2460
2461 /*
2462  * gst_avi_demux_parse_index:
2463  * @avi: calling element (used for debugging/errors).
2464  * @buf: buffer containing the full index.
2465  *
2466  * Read index entries from the provided buffer.
2467  * The buffer should contain a GST_RIFF_TAG_idx1 chunk.
2468  */
2469 static gboolean
2470 gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2471 {
2472   guint64 pos_before;
2473   guint8 *data;
2474   guint size;
2475   guint i, num, n;
2476   gst_riff_index_entry *index;
2477   GstClockTime stamp;
2478   GstAviStream *stream;
2479   GstAviIndexEntry entry;
2480   guint32 id;
2481
2482   if (!buf)
2483     return FALSE;
2484
2485   data = GST_BUFFER_DATA (buf);
2486   size = GST_BUFFER_SIZE (buf);
2487
2488   stamp = gst_util_get_timestamp ();
2489
2490   /* see how many items in the index */
2491   num = size / sizeof (gst_riff_index_entry);
2492   if (num == 0)
2493     goto empty_list;
2494
2495   GST_INFO_OBJECT (avi, "Parsing index, nr_entries = %6d", num);
2496
2497   index = (gst_riff_index_entry *) data;
2498   pos_before = avi->offset;
2499
2500   /* figure out if the index is 0 based or relative to the MOVI start */
2501   entry.offset = GST_READ_UINT32_LE (&index[0].offset);
2502   if (entry.offset < avi->offset) {
2503     avi->index_offset = avi->offset + 8;
2504     GST_DEBUG ("index_offset = %" G_GUINT64_FORMAT, avi->index_offset);
2505   } else {
2506     avi->index_offset = 0;
2507     GST_DEBUG ("index is 0 based");
2508   }
2509
2510   for (i = 0, n = 0; i < num; i++) {
2511     id = GST_READ_UINT32_LE (&index[i].id);
2512     entry.offset = GST_READ_UINT32_LE (&index[i].offset);
2513
2514     /* some sanity checks */
2515     if (G_UNLIKELY (id == GST_RIFF_rec || id == 0 ||
2516             (entry.offset == 0 && n > 0)))
2517       continue;
2518
2519     /* get the stream for this entry */
2520     stream = gst_avi_demux_stream_for_id (avi, id);
2521     if (G_UNLIKELY (!stream))
2522       continue;
2523
2524     /* handle offset and size */
2525     entry.offset += avi->index_offset + 8;
2526     entry.size = GST_READ_UINT32_LE (&index[i].size);
2527
2528     /* handle flags */
2529     if (stream->strh->type == GST_RIFF_FCC_auds) {
2530       /* all audio frames are keyframes */
2531       ENTRY_SET_KEYFRAME (&entry);
2532     } else {
2533       guint32 flags;
2534       /* else read flags */
2535       flags = GST_READ_UINT32_LE (&index[i].flags);
2536       if (flags & GST_RIFF_IF_KEYFRAME) {
2537         ENTRY_SET_KEYFRAME (&entry);
2538       } else {
2539         ENTRY_UNSET_KEYFRAME (&entry);
2540       }
2541     }
2542
2543     /* and add */
2544     if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2545       goto out_of_mem;
2546
2547     n++;
2548   }
2549   gst_buffer_unref (buf);
2550
2551   /* get stream stats now */
2552   avi->have_index = gst_avi_demux_do_index_stats (avi);
2553
2554   stamp = gst_util_get_timestamp () - stamp;
2555   GST_DEBUG_OBJECT (avi, "index parsing took %" GST_TIME_FORMAT,
2556       GST_TIME_ARGS (stamp));
2557
2558   return TRUE;
2559
2560   /* ERRORS */
2561 empty_list:
2562   {
2563     GST_DEBUG_OBJECT (avi, "empty index");
2564     gst_buffer_unref (buf);
2565     return FALSE;
2566   }
2567 out_of_mem:
2568   {
2569     GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2570         ("Cannot allocate memory for %u*%u=%u bytes",
2571             (guint) sizeof (GstAviIndexEntry), num,
2572             (guint) sizeof (GstAviIndexEntry) * num));
2573     gst_buffer_unref (buf);
2574     return FALSE;
2575   }
2576 }
2577
2578 /*
2579  * gst_avi_demux_stream_index:
2580  * @avi: avi demuxer object.
2581  *
2582  * Seeks to index and reads it.
2583  */
2584 static void
2585 gst_avi_demux_stream_index (GstAviDemux * avi)
2586 {
2587   GstFlowReturn res;
2588   guint64 offset = avi->offset;
2589   GstBuffer *buf;
2590   guint32 tag;
2591   guint32 size;
2592
2593   GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
2594
2595   /* get chunk information */
2596   res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2597   if (res != GST_FLOW_OK)
2598     goto pull_failed;
2599   else if (GST_BUFFER_SIZE (buf) < 8)
2600     goto too_small;
2601
2602   /* check tag first before blindy trying to read 'size' bytes */
2603   tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
2604   size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
2605   if (tag == GST_RIFF_TAG_LIST) {
2606     /* this is the movi tag */
2607     GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
2608         (8 + GST_ROUND_UP_2 (size)));
2609     offset += 8 + GST_ROUND_UP_2 (size);
2610     gst_buffer_unref (buf);
2611     res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2612     if (res != GST_FLOW_OK)
2613       goto pull_failed;
2614     else if (GST_BUFFER_SIZE (buf) < 8)
2615       goto too_small;
2616     tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
2617     size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
2618   }
2619
2620   if (tag != GST_RIFF_TAG_idx1)
2621     goto no_index;
2622   if (!size)
2623     goto zero_index;
2624
2625   gst_buffer_unref (buf);
2626
2627   GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
2628
2629   /* read chunk, advance offset */
2630   if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi),
2631           avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK)
2632     return;
2633
2634   GST_DEBUG ("will parse index chunk size %u for tag %"
2635       GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
2636
2637   gst_avi_demux_parse_index (avi, buf);
2638
2639 #ifndef GST_DISABLE_GST_DEBUG
2640   /* debug our indexes */
2641   {
2642     gint i;
2643     GstAviStream *stream;
2644
2645     for (i = 0; i < avi->num_streams; i++) {
2646       stream = &avi->stream[i];
2647       GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
2648           i, stream->idx_n, stream->total_bytes);
2649     }
2650   }
2651 #endif
2652   return;
2653
2654   /* ERRORS */
2655 pull_failed:
2656   {
2657     GST_DEBUG_OBJECT (avi,
2658         "pull range failed: pos=%" G_GUINT64_FORMAT " size=8", offset);
2659     return;
2660   }
2661 too_small:
2662   {
2663     GST_DEBUG_OBJECT (avi, "Buffer is too small");
2664     gst_buffer_unref (buf);
2665     return;
2666   }
2667 no_index:
2668   {
2669     GST_WARNING_OBJECT (avi,
2670         "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
2671         GST_FOURCC_ARGS (tag));
2672     gst_buffer_unref (buf);
2673     return;
2674   }
2675 zero_index:
2676   {
2677     GST_WARNING_OBJECT (avi, "Empty index data (idx1) after movi chunk");
2678     gst_buffer_unref (buf);
2679     return;
2680   }
2681 }
2682
2683 /*
2684  * gst_avi_demux_stream_index_push:
2685  * @avi: avi demuxer object.
2686  *
2687  * Read index.
2688  */
2689 static void
2690 gst_avi_demux_stream_index_push (GstAviDemux * avi)
2691 {
2692   guint64 offset = avi->idx1_offset;
2693   GstBuffer *buf;
2694   guint32 tag;
2695   guint32 size;
2696
2697   GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
2698
2699   /* get chunk information */
2700   if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
2701     return;
2702
2703   /* check tag first before blindly trying to read 'size' bytes */
2704   if (tag == GST_RIFF_TAG_LIST) {
2705     /* this is the movi tag */
2706     GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
2707         (8 + GST_ROUND_UP_2 (size)));
2708     avi->idx1_offset = offset + 8 + GST_ROUND_UP_2 (size);
2709     /* issue seek to allow chain function to handle it and return! */
2710     perform_seek_to_offset (avi, avi->idx1_offset);
2711     return;
2712   }
2713
2714   if (tag != GST_RIFF_TAG_idx1)
2715     goto no_index;
2716
2717   GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
2718
2719   /* flush chunk header */
2720   gst_adapter_flush (avi->adapter, 8);
2721   /* read chunk payload */
2722   buf = gst_adapter_take_buffer (avi->adapter, size);
2723   if (!buf)
2724     goto pull_failed;
2725   /* advance offset */
2726   offset += 8 + GST_ROUND_UP_2 (size);
2727
2728   GST_DEBUG ("will parse index chunk size %u for tag %"
2729       GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
2730
2731   avi->offset = avi->first_movi_offset - 8;
2732   gst_avi_demux_parse_index (avi, buf);
2733
2734 #ifndef GST_DISABLE_GST_DEBUG
2735   /* debug our indexes */
2736   {
2737     gint i;
2738     GstAviStream *stream;
2739
2740     for (i = 0; i < avi->num_streams; i++) {
2741       stream = &avi->stream[i];
2742       GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
2743           i, stream->idx_n, stream->total_bytes);
2744     }
2745   }
2746 #endif
2747   return;
2748
2749   /* ERRORS */
2750 pull_failed:
2751   {
2752     GST_DEBUG_OBJECT (avi,
2753         "taking data from adapter failed: pos=%" G_GUINT64_FORMAT " size=%u",
2754         offset, size);
2755     return;
2756   }
2757 no_index:
2758   {
2759     GST_WARNING_OBJECT (avi,
2760         "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
2761         GST_FOURCC_ARGS (tag));
2762     return;
2763   }
2764 }
2765
2766 /*
2767  * gst_avi_demux_peek_tag:
2768  *
2769  * Returns the tag and size of the next chunk
2770  */
2771 static GstFlowReturn
2772 gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
2773     guint * size)
2774 {
2775   GstFlowReturn res = GST_FLOW_OK;
2776   GstBuffer *buf = NULL;
2777   guint bufsize;
2778   guint8 *bufdata;
2779
2780   res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2781   if (res != GST_FLOW_OK)
2782     goto pull_failed;
2783
2784   bufsize = GST_BUFFER_SIZE (buf);
2785   if (bufsize != 8)
2786     goto wrong_size;
2787
2788   bufdata = GST_BUFFER_DATA (buf);
2789
2790   *tag = GST_READ_UINT32_LE (bufdata);
2791   *size = GST_READ_UINT32_LE (bufdata + 4);
2792
2793   GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
2794       G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
2795       *size, offset + 8, offset + 8 + (gint64) * size);
2796
2797 done:
2798   gst_buffer_unref (buf);
2799
2800   return res;
2801
2802   /* ERRORS */
2803 pull_failed:
2804   {
2805     GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
2806     return res;
2807   }
2808 wrong_size:
2809   {
2810     GST_DEBUG_OBJECT (avi, "got %d bytes which is <> 8 bytes", bufsize);
2811     res = GST_FLOW_ERROR;
2812     goto done;
2813   }
2814 }
2815
2816 /*
2817  * gst_avi_demux_next_data_buffer:
2818  *
2819  * Returns the offset and size of the next buffer
2820  * Position is the position of the buffer (after tag and size)
2821  */
2822 static GstFlowReturn
2823 gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
2824     guint32 * tag, guint * size)
2825 {
2826   guint64 off = *offset;
2827   guint _size = 0;
2828   GstFlowReturn res;
2829
2830   do {
2831     res = gst_avi_demux_peek_tag (avi, off, tag, &_size);
2832     if (res != GST_FLOW_OK)
2833       break;
2834     if (*tag == GST_RIFF_TAG_LIST || *tag == GST_RIFF_TAG_RIFF)
2835       off += 8 + 4;             /* skip tag + size + subtag */
2836     else {
2837       *offset = off + 8;
2838       *size = _size;
2839       break;
2840     }
2841   } while (TRUE);
2842
2843   return res;
2844 }
2845
2846 /*
2847  * gst_avi_demux_stream_scan:
2848  * @avi: calling element (used for debugging/errors).
2849  *
2850  * Scan the file for all chunks to "create" a new index.
2851  * pull-range based
2852  */
2853 static gboolean
2854 gst_avi_demux_stream_scan (GstAviDemux * avi)
2855 {
2856   GstFlowReturn res;
2857   GstAviStream *stream;
2858   GstFormat format;
2859   guint64 pos = 0;
2860   guint64 length;
2861   gint64 tmplength;
2862   guint32 tag = 0;
2863   guint num;
2864
2865   /* FIXME:
2866    * - implement non-seekable source support.
2867    */
2868   GST_DEBUG_OBJECT (avi, "Creating index");
2869
2870   /* get the size of the file */
2871   format = GST_FORMAT_BYTES;
2872   if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength))
2873     return FALSE;
2874   length = tmplength;
2875
2876   /* guess the total amount of entries we expect */
2877   num = 16000;
2878
2879   while (TRUE) {
2880     GstAviIndexEntry entry;
2881     guint size = 0;
2882
2883     /* start reading data buffers to find the id and offset */
2884     res = gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size);
2885     if (G_UNLIKELY (res != GST_FLOW_OK))
2886       break;
2887
2888     /* get stream */
2889     stream = gst_avi_demux_stream_for_id (avi, tag);
2890     if (G_UNLIKELY (!stream))
2891       goto next;
2892
2893     /* we can't figure out the keyframes, assume they all are */
2894     entry.flags = GST_AVI_KEYFRAME;
2895     entry.offset = pos;
2896     entry.size = size;
2897
2898     /* and add to the index of this stream */
2899     if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2900       goto out_of_mem;
2901
2902   next:
2903     /* update position */
2904     pos += GST_ROUND_UP_2 (size);
2905     if (G_UNLIKELY (pos > length)) {
2906       GST_WARNING_OBJECT (avi,
2907           "Stopping index lookup since we are further than EOF");
2908       break;
2909     }
2910   }
2911
2912   /* collect stats */
2913   avi->have_index = gst_avi_demux_do_index_stats (avi);
2914
2915   return TRUE;
2916
2917   /* ERRORS */
2918 out_of_mem:
2919   {
2920     GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2921         ("Cannot allocate memory for %u*%u=%u bytes",
2922             (guint) sizeof (GstAviIndexEntry), num,
2923             (guint) sizeof (GstAviIndexEntry) * num));
2924     return FALSE;
2925   }
2926 }
2927
2928 static void
2929 gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
2930 {
2931   guint i;
2932   GstClockTime total;
2933   GstAviStream *stream;
2934
2935   total = GST_CLOCK_TIME_NONE;
2936
2937   /* all streams start at a timestamp 0 */
2938   for (i = 0; i < avi->num_streams; i++) {
2939     GstClockTime duration, hduration;
2940     gst_riff_strh *strh;
2941
2942     stream = &avi->stream[i];
2943     if (G_UNLIKELY (!stream || !stream->idx_n || !(strh = stream->strh)))
2944       continue;
2945
2946     /* get header duration for the stream */
2947     hduration = stream->hdr_duration;
2948     /* index duration calculated during parsing */
2949     duration = stream->idx_duration;
2950
2951     /* now pick a good duration */
2952     if (GST_CLOCK_TIME_IS_VALID (duration)) {
2953       /* index gave valid duration, use that */
2954       GST_INFO ("Stream %p duration according to index: %" GST_TIME_FORMAT,
2955           stream, GST_TIME_ARGS (duration));
2956     } else {
2957       /* fall back to header info to calculate a duration */
2958       duration = hduration;
2959     }
2960     GST_INFO ("Setting duration of stream #%d to %" GST_TIME_FORMAT,
2961         i, GST_TIME_ARGS (duration));
2962     /* set duration for the stream */
2963     stream->duration = duration;
2964
2965     /* find total duration */
2966     if (total == GST_CLOCK_TIME_NONE ||
2967         (GST_CLOCK_TIME_IS_VALID (duration) && duration > total))
2968       total = duration;
2969   }
2970
2971   if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) {
2972     /* now update the duration for those streams where we had none */
2973     for (i = 0; i < avi->num_streams; i++) {
2974       stream = &avi->stream[i];
2975
2976       if (!GST_CLOCK_TIME_IS_VALID (stream->duration)
2977           || stream->duration == 0) {
2978         stream->duration = total;
2979
2980         GST_INFO ("Stream %p duration according to total: %" GST_TIME_FORMAT,
2981             stream, GST_TIME_ARGS (total));
2982       }
2983     }
2984   }
2985
2986   /* and set the total duration in the segment. */
2987   GST_INFO ("Setting total duration to: %" GST_TIME_FORMAT,
2988       GST_TIME_ARGS (total));
2989
2990   gst_segment_set_duration (&avi->segment, GST_FORMAT_TIME, total);
2991 }
2992
2993 /* returns FALSE if there are no pads to deliver event to,
2994  * otherwise TRUE (whatever the outcome of event sending),
2995  * takes ownership of the event. */
2996 static gboolean
2997 gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
2998 {
2999   gboolean result = FALSE;
3000   gint i;
3001
3002   GST_DEBUG_OBJECT (avi, "sending %s event to %d streams",
3003       GST_EVENT_TYPE_NAME (event), avi->num_streams);
3004
3005   for (i = 0; i < avi->num_streams; i++) {
3006     GstAviStream *stream = &avi->stream[i];
3007
3008     if (stream->pad) {
3009       result = TRUE;
3010       gst_pad_push_event (stream->pad, gst_event_ref (event));
3011     }
3012   }
3013   gst_event_unref (event);
3014   return result;
3015 }
3016
3017 static void
3018 gst_avi_demux_check_seekability (GstAviDemux * avi)
3019 {
3020   GstQuery *query;
3021   gboolean seekable = FALSE;
3022   gint64 start = -1, stop = -1;
3023
3024   query = gst_query_new_seeking (GST_FORMAT_BYTES);
3025   if (!gst_pad_peer_query (avi->sinkpad, query)) {
3026     GST_DEBUG_OBJECT (avi, "seeking query failed");
3027     goto done;
3028   }
3029
3030   gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
3031
3032   /* try harder to query upstream size if we didn't get it the first time */
3033   if (seekable && stop == -1) {
3034     GstFormat fmt = GST_FORMAT_BYTES;
3035
3036     GST_DEBUG_OBJECT (avi, "doing duration query to fix up unset stop");
3037     gst_pad_query_peer_duration (avi->sinkpad, &fmt, &stop);
3038   }
3039
3040   /* if upstream doesn't know the size, it's likely that it's not seekable in
3041    * practice even if it technically may be seekable */
3042   if (seekable && (start != 0 || stop <= start)) {
3043     GST_DEBUG_OBJECT (avi, "seekable but unknown start/stop -> disable");
3044     seekable = FALSE;
3045   }
3046
3047   if (!avi->element_index) {
3048     GST_DEBUG_OBJECT (avi, "no index");
3049     seekable = FALSE;
3050   }
3051
3052 done:
3053   GST_INFO_OBJECT (avi, "seekable: %d (%" G_GUINT64_FORMAT " - %"
3054       G_GUINT64_FORMAT ")", seekable, start, stop);
3055   avi->seekable = seekable;
3056
3057   gst_query_unref (query);
3058 }
3059
3060 /*
3061  * Read AVI headers when streaming
3062  */
3063 static GstFlowReturn
3064 gst_avi_demux_stream_header_push (GstAviDemux * avi)
3065 {
3066   GstFlowReturn ret = GST_FLOW_OK;
3067   guint32 tag = 0;
3068   guint32 ltag = 0;
3069   guint32 size = 0;
3070   const guint8 *data;
3071   GstBuffer *buf = NULL, *sub = NULL;
3072   guint offset = 4;
3073   gint64 stop;
3074   gint i;
3075   GstTagList *tags = NULL;
3076
3077   GST_DEBUG ("Reading and parsing avi headers: %d", avi->header_state);
3078
3079   switch (avi->header_state) {
3080     case GST_AVI_DEMUX_HEADER_TAG_LIST:
3081       if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3082         avi->offset += 8 + GST_ROUND_UP_2 (size);
3083         if (tag != GST_RIFF_TAG_LIST)
3084           goto header_no_list;
3085
3086         gst_adapter_flush (avi->adapter, 8);
3087         /* Find the 'hdrl' LIST tag */
3088         GST_DEBUG ("Reading %d bytes", size);
3089         buf = gst_adapter_take_buffer (avi->adapter, size);
3090
3091         if (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl)
3092           goto header_no_hdrl;
3093
3094         /* mind padding */
3095         if (size & 1)
3096           gst_adapter_flush (avi->adapter, 1);
3097
3098         GST_DEBUG ("'hdrl' LIST tag found. Parsing next chunk");
3099
3100         /* the hdrl starts with a 'avih' header */
3101         if (!gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
3102                 &sub))
3103           goto header_no_avih;
3104
3105         if (tag != GST_RIFF_TAG_avih)
3106           goto header_no_avih;
3107
3108         if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
3109           goto header_wrong_avih;
3110
3111         GST_DEBUG_OBJECT (avi, "AVI header ok, reading elemnts from header");
3112
3113         /* now, read the elements from the header until the end */
3114         while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
3115                 &sub)) {
3116           /* sub can be NULL on empty tags */
3117           if (!sub)
3118             continue;
3119
3120           switch (tag) {
3121             case GST_RIFF_TAG_LIST:
3122               if (GST_BUFFER_SIZE (sub) < 4)
3123                 goto next;
3124
3125               switch (GST_READ_UINT32_LE (GST_BUFFER_DATA (sub))) {
3126                 case GST_RIFF_LIST_strl:
3127                   if (!(gst_avi_demux_parse_stream (avi, sub))) {
3128                     sub = NULL;
3129                     GST_ELEMENT_WARNING (avi, STREAM, DEMUX, (NULL),
3130                         ("failed to parse stream, ignoring"));
3131                     goto next;
3132                   }
3133                   sub = NULL;
3134                   goto next;
3135                 case GST_RIFF_LIST_odml:
3136                   gst_avi_demux_parse_odml (avi, sub);
3137                   sub = NULL;
3138                   break;
3139                 default:
3140                   GST_WARNING_OBJECT (avi,
3141                       "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
3142                       GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA
3143                               (sub))));
3144                   /* fall-through */
3145                 case GST_RIFF_TAG_JUNQ:
3146                 case GST_RIFF_TAG_JUNK:
3147                   goto next;
3148               }
3149               break;
3150             case GST_RIFF_IDIT:
3151               gst_avi_demux_parse_idit (avi, sub);
3152               goto next;
3153             default:
3154               GST_WARNING_OBJECT (avi,
3155                   "Unknown off %d tag %" GST_FOURCC_FORMAT " in AVI header",
3156                   offset, GST_FOURCC_ARGS (tag));
3157               /* fall-through */
3158             case GST_RIFF_TAG_JUNQ:
3159             case GST_RIFF_TAG_JUNK:
3160             next:
3161               /* move to next chunk */
3162               if (sub)
3163                 gst_buffer_unref (sub);
3164               sub = NULL;
3165               break;
3166           }
3167         }
3168         gst_buffer_unref (buf);
3169         GST_DEBUG ("elements parsed");
3170
3171         /* check parsed streams */
3172         if (avi->num_streams == 0) {
3173           goto no_streams;
3174         } else if (avi->num_streams != avi->avih->streams) {
3175           GST_WARNING_OBJECT (avi,
3176               "Stream header mentioned %d streams, but %d available",
3177               avi->avih->streams, avi->num_streams);
3178         }
3179         GST_DEBUG ("Get junk and info next");
3180         avi->header_state = GST_AVI_DEMUX_HEADER_INFO;
3181       } else {
3182         /* Need more data */
3183         return ret;
3184       }
3185       /* fall-though */
3186     case GST_AVI_DEMUX_HEADER_INFO:
3187       GST_DEBUG_OBJECT (avi, "skipping junk between header and data ...");
3188       while (TRUE) {
3189         if (gst_adapter_available (avi->adapter) < 12)
3190           return GST_FLOW_OK;
3191
3192         data = gst_adapter_peek (avi->adapter, 12);
3193         tag = GST_READ_UINT32_LE (data);
3194         size = GST_READ_UINT32_LE (data + 4);
3195         ltag = GST_READ_UINT32_LE (data + 8);
3196
3197         if (tag == GST_RIFF_TAG_LIST) {
3198           switch (ltag) {
3199             case GST_RIFF_LIST_movi:
3200               gst_adapter_flush (avi->adapter, 12);
3201               if (!avi->first_movi_offset)
3202                 avi->first_movi_offset = avi->offset;
3203               avi->offset += 12;
3204               avi->idx1_offset = avi->offset + size - 4;
3205               goto skipping_done;
3206             case GST_RIFF_LIST_INFO:
3207               GST_DEBUG ("Found INFO chunk");
3208               if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3209                 GST_DEBUG ("got size %d", size);
3210                 avi->offset += 12;
3211                 gst_adapter_flush (avi->adapter, 12);
3212                 if (size > 4) {
3213                   buf = gst_adapter_take_buffer (avi->adapter, size - 4);
3214                   /* mind padding */
3215                   if (size & 1)
3216                     gst_adapter_flush (avi->adapter, 1);
3217                   gst_riff_parse_info (GST_ELEMENT_CAST (avi), buf, &tags);
3218                   if (tags) {
3219                     if (avi->globaltags) {
3220                       gst_tag_list_insert (avi->globaltags, tags,
3221                           GST_TAG_MERGE_REPLACE);
3222                     } else {
3223                       avi->globaltags = tags;
3224                     }
3225                   }
3226                   tags = NULL;
3227                   gst_buffer_unref (buf);
3228
3229                   avi->offset += GST_ROUND_UP_2 (size) - 4;
3230                 } else {
3231                   GST_DEBUG ("skipping INFO LIST prefix");
3232                 }
3233               } else {
3234                 /* Need more data */
3235                 return GST_FLOW_OK;
3236               }
3237               break;
3238             default:
3239               if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3240                 avi->offset += 8 + GST_ROUND_UP_2 (size);
3241                 gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
3242                 // ??? goto iterate; ???
3243               } else {
3244                 /* Need more data */
3245                 return GST_FLOW_OK;
3246               }
3247               break;
3248           }
3249         } else {
3250           if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
3251             avi->offset += 8 + GST_ROUND_UP_2 (size);
3252             gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
3253             //goto iterate;
3254           } else {
3255             /* Need more data */
3256             return GST_FLOW_OK;
3257           }
3258         }
3259       }
3260       break;
3261     default:
3262       GST_WARNING ("unhandled header state: %d", avi->header_state);
3263       break;
3264   }
3265 skipping_done:
3266
3267   GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)",
3268       avi->num_streams, avi->stream[0].indexes);
3269
3270   GST_DEBUG ("Found movi chunk. Starting to stream data");
3271   avi->state = GST_AVI_DEMUX_MOVI;
3272
3273   /* no indexes in push mode, but it still sets some variables */
3274   gst_avi_demux_calculate_durations_from_index (avi);
3275
3276   gst_avi_demux_expose_streams (avi, TRUE);
3277
3278   /* prepare all streams for index 0 */
3279   for (i = 0; i < avi->num_streams; i++)
3280     avi->stream[i].current_entry = 0;
3281
3282   /* create initial NEWSEGMENT event */
3283   if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
3284     stop = avi->segment.duration;
3285
3286   GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
3287
3288   if (avi->seg_event)
3289     gst_event_unref (avi->seg_event);
3290   avi->seg_event = gst_event_new_new_segment_full
3291       (FALSE, avi->segment.rate, avi->segment.applied_rate, GST_FORMAT_TIME,
3292       avi->segment.start, stop, avi->segment.time);
3293
3294   gst_avi_demux_check_seekability (avi);
3295
3296   /* at this point we know all the streams and we can signal the no more
3297    * pads signal */
3298   GST_DEBUG_OBJECT (avi, "signaling no more pads");
3299   gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
3300
3301   return GST_FLOW_OK;
3302
3303   /* ERRORS */
3304 no_streams:
3305   {
3306     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
3307     return GST_FLOW_ERROR;
3308   }
3309 header_no_list:
3310   {
3311     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3312         ("Invalid AVI header (no LIST at start): %"
3313             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3314     return GST_FLOW_ERROR;
3315   }
3316 header_no_hdrl:
3317   {
3318     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3319         ("Invalid AVI header (no hdrl at start): %"
3320             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3321     gst_buffer_unref (buf);
3322     return GST_FLOW_ERROR;
3323   }
3324 header_no_avih:
3325   {
3326     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3327         ("Invalid AVI header (no avih at start): %"
3328             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3329     if (sub)
3330       gst_buffer_unref (sub);
3331
3332     gst_buffer_unref (buf);
3333     return GST_FLOW_ERROR;
3334   }
3335 header_wrong_avih:
3336   {
3337     gst_buffer_unref (buf);
3338     return GST_FLOW_ERROR;
3339   }
3340 }
3341
3342 static void
3343 gst_avi_demux_add_date_tag (GstAviDemux * avi, gint y, gint m, gint d)
3344 {
3345   GDate *date;
3346   date = g_date_new_dmy (d, m, y);
3347   if (!g_date_valid (date)) {
3348     /* bogus date */
3349     GST_WARNING_OBJECT (avi, "Refusing to add invalid date %d-%d-%d", y, m, d);
3350     g_date_free (date);
3351     return;
3352   }
3353
3354   if (avi->globaltags == NULL)
3355     avi->globaltags = gst_tag_list_new ();
3356
3357   gst_tag_list_add (avi->globaltags, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date,
3358       NULL);
3359   g_date_free (date);
3360 }
3361
3362 static void
3363 gst_avi_demux_parse_idit_nums_only (GstAviDemux * avi, gchar * data)
3364 {
3365   gint y, m, d;
3366   gint ret;
3367
3368   ret = sscanf (data, "%d:%d:%d", &y, &m, &d);
3369   if (ret != 3) {
3370     GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
3371     return;
3372   }
3373   gst_avi_demux_add_date_tag (avi, y, m, d);
3374 }
3375
3376 static gint
3377 get_month_num (gchar * data, guint size)
3378 {
3379   if (g_ascii_strncasecmp (data, "jan", 3) == 0) {
3380     return 1;
3381   } else if (g_ascii_strncasecmp (data, "feb", 3) == 0) {
3382     return 2;
3383   } else if (g_ascii_strncasecmp (data, "mar", 3) == 0) {
3384     return 3;
3385   } else if (g_ascii_strncasecmp (data, "apr", 3) == 0) {
3386     return 4;
3387   } else if (g_ascii_strncasecmp (data, "may", 3) == 0) {
3388     return 5;
3389   } else if (g_ascii_strncasecmp (data, "jun", 3) == 0) {
3390     return 6;
3391   } else if (g_ascii_strncasecmp (data, "jul", 3) == 0) {
3392     return 7;
3393   } else if (g_ascii_strncasecmp (data, "aug", 3) == 0) {
3394     return 8;
3395   } else if (g_ascii_strncasecmp (data, "sep", 3) == 0) {
3396     return 9;
3397   } else if (g_ascii_strncasecmp (data, "oct", 3) == 0) {
3398     return 10;
3399   } else if (g_ascii_strncasecmp (data, "nov", 3) == 0) {
3400     return 11;
3401   } else if (g_ascii_strncasecmp (data, "dec", 3) == 0) {
3402     return 12;
3403   }
3404
3405   return 0;
3406 }
3407
3408 static void
3409 gst_avi_demux_parse_idit_text (GstAviDemux * avi, gchar * data)
3410 {
3411   gint year, month, day;
3412   gint hour, min, sec;
3413   gint ret;
3414   gchar weekday[4];
3415   gchar monthstr[4];
3416
3417   ret = sscanf (data, "%3s %3s %d %d:%d:%d %d", weekday, monthstr, &day, &hour,
3418       &min, &sec, &year);
3419   if (ret != 7) {
3420     GST_WARNING_OBJECT (avi, "Failed to parse IDIT tag");
3421     return;
3422   }
3423   month = get_month_num (monthstr, strlen (monthstr));
3424   gst_avi_demux_add_date_tag (avi, year, month, day);
3425 }
3426
3427 static void
3428 gst_avi_demux_parse_idit (GstAviDemux * avi, GstBuffer * buf)
3429 {
3430   gchar *data = (gchar *) GST_BUFFER_DATA (buf);
3431   guint size = GST_BUFFER_SIZE (buf);
3432   gchar *safedata = NULL;
3433
3434   /*
3435    * According to:
3436    * http://www.eden-foundation.org/products/code/film_date_stamp/index.html
3437    *
3438    * This tag could be in one of the below formats
3439    * 2005:08:17 11:42:43
3440    * THU OCT 26 16:46:04 2006
3441    * Mon Mar  3 09:44:56 2008
3442    *
3443    * FIXME: Our date tag doesn't include hours
3444    */
3445
3446   /* skip eventual initial whitespace */
3447   while (size > 0 && g_ascii_isspace (data[0])) {
3448     data++;
3449     size--;
3450   }
3451
3452   if (size == 0) {
3453     goto non_parsable;
3454   }
3455
3456   /* make a safe copy to add a \0 to the end of the string */
3457   safedata = g_strndup (data, size);
3458
3459   /* test if the first char is a alpha or a number */
3460   if (g_ascii_isdigit (data[0])) {
3461     gst_avi_demux_parse_idit_nums_only (avi, safedata);
3462     g_free (safedata);
3463     return;
3464   } else if (g_ascii_isalpha (data[0])) {
3465     gst_avi_demux_parse_idit_text (avi, safedata);
3466     g_free (safedata);
3467     return;
3468   }
3469
3470   g_free (safedata);
3471
3472 non_parsable:
3473   GST_WARNING_OBJECT (avi, "IDIT tag has no parsable info");
3474 }
3475
3476 /*
3477  * Read full AVI headers.
3478  */
3479 static GstFlowReturn
3480 gst_avi_demux_stream_header_pull (GstAviDemux * avi)
3481 {
3482   GstFlowReturn res;
3483   GstBuffer *buf, *sub = NULL;
3484   guint32 tag;
3485   guint offset = 4;
3486   gint64 stop;
3487   GstElement *element = GST_ELEMENT_CAST (avi);
3488   GstClockTime stamp;
3489   GstTagList *tags = NULL;
3490
3491   stamp = gst_util_get_timestamp ();
3492
3493   /* the header consists of a 'hdrl' LIST tag */
3494   res = gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag, &buf);
3495   if (res != GST_FLOW_OK)
3496     goto pull_range_failed;
3497   else if (tag != GST_RIFF_TAG_LIST)
3498     goto no_list;
3499   else if (GST_BUFFER_SIZE (buf) < 4)
3500     goto no_header;
3501
3502   GST_DEBUG_OBJECT (avi, "parsing headers");
3503
3504   /* Find the 'hdrl' LIST tag */
3505   while (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl) {
3506     GST_LOG_OBJECT (avi, "buffer contains %" GST_FOURCC_FORMAT,
3507         GST_FOURCC_ARGS (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf))));
3508
3509     /* Eat up */
3510     gst_buffer_unref (buf);
3511
3512     /* read new chunk */
3513     res = gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag, &buf);
3514     if (res != GST_FLOW_OK)
3515       goto pull_range_failed;
3516     else if (tag != GST_RIFF_TAG_LIST)
3517       goto no_list;
3518     else if (GST_BUFFER_SIZE (buf) < 4)
3519       goto no_header;
3520   }
3521
3522   GST_DEBUG_OBJECT (avi, "hdrl LIST tag found");
3523
3524   /* the hdrl starts with a 'avih' header */
3525   if (!gst_riff_parse_chunk (element, buf, &offset, &tag, &sub))
3526     goto no_avih;
3527   else if (tag != GST_RIFF_TAG_avih)
3528     goto no_avih;
3529   else if (!gst_avi_demux_parse_avih (avi, sub, &avi->avih))
3530     goto invalid_avih;
3531
3532   GST_DEBUG_OBJECT (avi, "AVI header ok, reading elements from header");
3533
3534   /* now, read the elements from the header until the end */
3535   while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
3536     /* sub can be NULL on empty tags */
3537     if (!sub)
3538       continue;
3539
3540     switch (tag) {
3541       case GST_RIFF_TAG_LIST:
3542       {
3543         guint8 *data;
3544         guint32 fourcc;
3545
3546         if (GST_BUFFER_SIZE (sub) < 4)
3547           goto next;
3548
3549         data = GST_BUFFER_DATA (sub);
3550         fourcc = GST_READ_UINT32_LE (data);
3551
3552         switch (fourcc) {
3553           case GST_RIFF_LIST_strl:
3554             if (!(gst_avi_demux_parse_stream (avi, sub))) {
3555               GST_ELEMENT_WARNING (avi, STREAM, DEMUX, (NULL),
3556                   ("failed to parse stream, ignoring"));
3557               sub = NULL;
3558             }
3559             sub = NULL;
3560             goto next;
3561           case GST_RIFF_LIST_odml:
3562             gst_avi_demux_parse_odml (avi, sub);
3563             sub = NULL;
3564             break;
3565           case GST_RIFF_LIST_INFO:
3566             GST_BUFFER_DATA (sub) = data + 4;
3567             GST_BUFFER_SIZE (sub) -= 4;
3568             gst_riff_parse_info (element, sub, &tags);
3569             if (tags) {
3570               if (avi->globaltags) {
3571                 gst_tag_list_insert (avi->globaltags, tags,
3572                     GST_TAG_MERGE_REPLACE);
3573               } else {
3574                 avi->globaltags = tags;
3575               }
3576             }
3577             tags = NULL;
3578             break;
3579           default:
3580             GST_WARNING_OBJECT (avi,
3581                 "Unknown list %" GST_FOURCC_FORMAT " in AVI header",
3582                 GST_FOURCC_ARGS (fourcc));
3583             GST_MEMDUMP_OBJECT (avi, "Unknown list", GST_BUFFER_DATA (sub),
3584                 GST_BUFFER_SIZE (sub));
3585             /* fall-through */
3586           case GST_RIFF_TAG_JUNQ:
3587           case GST_RIFF_TAG_JUNK:
3588             goto next;
3589         }
3590         break;
3591       }
3592       case GST_RIFF_IDIT:
3593         gst_avi_demux_parse_idit (avi, sub);
3594         goto next;
3595       default:
3596         GST_WARNING_OBJECT (avi,
3597             "Unknown tag %" GST_FOURCC_FORMAT " in AVI header at off %d",
3598             GST_FOURCC_ARGS (tag), offset);
3599         GST_MEMDUMP_OBJECT (avi, "Unknown tag", GST_BUFFER_DATA (sub),
3600             GST_BUFFER_SIZE (sub));
3601         /* fall-through */
3602       case GST_RIFF_TAG_JUNQ:
3603       case GST_RIFF_TAG_JUNK:
3604       next:
3605         if (sub)
3606           gst_buffer_unref (sub);
3607         sub = NULL;
3608         break;
3609     }
3610   }
3611   gst_buffer_unref (buf);
3612   GST_DEBUG ("elements parsed");
3613
3614   /* check parsed streams */
3615   if (avi->num_streams == 0)
3616     goto no_streams;
3617   else if (avi->num_streams != avi->avih->streams) {
3618     GST_WARNING_OBJECT (avi,
3619         "Stream header mentioned %d streams, but %d available",
3620         avi->avih->streams, avi->num_streams);
3621   }
3622
3623   GST_DEBUG_OBJECT (avi, "skipping junk between header and data, offset=%"
3624       G_GUINT64_FORMAT, avi->offset);
3625
3626   /* Now, find the data (i.e. skip all junk between header and data) */
3627   do {
3628     guint size;
3629     guint8 *data;
3630     guint32 tag, ltag;
3631
3632     res = gst_pad_pull_range (avi->sinkpad, avi->offset, 12, &buf);
3633     if (res != GST_FLOW_OK) {
3634       GST_DEBUG_OBJECT (avi, "pull_range failure while looking for tags");
3635       goto pull_range_failed;
3636     } else if (GST_BUFFER_SIZE (buf) < 12) {
3637       GST_DEBUG_OBJECT (avi, "got %d bytes which is less than 12 bytes",
3638           GST_BUFFER_SIZE (buf));
3639       gst_buffer_unref (buf);
3640       return GST_FLOW_ERROR;
3641     }
3642
3643     data = GST_BUFFER_DATA (buf);
3644
3645     tag = GST_READ_UINT32_LE (data);
3646     size = GST_READ_UINT32_LE (data + 4);
3647     ltag = GST_READ_UINT32_LE (data + 8);
3648
3649     GST_DEBUG ("tag %" GST_FOURCC_FORMAT ", size %u",
3650         GST_FOURCC_ARGS (tag), size);
3651     GST_MEMDUMP ("Tag content", data, GST_BUFFER_SIZE (buf));
3652     gst_buffer_unref (buf);
3653
3654     switch (tag) {
3655       case GST_RIFF_TAG_LIST:{
3656         switch (ltag) {
3657           case GST_RIFF_LIST_movi:
3658             GST_DEBUG_OBJECT (avi,
3659                 "Reached the 'movi' tag, we're done with skipping");
3660             goto skipping_done;
3661           case GST_RIFF_LIST_INFO:
3662             res =
3663                 gst_riff_read_chunk (element, avi->sinkpad, &avi->offset, &tag,
3664                 &buf);
3665             if (res != GST_FLOW_OK) {
3666               GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
3667               goto pull_range_failed;
3668             }
3669             GST_DEBUG ("got size %u", GST_BUFFER_SIZE (buf));
3670             if (size < 4) {
3671               GST_DEBUG ("skipping INFO LIST prefix");
3672               avi->offset += (4 - GST_ROUND_UP_2 (size));
3673               gst_buffer_unref (buf);
3674               continue;
3675             }
3676
3677             sub = gst_buffer_create_sub (buf, 4, GST_BUFFER_SIZE (buf) - 4);
3678             gst_riff_parse_info (element, sub, &tags);
3679             if (tags) {
3680               if (avi->globaltags) {
3681                 gst_tag_list_insert (avi->globaltags, tags,
3682                     GST_TAG_MERGE_REPLACE);
3683               } else {
3684                 avi->globaltags = tags;
3685               }
3686             }
3687             tags = NULL;
3688             if (sub) {
3689               gst_buffer_unref (sub);
3690               sub = NULL;
3691             }
3692             gst_buffer_unref (buf);
3693             /* gst_riff_read_chunk() has already advanced avi->offset */
3694             break;
3695           default:
3696             GST_WARNING_OBJECT (avi,
3697                 "Skipping unknown list tag %" GST_FOURCC_FORMAT,
3698                 GST_FOURCC_ARGS (ltag));
3699             avi->offset += 8 + GST_ROUND_UP_2 (size);
3700             break;
3701         }
3702       }
3703         break;
3704       default:
3705         GST_WARNING_OBJECT (avi, "Skipping unknown tag %" GST_FOURCC_FORMAT,
3706             GST_FOURCC_ARGS (tag));
3707         /* Fall-through */
3708       case GST_MAKE_FOURCC ('J', 'U', 'N', 'Q'):
3709       case GST_MAKE_FOURCC ('J', 'U', 'N', 'K'):
3710         /* Only get buffer for debugging if the memdump is needed  */
3711         if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= 9) {
3712           res = gst_pad_pull_range (avi->sinkpad, avi->offset, size, &buf);
3713           if (res != GST_FLOW_OK) {
3714             GST_DEBUG_OBJECT (avi, "couldn't read INFO chunk");
3715             goto pull_range_failed;
3716           }
3717           GST_MEMDUMP ("Junk", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
3718           gst_buffer_unref (buf);
3719         }
3720         avi->offset += 8 + GST_ROUND_UP_2 (size);
3721         break;
3722     }
3723   } while (1);
3724 skipping_done:
3725
3726   GST_DEBUG_OBJECT (avi, "skipping done ... (streams=%u, stream[0].indexes=%p)",
3727       avi->num_streams, avi->stream[0].indexes);
3728
3729   /* create or read stream index (for seeking) */
3730   if (avi->stream[0].indexes != NULL) {
3731     /* we read a super index already (gst_avi_demux_parse_superindex() ) */
3732     gst_avi_demux_read_subindexes_pull (avi);
3733   }
3734   if (!avi->have_index) {
3735     if (avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
3736       gst_avi_demux_stream_index (avi);
3737
3738     /* still no index, scan */
3739     if (!avi->have_index) {
3740       gst_avi_demux_stream_scan (avi);
3741
3742       /* still no index.. this is a fatal error for now.
3743        * FIXME, we should switch to plain push mode without seeking
3744        * instead of failing. */
3745       if (!avi->have_index)
3746         goto no_index;
3747     }
3748   }
3749   /* use the indexes now to construct nice durations */
3750   gst_avi_demux_calculate_durations_from_index (avi);
3751
3752   gst_avi_demux_expose_streams (avi, FALSE);
3753
3754   /* create initial NEWSEGMENT event */
3755   if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
3756     stop = avi->segment.duration;
3757
3758   GST_DEBUG_OBJECT (avi, "segment stop %" G_GINT64_FORMAT, stop);
3759
3760   /* do initial seek to the default segment values */
3761   gst_avi_demux_do_seek (avi, &avi->segment);
3762
3763   /* prepare initial segment */
3764   if (avi->seg_event)
3765     gst_event_unref (avi->seg_event);
3766   avi->seg_event = gst_event_new_new_segment_full
3767       (FALSE, avi->segment.rate, avi->segment.applied_rate, GST_FORMAT_TIME,
3768       avi->segment.start, stop, avi->segment.time);
3769
3770   stamp = gst_util_get_timestamp () - stamp;
3771   GST_DEBUG_OBJECT (avi, "pulling header took %" GST_TIME_FORMAT,
3772       GST_TIME_ARGS (stamp));
3773
3774   /* at this point we know all the streams and we can signal the no more
3775    * pads signal */
3776   GST_DEBUG_OBJECT (avi, "signaling no more pads");
3777   gst_element_no_more_pads (GST_ELEMENT_CAST (avi));
3778
3779   return GST_FLOW_OK;
3780
3781   /* ERRORS */
3782 no_list:
3783   {
3784     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3785         ("Invalid AVI header (no LIST at start): %"
3786             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3787     gst_buffer_unref (buf);
3788     return GST_FLOW_ERROR;
3789   }
3790 no_header:
3791   {
3792     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3793         ("Invalid AVI header (no hdrl at start): %"
3794             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3795     gst_buffer_unref (buf);
3796     return GST_FLOW_ERROR;
3797   }
3798 no_avih:
3799   {
3800     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3801         ("Invalid AVI header (no avih at start): %"
3802             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (tag)));
3803     if (sub)
3804       gst_buffer_unref (sub);
3805     gst_buffer_unref (buf);
3806     return GST_FLOW_ERROR;
3807   }
3808 invalid_avih:
3809   {
3810     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3811         ("Invalid AVI header (cannot parse avih at start)"));
3812     gst_buffer_unref (buf);
3813     return GST_FLOW_ERROR;
3814   }
3815 no_streams:
3816   {
3817     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No streams found"));
3818     return GST_FLOW_ERROR;
3819   }
3820 no_index:
3821   {
3822     GST_WARNING ("file without or too big index");
3823     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3824         ("Could not get/create index"));
3825     return GST_FLOW_ERROR;
3826   }
3827 pull_range_failed:
3828   {
3829     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
3830         ("pull_range flow reading header: %s", gst_flow_get_name (res)));
3831     return GST_FLOW_ERROR;
3832   }
3833 }
3834
3835 /* move a stream to @index */
3836 static void
3837 gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream,
3838     GstSegment * segment, guint index)
3839 {
3840   GST_DEBUG_OBJECT (avi, "Move stream %d to %u", stream->num, index);
3841
3842   if (segment->rate < 0.0) {
3843     guint next_key;
3844     /* Because we don't know the frame order we need to push from the prev keyframe
3845      * to the next keyframe. If there is a smart decoder downstream he will notice
3846      * that there are too many encoded frames send and return UNEXPECTED when there
3847      * are enough decoded frames to fill the segment. */
3848     next_key = gst_avi_demux_index_next (avi, stream, index, TRUE);
3849
3850     /* FIXME, we go back to 0, we should look at segment.start. We will however
3851      * stop earlier when the see the timestamp < segment.start */
3852     stream->start_entry = 0;
3853     stream->step_entry = index;
3854     stream->current_entry = index;
3855     stream->stop_entry = next_key;
3856
3857     GST_DEBUG_OBJECT (avi, "reverse seek: start %u, step %u, stop %u",
3858         stream->start_entry, stream->step_entry, stream->stop_entry);
3859   } else {
3860     stream->start_entry = index;
3861     stream->step_entry = index;
3862     stream->stop_entry = gst_avi_demux_index_last (avi, stream);
3863   }
3864   if (stream->current_entry != index) {
3865     GST_DEBUG_OBJECT (avi, "Move DISCONT from %u to %u",
3866         stream->current_entry, index);
3867     stream->current_entry = index;
3868     stream->discont = TRUE;
3869   }
3870
3871   /* update the buffer info */
3872   gst_avi_demux_get_buffer_info (avi, stream, index,
3873       &stream->current_timestamp, &stream->current_ts_end,
3874       &stream->current_offset, &stream->current_offset_end);
3875
3876   GST_DEBUG_OBJECT (avi, "Moved to %u, ts %" GST_TIME_FORMAT
3877       ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
3878       ", off_end %" G_GUINT64_FORMAT, index,
3879       GST_TIME_ARGS (stream->current_timestamp),
3880       GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
3881       stream->current_offset_end);
3882
3883   GST_DEBUG_OBJECT (avi, "Seeking to offset %" G_GUINT64_FORMAT,
3884       stream->index[index].offset);
3885 }
3886
3887 /*
3888  * Do the actual seeking.
3889  */
3890 static gboolean
3891 gst_avi_demux_do_seek (GstAviDemux * avi, GstSegment * segment)
3892 {
3893   GstClockTime seek_time;
3894   gboolean keyframe;
3895   guint i, index;
3896   GstAviStream *stream;
3897
3898   seek_time = segment->last_stop;
3899   keyframe = !!(segment->flags & GST_SEEK_FLAG_KEY_UNIT);
3900
3901   GST_DEBUG_OBJECT (avi, "seek to: %" GST_TIME_FORMAT
3902       " keyframe seeking:%d", GST_TIME_ARGS (seek_time), keyframe);
3903
3904   /* FIXME, this code assumes the main stream with keyframes is stream 0,
3905    * which is mostly correct... */
3906   stream = &avi->stream[avi->main_stream];
3907
3908   /* get the entry index for the requested position */
3909   index = gst_avi_demux_index_for_time (avi, stream, seek_time);
3910   GST_DEBUG_OBJECT (avi, "Got entry %u", index);
3911
3912   /* check if we are already on a keyframe */
3913   if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
3914     GST_DEBUG_OBJECT (avi, "not keyframe, searching back");
3915     /* now go to the previous keyframe, this is where we should start
3916      * decoding from. */
3917     index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
3918     GST_DEBUG_OBJECT (avi, "previous keyframe at %u", index);
3919   }
3920
3921   /* move the main stream to this position */
3922   gst_avi_demux_move_stream (avi, stream, segment, index);
3923
3924   if (keyframe) {
3925     /* when seeking to a keyframe, we update the result seek time
3926      * to the time of the keyframe. */
3927     seek_time = stream->current_timestamp;
3928     GST_DEBUG_OBJECT (avi, "keyframe adjusted to %" GST_TIME_FORMAT,
3929         GST_TIME_ARGS (seek_time));
3930   }
3931
3932   /* the seek time is also the last_stop and stream time when going
3933    * forwards */
3934   segment->last_stop = seek_time;
3935   if (segment->rate > 0.0)
3936     segment->time = seek_time;
3937
3938   /* now set DISCONT and align the other streams */
3939   for (i = 0; i < avi->num_streams; i++) {
3940     GstAviStream *ostream;
3941
3942     ostream = &avi->stream[i];
3943     if ((ostream == stream) || (ostream->index == NULL))
3944       continue;
3945
3946     /* get the entry index for the requested position */
3947     index = gst_avi_demux_index_for_time (avi, ostream, seek_time);
3948
3949     /* move to previous keyframe */
3950     if (!ENTRY_IS_KEYFRAME (&ostream->index[index]))
3951       index = gst_avi_demux_index_prev (avi, ostream, index, TRUE);
3952
3953     gst_avi_demux_move_stream (avi, ostream, segment, index);
3954   }
3955   GST_DEBUG_OBJECT (avi, "done seek to: %" GST_TIME_FORMAT,
3956       GST_TIME_ARGS (seek_time));
3957
3958   return TRUE;
3959 }
3960
3961 /*
3962  * Handle seek event in pull mode.
3963  */
3964 static gboolean
3965 gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event)
3966 {
3967   gdouble rate;
3968   GstFormat format;
3969   GstSeekFlags flags;
3970   GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
3971   gint64 cur, stop;
3972   gboolean flush;
3973   gboolean update;
3974   GstSegment seeksegment = { 0, };
3975   gint i;
3976
3977   if (event) {
3978     GST_DEBUG_OBJECT (avi, "doing seek with event");
3979
3980     gst_event_parse_seek (event, &rate, &format, &flags,
3981         &cur_type, &cur, &stop_type, &stop);
3982
3983     /* we have to have a format as the segment format. Try to convert
3984      * if not. */
3985     if (format != GST_FORMAT_TIME) {
3986       GstFormat fmt = GST_FORMAT_TIME;
3987       gboolean res = TRUE;
3988
3989       if (cur_type != GST_SEEK_TYPE_NONE)
3990         res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
3991       if (res && stop_type != GST_SEEK_TYPE_NONE)
3992         res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
3993       if (!res)
3994         goto no_format;
3995
3996       format = fmt;
3997     }
3998     GST_DEBUG_OBJECT (avi,
3999         "seek requested: rate %g cur %" GST_TIME_FORMAT " stop %"
4000         GST_TIME_FORMAT, rate, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
4001     /* FIXME: can we do anything with rate!=1.0 */
4002   } else {
4003     GST_DEBUG_OBJECT (avi, "doing seek without event");
4004     flags = 0;
4005     rate = 1.0;
4006   }
4007
4008   /* save flush flag */
4009   flush = flags & GST_SEEK_FLAG_FLUSH;
4010
4011   if (flush) {
4012     GstEvent *fevent = gst_event_new_flush_start ();
4013
4014     /* for a flushing seek, we send a flush_start on all pads. This will
4015      * eventually stop streaming with a WRONG_STATE. We can thus eventually
4016      * take the STREAM_LOCK. */
4017     GST_DEBUG_OBJECT (avi, "sending flush start");
4018     gst_avi_demux_push_event (avi, gst_event_ref (fevent));
4019     gst_pad_push_event (avi->sinkpad, fevent);
4020   } else {
4021     /* a non-flushing seek, we PAUSE the task so that we can take the
4022      * STREAM_LOCK */
4023     GST_DEBUG_OBJECT (avi, "non flushing seek, pausing task");
4024     gst_pad_pause_task (avi->sinkpad);
4025   }
4026
4027   /* wait for streaming to stop */
4028   GST_DEBUG_OBJECT (avi, "wait for streaming to stop");
4029   GST_PAD_STREAM_LOCK (avi->sinkpad);
4030
4031   /* copy segment, we need this because we still need the old
4032    * segment when we close the current segment. */
4033   memcpy (&seeksegment, &avi->segment, sizeof (GstSegment));
4034
4035   if (event) {
4036     GST_DEBUG_OBJECT (avi, "configuring seek");
4037     gst_segment_set_seek (&seeksegment, rate, format, flags,
4038         cur_type, cur, stop_type, stop, &update);
4039   }
4040   /* do the seek, seeksegment.last_stop contains the new position, this
4041    * actually never fails. */
4042   gst_avi_demux_do_seek (avi, &seeksegment);
4043
4044   if (flush) {
4045     GstEvent *fevent = gst_event_new_flush_stop ();
4046
4047     GST_DEBUG_OBJECT (avi, "sending flush stop");
4048     gst_avi_demux_push_event (avi, gst_event_ref (fevent));
4049     gst_pad_push_event (avi->sinkpad, fevent);
4050
4051     /* reset the last flow and mark discont, FLUSH is always DISCONT */
4052     for (i = 0; i < avi->num_streams; i++) {
4053       GST_DEBUG_OBJECT (avi, "marking DISCONT");
4054       avi->stream[i].last_flow = GST_FLOW_OK;
4055       avi->stream[i].discont = TRUE;
4056     }
4057   } else if (avi->segment_running) {
4058     GstEvent *seg;
4059
4060     /* we are running the current segment and doing a non-flushing seek,
4061      * close the segment first based on the last_stop. */
4062     GST_DEBUG_OBJECT (avi, "closing running segment %" G_GINT64_FORMAT
4063         " to %" G_GINT64_FORMAT, avi->segment.start, avi->segment.last_stop);
4064     seg = gst_event_new_new_segment_full (TRUE,
4065         avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
4066         avi->segment.start, avi->segment.last_stop, avi->segment.time);
4067     gst_avi_demux_push_event (avi, seg);
4068   }
4069
4070   /* now update the real segment info */
4071   memcpy (&avi->segment, &seeksegment, sizeof (GstSegment));
4072
4073   /* post the SEGMENT_START message when we do segmented playback */
4074   if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4075     gst_element_post_message (GST_ELEMENT_CAST (avi),
4076         gst_message_new_segment_start (GST_OBJECT_CAST (avi),
4077             avi->segment.format, avi->segment.last_stop));
4078   }
4079
4080   /* prepare for streaming again */
4081   if ((stop = avi->segment.stop) == GST_CLOCK_TIME_NONE)
4082     stop = avi->segment.duration;
4083
4084   /* queue the segment event for the streaming thread. */
4085   if (avi->seg_event)
4086     gst_event_unref (avi->seg_event);
4087   if (avi->segment.rate > 0.0) {
4088     /* forwards goes from last_stop to stop */
4089     avi->seg_event = gst_event_new_new_segment_full (FALSE,
4090         avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
4091         avi->segment.last_stop, stop, avi->segment.time);
4092   } else {
4093     /* reverse goes from start to last_stop */
4094     avi->seg_event = gst_event_new_new_segment_full (FALSE,
4095         avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
4096         avi->segment.start, avi->segment.last_stop, avi->segment.time);
4097   }
4098
4099   if (!avi->streaming) {
4100     avi->segment_running = TRUE;
4101     gst_pad_start_task (avi->sinkpad, (GstTaskFunction) gst_avi_demux_loop,
4102         avi->sinkpad);
4103   }
4104   GST_PAD_STREAM_UNLOCK (avi->sinkpad);
4105
4106   return TRUE;
4107
4108   /* ERRORS */
4109 no_format:
4110   {
4111     GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
4112     return FALSE;
4113   }
4114 }
4115
4116 /*
4117  * Handle seek event in push mode.
4118  */
4119 static gboolean
4120 avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad, GstEvent * event)
4121 {
4122   gdouble rate;
4123   GstFormat format;
4124   GstSeekFlags flags;
4125   GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
4126   gint64 cur, stop;
4127   gboolean keyframe;
4128   GstAviStream *stream;
4129   guint index;
4130   guint n, str_num;
4131   guint64 min_offset;
4132
4133   /* check we have the index */
4134   if (!avi->have_index) {
4135     GST_DEBUG_OBJECT (avi, "no seek index built, seek aborted.");
4136     return FALSE;
4137   } else {
4138     GST_DEBUG_OBJECT (avi, "doing push-based seek with event");
4139   }
4140
4141   gst_event_parse_seek (event, &rate, &format, &flags,
4142       &cur_type, &cur, &stop_type, &stop);
4143
4144   if (format != GST_FORMAT_TIME) {
4145     GstFormat fmt = GST_FORMAT_TIME;
4146     gboolean res = TRUE;
4147
4148     if (cur_type != GST_SEEK_TYPE_NONE)
4149       res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
4150     if (res && stop_type != GST_SEEK_TYPE_NONE)
4151       res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
4152     if (!res) {
4153       GST_DEBUG_OBJECT (avi, "unsupported format given, seek aborted.");
4154       return FALSE;
4155     }
4156
4157     format = fmt;
4158   }
4159
4160   keyframe = !!(flags & GST_SEEK_FLAG_KEY_UNIT);
4161
4162   GST_DEBUG_OBJECT (avi,
4163       "Seek requested: ts %" GST_TIME_FORMAT " stop %" GST_TIME_FORMAT
4164       ", kf %u, rate %lf", GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), keyframe,
4165       rate);
4166   /* FIXME: can we do anything with rate!=1.0 */
4167
4168   /* FIXME, this code assumes the main stream with keyframes is stream 0,
4169    * which is mostly correct... */
4170   str_num = avi->main_stream;
4171   stream = &avi->stream[str_num];
4172
4173   /* get the entry index for the requested position */
4174   index = gst_avi_demux_index_for_time (avi, stream, cur);
4175   GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT,
4176       str_num, index, GST_TIME_ARGS (cur));
4177
4178   /* check if we are already on a keyframe */
4179   if (!ENTRY_IS_KEYFRAME (&stream->index[index])) {
4180     GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
4181     /* now go to the previous keyframe, this is where we should start
4182      * decoding from. */
4183     index = gst_avi_demux_index_prev (avi, stream, index, TRUE);
4184     GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", index);
4185   }
4186
4187   gst_avi_demux_get_buffer_info (avi, stream, index,
4188       &stream->current_timestamp, &stream->current_ts_end,
4189       &stream->current_offset, &stream->current_offset_end);
4190
4191   /* re-use cur to be the timestamp of the seek as it _will_ be */
4192   cur = stream->current_timestamp;
4193
4194   min_offset = stream->index[index].offset;
4195
4196   GST_DEBUG_OBJECT (avi,
4197       "Seek to: ts %" GST_TIME_FORMAT " (on str %u, idx %u, offset %"
4198       G_GUINT64_FORMAT ")", GST_TIME_ARGS (stream->current_timestamp), str_num,
4199       index, min_offset);
4200
4201   for (n = 0; n < avi->num_streams; n++) {
4202     GstAviStream *str = &avi->stream[n];
4203     guint idx;
4204
4205     if (n == avi->main_stream)
4206       continue;
4207
4208     /* get the entry index for the requested position */
4209     idx = gst_avi_demux_index_for_time (avi, str, cur);
4210     GST_DEBUG_OBJECT (avi, "str %u: Found entry %u for %" GST_TIME_FORMAT, n,
4211         idx, GST_TIME_ARGS (cur));
4212
4213     /* check if we are already on a keyframe */
4214     if (!ENTRY_IS_KEYFRAME (&str->index[idx])) {
4215       GST_DEBUG_OBJECT (avi, "Entry is not a keyframe - searching back");
4216       /* now go to the previous keyframe, this is where we should start
4217        * decoding from. */
4218       idx = gst_avi_demux_index_prev (avi, str, idx, TRUE);
4219       GST_DEBUG_OBJECT (avi, "Found previous keyframe at %u", idx);
4220     }
4221
4222     gst_avi_demux_get_buffer_info (avi, str, idx,
4223         &str->current_timestamp, &str->current_ts_end,
4224         &str->current_offset, &str->current_offset_end);
4225
4226     if (str->index[idx].offset < min_offset) {
4227       min_offset = str->index[idx].offset;
4228       GST_DEBUG_OBJECT (avi,
4229           "Found an earlier offset at %" G_GUINT64_FORMAT ", str %u",
4230           min_offset, n);
4231       str_num = n;
4232       stream = str;
4233       index = idx;
4234     }
4235   }
4236
4237   GST_DEBUG_OBJECT (avi,
4238       "Seek performed: str %u, offset %" G_GUINT64_FORMAT ", idx %u, ts %"
4239       GST_TIME_FORMAT ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4240       ", off_end %" G_GUINT64_FORMAT, str_num, min_offset, index,
4241       GST_TIME_ARGS (stream->current_timestamp),
4242       GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
4243       stream->current_offset_end);
4244
4245   if (!perform_seek_to_offset (avi,
4246           min_offset - (avi->stream[0].indexes ? 8 : 0))) {
4247     GST_DEBUG_OBJECT (avi, "seek event failed!");
4248     return FALSE;
4249   }
4250
4251   return TRUE;
4252 }
4253
4254 /*
4255  * Handle whether we can perform the seek event or if we have to let the chain
4256  * function handle seeks to build the seek indexes first.
4257  */
4258 static gboolean
4259 gst_avi_demux_handle_seek_push (GstAviDemux * avi, GstPad * pad,
4260     GstEvent * event)
4261 {
4262   /* check for having parsed index already */
4263   if (!avi->have_index) {
4264     guint64 offset;
4265     gboolean building_index;
4266
4267     GST_OBJECT_LOCK (avi);
4268     /* handle the seek event in the chain function */
4269     avi->state = GST_AVI_DEMUX_SEEK;
4270
4271     /* copy the event */
4272     if (avi->seek_event)
4273       gst_event_unref (avi->seek_event);
4274     avi->seek_event = gst_event_ref (event);
4275
4276     /* set the building_index flag so that only one thread can setup the
4277      * structures for index seeking. */
4278     building_index = avi->building_index;
4279     if (!building_index) {
4280       avi->building_index = TRUE;
4281       if (avi->stream[0].indexes) {
4282         avi->odml_stream = 0;
4283         avi->odml_subidxs = avi->stream[avi->odml_stream].indexes;
4284         offset = avi->odml_subidxs[0];
4285       } else {
4286         offset = avi->idx1_offset;
4287       }
4288     }
4289     GST_OBJECT_UNLOCK (avi);
4290
4291     if (!building_index) {
4292       /* seek to the first subindex or legacy index */
4293       GST_INFO_OBJECT (avi,
4294           "Seeking to legacy index/first subindex at %" G_GUINT64_FORMAT,
4295           offset);
4296       return perform_seek_to_offset (avi, offset);
4297     }
4298
4299     /* FIXME: we have to always return true so that we don't block the seek
4300      * thread.
4301      * Note: maybe it is OK to return true if we're still building the index */
4302     return TRUE;
4303   }
4304
4305   return avi_demux_handle_seek_push (avi, pad, event);
4306 }
4307
4308 /*
4309  * Helper for gst_avi_demux_invert()
4310  */
4311 static inline void
4312 swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes)
4313 {
4314   memcpy (tmp, d1, bytes);
4315   memcpy (d1, d2, bytes);
4316   memcpy (d2, tmp, bytes);
4317 }
4318
4319
4320 #define gst_avi_demux_is_uncompressed(fourcc)           \
4321   (fourcc == GST_RIFF_DIB ||                            \
4322    fourcc == GST_RIFF_rgb ||                            \
4323    fourcc == GST_RIFF_RGB || fourcc == GST_RIFF_RAW)
4324
4325 /*
4326  * Invert DIB buffers... Takes existing buffer and
4327  * returns either the buffer or a new one (with old
4328  * one dereferenced).
4329  * FIXME: can't we preallocate tmp? and remember stride, bpp?
4330  */
4331 static GstBuffer *
4332 gst_avi_demux_invert (GstAviStream * stream, GstBuffer * buf)
4333 {
4334   GstStructure *s;
4335   gint y, w, h;
4336   gint bpp, stride;
4337   guint8 *tmp = NULL;
4338
4339   if (stream->strh->type != GST_RIFF_FCC_vids)
4340     return buf;
4341
4342   if (!gst_avi_demux_is_uncompressed (stream->strh->fcc_handler)) {
4343     return buf;                 /* Ignore non DIB buffers */
4344   }
4345
4346   s = gst_caps_get_structure (GST_PAD_CAPS (stream->pad), 0);
4347   if (!gst_structure_get_int (s, "bpp", &bpp)) {
4348     GST_WARNING ("Failed to retrieve depth from caps");
4349     return buf;
4350   }
4351
4352   if (stream->strf.vids == NULL) {
4353     GST_WARNING ("Failed to retrieve vids for stream");
4354     return buf;
4355   }
4356
4357   h = stream->strf.vids->height;
4358   w = stream->strf.vids->width;
4359   stride = w * (bpp / 8);
4360
4361   buf = gst_buffer_make_writable (buf);
4362   if (GST_BUFFER_SIZE (buf) < (stride * h)) {
4363     GST_WARNING ("Buffer is smaller than reported Width x Height x Depth");
4364     return buf;
4365   }
4366
4367   tmp = g_malloc (stride);
4368
4369   for (y = 0; y < h / 2; y++) {
4370     swap_line (GST_BUFFER_DATA (buf) + stride * y,
4371         GST_BUFFER_DATA (buf) + stride * (h - 1 - y), tmp, stride);
4372   }
4373
4374   g_free (tmp);
4375
4376   return buf;
4377 }
4378
4379 static void
4380 gst_avi_demux_add_assoc (GstAviDemux * avi, GstAviStream * stream,
4381     GstClockTime timestamp, guint64 offset, gboolean keyframe)
4382 {
4383   /* do not add indefinitely for open-ended streaming */
4384   if (G_UNLIKELY (avi->element_index && avi->seekable)) {
4385     GST_LOG_OBJECT (avi, "adding association %" GST_TIME_FORMAT "-> %"
4386         G_GUINT64_FORMAT, GST_TIME_ARGS (timestamp), offset);
4387     gst_index_add_association (avi->element_index, avi->index_id,
4388         keyframe ? GST_ASSOCIATION_FLAG_KEY_UNIT : GST_ASSOCIATION_FLAG_NONE,
4389         GST_FORMAT_TIME, timestamp, GST_FORMAT_BYTES, offset, NULL);
4390     /* well, current_total determines TIME and entry DEFAULT (frame #) ... */
4391     gst_index_add_association (avi->element_index, stream->index_id,
4392         GST_ASSOCIATION_FLAG_NONE,
4393         GST_FORMAT_TIME, stream->current_total, GST_FORMAT_BYTES, offset,
4394         GST_FORMAT_DEFAULT, stream->current_entry, NULL);
4395   }
4396 }
4397
4398 /*
4399  * Returns the aggregated GstFlowReturn.
4400  */
4401 static GstFlowReturn
4402 gst_avi_demux_combine_flows (GstAviDemux * avi, GstAviStream * stream,
4403     GstFlowReturn ret)
4404 {
4405   guint i;
4406   gboolean unexpected = FALSE, not_linked = TRUE;
4407
4408   /* store the value */
4409   stream->last_flow = ret;
4410
4411   /* any other error that is not-linked or eos can be returned right away */
4412   if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
4413     goto done;
4414
4415   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4416   for (i = 0; i < avi->num_streams; i++) {
4417     GstAviStream *ostream = &avi->stream[i];
4418
4419     ret = ostream->last_flow;
4420     /* no unexpected or unlinked, return */
4421     if (G_LIKELY (ret != GST_FLOW_UNEXPECTED && ret != GST_FLOW_NOT_LINKED))
4422       goto done;
4423
4424     /* we check to see if we have at least 1 unexpected or all unlinked */
4425     unexpected |= (ret == GST_FLOW_UNEXPECTED);
4426     not_linked &= (ret == GST_FLOW_NOT_LINKED);
4427   }
4428   /* when we get here, we all have unlinked or unexpected */
4429   if (not_linked)
4430     ret = GST_FLOW_NOT_LINKED;
4431   else if (unexpected)
4432     ret = GST_FLOW_UNEXPECTED;
4433 done:
4434   GST_LOG_OBJECT (avi, "combined %s to return %s",
4435       gst_flow_get_name (stream->last_flow), gst_flow_get_name (ret));
4436   return ret;
4437 }
4438
4439 /* move @stream to the next position in its index */
4440 static GstFlowReturn
4441 gst_avi_demux_advance (GstAviDemux * avi, GstAviStream * stream,
4442     GstFlowReturn ret)
4443 {
4444   guint old_entry, new_entry;
4445
4446   old_entry = stream->current_entry;
4447   /* move forwards */
4448   new_entry = old_entry + 1;
4449
4450   /* see if we reached the end */
4451   if (new_entry > stream->stop_entry) {
4452     if (avi->segment.rate < 0.0) {
4453       if (stream->step_entry == stream->start_entry) {
4454         /* we stepped all the way to the start, eos */
4455         GST_DEBUG_OBJECT (avi, "reverse reached start %u", stream->start_entry);
4456         goto eos;
4457       }
4458       /* backwards, stop becomes step, find a new step */
4459       stream->stop_entry = stream->step_entry;
4460       stream->step_entry = gst_avi_demux_index_prev (avi, stream,
4461           stream->stop_entry, TRUE);
4462
4463       GST_DEBUG_OBJECT (avi,
4464           "reverse playback jump: start %u, step %u, stop %u",
4465           stream->start_entry, stream->step_entry, stream->stop_entry);
4466
4467       /* and start from the previous keyframe now */
4468       new_entry = stream->step_entry;
4469     } else {
4470       /* EOS */
4471       GST_DEBUG_OBJECT (avi, "forward reached stop %u", stream->stop_entry);
4472       goto eos;
4473     }
4474   }
4475
4476   if (new_entry != old_entry) {
4477     stream->current_entry = new_entry;
4478     stream->current_total = stream->index[new_entry].total;
4479
4480     if (new_entry == old_entry + 1) {
4481       GST_DEBUG_OBJECT (avi, "moved forwards from %u to %u",
4482           old_entry, new_entry);
4483       /* we simply moved one step forwards, reuse current info */
4484       stream->current_timestamp = stream->current_ts_end;
4485       stream->current_offset = stream->current_offset_end;
4486       gst_avi_demux_get_buffer_info (avi, stream, new_entry,
4487           NULL, &stream->current_ts_end, NULL, &stream->current_offset_end);
4488     } else {
4489       /* we moved DISCONT, full update */
4490       gst_avi_demux_get_buffer_info (avi, stream, new_entry,
4491           &stream->current_timestamp, &stream->current_ts_end,
4492           &stream->current_offset, &stream->current_offset_end);
4493       /* and MARK discont for this stream */
4494       stream->last_flow = GST_FLOW_OK;
4495       stream->discont = TRUE;
4496       GST_DEBUG_OBJECT (avi, "Moved from %u to %u, ts %" GST_TIME_FORMAT
4497           ", ts_end %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4498           ", off_end %" G_GUINT64_FORMAT, old_entry, new_entry,
4499           GST_TIME_ARGS (stream->current_timestamp),
4500           GST_TIME_ARGS (stream->current_ts_end), stream->current_offset,
4501           stream->current_offset_end);
4502     }
4503   }
4504   return ret;
4505
4506   /* ERROR */
4507 eos:
4508   {
4509     GST_DEBUG_OBJECT (avi, "we are EOS");
4510     /* setting current_timestamp to -1 marks EOS */
4511     stream->current_timestamp = -1;
4512     return GST_FLOW_UNEXPECTED;
4513   }
4514 }
4515
4516 /* find the stream with the lowest current position when going forwards or with
4517  * the highest position when going backwards, this is the stream
4518  * we should push from next */
4519 static gint
4520 gst_avi_demux_find_next (GstAviDemux * avi, gfloat rate)
4521 {
4522   guint64 min_time, max_time;
4523   guint stream_num, i;
4524
4525   max_time = 0;
4526   min_time = G_MAXUINT64;
4527   stream_num = -1;
4528
4529   for (i = 0; i < avi->num_streams; i++) {
4530     guint64 position;
4531     GstAviStream *stream;
4532
4533     stream = &avi->stream[i];
4534
4535     /* ignore streams that finished */
4536     if (stream->last_flow == GST_FLOW_UNEXPECTED)
4537       continue;
4538
4539     position = stream->current_timestamp;
4540
4541     /* position of -1 is EOS */
4542     if (position != -1) {
4543       if (rate > 0.0 && position < min_time) {
4544         min_time = position;
4545         stream_num = i;
4546       } else if (rate < 0.0 && position >= max_time) {
4547         max_time = position;
4548         stream_num = i;
4549       }
4550     }
4551   }
4552   return stream_num;
4553 }
4554
4555 static GstFlowReturn
4556 gst_avi_demux_loop_data (GstAviDemux * avi)
4557 {
4558   GstFlowReturn ret = GST_FLOW_OK;
4559   guint stream_num;
4560   GstAviStream *stream;
4561   gboolean processed = FALSE;
4562   GstBuffer *buf;
4563   guint64 offset, size;
4564   GstClockTime timestamp, duration;
4565   guint64 out_offset, out_offset_end;
4566   gboolean keyframe;
4567   GstAviIndexEntry *entry;
4568
4569   do {
4570     stream_num = gst_avi_demux_find_next (avi, avi->segment.rate);
4571
4572     /* all are EOS */
4573     if (G_UNLIKELY (stream_num == -1)) {
4574       GST_DEBUG_OBJECT (avi, "all streams are EOS");
4575       goto eos;
4576     }
4577
4578     /* we have the stream now */
4579     stream = &avi->stream[stream_num];
4580
4581     /* skip streams without pads */
4582     if (!stream->pad) {
4583       GST_DEBUG_OBJECT (avi, "skipping entry from stream %d without pad",
4584           stream_num);
4585       goto next;
4586     }
4587
4588     /* get the timing info for the entry */
4589     timestamp = stream->current_timestamp;
4590     duration = stream->current_ts_end - timestamp;
4591     out_offset = stream->current_offset;
4592     out_offset_end = stream->current_offset_end;
4593
4594     /* get the entry data info */
4595     entry = &stream->index[stream->current_entry];
4596     offset = entry->offset;
4597     size = entry->size;
4598     keyframe = ENTRY_IS_KEYFRAME (entry);
4599
4600     /* skip empty entries */
4601     if (size == 0) {
4602       GST_DEBUG_OBJECT (avi, "Skipping entry %u (%" G_GUINT64_FORMAT ", %p)",
4603           stream->current_entry, size, stream->pad);
4604       goto next;
4605     }
4606
4607     if (avi->segment.rate > 0.0) {
4608       /* only check this for fowards playback for now */
4609       if (keyframe && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
4610           && (timestamp > avi->segment.stop)) {
4611         goto eos_stop;
4612       }
4613     }
4614
4615     GST_LOG ("reading buffer (size=%" G_GUINT64_FORMAT "), stream %d, pos %"
4616         G_GUINT64_FORMAT " (0x%" G_GINT64_MODIFIER "x), kf %d", size,
4617         stream_num, offset, offset, keyframe);
4618
4619     /* FIXME, check large chunks and cut them up */
4620
4621     /* pull in the data */
4622     ret = gst_pad_pull_range (avi->sinkpad, offset, size, &buf);
4623     if (ret != GST_FLOW_OK)
4624       goto pull_failed;
4625
4626     /* check for short buffers, this is EOS as well */
4627     if (GST_BUFFER_SIZE (buf) < size)
4628       goto short_buffer;
4629
4630     /* invert the picture if needed */
4631     buf = gst_avi_demux_invert (stream, buf);
4632
4633     /* mark non-keyframes */
4634     if (keyframe)
4635       GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
4636     else
4637       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
4638
4639     GST_BUFFER_TIMESTAMP (buf) = timestamp;
4640     GST_BUFFER_DURATION (buf) = duration;
4641     GST_BUFFER_OFFSET (buf) = out_offset;
4642     GST_BUFFER_OFFSET_END (buf) = out_offset_end;
4643
4644     /* mark discont when pending */
4645     if (stream->discont) {
4646       GST_DEBUG_OBJECT (avi, "setting DISCONT flag");
4647       GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
4648       stream->discont = FALSE;
4649     }
4650
4651     gst_avi_demux_add_assoc (avi, stream, timestamp, offset, keyframe);
4652
4653     gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
4654
4655     /* update current position in the segment */
4656     gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, timestamp);
4657
4658     GST_DEBUG_OBJECT (avi, "Pushing buffer of size %u, ts %"
4659         GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT ", off %" G_GUINT64_FORMAT
4660         ", off_end %" G_GUINT64_FORMAT,
4661         GST_BUFFER_SIZE (buf), GST_TIME_ARGS (timestamp),
4662         GST_TIME_ARGS (duration), out_offset, out_offset_end);
4663
4664     ret = gst_pad_push (stream->pad, buf);
4665
4666     /* mark as processed, we increment the frame and byte counters then
4667      * leave the while loop and return the GstFlowReturn */
4668     processed = TRUE;
4669
4670     if (avi->segment.rate < 0) {
4671       if (timestamp > avi->segment.stop && ret == GST_FLOW_UNEXPECTED) {
4672         /* In reverse playback we can get a GST_FLOW_UNEXPECTED when
4673          * we are at the end of the segment, so we just need to jump
4674          * back to the previous section. */
4675         GST_DEBUG_OBJECT (avi, "downstream has reached end of segment");
4676         ret = GST_FLOW_OK;
4677       }
4678     }
4679   next:
4680     /* move to next item */
4681     ret = gst_avi_demux_advance (avi, stream, ret);
4682
4683     /* combine flows */
4684     ret = gst_avi_demux_combine_flows (avi, stream, ret);
4685   } while (!processed);
4686
4687 beach:
4688   return ret;
4689
4690   /* special cases */
4691 eos:
4692   {
4693     GST_DEBUG_OBJECT (avi, "No samples left for any streams - EOS");
4694     ret = GST_FLOW_UNEXPECTED;
4695     goto beach;
4696   }
4697 eos_stop:
4698   {
4699     GST_LOG_OBJECT (avi, "Found keyframe after segment,"
4700         " setting EOS (%" GST_TIME_FORMAT " > %" GST_TIME_FORMAT ")",
4701         GST_TIME_ARGS (timestamp), GST_TIME_ARGS (avi->segment.stop));
4702     ret = GST_FLOW_UNEXPECTED;
4703     /* move to next stream */
4704     goto next;
4705   }
4706 pull_failed:
4707   {
4708     GST_DEBUG_OBJECT (avi, "pull range failed: pos=%" G_GUINT64_FORMAT
4709         " size=%" G_GUINT64_FORMAT, offset, size);
4710     goto beach;
4711   }
4712 short_buffer:
4713   {
4714     GST_WARNING_OBJECT (avi, "Short read at offset %" G_GUINT64_FORMAT
4715         ", only got %d/%" G_GUINT64_FORMAT " bytes (truncated file?)", offset,
4716         GST_BUFFER_SIZE (buf), size);
4717     gst_buffer_unref (buf);
4718     ret = GST_FLOW_UNEXPECTED;
4719     goto beach;
4720   }
4721 }
4722
4723 /*
4724  * Read data. If we have an index it delegates to
4725  * gst_avi_demux_process_next_entry().
4726  */
4727 static GstFlowReturn
4728 gst_avi_demux_stream_data (GstAviDemux * avi)
4729 {
4730   guint32 tag = 0;
4731   guint32 size = 0;
4732   gint stream_nr = 0;
4733   GstFlowReturn res = GST_FLOW_OK;
4734   GstFormat format = GST_FORMAT_TIME;
4735
4736   if (G_UNLIKELY (avi->have_eos)) {
4737     /* Clean adapter, we're done */
4738     gst_adapter_clear (avi->adapter);
4739     return GST_FLOW_UNEXPECTED;
4740   }
4741
4742   if (G_UNLIKELY (avi->todrop)) {
4743     guint drop;
4744
4745     if ((drop = gst_adapter_available (avi->adapter))) {
4746       if (drop > avi->todrop)
4747         drop = avi->todrop;
4748       GST_DEBUG_OBJECT (avi, "Dropping %d bytes", drop);
4749       gst_adapter_flush (avi->adapter, drop);
4750       avi->todrop -= drop;
4751       avi->offset += drop;
4752     }
4753   }
4754
4755   /* Iterate until need more data, so adapter won't grow too much */
4756   while (1) {
4757     if (G_UNLIKELY (!gst_avi_demux_peek_chunk_info (avi, &tag, &size))) {
4758       return GST_FLOW_OK;
4759     }
4760
4761     GST_DEBUG ("Trying chunk (%" GST_FOURCC_FORMAT "), size %d",
4762         GST_FOURCC_ARGS (tag), size);
4763
4764     if (G_LIKELY ((tag & 0xff) >= '0' && (tag & 0xff) <= '9' &&
4765             ((tag >> 8) & 0xff) >= '0' && ((tag >> 8) & 0xff) <= '9')) {
4766       GST_LOG ("Chunk ok");
4767     } else if ((tag & 0xffff) == (('x' << 8) | 'i')) {
4768       GST_DEBUG ("Found sub-index tag");
4769       if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
4770         /* accept 0 size buffer here */
4771         avi->abort_buffering = FALSE;
4772         GST_DEBUG ("  skipping %d bytes for now", size);
4773         gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
4774       }
4775       return GST_FLOW_OK;
4776     } else if (tag == GST_RIFF_TAG_RIFF) {
4777       /* RIFF tags can appear in ODML files, just jump over them */
4778       if (gst_adapter_available (avi->adapter) >= 12) {
4779         GST_DEBUG ("Found RIFF tag, skipping RIFF header");
4780         gst_adapter_flush (avi->adapter, 12);
4781         continue;
4782       }
4783       return GST_FLOW_OK;
4784     } else if (tag == GST_RIFF_TAG_idx1) {
4785       GST_DEBUG ("Found index tag");
4786       if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
4787         /* accept 0 size buffer here */
4788         avi->abort_buffering = FALSE;
4789         GST_DEBUG ("  skipping %d bytes for now", size);
4790         gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
4791       }
4792       return GST_FLOW_OK;
4793     } else if (tag == GST_RIFF_TAG_LIST) {
4794       /* movi chunks might be grouped in rec list */
4795       if (gst_adapter_available (avi->adapter) >= 12) {
4796         GST_DEBUG ("Found LIST tag, skipping LIST header");
4797         gst_adapter_flush (avi->adapter, 12);
4798         continue;
4799       }
4800       return GST_FLOW_OK;
4801     } else if (tag == GST_RIFF_TAG_JUNK || tag == GST_RIFF_TAG_JUNQ) {
4802       /* rec list might contain JUNK chunks */
4803       GST_DEBUG ("Found JUNK tag");
4804       if (gst_avi_demux_peek_chunk (avi, &tag, &size) || size == 0) {
4805         /* accept 0 size buffer here */
4806         avi->abort_buffering = FALSE;
4807         GST_DEBUG ("  skipping %d bytes for now", size);
4808         gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
4809       }
4810       return GST_FLOW_OK;
4811     } else {
4812       GST_DEBUG ("No more stream chunks, send EOS");
4813       avi->have_eos = TRUE;
4814       return GST_FLOW_UNEXPECTED;
4815     }
4816
4817     if (G_UNLIKELY (!gst_avi_demux_peek_chunk (avi, &tag, &size))) {
4818       /* supposedly one hopes to catch a nicer chunk later on ... */
4819       /* FIXME ?? give up here rather than possibly ending up going
4820        * through the whole file */
4821       if (avi->abort_buffering) {
4822         avi->abort_buffering = FALSE;
4823         gst_adapter_flush (avi->adapter, 8);
4824       }
4825       return GST_FLOW_OK;
4826     }
4827     GST_DEBUG ("chunk ID %" GST_FOURCC_FORMAT ", size %u",
4828         GST_FOURCC_ARGS (tag), size);
4829
4830     stream_nr = CHUNKID_TO_STREAMNR (tag);
4831
4832     if (G_UNLIKELY (stream_nr < 0 || stream_nr >= avi->num_streams)) {
4833       /* recoverable */
4834       GST_WARNING ("Invalid stream ID %d (%" GST_FOURCC_FORMAT ")",
4835           stream_nr, GST_FOURCC_ARGS (tag));
4836       avi->offset += 8 + GST_ROUND_UP_2 (size);
4837       gst_adapter_flush (avi->adapter, 8 + GST_ROUND_UP_2 (size));
4838     } else {
4839       GstAviStream *stream;
4840       GstClockTime next_ts = 0;
4841       GstBuffer *buf;
4842       guint64 offset;
4843
4844       gst_adapter_flush (avi->adapter, 8);
4845
4846       /* get buffer */
4847       buf = gst_adapter_take_buffer (avi->adapter, GST_ROUND_UP_2 (size));
4848       /* patch the size */
4849       GST_BUFFER_SIZE (buf) = size;
4850       offset = avi->offset;
4851       avi->offset += 8 + GST_ROUND_UP_2 (size);
4852
4853       stream = &avi->stream[stream_nr];
4854
4855       /* set delay (if any)
4856          if (stream->strh->init_frames == stream->current_frame &&
4857          stream->delay == 0)
4858          stream->delay = next_ts;
4859        */
4860
4861       /* parsing of corresponding header may have failed */
4862       if (G_UNLIKELY (!stream->pad)) {
4863         GST_WARNING_OBJECT (avi, "no pad for stream ID %" GST_FOURCC_FORMAT,
4864             GST_FOURCC_ARGS (tag));
4865         gst_buffer_unref (buf);
4866       } else {
4867         GstClockTime dur_ts = 0;
4868
4869         /* get time of this buffer */
4870         gst_pad_query_position (stream->pad, &format, (gint64 *) & next_ts);
4871         if (G_UNLIKELY (format != GST_FORMAT_TIME))
4872           goto wrong_format;
4873
4874         gst_avi_demux_add_assoc (avi, stream, next_ts, offset, FALSE);
4875
4876         /* increment our positions */
4877         stream->current_entry++;
4878         stream->current_total += size;
4879
4880         /* invert the picture if needed */
4881         buf = gst_avi_demux_invert (stream, buf);
4882
4883         gst_pad_query_position (stream->pad, &format, (gint64 *) & dur_ts);
4884         if (G_UNLIKELY (format != GST_FORMAT_TIME))
4885           goto wrong_format;
4886
4887         GST_BUFFER_TIMESTAMP (buf) = next_ts;
4888         GST_BUFFER_DURATION (buf) = dur_ts - next_ts;
4889         if (stream->strh->type == GST_RIFF_FCC_vids) {
4890           GST_BUFFER_OFFSET (buf) = stream->current_entry - 1;
4891           GST_BUFFER_OFFSET_END (buf) = stream->current_entry;
4892         } else {
4893           GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
4894           GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
4895         }
4896
4897         gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
4898         GST_DEBUG_OBJECT (avi,
4899             "Pushing buffer with time=%" GST_TIME_FORMAT ", duration %"
4900             GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT
4901             " and size %d over pad %s", GST_TIME_ARGS (next_ts),
4902             GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
4903             size, GST_PAD_NAME (stream->pad));
4904
4905         /* update current position in the segment */
4906         gst_segment_set_last_stop (&avi->segment, GST_FORMAT_TIME, next_ts);
4907
4908         /* mark discont when pending */
4909         if (G_UNLIKELY (stream->discont)) {
4910           GST_DEBUG_OBJECT (avi, "Setting DISCONT");
4911           GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
4912           stream->discont = FALSE;
4913         }
4914         res = gst_pad_push (stream->pad, buf);
4915
4916         /* combine flows */
4917         res = gst_avi_demux_combine_flows (avi, stream, res);
4918         if (G_UNLIKELY (res != GST_FLOW_OK)) {
4919           GST_DEBUG ("Push failed; %s", gst_flow_get_name (res));
4920           return res;
4921         }
4922       }
4923     }
4924   }
4925
4926 done:
4927   return res;
4928
4929   /* ERRORS */
4930 wrong_format:
4931   {
4932     GST_DEBUG_OBJECT (avi, "format %s != GST_FORMAT_TIME",
4933         gst_format_get_name (format));
4934     res = GST_FLOW_ERROR;
4935     goto done;
4936   }
4937 }
4938
4939 /*
4940  * Send pending tags.
4941  */
4942 static void
4943 push_tag_lists (GstAviDemux * avi)
4944 {
4945   guint i;
4946   GstTagList *tags;
4947
4948   if (!avi->got_tags)
4949     return;
4950
4951   GST_DEBUG_OBJECT (avi, "Pushing pending tag lists");
4952
4953   for (i = 0; i < avi->num_streams; i++) {
4954     GstAviStream *stream = &avi->stream[i];
4955     GstPad *pad = stream->pad;
4956
4957     tags = stream->taglist;
4958
4959     if (pad && tags) {
4960       GST_DEBUG_OBJECT (pad, "Tags: %" GST_PTR_FORMAT, tags);
4961
4962       gst_element_found_tags_for_pad (GST_ELEMENT_CAST (avi), pad, tags);
4963       stream->taglist = NULL;
4964     }
4965   }
4966
4967   if (!(tags = avi->globaltags))
4968     tags = gst_tag_list_new ();
4969
4970   gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
4971       GST_TAG_CONTAINER_FORMAT, "AVI", NULL);
4972
4973   GST_DEBUG_OBJECT (avi, "Global tags: %" GST_PTR_FORMAT, tags);
4974   gst_element_found_tags (GST_ELEMENT_CAST (avi), tags);
4975   avi->globaltags = NULL;
4976   avi->got_tags = FALSE;
4977 }
4978
4979 static void
4980 gst_avi_demux_loop (GstPad * pad)
4981 {
4982   GstFlowReturn res;
4983   GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
4984
4985   switch (avi->state) {
4986     case GST_AVI_DEMUX_START:
4987       res = gst_avi_demux_stream_init_pull (avi);
4988       if (G_UNLIKELY (res != GST_FLOW_OK)) {
4989         GST_WARNING ("stream_init flow: %s", gst_flow_get_name (res));
4990         goto pause;
4991       }
4992       avi->state = GST_AVI_DEMUX_HEADER;
4993       /* fall-through */
4994     case GST_AVI_DEMUX_HEADER:
4995       res = gst_avi_demux_stream_header_pull (avi);
4996       if (G_UNLIKELY (res != GST_FLOW_OK)) {
4997         GST_WARNING ("stream_header flow: %s", gst_flow_get_name (res));
4998         goto pause;
4999       }
5000       avi->state = GST_AVI_DEMUX_MOVI;
5001       break;
5002     case GST_AVI_DEMUX_MOVI:
5003       if (G_UNLIKELY (avi->seg_event)) {
5004         gst_avi_demux_push_event (avi, avi->seg_event);
5005         avi->seg_event = NULL;
5006       }
5007       if (G_UNLIKELY (avi->got_tags)) {
5008         push_tag_lists (avi);
5009       }
5010       /* process each index entry in turn */
5011       res = gst_avi_demux_loop_data (avi);
5012
5013       /* pause when error */
5014       if (G_UNLIKELY (res != GST_FLOW_OK)) {
5015         GST_INFO ("stream_movi flow: %s", gst_flow_get_name (res));
5016         goto pause;
5017       }
5018       break;
5019     default:
5020       GST_ERROR_OBJECT (avi, "unknown state %d", avi->state);
5021       res = GST_FLOW_ERROR;
5022       goto pause;
5023   }
5024
5025   return;
5026
5027   /* ERRORS */
5028 pause:
5029   GST_LOG_OBJECT (avi, "pausing task, reason %s", gst_flow_get_name (res));
5030   avi->segment_running = FALSE;
5031   gst_pad_pause_task (avi->sinkpad);
5032
5033   if (GST_FLOW_IS_FATAL (res) || (res == GST_FLOW_NOT_LINKED)) {
5034     gboolean push_eos = TRUE;
5035
5036     if (res == GST_FLOW_UNEXPECTED) {
5037       /* handle end-of-stream/segment */
5038       if (avi->segment.flags & GST_SEEK_FLAG_SEGMENT) {
5039         gint64 stop;
5040
5041         if ((stop = avi->segment.stop) == -1)
5042           stop = avi->segment.duration;
5043
5044         GST_INFO_OBJECT (avi, "sending segment_done");
5045
5046         gst_element_post_message
5047             (GST_ELEMENT_CAST (avi),
5048             gst_message_new_segment_done (GST_OBJECT_CAST (avi),
5049                 GST_FORMAT_TIME, stop));
5050         push_eos = FALSE;
5051       }
5052     } else {
5053       /* for fatal errors we post an error message */
5054       GST_ELEMENT_ERROR (avi, STREAM, FAILED,
5055           (_("Internal data stream error.")),
5056           ("streaming stopped, reason %s", gst_flow_get_name (res)));
5057     }
5058     if (push_eos) {
5059       GST_INFO_OBJECT (avi, "sending eos");
5060       if (!gst_avi_demux_push_event (avi, gst_event_new_eos ()) &&
5061           (res == GST_FLOW_UNEXPECTED)) {
5062         GST_ELEMENT_ERROR (avi, STREAM, DEMUX,
5063             (NULL), ("got eos but no streams (yet)"));
5064       }
5065     }
5066   }
5067 }
5068
5069
5070 static GstFlowReturn
5071 gst_avi_demux_chain (GstPad * pad, GstBuffer * buf)
5072 {
5073   GstFlowReturn res;
5074   GstAviDemux *avi = GST_AVI_DEMUX (GST_PAD_PARENT (pad));
5075   gint i;
5076
5077   if (GST_BUFFER_IS_DISCONT (buf)) {
5078     GST_DEBUG_OBJECT (avi, "got DISCONT");
5079     gst_adapter_clear (avi->adapter);
5080     /* mark all streams DISCONT */
5081     for (i = 0; i < avi->num_streams; i++)
5082       avi->stream[i].discont = TRUE;
5083   }
5084
5085   GST_DEBUG ("Store %d bytes in adapter", GST_BUFFER_SIZE (buf));
5086   gst_adapter_push (avi->adapter, buf);
5087
5088   switch (avi->state) {
5089     case GST_AVI_DEMUX_START:
5090       if ((res = gst_avi_demux_stream_init_push (avi)) != GST_FLOW_OK) {
5091         GST_WARNING ("stream_init flow: %s", gst_flow_get_name (res));
5092         break;
5093       }
5094       break;
5095     case GST_AVI_DEMUX_HEADER:
5096       if ((res = gst_avi_demux_stream_header_push (avi)) != GST_FLOW_OK) {
5097         GST_WARNING ("stream_header flow: %s", gst_flow_get_name (res));
5098         break;
5099       }
5100       break;
5101     case GST_AVI_DEMUX_MOVI:
5102       if (G_UNLIKELY (avi->seg_event)) {
5103         gst_avi_demux_push_event (avi, avi->seg_event);
5104         avi->seg_event = NULL;
5105       }
5106       if (G_UNLIKELY (avi->got_tags)) {
5107         push_tag_lists (avi);
5108       }
5109       res = gst_avi_demux_stream_data (avi);
5110       break;
5111     case GST_AVI_DEMUX_SEEK:
5112     {
5113       GstEvent *event;
5114
5115       res = GST_FLOW_OK;
5116
5117       /* obtain and parse indexes */
5118       if (avi->stream[0].indexes && !gst_avi_demux_read_subindexes_push (avi))
5119         /* seek in subindex read function failed */
5120         goto index_failed;
5121
5122       if (!avi->stream[0].indexes && !avi->have_index
5123           && avi->avih->flags & GST_RIFF_AVIH_HASINDEX)
5124         gst_avi_demux_stream_index_push (avi);
5125
5126       if (avi->have_index) {
5127         /* use the indexes now to construct nice durations */
5128         gst_avi_demux_calculate_durations_from_index (avi);
5129       } else {
5130         /* still parsing indexes */
5131         break;
5132       }
5133
5134       GST_OBJECT_LOCK (avi);
5135       event = avi->seek_event;
5136       avi->seek_event = NULL;
5137       GST_OBJECT_UNLOCK (avi);
5138
5139       /* calculate and perform seek */
5140       if (!avi_demux_handle_seek_push (avi, avi->sinkpad, event))
5141         goto seek_failed;
5142
5143       gst_event_unref (event);
5144       avi->state = GST_AVI_DEMUX_MOVI;
5145       break;
5146     }
5147     default:
5148       GST_ELEMENT_ERROR (avi, STREAM, FAILED, (NULL),
5149           ("Illegal internal state"));
5150       res = GST_FLOW_ERROR;
5151       break;
5152   }
5153
5154   GST_DEBUG_OBJECT (avi, "state: %d res:%s", avi->state,
5155       gst_flow_get_name (res));
5156
5157   if (G_UNLIKELY (avi->abort_buffering))
5158     goto abort_buffering;
5159
5160   return res;
5161
5162   /* ERRORS */
5163 index_failed:
5164   {
5165     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("failed to read indexes"));
5166     return GST_FLOW_ERROR;
5167   }
5168 seek_failed:
5169   {
5170     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("push mode seek failed"));
5171     return GST_FLOW_ERROR;
5172   }
5173 abort_buffering:
5174   {
5175     avi->abort_buffering = FALSE;
5176     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("unhandled buffer size"));
5177     return GST_FLOW_ERROR;
5178   }
5179 }
5180
5181 static gboolean
5182 gst_avi_demux_sink_activate (GstPad * sinkpad)
5183 {
5184   if (gst_pad_check_pull_range (sinkpad)) {
5185     GST_DEBUG ("going to pull mode");
5186     return gst_pad_activate_pull (sinkpad, TRUE);
5187   } else {
5188     GST_DEBUG ("going to push (streaming) mode");
5189     return gst_pad_activate_push (sinkpad, TRUE);
5190   }
5191 }
5192
5193 static gboolean
5194 gst_avi_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
5195 {
5196   GstAviDemux *avi = GST_AVI_DEMUX (GST_OBJECT_PARENT (sinkpad));
5197
5198   if (active) {
5199     avi->segment_running = TRUE;
5200     avi->streaming = FALSE;
5201     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_avi_demux_loop,
5202         sinkpad);
5203   } else {
5204     avi->segment_running = FALSE;
5205     return gst_pad_stop_task (sinkpad);
5206   }
5207 }
5208
5209 static gboolean
5210 gst_avi_demux_activate_push (GstPad * pad, gboolean active)
5211 {
5212   GstAviDemux *avi = GST_AVI_DEMUX (GST_OBJECT_PARENT (pad));
5213
5214   if (active) {
5215     GST_DEBUG ("avi: activating push/chain function");
5216     avi->streaming = TRUE;
5217 #if 0
5218     /* create index for some push based seeking if not provided */
5219     GST_OBJECT_LOCK (avi);
5220     if (!avi->element_index) {
5221       GST_DEBUG_OBJECT (avi, "creating index");
5222       avi->element_index = gst_index_factory_make ("memindex");
5223     }
5224     GST_OBJECT_UNLOCK (avi);
5225     /* object lock might be taken again */
5226     gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (avi),
5227         &avi->index_id);
5228 #endif
5229   } else {
5230     GST_DEBUG ("avi: deactivating push/chain function");
5231   }
5232
5233   return TRUE;
5234 }
5235
5236 static void
5237 gst_avi_demux_set_index (GstElement * element, GstIndex * index)
5238 {
5239   GstAviDemux *avi = GST_AVI_DEMUX (element);
5240
5241   GST_OBJECT_LOCK (avi);
5242   if (avi->element_index)
5243     gst_object_unref (avi->element_index);
5244   if (index) {
5245     avi->element_index = gst_object_ref (index);
5246   } else {
5247     avi->element_index = NULL;
5248   }
5249   GST_OBJECT_UNLOCK (avi);
5250   /* object lock might be taken again */
5251   if (index)
5252     gst_index_get_writer_id (index, GST_OBJECT_CAST (element), &avi->index_id);
5253   GST_DEBUG_OBJECT (avi, "Set index %" GST_PTR_FORMAT, avi->element_index);
5254 }
5255
5256 static GstIndex *
5257 gst_avi_demux_get_index (GstElement * element)
5258 {
5259   GstIndex *result = NULL;
5260   GstAviDemux *avi = GST_AVI_DEMUX (element);
5261
5262   GST_OBJECT_LOCK (avi);
5263   if (avi->element_index)
5264     result = gst_object_ref (avi->element_index);
5265   GST_OBJECT_UNLOCK (avi);
5266
5267   GST_DEBUG_OBJECT (avi, "Returning index %" GST_PTR_FORMAT, result);
5268
5269   return result;
5270 }
5271
5272 static GstStateChangeReturn
5273 gst_avi_demux_change_state (GstElement * element, GstStateChange transition)
5274 {
5275   GstStateChangeReturn ret;
5276   GstAviDemux *avi = GST_AVI_DEMUX (element);
5277
5278   switch (transition) {
5279     case GST_STATE_CHANGE_READY_TO_PAUSED:
5280       avi->streaming = FALSE;
5281       gst_segment_init (&avi->segment, GST_FORMAT_TIME);
5282       break;
5283     default:
5284       break;
5285   }
5286
5287   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5288   if (ret == GST_STATE_CHANGE_FAILURE)
5289     goto done;
5290
5291   switch (transition) {
5292     case GST_STATE_CHANGE_PAUSED_TO_READY:
5293       gst_avi_demux_reset (avi);
5294       break;
5295     default:
5296       break;
5297   }
5298
5299 done:
5300   return ret;
5301 }