avidemux: rename some variable
[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_CAST (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_CAST (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->seg_event) {
283     gst_event_unref (avi->seg_event);
284     avi->seg_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_CAST (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  * @avi: 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 (GstAviDemux * avi,
1017     GstBuffer * buf, gst_riff_avih ** _avih)
1018 {
1019   gst_riff_avih *avih;
1020
1021   if (buf == NULL)
1022     goto no_buffer;
1023
1024   if (GST_BUFFER_SIZE (buf) < sizeof (gst_riff_avih))
1025     goto avih_too_small;
1026
1027   avih = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1028
1029 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1030   avih->us_frame = GUINT32_FROM_LE (avih->us_frame);
1031   avih->max_bps = GUINT32_FROM_LE (avih->max_bps);
1032   avih->pad_gran = GUINT32_FROM_LE (avih->pad_gran);
1033   avih->flags = GUINT32_FROM_LE (avih->flags);
1034   avih->tot_frames = GUINT32_FROM_LE (avih->tot_frames);
1035   avih->init_frames = GUINT32_FROM_LE (avih->init_frames);
1036   avih->streams = GUINT32_FROM_LE (avih->streams);
1037   avih->bufsize = GUINT32_FROM_LE (avih->bufsize);
1038   avih->width = GUINT32_FROM_LE (avih->width);
1039   avih->height = GUINT32_FROM_LE (avih->height);
1040   avih->scale = GUINT32_FROM_LE (avih->scale);
1041   avih->rate = GUINT32_FROM_LE (avih->rate);
1042   avih->start = GUINT32_FROM_LE (avih->start);
1043   avih->length = GUINT32_FROM_LE (avih->length);
1044 #endif
1045
1046   /* debug stuff */
1047   GST_INFO_OBJECT (avi, "avih tag found:");
1048   GST_INFO_OBJECT (avi, " us_frame    %u", avih->us_frame);
1049   GST_INFO_OBJECT (avi, " max_bps     %u", avih->max_bps);
1050   GST_INFO_OBJECT (avi, " pad_gran    %u", avih->pad_gran);
1051   GST_INFO_OBJECT (avi, " flags       0x%08x", avih->flags);
1052   GST_INFO_OBJECT (avi, " tot_frames  %u", avih->tot_frames);
1053   GST_INFO_OBJECT (avi, " init_frames %u", avih->init_frames);
1054   GST_INFO_OBJECT (avi, " streams     %u", avih->streams);
1055   GST_INFO_OBJECT (avi, " bufsize     %u", avih->bufsize);
1056   GST_INFO_OBJECT (avi, " width       %u", avih->width);
1057   GST_INFO_OBJECT (avi, " height      %u", avih->height);
1058   GST_INFO_OBJECT (avi, " scale       %u", avih->scale);
1059   GST_INFO_OBJECT (avi, " rate        %u", avih->rate);
1060   GST_INFO_OBJECT (avi, " start       %u", avih->start);
1061   GST_INFO_OBJECT (avi, " length      %u", avih->length);
1062
1063   *_avih = avih;
1064   gst_buffer_unref (buf);
1065
1066   if (avih->us_frame != 0 && avih->tot_frames != 0)
1067     avi->duration =
1068         (guint64) avih->us_frame * (guint64) avih->tot_frames * 1000;
1069   else
1070     avi->duration = GST_CLOCK_TIME_NONE;
1071
1072   GST_INFO_OBJECT (avi, " header duration  %" GST_TIME_FORMAT,
1073       GST_TIME_ARGS (avi->duration));
1074
1075   return TRUE;
1076
1077   /* ERRORS */
1078 no_buffer:
1079   {
1080     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL), ("No buffer"));
1081     return FALSE;
1082   }
1083 avih_too_small:
1084   {
1085     GST_ELEMENT_ERROR (avi, STREAM, DEMUX, (NULL),
1086         ("Too small avih (%d available, %d needed)",
1087             GST_BUFFER_SIZE (buf), (int) sizeof (gst_riff_avih)));
1088     gst_buffer_unref (buf);
1089     return FALSE;
1090   }
1091 }
1092
1093 /*
1094  * gst_avi_demux_parse_superindex:
1095  * @avi: caller element (used for debugging/errors).
1096  * @buf: input data to use for parsing.
1097  * @locations: locations in the file (byte-offsets) that contain
1098  *             the actual indexes (see get_avi_demux_parse_subindex()).
1099  *             The array ends with GST_BUFFER_OFFSET_NONE.
1100  *
1101  * Reads superindex (openDML-2 spec stuff) from the provided data.
1102  *
1103  * Returns: TRUE on success, FALSE otherwise. Indexes should be skipped
1104  *          on error, but they are not fatal.
1105  */
1106 static gboolean
1107 gst_avi_demux_parse_superindex (GstAviDemux * avi,
1108     GstBuffer * buf, guint64 ** _indexes)
1109 {
1110   guint8 *data;
1111   guint16 bpe = 16;
1112   guint32 num, i;
1113   guint64 *indexes;
1114   guint size;
1115
1116   *_indexes = NULL;
1117
1118   size = buf ? GST_BUFFER_SIZE (buf) : 0;
1119   if (size < 24)
1120     goto too_small;
1121
1122   data = GST_BUFFER_DATA (buf);
1123
1124   /* check type of index. The opendml2 specs state that
1125    * there should be 4 dwords per array entry. Type can be
1126    * either frame or field (and we don't care). */
1127   if (GST_READ_UINT16_LE (data) != 4 ||
1128       (data[2] & 0xfe) != 0x0 || data[3] != 0x0) {
1129     GST_WARNING_OBJECT (avi,
1130         "Superindex for stream has unexpected "
1131         "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1132         GST_READ_UINT16_LE (data), data[2], data[3]);
1133     bpe = GST_READ_UINT16_LE (data) * 4;
1134   }
1135   num = GST_READ_UINT32_LE (&data[4]);
1136
1137   GST_DEBUG_OBJECT (avi, "got %d indexes", num);
1138
1139   indexes = g_new (guint64, num + 1);
1140   for (i = 0; i < num; i++) {
1141     if (size < 24 + bpe * (i + 1))
1142       break;
1143     indexes[i] = GST_READ_UINT64_LE (&data[24 + bpe * i]);
1144     GST_DEBUG_OBJECT (avi, "index %d at %" G_GUINT64_FORMAT, i, indexes[i]);
1145   }
1146   indexes[i] = GST_BUFFER_OFFSET_NONE;
1147   *_indexes = indexes;
1148
1149   gst_buffer_unref (buf);
1150
1151   return TRUE;
1152
1153   /* ERRORS */
1154 too_small:
1155   {
1156     GST_ERROR_OBJECT (avi,
1157         "Not enough data to parse superindex (%d available, 24 needed)", size);
1158     if (buf)
1159       gst_buffer_unref (buf);
1160     return FALSE;
1161   }
1162 }
1163
1164 /* add an entry to the index of a stream. @num should be an estimate of the
1165  * total amount of index entries for all streams and is used to dynamically
1166  * allocate memory for the index entries. */
1167 static inline gboolean
1168 gst_avi_demux_add_index (GstAviDemux * avi, GstAviStream * stream,
1169     guint num, GstAviIndexEntry * entry)
1170 {
1171   /* ensure index memory */
1172   if (G_UNLIKELY (stream->idx_n >= stream->idx_max)) {
1173     guint idx_max = stream->idx_max;
1174     GstAviIndexEntry *new_idx;
1175
1176     /* we need to make some more room */
1177     if (idx_max == 0) {
1178       /* initial size guess, assume each stream has an equal amount of entries,
1179        * overshoot with at least 8K */
1180       idx_max = (num / avi->num_streams) + (8192 / sizeof (GstAviIndexEntry));
1181     } else {
1182       idx_max += 8192 / sizeof (GstAviIndexEntry);
1183       GST_DEBUG_OBJECT (avi, "expanded index from %u to %u",
1184           stream->idx_max, idx_max);
1185     }
1186     new_idx = g_try_renew (GstAviIndexEntry, stream->index, idx_max);
1187     /* out of memory, if this fails stream->index is untouched. */
1188     if (G_UNLIKELY (!new_idx))
1189       return FALSE;
1190     /* use new index */
1191     stream->index = new_idx;
1192     stream->idx_max = idx_max;
1193   }
1194
1195   /* update entry total and stream stats. The entry total can be converted to
1196    * the timestamp of the entry easily. */
1197   if (stream->strh->type == GST_RIFF_FCC_auds) {
1198     gint blockalign;
1199
1200     if (stream->is_vbr) {
1201       entry->total = stream->total_blocks;
1202     } else {
1203       entry->total = stream->total_bytes;
1204     }
1205     blockalign = stream->strf.auds->blockalign;
1206     if (blockalign > 0)
1207       stream->total_blocks += DIV_ROUND_UP (entry->size, blockalign);
1208     else
1209       stream->total_blocks++;
1210   } else {
1211     if (stream->is_vbr) {
1212       entry->total = stream->idx_n;
1213     } else {
1214       entry->total = stream->total_bytes;
1215     }
1216   }
1217   stream->total_bytes += entry->size;
1218   if (ENTRY_IS_KEYFRAME (entry))
1219     stream->n_keyframes++;
1220
1221   /* and add */
1222   GST_LOG_OBJECT (avi,
1223       "Adding stream %u, index entry %d, kf %d, size %u "
1224       ", offset %" G_GUINT64_FORMAT ", total %" G_GUINT64_FORMAT, stream->num,
1225       stream->idx_n, ENTRY_IS_KEYFRAME (entry), entry->size, entry->offset,
1226       entry->total);
1227   stream->index[stream->idx_n++] = *entry;
1228
1229   return TRUE;
1230 }
1231
1232 /* given @entry_n in @stream, calculate info such as timestamps and
1233  * offsets for the entry. */
1234 static void
1235 gst_avi_demux_get_buffer_info (GstAviDemux * avi, GstAviStream * stream,
1236     guint entry_n, GstClockTime * timestamp, GstClockTime * ts_end,
1237     guint64 * offset, guint64 * offset_end)
1238 {
1239   GstAviIndexEntry *entry;
1240
1241   entry = &stream->index[entry_n];
1242
1243   if (stream->is_vbr) {
1244     /* VBR stream next timestamp */
1245     if (stream->strh->type == GST_RIFF_FCC_auds) {
1246       if (timestamp)
1247         *timestamp =
1248             avi_stream_convert_frames_to_time_unchecked (stream, entry->total);
1249       if (ts_end)
1250         *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1251             entry->total + 1);
1252     } else {
1253       if (timestamp)
1254         *timestamp =
1255             avi_stream_convert_frames_to_time_unchecked (stream, entry_n);
1256       if (ts_end)
1257         *ts_end = avi_stream_convert_frames_to_time_unchecked (stream,
1258             entry_n + 1);
1259     }
1260   } else if (stream->strh->type == GST_RIFF_FCC_auds) {
1261     /* constant rate stream */
1262     if (timestamp)
1263       *timestamp =
1264           avi_stream_convert_bytes_to_time_unchecked (stream, entry->total);
1265     if (ts_end)
1266       *ts_end = avi_stream_convert_bytes_to_time_unchecked (stream,
1267           entry->total + entry->size);
1268   }
1269   if (stream->strh->type == GST_RIFF_FCC_vids) {
1270     /* video offsets are the frame number */
1271     if (offset)
1272       *offset = entry_n;
1273     if (offset_end)
1274       *offset_end = entry_n + 1;
1275   } else {
1276     /* no offsets for audio */
1277     if (offset)
1278       *offset = -1;
1279     if (offset_end)
1280       *offset_end = -1;
1281   }
1282 }
1283
1284 /* collect and debug stats about the indexes for all streams.
1285  * This method is also responsible for filling in the stream duration
1286  * as measured by the amount of index entries.
1287  *
1288  * Returns TRUE if the index is not empty, else FALSE */
1289 static gboolean
1290 gst_avi_demux_do_index_stats (GstAviDemux * avi)
1291 {
1292   guint total_idx = 0;
1293   guint i;
1294 #ifndef GST_DISABLE_GST_DEBUG
1295   guint total_max = 0;
1296 #endif
1297
1298   /* get stream stats now */
1299   for (i = 0; i < avi->num_streams; i++) {
1300     GstAviStream *stream;
1301
1302     if (G_UNLIKELY (!(stream = &avi->stream[i])))
1303       continue;
1304     if (G_UNLIKELY (!stream->strh))
1305       continue;
1306     if (G_UNLIKELY (!stream->index || stream->idx_n == 0))
1307       continue;
1308
1309     /* we interested in the end_ts of the last entry, which is the total
1310      * duration of this stream */
1311     gst_avi_demux_get_buffer_info (avi, stream, stream->idx_n - 1,
1312         NULL, &stream->idx_duration, NULL, NULL);
1313
1314     total_idx += stream->idx_n;
1315 #ifndef GST_DISABLE_GST_DEBUG
1316     total_max += stream->idx_max;
1317 #endif
1318     GST_INFO_OBJECT (avi, "Stream %d, dur %" GST_TIME_FORMAT ", %6u entries, "
1319         "%5u keyframes, entry size = %2u, total size = %10u, allocated %10u",
1320         i, GST_TIME_ARGS (stream->idx_duration), stream->idx_n,
1321         stream->n_keyframes, (guint) sizeof (GstAviIndexEntry),
1322         (guint) (stream->idx_n * sizeof (GstAviIndexEntry)),
1323         (guint) (stream->idx_max * sizeof (GstAviIndexEntry)));
1324   }
1325   total_idx *= sizeof (GstAviIndexEntry);
1326 #ifndef GST_DISABLE_GST_DEBUG
1327   total_max *= sizeof (GstAviIndexEntry);
1328 #endif
1329   GST_INFO_OBJECT (avi, "%u bytes for index vs %u ideally, %u wasted",
1330       total_max, total_idx, total_max - total_idx);
1331
1332   if (total_idx == 0) {
1333     GST_WARNING_OBJECT (avi, "Index is empty !");
1334     return FALSE;
1335   }
1336   return TRUE;
1337 }
1338
1339 /*
1340  * gst_avi_demux_parse_subindex:
1341  * @avi: Avi object
1342  * @buf: input data to use for parsing.
1343  * @stream: stream context.
1344  * @entries_list: a list (returned by the function) containing all the
1345  *           indexes parsed in this specific subindex. The first
1346  *           entry is also a pointer to allocated memory that needs
1347  *           to be free´ed. May be NULL if no supported indexes were
1348  *           found.
1349  *
1350  * Reads superindex (openDML-2 spec stuff) from the provided data.
1351  * The buffer should contain a GST_RIFF_TAG_ix?? chunk.
1352  *
1353  * Returns: TRUE on success, FALSE otherwise. Errors are fatal, we
1354  *          throw an error, caller should bail out asap.
1355  */
1356 static gboolean
1357 gst_avi_demux_parse_subindex (GstAviDemux * avi, GstAviStream * stream,
1358     GstBuffer * buf)
1359 {
1360   guint8 *data;
1361   guint16 bpe;
1362   guint32 num, i;
1363   guint64 baseoff;
1364   guint size;
1365
1366   if (!buf)
1367     return TRUE;
1368
1369   size = GST_BUFFER_SIZE (buf);
1370
1371   /* check size */
1372   if (size < 24)
1373     goto too_small;
1374
1375   data = GST_BUFFER_DATA (buf);
1376
1377   /* We don't support index-data yet */
1378   if (data[3] & 0x80)
1379     goto not_implemented;
1380
1381   /* check type of index. The opendml2 specs state that
1382    * there should be 4 dwords per array entry. Type can be
1383    * either frame or field (and we don't care). */
1384   bpe = (data[2] & 0x01) ? 12 : 8;
1385   if (GST_READ_UINT16_LE (data) != bpe / 4 ||
1386       (data[2] & 0xfe) != 0x0 || data[3] != 0x1) {
1387     GST_WARNING_OBJECT (avi,
1388         "Superindex for stream %d has unexpected "
1389         "size_entry %d (bytes) or flags 0x%02x/0x%02x",
1390         stream->num, GST_READ_UINT16_LE (data), data[2], data[3]);
1391     bpe = GST_READ_UINT16_LE (data) * 4;
1392   }
1393   num = GST_READ_UINT32_LE (&data[4]);
1394   baseoff = GST_READ_UINT64_LE (&data[12]);
1395
1396   /* If there's nothing, just return ! */
1397   if (num == 0)
1398     goto empty_index;
1399
1400   GST_INFO_OBJECT (avi, "Parsing subindex, nr_entries = %6d", num);
1401
1402   for (i = 0; i < num; i++) {
1403     GstAviIndexEntry entry;
1404
1405     if (size < 24 + bpe * (i + 1))
1406       break;
1407
1408     /* fill in offset and size. offset contains the keyframe flag in the
1409      * upper bit*/
1410     entry.offset = baseoff + GST_READ_UINT32_LE (&data[24 + bpe * i]);
1411     entry.size = GST_READ_UINT32_LE (&data[24 + bpe * i + 4]);
1412     /* handle flags */
1413     if (stream->strh->type == GST_RIFF_FCC_auds) {
1414       /* all audio frames are keyframes */
1415       ENTRY_SET_KEYFRAME (&entry);
1416     } else {
1417       /* else read flags */
1418       entry.flags = (entry.size & 0x80000000) ? 0 : GST_AVI_KEYFRAME;
1419     }
1420     entry.size &= ~0x80000000;
1421
1422     /* and add */
1423     if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
1424       goto out_of_mem;
1425   }
1426   gst_buffer_unref (buf);
1427
1428   return TRUE;
1429
1430   /* ERRORS */
1431 too_small:
1432   {
1433     GST_ERROR_OBJECT (avi,
1434         "Not enough data to parse subindex (%d available, 24 needed)", size);
1435     gst_buffer_unref (buf);
1436     return TRUE;                /* continue */
1437   }
1438 not_implemented:
1439   {
1440     GST_ELEMENT_ERROR (avi, STREAM, NOT_IMPLEMENTED, (NULL),
1441         ("Subindex-is-data is not implemented"));
1442     gst_buffer_unref (buf);
1443     return FALSE;
1444   }
1445 empty_index:
1446   {
1447     GST_DEBUG_OBJECT (avi, "the index is empty");
1448     gst_buffer_unref (buf);
1449     return TRUE;
1450   }
1451 out_of_mem:
1452   {
1453     GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
1454         ("Cannot allocate memory for %u*%u=%u bytes",
1455             (guint) sizeof (GstAviIndexEntry), num,
1456             (guint) sizeof (GstAviIndexEntry) * num));
1457     gst_buffer_unref (buf);
1458     return FALSE;
1459   }
1460 }
1461
1462 #if 0
1463 /*
1464  * Read AVI index when streaming
1465  */
1466 static void
1467 gst_avi_demux_read_subindexes_push (GstAviDemux * avi)
1468 {
1469   guint32 tag = 0, size;
1470   GstBuffer *buf = NULL;
1471   gint i, n;
1472
1473   GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1474
1475   for (n = 0; n < avi->num_streams; n++) {
1476     GstAviStream *stream = &avi->stream[n];
1477
1478     for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
1479       if (!gst_avi_demux_peek_chunk (avi, &tag, &size))
1480         continue;
1481       else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
1482                   '0' + stream->num % 10)) &&
1483           (tag != GST_MAKE_FOURCC ('0' + stream->num / 10,
1484                   '0' + stream->num % 10, 'i', 'x'))) {
1485         GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1486             GST_FOURCC_ARGS (tag));
1487         continue;
1488       }
1489
1490       avi->offset += 8 + GST_ROUND_UP_2 (size);
1491
1492       buf = gst_buffer_new ();
1493       GST_BUFFER_DATA (buf) = gst_adapter_take (avi->adapter, size);
1494       GST_BUFFER_SIZE (buf) = size;
1495
1496       if (!gst_avi_demux_parse_subindex (avi, stream, buf))
1497         continue;
1498     }
1499
1500     g_free (stream->indexes);
1501     stream->indexes = NULL;
1502   }
1503   /* get stream stats now */
1504   avi->have_index = gst_avi_demux_do_index_stats (avi);
1505 }
1506 #endif
1507
1508 /*
1509  * Read AVI index
1510  */
1511 static void
1512 gst_avi_demux_read_subindexes_pull (GstAviDemux * avi)
1513 {
1514   guint32 tag;
1515   GstBuffer *buf;
1516   gint i, n;
1517
1518   GST_DEBUG_OBJECT (avi, "read subindexes for %d streams", avi->num_streams);
1519
1520   for (n = 0; n < avi->num_streams; n++) {
1521     GstAviStream *stream = &avi->stream[n];
1522
1523     for (i = 0; stream->indexes[i] != GST_BUFFER_OFFSET_NONE; i++) {
1524       if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi), avi->sinkpad,
1525               &stream->indexes[i], &tag, &buf) != GST_FLOW_OK)
1526         continue;
1527       else if ((tag != GST_MAKE_FOURCC ('i', 'x', '0' + stream->num / 10,
1528                   '0' + stream->num % 10)) &&
1529           (tag != GST_MAKE_FOURCC ('0' + stream->num / 10,
1530                   '0' + stream->num % 10, 'i', 'x'))) {
1531         /* Some ODML files (created by god knows what muxer) have a ##ix format
1532          * instead of the 'official' ix##. They are still valid though. */
1533         GST_WARNING_OBJECT (avi, "Not an ix## chunk (%" GST_FOURCC_FORMAT ")",
1534             GST_FOURCC_ARGS (tag));
1535         gst_buffer_unref (buf);
1536         continue;
1537       }
1538
1539       if (!gst_avi_demux_parse_subindex (avi, stream, buf))
1540         continue;
1541     }
1542
1543     g_free (stream->indexes);
1544     stream->indexes = NULL;
1545   }
1546   /* get stream stats now */
1547   avi->have_index = gst_avi_demux_do_index_stats (avi);
1548 }
1549
1550 /*
1551  * gst_avi_demux_riff_parse_vprp:
1552  * @element: caller element (used for debugging/error).
1553  * @buf: input data to be used for parsing, stripped from header.
1554  * @vprp: a pointer (returned by this function) to a filled-in vprp
1555  *        structure. Caller should free it.
1556  *
1557  * Parses a video stream´s vprp. This function takes ownership of @buf.
1558  *
1559  * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
1560  *          should be skipped on error, but it is not fatal.
1561  */
1562 static gboolean
1563 gst_avi_demux_riff_parse_vprp (GstElement * element,
1564     GstBuffer * buf, gst_riff_vprp ** _vprp)
1565 {
1566   gst_riff_vprp *vprp;
1567   gint k;
1568
1569   g_return_val_if_fail (buf != NULL, FALSE);
1570   g_return_val_if_fail (_vprp != NULL, FALSE);
1571
1572   if (GST_BUFFER_SIZE (buf) < G_STRUCT_OFFSET (gst_riff_vprp, field_info))
1573     goto too_small;
1574
1575   vprp = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
1576
1577 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1578   vprp->format_token = GUINT32_FROM_LE (vprp->format_token);
1579   vprp->standard = GUINT32_FROM_LE (vprp->standard);
1580   vprp->vert_rate = GUINT32_FROM_LE (vprp->vert_rate);
1581   vprp->hor_t_total = GUINT32_FROM_LE (vprp->hor_t_total);
1582   vprp->vert_lines = GUINT32_FROM_LE (vprp->vert_lines);
1583   vprp->aspect = GUINT32_FROM_LE (vprp->aspect);
1584   vprp->width = GUINT32_FROM_LE (vprp->width);
1585   vprp->height = GUINT32_FROM_LE (vprp->height);
1586   vprp->fields = GUINT32_FROM_LE (vprp->fields);
1587 #endif
1588
1589   /* size checking */
1590   /* calculate fields based on size */
1591   k = (GST_BUFFER_SIZE (buf) - G_STRUCT_OFFSET (gst_riff_vprp, field_info)) /
1592       vprp->fields;
1593   if (vprp->fields > k) {
1594     GST_WARNING_OBJECT (element,
1595         "vprp header indicated %d fields, only %d available", vprp->fields, k);
1596     vprp->fields = k;
1597   }
1598   if (vprp->fields > GST_RIFF_VPRP_VIDEO_FIELDS) {
1599     GST_WARNING_OBJECT (element,
1600         "vprp header indicated %d fields, at most %d supported", vprp->fields,
1601         GST_RIFF_VPRP_VIDEO_FIELDS);
1602     vprp->fields = GST_RIFF_VPRP_VIDEO_FIELDS;
1603   }
1604 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
1605   for (k = 0; k < vprp->fields; k++) {
1606     gst_riff_vprp_video_field_desc *fd;
1607
1608     fd = &vprp->field_info[k];
1609     fd->compressed_bm_height = GUINT32_FROM_LE (fd->compressed_bm_height);
1610     fd->compressed_bm_width = GUINT32_FROM_LE (fd->compressed_bm_width);
1611     fd->valid_bm_height = GUINT32_FROM_LE (fd->valid_bm_height);
1612     fd->valid_bm_width = GUINT16_FROM_LE (fd->valid_bm_width);
1613     fd->valid_bm_x_offset = GUINT16_FROM_LE (fd->valid_bm_x_offset);
1614     fd->valid_bm_y_offset = GUINT32_FROM_LE (fd->valid_bm_y_offset);
1615     fd->video_x_t_offset = GUINT32_FROM_LE (fd->video_x_t_offset);
1616     fd->video_y_start = GUINT32_FROM_LE (fd->video_y_start);
1617   }
1618 #endif
1619
1620   /* debug */
1621   GST_INFO_OBJECT (element, "vprp tag found in context vids:");
1622   GST_INFO_OBJECT (element, " format_token  %d", vprp->format_token);
1623   GST_INFO_OBJECT (element, " standard      %d", vprp->standard);
1624   GST_INFO_OBJECT (element, " vert_rate     %d", vprp->vert_rate);
1625   GST_INFO_OBJECT (element, " hor_t_total   %d", vprp->hor_t_total);
1626   GST_INFO_OBJECT (element, " vert_lines    %d", vprp->vert_lines);
1627   GST_INFO_OBJECT (element, " aspect        %d:%d", vprp->aspect >> 16,
1628       vprp->aspect & 0xffff);
1629   GST_INFO_OBJECT (element, " width         %d", vprp->width);
1630   GST_INFO_OBJECT (element, " height        %d", vprp->height);
1631   GST_INFO_OBJECT (element, " fields        %d", vprp->fields);
1632   for (k = 0; k < vprp->fields; k++) {
1633     gst_riff_vprp_video_field_desc *fd;
1634
1635     fd = &(vprp->field_info[k]);
1636     GST_INFO_OBJECT (element, " field %u description:", k);
1637     GST_INFO_OBJECT (element, "  compressed_bm_height  %d",
1638         fd->compressed_bm_height);
1639     GST_INFO_OBJECT (element, "  compressed_bm_width  %d",
1640         fd->compressed_bm_width);
1641     GST_INFO_OBJECT (element, "  valid_bm_height       %d",
1642         fd->valid_bm_height);
1643     GST_INFO_OBJECT (element, "  valid_bm_width        %d", fd->valid_bm_width);
1644     GST_INFO_OBJECT (element, "  valid_bm_x_offset     %d",
1645         fd->valid_bm_x_offset);
1646     GST_INFO_OBJECT (element, "  valid_bm_y_offset     %d",
1647         fd->valid_bm_y_offset);
1648     GST_INFO_OBJECT (element, "  video_x_t_offset      %d",
1649         fd->video_x_t_offset);
1650     GST_INFO_OBJECT (element, "  video_y_start         %d", fd->video_y_start);
1651   }
1652
1653   gst_buffer_unref (buf);
1654
1655   *_vprp = vprp;
1656
1657   return TRUE;
1658
1659   /* ERRORS */
1660 too_small:
1661   {
1662     GST_ERROR_OBJECT (element,
1663         "Too small vprp (%d available, at least %d needed)",
1664         GST_BUFFER_SIZE (buf),
1665         (int) G_STRUCT_OFFSET (gst_riff_vprp, field_info));
1666     gst_buffer_unref (buf);
1667     return FALSE;
1668   }
1669 }
1670
1671 static void
1672 gst_avi_demux_expose_streams (GstAviDemux * avi, gboolean force)
1673 {
1674   guint i;
1675
1676   GST_DEBUG_OBJECT (avi, "force : %d", force);
1677
1678   for (i = 0; i < avi->num_streams; i++) {
1679     GstAviStream *stream = &avi->stream[i];
1680
1681     if (force || stream->idx_n != 0) {
1682       GST_LOG_OBJECT (avi, "Added pad %s with caps %" GST_PTR_FORMAT,
1683           GST_PAD_NAME (stream->pad), GST_PAD_CAPS (stream->pad));
1684       gst_element_add_pad ((GstElement *) avi, stream->pad);
1685       stream->exposed = TRUE;
1686       if (avi->main_stream == -1)
1687         avi->main_stream = i;
1688     } else {
1689       GST_WARNING_OBJECT (avi, "Stream #%d doesn't have any entry, removing it",
1690           i);
1691       gst_avi_demux_reset_stream (avi, stream);
1692     }
1693   }
1694 }
1695
1696 /*
1697  * gst_avi_demux_parse_stream:
1698  * @avi: calling element (used for debugging/errors).
1699  * @buf: input buffer used to parse the stream.
1700  *
1701  * Parses all subchunks in a strl chunk (which defines a single
1702  * stream). Discards the buffer after use. This function will
1703  * increment the stream counter internally.
1704  *
1705  * Returns: whether the stream was identified successfully.
1706  *          Errors are not fatal. It does indicate the stream
1707  *          was skipped.
1708  */
1709 static gboolean
1710 gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
1711 {
1712   GstAviStream *stream;
1713   GstElementClass *klass;
1714   GstPadTemplate *templ;
1715   GstBuffer *sub = NULL;
1716   guint offset = 4;
1717   guint32 tag = 0;
1718   gchar *codec_name = NULL, *padname = NULL;
1719   const gchar *tag_name;
1720   GstCaps *caps = NULL;
1721   GstPad *pad;
1722   GstElement *element;
1723   gboolean got_strh = FALSE, got_strf = FALSE, got_vprp = FALSE;
1724   gst_riff_vprp *vprp = NULL;
1725
1726   element = GST_ELEMENT_CAST (avi);
1727
1728   GST_DEBUG_OBJECT (avi, "Parsing stream");
1729
1730   if (avi->num_streams >= GST_AVI_DEMUX_MAX_STREAMS) {
1731     GST_WARNING_OBJECT (avi,
1732         "maximum no of streams (%d) exceeded, ignoring stream",
1733         GST_AVI_DEMUX_MAX_STREAMS);
1734     gst_buffer_unref (buf);
1735     /* not a fatal error, let's say */
1736     return TRUE;
1737   }
1738
1739   stream = &avi->stream[avi->num_streams];
1740
1741   /* initial settings */
1742   stream->idx_duration = GST_CLOCK_TIME_NONE;
1743   stream->hdr_duration = GST_CLOCK_TIME_NONE;
1744   stream->duration = GST_CLOCK_TIME_NONE;
1745
1746   while (gst_riff_parse_chunk (element, buf, &offset, &tag, &sub)) {
1747     /* sub can be NULL if the chunk is empty */
1748     if (sub == NULL) {
1749       GST_DEBUG_OBJECT (avi, "ignoring empty chunk %" GST_FOURCC_FORMAT,
1750           GST_FOURCC_ARGS (tag));
1751       continue;
1752     }
1753     switch (tag) {
1754       case GST_RIFF_TAG_strh:
1755       {
1756         gst_riff_strh *strh;
1757
1758         if (got_strh) {
1759           GST_WARNING_OBJECT (avi, "Ignoring additional strh chunk");
1760           break;
1761         }
1762         if (!gst_riff_parse_strh (element, sub, &stream->strh)) {
1763           /* ownership given away */
1764           sub = NULL;
1765           GST_WARNING_OBJECT (avi, "Failed to parse strh chunk");
1766           goto fail;
1767         }
1768         sub = NULL;
1769         strh = stream->strh;
1770         /* sanity check; stream header frame rate matches global header
1771          * frame duration */
1772         if (stream->strh->type == GST_RIFF_FCC_vids) {
1773           GstClockTime s_dur;
1774           GstClockTime h_dur = avi->avih->us_frame * GST_USECOND;
1775
1776           s_dur = gst_util_uint64_scale (GST_SECOND, strh->scale, strh->rate);
1777           GST_DEBUG_OBJECT (avi, "verifying stream framerate %d/%d, "
1778               "frame duration = %d ms", strh->rate, strh->scale,
1779               (gint) (s_dur / GST_MSECOND));
1780           if (h_dur > (10 * GST_MSECOND) && (s_dur > 10 * h_dur)) {
1781             strh->rate = GST_SECOND / GST_USECOND;
1782             strh->scale = h_dur / GST_USECOND;
1783             GST_DEBUG_OBJECT (avi, "correcting stream framerate to %d/%d",
1784                 strh->rate, strh->scale);
1785           }
1786         }
1787         /* determine duration as indicated by header */
1788         stream->hdr_duration = gst_util_uint64_scale ((guint64) strh->length *
1789             strh->scale, GST_SECOND, (guint64) strh->rate);
1790         GST_INFO ("Stream duration according to header: %" GST_TIME_FORMAT,
1791             GST_TIME_ARGS (stream->hdr_duration));
1792         if (stream->hdr_duration == 0)
1793           stream->hdr_duration = GST_CLOCK_TIME_NONE;
1794
1795         got_strh = TRUE;
1796         break;
1797       }
1798       case GST_RIFF_TAG_strf:
1799       {
1800         gboolean res = FALSE;
1801
1802         if (got_strf) {
1803           GST_WARNING_OBJECT (avi, "Ignoring additional strf chunk");
1804           break;
1805         }
1806         if (!got_strh) {
1807           GST_ERROR_OBJECT (avi, "Found strf chunk before strh chunk");
1808           goto fail;
1809         }
1810         switch (stream->strh->type) {
1811           case GST_RIFF_FCC_vids:
1812             stream->is_vbr = TRUE;
1813             res = gst_riff_parse_strf_vids (element, sub,
1814                 &stream->strf.vids, &stream->extradata);
1815             sub = NULL;
1816             GST_DEBUG_OBJECT (element, "marking video as VBR, res %d", res);
1817             break;
1818           case GST_RIFF_FCC_auds:
1819             stream->is_vbr = (stream->strh->samplesize == 0)
1820                 && stream->strh->scale > 1;
1821             res =
1822                 gst_riff_parse_strf_auds (element, sub, &stream->strf.auds,
1823                 &stream->extradata);
1824             sub = NULL;
1825             GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
1826                 stream->is_vbr, res);
1827             break;
1828           case GST_RIFF_FCC_iavs:
1829             stream->is_vbr = TRUE;
1830             res = gst_riff_parse_strf_iavs (element, sub,
1831                 &stream->strf.iavs, &stream->extradata);
1832             sub = NULL;
1833             GST_DEBUG_OBJECT (element, "marking iavs as VBR, res %d", res);
1834             break;
1835           case GST_RIFF_FCC_txts:
1836             /* nothing to parse here */
1837             stream->is_vbr = (stream->strh->samplesize == 0)
1838                 && (stream->strh->scale > 1);
1839             res = TRUE;
1840             break;
1841           default:
1842             GST_ERROR_OBJECT (avi,
1843                 "Don´t know how to handle stream type %" GST_FOURCC_FORMAT,
1844                 GST_FOURCC_ARGS (stream->strh->type));
1845             break;
1846         }
1847         if (sub) {
1848           gst_buffer_unref (sub);
1849           sub = NULL;
1850         }
1851         if (!res)
1852           goto fail;
1853         got_strf = TRUE;
1854         break;
1855       }
1856       case GST_RIFF_TAG_vprp:
1857       {
1858         if (got_vprp) {
1859           GST_WARNING_OBJECT (avi, "Ignoring additional vprp chunk");
1860           break;
1861         }
1862         if (!got_strh) {
1863           GST_ERROR_OBJECT (avi, "Found vprp chunk before strh chunk");
1864           goto fail;
1865         }
1866         if (!got_strf) {
1867           GST_ERROR_OBJECT (avi, "Found vprp chunk before strf chunk");
1868           goto fail;
1869         }
1870
1871         if (!gst_avi_demux_riff_parse_vprp (element, sub, &vprp)) {
1872           GST_WARNING_OBJECT (avi, "Failed to parse vprp chunk");
1873           /* not considered fatal */
1874           g_free (vprp);
1875           vprp = NULL;
1876         } else
1877           got_vprp = TRUE;
1878         sub = NULL;
1879         break;
1880       }
1881       case GST_RIFF_TAG_strd:
1882         if (stream->initdata)
1883           gst_buffer_unref (stream->initdata);
1884         stream->initdata = sub;
1885         sub = NULL;
1886         break;
1887       case GST_RIFF_TAG_strn:
1888         g_free (stream->name);
1889         if (sub != NULL) {
1890           stream->name =
1891               g_strndup ((gchar *) GST_BUFFER_DATA (sub),
1892               (gsize) GST_BUFFER_SIZE (sub));
1893           gst_buffer_unref (sub);
1894           sub = NULL;
1895         } else {
1896           stream->name = g_strdup ("");
1897         }
1898         GST_DEBUG_OBJECT (avi, "stream name: %s", stream->name);
1899         break;
1900       default:
1901         if (tag == GST_MAKE_FOURCC ('i', 'n', 'd', 'x') ||
1902             tag == GST_MAKE_FOURCC ('i', 'x', '0' + avi->num_streams / 10,
1903                 '0' + avi->num_streams % 10)) {
1904           g_free (stream->indexes);
1905           gst_avi_demux_parse_superindex (avi, sub, &stream->indexes);
1906           stream->superindex = TRUE;
1907           sub = NULL;
1908           break;
1909         }
1910         GST_WARNING_OBJECT (avi,
1911             "Unknown stream header tag %" GST_FOURCC_FORMAT ", ignoring",
1912             GST_FOURCC_ARGS (tag));
1913         /* fall-through */
1914       case GST_RIFF_TAG_JUNK:
1915         break;
1916     }
1917     if (sub != NULL) {
1918       gst_buffer_unref (sub);
1919       sub = NULL;
1920     }
1921   }
1922
1923   if (!got_strh) {
1924     GST_WARNING_OBJECT (avi, "Failed to find strh chunk");
1925     goto fail;
1926   }
1927
1928   if (!got_strf) {
1929     GST_WARNING_OBJECT (avi, "Failed to find strf chunk");
1930     goto fail;
1931   }
1932
1933   /* get class to figure out the template */
1934   klass = GST_ELEMENT_GET_CLASS (avi);
1935
1936   /* we now have all info, let´s set up a pad and a caps and be done */
1937   /* create stream name + pad */
1938   switch (stream->strh->type) {
1939     case GST_RIFF_FCC_vids:{
1940       guint32 fourcc;
1941
1942       fourcc = (stream->strf.vids->compression) ?
1943           stream->strf.vids->compression : stream->strh->fcc_handler;
1944       padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
1945       templ = gst_element_class_get_pad_template (klass, "video_%02d");
1946       caps = gst_riff_create_video_caps (fourcc, stream->strh,
1947           stream->strf.vids, stream->extradata, stream->initdata, &codec_name);
1948       if (!caps) {
1949         caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
1950             GST_TYPE_FOURCC, fourcc, NULL);
1951       } else if (got_vprp && vprp) {
1952         guint32 aspect_n, aspect_d;
1953         gint n, d;
1954
1955         aspect_n = vprp->aspect >> 16;
1956         aspect_d = vprp->aspect & 0xffff;
1957         /* calculate the pixel aspect ratio using w/h and aspect ratio */
1958         n = aspect_n * stream->strf.vids->height;
1959         d = aspect_d * stream->strf.vids->width;
1960         if (n && d)
1961           gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1962               n, d, NULL);
1963         /* very local, not needed elsewhere */
1964         g_free (vprp);
1965         vprp = NULL;
1966       }
1967       tag_name = GST_TAG_VIDEO_CODEC;
1968       avi->num_v_streams++;
1969       break;
1970     }
1971     case GST_RIFF_FCC_auds:{
1972       padname = g_strdup_printf ("audio_%02d", avi->num_a_streams);
1973       templ = gst_element_class_get_pad_template (klass, "audio_%02d");
1974       caps = gst_riff_create_audio_caps (stream->strf.auds->format,
1975           stream->strh, stream->strf.auds, stream->extradata,
1976           stream->initdata, &codec_name);
1977       if (!caps) {
1978         caps = gst_caps_new_simple ("audio/x-avi-unknown", "codec_id",
1979             G_TYPE_INT, stream->strf.auds->format, NULL);
1980       }
1981       tag_name = GST_TAG_AUDIO_CODEC;
1982       avi->num_a_streams++;
1983       break;
1984     }
1985     case GST_RIFF_FCC_iavs:{
1986       guint32 fourcc = stream->strh->fcc_handler;
1987
1988       padname = g_strdup_printf ("video_%02d", avi->num_v_streams);
1989       templ = gst_element_class_get_pad_template (klass, "video_%02d");
1990       caps = gst_riff_create_iavs_caps (fourcc, stream->strh,
1991           stream->strf.iavs, stream->extradata, stream->initdata, &codec_name);
1992       if (!caps) {
1993         caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
1994             GST_TYPE_FOURCC, fourcc, NULL);
1995       }
1996       tag_name = GST_TAG_VIDEO_CODEC;
1997       avi->num_v_streams++;
1998       break;
1999     }
2000     case GST_RIFF_FCC_txts:{
2001       padname = g_strdup_printf ("subtitle_%02d", avi->num_t_streams);
2002       templ = gst_element_class_get_pad_template (klass, "subtitle_%02d");
2003       caps = gst_caps_new_simple ("application/x-subtitle-avi", NULL);
2004       tag_name = NULL;
2005       avi->num_t_streams++;
2006       break;
2007     }
2008     default:
2009       g_assert_not_reached ();
2010   }
2011
2012   /* no caps means no stream */
2013   if (!caps) {
2014     GST_ERROR_OBJECT (element, "Did not find caps for stream %s", padname);
2015     goto fail;
2016   }
2017
2018   GST_DEBUG_OBJECT (element, "codec-name=%s",
2019       (codec_name ? codec_name : "NULL"));
2020   GST_DEBUG_OBJECT (element, "caps=%" GST_PTR_FORMAT, caps);
2021
2022   /* set proper settings and add it */
2023   if (stream->pad)
2024     gst_object_unref (stream->pad);
2025   pad = stream->pad = gst_pad_new_from_template (templ, padname);
2026   g_free (padname);
2027
2028   gst_pad_use_fixed_caps (pad);
2029 #if 0
2030   gst_pad_set_formats_function (pad,
2031       GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_formats));
2032   gst_pad_set_event_mask_function (pad,
2033       GST_DEBUG_FUNCPTR (gst_avi_demux_get_event_mask));
2034 #endif
2035   gst_pad_set_event_function (pad,
2036       GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_event));
2037   gst_pad_set_query_type_function (pad,
2038       GST_DEBUG_FUNCPTR (gst_avi_demux_get_src_query_types));
2039   gst_pad_set_query_function (pad,
2040       GST_DEBUG_FUNCPTR (gst_avi_demux_handle_src_query));
2041 #if 0
2042   gst_pad_set_convert_function (pad,
2043       GST_DEBUG_FUNCPTR (gst_avi_demux_src_convert));
2044 #endif
2045
2046   if (avi->element_index)
2047     gst_index_get_writer_id (avi->element_index, GST_OBJECT_CAST (stream->pad),
2048         &stream->index_id);
2049
2050   stream->num = avi->num_streams;
2051
2052   stream->start_entry = 0;
2053   stream->step_entry = 0;
2054   stream->stop_entry = 0;
2055
2056   stream->current_entry = -1;
2057   stream->current_total = 0;
2058
2059   stream->last_flow = GST_FLOW_OK;
2060   stream->discont = TRUE;
2061
2062   stream->total_bytes = 0;
2063   stream->total_blocks = 0;
2064   stream->n_keyframes = 0;
2065
2066   stream->idx_n = 0;
2067   stream->idx_max = 0;
2068
2069   gst_pad_set_element_private (pad, stream);
2070   avi->num_streams++;
2071
2072   gst_pad_set_caps (pad, caps);
2073   gst_pad_set_active (pad, TRUE);
2074   gst_caps_unref (caps);
2075
2076   /* make tags */
2077   if (codec_name) {
2078     if (!stream->taglist)
2079       stream->taglist = gst_tag_list_new ();
2080
2081     avi->got_tags = TRUE;
2082
2083     gst_tag_list_add (stream->taglist, GST_TAG_MERGE_APPEND, tag_name,
2084         codec_name, NULL);
2085     g_free (codec_name);
2086   }
2087
2088   gst_buffer_unref (buf);
2089
2090   return TRUE;
2091
2092   /* ERRORS */
2093 fail:
2094   {
2095     /* unref any mem that may be in use */
2096     if (buf)
2097       gst_buffer_unref (buf);
2098     if (sub)
2099       gst_buffer_unref (sub);
2100     g_free (vprp);
2101     g_free (codec_name);
2102     gst_avi_demux_reset_stream (avi, stream);
2103     avi->num_streams++;
2104     return FALSE;
2105   }
2106 }
2107
2108 /*
2109  * gst_avi_demux_parse_odml:
2110  * @avi: calling element (used for debug/error).
2111  * @buf: input buffer to be used for parsing.
2112  *
2113  * Read an openDML-2.0 extension header. Fills in the frame number
2114  * in the avi demuxer object when reading succeeds.
2115  */
2116 static void
2117 gst_avi_demux_parse_odml (GstAviDemux * avi, GstBuffer * buf)
2118 {
2119   guint32 tag = 0;
2120   guint offset = 4;
2121   GstBuffer *sub = NULL;
2122
2123   while (gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
2124           &sub)) {
2125     switch (tag) {
2126       case GST_RIFF_TAG_dmlh:{
2127         gst_riff_dmlh dmlh, *_dmlh;
2128         guint size;
2129
2130         /* sub == NULL is possible and means an empty buffer */
2131         size = sub ? GST_BUFFER_SIZE (sub) : 0;
2132
2133         /* check size */
2134         if (size < sizeof (gst_riff_dmlh)) {
2135           GST_ERROR_OBJECT (avi,
2136               "DMLH entry is too small (%d bytes, %d needed)",
2137               size, (int) sizeof (gst_riff_dmlh));
2138           goto next;
2139         }
2140         _dmlh = (gst_riff_dmlh *) GST_BUFFER_DATA (sub);
2141         dmlh.totalframes = GST_READ_UINT32_LE (&_dmlh->totalframes);
2142
2143         GST_INFO_OBJECT (avi, "dmlh tag found: totalframes: %u",
2144             dmlh.totalframes);
2145
2146         avi->avih->tot_frames = dmlh.totalframes;
2147         goto next;
2148       }
2149
2150       default:
2151         GST_WARNING_OBJECT (avi,
2152             "Unknown tag %" GST_FOURCC_FORMAT " in ODML header",
2153             GST_FOURCC_ARGS (tag));
2154         /* fall-through */
2155       case GST_RIFF_TAG_JUNK:
2156       next:
2157         /* skip and move to next chunk */
2158         if (sub) {
2159           gst_buffer_unref (sub);
2160           sub = NULL;
2161         }
2162         break;
2163     }
2164   }
2165   if (buf)
2166     gst_buffer_unref (buf);
2167 }
2168
2169 /* Index helper */
2170 static guint
2171 gst_avi_demux_index_last (GstAviDemux * avi, GstAviStream * stream)
2172 {
2173   return stream->idx_n - 1;
2174 }
2175
2176 /* find a previous entry in the index with the given flags */
2177 static guint
2178 gst_avi_demux_index_prev (GstAviDemux * avi, GstAviStream * stream,
2179     guint last, gboolean keyframe)
2180 {
2181   GstAviIndexEntry *entry;
2182   guint i;
2183
2184   for (i = last; i > 0; i--) {
2185     entry = &stream->index[i - 1];
2186     if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2187       return i - 1;
2188     }
2189   }
2190   return 0;
2191 }
2192
2193 static guint
2194 gst_avi_demux_index_next (GstAviDemux * avi, GstAviStream * stream,
2195     guint last, gboolean keyframe)
2196 {
2197   GstAviIndexEntry *entry;
2198   gint i;
2199
2200   for (i = last + 1; i < stream->idx_n; i++) {
2201     entry = &stream->index[i];
2202     if (!keyframe || ENTRY_IS_KEYFRAME (entry)) {
2203       return i;
2204     }
2205   }
2206   return stream->idx_n - 1;
2207 }
2208
2209 static guint
2210 gst_avi_demux_index_entry_search (GstAviIndexEntry * entry, guint64 * total)
2211 {
2212   if (entry->total < *total)
2213     return -1;
2214   else if (entry->total > *total)
2215     return 1;
2216   return 0;
2217 }
2218
2219 /*
2220  * gst_avi_demux_index_for_time:
2221  * @avi: Avi object
2222  * @stream: the stream
2223  * @time: a time position
2224  *
2225  * Finds the index entry which time is less or equal than the requested time.
2226  * Try to avoid binary search when we can convert the time to an index
2227  * position directly (for example for video frames with a fixed duration).
2228  *
2229  * Returns: the found position in the index.
2230  */
2231 static guint
2232 gst_avi_demux_index_for_time (GstAviDemux * avi,
2233     GstAviStream * stream, guint64 time)
2234 {
2235   guint index = -1;
2236   guint64 total;
2237
2238   GST_LOG_OBJECT (avi, "search time:%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
2239
2240   /* easy (and common) cases */
2241   if (time == 0 || stream->idx_n == 0)
2242     return 0;
2243   if (time >= stream->idx_duration)
2244     return stream->idx_n - 1;
2245
2246   /* figure out where we need to go. For that we convert the time to an
2247    * index entry or we convert it to a total and then do a binary search. */
2248   if (stream->is_vbr) {
2249     /* VBR stream next timestamp */
2250     if (stream->strh->type == GST_RIFF_FCC_auds) {
2251       total = avi_stream_convert_time_to_frames_unchecked (stream, time);
2252     } else {
2253       index = avi_stream_convert_time_to_frames_unchecked (stream, time);
2254     }
2255   } else {
2256     /* constant rate stream */
2257     total = avi_stream_convert_time_to_bytes_unchecked (stream, time);
2258   }
2259
2260   if (index == -1) {
2261     GstAviIndexEntry *entry;
2262
2263     /* no index, find index with binary search on total */
2264     GST_LOG_OBJECT (avi, "binary search for entry with total %"
2265         G_GUINT64_FORMAT, total);
2266
2267     entry = gst_util_array_binary_search (stream->index,
2268         stream->idx_n, sizeof (GstAviIndexEntry),
2269         (GCompareDataFunc) gst_avi_demux_index_entry_search,
2270         GST_SEARCH_MODE_BEFORE, &total, NULL);
2271
2272     if (entry == NULL) {
2273       GST_LOG_OBJECT (avi, "not found, assume index 0");
2274       index = 0;
2275     } else {
2276       index = entry - stream->index;
2277       GST_LOG_OBJECT (avi, "found at %u", index);
2278     }
2279   } else {
2280     GST_LOG_OBJECT (avi, "converted time to index %u", index);
2281   }
2282
2283   return index;
2284 }
2285
2286 static inline GstAviStream *
2287 gst_avi_demux_stream_for_id (GstAviDemux * avi, guint32 id)
2288 {
2289   guint stream_nr;
2290   GstAviStream *stream;
2291
2292   /* get the stream for this entry */
2293   stream_nr = CHUNKID_TO_STREAMNR (id);
2294   if (G_UNLIKELY (stream_nr >= avi->num_streams)) {
2295     GST_WARNING_OBJECT (avi, "invalid stream nr %d", stream_nr);
2296     return NULL;
2297   }
2298   stream = &avi->stream[stream_nr];
2299   if (G_UNLIKELY (!stream->strh)) {
2300     GST_WARNING_OBJECT (avi, "Unhandled stream %d, skipping", stream_nr);
2301     return NULL;
2302   }
2303   return stream;
2304 }
2305
2306 /*
2307  * gst_avi_demux_parse_index:
2308  * @avi: calling element (used for debugging/errors).
2309  * @buf: buffer containing the full index.
2310  *
2311  * Read index entries from the provided buffer.
2312  * The buffer should contain a GST_RIFF_TAG_idx1 chunk.
2313  */
2314 static gboolean
2315 gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf)
2316 {
2317   guint64 pos_before;
2318   guint8 *data;
2319   guint size;
2320   guint i, num, n;
2321   gst_riff_index_entry *index;
2322   GstClockTime stamp;
2323   GstAviStream *stream;
2324   GstAviIndexEntry entry;
2325   guint32 id;
2326
2327   if (!buf)
2328     return FALSE;
2329
2330   data = GST_BUFFER_DATA (buf);
2331   size = GST_BUFFER_SIZE (buf);
2332
2333   stamp = gst_util_get_timestamp ();
2334
2335   /* see how many items in the index */
2336   num = size / sizeof (gst_riff_index_entry);
2337   if (num == 0)
2338     goto empty_list;
2339
2340   GST_INFO_OBJECT (avi, "Parsing index, nr_entries = %6d", num);
2341
2342   index = (gst_riff_index_entry *) data;
2343   pos_before = avi->offset;
2344
2345   /* figure out if the index is 0 based or relative to the MOVI start */
2346   entry.offset = GST_READ_UINT32_LE (&index[0].offset);
2347   if (entry.offset < avi->offset) {
2348     avi->index_offset = avi->offset + 8;
2349     GST_DEBUG ("index_offset = %" G_GUINT64_FORMAT, avi->index_offset);
2350   } else {
2351     avi->index_offset = 0;
2352     GST_DEBUG ("index is 0 based");
2353   }
2354
2355   for (i = 0, n = 0; i < num; i++) {
2356     id = GST_READ_UINT32_LE (&index[i].id);
2357     entry.offset = GST_READ_UINT32_LE (&index[i].offset);
2358
2359     /* some sanity checks */
2360     if (G_UNLIKELY (id == GST_RIFF_rec || id == 0 ||
2361             (entry.offset == 0 && n > 0)))
2362       continue;
2363
2364     /* get the stream for this entry */
2365     stream = gst_avi_demux_stream_for_id (avi, id);
2366     if (G_UNLIKELY (!stream))
2367       continue;
2368
2369     /* handle offset and size */
2370     entry.offset += avi->index_offset + 8;
2371     entry.size = GST_READ_UINT32_LE (&index[i].size);
2372
2373     /* handle flags */
2374     if (stream->strh->type == GST_RIFF_FCC_auds) {
2375       /* all audio frames are keyframes */
2376       ENTRY_SET_KEYFRAME (&entry);
2377     } else {
2378       guint32 flags;
2379       /* else read flags */
2380       flags = GST_READ_UINT32_LE (&index[i].flags);
2381       if (flags & GST_RIFF_IF_KEYFRAME) {
2382         ENTRY_SET_KEYFRAME (&entry);
2383       } else {
2384         ENTRY_UNSET_KEYFRAME (&entry);
2385       }
2386     }
2387
2388     /* and add */
2389     if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2390       goto out_of_mem;
2391
2392     n++;
2393   }
2394   gst_buffer_unref (buf);
2395
2396   /* get stream stats now */
2397   avi->have_index = gst_avi_demux_do_index_stats (avi);
2398
2399   stamp = gst_util_get_timestamp () - stamp;
2400   GST_DEBUG_OBJECT (avi, "index parsing took %" GST_TIME_FORMAT,
2401       GST_TIME_ARGS (stamp));
2402
2403   return TRUE;
2404
2405   /* ERRORS */
2406 empty_list:
2407   {
2408     GST_DEBUG_OBJECT (avi, "empty index");
2409     gst_buffer_unref (buf);
2410     return FALSE;
2411   }
2412 out_of_mem:
2413   {
2414     GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2415         ("Cannot allocate memory for %u*%u=%u bytes",
2416             (guint) sizeof (GstAviIndexEntry), num,
2417             (guint) sizeof (GstAviIndexEntry) * num));
2418     gst_buffer_unref (buf);
2419     return FALSE;
2420   }
2421 }
2422
2423 /*
2424  * gst_avi_demux_stream_index:
2425  * @avi: avi demuxer object.
2426  *
2427  * Seeks to index and reads it.
2428  */
2429 static void
2430 gst_avi_demux_stream_index (GstAviDemux * avi)
2431 {
2432   GstFlowReturn res;
2433   guint64 offset = avi->offset;
2434   GstBuffer *buf;
2435   guint32 tag;
2436   guint32 size;
2437
2438   GST_DEBUG ("demux stream index at offset %" G_GUINT64_FORMAT, offset);
2439
2440   /* get chunk information */
2441   res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2442   if (res != GST_FLOW_OK)
2443     goto pull_failed;
2444   else if (GST_BUFFER_SIZE (buf) < 8)
2445     goto too_small;
2446
2447   /* check tag first before blindy trying to read 'size' bytes */
2448   tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
2449   size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
2450   if (tag == GST_RIFF_TAG_LIST) {
2451     /* this is the movi tag */
2452     GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
2453         (8 + GST_ROUND_UP_2 (size)));
2454     offset += 8 + GST_ROUND_UP_2 (size);
2455     gst_buffer_unref (buf);
2456     res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2457     if (res != GST_FLOW_OK)
2458       goto pull_failed;
2459     else if (GST_BUFFER_SIZE (buf) < 8)
2460       goto too_small;
2461     tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
2462     size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
2463   }
2464
2465   if (tag != GST_RIFF_TAG_idx1)
2466     goto no_index;
2467   if (!size)
2468     goto zero_index;
2469
2470   gst_buffer_unref (buf);
2471
2472   GST_DEBUG ("index found at offset %" G_GUINT64_FORMAT, offset);
2473
2474   /* read chunk, advance offset */
2475   if (gst_riff_read_chunk (GST_ELEMENT_CAST (avi),
2476           avi->sinkpad, &offset, &tag, &buf) != GST_FLOW_OK)
2477     return;
2478
2479   GST_DEBUG ("will parse index chunk size %u for tag %"
2480       GST_FOURCC_FORMAT, GST_BUFFER_SIZE (buf), GST_FOURCC_ARGS (tag));
2481
2482   gst_avi_demux_parse_index (avi, buf);
2483
2484 #ifndef GST_DISABLE_GST_DEBUG
2485   /* debug our indexes */
2486   {
2487     gint i;
2488     GstAviStream *stream;
2489
2490     for (i = 0; i < avi->num_streams; i++) {
2491       stream = &avi->stream[i];
2492       GST_DEBUG_OBJECT (avi, "stream %u: %u frames, %" G_GINT64_FORMAT " bytes",
2493           i, stream->idx_n, stream->total_bytes);
2494     }
2495   }
2496 #endif
2497   return;
2498
2499   /* ERRORS */
2500 pull_failed:
2501   {
2502     GST_DEBUG_OBJECT (avi,
2503         "pull range failed: pos=%" G_GUINT64_FORMAT " size=8", offset);
2504     return;
2505   }
2506 too_small:
2507   {
2508     GST_DEBUG_OBJECT (avi, "Buffer is too small");
2509     gst_buffer_unref (buf);
2510     return;
2511   }
2512 no_index:
2513   {
2514     GST_WARNING_OBJECT (avi,
2515         "No index data (idx1) after movi chunk, but %" GST_FOURCC_FORMAT,
2516         GST_FOURCC_ARGS (tag));
2517     gst_buffer_unref (buf);
2518     return;
2519   }
2520 zero_index:
2521   {
2522     GST_WARNING_OBJECT (avi, "Empty index data (idx1) after movi chunk");
2523     gst_buffer_unref (buf);
2524     return;
2525   }
2526 }
2527
2528 /*
2529  * gst_avi_demux_peek_tag:
2530  *
2531  * Returns the tag and size of the next chunk
2532  */
2533 static GstFlowReturn
2534 gst_avi_demux_peek_tag (GstAviDemux * avi, guint64 offset, guint32 * tag,
2535     guint * size)
2536 {
2537   GstFlowReturn res = GST_FLOW_OK;
2538   GstBuffer *buf = NULL;
2539   guint bufsize;
2540   guint8 *bufdata;
2541
2542   res = gst_pad_pull_range (avi->sinkpad, offset, 8, &buf);
2543   if (res != GST_FLOW_OK)
2544     goto pull_failed;
2545
2546   bufsize = GST_BUFFER_SIZE (buf);
2547   if (bufsize != 8)
2548     goto wrong_size;
2549
2550   bufdata = GST_BUFFER_DATA (buf);
2551
2552   *tag = GST_READ_UINT32_LE (bufdata);
2553   *size = GST_READ_UINT32_LE (bufdata + 4);
2554
2555   GST_LOG_OBJECT (avi, "Tag[%" GST_FOURCC_FORMAT "] (size:%d) %"
2556       G_GINT64_FORMAT " -- %" G_GINT64_FORMAT, GST_FOURCC_ARGS (*tag),
2557       *size, offset + 8, offset + 8 + (gint64) * size);
2558
2559 done:
2560   gst_buffer_unref (buf);
2561
2562   return res;
2563
2564   /* ERRORS */
2565 pull_failed:
2566   {
2567     GST_DEBUG_OBJECT (avi, "pull_ranged returned %s", gst_flow_get_name (res));
2568     return res;
2569   }
2570 wrong_size:
2571   {
2572     GST_DEBUG_OBJECT (avi, "got %d bytes which is <> 8 bytes", bufsize);
2573     res = GST_FLOW_ERROR;
2574     goto done;
2575   }
2576 }
2577
2578 /*
2579  * gst_avi_demux_next_data_buffer:
2580  *
2581  * Returns the offset and size of the next buffer
2582  * Position is the position of the buffer (after tag and size)
2583  */
2584 static GstFlowReturn
2585 gst_avi_demux_next_data_buffer (GstAviDemux * avi, guint64 * offset,
2586     guint32 * tag, guint * size)
2587 {
2588   guint64 off = *offset;
2589   guint _size = 0;
2590   GstFlowReturn res;
2591
2592   do {
2593     res = gst_avi_demux_peek_tag (avi, off, tag, &_size);
2594     if (res != GST_FLOW_OK)
2595       break;
2596     if (*tag == GST_RIFF_TAG_LIST || *tag == GST_RIFF_TAG_RIFF)
2597       off += 8 + 4;             /* skip tag + size + subtag */
2598     else {
2599       *offset = off + 8;
2600       *size = _size;
2601       break;
2602     }
2603   } while (TRUE);
2604
2605   return res;
2606 }
2607
2608 /*
2609  * gst_avi_demux_stream_scan:
2610  * @avi: calling element (used for debugging/errors).
2611  *
2612  * Scan the file for all chunks to "create" a new index.
2613  * pull-range based
2614  */
2615 static gboolean
2616 gst_avi_demux_stream_scan (GstAviDemux * avi)
2617 {
2618   GstFlowReturn res;
2619   GstAviStream *stream;
2620   GstFormat format;
2621   guint64 pos = 0;
2622   guint64 length;
2623   gint64 tmplength;
2624   guint32 tag = 0;
2625   guint num;
2626
2627   /* FIXME:
2628    * - implement non-seekable source support.
2629    */
2630   GST_DEBUG_OBJECT (avi, "Creating index");
2631
2632   /* get the size of the file */
2633   format = GST_FORMAT_BYTES;
2634   if (!gst_pad_query_peer_duration (avi->sinkpad, &format, &tmplength))
2635     return FALSE;
2636   length = tmplength;
2637
2638   /* guess the total amount of entries we expect */
2639   num = 16000;
2640
2641   while (TRUE) {
2642     GstAviIndexEntry entry;
2643     guint size = 0;
2644
2645     /* start reading data buffers to find the id and offset */
2646     res = gst_avi_demux_next_data_buffer (avi, &pos, &tag, &size);
2647     if (G_UNLIKELY (res != GST_FLOW_OK))
2648       break;
2649
2650     /* get stream */
2651     stream = gst_avi_demux_stream_for_id (avi, tag);
2652     if (G_UNLIKELY (!stream))
2653       goto next;
2654
2655     /* we can't figure out the keyframes, assume they all are */
2656     entry.flags = GST_AVI_KEYFRAME;
2657     entry.offset = pos;
2658     entry.size = size;
2659
2660     /* and add to the index of this stream */
2661     if (G_UNLIKELY (!gst_avi_demux_add_index (avi, stream, num, &entry)))
2662       goto out_of_mem;
2663
2664   next:
2665     /* update position */
2666     pos += GST_ROUND_UP_2 (size);
2667     if (G_UNLIKELY (pos > length)) {
2668       GST_WARNING_OBJECT (avi,
2669           "Stopping index lookup since we are further than EOF");
2670       break;
2671     }
2672   }
2673
2674   /* collect stats */
2675   avi->have_index = gst_avi_demux_do_index_stats (avi);
2676
2677
2678   return TRUE;
2679
2680   /* ERRORS */
2681 out_of_mem:
2682   {
2683     GST_ELEMENT_ERROR (avi, RESOURCE, NO_SPACE_LEFT, (NULL),
2684         ("Cannot allocate memory for %u*%u=%u bytes",
2685             (guint) sizeof (GstAviIndexEntry), num,
2686             (guint) sizeof (GstAviIndexEntry) * num));
2687     return FALSE;
2688   }
2689 }
2690
2691 static void
2692 gst_avi_demux_calculate_durations_from_index (GstAviDemux * avi)
2693 {
2694   guint i;
2695   GstClockTime total;
2696   GstAviStream *stream;
2697
2698   total = GST_CLOCK_TIME_NONE;
2699
2700   /* all streams start at a timestamp 0 */
2701   for (i = 0; i < avi->num_streams; i++) {
2702     GstClockTime duration, hduration;
2703     gst_riff_strh *strh;
2704
2705     stream = &avi->stream[i];
2706     if (G_UNLIKELY (!stream || !stream->idx_n || !(strh = stream->strh)))
2707       continue;
2708
2709     /* get header duration for the stream */
2710     hduration = stream->hdr_duration;
2711     /* index duration calculated during parsing */
2712     duration = stream->idx_duration;
2713
2714     /* now pick a good duration */
2715     if (GST_CLOCK_TIME_IS_VALID (duration)) {
2716       /* index gave valid duration, use that */
2717       GST_INFO ("Stream %p duration according to index: %" GST_TIME_FORMAT,
2718           stream, GST_TIME_ARGS (duration));
2719     } else {
2720       /* fall back to header info to calculate a duration */
2721       duration = hduration;
2722     }
2723     GST_INFO ("Setting duration of stream #%d to %" GST_TIME_FORMAT,
2724         i, GST_TIME_ARGS (duration));
2725     /* set duration for the stream */
2726     stream->duration = duration;
2727
2728     /* find total duration */
2729     if (total == GST_CLOCK_TIME_NONE ||
2730         (GST_CLOCK_TIME_IS_VALID (duration) && duration > total))
2731       total = duration;
2732   }
2733
2734   if (GST_CLOCK_TIME_IS_VALID (total) && (total > 0)) {
2735     /* now update the duration for those streams where we had none */
2736     for (i = 0; i < avi->num_streams; i++) {
2737       stream = &avi->stream[i];
2738
2739       if (!GST_CLOCK_TIME_IS_VALID (stream->duration)
2740           || stream->duration == 0) {
2741         stream->duration = total;
2742
2743         GST_INFO ("Stream %p duration according to total: %" GST_TIME_FORMAT,
2744             stream, GST_TIME_ARGS (total));
2745       }
2746     }
2747   }
2748
2749   /* and set the total duration in the segment. */
2750   GST_INFO ("Setting total duration to: %" GST_TIME_FORMAT,
2751       GST_TIME_ARGS (total));
2752
2753   gst_segment_set_duration (&avi->segment, GST_FORMAT_TIME, total);
2754 }
2755
2756 /* returns FALSE if there are no pads to deliver event to,
2757  * otherwise TRUE (whatever the outcome of event sending),
2758  * takes ownership of the event. */
2759 static gboolean
2760 gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event)
2761 {
2762   gboolean result = FALSE;
2763   gint i;
2764
2765   GST_DEBUG_OBJECT (avi, "sending %s event to %d streams",
2766       GST_EVENT_TYPE_NAME (event), avi->num_streams);
2767
2768   for (i = 0; i < avi->num_streams; i++) {
2769     GstAviStream *stream = &avi->stream[i];
2770
2771     if (stream->pad) {
2772       result = TRUE;
2773       gst_pad_push_event (stream->pad, gst_event_ref (event));
2774     }
2775   }
2776   gst_event_unref (event);
2777   return result;
2778 }
2779
2780 static void
2781 gst_avi_demux_check_seekability (GstAviDemux * avi)
2782 {
2783   GstQuery *query;
2784   gboolean seekable = FALSE;
2785   gint64 start = -1, stop = -1;
2786
2787   query = gst_query_new_seeking (GST_FORMAT_BYTES);
2788   if (!gst_pad_peer_query (avi->sinkpad, query)) {
2789     GST_DEBUG_OBJECT (avi, "seeking query failed");
2790     goto done;
2791   }
2792
2793   gst_query_parse_seeking (query, NULL, &seekable, &start, &stop);
2794
2795   /* try harder to query upstream size if we didn't get it the first time */
2796   if (seekable && stop == -1) {
2797     GstFormat fmt = GST_FORMAT_BYTES;
2798
2799     GST_DEBUG_OBJECT (avi, "doing duration query to fix up unset stop");
2800     gst_pad_query_peer_duration (avi->sinkpad, &fmt, &stop);
2801   }
2802
2803   /* if upstream doesn't know the size, it's likely that it's not seekable in
2804    * practice even if it technically may be seekable */
2805   if (seekable && (start != 0 || stop <= start)) {
2806     GST_DEBUG_OBJECT (avi, "seekable but unknown start/stop -> disable");
2807     seekable = FALSE;
2808   }
2809
2810   if (!avi->element_index) {
2811     GST_DEBUG_OBJECT (avi, "no index");
2812     seekable = FALSE;
2813   }
2814
2815 done:
2816   GST_INFO_OBJECT (avi, "seekable: %d (%" G_GUINT64_FORMAT " - %"
2817       G_GUINT64_FORMAT ")", seekable, start, stop);
2818   avi->seekable = seekable;
2819
2820   gst_query_unref (query);
2821 }
2822
2823 /*
2824  * Read AVI headers when streaming
2825  */
2826 static GstFlowReturn
2827 gst_avi_demux_stream_header_push (GstAviDemux * avi)
2828 {
2829   GstFlowReturn ret = GST_FLOW_OK;
2830   guint32 tag = 0;
2831   guint32 ltag = 0;
2832   guint32 size = 0;
2833   const guint8 *data;
2834   GstBuffer *buf = NULL, *sub = NULL;
2835   guint offset = 4;
2836   gint64 stop;
2837   gint i;
2838   GstTagList *tags = NULL;
2839
2840   GST_DEBUG ("Reading and parsing avi headers: %d", avi->header_state);
2841
2842   switch (avi->header_state) {
2843     case GST_AVI_DEMUX_HEADER_TAG_LIST:
2844       if (gst_avi_demux_peek_chunk (avi, &tag, &size)) {
2845         avi->offset += 8 + GST_ROUND_UP_2 (size);
2846         if (tag != GST_RIFF_TAG_LIST)
2847           goto header_no_list;
2848
2849         gst_adapter_flush (avi->adapter, 8);
2850         /* Find the 'hdrl' LIST tag */
2851         GST_DEBUG ("Reading %d bytes", size);
2852         buf = gst_adapter_take_buffer (avi->adapter, size);
2853
2854         if (GST_READ_UINT32_LE (GST_BUFFER_DATA (buf)) != GST_RIFF_LIST_hdrl)
2855           goto header_no_hdrl;
2856
2857         /* mind padding */
2858         if (size & 1)
2859           gst_adapter_flush (avi->adapter, 1);
2860
2861         GST_DEBUG ("'hdrl' LIST tag found. Parsing next chunk");
2862
2863         /* the hdrl starts with a 'avih' header */
2864         if (!gst_riff_parse_chunk (GST_ELEMENT_CAST (avi), buf, &offset, &tag,
2865                 &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 (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_CAST (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_CAST (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->seg_event)
3047     gst_event_unref (avi->seg_event);
3048   avi->seg_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_CAST (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 (avi, 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->seg_event)
3521     gst_event_unref (avi->seg_event);
3522   avi->seg_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_CAST (avi),
3828         gst_message_new_segment_start (GST_OBJECT_CAST (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->seg_event)
3838     gst_event_unref (avi->seg_event);
3839   if (avi->segment.rate > 0.0) {
3840     /* forwards goes from last_stop to stop */
3841     avi->seg_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->seg_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->seg_event)) {
4642         gst_avi_demux_push_event (avi, avi->seg_event);
4643         avi->seg_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_CAST (avi),
4686             gst_message_new_segment_done (GST_OBJECT_CAST (avi),
4687                 GST_FORMAT_TIME, 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->seg_event)) {
4732         gst_avi_demux_push_event (avi, avi->seg_event);
4733         avi->seg_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_CAST (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_CAST (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 }