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