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