Replace audio/mp3 with audio/x-mp3 and audio/x-flac with application/x-flac
[platform/upstream/gst-plugins-good.git] / gst / avi / gstavimux.c
index 4273a40..880e8a8 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include <gst/video/video.h>
+
 #include "gstavimux.h"
 
 #ifndef LE_FROM_GUINT16
 #define LE_FROM_GUINT16 GUINT16_FROM_LE
 #endif
-
 #ifndef LE_FROM_GUINT32
 #define LE_FROM_GUINT32 GUINT32_FROM_LE
 #endif
 
-
 /* elementfactory information */
 static GstElementDetails 
 gst_avimux_details = 
 {
-  ".avi mux",
-  "Mux/Video",
+  "Avi multiplexer",
+  "Codec/Muxer",
+  "LGPL",
   "Muxes audio and video into an avi stream",
   VERSION,
   "Ronald Bultje <rbultje@ronald.bitfreak.net>",
@@ -62,10 +63,9 @@ enum {
 enum {
   ARG_0,
   ARG_BIGFILE,
-  ARG_FRAMERATE,
 };
 
-GST_PADTEMPLATE_FACTORY (src_factory,
+GST_PAD_TEMPLATE_FACTORY (src_factory,
   "src",
   GST_PAD_SRC,
   GST_PAD_ALWAYS,
@@ -76,7 +76,7 @@ GST_PADTEMPLATE_FACTORY (src_factory,
   )
 )
     
-GST_PADTEMPLATE_FACTORY (video_sink_factory,
+GST_PAD_TEMPLATE_FACTORY (video_sink_factory,
   "video_%d",
   GST_PAD_SINK,
   GST_PAD_REQUEST,
@@ -123,7 +123,7 @@ GST_PADTEMPLATE_FACTORY (video_sink_factory,
   )
 )
     
-GST_PADTEMPLATE_FACTORY (audio_sink_factory,
+GST_PAD_TEMPLATE_FACTORY (audio_sink_factory,
   "audio_%d",
   GST_PAD_SINK,
   GST_PAD_REQUEST,
@@ -150,12 +150,17 @@ GST_PADTEMPLATE_FACTORY (audio_sink_factory,
                            GST_PROPS_INT (8),
                            GST_PROPS_INT (16)
                          ),
-      "rate",             GST_PROPS_INT_RANGE (11025, 44100),
+      "rate",             GST_PROPS_INT_RANGE (1000, 48000),
       "channels",         GST_PROPS_INT_RANGE (1, 2)
   ),
   GST_CAPS_NEW (
     "avimux_sink_audio",
-    "audio/mp3",
+    "audio/x-mp3",
+      NULL
+  ),
+  GST_CAPS_NEW (
+    "avimux_sink_audio",
+    "application/x-ogg",
       NULL
   )
 )
@@ -164,8 +169,7 @@ GST_PADTEMPLATE_FACTORY (audio_sink_factory,
 static void    gst_avimux_class_init                (GstAviMuxClass *klass);
 static void    gst_avimux_init                      (GstAviMux      *avimux);
 
-static void     gst_avimux_chain                     (GstPad         *pad,
-                                                      GstBuffer      *buf);
+static void     gst_avimux_loop                      (GstElement     *element);
 static gboolean gst_avimux_handle_event              (GstPad         *pad,
                                                       GstEvent       *event);
 static GstPad*  gst_avimux_request_new_pad           (GstElement     *element,
@@ -221,10 +225,6 @@ gst_avimux_class_init (GstAviMuxClass *klass)
     g_param_spec_boolean("bigfile","Bigfile Support","Whether to capture large or small AVI files",
     0,G_PARAM_READWRITE));
 
-  g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAMERATE,
-    g_param_spec_double("framerate","Framerate","Frames/sec",
-    G_MINDOUBLE,G_MAXDOUBLE,25.0,G_PARAM_READWRITE));
-
   gstelement_class->request_new_pad = gst_avimux_request_new_pad;
 
   gstelement_class->change_state = gst_avimux_change_state;
@@ -233,23 +233,42 @@ gst_avimux_class_init (GstAviMuxClass *klass)
   gstelement_class->set_property = gst_avimux_set_property;
 }
 
+static const GstEventMask *
+gst_avimux_get_event_masks (GstPad *pad)
+{
+  static const GstEventMask gst_avimux_src_event_masks[] = {
+    { GST_EVENT_NEW_MEDIA, 0 },
+    { 0, }
+  };
+  static const GstEventMask gst_avimux_sink_event_masks[] = {
+    { GST_EVENT_EOS, 0 },
+    { 0, }
+  };
+
+  if (GST_PAD_IS_SRC(pad))
+    return gst_avimux_src_event_masks;
+  else
+    return gst_avimux_sink_event_masks;
+}
+
 static void 
 gst_avimux_init (GstAviMux *avimux) 
 {
-  gint i;
   avimux->srcpad = gst_pad_new_from_template (
-                 GST_PADTEMPLATE_GET (src_factory), "src");
+                 GST_PAD_TEMPLATE_GET (src_factory), "src");
   gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
 
   GST_FLAG_SET (GST_ELEMENT(avimux), GST_ELEMENT_EVENT_AWARE);
   gst_pad_set_event_function(avimux->srcpad, gst_avimux_handle_event);
+  gst_pad_set_event_mask_function(avimux->srcpad, gst_avimux_get_event_masks);
+
+  avimux->audiosinkpad = NULL;
+  avimux->audio_pad_connected = FALSE;
+  avimux->videosinkpad = NULL;
+  avimux->video_pad_connected = FALSE;
 
-  for (i=0;i<MAX_NUM_AUDIO_PADS;i++)
-    avimux->audiosinkpad[i] = NULL;
-  avimux->num_audio_pads = 0;
-  for (i=0;i<MAX_NUM_VIDEO_PADS;i++)
-    avimux->videosinkpad[i] = NULL;
-  avimux->num_video_pads = 0;
+  avimux->audio_buffer_queue = NULL;
+  avimux->video_buffer_queue = NULL;
 
   avimux->num_frames = 0;
 
@@ -269,7 +288,9 @@ gst_avimux_init (GstAviMux *avimux)
 
   avimux->enable_large_avi = TRUE;
 
-  avimux->framerate = 25.;
+  avimux->framerate = 0;
+
+  gst_element_set_loop_function(GST_ELEMENT(avimux), gst_avimux_loop);
 }
 
 static GstPadConnectReturn
@@ -292,58 +313,78 @@ gst_avimux_sinkconnect (GstPad *pad, GstCaps *vscaps)
 
     if (!strcmp (mimetype, "video/avi"))
     {
-      const gchar* format = gst_caps_get_string(caps, "format");
+      const gchar* format;
+      
+      gst_caps_get_string (caps, "format", &format);
 
       if (!strncmp (format, "strf_vids", 9)) {
         avimux->vids.size        = sizeof(gst_riff_strf_vids);
-        avimux->vids.width       = gst_caps_get_int (caps, "width");
-        avimux->vids.height      = gst_caps_get_int (caps, "height");
-        avimux->vids.planes      = gst_caps_get_int (caps, "planes");
-        avimux->vids.bit_cnt     = gst_caps_get_int (caps, "bit_cnt");
-        avimux->vids.compression = gst_caps_get_fourcc_int (caps, "compression");
-        avimux->vids.image_size  = gst_caps_get_int (caps, "image_size");
-        avimux->vids.xpels_meter = gst_caps_get_int (caps, "xpels_meter");
-        avimux->vids.ypels_meter = gst_caps_get_int (caps, "ypels_meter");
-        avimux->vids.num_colors  = gst_caps_get_int (caps, "num_colors");
-        avimux->vids.imp_colors  = gst_caps_get_int (caps, "imp_colors");
+       gst_caps_get (caps,
+                     "width",       &avimux->vids.width,
+                     "height",      &avimux->vids.height,
+                     "planes",      &avimux->vids.planes,
+                     "bit_cnt",     &avimux->vids.bit_cnt,
+                     "compression", &avimux->vids.compression,
+                     "image_size",  &avimux->vids.image_size,
+                     "xpels_meter", &avimux->vids.xpels_meter,
+                     "ypels_meter", &avimux->vids.ypels_meter,
+                     "num_colors",  &avimux->vids.num_colors,
+                     "imp_colors",  &avimux->vids.imp_colors,
+                     NULL);
+        avimux->vids_hdr.fcc_handler = avimux->vids.compression;
+        avimux->avi_hdr.width = avimux->vids.width;
+        avimux->avi_hdr.height = avimux->vids.height;
+        goto done;
       }
       else if (!strncmp (format, "strf_auds", 9)) {
-        avimux->auds.format      = gst_caps_get_int (caps, "format");
-        avimux->auds.channels    = gst_caps_get_int (caps, "channels");
-        avimux->auds.rate        = gst_caps_get_int (caps, "rate");
-        avimux->auds.av_bps      = gst_caps_get_int (caps, "av_bps");
-        avimux->auds.blockalign  = gst_caps_get_int (caps, "blockalign");
-        avimux->auds.size        = gst_caps_get_int (caps, "size");
+       gst_caps_get (caps,
+                     "format",      &avimux->auds.format,
+                     "channels",    &avimux->auds.channels,
+                     "rate",        &avimux->auds.rate,
+                     "av_bps",      &avimux->auds.av_bps,
+                     "blockalign",  &avimux->auds.blockalign,
+                     "size",        &avimux->auds.size,
+                     NULL);
+        avimux->auds_hdr.samplesize = avimux->auds_hdr.scale = avimux->auds.blockalign;
+        avimux->auds_hdr.rate = avimux->auds.av_bps;
+        goto done;
       }
-      goto done;
     }
     else if (!strcmp (mimetype, "video/raw"))
     {
-      switch (gst_caps_get_fourcc_int(caps, "format"))
+      guint32 format;
+      gint temp;
+
+      gst_caps_get_fourcc_int (caps, "format", &format);
+      switch (format)
       {
         case GST_MAKE_FOURCC('Y','U','Y','2'):
         case GST_MAKE_FOURCC('I','4','2','0'):
         case GST_MAKE_FOURCC('Y','4','1','P'):
         case GST_MAKE_FOURCC('R','G','B',' '):
           avimux->vids.size        = sizeof(gst_riff_strf_vids);
-          avimux->vids.width       = gst_caps_get_int (caps, "width");
-          avimux->vids.height      = gst_caps_get_int (caps, "height");
+         gst_caps_get (caps, "width", &avimux->vids.width,
+                             "height", &avimux->vids.height, NULL);
           avimux->vids.planes      = 1;
-          switch (gst_caps_get_fourcc_int(caps, "format"))
+          switch (format)
           {
             case GST_MAKE_FOURCC('Y','U','Y','2'):
               avimux->vids.bit_cnt     = 16; /* YUY2 */
               break;
             case GST_MAKE_FOURCC('R','G','B',' '):
-              avimux->vids.bit_cnt     = gst_caps_get_fourcc_int(caps, "bpp"); /* RGB */
+              gst_caps_get_int (caps, "bpp", &temp); /* RGB */
+             avimux->vids.bit_cnt = temp;
               break;
             case GST_MAKE_FOURCC('Y','4','1','P'):
             case GST_MAKE_FOURCC('I','4','2','0'):
               avimux->vids.bit_cnt     = 12; /* Y41P or I420 */
               break;
           }
-          avimux->vids.compression = gst_caps_get_fourcc_int(caps, "format");
+          gst_caps_get_fourcc_int(caps, "format", &avimux->vids.compression);
+          avimux->vids_hdr.fcc_handler = avimux->vids.compression;
           avimux->vids.image_size  = avimux->vids.height * avimux->vids.width;
+          avimux->avi_hdr.width = avimux->vids.width;
+          avimux->avi_hdr.height = avimux->vids.height;
           goto done;
         default:
           break;
@@ -352,30 +393,45 @@ gst_avimux_sinkconnect (GstPad *pad, GstCaps *vscaps)
     else if (!strcmp (mimetype, "video/jpeg"))
     {
       avimux->vids.size        = sizeof(gst_riff_strf_vids);
-      avimux->vids.width       = gst_caps_get_int (caps, "width");
-      avimux->vids.height      = gst_caps_get_int (caps, "height");
+      gst_caps_get (caps, "width", &avimux->vids.width,
+                         "height", &avimux->vids.height, NULL);
       avimux->vids.planes      = 1;
       avimux->vids.bit_cnt     = 24;
-      avimux->vids.compression = GST_MAKE_FOURCC('M','J','P','G');
+      avimux->vids_hdr.fcc_handler = avimux->vids.compression = GST_MAKE_FOURCC('M','J','P','G');
+      avimux->avi_hdr.width = avimux->vids.width;
+      avimux->avi_hdr.height = avimux->vids.height;
       avimux->vids.image_size  = avimux->vids.height * avimux->vids.width;
       goto done;
     }
     else if (!strcmp (mimetype, "audio/raw"))
     {
+      gint width;
+
       avimux->auds.format      = GST_RIFF_WAVE_FORMAT_PCM;
-      avimux->auds.channels    = gst_caps_get_int (caps, "channels");
-      avimux->auds.rate        = gst_caps_get_int (caps, "rate");
-      avimux->auds.av_bps      = gst_caps_get_int (caps, "width") * avimux->auds.rate *
-                                               avimux->auds.channels / 8;
-      avimux->auds.blockalign  = gst_caps_get_int (caps, "width") * avimux->auds.channels/8;
-      avimux->auds.size        = gst_caps_get_int (caps, "depth");
+      gst_caps_get (caps, "channels",  &avimux->auds.channels,
+                         "rate",       &avimux->auds.rate,
+                         "width",      &width,
+                         "depth",      &avimux->auds.size,
+                         NULL);
+      avimux->auds_hdr.rate = avimux->auds.av_bps = width * avimux->auds.rate * avimux->auds.channels / 8;
+      avimux->auds_hdr.samplesize = avimux->auds_hdr.scale = avimux->auds.blockalign = width * avimux->auds.channels/8;
       goto done;
     }
-    else if (!strcmp (mimetype, "audio/mp3"))
+    else if (!strcmp (mimetype, "audio/x-mp3"))
     {
+      gint layer;
+
+      gst_caps_get_int(caps, "layer", &layer);
+
       /* we don't need to do anything here, compressed mp3 contains it all */
-      avimux->auds.format      = gst_caps_get_int(caps, "layer")==3?
-                                   GST_RIFF_WAVE_FORMAT_MPEGL3:GST_RIFF_WAVE_FORMAT_MPEGL12;
+      avimux->auds.format      = (layer == 3?
+                                   GST_RIFF_WAVE_FORMAT_MPEGL3 : 
+                                  GST_RIFF_WAVE_FORMAT_MPEGL12);
+      goto done;
+    }
+    else if (!strcmp (mimetype, "application/x-ogg"))
+    {
+      avimux->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS1;
       goto done;
     }
   }
@@ -385,13 +441,67 @@ done:
   return GST_PAD_CONNECT_OK;
 }
 
+static void
+gst_avimux_pad_connect (GstPad   *pad,
+                        GstPad   *peer,
+                        gpointer  data)
+{
+  GstAviMux *avimux = GST_AVIMUX(data);
+  const gchar *padname = gst_pad_get_name (pad);
+
+  if (pad == avimux->audiosinkpad)
+  {
+    avimux->audio_pad_connected = TRUE;
+  }
+  else if (pad == avimux->videosinkpad)
+  {
+    avimux->video_pad_connected = TRUE;
+  }
+  else
+  {
+    g_warning("Unknown padname '%s'", padname);
+    return;
+  }
+
+  GST_DEBUG(GST_CAT_PLUGIN_INFO, "pad '%s' connected", padname);
+}
+
+static void
+gst_avimux_pad_disconnect (GstPad   *pad,
+                           GstPad   *peer,
+                           gpointer  data)
+{
+  GstAviMux *avimux = GST_AVIMUX(data);
+  const gchar *padname = gst_pad_get_name (pad);
+
+  if (pad == avimux->audiosinkpad)
+  {
+    avimux->audio_pad_connected = FALSE;
+    avimux->audiosinkpad = NULL;
+  }
+  else if (pad == avimux->videosinkpad)
+  {
+    avimux->video_pad_connected = FALSE;
+    avimux->videosinkpad = NULL;
+  }
+  else
+  {
+    g_warning("Unknown padname '%s'", padname);
+    return;
+  }
+
+  GST_DEBUG(GST_CAT_PLUGIN_INFO, "pad '%s' disconnected", padname);
+
+  /* is this allowed? */
+  gst_pad_destroy(pad);
+}
+
 static GstPad*
 gst_avimux_request_new_pad (GstElement     *element,
                            GstPadTemplate *templ,
                            const gchar    *req_name)
 {
   GstAviMux *avimux;
-  gchar *name = NULL;
   GstPad *newpad;
 
   g_return_val_if_fail (templ != NULL, NULL);
@@ -405,32 +515,29 @@ gst_avimux_request_new_pad (GstElement     *element,
 
   avimux = GST_AVIMUX (element);
 
-  if (templ == GST_PADTEMPLATE_GET (audio_sink_factory)) {
-    g_return_val_if_fail(avimux->num_audio_pads == 0 /*< MAX_NUM_AUDIO_PADS*/, NULL);
-    name = g_strdup_printf ("audio_%02d", avimux->num_audio_pads);
-    newpad = gst_pad_new_from_template (templ, name);
-    gst_pad_set_element_private (newpad, GINT_TO_POINTER (avimux->num_audio_pads));
-
-    avimux->audiosinkpad[avimux->num_audio_pads] = newpad;
-    avimux->num_audio_pads++;
+  if (templ == GST_PAD_TEMPLATE_GET (audio_sink_factory)) {
+    g_return_val_if_fail(avimux->audiosinkpad == NULL, NULL);
+    newpad = gst_pad_new_from_template (templ, "audio_00");
+    avimux->audiosinkpad = newpad;
   }
-  else if (templ == GST_PADTEMPLATE_GET (video_sink_factory)) {
-    g_return_val_if_fail(avimux->num_video_pads == 0 /*< MAX_NUM_VIDEO_PADS*/, NULL);
-    name = g_strdup_printf ("video_%02d", avimux->num_video_pads);
-    newpad = gst_pad_new_from_template (templ, name);
-    gst_pad_set_element_private (newpad, GINT_TO_POINTER (avimux->num_video_pads));
-
-    avimux->videosinkpad[avimux->num_video_pads] = newpad;
-    avimux->num_video_pads++;
+  else if (templ == GST_PAD_TEMPLATE_GET (video_sink_factory)) {
+    g_return_val_if_fail(avimux->videosinkpad == NULL, NULL);
+    newpad = gst_pad_new_from_template (templ, "video_00");
+    avimux->videosinkpad = newpad;
   }
   else {
     g_warning ("avimux: this is not our template!\n");
     return NULL;
   }
 
-  gst_pad_set_chain_function (newpad, gst_avimux_chain);
+  g_signal_connect(newpad, "connected",
+    G_CALLBACK(gst_avimux_pad_connect), (gpointer)avimux);
+  g_signal_connect(newpad, "disconnected",
+    G_CALLBACK(gst_avimux_pad_disconnect), (gpointer)avimux);
   gst_pad_set_connect_function (newpad, gst_avimux_sinkconnect);
   gst_element_add_pad (element, newpad);
+  gst_pad_set_event_function(newpad, gst_avimux_handle_event);
+  gst_pad_set_event_mask_function(newpad, gst_avimux_get_event_masks);
   
   return newpad;
 }
@@ -452,12 +559,12 @@ gst_avimux_riff_get_avi_header (GstAviMux *avimux)
   /* first, let's see what actually needs to be in the buffer */
   GST_BUFFER_SIZE(buffer) = 0;
   GST_BUFFER_SIZE(buffer) += 32 + sizeof(gst_riff_avih); /* avi header */
-  if (avimux->num_video_pads)
+  if (avimux->video_pad_connected)
   { /* we have video */
     GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids); /* vid hdr */
     GST_BUFFER_SIZE(buffer) += 24; /* odml header */
   }
-  if (avimux->num_audio_pads)
+  if (avimux->audio_pad_connected)
   { /* we have audio */
     GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds); /* aud hdr */
   }
@@ -510,7 +617,7 @@ gst_avimux_riff_get_avi_header (GstAviMux *avimux)
   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.length);
   memcpy(buffdata, &temp32, 4); buffdata += 4;
 
-  if (avimux->num_video_pads)
+  if (avimux->video_pad_connected)
   {
     /* video header metadata */
     memcpy(buffdata, "LIST", 4); buffdata += 4;
@@ -575,7 +682,7 @@ gst_avimux_riff_get_avi_header (GstAviMux *avimux)
     memcpy(buffdata, &temp32, 4); buffdata += 4;
   }
 
-  if (avimux->num_audio_pads)
+  if (avimux->audio_pad_connected)
   {
     /* audio header */
     memcpy(buffdata, "LIST", 4); buffdata += 4;
@@ -613,7 +720,7 @@ gst_avimux_riff_get_avi_header (GstAviMux *avimux)
     memcpy(buffdata, &temp32, 4); buffdata += 4;
     /* the audio header */
     memcpy(buffdata, "strf", 4); buffdata += 4;
-    temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_vids));
+    temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_auds));
     memcpy(buffdata, &temp32, 4); buffdata += 4;
     /* the actual header */
     temp16 = LE_FROM_GUINT16(avimux->auds.format);
@@ -630,7 +737,7 @@ gst_avimux_riff_get_avi_header (GstAviMux *avimux)
     memcpy(buffdata, &temp16, 2); buffdata += 2;
   }
 
-  if (avimux->num_video_pads)
+  if (avimux->video_pad_connected)
   {
     /* odml header */
     memcpy(buffdata, "LIST", 4); buffdata += 4;
@@ -711,22 +818,19 @@ gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
 /* some other usable functions (thankyou xawtv ;-) ) */
 
 static void
-gst_avimux_add_index (GstAviMux *avimux, guint32 fourcc, guint32 flags, guint32 size)
+gst_avimux_add_index (GstAviMux *avimux, guchar *code, guint32 flags, guint32 size)
 {
-  guint32 temp32;
-  
   if (avimux->idx_index == avimux->idx_count)
   {
     avimux->idx_count += 256;
     avimux->idx = realloc(avimux->idx, avimux->idx_count*sizeof(gst_riff_index_entry));
   }
-  temp32 = LE_FROM_GUINT32(fourcc);
-  memcpy(&(avimux->idx[avimux->idx_index].id), &temp32, 4);
+  memcpy(&(avimux->idx[avimux->idx_index].id), code, 4);
   avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32(flags);
-  avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32(avimux->idx_offset-avimux->header_size-8);
+  avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32(avimux->idx_offset);
   avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32(size);
   avimux->idx_index++;
-  avimux->idx_offset += size + sizeof(gst_riff_index_entry);
+  avimux->idx_offset += size;
 }
 
 static void
@@ -765,16 +869,24 @@ gst_avimux_bigfile(GstAviMux *avimux, gboolean last)
   if (avimux->is_bigfile)
   {
     /* sarch back */
-    event = gst_event_new_seek(GST_SEEK_BYTEOFFSET_SET, avimux->avix_start, TRUE);
-    gst_pad_send_event(avimux->srcpad, event);
-
-    /* rewrite AVIX header */
-    header = gst_avimux_riff_get_avix_header(avimux->datax_size);
-    gst_pad_push(avimux->srcpad, header);
-
-    /* go back to current location */
-    event = gst_event_new_seek(GST_SEEK_BYTEOFFSET_SET, avimux->total_data, TRUE);
-    gst_pad_send_event(avimux->srcpad, event);
+    event = gst_event_new_seek (GST_FORMAT_BYTES | 
+                               GST_SEEK_METHOD_SET | 
+                               GST_SEEK_FLAG_FLUSH, 
+                               avimux->avix_start);
+    /* if the event succeeds */
+    if (gst_pad_send_event(GST_PAD_PEER(avimux->srcpad), event)) {
+
+      /* rewrite AVIX header */
+      header = gst_avimux_riff_get_avix_header(avimux->datax_size);
+      gst_pad_push(avimux->srcpad, header);
+
+      /* go back to current location */
+      event = gst_event_new_seek (GST_FORMAT_BYTES | 
+                                 GST_SEEK_METHOD_SET | 
+                                 GST_SEEK_FLAG_FLUSH, 
+                                 avimux->total_data);
+      gst_pad_send_event(GST_PAD_PEER(avimux->srcpad), event);
+    }
   }
   avimux->avix_start = avimux->total_data;
 
@@ -804,18 +916,20 @@ gst_avimux_start_file (GstAviMux *avimux)
   avimux->num_frames = 0;
   avimux->numx_frames = 0;
   avimux->audio_size = 0;
+  avimux->avix_start = 0;
 
   avimux->idx_index = 0;
-  avimux->idx_offset = avimux->header_size + 12;
+  avimux->idx_offset = 0; /* see 10 lines below */
   avimux->idx_size = 0;
   avimux->idx_count = 0;
   avimux->idx = NULL;
 
   /* header */
-  avimux->avi_hdr.streams = avimux->num_video_pads + avimux->num_audio_pads;
+  avimux->avi_hdr.streams = avimux->video_pad_connected?1:0 + avimux->audio_pad_connected?1:0;
   avimux->is_bigfile = FALSE;
 
   header = gst_avimux_riff_get_avi_header(avimux);
+  avimux->idx_offset = avimux->header_size + 12;
   avimux->total_data += GST_BUFFER_SIZE(header);
   gst_pad_push(avimux->srcpad, header);
 
@@ -830,7 +944,7 @@ gst_avimux_stop_file (GstAviMux *avimux)
   GstBuffer *header;
 
   /* if bigfile, rewrite header, else write indexes */
-  if (avimux->num_video_pads)
+  if (avimux->video_pad_connected)
   {
     if (avimux->is_bigfile)
     {
@@ -845,23 +959,32 @@ gst_avimux_stop_file (GstAviMux *avimux)
 
   /* statistics/total_frames/... */
   avimux->avi_hdr.tot_frames = avimux->num_frames;
-  if (avimux->num_video_pads)
+  if (avimux->video_pad_connected)
     avimux->vids_hdr.length = avimux->num_frames;
-  if (avimux->num_audio_pads)
-    avimux->auds_hdr.length = avimux->audio_size/avimux->auds_hdr.scale;
+  if (avimux->audio_pad_connected)
+  {
+    if (avimux->auds_hdr.scale)
+      avimux->auds_hdr.length = avimux->audio_size/avimux->auds_hdr.scale;
+    else
+      avimux->auds_hdr.length = 0; /* urm...? FIXME! ;-) */
+  }
 
-  /* TODO: fps calculation!! */
-  avimux->avi_hdr.us_frame = 1000000/avimux->framerate;
+  /* set rate and everything having to do with that */
+  avimux->avi_hdr.us_frame = avimux->vids_hdr.scale = 1000000/avimux->framerate;
   avimux->avi_hdr.max_bps = 0;
-  if (avimux->num_audio_pads)
+  if (avimux->audio_pad_connected)
     avimux->avi_hdr.max_bps += avimux->auds.av_bps;
-  if (avimux->num_video_pads)
-    avimux->avi_hdr.max_bps += avimux->vids.bit_cnt/8 * avimux->framerate;
+  if (avimux->video_pad_connected)
+    avimux->avi_hdr.max_bps += ((avimux->vids.bit_cnt+7)/8) *
+                               avimux->framerate *
+                               avimux->vids.image_size;
 
   /* seek and rewrite the header */
   header = gst_avimux_riff_get_avi_header(avimux);
-  event = gst_event_new_seek(GST_SEEK_BYTEOFFSET_SET, 0, TRUE);
-  gst_pad_send_event(avimux->srcpad, event);
+  event = gst_event_new_seek (GST_FORMAT_BYTES | 
+                             GST_SEEK_METHOD_SET |
+                             GST_SEEK_FLAG_FLUSH, 0);
+  gst_pad_send_event(GST_PAD_PEER(avimux->srcpad), event);
   gst_pad_push(avimux->srcpad, header);
 
   avimux->write_header = TRUE;
@@ -895,6 +1018,15 @@ gst_avimux_handle_event (GstPad *pad, GstEvent *event)
     case GST_EVENT_NEW_MEDIA:
       avimux->restart = TRUE;
       break;
+    case GST_EVENT_EOS:
+      /* is this allright? */
+      if (pad == avimux->videosinkpad)
+        avimux->video_pad_eos = TRUE;
+      else if (pad == avimux->audiosinkpad)
+        avimux->audio_pad_eos = TRUE;
+      else
+        g_warning("Unknown pad for EOS!");
+      break;
     default:
       break;
   }
@@ -902,91 +1034,202 @@ gst_avimux_handle_event (GstPad *pad, GstEvent *event)
   return TRUE;
 }
 
+
+/* fill the internal queue for each available pad */
 static void
-gst_avimux_chain (GstPad *pad, GstBuffer *buf)
+gst_avimux_fill_queue (GstAviMux *avimux)
 {
-  GstAviMux *avimux;
-  GstBuffer *newbuf;
-  const gchar *padname = gst_pad_get_name (pad);
+  GstBuffer *buffer;
 
-  g_return_if_fail (pad != NULL);
-  g_return_if_fail (GST_IS_PAD (pad));
-  g_return_if_fail (buf != NULL);
-  g_return_if_fail (GST_BUFFER_DATA (buf) != NULL);
+  if (!avimux->audio_buffer_queue &&
+       GST_PAD_IS_USABLE(avimux->audiosinkpad) &&
+      !avimux->audio_pad_eos)
+  {
+    while (1)
+    {
+      buffer = gst_pad_pull(avimux->audiosinkpad);
+      if (GST_IS_EVENT(buffer))
+      {
+        gst_avimux_handle_event(avimux->audiosinkpad, GST_EVENT(buffer));
+      }
+      else
+      {
+        avimux->audio_buffer_queue = buffer;
+        break;
+      }
+    }
+  }
 
-  avimux = GST_AVIMUX (gst_pad_get_parent (pad));
-  
-  if (GST_IS_EVENT(buf))
+  if (!avimux->video_buffer_queue &&
+       GST_PAD_IS_USABLE(avimux->videosinkpad) &&
+      !avimux->video_pad_eos)
   {
-    gst_avimux_handle_event(pad, GST_EVENT(buf));
-    return;
+    while (1)
+    {
+      buffer = gst_pad_pull(avimux->videosinkpad);
+      if (GST_IS_EVENT(buffer))
+      {
+        gst_avimux_handle_event(avimux->videosinkpad, GST_EVENT(buffer));
+      }
+      else
+      {
+        avimux->video_buffer_queue = buffer;
+       if (avimux->framerate < 0)
+          avimux->framerate = gst_video_frame_rate(GST_PAD_PEER(avimux->videosinkpad));
+        break;
+      }
+    }
   }
+}
 
 
-  if (avimux->write_header)
-    gst_avimux_start_file(avimux);
+/* send extra 'padding' data */
+static void
+gst_avimux_send_pad_data (GstAviMux *avimux,
+                          gulong     num_bytes)
+{
+  GstBuffer *buffer;
+
+  buffer = gst_buffer_new();
+  GST_BUFFER_SIZE(buffer) = num_bytes;
+  GST_BUFFER_DATA(buffer) = g_malloc(num_bytes);
+  memset(GST_BUFFER_DATA(buffer), 0, num_bytes);
+
+  gst_pad_push(avimux->srcpad, buffer);
+}
+
+/* do audio buffer */
+static void
+gst_avimux_do_audio_buffer (GstAviMux *avimux)
+{
+  GstBuffer *data = avimux->audio_buffer_queue, *header;
+  gulong total_size, pad_bytes;
 
-  if (strncmp(padname, "audio_", 6) == 0)
+  /* write a audio header + index entry */
+  header = gst_avimux_riff_get_audio_header((GST_BUFFER_SIZE(data)+3)&~3);
+  total_size = GST_BUFFER_SIZE(header) + GST_BUFFER_SIZE(data);
+  avimux->total_data += total_size;
+
+  if (avimux->is_bigfile)
+  {
+    avimux->datax_size += total_size;
+  }
+  else
   {
-    /* write a audio header + index entry */
-    newbuf = gst_avimux_riff_get_audio_header(GST_BUFFER_SIZE(buf));
-    avimux->total_data += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf);
+    avimux->data_size += total_size;
+    avimux->audio_size += GST_BUFFER_SIZE(data);
+    gst_avimux_add_index(avimux, "01wb", 0x0, total_size);
+  }
 
-    if (avimux->is_bigfile)
-    {
-      avimux->datax_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf);
-    }
-    else
+  gst_pad_push(avimux->srcpad, header);
+  pad_bytes = ((GST_BUFFER_SIZE(data)+3)&~3) - GST_BUFFER_SIZE(data);
+  if (pad_bytes)
+    if (GST_BUFFER_MAXSIZE(data) >= GST_BUFFER_SIZE(data) + pad_bytes)
     {
-      avimux->data_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf);
-      avimux->audio_size += GST_BUFFER_SIZE(buf);
-      gst_avimux_add_index(avimux, avimux->auds.format, 0x0, GST_BUFFER_SIZE(buf));
+      GST_BUFFER_SIZE(data) += pad_bytes;
+      pad_bytes = 0;
     }
+  gst_pad_push(avimux->srcpad, data);
+  if (pad_bytes)
+    gst_avimux_send_pad_data(avimux, pad_bytes);
+  avimux->audio_buffer_queue = NULL;
+}
 
-    gst_pad_push(avimux->srcpad, newbuf);
-  }
-  else if (strncmp(padname, "video_", 6) == 0)
+
+/* do video buffer */
+static void
+gst_avimux_do_video_buffer (GstAviMux *avimux)
+{
+  GstBuffer *data = avimux->video_buffer_queue, *header;
+  gulong total_size, pad_bytes;
+
+  if (avimux->restart)
+    gst_avimux_restart_file(avimux);
+
+  /* write a video header + index entry */
+  if ((avimux->is_bigfile?avimux->datax_size:avimux->data_size)+GST_BUFFER_SIZE(data)>1024*1024*2000)
   {
-    if (avimux->restart)
+    if (avimux->enable_large_avi)
+      gst_avimux_bigfile(avimux, FALSE);
+    else
       gst_avimux_restart_file(avimux);
+  }
+
+  header = gst_avimux_riff_get_video_header((GST_BUFFER_SIZE(data)+3)&~3);
+  total_size = GST_BUFFER_SIZE(header) + GST_BUFFER_SIZE(data);
+  avimux->total_data += total_size;
+  avimux->total_frames++;
 
-    /* write a video header + index entry */
-    GST_BUFFER_SIZE(buf) = (GST_BUFFER_SIZE(buf)+3)&~3;
+  if (avimux->is_bigfile)
+  {
+    avimux->datax_size += total_size;
+    avimux->numx_frames++;
+  }
+  else
+  {
+    avimux->data_size += total_size;
+    avimux->num_frames++;
+    gst_avimux_add_index(avimux, "00db", 0x12, total_size);
+  }
 
-    if ((avimux->is_bigfile?avimux->datax_size:avimux->data_size)+GST_BUFFER_SIZE(buf)>1024*1024*2000)
+  gst_pad_push(avimux->srcpad, header);
+  pad_bytes = ((GST_BUFFER_SIZE(data)+3)&~3) - GST_BUFFER_SIZE(data);
+  if (pad_bytes)
+    if (GST_BUFFER_MAXSIZE(data) >= GST_BUFFER_SIZE(data) + pad_bytes)
     {
-      if (avimux->enable_large_avi)
-        gst_avimux_bigfile(avimux, FALSE);
-      else
-        gst_avimux_restart_file(avimux);
+      GST_BUFFER_SIZE(data) += pad_bytes;
+      pad_bytes = 0;
     }
+  gst_pad_push(avimux->srcpad, data);
+  if (pad_bytes)
+    gst_avimux_send_pad_data(avimux, pad_bytes);
+  avimux->video_buffer_queue = NULL;
+}
 
-    newbuf = gst_avimux_riff_get_video_header(GST_BUFFER_SIZE(buf));
-    avimux->total_data += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf);
-    avimux->total_frames++;
 
-    if (avimux->is_bigfile)
-    {
-      avimux->datax_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf);
-      avimux->numx_frames++;
-    }
+/* take the oldest buffer in our internal queue and push-it */
+static gboolean
+gst_avimux_do_one_buffer (GstAviMux *avimux)
+{
+  if (avimux->video_buffer_queue &&
+      avimux->audio_buffer_queue)
+  {
+    if (GST_BUFFER_TIMESTAMP(avimux->video_buffer_queue) <=
+        GST_BUFFER_TIMESTAMP(avimux->audio_buffer_queue))
+      gst_avimux_do_video_buffer(avimux);
     else
-    {
-      avimux->data_size += GST_BUFFER_SIZE(newbuf) + GST_BUFFER_SIZE(buf);
-      avimux->num_frames++;
-      gst_avimux_add_index(avimux, avimux->vids.compression, 0x12, GST_BUFFER_SIZE(buf));
-    }
-
-    gst_pad_push(avimux->srcpad, newbuf);
+      gst_avimux_do_audio_buffer(avimux);
   }
-  else
+  else if (avimux->video_buffer_queue ||
+           avimux->audio_buffer_queue)
   {
-    g_warning("Unknown padname \'%s\'\n", padname);
-    return;
+    if (avimux->video_buffer_queue)
+      gst_avimux_do_video_buffer(avimux);
+    else
+      gst_avimux_do_audio_buffer(avimux);
   }
+  else
+    return FALSE;
 
-  /* data */
-  gst_pad_push(avimux->srcpad, buf);
+  return TRUE;
+}
+
+
+static void
+gst_avimux_loop (GstElement *element)
+{
+  GstAviMux *avimux;
+
+  avimux = GST_AVIMUX(element);
+
+  /* first fill queue (some elements only set caps when
+   * flowing data), then write header */
+  gst_avimux_fill_queue(avimux);
+  
+  if (avimux->write_header)
+    gst_avimux_start_file(avimux);
+
+  gst_avimux_do_one_buffer(avimux);
 }
 
 static void
@@ -1006,9 +1249,6 @@ gst_avimux_get_property (GObject    *object,
     case ARG_BIGFILE:
       g_value_set_boolean(value, avimux->enable_large_avi);
       break;
-    case ARG_FRAMERATE:
-      g_value_set_double(value, avimux->framerate);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1032,9 +1272,6 @@ gst_avimux_set_property (GObject      *object,
     case ARG_BIGFILE:
       avimux->enable_large_avi = g_value_get_boolean(value);
       break;
-    case ARG_FRAMERATE:
-      avimux->framerate = g_value_get_double(value);
-      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1047,23 +1284,23 @@ gst_avimux_change_state (GstElement *element)
   GstAviMux *avimux;
   gint transition = GST_STATE_TRANSITION (element);
 
-  /* TODO: timer (for fps calculations) */
-
   g_return_val_if_fail(GST_IS_AVIMUX(element), GST_STATE_FAILURE);
   
   avimux = GST_AVIMUX(element);
 
   switch (transition) {
     case GST_STATE_READY_TO_PAUSED:
-      /*gst_avimux_start_file(avimux);*/
       break;
     case GST_STATE_PAUSED_TO_PLAYING:
+      avimux->framerate = -1; /* means that we fill it in later */
+      avimux->video_pad_eos = avimux->audio_pad_eos = FALSE;
       break;
     case GST_STATE_PLAYING_TO_PAUSED:
+      /* this function returns TRUE while it handles buffers */
+      while (gst_avimux_do_one_buffer(avimux));
       gst_avimux_stop_file(avimux);
       break;
     case GST_STATE_PAUSED_TO_READY:
-      /*gst_avimux_stop_file(avimux);*/
       break;
   }
 
@@ -1078,22 +1315,17 @@ plugin_init (GModule *module, GstPlugin *plugin)
 {
   GstElementFactory *factory;
 
-  /* this filter needs the riff parser */
-#if 0
-  if (!gst_library_load ("gstriff")) {
-    gst_info ("avimux: could not load support library: 'gstriff'\n");
+  if (!gst_library_load("gstvideo"))
     return FALSE;
-  }
-#endif
 
   /* create an elementfactory for the avimux element */
-  factory = gst_elementfactory_new ("avimux", GST_TYPE_AVIMUX,
-                                    &gst_avimux_details);
+  factory = gst_element_factory_new ("avimux", GST_TYPE_AVIMUX,
+                                     &gst_avimux_details);
   g_return_val_if_fail (factory != NULL, FALSE);
 
-  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_factory));
-  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (audio_sink_factory));
-  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (video_sink_factory));
+  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_factory));
+  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (audio_sink_factory));
+  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (video_sink_factory));
   
   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));