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