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