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