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