gst_plugins_good/gst/avi/gstavimux.c
changeset 26 69c7080681bf
parent 24 bc39b352897e
child 28 4ed5253bb6ba
equal deleted inserted replaced
24:bc39b352897e 26:69c7080681bf
     1 /* AVI muxer plugin for GStreamer
       
     2  * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
       
     3  *           (C) 2006 Mark Nauwelaerts <manauw@skynet.be>
       
     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 
       
    21 /* based on:
       
    22  * - the old avimuxer (by Wim Taymans)
       
    23  * - xawtv's aviwriter (by Gerd Knorr)
       
    24  * - mjpegtools' avilib (by Rainer Johanni)
       
    25  * - openDML large-AVI docs
       
    26  */
       
    27 
       
    28 /**
       
    29  * SECTION:element-avimux
       
    30  *
       
    31  * Muxes raw or compressed audio and/or video streams into an AVI file.
       
    32  *
       
    33  * <refsect2>
       
    34  * <title>Example launch lines</title>
       
    35  * <para>(write everything in one line, without the backslash characters)</para>
       
    36  * |[
       
    37  * gst-launch videotestsrc num-buffers=250 \
       
    38  * ! 'video/x-raw-yuv,format=(fourcc)I420,width=320,height=240,framerate=(fraction)25/1' \
       
    39  * ! queue ! mux. \
       
    40  * audiotestsrc num-buffers=440 ! audioconvert \
       
    41  * ! 'audio/x-raw-int,rate=44100,channels=2' ! queue ! mux. \
       
    42  * avimux name=mux ! filesink location=test.avi
       
    43  * ]| This will create an .AVI file containing an uncompressed video stream
       
    44  * with a test picture and an uncompressed audio stream containing a 
       
    45  * test sound.
       
    46  * |[
       
    47  * gst-launch videotestsrc num-buffers=250 \
       
    48  * ! 'video/x-raw-yuv,format=(fourcc)I420,width=320,height=240,framerate=(fraction)25/1' \
       
    49  * ! xvidenc ! queue ! mux. \
       
    50  * audiotestsrc num-buffers=440 ! audioconvert ! 'audio/x-raw-int,rate=44100,channels=2' \
       
    51  * ! lame ! queue ! mux. \
       
    52  * avimux name=mux ! filesink location=test.avi
       
    53  * ]| This will create an .AVI file containing the same test video and sound
       
    54  * as above, only that both streams will be compressed this time. This will
       
    55  * only work if you have the necessary encoder elements installed of course.
       
    56  * </refsect2>
       
    57  */
       
    58 
       
    59 #ifdef HAVE_CONFIG_H
       
    60 #include "config.h"
       
    61 #endif
       
    62 
       
    63 #include "gst/gst-i18n-plugin.h"
       
    64 #include <stdlib.h>
       
    65 #include <string.h>
       
    66 
       
    67 #include <gst/video/video.h>
       
    68 
       
    69 #include "gstavimux.h"
       
    70 
       
    71 GST_DEBUG_CATEGORY_STATIC (avimux_debug);
       
    72 #define GST_CAT_DEFAULT avimux_debug
       
    73 
       
    74 enum
       
    75 {
       
    76   ARG_0,
       
    77   ARG_BIGFILE
       
    78 };
       
    79 
       
    80 #define DEFAULT_BIGFILE TRUE
       
    81 
       
    82 static const GstElementDetails gst_avi_mux_details =
       
    83 GST_ELEMENT_DETAILS ("Avi muxer",
       
    84     "Codec/Muxer",
       
    85     "Muxes audio and video into an avi stream",
       
    86     "Ronald Bultje <rbultje@ronald.bitfreak.net>");
       
    87 
       
    88 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
       
    89     GST_PAD_SRC,
       
    90     GST_PAD_ALWAYS,
       
    91     GST_STATIC_CAPS ("video/x-msvideo")
       
    92     );
       
    93 
       
    94 static GstStaticPadTemplate video_sink_factory =
       
    95     GST_STATIC_PAD_TEMPLATE ("video_%d",
       
    96     GST_PAD_SINK,
       
    97     GST_PAD_REQUEST,
       
    98     GST_STATIC_CAPS ("video/x-raw-yuv, "
       
    99         "format = (fourcc) { YUY2, I420 }, "
       
   100         "width = (int) [ 16, 4096 ], "
       
   101         "height = (int) [ 16, 4096 ], "
       
   102         "framerate = (fraction) [ 0, MAX ]; "
       
   103         "image/jpeg, "
       
   104         "width = (int) [ 16, 4096 ], "
       
   105         "height = (int) [ 16, 4096 ], "
       
   106         "framerate = (fraction) [ 0, MAX ]; "
       
   107         "video/x-divx, "
       
   108         "width = (int) [ 16, 4096 ], "
       
   109         "height = (int) [ 16, 4096 ], "
       
   110         "framerate = (fraction) [ 0, MAX ], "
       
   111         "divxversion = (int) [ 3, 5 ]; "
       
   112         "video/x-xvid, "
       
   113         "width = (int) [ 16, 4096 ], "
       
   114         "height = (int) [ 16, 4096 ], "
       
   115         "framerate = (fraction) [ 0, MAX ]; "
       
   116         "video/x-3ivx, "
       
   117         "width = (int) [ 16, 4096 ], "
       
   118         "height = (int) [ 16, 4096 ], "
       
   119         "framerate = (fraction) [ 0, MAX ]; "
       
   120         "video/x-msmpeg, "
       
   121         "width = (int) [ 16, 4096 ], "
       
   122         "height = (int) [ 16, 4096 ], "
       
   123         "framerate = (fraction) [ 0, MAX ], "
       
   124         "msmpegversion = (int) [ 41, 43 ]; "
       
   125         "video/mpeg, "
       
   126         "width = (int) [ 16, 4096 ], "
       
   127         "height = (int) [ 16, 4096 ], "
       
   128         "framerate = (fraction) [ 0, MAX ], "
       
   129         "mpegversion = (int) { 1, 2, 4}, "
       
   130         "systemstream = (boolean) FALSE; "
       
   131         "video/x-h263, "
       
   132         "width = (int) [ 16, 4096 ], "
       
   133         "height = (int) [ 16, 4096 ], "
       
   134         "framerate = (fraction) [ 0, MAX ]; "
       
   135         "video/x-h264, "
       
   136         "width = (int) [ 16, 4096 ], "
       
   137         "height = (int) [ 16, 4096 ], "
       
   138         "framerate = (fraction) [ 0, MAX ]; "
       
   139         "video/x-dv, "
       
   140         "width = (int) 720, "
       
   141         "height = (int) { 576, 480 }, "
       
   142         "framerate = (fraction) [ 0, MAX ], "
       
   143         "systemstream = (boolean) FALSE; "
       
   144         "video/x-huffyuv, "
       
   145         "width = (int) [ 16, 4096 ], "
       
   146         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ];"
       
   147         "video/x-dirac, "
       
   148         "width = (int) [ 16, 4096 ], "
       
   149         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ];"
       
   150         "video/x-wmv, "
       
   151         "width = (int) [ 16, 4096 ], "
       
   152         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ], "
       
   153         "wmvversion = (int) [ 1, 3]")
       
   154     );
       
   155 
       
   156 static GstStaticPadTemplate audio_sink_factory =
       
   157     GST_STATIC_PAD_TEMPLATE ("audio_%d",
       
   158     GST_PAD_SINK,
       
   159     GST_PAD_REQUEST,
       
   160     GST_STATIC_CAPS ("audio/x-raw-int, "
       
   161         "endianness = (int) LITTLE_ENDIAN, "
       
   162         "signed = (boolean) { TRUE, FALSE }, "
       
   163         "width = (int) { 8, 16 }, "
       
   164         "depth = (int) { 8, 16 }, "
       
   165         "rate = (int) [ 1000, 96000 ], "
       
   166         "channels = (int) [ 1, 2 ]; "
       
   167         "audio/mpeg, "
       
   168         "mpegversion = (int) 1, "
       
   169         "layer = (int) [ 1, 3 ], "
       
   170         "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
       
   171         "audio/mpeg, "
       
   172         "mpegversion = (int) 4, "
       
   173         "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
       
   174 /*#if 0 VC6 doesn't support #if here ...
       
   175         "audio/x-vorbis, "
       
   176         "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
       
   177 #endif*/
       
   178         "audio/x-ac3, "
       
   179         "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
       
   180         "audio/x-alaw, "
       
   181         "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; "
       
   182         "audio/x-mulaw, "
       
   183         "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; "
       
   184         "audio/x-wma, "
       
   185         "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ], "
       
   186         "wmaversion = (int) [ 1, 2 ] ")
       
   187     );
       
   188 
       
   189 static void gst_avi_mux_base_init (gpointer g_class);
       
   190 static void gst_avi_mux_class_init (GstAviMuxClass * klass);
       
   191 static void gst_avi_mux_init (GstAviMux * avimux);
       
   192 static void gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free);
       
   193 
       
   194 static GstFlowReturn gst_avi_mux_collect_pads (GstCollectPads * pads,
       
   195     GstAviMux * avimux);
       
   196 static gboolean gst_avi_mux_handle_event (GstPad * pad, GstEvent * event);
       
   197 static GstPad *gst_avi_mux_request_new_pad (GstElement * element,
       
   198     GstPadTemplate * templ, const gchar * name);
       
   199 static void gst_avi_mux_release_pad (GstElement * element, GstPad * pad);
       
   200 static void gst_avi_mux_set_property (GObject * object,
       
   201     guint prop_id, const GValue * value, GParamSpec * pspec);
       
   202 static void gst_avi_mux_get_property (GObject * object,
       
   203     guint prop_id, GValue * value, GParamSpec * pspec);
       
   204 static GstStateChangeReturn gst_avi_mux_change_state (GstElement * element,
       
   205     GstStateChange transition);
       
   206 
       
   207 static GstElementClass *parent_class = NULL;
       
   208 
       
   209 GType
       
   210 gst_avi_mux_get_type (void)
       
   211 {
       
   212   static GType avimux_type = 0;
       
   213 
       
   214   if (!avimux_type) {
       
   215     static const GTypeInfo avimux_info = {
       
   216       sizeof (GstAviMuxClass),
       
   217       gst_avi_mux_base_init,
       
   218       NULL,
       
   219       (GClassInitFunc) gst_avi_mux_class_init,
       
   220       NULL,
       
   221       NULL,
       
   222       sizeof (GstAviMux),
       
   223       0,
       
   224       (GInstanceInitFunc) gst_avi_mux_init,
       
   225     };
       
   226     static const GInterfaceInfo tag_setter_info = {
       
   227       NULL,
       
   228       NULL,
       
   229       NULL
       
   230     };
       
   231 
       
   232     avimux_type =
       
   233         g_type_register_static (GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
       
   234     g_type_add_interface_static (avimux_type, GST_TYPE_TAG_SETTER,
       
   235         &tag_setter_info);
       
   236   }
       
   237   return avimux_type;
       
   238 }
       
   239 
       
   240 static void
       
   241 gst_avi_mux_base_init (gpointer g_class)
       
   242 {
       
   243   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   244 
       
   245   gst_element_class_add_pad_template (element_class,
       
   246       gst_static_pad_template_get (&src_factory));
       
   247   gst_element_class_add_pad_template (element_class,
       
   248       gst_static_pad_template_get (&audio_sink_factory));
       
   249   gst_element_class_add_pad_template (element_class,
       
   250       gst_static_pad_template_get (&video_sink_factory));
       
   251 
       
   252   gst_element_class_set_details (element_class, &gst_avi_mux_details);
       
   253 
       
   254   GST_DEBUG_CATEGORY_INIT (avimux_debug, "avimux", 0, "Muxer for AVI streams");
       
   255 }
       
   256 
       
   257 static void
       
   258 gst_avi_mux_finalize (GObject * object)
       
   259 {
       
   260   GstAviMux *mux = GST_AVI_MUX (object);
       
   261   GSList *node;
       
   262 
       
   263   /* completely free each sinkpad */
       
   264   node = mux->sinkpads;
       
   265   while (node) {
       
   266     GstAviPad *avipad = (GstAviPad *) node->data;
       
   267 
       
   268     node = node->next;
       
   269 
       
   270     gst_avi_mux_pad_reset (avipad, TRUE);
       
   271     g_free (avipad);
       
   272   }
       
   273   g_slist_free (mux->sinkpads);
       
   274   mux->sinkpads = NULL;
       
   275 
       
   276   g_free (mux->idx);
       
   277   mux->idx = NULL;
       
   278 
       
   279   gst_object_unref (mux->collect);
       
   280 
       
   281   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   282 }
       
   283 
       
   284 static void
       
   285 gst_avi_mux_class_init (GstAviMuxClass * klass)
       
   286 {
       
   287   GObjectClass *gobject_class;
       
   288   GstElementClass *gstelement_class;
       
   289 
       
   290   gobject_class = (GObjectClass *) klass;
       
   291   gstelement_class = (GstElementClass *) klass;
       
   292 
       
   293   parent_class = g_type_class_peek_parent (klass);
       
   294 
       
   295   gobject_class->get_property = gst_avi_mux_get_property;
       
   296   gobject_class->set_property = gst_avi_mux_set_property;
       
   297   gobject_class->finalize = gst_avi_mux_finalize;
       
   298 
       
   299   g_object_class_install_property (gobject_class, ARG_BIGFILE,
       
   300       g_param_spec_boolean ("bigfile", "Bigfile Support (>2GB)",
       
   301           "Support for openDML-2.0 (big) AVI files", DEFAULT_BIGFILE,
       
   302           G_PARAM_READWRITE));
       
   303 
       
   304   gstelement_class->request_new_pad =
       
   305       GST_DEBUG_FUNCPTR (gst_avi_mux_request_new_pad);
       
   306   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_avi_mux_release_pad);
       
   307   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_avi_mux_change_state);
       
   308 }
       
   309 
       
   310 /* reset pad to initial state
       
   311  * free - if true, release all, not only stream related, data */
       
   312 static void
       
   313 gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
       
   314 {
       
   315   /* generic part */
       
   316   memset (&(avipad->hdr), 0, sizeof (gst_riff_strh));
       
   317 
       
   318   memset (&(avipad->idx[0]), 0, sizeof (avipad->idx));
       
   319 
       
   320   if (free) {
       
   321     g_free (avipad->tag);
       
   322     avipad->tag = NULL;
       
   323     g_free (avipad->idx_tag);
       
   324     avipad->idx_tag = NULL;
       
   325   }
       
   326 
       
   327   if (avipad->is_video) {
       
   328     GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
       
   329 
       
   330     avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
       
   331     if (vidpad->vids_codec_data) {
       
   332       gst_buffer_unref (vidpad->vids_codec_data);
       
   333       vidpad->vids_codec_data = NULL;
       
   334     }
       
   335 
       
   336     memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids));
       
   337     memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
       
   338   } else {
       
   339     GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
       
   340 
       
   341     avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
       
   342     if (audpad->auds_codec_data) {
       
   343       gst_buffer_unref (audpad->auds_codec_data);
       
   344       audpad->auds_codec_data = NULL;
       
   345     }
       
   346 
       
   347     memset (&(audpad->auds), 0, sizeof (gst_riff_strf_auds));
       
   348   }
       
   349 }
       
   350 
       
   351 static void
       
   352 gst_avi_mux_reset (GstAviMux * avimux)
       
   353 {
       
   354   GSList *node, *newlist = NULL;
       
   355 
       
   356   /* free and reset each sinkpad */
       
   357   node = avimux->sinkpads;
       
   358   while (node) {
       
   359     GstAviPad *avipad = (GstAviPad *) node->data;
       
   360 
       
   361     node = node->next;
       
   362 
       
   363     gst_avi_mux_pad_reset (avipad, FALSE);
       
   364     /* if this pad has collectdata, keep it, otherwise dump it completely */
       
   365     if (avipad->collect)
       
   366       newlist = g_slist_append (newlist, avipad);
       
   367     else {
       
   368       gst_avi_mux_pad_reset (avipad, TRUE);
       
   369       g_free (avipad);
       
   370     }
       
   371   }
       
   372 
       
   373   /* free the old list of sinkpads, only keep the real collecting ones */
       
   374   g_slist_free (avimux->sinkpads);
       
   375   avimux->sinkpads = newlist;
       
   376 
       
   377   /* avi data */
       
   378   avimux->num_frames = 0;
       
   379   memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
       
   380   avimux->avi_hdr.max_bps = 10000000;
       
   381   avimux->codec_data_size = 0;
       
   382 
       
   383   if (avimux->tags_snap) {
       
   384     gst_tag_list_free (avimux->tags_snap);
       
   385     avimux->tags_snap = NULL;
       
   386   }
       
   387 
       
   388   g_free (avimux->idx);
       
   389   avimux->idx = NULL;
       
   390 
       
   391   /* state info */
       
   392   avimux->write_header = TRUE;
       
   393 
       
   394   /* tags */
       
   395   gst_tag_setter_reset_tags (GST_TAG_SETTER (avimux));
       
   396 }
       
   397 
       
   398 static void
       
   399 gst_avi_mux_init (GstAviMux * avimux)
       
   400 {
       
   401   avimux->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
       
   402   gst_pad_use_fixed_caps (avimux->srcpad);
       
   403   gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
       
   404 
       
   405   /* property */
       
   406   avimux->enable_large_avi = DEFAULT_BIGFILE;
       
   407 
       
   408   avimux->collect = gst_collect_pads_new ();
       
   409   gst_collect_pads_set_function (avimux->collect,
       
   410       (GstCollectPadsFunction) (GST_DEBUG_FUNCPTR (gst_avi_mux_collect_pads)),
       
   411       avimux);
       
   412 
       
   413   /* set to clean state */
       
   414   gst_avi_mux_reset (avimux);
       
   415 }
       
   416 
       
   417 static gboolean
       
   418 gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
       
   419 {
       
   420   GstAviMux *avimux;
       
   421   GstAviVideoPad *avipad;
       
   422   GstAviCollectData *collect_pad;
       
   423   GstStructure *structure;
       
   424   const gchar *mimetype;
       
   425   const GValue *fps, *par;
       
   426   const GValue *codec_data;
       
   427   gint width, height;
       
   428   gint par_n, par_d;
       
   429 
       
   430   avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
       
   431 
       
   432   /* find stream data */
       
   433   collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad);
       
   434   g_assert (collect_pad);
       
   435   avipad = (GstAviVideoPad *) collect_pad->avipad;
       
   436   g_assert (avipad);
       
   437   g_assert (avipad->parent.is_video);
       
   438   g_assert (avipad->parent.hdr.type == GST_MAKE_FOURCC ('v', 'i', 'd', 's'));
       
   439 
       
   440   GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
       
   441       GST_DEBUG_PAD_NAME (pad), vscaps);
       
   442 
       
   443   structure = gst_caps_get_structure (vscaps, 0);
       
   444   mimetype = gst_structure_get_name (structure);
       
   445 
       
   446   /* global */
       
   447   avipad->vids.size = sizeof (gst_riff_strf_vids);
       
   448   avipad->vids.planes = 1;
       
   449   if (!gst_structure_get_int (structure, "width", &width) ||
       
   450       !gst_structure_get_int (structure, "height", &height)) {
       
   451     goto refuse_caps;
       
   452   }
       
   453 
       
   454   avipad->vids.width = width;
       
   455   avipad->vids.height = height;
       
   456 
       
   457   fps = gst_structure_get_value (structure, "framerate");
       
   458   if (fps == NULL || !GST_VALUE_HOLDS_FRACTION (fps))
       
   459     goto refuse_caps;
       
   460 
       
   461   avipad->parent.hdr.rate = gst_value_get_fraction_numerator (fps);
       
   462   avipad->parent.hdr.scale = gst_value_get_fraction_denominator (fps);
       
   463 
       
   464   /* (pixel) aspect ratio data, if any */
       
   465   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
       
   466   /* only use video properties header if there is non-trivial aspect info */
       
   467   if (par && GST_VALUE_HOLDS_FRACTION (par) &&
       
   468       ((par_n = gst_value_get_fraction_numerator (par)) !=
       
   469           (par_d = gst_value_get_fraction_denominator (par)))) {
       
   470     GValue to_ratio = { 0, };
       
   471     guint ratio_n, ratio_d;
       
   472 
       
   473     /* some fraction voodoo to obtain simplest possible ratio */
       
   474     g_value_init (&to_ratio, GST_TYPE_FRACTION);
       
   475     gst_value_set_fraction (&to_ratio, width * par_n, height * par_d);
       
   476     ratio_n = gst_value_get_fraction_numerator (&to_ratio);
       
   477     ratio_d = gst_value_get_fraction_denominator (&to_ratio);
       
   478     GST_DEBUG_OBJECT (avimux, "generating vprp data with aspect ratio %d/%d",
       
   479         ratio_n, ratio_d);
       
   480     /* simply fill in */
       
   481     avipad->vprp.vert_rate = avipad->parent.hdr.rate / avipad->parent.hdr.scale;
       
   482     avipad->vprp.hor_t_total = width;
       
   483     avipad->vprp.vert_lines = height;
       
   484     avipad->vprp.aspect = (ratio_n) << 16 | (ratio_d & 0xffff);
       
   485     avipad->vprp.width = width;
       
   486     avipad->vprp.height = height;
       
   487     avipad->vprp.fields = 1;
       
   488     avipad->vprp.field_info[0].compressed_bm_height = height;
       
   489     avipad->vprp.field_info[0].compressed_bm_width = width;
       
   490     avipad->vprp.field_info[0].valid_bm_height = height;
       
   491     avipad->vprp.field_info[0].valid_bm_width = width;
       
   492   }
       
   493 
       
   494   /* codec initialization data, if any */
       
   495   codec_data = gst_structure_get_value (structure, "codec_data");
       
   496   if (codec_data) {
       
   497     avipad->vids_codec_data = gst_value_get_buffer (codec_data);
       
   498     gst_buffer_ref (avipad->vids_codec_data);
       
   499     /* keep global track of size */
       
   500     avimux->codec_data_size += GST_BUFFER_SIZE (avipad->vids_codec_data);
       
   501   }
       
   502 
       
   503   if (!strcmp (mimetype, "video/x-raw-yuv")) {
       
   504     guint32 format;
       
   505 
       
   506     gst_structure_get_fourcc (structure, "format", &format);
       
   507     avipad->vids.compression = format;
       
   508     switch (format) {
       
   509       case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
       
   510         avipad->vids.bit_cnt = 16;
       
   511         break;
       
   512       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
       
   513         avipad->vids.bit_cnt = 12;
       
   514         break;
       
   515     }
       
   516   } else {
       
   517     avipad->vids.bit_cnt = 24;
       
   518     avipad->vids.compression = 0;
       
   519 
       
   520     /* find format */
       
   521     if (!strcmp (mimetype, "video/x-huffyuv")) {
       
   522       avipad->vids.compression = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
       
   523     } else if (!strcmp (mimetype, "image/jpeg")) {
       
   524       avipad->vids.compression = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
       
   525     } else if (!strcmp (mimetype, "video/x-divx")) {
       
   526       gint divxversion;
       
   527 
       
   528       gst_structure_get_int (structure, "divxversion", &divxversion);
       
   529       switch (divxversion) {
       
   530         case 3:
       
   531           avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
       
   532           break;
       
   533         case 4:
       
   534           avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
       
   535           break;
       
   536         case 5:
       
   537           avipad->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
       
   538           break;
       
   539       }
       
   540     } else if (!strcmp (mimetype, "video/x-xvid")) {
       
   541       avipad->vids.compression = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
       
   542     } else if (!strcmp (mimetype, "video/x-3ivx")) {
       
   543       avipad->vids.compression = GST_MAKE_FOURCC ('3', 'I', 'V', '2');
       
   544     } else if (gst_structure_has_name (structure, "video/x-msmpeg")) {
       
   545       gint msmpegversion;
       
   546 
       
   547       gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
       
   548       switch (msmpegversion) {
       
   549         case 41:
       
   550           avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
       
   551           break;
       
   552         case 42:
       
   553           avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
       
   554           break;
       
   555         case 43:
       
   556           avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
       
   557           break;
       
   558         default:
       
   559           GST_INFO ("unhandled msmpegversion : %d, fall back to fourcc=MPEG",
       
   560               msmpegversion);
       
   561           avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
       
   562           break;
       
   563       }
       
   564     } else if (!strcmp (mimetype, "video/x-dv")) {
       
   565       avipad->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
       
   566     } else if (!strcmp (mimetype, "video/x-h263")) {
       
   567       avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
       
   568     } else if (!strcmp (mimetype, "video/x-h264")) {
       
   569       avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '4');
       
   570     } else if (!strcmp (mimetype, "video/mpeg")) {
       
   571       gint mpegversion;
       
   572 
       
   573       gst_structure_get_int (structure, "mpegversion", &mpegversion);
       
   574 
       
   575       switch (mpegversion) {
       
   576         case 2:
       
   577           avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '2');
       
   578           break;
       
   579         case 4:
       
   580           /* mplayer/ffmpeg might not work with DIVX, but with FMP4 */
       
   581           avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
       
   582           break;
       
   583         default:
       
   584           GST_INFO ("unhandled mpegversion : %d, fall back to fourcc=MPEG",
       
   585               mpegversion);
       
   586           avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
       
   587           break;
       
   588       }
       
   589     } else if (!strcmp (mimetype, "video/x-dirac")) {
       
   590       avipad->vids.compression = GST_MAKE_FOURCC ('d', 'r', 'a', 'c');
       
   591     } else if (!strcmp (mimetype, "video/x-wmv")) {
       
   592       gint wmvversion;
       
   593 
       
   594       if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
       
   595         switch (wmvversion) {
       
   596           case 1:
       
   597             avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
       
   598             break;
       
   599           case 2:
       
   600             avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
       
   601             break;
       
   602           case 3:
       
   603             avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
       
   604           default:
       
   605             break;
       
   606         }
       
   607       }
       
   608     }
       
   609 
       
   610     if (!avipad->vids.compression)
       
   611       goto refuse_caps;
       
   612   }
       
   613 
       
   614   avipad->parent.hdr.fcc_handler = avipad->vids.compression;
       
   615   avipad->vids.image_size = avipad->vids.height * avipad->vids.width;
       
   616   /* hm, maybe why avi only handles one stream well ... */
       
   617   avimux->avi_hdr.width = avipad->vids.width;
       
   618   avimux->avi_hdr.height = avipad->vids.height;
       
   619   avimux->avi_hdr.us_frame = 1000000. * avipad->parent.hdr.scale /
       
   620       avipad->parent.hdr.rate;
       
   621 
       
   622   gst_object_unref (avimux);
       
   623   return TRUE;
       
   624 
       
   625 refuse_caps:
       
   626   {
       
   627     GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
       
   628     gst_object_unref (avimux);
       
   629     return FALSE;
       
   630   }
       
   631 }
       
   632 
       
   633 static gboolean
       
   634 gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps)
       
   635 {
       
   636   GstAviMux *avimux;
       
   637   GstAviAudioPad *avipad;
       
   638   GstAviCollectData *collect_pad;
       
   639   GstStructure *structure;
       
   640   const gchar *mimetype;
       
   641   const GValue *codec_data;
       
   642   gint channels, rate;
       
   643 
       
   644   avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
       
   645 
       
   646   /* find stream data */
       
   647   collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad);
       
   648   g_assert (collect_pad);
       
   649   avipad = (GstAviAudioPad *) collect_pad->avipad;
       
   650   g_assert (avipad);
       
   651   g_assert (!avipad->parent.is_video);
       
   652   g_assert (avipad->parent.hdr.type == GST_MAKE_FOURCC ('a', 'u', 'd', 's'));
       
   653 
       
   654   GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
       
   655       GST_DEBUG_PAD_NAME (pad), vscaps);
       
   656 
       
   657   structure = gst_caps_get_structure (vscaps, 0);
       
   658   mimetype = gst_structure_get_name (structure);
       
   659 
       
   660   /* we want these for all */
       
   661   if (!gst_structure_get_int (structure, "channels", &channels) ||
       
   662       !gst_structure_get_int (structure, "rate", &rate)) {
       
   663     goto refuse_caps;
       
   664   }
       
   665 
       
   666   avipad->auds.channels = channels;
       
   667   avipad->auds.rate = rate;
       
   668 
       
   669   /* codec initialization data, if any */
       
   670   codec_data = gst_structure_get_value (structure, "codec_data");
       
   671   if (codec_data) {
       
   672     avipad->auds_codec_data = gst_value_get_buffer (codec_data);
       
   673     gst_buffer_ref (avipad->auds_codec_data);
       
   674     /* keep global track of size */
       
   675     avimux->codec_data_size += GST_BUFFER_SIZE (avipad->auds_codec_data);
       
   676   }
       
   677 
       
   678   if (!strcmp (mimetype, "audio/x-raw-int")) {
       
   679     gint width, depth;
       
   680     gboolean signedness;
       
   681 
       
   682     avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
       
   683 
       
   684     if (!gst_structure_get_int (structure, "width", &width) ||
       
   685         !gst_structure_get_int (structure, "depth", &depth) ||
       
   686         !gst_structure_get_boolean (structure, "signed", &signedness)) {
       
   687       GST_DEBUG_OBJECT (avimux,
       
   688           "broken caps, width/depth/signed field missing");
       
   689       goto refuse_caps;
       
   690     }
       
   691 
       
   692     /* no clear place to put different values for these while keeping to spec */
       
   693     if (width != depth) {
       
   694       GST_DEBUG_OBJECT (avimux, "width must be same as depth!");
       
   695       goto refuse_caps;
       
   696     }
       
   697 
       
   698     /* because that's the way the caps will be recreated from riff data */
       
   699     if ((width == 8 && signedness) || (width == 16 && !signedness)) {
       
   700       GST_DEBUG_OBJECT (avimux,
       
   701           "8-bit PCM must be unsigned, 16-bit PCM signed");
       
   702       goto refuse_caps;
       
   703     }
       
   704 
       
   705     avipad->auds.blockalign = width;
       
   706     avipad->auds.size = (width == 8) ? 8 : depth;
       
   707 
       
   708     /* set some more info straight */
       
   709     avipad->auds.blockalign /= 8;
       
   710     avipad->auds.blockalign *= avipad->auds.channels;
       
   711     avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
       
   712   } else {
       
   713     avipad->auds.format = 0;
       
   714     /* set some defaults */
       
   715     avipad->auds.blockalign = 1;
       
   716     avipad->auds.av_bps = 0;
       
   717     avipad->auds.size = 16;
       
   718 
       
   719     if (!strcmp (mimetype, "audio/mpeg")) {
       
   720       gint mpegversion;
       
   721 
       
   722       gst_structure_get_int (structure, "mpegversion", &mpegversion);
       
   723       switch (mpegversion) {
       
   724         case 1:{
       
   725           gint layer = 3;
       
   726 
       
   727           gst_structure_get_int (structure, "layer", &layer);
       
   728           switch (layer) {
       
   729             case 3:
       
   730               avipad->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
       
   731               break;
       
   732             case 1:
       
   733             case 2:
       
   734               avipad->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
       
   735               break;
       
   736           }
       
   737           break;
       
   738         }
       
   739         case 4:
       
   740           GST_WARNING ("AAC");
       
   741           avipad->auds.format = GST_RIFF_WAVE_FORMAT_AAC;
       
   742           break;
       
   743       }
       
   744     } else if (!strcmp (mimetype, "audio/x-vorbis")) {
       
   745       avipad->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
       
   746     } else if (!strcmp (mimetype, "audio/x-ac3")) {
       
   747       avipad->auds.format = GST_RIFF_WAVE_FORMAT_A52;
       
   748     } else if (!strcmp (mimetype, "audio/x-alaw")) {
       
   749       avipad->auds.format = GST_RIFF_WAVE_FORMAT_ALAW;
       
   750       avipad->auds.size = 8;
       
   751       avipad->auds.blockalign = avipad->auds.channels;
       
   752       avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
       
   753     } else if (!strcmp (mimetype, "audio/x-mulaw")) {
       
   754       avipad->auds.format = GST_RIFF_WAVE_FORMAT_MULAW;
       
   755       avipad->auds.size = 8;
       
   756       avipad->auds.blockalign = avipad->auds.channels;
       
   757       avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
       
   758     } else if (!strcmp (mimetype, "audio/x-wma")) {
       
   759       gint version;
       
   760       gint bitrate;
       
   761       gint block_align;
       
   762 
       
   763       if (gst_structure_get_int (structure, "wmaversion", &version)) {
       
   764         switch (version) {
       
   765           case 1:
       
   766             avipad->auds.format = GST_RIFF_WAVE_FORMAT_WMAV1;
       
   767             break;
       
   768           case 2:
       
   769             avipad->auds.format = GST_RIFF_WAVE_FORMAT_WMAV2;
       
   770             break;
       
   771           default:
       
   772             break;
       
   773         }
       
   774       }
       
   775 
       
   776       if (avipad->auds.format != 0) {
       
   777         if (gst_structure_get_int (structure, "block_align", &block_align)) {
       
   778           avipad->auds.blockalign = block_align;
       
   779         }
       
   780         if (gst_structure_get_int (structure, "bitrate", &bitrate)) {
       
   781           avipad->auds.av_bps = bitrate / 8;
       
   782         }
       
   783       }
       
   784     }
       
   785   }
       
   786 
       
   787   if (!avipad->auds.format)
       
   788     goto refuse_caps;
       
   789 
       
   790   /* by spec, hdr.rate is av_bps related, is calculated that way in stop_file,
       
   791    * and reduces to sample rate in PCM like cases */
       
   792   avipad->parent.hdr.rate = avipad->auds.av_bps / avipad->auds.blockalign;
       
   793   avipad->parent.hdr.samplesize = avipad->auds.blockalign;
       
   794   avipad->parent.hdr.scale = 1;
       
   795 
       
   796   gst_object_unref (avimux);
       
   797   return TRUE;
       
   798 
       
   799 refuse_caps:
       
   800   {
       
   801     GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
       
   802     gst_object_unref (avimux);
       
   803     return FALSE;
       
   804   }
       
   805 }
       
   806 
       
   807 
       
   808 static GstPad *
       
   809 gst_avi_mux_request_new_pad (GstElement * element,
       
   810     GstPadTemplate * templ, const gchar * req_name)
       
   811 {
       
   812   GstAviMux *avimux;
       
   813   GstPad *newpad;
       
   814   GstAviPad *avipad;
       
   815   GstElementClass *klass;
       
   816 
       
   817   g_return_val_if_fail (templ != NULL, NULL);
       
   818 
       
   819   if (templ->direction != GST_PAD_SINK)
       
   820     goto wrong_direction;
       
   821 
       
   822   g_return_val_if_fail (GST_IS_AVI_MUX (element), NULL);
       
   823   avimux = GST_AVI_MUX (element);
       
   824 
       
   825   if (!avimux->write_header)
       
   826     goto too_late;
       
   827 
       
   828   klass = GST_ELEMENT_GET_CLASS (element);
       
   829 
       
   830   if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
       
   831     gchar *name;
       
   832 
       
   833     /* setup pad */
       
   834     name = g_strdup_printf ("audio_%02d", avimux->audio_pads);
       
   835     GST_DEBUG_OBJECT (avimux, "adding new pad: %s", name);
       
   836     newpad = gst_pad_new_from_template (templ, name);
       
   837     g_free (name);
       
   838     gst_pad_set_setcaps_function (newpad,
       
   839         GST_DEBUG_FUNCPTR (gst_avi_mux_audsink_set_caps));
       
   840 
       
   841     /* init pad specific data */
       
   842     avipad = g_malloc0 (sizeof (GstAviAudioPad));
       
   843     avipad->is_video = FALSE;
       
   844     avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
       
   845     avimux->audio_pads++;
       
   846     /* audio goes last */
       
   847     avimux->sinkpads = g_slist_append (avimux->sinkpads, avipad);
       
   848   } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
       
   849     /* though streams are pretty generic and relatively self-contained,
       
   850      * some video info goes in a single avi header -and therefore mux struct-
       
   851      * so video restricted to one stream */
       
   852     if (avimux->video_pads > 0)
       
   853       return NULL;
       
   854     /* setup pad */
       
   855     GST_DEBUG_OBJECT (avimux, "adding new pad: video_00");
       
   856     newpad = gst_pad_new_from_template (templ, "video_00");
       
   857     gst_pad_set_setcaps_function (newpad,
       
   858         GST_DEBUG_FUNCPTR (gst_avi_mux_vidsink_set_caps));
       
   859     avipad = g_malloc0 (sizeof (GstAviVideoPad));
       
   860 
       
   861     /* init pad specific data */
       
   862     avipad->is_video = TRUE;
       
   863     avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
       
   864     avimux->video_pads++;
       
   865     /* video goes first */
       
   866     avimux->sinkpads = g_slist_prepend (avimux->sinkpads, avipad);
       
   867   } else
       
   868     goto wrong_template;
       
   869 
       
   870   avipad->collect = gst_collect_pads_add_pad (avimux->collect,
       
   871       newpad, sizeof (GstAviCollectData));
       
   872   ((GstAviCollectData *) (avipad->collect))->avipad = avipad;
       
   873   /* FIXME: hacked way to override/extend the event function of
       
   874    * GstCollectPads; because it sets its own event function giving the
       
   875    * element no access to events */
       
   876   avimux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
       
   877   gst_pad_set_event_function (newpad,
       
   878       GST_DEBUG_FUNCPTR (gst_avi_mux_handle_event));
       
   879 
       
   880   gst_element_add_pad (element, newpad);
       
   881 
       
   882   return newpad;
       
   883 
       
   884   /* ERRORS */
       
   885 wrong_direction:
       
   886   {
       
   887     g_warning ("avimux: request pad that is not a SINK pad\n");
       
   888     return NULL;
       
   889   }
       
   890 too_late:
       
   891   {
       
   892     g_warning ("avimux: request pad cannot be added after streaming started\n");
       
   893     return NULL;
       
   894   }
       
   895 wrong_template:
       
   896   {
       
   897     g_warning ("avimuxx: this is not our template!\n");
       
   898     return NULL;
       
   899   }
       
   900 }
       
   901 
       
   902 static void
       
   903 gst_avi_mux_release_pad (GstElement * element, GstPad * pad)
       
   904 {
       
   905   GstAviMux *avimux = GST_AVI_MUX (element);
       
   906   GSList *node;
       
   907 
       
   908   node = avimux->sinkpads;
       
   909   while (node) {
       
   910     GstAviPad *avipad = (GstAviPad *) node->data;
       
   911 
       
   912     if (avipad->collect->pad == pad) {
       
   913       /* pad count should not be adjusted,
       
   914        * as it also represent number of streams present */
       
   915       avipad->collect = NULL;
       
   916       GST_DEBUG_OBJECT (avimux, "removed pad '%s'", GST_PAD_NAME (pad));
       
   917       gst_collect_pads_remove_pad (avimux->collect, pad);
       
   918       gst_element_remove_pad (element, pad);
       
   919       /* if not started yet, we can remove any sign this pad ever existed */
       
   920       /* in this case _start will take care of the real pad count */
       
   921       if (avimux->write_header) {
       
   922         avimux->sinkpads = g_slist_remove (avimux->sinkpads, avipad);
       
   923         gst_avi_mux_pad_reset (avipad, TRUE);
       
   924         g_free (avipad);
       
   925       }
       
   926       return;
       
   927     }
       
   928 
       
   929     node = node->next;
       
   930   }
       
   931 
       
   932   g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
       
   933 }
       
   934 
       
   935 /* maybe some of these functions should be moved to riff.h? */
       
   936 
       
   937 /* DISCLAIMER: this function is fairly ugly. So be it (i.e. it makes the rest easier)
       
   938  * so is this struct */
       
   939 
       
   940 typedef struct _GstMarkedBuffer
       
   941 {
       
   942   guint *highmark;
       
   943   GstBuffer *buffer;
       
   944 } GstMarkedBuffer;
       
   945 
       
   946 static void
       
   947 gst_avi_mux_write_tag (const GstTagList * list, const gchar * tag,
       
   948     gpointer data)
       
   949 {
       
   950   const struct
       
   951   {
       
   952     guint32 fcc;
       
   953     gchar *tag;
       
   954   } rifftags[] = {
       
   955     {
       
   956     GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
       
   957     GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
       
   958     GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
       
   959     GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
       
   960     GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
       
   961     GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
       
   962     GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
       
   963     GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
       
   964     GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
       
   965     GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
       
   966     0, NULL}
       
   967   };
       
   968   gint n, len, plen;
       
   969   GstBuffer *buf = ((GstMarkedBuffer *) data)->buffer;
       
   970   guint *highmark = ((GstMarkedBuffer *) data)->highmark;
       
   971   guint8 *buffdata = GST_BUFFER_DATA (buf) + *highmark;
       
   972   gchar *str;
       
   973 
       
   974   for (n = 0; rifftags[n].fcc != 0; n++) {
       
   975     if (!strcmp (rifftags[n].tag, tag) &&
       
   976         gst_tag_list_get_string (list, tag, &str) && str) {
       
   977       len = strlen (str);
       
   978       plen = len + 1;
       
   979       if (plen & 1)
       
   980         plen++;
       
   981       if (GST_BUFFER_SIZE (buf) >= *highmark + 8 + plen) {
       
   982         GST_WRITE_UINT32_LE (buffdata, rifftags[n].fcc);
       
   983         GST_WRITE_UINT32_LE (buffdata + 4, len + 1);
       
   984         memcpy (buffdata + 8, str, len);
       
   985         buffdata[8 + len] = 0;
       
   986         *highmark += 8 + plen;
       
   987         GST_DEBUG ("writing tag in buffer %p, highmark at %d", buf, *highmark);
       
   988       }
       
   989       g_free (str);
       
   990       break;
       
   991     }
       
   992   }
       
   993 }
       
   994 
       
   995 #define ODML_SUPERINDEX_SIZE    \
       
   996     (32 + GST_AVI_SUPERINDEX_COUNT * sizeof (gst_avi_superindex_entry))
       
   997 
       
   998 static GstBuffer *
       
   999 gst_avi_mux_riff_get_avi_header (GstAviMux * avimux)
       
  1000 {
       
  1001   const GstTagList *tags;
       
  1002   GstBuffer *buffer;
       
  1003   guint8 *buffdata;
       
  1004   guint size = 0;
       
  1005   guint highmark = 0;
       
  1006 
       
  1007   /* pointer to list size field */
       
  1008   guint8 *riff_size, *hdrl_size;
       
  1009   GSList *node;
       
  1010 
       
  1011   GST_DEBUG_OBJECT (avimux, "creating avi header, data_size %u, idx_size %u",
       
  1012       avimux->data_size, avimux->idx_size);
       
  1013 
       
  1014   if (avimux->tags_snap)
       
  1015     tags = avimux->tags_snap;
       
  1016   else {
       
  1017     /* need to make snapshot of current state of tags to ensure the same set
       
  1018      * is used next time around during header rewrite at the end */
       
  1019     tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (avimux));
       
  1020     if (tags)
       
  1021       tags = avimux->tags_snap = gst_tag_list_copy (tags);
       
  1022   }
       
  1023   if (tags) {
       
  1024     /* that should be the strlen of all tags + header sizes
       
  1025      * not all of tags end up in a avi, still this is a good estimate
       
  1026      */
       
  1027     gchar *str = gst_structure_to_string (tags);
       
  1028     size += strlen (str) + 8 * gst_structure_n_fields (tags);
       
  1029     g_free (str);
       
  1030   }
       
  1031 
       
  1032   /* allocate the buffer, starting with some wild/safe upper bound */
       
  1033   size += avimux->codec_data_size + 100 + sizeof (gst_riff_avih)
       
  1034       + (g_slist_length (avimux->sinkpads) * (100 + sizeof (gst_riff_strh_full)
       
  1035           + sizeof (gst_riff_strf_vids)
       
  1036           + sizeof (gst_riff_vprp)
       
  1037           + sizeof (gst_riff_vprp_video_field_desc) * 2
       
  1038           + sizeof (gst_riff_strf_auds) + 2 + ODML_SUPERINDEX_SIZE));
       
  1039   buffer = gst_buffer_new_and_alloc (size);
       
  1040   buffdata = GST_BUFFER_DATA (buffer);
       
  1041   highmark = 0;
       
  1042   GST_DEBUG_OBJECT (avimux, "creating buffer %p, size %d, highmark at 0",
       
  1043       buffer, GST_BUFFER_SIZE (buffer));
       
  1044 
       
  1045   /* avi header metadata */
       
  1046   memcpy (buffdata + 0, "RIFF", 4);
       
  1047   /* fill in RIFF size later */
       
  1048   riff_size = buffdata + 4;
       
  1049   memcpy (buffdata + 8, "AVI ", 4);
       
  1050   memcpy (buffdata + 12, "LIST", 4);
       
  1051   /* fill in header size later */
       
  1052   hdrl_size = buffdata + 16;
       
  1053   memcpy (buffdata + 20, "hdrl", 4);
       
  1054   memcpy (buffdata + 24, "avih", 4);
       
  1055   GST_WRITE_UINT32_LE (buffdata + 28, sizeof (gst_riff_avih));
       
  1056   buffdata += 32;
       
  1057   highmark += 32;
       
  1058 
       
  1059   /* the AVI header itself */
       
  1060   GST_WRITE_UINT32_LE (buffdata + 0, avimux->avi_hdr.us_frame);
       
  1061   GST_WRITE_UINT32_LE (buffdata + 4, avimux->avi_hdr.max_bps);
       
  1062   GST_WRITE_UINT32_LE (buffdata + 8, avimux->avi_hdr.pad_gran);
       
  1063   GST_WRITE_UINT32_LE (buffdata + 12, avimux->avi_hdr.flags);
       
  1064   GST_WRITE_UINT32_LE (buffdata + 16, avimux->avi_hdr.tot_frames);
       
  1065   GST_WRITE_UINT32_LE (buffdata + 20, avimux->avi_hdr.init_frames);
       
  1066   GST_WRITE_UINT32_LE (buffdata + 24, avimux->avi_hdr.streams);
       
  1067   GST_WRITE_UINT32_LE (buffdata + 28, avimux->avi_hdr.bufsize);
       
  1068   GST_WRITE_UINT32_LE (buffdata + 32, avimux->avi_hdr.width);
       
  1069   GST_WRITE_UINT32_LE (buffdata + 36, avimux->avi_hdr.height);
       
  1070   GST_WRITE_UINT32_LE (buffdata + 40, avimux->avi_hdr.scale);
       
  1071   GST_WRITE_UINT32_LE (buffdata + 44, avimux->avi_hdr.rate);
       
  1072   GST_WRITE_UINT32_LE (buffdata + 48, avimux->avi_hdr.start);
       
  1073   GST_WRITE_UINT32_LE (buffdata + 52, avimux->avi_hdr.length);
       
  1074   buffdata += 56;
       
  1075   highmark += 56;
       
  1076 
       
  1077   /* stream data */
       
  1078   node = avimux->sinkpads;
       
  1079   while (node) {
       
  1080     GstAviPad *avipad = (GstAviPad *) node->data;
       
  1081     GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
       
  1082     GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
       
  1083     guint codec_size = 0, strl_size = 0, vprp_size = 0;
       
  1084 
       
  1085     if (avipad->is_video) {
       
  1086       if (vidpad->vids_codec_data)
       
  1087         codec_size = GST_BUFFER_SIZE (vidpad->vids_codec_data);
       
  1088       strl_size = sizeof (gst_riff_strh_full) + sizeof (gst_riff_strf_vids)
       
  1089           + GST_ROUND_UP_2 (codec_size) + 4 * 5 + ODML_SUPERINDEX_SIZE;
       
  1090       if (vidpad->vprp.aspect) {
       
  1091         /* let's be on the safe side */
       
  1092         vidpad->vprp.fields = MIN (vidpad->vprp.fields,
       
  1093             GST_RIFF_VPRP_VIDEO_FIELDS);
       
  1094         vprp_size = G_STRUCT_OFFSET (gst_riff_vprp, field_info)
       
  1095             + (vidpad->vprp.fields * sizeof (gst_riff_vprp_video_field_desc));
       
  1096         strl_size += 4 * 2 + vprp_size;
       
  1097       }
       
  1098     } else {
       
  1099       if (audpad->auds_codec_data)
       
  1100         codec_size = GST_BUFFER_SIZE (audpad->auds_codec_data);
       
  1101       /* +2 is codec_size field, not part of gst_riff_strf_auds */
       
  1102       strl_size = sizeof (gst_riff_strh_full) + sizeof (gst_riff_strf_auds) + 2
       
  1103           + GST_ROUND_UP_2 (codec_size) + 4 * 5 + ODML_SUPERINDEX_SIZE;
       
  1104     }
       
  1105 
       
  1106     /* stream list metadata */
       
  1107     memcpy (buffdata + 0, "LIST", 4);
       
  1108     GST_WRITE_UINT32_LE (buffdata + 4, strl_size);
       
  1109     memcpy (buffdata + 8, "strl", 4);
       
  1110     /* generic header */
       
  1111     memcpy (buffdata + 12, "strh", 4);
       
  1112     GST_WRITE_UINT32_LE (buffdata + 16, sizeof (gst_riff_strh_full));
       
  1113     /* the actual header */
       
  1114     GST_WRITE_UINT32_LE (buffdata + 20, avipad->hdr.type);
       
  1115     GST_WRITE_UINT32_LE (buffdata + 24, avipad->hdr.fcc_handler);
       
  1116     GST_WRITE_UINT32_LE (buffdata + 28, avipad->hdr.flags);
       
  1117     GST_WRITE_UINT32_LE (buffdata + 32, avipad->hdr.priority);
       
  1118     GST_WRITE_UINT32_LE (buffdata + 36, avipad->hdr.init_frames);
       
  1119     GST_WRITE_UINT32_LE (buffdata + 40, avipad->hdr.scale);
       
  1120     GST_WRITE_UINT32_LE (buffdata + 44, avipad->hdr.rate);
       
  1121     GST_WRITE_UINT32_LE (buffdata + 48, avipad->hdr.start);
       
  1122     GST_WRITE_UINT32_LE (buffdata + 52, avipad->hdr.length);
       
  1123     GST_WRITE_UINT32_LE (buffdata + 56, avipad->hdr.bufsize);
       
  1124     GST_WRITE_UINT32_LE (buffdata + 60, avipad->hdr.quality);
       
  1125     GST_WRITE_UINT32_LE (buffdata + 64, avipad->hdr.samplesize);
       
  1126     GST_WRITE_UINT16_LE (buffdata + 68, 0);
       
  1127     GST_WRITE_UINT16_LE (buffdata + 70, 0);
       
  1128     GST_WRITE_UINT16_LE (buffdata + 72, 0);
       
  1129     GST_WRITE_UINT16_LE (buffdata + 74, 0);
       
  1130     buffdata += 76;
       
  1131     highmark += 76;
       
  1132 
       
  1133     if (avipad->is_video) {
       
  1134       /* the video header */
       
  1135       memcpy (buffdata + 0, "strf", 4);
       
  1136       GST_WRITE_UINT32_LE (buffdata + 4,
       
  1137           sizeof (gst_riff_strf_vids) + codec_size);
       
  1138       /* the actual header */
       
  1139       GST_WRITE_UINT32_LE (buffdata + 8, vidpad->vids.size + codec_size);
       
  1140       GST_WRITE_UINT32_LE (buffdata + 12, vidpad->vids.width);
       
  1141       GST_WRITE_UINT32_LE (buffdata + 16, vidpad->vids.height);
       
  1142       GST_WRITE_UINT16_LE (buffdata + 20, vidpad->vids.planes);
       
  1143       GST_WRITE_UINT16_LE (buffdata + 22, vidpad->vids.bit_cnt);
       
  1144       GST_WRITE_UINT32_LE (buffdata + 24, vidpad->vids.compression);
       
  1145       GST_WRITE_UINT32_LE (buffdata + 28, vidpad->vids.image_size);
       
  1146       GST_WRITE_UINT32_LE (buffdata + 32, vidpad->vids.xpels_meter);
       
  1147       GST_WRITE_UINT32_LE (buffdata + 36, vidpad->vids.ypels_meter);
       
  1148       GST_WRITE_UINT32_LE (buffdata + 40, vidpad->vids.num_colors);
       
  1149       GST_WRITE_UINT32_LE (buffdata + 44, vidpad->vids.imp_colors);
       
  1150       buffdata += 48;
       
  1151       highmark += 48;
       
  1152 
       
  1153       /* include codec data, if any */
       
  1154       if (codec_size) {
       
  1155         memcpy (buffdata, GST_BUFFER_DATA (vidpad->vids_codec_data),
       
  1156             codec_size);
       
  1157 
       
  1158         buffdata += codec_size;
       
  1159         highmark += codec_size;
       
  1160       }
       
  1161       /* padding */
       
  1162       if (highmark & 0x1) {
       
  1163         highmark++;
       
  1164         buffdata++;
       
  1165       }
       
  1166 
       
  1167       /* add video property data, mainly for aspect ratio, if any */
       
  1168       if (vprp_size) {
       
  1169         gint f;
       
  1170 
       
  1171         /* the vprp header */
       
  1172         memcpy (buffdata + 0, "vprp", 4);
       
  1173         GST_WRITE_UINT32_LE (buffdata + 4, vprp_size);
       
  1174         /* the actual data */
       
  1175         GST_WRITE_UINT32_LE (buffdata + 8, vidpad->vprp.format_token);
       
  1176         GST_WRITE_UINT32_LE (buffdata + 12, vidpad->vprp.standard);
       
  1177         GST_WRITE_UINT32_LE (buffdata + 16, vidpad->vprp.vert_rate);
       
  1178         GST_WRITE_UINT32_LE (buffdata + 20, vidpad->vprp.hor_t_total);
       
  1179         GST_WRITE_UINT32_LE (buffdata + 24, vidpad->vprp.vert_lines);
       
  1180         GST_WRITE_UINT32_LE (buffdata + 28, vidpad->vprp.aspect);
       
  1181         GST_WRITE_UINT32_LE (buffdata + 32, vidpad->vprp.width);
       
  1182         GST_WRITE_UINT32_LE (buffdata + 36, vidpad->vprp.height);
       
  1183         GST_WRITE_UINT32_LE (buffdata + 40, vidpad->vprp.fields);
       
  1184         buffdata += 44;
       
  1185         highmark += 44;
       
  1186         for (f = 0; f < vidpad->vprp.fields; ++f) {
       
  1187           gst_riff_vprp_video_field_desc *fd;
       
  1188 
       
  1189           fd = &(vidpad->vprp.field_info[f]);
       
  1190           GST_WRITE_UINT32_LE (buffdata + 0, fd->compressed_bm_height);
       
  1191           GST_WRITE_UINT32_LE (buffdata + 4, fd->compressed_bm_width);
       
  1192           GST_WRITE_UINT32_LE (buffdata + 8, fd->valid_bm_height);
       
  1193           GST_WRITE_UINT32_LE (buffdata + 12, fd->valid_bm_width);
       
  1194           GST_WRITE_UINT32_LE (buffdata + 16, fd->valid_bm_x_offset);
       
  1195           GST_WRITE_UINT32_LE (buffdata + 20, fd->valid_bm_y_offset);
       
  1196           GST_WRITE_UINT32_LE (buffdata + 24, fd->video_x_t_offset);
       
  1197           GST_WRITE_UINT32_LE (buffdata + 28, fd->video_y_start);
       
  1198           buffdata += 32;
       
  1199           highmark += 32;
       
  1200         }
       
  1201       }
       
  1202     } else {
       
  1203       /* the audio header */
       
  1204       memcpy (buffdata + 0, "strf", 4);
       
  1205       GST_WRITE_UINT32_LE (buffdata + 4,
       
  1206           sizeof (gst_riff_strf_auds) + 2 + codec_size);
       
  1207       /* the actual header */
       
  1208       GST_WRITE_UINT16_LE (buffdata + 8, audpad->auds.format);
       
  1209       GST_WRITE_UINT16_LE (buffdata + 10, audpad->auds.channels);
       
  1210       GST_WRITE_UINT32_LE (buffdata + 12, audpad->auds.rate);
       
  1211       GST_WRITE_UINT32_LE (buffdata + 16, audpad->auds.av_bps);
       
  1212       GST_WRITE_UINT16_LE (buffdata + 20, audpad->auds.blockalign);
       
  1213       GST_WRITE_UINT16_LE (buffdata + 22, audpad->auds.size);
       
  1214       GST_WRITE_UINT16_LE (buffdata + 24, codec_size);
       
  1215       buffdata += 26;
       
  1216       highmark += 26;
       
  1217 
       
  1218       /* include codec data, if any */
       
  1219       if (codec_size) {
       
  1220         memcpy (buffdata, GST_BUFFER_DATA (audpad->auds_codec_data),
       
  1221             codec_size);
       
  1222 
       
  1223         buffdata += codec_size;
       
  1224         highmark += codec_size;
       
  1225       }
       
  1226       /* padding */
       
  1227       if (highmark & 0x1) {
       
  1228         highmark++;
       
  1229         buffdata++;
       
  1230       }
       
  1231     }
       
  1232 
       
  1233     /* odml superindex chunk */
       
  1234     if (avipad->idx_index > 0)
       
  1235       memcpy (buffdata, "indx", 4);
       
  1236     else
       
  1237       memcpy (buffdata, "JUNK", 4);
       
  1238     GST_WRITE_UINT32_LE (buffdata + 4, ODML_SUPERINDEX_SIZE - 8);       /* chunk size */
       
  1239     GST_WRITE_UINT16_LE (buffdata + 8, 4);      /* bytes per entry */
       
  1240     buffdata[10] = 0;           /* index subtype */
       
  1241     buffdata[11] = GST_AVI_INDEX_OF_INDEXES;    /* index type */
       
  1242     GST_WRITE_UINT32_LE (buffdata + 12, avipad->idx_index);     /* entries in use */
       
  1243     memcpy (buffdata + 16, avipad->tag, 4);     /* stream id */
       
  1244     GST_WRITE_UINT32_LE (buffdata + 20, 0);     /* reserved */
       
  1245     GST_WRITE_UINT32_LE (buffdata + 24, 0);     /* reserved */
       
  1246     GST_WRITE_UINT32_LE (buffdata + 28, 0);     /* reserved */
       
  1247     memcpy (buffdata + 32, avipad->idx,
       
  1248         GST_AVI_SUPERINDEX_COUNT * sizeof (gst_avi_superindex_entry));
       
  1249     buffdata += ODML_SUPERINDEX_SIZE;
       
  1250     highmark += ODML_SUPERINDEX_SIZE;
       
  1251 
       
  1252     node = node->next;
       
  1253   }
       
  1254 
       
  1255   if (avimux->video_pads > 0) {
       
  1256     /* odml header */
       
  1257     memcpy (buffdata + 0, "LIST", 4);
       
  1258     GST_WRITE_UINT32_LE (buffdata + 4, sizeof (guint32) + 4 * 3);
       
  1259     memcpy (buffdata + 8, "odml", 4);
       
  1260     memcpy (buffdata + 12, "dmlh", 4);
       
  1261     GST_WRITE_UINT32_LE (buffdata + 16, sizeof (guint32));
       
  1262     GST_WRITE_UINT32_LE (buffdata + 20, avimux->total_frames);
       
  1263     buffdata += 24;
       
  1264     highmark += 24;
       
  1265   }
       
  1266 
       
  1267   GST_WRITE_UINT32_LE (hdrl_size, (guint32) (buffdata - hdrl_size) - 4);
       
  1268 
       
  1269   /* tags */
       
  1270   if (tags) {
       
  1271     guint8 *ptr;
       
  1272     guint startsize;
       
  1273     GstMarkedBuffer data ;
       
  1274     data.highmark = &highmark;
       
  1275     data.buffer = buffer;
       
  1276 
       
  1277     memcpy (buffdata + 0, "LIST", 4);
       
  1278     ptr = buffdata + 4;         /* fill in later */
       
  1279     startsize = highmark + 4;
       
  1280     memcpy (buffdata + 8, "INFO", 4);
       
  1281     buffdata += 12;
       
  1282     highmark += 12;
       
  1283 
       
  1284     /* 12 bytes is needed for data header */
       
  1285     GST_BUFFER_SIZE (buffer) -= 12;
       
  1286     gst_tag_list_foreach (tags, gst_avi_mux_write_tag, &data);
       
  1287     GST_BUFFER_SIZE (buffer) += 12;
       
  1288     buffdata = GST_BUFFER_DATA (buffer) + highmark;
       
  1289 
       
  1290     /* update list size */
       
  1291     GST_WRITE_UINT32_LE (ptr, highmark - startsize - 4);
       
  1292   }
       
  1293 
       
  1294   /* avi data header */
       
  1295   memcpy (buffdata + 0, "LIST", 4);
       
  1296   GST_WRITE_UINT32_LE (buffdata + 4, avimux->data_size);
       
  1297   memcpy (buffdata + 8, "movi", 4);
       
  1298   buffdata += 12;
       
  1299   highmark += 12;
       
  1300 
       
  1301   /* finally we can fill in the RIFF size */
       
  1302   /* note that riff only counts the first avi chunk */
       
  1303   GST_WRITE_UINT32_LE (riff_size, (guint32) (buffdata - riff_size - 4)  /* header and movi tags */
       
  1304       +avimux->idx_size + avimux->data_size - 4);       /* movi data and index */
       
  1305 
       
  1306   {                             /* only the part that is filled in actually makes up the header
       
  1307                                  *  unref the parent as we only need this part from now on */
       
  1308     GstBuffer *subbuffer = gst_buffer_create_sub (buffer, 0, highmark);
       
  1309 
       
  1310     gst_buffer_unref (buffer);
       
  1311     return subbuffer;
       
  1312   }
       
  1313 }
       
  1314 
       
  1315 static GstBuffer *
       
  1316 gst_avi_mux_riff_get_avix_header (guint32 datax_size)
       
  1317 {
       
  1318   GstBuffer *buffer;
       
  1319   guint8 *buffdata;
       
  1320 
       
  1321   buffer = gst_buffer_new_and_alloc (24);
       
  1322   buffdata = GST_BUFFER_DATA (buffer);
       
  1323 
       
  1324   memcpy (buffdata + 0, "RIFF", 4);
       
  1325   GST_WRITE_UINT32_LE (buffdata + 4, datax_size + 3 * 4);
       
  1326   memcpy (buffdata + 8, "AVIX", 4);
       
  1327   memcpy (buffdata + 12, "LIST", 4);
       
  1328   GST_WRITE_UINT32_LE (buffdata + 16, datax_size);
       
  1329   memcpy (buffdata + 20, "movi", 4);
       
  1330 
       
  1331   return buffer;
       
  1332 }
       
  1333 
       
  1334 static inline GstBuffer *
       
  1335 gst_avi_mux_riff_get_header (GstAviPad * avipad, guint32 video_frame_size)
       
  1336 {
       
  1337   GstBuffer *buffer;
       
  1338   guint8 *buffdata;
       
  1339 
       
  1340   buffer = gst_buffer_new_and_alloc (8);
       
  1341   buffdata = GST_BUFFER_DATA (buffer);
       
  1342   memcpy (buffdata + 0, avipad->tag, 4);
       
  1343   GST_WRITE_UINT32_LE (buffdata + 4, video_frame_size);
       
  1344 
       
  1345   return buffer;
       
  1346 }
       
  1347 
       
  1348 /* write an odml index chunk in the movi list */
       
  1349 static GstFlowReturn
       
  1350 gst_avi_mux_write_avix_index (GstAviMux * avimux, gchar * code,
       
  1351     gchar * chunk, gst_avi_superindex_entry * super_index,
       
  1352     gint * super_index_count)
       
  1353 {
       
  1354   GstFlowReturn res;
       
  1355   GstBuffer *buffer;
       
  1356   guint8 *buffdata, *data;
       
  1357   gst_riff_index_entry *entry;
       
  1358   gint i;
       
  1359   guint32 size, entry_count;
       
  1360 
       
  1361   /* allocate the maximum possible */
       
  1362   buffer = gst_buffer_new_and_alloc (32 + 8 * avimux->idx_index);
       
  1363   buffdata = GST_BUFFER_DATA (buffer);
       
  1364 
       
  1365   /* general index chunk info */
       
  1366   memcpy (buffdata + 0, chunk, 4);      /* chunk id */
       
  1367   GST_WRITE_UINT32_LE (buffdata + 4, 0);        /* chunk size; fill later */
       
  1368   GST_WRITE_UINT16_LE (buffdata + 8, 2);        /* index entry is 2 words */
       
  1369   buffdata[10] = 0;             /* index subtype */
       
  1370   buffdata[11] = GST_AVI_INDEX_OF_CHUNKS;       /* index type: AVI_INDEX_OF_CHUNKS */
       
  1371   GST_WRITE_UINT32_LE (buffdata + 12, 0);       /* entries in use; fill later */
       
  1372   memcpy (buffdata + 16, code, 4);      /* stream to which index refers */
       
  1373   GST_WRITE_UINT64_LE (buffdata + 20, avimux->avix_start);      /* base offset */
       
  1374   GST_WRITE_UINT32_LE (buffdata + 28, 0);       /* reserved */
       
  1375   buffdata += 32;
       
  1376 
       
  1377   /* now the actual index entries */
       
  1378   i = avimux->idx_index;
       
  1379   entry = avimux->idx;
       
  1380   while (i > 0) {
       
  1381     if (memcmp (&entry->id, code, 4) == 0) {
       
  1382       /* enter relative offset to the data (!) */
       
  1383       GST_WRITE_UINT32_LE (buffdata, GUINT32_FROM_LE (entry->offset) + 8);
       
  1384       /* msb is set if not (!) keyframe */
       
  1385       GST_WRITE_UINT32_LE (buffdata + 4, GUINT32_FROM_LE (entry->size)
       
  1386           | (GUINT32_FROM_LE (entry->flags)
       
  1387               & GST_RIFF_IF_KEYFRAME ? 0 : 1U << 31));
       
  1388       buffdata += 8;
       
  1389     }
       
  1390     i--;
       
  1391     entry++;
       
  1392   }
       
  1393 
       
  1394   /* ok, now we know the size and no of entries, fill in where needed */
       
  1395   data = GST_BUFFER_DATA (buffer);
       
  1396   GST_BUFFER_SIZE (buffer) = size = buffdata - data;
       
  1397   GST_WRITE_UINT32_LE (data + 4, size - 8);
       
  1398   entry_count = (size - 32) / 8;
       
  1399   GST_WRITE_UINT32_LE (data + 12, entry_count);
       
  1400 
       
  1401   /* decorate and send */
       
  1402   gst_buffer_set_caps (buffer, GST_PAD_CAPS (avimux->srcpad));
       
  1403   if ((res = gst_pad_push (avimux->srcpad, buffer)) != GST_FLOW_OK)
       
  1404     return res;
       
  1405 
       
  1406   /* keep track of this in superindex (if room) ... */
       
  1407   if (*super_index_count < GST_AVI_SUPERINDEX_COUNT) {
       
  1408     i = *super_index_count;
       
  1409     super_index[i].offset = GUINT64_TO_LE (avimux->total_data);
       
  1410     super_index[i].size = GUINT32_TO_LE (size);
       
  1411     super_index[i].duration = GUINT32_TO_LE (entry_count);
       
  1412     (*super_index_count)++;
       
  1413   } else
       
  1414     GST_WARNING_OBJECT (avimux, "No more room in superindex of stream %s",
       
  1415         code);
       
  1416 
       
  1417   /* ... and in size */
       
  1418   avimux->total_data += size;
       
  1419   if (avimux->is_bigfile)
       
  1420     avimux->datax_size += size;
       
  1421   else
       
  1422     avimux->data_size += size;
       
  1423 
       
  1424   return GST_FLOW_OK;
       
  1425 }
       
  1426 
       
  1427 /* some other usable functions (thankyou xawtv ;-) ) */
       
  1428 
       
  1429 static void
       
  1430 gst_avi_mux_add_index (GstAviMux * avimux, gchar * code, guint32 flags,
       
  1431     guint32 size)
       
  1432 {
       
  1433   if (avimux->idx_index == avimux->idx_count) {
       
  1434     avimux->idx_count += 256;
       
  1435     avimux->idx =
       
  1436         g_realloc (avimux->idx,
       
  1437         avimux->idx_count * sizeof (gst_riff_index_entry));
       
  1438   }
       
  1439   memcpy (&(avimux->idx[avimux->idx_index].id), code, 4);
       
  1440   avimux->idx[avimux->idx_index].flags = GUINT32_TO_LE (flags);
       
  1441   avimux->idx[avimux->idx_index].offset = GUINT32_TO_LE (avimux->idx_offset);
       
  1442   avimux->idx[avimux->idx_index].size = GUINT32_TO_LE (size);
       
  1443   avimux->idx_index++;
       
  1444 }
       
  1445 
       
  1446 static GstFlowReturn
       
  1447 gst_avi_mux_write_index (GstAviMux * avimux)
       
  1448 {
       
  1449   GstFlowReturn res;
       
  1450   GstBuffer *buffer;
       
  1451   guint8 *buffdata;
       
  1452 
       
  1453   buffer = gst_buffer_new_and_alloc (8);
       
  1454   buffdata = GST_BUFFER_DATA (buffer);
       
  1455   memcpy (buffdata + 0, "idx1", 4);
       
  1456   GST_WRITE_UINT32_LE (buffdata + 4,
       
  1457       avimux->idx_index * sizeof (gst_riff_index_entry));
       
  1458 
       
  1459   gst_buffer_set_caps (buffer, GST_PAD_CAPS (avimux->srcpad));
       
  1460   res = gst_pad_push (avimux->srcpad, buffer);
       
  1461   if (res != GST_FLOW_OK)
       
  1462     return res;
       
  1463 
       
  1464   buffer = gst_buffer_new ();
       
  1465   GST_BUFFER_SIZE (buffer) = avimux->idx_index * sizeof (gst_riff_index_entry);
       
  1466   GST_BUFFER_DATA (buffer) = (guint8 *) avimux->idx;
       
  1467   GST_BUFFER_MALLOCDATA (buffer) = GST_BUFFER_DATA (buffer);
       
  1468   avimux->idx = NULL;           /* will be free()'ed by gst_buffer_unref() */
       
  1469   avimux->total_data += GST_BUFFER_SIZE (buffer) + 8;
       
  1470 
       
  1471   gst_buffer_set_caps (buffer, GST_PAD_CAPS (avimux->srcpad));
       
  1472   res = gst_pad_push (avimux->srcpad, buffer);
       
  1473   if (res != GST_FLOW_OK)
       
  1474     return res;
       
  1475 
       
  1476   avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8;
       
  1477 
       
  1478   /* update header */
       
  1479   avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
       
  1480   return GST_FLOW_OK;
       
  1481 }
       
  1482 
       
  1483 static GstFlowReturn
       
  1484 gst_avi_mux_bigfile (GstAviMux * avimux, gboolean last)
       
  1485 {
       
  1486   GstFlowReturn res = GST_FLOW_OK;
       
  1487   GstBuffer *header;
       
  1488   GstEvent *event;
       
  1489   GSList *node;
       
  1490 
       
  1491   /* first some odml standard index chunks in the movi list */
       
  1492   node = avimux->sinkpads;
       
  1493   while (node) {
       
  1494     GstAviPad *avipad = (GstAviPad *) node->data;
       
  1495 
       
  1496     node = node->next;
       
  1497 
       
  1498     res = gst_avi_mux_write_avix_index (avimux, avipad->tag,
       
  1499         avipad->idx_tag, avipad->idx, &avipad->idx_index);
       
  1500     if (res != GST_FLOW_OK)
       
  1501       return res;
       
  1502   }
       
  1503 
       
  1504   if (avimux->is_bigfile) {
       
  1505     /* search back */
       
  1506     event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
       
  1507         avimux->avix_start, GST_CLOCK_TIME_NONE, avimux->avix_start);
       
  1508     /* if the event succeeds */
       
  1509     gst_pad_push_event (avimux->srcpad, event);
       
  1510 
       
  1511     /* rewrite AVIX header */
       
  1512     header = gst_avi_mux_riff_get_avix_header (avimux->datax_size);
       
  1513     gst_buffer_set_caps (header, GST_PAD_CAPS (avimux->srcpad));
       
  1514     res = gst_pad_push (avimux->srcpad, header);
       
  1515 
       
  1516     /* go back to current location, at least try */
       
  1517     event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
       
  1518         avimux->total_data, GST_CLOCK_TIME_NONE, avimux->total_data);
       
  1519     gst_pad_push_event (avimux->srcpad, event);
       
  1520 
       
  1521     if (res != GST_FLOW_OK)
       
  1522       return res;
       
  1523   } else {                      /* write a standard index in the first riff chunk */
       
  1524     res = gst_avi_mux_write_index (avimux);
       
  1525     /* the index data/buffer is freed by pushing it */
       
  1526     avimux->idx_count = 0;
       
  1527     if (res != GST_FLOW_OK)
       
  1528       return res;
       
  1529   }
       
  1530 
       
  1531   avimux->avix_start = avimux->total_data;
       
  1532 
       
  1533   if (last)
       
  1534     return res;
       
  1535 
       
  1536   avimux->is_bigfile = TRUE;
       
  1537   avimux->numx_frames = 0;
       
  1538   avimux->datax_size = 4;       /* movi tag */
       
  1539   avimux->idx_index = 0;
       
  1540 
       
  1541   header = gst_avi_mux_riff_get_avix_header (0);
       
  1542   avimux->total_data += GST_BUFFER_SIZE (header);
       
  1543   /* avix_start is used as base offset for the odml index chunk */
       
  1544   avimux->idx_offset = avimux->total_data - avimux->avix_start;
       
  1545   gst_buffer_set_caps (header, GST_PAD_CAPS (avimux->srcpad));
       
  1546   return gst_pad_push (avimux->srcpad, header);
       
  1547 }
       
  1548 
       
  1549 /* enough header blabla now, let's go on to actually writing the headers */
       
  1550 
       
  1551 static GstFlowReturn
       
  1552 gst_avi_mux_start_file (GstAviMux * avimux)
       
  1553 {
       
  1554   GstFlowReturn res;
       
  1555   GstBuffer *header;
       
  1556   GSList *node;
       
  1557 
       
  1558   avimux->total_data = 0;
       
  1559   avimux->total_frames = 0;
       
  1560   avimux->data_size = 4;        /* movi tag */
       
  1561   avimux->datax_size = 0;
       
  1562   avimux->num_frames = 0;
       
  1563   avimux->numx_frames = 0;
       
  1564   avimux->avix_start = 0;
       
  1565 
       
  1566   avimux->idx_index = 0;
       
  1567   avimux->idx_offset = 0;       /* see 10 lines below */
       
  1568   avimux->idx_size = 0;
       
  1569   avimux->idx_count = 0;
       
  1570   avimux->idx = NULL;
       
  1571 
       
  1572   /* state */
       
  1573   avimux->write_header = FALSE;
       
  1574   avimux->restart = FALSE;
       
  1575 
       
  1576   /* init streams, see what we've got */
       
  1577   node = avimux->sinkpads;
       
  1578   avimux->audio_pads = avimux->video_pads = 0;
       
  1579   while (node) {
       
  1580     GstAviPad *avipad = (GstAviPad *) node->data;
       
  1581 
       
  1582     node = node->next;
       
  1583 
       
  1584     if (!avipad->is_video) {
       
  1585       /* audio stream numbers must start at 1 iff there is a video stream 0;
       
  1586        * request_pad inserts video pad at head of list, so this test suffices */
       
  1587       if (avimux->video_pads)
       
  1588         avimux->audio_pads++;
       
  1589       avipad->tag = g_strdup_printf ("%02uwb", avimux->audio_pads);
       
  1590       avipad->idx_tag = g_strdup_printf ("ix%02u", avimux->audio_pads);
       
  1591       if (!avimux->video_pads)
       
  1592         avimux->audio_pads++;
       
  1593     } else {
       
  1594       avipad->tag = g_strdup_printf ("%02udb", avimux->video_pads);
       
  1595       avipad->idx_tag = g_strdup_printf ("ix%02u", avimux->video_pads++);
       
  1596     }
       
  1597   }
       
  1598 
       
  1599   /* let downstream know we think in BYTES and expect to do seeking later on */
       
  1600   gst_pad_push_event (avimux->srcpad,
       
  1601       gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
       
  1602 
       
  1603   /* header */
       
  1604   avimux->avi_hdr.streams = g_slist_length (avimux->sinkpads);
       
  1605   avimux->is_bigfile = FALSE;
       
  1606 
       
  1607   header = gst_avi_mux_riff_get_avi_header (avimux);
       
  1608   avimux->total_data += GST_BUFFER_SIZE (header);
       
  1609 
       
  1610   gst_buffer_set_caps (header, GST_PAD_CAPS (avimux->srcpad));
       
  1611   res = gst_pad_push (avimux->srcpad, header);
       
  1612 
       
  1613   avimux->idx_offset = avimux->total_data;
       
  1614 
       
  1615   return res;
       
  1616 }
       
  1617 
       
  1618 static GstFlowReturn
       
  1619 gst_avi_mux_stop_file (GstAviMux * avimux)
       
  1620 {
       
  1621   GstFlowReturn res = GST_FLOW_OK;
       
  1622   GstEvent *event;
       
  1623   GstBuffer *header;
       
  1624   GSList *node;
       
  1625 
       
  1626   /* if bigfile, rewrite header, else write indexes */
       
  1627   /* don't bail out at once if error, still try to re-write header */
       
  1628   if (avimux->video_pads > 0) {
       
  1629     if (avimux->is_bigfile) {
       
  1630       res = gst_avi_mux_bigfile (avimux, TRUE);
       
  1631     } else {
       
  1632       res = gst_avi_mux_write_index (avimux);
       
  1633     }
       
  1634   }
       
  1635 
       
  1636   /* we do our best to make it interleaved at least ... */
       
  1637   if (avimux->audio_pads > 0 && avimux->video_pads > 0)
       
  1638     avimux->avi_hdr.flags |= GST_RIFF_AVIH_ISINTERLEAVED;
       
  1639 
       
  1640   /* set rate and everything having to do with that */
       
  1641   avimux->avi_hdr.max_bps = 0;
       
  1642   node = avimux->sinkpads;
       
  1643   while (node) {
       
  1644     GstAviPad *avipad = (GstAviPad *) node->data;
       
  1645 
       
  1646     node = node->next;
       
  1647 
       
  1648     if (!avipad->is_video) {
       
  1649       GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
       
  1650 
       
  1651       /* calculate bps if needed */
       
  1652       if (!audpad->auds.av_bps) {
       
  1653         if (audpad->audio_time) {
       
  1654           audpad->auds.av_bps =
       
  1655               (GST_SECOND * audpad->audio_size) / audpad->audio_time;
       
  1656           /* round bps to nearest multiple of 8;
       
  1657            * which is much more likely to be the (cbr) bitrate in use;
       
  1658            * which in turn results in better timestamp calculation on playback */
       
  1659           audpad->auds.av_bps = GST_ROUND_UP_8 (audpad->auds.av_bps - 4);
       
  1660         } else {
       
  1661           GST_ELEMENT_WARNING (avimux, STREAM, MUX,
       
  1662               (_("No or invalid input audio, AVI stream will be corrupt.")),
       
  1663               (NULL));
       
  1664           audpad->auds.av_bps = 0;
       
  1665         }
       
  1666         avipad->hdr.rate = audpad->auds.av_bps * avipad->hdr.scale;
       
  1667       }
       
  1668       avimux->avi_hdr.max_bps += audpad->auds.av_bps;
       
  1669       avipad->hdr.length = (audpad->audio_time * avipad->hdr.rate) / GST_SECOND;
       
  1670     } else {
       
  1671       GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
       
  1672 
       
  1673       avimux->avi_hdr.max_bps += ((vidpad->vids.bit_cnt + 7) / 8) *
       
  1674           (1000000. / avimux->avi_hdr.us_frame) * vidpad->vids.image_size;
       
  1675       avipad->hdr.length = avimux->total_frames;
       
  1676     }
       
  1677   }
       
  1678 
       
  1679   /* statistics/total_frames/... */
       
  1680   avimux->avi_hdr.tot_frames = avimux->num_frames;
       
  1681 
       
  1682   /* seek and rewrite the header */
       
  1683   header = gst_avi_mux_riff_get_avi_header (avimux);
       
  1684   event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
       
  1685       0, GST_CLOCK_TIME_NONE, 0);
       
  1686   gst_pad_push_event (avimux->srcpad, event);
       
  1687 
       
  1688   gst_buffer_set_caps (header, GST_PAD_CAPS (avimux->srcpad));
       
  1689   /* the first error survives */
       
  1690   if (res == GST_FLOW_OK)
       
  1691     res = gst_pad_push (avimux->srcpad, header);
       
  1692   else
       
  1693     gst_pad_push (avimux->srcpad, header);
       
  1694 
       
  1695   event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
       
  1696       avimux->total_data, GST_CLOCK_TIME_NONE, avimux->total_data);
       
  1697   gst_pad_push_event (avimux->srcpad, event);
       
  1698 
       
  1699   avimux->write_header = TRUE;
       
  1700 
       
  1701   return res;
       
  1702 }
       
  1703 
       
  1704 static GstFlowReturn
       
  1705 gst_avi_mux_restart_file (GstAviMux * avimux)
       
  1706 {
       
  1707   GstFlowReturn res;
       
  1708 
       
  1709   if ((res = gst_avi_mux_stop_file (avimux)) != GST_FLOW_OK)
       
  1710     return res;
       
  1711 
       
  1712   gst_pad_push_event (avimux->srcpad, gst_event_new_eos ());
       
  1713 
       
  1714   return gst_avi_mux_start_file (avimux);
       
  1715 }
       
  1716 
       
  1717 /* handle events (search) */
       
  1718 static gboolean
       
  1719 gst_avi_mux_handle_event (GstPad * pad, GstEvent * event)
       
  1720 {
       
  1721   GstAviMux *avimux;
       
  1722   gboolean ret;
       
  1723 
       
  1724   avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
       
  1725 
       
  1726   switch (GST_EVENT_TYPE (event)) {
       
  1727     case GST_EVENT_TAG:{
       
  1728       GstTagList *list;
       
  1729       GstTagSetter *setter = GST_TAG_SETTER (avimux);
       
  1730       const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
       
  1731 
       
  1732       gst_event_parse_tag (event, &list);
       
  1733       gst_tag_setter_merge_tags (setter, list, mode);
       
  1734       break;
       
  1735     }
       
  1736     default:
       
  1737       break;
       
  1738   }
       
  1739 
       
  1740   /* now GstCollectPads can take care of the rest, e.g. EOS */
       
  1741   ret = avimux->collect_event (pad, event);
       
  1742 
       
  1743   gst_object_unref (avimux);
       
  1744 
       
  1745   return ret;
       
  1746 }
       
  1747 
       
  1748 /* send extra 'padding' data */
       
  1749 static GstFlowReturn
       
  1750 gst_avi_mux_send_pad_data (GstAviMux * avimux, gulong num_bytes)
       
  1751 {
       
  1752   GstBuffer *buffer;
       
  1753 
       
  1754   buffer = gst_buffer_new_and_alloc (num_bytes);
       
  1755   memset (GST_BUFFER_DATA (buffer), 0, num_bytes);
       
  1756   gst_buffer_set_caps (buffer, GST_PAD_CAPS (avimux->srcpad));
       
  1757   return gst_pad_push (avimux->srcpad, buffer);
       
  1758 }
       
  1759 
       
  1760 /* do buffer */
       
  1761 static GstFlowReturn
       
  1762 gst_avi_mux_do_buffer (GstAviMux * avimux, GstAviPad * avipad)
       
  1763 {
       
  1764   GstFlowReturn res;
       
  1765   GstBuffer *data, *header;
       
  1766   gulong total_size, pad_bytes = 0;
       
  1767   guint flags;
       
  1768 
       
  1769   data = gst_collect_pads_pop (avimux->collect, avipad->collect);
       
  1770 
       
  1771   if (avimux->restart) {
       
  1772     if ((res = gst_avi_mux_restart_file (avimux)) != GST_FLOW_OK)
       
  1773       return res;
       
  1774   }
       
  1775 
       
  1776   /* need to restart or start a next avix chunk ? */
       
  1777   if ((avimux->is_bigfile ? avimux->datax_size : avimux->data_size) +
       
  1778       GST_BUFFER_SIZE (data) > 1024 * 1024 * 2000) {
       
  1779     if (avimux->enable_large_avi) {
       
  1780       if ((res = gst_avi_mux_bigfile (avimux, FALSE)) != GST_FLOW_OK)
       
  1781         return res;
       
  1782     } else {
       
  1783       if ((res = gst_avi_mux_restart_file (avimux)) != GST_FLOW_OK)
       
  1784         return res;
       
  1785     }
       
  1786   }
       
  1787 
       
  1788   /* get header and record some stats */
       
  1789   if (GST_BUFFER_SIZE (data) & 1) {
       
  1790     pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
       
  1791   }
       
  1792   header = gst_avi_mux_riff_get_header (avipad, GST_BUFFER_SIZE (data));
       
  1793   total_size = GST_BUFFER_SIZE (header) + GST_BUFFER_SIZE (data) + pad_bytes;
       
  1794 
       
  1795   if (avimux->is_bigfile) {
       
  1796     avimux->datax_size += total_size;
       
  1797   } else {
       
  1798     avimux->data_size += total_size;
       
  1799   }
       
  1800 
       
  1801   if (avipad->is_video) {
       
  1802     avimux->total_frames++;
       
  1803 
       
  1804     if (avimux->is_bigfile) {
       
  1805       avimux->numx_frames++;
       
  1806     } else {
       
  1807       avimux->num_frames++;
       
  1808     }
       
  1809 
       
  1810     flags = 0x02;
       
  1811     if (!GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_FLAG_DELTA_UNIT))
       
  1812       flags |= 0x10;
       
  1813   } else {
       
  1814     GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
       
  1815 
       
  1816     flags = 0;
       
  1817     audpad->audio_size += GST_BUFFER_SIZE (data);
       
  1818     audpad->audio_time += GST_BUFFER_DURATION (data);
       
  1819   }
       
  1820 
       
  1821   gst_avi_mux_add_index (avimux, avipad->tag, flags, GST_BUFFER_SIZE (data));
       
  1822 
       
  1823   /* prepare buffers for sending */
       
  1824   gst_buffer_set_caps (header, GST_PAD_CAPS (avimux->srcpad));
       
  1825   data = gst_buffer_make_metadata_writable (data);
       
  1826   gst_buffer_set_caps (data, GST_PAD_CAPS (avimux->srcpad));
       
  1827 
       
  1828   GST_LOG_OBJECT (avimux, "pushing buffers: head, data");
       
  1829 
       
  1830   if ((res = gst_pad_push (avimux->srcpad, header)) != GST_FLOW_OK)
       
  1831     return res;
       
  1832   if ((res = gst_pad_push (avimux->srcpad, data)) != GST_FLOW_OK)
       
  1833     return res;
       
  1834 
       
  1835   if (pad_bytes) {
       
  1836     if ((res = gst_avi_mux_send_pad_data (avimux, pad_bytes)) != GST_FLOW_OK)
       
  1837       return res;
       
  1838   }
       
  1839 
       
  1840   /* if any push above fails, we're in trouble with file consistency anyway */
       
  1841   avimux->total_data += total_size;
       
  1842   avimux->idx_offset += total_size;
       
  1843 
       
  1844   return res;
       
  1845 }
       
  1846 
       
  1847 /* pick the oldest buffer from the pads and push it */
       
  1848 static GstFlowReturn
       
  1849 gst_avi_mux_do_one_buffer (GstAviMux * avimux)
       
  1850 {
       
  1851   GstAviPad *avipad, *best_pad;
       
  1852   GSList *node;
       
  1853   GstBuffer *buffer;
       
  1854   GstClockTime time, best_time;
       
  1855 
       
  1856   node = avimux->sinkpads;
       
  1857   best_pad = NULL;
       
  1858   best_time = GST_CLOCK_TIME_NONE;
       
  1859   for (; node; node = node->next) {
       
  1860     avipad = (GstAviPad *) node->data;
       
  1861 
       
  1862     if (!avipad->collect)
       
  1863       continue;
       
  1864 
       
  1865     buffer = gst_collect_pads_peek (avimux->collect, avipad->collect);
       
  1866     if (!buffer)
       
  1867       continue;
       
  1868     time = GST_BUFFER_TIMESTAMP (buffer);
       
  1869     gst_buffer_unref (buffer);
       
  1870 
       
  1871     /* invalid timestamp buffers pass first,
       
  1872      * these are probably initialization buffers */
       
  1873     if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time)
       
  1874         || (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) {
       
  1875       best_pad = avipad;
       
  1876       best_time = time;
       
  1877     }
       
  1878   }
       
  1879 
       
  1880   if (best_pad) {
       
  1881     GST_LOG_OBJECT (avimux, "selected pad %s with time %" GST_TIME_FORMAT,
       
  1882         GST_PAD_NAME (best_pad->collect->pad), GST_TIME_ARGS (best_time));
       
  1883 
       
  1884     return gst_avi_mux_do_buffer (avimux, best_pad);
       
  1885   } else {
       
  1886     /* simply finish off the file and send EOS */
       
  1887     gst_avi_mux_stop_file (avimux);
       
  1888     gst_pad_push_event (avimux->srcpad, gst_event_new_eos ());
       
  1889     return GST_FLOW_UNEXPECTED;
       
  1890   }
       
  1891 }
       
  1892 
       
  1893 static GstFlowReturn
       
  1894 gst_avi_mux_collect_pads (GstCollectPads * pads, GstAviMux * avimux)
       
  1895 {
       
  1896   GstFlowReturn res;
       
  1897 
       
  1898   if (G_UNLIKELY (avimux->write_header)) {
       
  1899     if ((res = gst_avi_mux_start_file (avimux)) != GST_FLOW_OK)
       
  1900       return res;
       
  1901   }
       
  1902 
       
  1903   return gst_avi_mux_do_one_buffer (avimux);
       
  1904 }
       
  1905 
       
  1906 
       
  1907 static void
       
  1908 gst_avi_mux_get_property (GObject * object,
       
  1909     guint prop_id, GValue * value, GParamSpec * pspec)
       
  1910 {
       
  1911   GstAviMux *avimux;
       
  1912 
       
  1913   avimux = GST_AVI_MUX (object);
       
  1914 
       
  1915   switch (prop_id) {
       
  1916     case ARG_BIGFILE:
       
  1917       g_value_set_boolean (value, avimux->enable_large_avi);
       
  1918       break;
       
  1919     default:
       
  1920       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
  1921       break;
       
  1922   }
       
  1923 }
       
  1924 
       
  1925 static void
       
  1926 gst_avi_mux_set_property (GObject * object,
       
  1927     guint prop_id, const GValue * value, GParamSpec * pspec)
       
  1928 {
       
  1929   GstAviMux *avimux;
       
  1930 
       
  1931   avimux = GST_AVI_MUX (object);
       
  1932 
       
  1933   switch (prop_id) {
       
  1934     case ARG_BIGFILE:
       
  1935       avimux->enable_large_avi = g_value_get_boolean (value);
       
  1936       break;
       
  1937     default:
       
  1938       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
  1939       break;
       
  1940   }
       
  1941 }
       
  1942 
       
  1943 static GstStateChangeReturn
       
  1944 gst_avi_mux_change_state (GstElement * element, GstStateChange transition)
       
  1945 {
       
  1946   GstAviMux *avimux;
       
  1947   GstStateChangeReturn ret;
       
  1948 
       
  1949   avimux = GST_AVI_MUX (element);
       
  1950 
       
  1951   switch (transition) {
       
  1952     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1953       gst_collect_pads_start (avimux->collect);
       
  1954       break;
       
  1955     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       
  1956       break;
       
  1957     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  1958       gst_collect_pads_stop (avimux->collect);
       
  1959       break;
       
  1960     default:
       
  1961       break;
       
  1962   }
       
  1963 
       
  1964   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
       
  1965   if (ret == GST_STATE_CHANGE_FAILURE)
       
  1966     goto done;
       
  1967 
       
  1968   switch (transition) {
       
  1969     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
  1970       break;
       
  1971     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  1972       gst_avi_mux_reset (avimux);
       
  1973       break;
       
  1974     case GST_STATE_CHANGE_READY_TO_NULL:
       
  1975       break;
       
  1976     default:
       
  1977       break;
       
  1978   }
       
  1979 
       
  1980 done:
       
  1981   return ret;
       
  1982 }