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