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
1 /* AVI muxer plugin for GStreamer
2  * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /* based on:
21  * - the old avimuxer (by Wim Taymans)
22  * - xawtv's aviwriter (by Gerd Knorr)
23  * - mjpegtools' avilib (by Rainer Johanni)
24  * - openDML large-AVI docs
25  */
26
27
28 #include <config.h>
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <gst/video/video.h>
34
35 #include "gstavimux.h"
36
37 #ifndef LE_FROM_GUINT16
38 #define LE_FROM_GUINT16 GUINT16_FROM_LE
39 #endif
40 #ifndef LE_FROM_GUINT32
41 #define LE_FROM_GUINT32 GUINT32_FROM_LE
42 #endif
43
44 /* elementfactory information */
45 static GstElementDetails 
46 gst_avimux_details = 
47 {
48   "Avi multiplexer",
49   "Codec/Muxer",
50   "LGPL",
51   "Muxes audio and video into an avi stream",
52   VERSION,
53   "Ronald Bultje <rbultje@ronald.bitfreak.net>",
54   "(C) 2002",
55 };
56
57 /* AviMux signals and args */
58 enum {
59   /* FILL ME */
60   LAST_SIGNAL
61 };
62
63 enum {
64   ARG_0,
65   ARG_BIGFILE,
66 };
67
68 GST_PAD_TEMPLATE_FACTORY (src_factory,
69   "src",
70   GST_PAD_SRC,
71   GST_PAD_ALWAYS,
72   GST_CAPS_NEW (
73     "avimux_src_video",
74     "video/avi",
75     NULL
76   )
77 )
78     
79 GST_PAD_TEMPLATE_FACTORY (video_sink_factory,
80   "video_%d",
81   GST_PAD_SINK,
82   GST_PAD_REQUEST,
83   GST_CAPS_NEW (
84     "avimux_sink_video",
85     "video/avi",
86       "format",   GST_PROPS_STRING ("strf_vids")
87   ),
88   GST_CAPS_NEW (
89     "avimux_sink_video",
90     "video/raw",
91       "format", GST_PROPS_LIST (
92                   GST_PROPS_FOURCC (GST_MAKE_FOURCC('Y','U','Y','2')),
93                   GST_PROPS_FOURCC (GST_MAKE_FOURCC('I','4','2','0')),
94                   GST_PROPS_FOURCC (GST_MAKE_FOURCC('Y','4','1','P'))
95                 ),
96       "width",  GST_PROPS_INT_RANGE (16, 4096),
97       "height", GST_PROPS_INT_RANGE (16, 4096)
98   ),
99   GST_CAPS_NEW (
100     "avimux_sink_video",
101     "video/raw",
102       "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC('R','G','B',' ')),
103       "width",  GST_PROPS_INT_RANGE (16, 4096),
104       "height", GST_PROPS_INT_RANGE (16, 4096),
105       "depth",  GST_PROPS_LIST(
106                   GST_PROPS_INT(16),
107                   GST_PROPS_INT(16),
108                   GST_PROPS_INT(24),
109                   GST_PROPS_INT(32)
110                 ),
111       "bpp",    GST_PROPS_LIST(
112                   GST_PROPS_INT(15),
113                   GST_PROPS_INT(16),
114                   GST_PROPS_INT(24),
115                   GST_PROPS_INT(32)
116                 )
117   ),
118   GST_CAPS_NEW (
119     "avimux_sink_video",
120     "video/jpeg",
121       "width",  GST_PROPS_INT_RANGE (16, 4096),
122       "height", GST_PROPS_INT_RANGE (16, 4096)
123   )
124 )
125     
126 GST_PAD_TEMPLATE_FACTORY (audio_sink_factory,
127   "audio_%d",
128   GST_PAD_SINK,
129   GST_PAD_REQUEST,
130   GST_CAPS_NEW (
131     "avimux_sink_audio",
132     "video/avi",
133       "format",   GST_PROPS_STRING ("strf_auds")
134   ),
135   GST_CAPS_NEW (
136     "avimux_sink_audio",
137     "audio/raw",
138       "format",           GST_PROPS_STRING ("int"),
139       "law",              GST_PROPS_INT (0),
140       "endianness",       GST_PROPS_INT (G_BYTE_ORDER),
141       "signed",           GST_PROPS_LIST (
142                             GST_PROPS_BOOLEAN (TRUE),
143                             GST_PROPS_BOOLEAN (FALSE)
144                           ),
145       "width",            GST_PROPS_LIST (
146                             GST_PROPS_INT (8),
147                             GST_PROPS_INT (16)
148                           ),
149       "depth",            GST_PROPS_LIST (
150                             GST_PROPS_INT (8),
151                             GST_PROPS_INT (16)
152                           ),
153       "rate",             GST_PROPS_INT_RANGE (1000, 48000),
154       "channels",         GST_PROPS_INT_RANGE (1, 2)
155   ),
156   GST_CAPS_NEW (
157     "avimux_sink_audio",
158     "audio/x-mp3",
159       NULL
160   ),
161   GST_CAPS_NEW (
162     "avimux_sink_audio",
163     "application/x-ogg",
164       NULL
165   )
166 )
167     
168
169 static void     gst_avimux_class_init                (GstAviMuxClass *klass);
170 static void     gst_avimux_init                      (GstAviMux      *avimux);
171
172 static void     gst_avimux_loop                      (GstElement     *element);
173 static gboolean gst_avimux_handle_event              (GstPad         *pad,
174                                                       GstEvent       *event);
175 static GstPad*  gst_avimux_request_new_pad           (GstElement     *element,
176                                                       GstPadTemplate *templ,
177                                                       const gchar    *name);
178 static void     gst_avimux_set_property              (GObject        *object,
179                                                       guint           prop_id,
180                                                       const GValue   *value,
181                                                       GParamSpec     *pspec);
182 static void     gst_avimux_get_property              (GObject        *object,
183                                                       guint           prop_id,
184                                                       GValue         *value,
185                                                       GParamSpec     *pspec);
186 static GstElementStateReturn gst_avimux_change_state (GstElement     *element);
187
188 static GstElementClass *parent_class = NULL;
189 /*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */
190
191 GType
192 gst_avimux_get_type (void) 
193 {
194   static GType avimux_type = 0;
195
196   if (!avimux_type) {
197     static const GTypeInfo avimux_info = {
198       sizeof(GstAviMuxClass),      
199       NULL,
200       NULL,
201       (GClassInitFunc)gst_avimux_class_init,
202       NULL,
203       NULL,
204       sizeof(GstAviMux),
205       0,
206       (GInstanceInitFunc)gst_avimux_init,
207     };
208     avimux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
209   }
210   return avimux_type;
211 }
212
213 static void
214 gst_avimux_class_init (GstAviMuxClass *klass) 
215 {
216   GObjectClass *gobject_class;
217   GstElementClass *gstelement_class;
218
219   gobject_class = (GObjectClass*)klass;
220   gstelement_class = (GstElementClass*)klass;
221
222   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
223
224   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BIGFILE,
225     g_param_spec_boolean("bigfile","Bigfile Support","Whether to capture large or small AVI files",
226     0,G_PARAM_READWRITE));
227
228   gstelement_class->request_new_pad = gst_avimux_request_new_pad;
229
230   gstelement_class->change_state = gst_avimux_change_state;
231
232   gstelement_class->get_property = gst_avimux_get_property;
233   gstelement_class->set_property = gst_avimux_set_property;
234 }
235
236 static const GstEventMask *
237 gst_avimux_get_event_masks (GstPad *pad)
238 {
239   static const GstEventMask gst_avimux_src_event_masks[] = {
240     { GST_EVENT_NEW_MEDIA, 0 },
241     { 0, }
242   };
243   static const GstEventMask gst_avimux_sink_event_masks[] = {
244     { GST_EVENT_EOS, 0 },
245     { 0, }
246   };
247
248   if (GST_PAD_IS_SRC(pad))
249     return gst_avimux_src_event_masks;
250   else
251     return gst_avimux_sink_event_masks;
252 }
253
254 static void 
255 gst_avimux_init (GstAviMux *avimux) 
256 {
257   avimux->srcpad = gst_pad_new_from_template (
258                   GST_PAD_TEMPLATE_GET (src_factory), "src");
259   gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
260
261   GST_FLAG_SET (GST_ELEMENT(avimux), GST_ELEMENT_EVENT_AWARE);
262   gst_pad_set_event_function(avimux->srcpad, gst_avimux_handle_event);
263   gst_pad_set_event_mask_function(avimux->srcpad, gst_avimux_get_event_masks);
264
265   avimux->audiosinkpad = NULL;
266   avimux->audio_pad_connected = FALSE;
267   avimux->videosinkpad = NULL;
268   avimux->video_pad_connected = FALSE;
269
270   avimux->audio_buffer_queue = NULL;
271   avimux->video_buffer_queue = NULL;
272
273   avimux->num_frames = 0;
274
275   /* audio/video/AVI header initialisation */
276   memset(&(avimux->avi_hdr),0,sizeof(gst_riff_avih));
277   memset(&(avimux->vids_hdr),0,sizeof(gst_riff_strh));
278   memset(&(avimux->vids),0,sizeof(gst_riff_strf_vids));
279   memset(&(avimux->auds_hdr),0,sizeof(gst_riff_strh));
280   memset(&(avimux->auds),0,sizeof(gst_riff_strf_auds));
281   avimux->vids_hdr.type = GST_MAKE_FOURCC('v','i','d','s');
282   avimux->vids_hdr.rate = 1000000;
283   avimux->auds_hdr.type = GST_MAKE_FOURCC('a','u','d','s');
284
285   avimux->idx = NULL;
286
287   avimux->write_header = TRUE;
288
289   avimux->enable_large_avi = TRUE;
290
291   avimux->framerate = 0;
292
293   gst_element_set_loop_function(GST_ELEMENT(avimux), gst_avimux_loop);
294 }
295
296 static GstPadConnectReturn
297 gst_avimux_sinkconnect (GstPad *pad, GstCaps *vscaps)
298 {
299   GstAviMux *avimux;
300   GstCaps *caps;
301
302   avimux = GST_AVIMUX (gst_pad_get_parent (pad));
303
304   /* we are not going to act on variable caps */
305   if (!GST_CAPS_IS_FIXED (vscaps))
306     return GST_PAD_CONNECT_DELAYED;
307
308   GST_DEBUG (0, "avimux: sinkconnect triggered on %s", gst_pad_get_name (pad));
309
310   for (caps = vscaps; caps != NULL; caps = vscaps = vscaps->next)
311   {
312     const gchar* mimetype = gst_caps_get_mime(caps);
313
314     if (!strcmp (mimetype, "video/avi"))
315     {
316       const gchar* format;
317       
318       gst_caps_get_string (caps, "format", &format);
319
320       if (!strncmp (format, "strf_vids", 9)) {
321         avimux->vids.size        = sizeof(gst_riff_strf_vids);
322         gst_caps_get (caps,
323                       "width",       &avimux->vids.width,
324                       "height",      &avimux->vids.height,
325                       "planes",      &avimux->vids.planes,
326                       "bit_cnt",     &avimux->vids.bit_cnt,
327                       "compression", &avimux->vids.compression,
328                       "image_size",  &avimux->vids.image_size,
329                       "xpels_meter", &avimux->vids.xpels_meter,
330                       "ypels_meter", &avimux->vids.ypels_meter,
331                       "num_colors",  &avimux->vids.num_colors,
332                       "imp_colors",  &avimux->vids.imp_colors,
333                       NULL);
334         avimux->vids_hdr.fcc_handler = avimux->vids.compression;
335         avimux->avi_hdr.width = avimux->vids.width;
336         avimux->avi_hdr.height = avimux->vids.height;
337         goto done;
338       }
339       else if (!strncmp (format, "strf_auds", 9)) {
340         gst_caps_get (caps,
341                       "format",      &avimux->auds.format,
342                       "channels",    &avimux->auds.channels,
343                       "rate",        &avimux->auds.rate,
344                       "av_bps",      &avimux->auds.av_bps,
345                       "blockalign",  &avimux->auds.blockalign,
346                       "size",        &avimux->auds.size,
347                       NULL);
348         avimux->auds_hdr.samplesize = avimux->auds_hdr.scale = avimux->auds.blockalign;
349         avimux->auds_hdr.rate = avimux->auds.av_bps;
350         goto done;
351       }
352     }
353     else if (!strcmp (mimetype, "video/raw"))
354     {
355       guint32 format;
356       gint temp;
357
358       gst_caps_get_fourcc_int (caps, "format", &format);
359       switch (format)
360       {
361         case GST_MAKE_FOURCC('Y','U','Y','2'):
362         case GST_MAKE_FOURCC('I','4','2','0'):
363         case GST_MAKE_FOURCC('Y','4','1','P'):
364         case GST_MAKE_FOURCC('R','G','B',' '):
365           avimux->vids.size        = sizeof(gst_riff_strf_vids);
366           gst_caps_get (caps, "width", &avimux->vids.width,
367                               "height", &avimux->vids.height, NULL);
368           avimux->vids.planes      = 1;
369           switch (format)
370           {
371             case GST_MAKE_FOURCC('Y','U','Y','2'):
372               avimux->vids.bit_cnt     = 16; /* YUY2 */
373               break;
374             case GST_MAKE_FOURCC('R','G','B',' '):
375               gst_caps_get_int (caps, "bpp", &temp); /* RGB */
376               avimux->vids.bit_cnt = temp;
377               break;
378             case GST_MAKE_FOURCC('Y','4','1','P'):
379             case GST_MAKE_FOURCC('I','4','2','0'):
380               avimux->vids.bit_cnt     = 12; /* Y41P or I420 */
381               break;
382           }
383           gst_caps_get_fourcc_int(caps, "format", &avimux->vids.compression);
384           avimux->vids_hdr.fcc_handler = avimux->vids.compression;
385           avimux->vids.image_size  = avimux->vids.height * avimux->vids.width;
386           avimux->avi_hdr.width = avimux->vids.width;
387           avimux->avi_hdr.height = avimux->vids.height;
388           goto done;
389         default:
390           break;
391       }
392     }
393     else if (!strcmp (mimetype, "video/jpeg"))
394     {
395       avimux->vids.size        = sizeof(gst_riff_strf_vids);
396       gst_caps_get (caps, "width", &avimux->vids.width,
397                           "height", &avimux->vids.height, NULL);
398       avimux->vids.planes      = 1;
399       avimux->vids.bit_cnt     = 24;
400       avimux->vids_hdr.fcc_handler = avimux->vids.compression = GST_MAKE_FOURCC('M','J','P','G');
401       avimux->avi_hdr.width = avimux->vids.width;
402       avimux->avi_hdr.height = avimux->vids.height;
403       avimux->vids.image_size  = avimux->vids.height * avimux->vids.width;
404       goto done;
405     }
406     else if (!strcmp (mimetype, "audio/raw"))
407     {
408       gint width;
409
410       avimux->auds.format      = GST_RIFF_WAVE_FORMAT_PCM;
411       gst_caps_get (caps, "channels",   &avimux->auds.channels,
412                           "rate",       &avimux->auds.rate,
413                           "width",      &width,
414                           "depth",      &avimux->auds.size,
415                           NULL);
416       avimux->auds_hdr.rate = avimux->auds.av_bps = width * avimux->auds.rate * avimux->auds.channels / 8;
417       avimux->auds_hdr.samplesize = avimux->auds_hdr.scale = avimux->auds.blockalign = width * avimux->auds.channels/8;
418       goto done;
419     }
420     else if (!strcmp (mimetype, "audio/x-mp3"))
421     {
422       gint layer;
423
424       gst_caps_get_int(caps, "layer", &layer);
425
426       /* we don't need to do anything here, compressed mp3 contains it all */
427       avimux->auds.format      = (layer == 3?
428                                    GST_RIFF_WAVE_FORMAT_MPEGL3 : 
429                                    GST_RIFF_WAVE_FORMAT_MPEGL12);
430       goto done;
431     }
432     else if (!strcmp (mimetype, "application/x-ogg"))
433     {
434       avimux->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS1;
435       goto done;
436     }
437   }
438   return GST_PAD_CONNECT_REFUSED;
439
440 done:
441   return GST_PAD_CONNECT_OK;
442 }
443
444 static void
445 gst_avimux_pad_connect (GstPad   *pad,
446                         GstPad   *peer,
447                         gpointer  data)
448 {
449   GstAviMux *avimux = GST_AVIMUX(data);
450   const gchar *padname = gst_pad_get_name (pad);
451
452   if (pad == avimux->audiosinkpad)
453   {
454     avimux->audio_pad_connected = TRUE;
455   }
456   else if (pad == avimux->videosinkpad)
457   {
458     avimux->video_pad_connected = TRUE;
459   }
460   else
461   {
462     g_warning("Unknown padname '%s'", padname);
463     return;
464   }
465
466   GST_DEBUG(GST_CAT_PLUGIN_INFO, "pad '%s' connected", padname);
467 }
468
469 static void
470 gst_avimux_pad_disconnect (GstPad   *pad,
471                            GstPad   *peer,
472                            gpointer  data)
473 {
474   GstAviMux *avimux = GST_AVIMUX(data);
475   const gchar *padname = gst_pad_get_name (pad);
476
477   if (pad == avimux->audiosinkpad)
478   {
479     avimux->audio_pad_connected = FALSE;
480     avimux->audiosinkpad = NULL;
481   }
482   else if (pad == avimux->videosinkpad)
483   {
484     avimux->video_pad_connected = FALSE;
485     avimux->videosinkpad = NULL;
486   }
487   else
488   {
489     g_warning("Unknown padname '%s'", padname);
490     return;
491   }
492
493   GST_DEBUG(GST_CAT_PLUGIN_INFO, "pad '%s' disconnected", padname);
494
495   /* is this allowed? */
496   gst_pad_destroy(pad);
497 }
498
499 static GstPad*
500 gst_avimux_request_new_pad (GstElement     *element,
501                             GstPadTemplate *templ,
502                             const gchar    *req_name)
503 {
504   GstAviMux *avimux;
505   GstPad *newpad;
506
507   g_return_val_if_fail (templ != NULL, NULL);
508
509   if (templ->direction != GST_PAD_SINK) {
510     g_warning ("avimux: request pad that is not a SINK pad\n");
511     return NULL;
512   }
513
514   g_return_val_if_fail (GST_IS_AVIMUX (element), NULL);
515
516   avimux = GST_AVIMUX (element);
517
518   if (templ == GST_PAD_TEMPLATE_GET (audio_sink_factory)) {
519     g_return_val_if_fail(avimux->audiosinkpad == NULL, NULL);
520     newpad = gst_pad_new_from_template (templ, "audio_00");
521     avimux->audiosinkpad = newpad;
522   }
523   else if (templ == GST_PAD_TEMPLATE_GET (video_sink_factory)) {
524     g_return_val_if_fail(avimux->videosinkpad == NULL, NULL);
525     newpad = gst_pad_new_from_template (templ, "video_00");
526     avimux->videosinkpad = newpad;
527   }
528   else {
529     g_warning ("avimux: this is not our template!\n");
530     return NULL;
531   }
532
533   g_signal_connect(newpad, "connected",
534     G_CALLBACK(gst_avimux_pad_connect), (gpointer)avimux);
535   g_signal_connect(newpad, "disconnected",
536     G_CALLBACK(gst_avimux_pad_disconnect), (gpointer)avimux);
537   gst_pad_set_connect_function (newpad, gst_avimux_sinkconnect);
538   gst_element_add_pad (element, newpad);
539   gst_pad_set_event_function(newpad, gst_avimux_handle_event);
540   gst_pad_set_event_mask_function(newpad, gst_avimux_get_event_masks);
541   
542   return newpad;
543 }
544
545 /* maybe some of these functions should be moved to riff.h? */
546
547 /* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */
548
549 static GstBuffer *
550 gst_avimux_riff_get_avi_header (GstAviMux *avimux)
551 {
552   GstBuffer *buffer;
553   guint8 *buffdata;
554   guint16 temp16;
555   guint32 temp32;
556
557   buffer = gst_buffer_new();
558
559   /* first, let's see what actually needs to be in the buffer */
560   GST_BUFFER_SIZE(buffer) = 0;
561   GST_BUFFER_SIZE(buffer) += 32 + sizeof(gst_riff_avih); /* avi header */
562   if (avimux->video_pad_connected)
563   { /* we have video */
564     GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids); /* vid hdr */
565     GST_BUFFER_SIZE(buffer) += 24; /* odml header */
566   }
567   if (avimux->audio_pad_connected)
568   { /* we have audio */
569     GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds); /* aud hdr */
570   }
571   /* this is the "riff size" */
572   avimux->header_size = GST_BUFFER_SIZE(buffer);
573   GST_BUFFER_SIZE(buffer) += 12; /* avi data header */
574
575   /* allocate the buffer */
576   buffdata = GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(buffer));
577
578   /* avi header metadata */
579   memcpy(buffdata, "RIFF", 4); buffdata += 4;
580   temp32 = LE_FROM_GUINT32(avimux->header_size + avimux->idx_size + avimux->data_size);
581   memcpy(buffdata, &temp32, 4); buffdata += 4;
582   memcpy(buffdata, "AVI ", 4); buffdata += 4;
583   memcpy(buffdata, "LIST", 4); buffdata += 4;
584   temp32 = LE_FROM_GUINT32(avimux->header_size - 4*5);
585   memcpy(buffdata, &temp32, 4); buffdata += 4;
586   memcpy(buffdata, "hdrl", 4); buffdata += 4;
587   memcpy(buffdata, "avih", 4); buffdata += 4;
588   temp32 = LE_FROM_GUINT32(sizeof(gst_riff_avih));
589   memcpy(buffdata, &temp32, 4); buffdata += 4;
590   /* the AVI header itself */
591   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.us_frame);
592   memcpy(buffdata, &temp32, 4); buffdata += 4;
593   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.max_bps);
594   memcpy(buffdata, &temp32, 4); buffdata += 4;
595   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.pad_gran);
596   memcpy(buffdata, &temp32, 4); buffdata += 4;
597   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.flags);
598   memcpy(buffdata, &temp32, 4); buffdata += 4;
599   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.tot_frames);
600   memcpy(buffdata, &temp32, 4); buffdata += 4;
601   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.init_frames);
602   memcpy(buffdata, &temp32, 4); buffdata += 4;
603   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.streams);
604   memcpy(buffdata, &temp32, 4); buffdata += 4;
605   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.bufsize);
606   memcpy(buffdata, &temp32, 4); buffdata += 4;
607   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.width);
608   memcpy(buffdata, &temp32, 4); buffdata += 4;
609   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.height);
610   memcpy(buffdata, &temp32, 4); buffdata += 4;
611   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.scale);
612   memcpy(buffdata, &temp32, 4); buffdata += 4;
613   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.rate);
614   memcpy(buffdata, &temp32, 4); buffdata += 4;
615   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.start);
616   memcpy(buffdata, &temp32, 4); buffdata += 4;
617   temp32 = LE_FROM_GUINT32(avimux->avi_hdr.length);
618   memcpy(buffdata, &temp32, 4); buffdata += 4;
619
620   if (avimux->video_pad_connected)
621   {
622     /* video header metadata */
623     memcpy(buffdata, "LIST", 4); buffdata += 4;
624     temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids) + 4*5);
625     memcpy(buffdata, &temp32, 4); buffdata += 4;
626     memcpy(buffdata, "strl", 4); buffdata += 4;
627     /* generic header */
628     memcpy(buffdata, "strh", 4); buffdata += 4;
629     temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh));
630     memcpy(buffdata, &temp32, 4); buffdata += 4;
631     /* the actual header */
632     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.type);
633     memcpy(buffdata, &temp32, 4); buffdata += 4;
634     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.fcc_handler);
635     memcpy(buffdata, &temp32, 4); buffdata += 4;
636     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.flags);
637     memcpy(buffdata, &temp32, 4); buffdata += 4;
638     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.priority);
639     memcpy(buffdata, &temp32, 4); buffdata += 4;
640     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.init_frames);
641     memcpy(buffdata, &temp32, 4); buffdata += 4;
642     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.scale);
643     memcpy(buffdata, &temp32, 4); buffdata += 4;
644     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.rate);
645     memcpy(buffdata, &temp32, 4); buffdata += 4;
646     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.start);
647     memcpy(buffdata, &temp32, 4); buffdata += 4;
648     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.length);
649     memcpy(buffdata, &temp32, 4); buffdata += 4;
650     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.bufsize);
651     memcpy(buffdata, &temp32, 4); buffdata += 4;
652     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.quality);
653     memcpy(buffdata, &temp32, 4); buffdata += 4;
654     temp32 = LE_FROM_GUINT32(avimux->vids_hdr.samplesize);
655     memcpy(buffdata, &temp32, 4); buffdata += 4;
656     /* the video header */
657     memcpy(buffdata, "strf", 4); buffdata += 4;
658     temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_vids));
659     memcpy(buffdata, &temp32, 4); buffdata += 4;
660     /* the actual header */
661     temp32 = LE_FROM_GUINT32(avimux->vids.size);
662     memcpy(buffdata, &temp32, 4); buffdata += 4;
663     temp32 = LE_FROM_GUINT32(avimux->vids.width);
664     memcpy(buffdata, &temp32, 4); buffdata += 4;
665     temp32 = LE_FROM_GUINT32(avimux->vids.height);
666     memcpy(buffdata, &temp32, 4); buffdata += 4;
667     temp16 = LE_FROM_GUINT16(avimux->vids.planes);
668     memcpy(buffdata, &temp16, 2); buffdata += 2;
669     temp16 = LE_FROM_GUINT16(avimux->vids.bit_cnt);
670     memcpy(buffdata, &temp16, 2); buffdata += 2;
671     temp32 = LE_FROM_GUINT32(avimux->vids.compression);
672     memcpy(buffdata, &temp32, 4); buffdata += 4;
673     temp32 = LE_FROM_GUINT32(avimux->vids.image_size);
674     memcpy(buffdata, &temp32, 4); buffdata += 4;
675     temp32 = LE_FROM_GUINT32(avimux->vids.xpels_meter);
676     memcpy(buffdata, &temp32, 4); buffdata += 4;
677     temp32 = LE_FROM_GUINT32(avimux->vids.ypels_meter);
678     memcpy(buffdata, &temp32, 4); buffdata += 4;
679     temp32 = LE_FROM_GUINT32(avimux->vids.num_colors);
680     memcpy(buffdata, &temp32, 4); buffdata += 4;
681     temp32 = LE_FROM_GUINT32(avimux->vids.imp_colors);
682     memcpy(buffdata, &temp32, 4); buffdata += 4;
683   }
684
685   if (avimux->audio_pad_connected)
686   {
687     /* audio header */
688     memcpy(buffdata, "LIST", 4); buffdata += 4;
689     temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds) + 4*5);
690     memcpy(buffdata, &temp32, 4); buffdata += 4;
691     memcpy(buffdata, "strl", 4); buffdata += 4;
692     /* generic header */
693     memcpy(buffdata, "strh", 4); buffdata += 4;
694     temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh));
695     memcpy(buffdata, &temp32, 4); buffdata += 4;
696     /* the actual header */
697     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.type);
698     memcpy(buffdata, &temp32, 4); buffdata += 4;
699     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.fcc_handler);
700     memcpy(buffdata, &temp32, 4); buffdata += 4;
701     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.flags);
702     memcpy(buffdata, &temp32, 4); buffdata += 4;
703     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.priority);
704     memcpy(buffdata, &temp32, 4); buffdata += 4;
705     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.init_frames);
706     memcpy(buffdata, &temp32, 4); buffdata += 4;
707     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.scale);
708     memcpy(buffdata, &temp32, 4); buffdata += 4;
709     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.rate);
710     memcpy(buffdata, &temp32, 4); buffdata += 4;
711     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.start);
712     memcpy(buffdata, &temp32, 4); buffdata += 4;
713     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.length);
714     memcpy(buffdata, &temp32, 4); buffdata += 4;
715     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.bufsize);
716     memcpy(buffdata, &temp32, 4); buffdata += 4;
717     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.quality);
718     memcpy(buffdata, &temp32, 4); buffdata += 4;
719     temp32 = LE_FROM_GUINT32(avimux->auds_hdr.samplesize);
720     memcpy(buffdata, &temp32, 4); buffdata += 4;
721     /* the audio header */
722     memcpy(buffdata, "strf", 4); buffdata += 4;
723     temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_auds));
724     memcpy(buffdata, &temp32, 4); buffdata += 4;
725     /* the actual header */
726     temp16 = LE_FROM_GUINT16(avimux->auds.format);
727     memcpy(buffdata, &temp16, 2); buffdata += 2;
728     temp16 = LE_FROM_GUINT16(avimux->auds.channels);
729     memcpy(buffdata, &temp16, 2); buffdata += 2;
730     temp32 = LE_FROM_GUINT32(avimux->auds.rate);
731     memcpy(buffdata, &temp32, 4); buffdata += 4;
732     temp32 = LE_FROM_GUINT32(avimux->auds.av_bps);
733     memcpy(buffdata, &temp32, 4); buffdata += 4;
734     temp16 = LE_FROM_GUINT16(avimux->auds.blockalign);
735     memcpy(buffdata, &temp16, 2); buffdata += 2;
736     temp16 = LE_FROM_GUINT16(avimux->auds.size);
737     memcpy(buffdata, &temp16, 2); buffdata += 2;
738   }
739
740   if (avimux->video_pad_connected)
741   {
742     /* odml header */
743     memcpy(buffdata, "LIST", 4); buffdata += 4;
744     temp32 = LE_FROM_GUINT32(sizeof(guint32)+4*3);
745     memcpy(buffdata, &temp32, 4); buffdata += 4;
746     memcpy(buffdata, "odml", 4); buffdata += 4;
747     memcpy(buffdata, "dmlh", 4); buffdata += 4;
748     temp32 = LE_FROM_GUINT32(sizeof(guint32));
749     memcpy(buffdata, &temp32, 4); buffdata += 4;
750     temp32 = LE_FROM_GUINT32(avimux->total_frames);
751     memcpy(buffdata, &temp32, 4); buffdata += 4;
752   }
753
754   /* avi data header */
755   memcpy(buffdata, "LIST", 4); buffdata += 4;
756   temp32 = LE_FROM_GUINT32(avimux->data_size);
757   memcpy(buffdata, &temp32, 4); buffdata += 4;
758   memcpy(buffdata, "movi", 4);
759
760   return buffer;
761 }
762
763 static GstBuffer *
764 gst_avimux_riff_get_avix_header (guint32 datax_size)
765 {
766   GstBuffer *buffer;
767   guint8 *buffdata;
768   guint32 temp32;
769
770   buffer = gst_buffer_new();
771   GST_BUFFER_SIZE(buffer) = 24;
772   buffdata = GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(buffer));
773
774   memcpy(buffdata, "LIST", 4); buffdata += 4;
775   temp32 = LE_FROM_GUINT32(datax_size+4*4);
776   memcpy(buffdata, &temp32, 4); buffdata += 4;
777   memcpy(buffdata, "AVIX", 4); buffdata += 4;
778   memcpy(buffdata, "LIST", 4); buffdata += 4;
779   temp32 = LE_FROM_GUINT32(datax_size);
780   memcpy(buffdata, &temp32, 4); buffdata += 4;
781   memcpy(buffdata, "movi", 4);
782
783   return buffer;
784 }
785
786 static GstBuffer *
787 gst_avimux_riff_get_video_header (guint32 video_frame_size)
788 {
789   GstBuffer *buffer;
790   guint32 temp32;
791
792   buffer = gst_buffer_new();
793   GST_BUFFER_DATA(buffer) = g_malloc(8);
794   GST_BUFFER_SIZE(buffer) = 8;
795   memcpy(GST_BUFFER_DATA(buffer), "00db", 4);
796   temp32 = LE_FROM_GUINT32(video_frame_size);
797   memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4);
798
799   return buffer;
800 }
801
802 static GstBuffer *
803 gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
804 {
805   GstBuffer *buffer;
806   guint32 temp32;
807
808   buffer = gst_buffer_new();
809   GST_BUFFER_DATA(buffer) = g_malloc(8);
810   GST_BUFFER_SIZE(buffer) = 8;
811   memcpy(GST_BUFFER_DATA(buffer), "01wb", 4);
812   temp32 = LE_FROM_GUINT32(audio_sample_size);
813   memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4);
814
815   return buffer;
816 }
817
818 /* some other usable functions (thankyou xawtv ;-) ) */
819
820 static void
821 gst_avimux_add_index (GstAviMux *avimux, guchar *code, guint32 flags, guint32 size)
822 {
823   if (avimux->idx_index == avimux->idx_count)
824   {
825     avimux->idx_count += 256;
826     avimux->idx = realloc(avimux->idx, avimux->idx_count*sizeof(gst_riff_index_entry));
827   }
828   memcpy(&(avimux->idx[avimux->idx_index].id), code, 4);
829   avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32(flags);
830   avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32(avimux->idx_offset);
831   avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32(size);
832   avimux->idx_index++;
833   avimux->idx_offset += size;
834 }
835
836 static void
837 gst_avimux_write_index (GstAviMux *avimux)
838 {
839   GstBuffer *buffer;
840   guint32 temp32;
841
842   buffer = gst_buffer_new();
843   GST_BUFFER_SIZE(buffer) = 8;
844   GST_BUFFER_DATA(buffer) = g_malloc(8);
845   memcpy(GST_BUFFER_DATA(buffer), "idx1", 4);
846   temp32 = LE_FROM_GUINT32(avimux->idx_index * sizeof(gst_riff_index_entry)); 
847   memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4);
848   gst_pad_push(avimux->srcpad, buffer);
849
850   buffer = gst_buffer_new();
851   GST_BUFFER_SIZE(buffer) = avimux->idx_index * sizeof(gst_riff_index_entry);
852   GST_BUFFER_DATA(buffer) = (unsigned char*) avimux->idx;
853   avimux->idx = NULL; /* will be free()'ed by gst_buffer_unref() */
854   avimux->total_data += GST_BUFFER_SIZE(buffer);
855   gst_pad_push(avimux->srcpad, buffer);
856
857   avimux->idx_size += avimux->idx_index * sizeof(gst_riff_index_entry) + 8;
858
859   /* update header */
860   avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
861 }
862
863 static void
864 gst_avimux_bigfile(GstAviMux *avimux, gboolean last)
865 {
866   GstBuffer *header;
867   GstEvent *event;
868     
869   if (avimux->is_bigfile)
870   {
871     /* sarch back */
872     event = gst_event_new_seek (GST_FORMAT_BYTES | 
873                                 GST_SEEK_METHOD_SET | 
874                                 GST_SEEK_FLAG_FLUSH, 
875                                 avimux->avix_start);
876     /* if the event succeeds */
877     if (gst_pad_send_event(GST_PAD_PEER(avimux->srcpad), event)) {
878
879       /* rewrite AVIX header */
880       header = gst_avimux_riff_get_avix_header(avimux->datax_size);
881       gst_pad_push(avimux->srcpad, header);
882
883       /* go back to current location */
884       event = gst_event_new_seek (GST_FORMAT_BYTES | 
885                                   GST_SEEK_METHOD_SET | 
886                                   GST_SEEK_FLAG_FLUSH, 
887                                   avimux->total_data);
888       gst_pad_send_event(GST_PAD_PEER(avimux->srcpad), event);
889     }
890   }
891   avimux->avix_start = avimux->total_data;
892
893   if (last)
894     return;
895
896   avimux->is_bigfile = TRUE;
897   avimux->numx_frames = 0;
898   avimux->datax_size = 0;
899
900   header = gst_avimux_riff_get_avix_header(0);
901   avimux->total_data += GST_BUFFER_SIZE(header);
902   gst_pad_push(avimux->srcpad, header);
903 }
904
905 /* enough header blabla now, let's go on to actually writing the headers */
906
907 static void
908 gst_avimux_start_file (GstAviMux *avimux)
909 {
910   GstBuffer *header;
911
912   avimux->total_data = 0;
913   avimux->total_frames = 0;
914   avimux->data_size = 4; /* ? */
915   avimux->datax_size = 0;
916   avimux->num_frames = 0;
917   avimux->numx_frames = 0;
918   avimux->audio_size = 0;
919   avimux->avix_start = 0;
920
921   avimux->idx_index = 0;
922   avimux->idx_offset = 0; /* see 10 lines below */
923   avimux->idx_size = 0;
924   avimux->idx_count = 0;
925   avimux->idx = NULL;
926
927   /* header */
928   avimux->avi_hdr.streams = avimux->video_pad_connected?1:0 + avimux->audio_pad_connected?1:0;
929   avimux->is_bigfile = FALSE;
930
931   header = gst_avimux_riff_get_avi_header(avimux);
932   avimux->idx_offset = avimux->header_size + 12;
933   avimux->total_data += GST_BUFFER_SIZE(header);
934   gst_pad_push(avimux->srcpad, header);
935
936   avimux->write_header = FALSE;
937   avimux->restart = FALSE;
938 }
939
940 static void
941 gst_avimux_stop_file (GstAviMux *avimux)
942 {
943   GstEvent *event;
944   GstBuffer *header;
945
946   /* if bigfile, rewrite header, else write indexes */
947   if (avimux->video_pad_connected)
948   {
949     if (avimux->is_bigfile)
950     {
951       gst_avimux_bigfile(avimux, TRUE);
952       avimux->idx_size = 0;
953     }
954     else
955     {
956       gst_avimux_write_index(avimux);
957     }
958   }
959
960   /* statistics/total_frames/... */
961   avimux->avi_hdr.tot_frames = avimux->num_frames;
962   if (avimux->video_pad_connected)
963     avimux->vids_hdr.length = avimux->num_frames;
964   if (avimux->audio_pad_connected)
965   {
966     if (avimux->auds_hdr.scale)
967       avimux->auds_hdr.length = avimux->audio_size/avimux->auds_hdr.scale;
968     else
969       avimux->auds_hdr.length = 0; /* urm...? FIXME! ;-) */
970   }
971
972   /* set rate and everything having to do with that */
973   avimux->avi_hdr.us_frame = avimux->vids_hdr.scale = 1000000/avimux->framerate;
974   avimux->avi_hdr.max_bps = 0;
975   if (avimux->audio_pad_connected)
976     avimux->avi_hdr.max_bps += avimux->auds.av_bps;
977   if (avimux->video_pad_connected)
978     avimux->avi_hdr.max_bps += ((avimux->vids.bit_cnt+7)/8) *
979                                 avimux->framerate *
980                                 avimux->vids.image_size;
981
982   /* seek and rewrite the header */
983   header = gst_avimux_riff_get_avi_header(avimux);
984   event = gst_event_new_seek (GST_FORMAT_BYTES | 
985                               GST_SEEK_METHOD_SET |
986                               GST_SEEK_FLAG_FLUSH, 0);
987   gst_pad_send_event(GST_PAD_PEER(avimux->srcpad), event);
988   gst_pad_push(avimux->srcpad, header);
989
990   avimux->write_header = TRUE;
991 }
992
993 static void
994 gst_avimux_restart_file (GstAviMux *avimux)
995 {
996   GstEvent *event;
997
998   gst_avimux_stop_file(avimux);
999
1000   event = gst_event_new(GST_EVENT_NEW_MEDIA);
1001   gst_pad_send_event(avimux->srcpad, event);
1002
1003   gst_avimux_start_file(avimux);
1004 }
1005
1006 /* handle events (search) */
1007 static gboolean
1008 gst_avimux_handle_event (GstPad *pad, GstEvent *event)
1009 {
1010   GstAviMux *avimux;
1011   GstEventType type;
1012
1013   avimux = GST_AVIMUX (gst_pad_get_parent (pad));
1014   
1015   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
1016
1017   switch (type) {
1018     case GST_EVENT_NEW_MEDIA:
1019       avimux->restart = TRUE;
1020       break;
1021     case GST_EVENT_EOS:
1022       /* is this allright? */
1023       if (pad == avimux->videosinkpad)
1024         avimux->video_pad_eos = TRUE;
1025       else if (pad == avimux->audiosinkpad)
1026         avimux->audio_pad_eos = TRUE;
1027       else
1028         g_warning("Unknown pad for EOS!");
1029       break;
1030     default:
1031       break;
1032   }
1033
1034   return TRUE;
1035 }
1036
1037
1038 /* fill the internal queue for each available pad */
1039 static void
1040 gst_avimux_fill_queue (GstAviMux *avimux)
1041 {
1042   GstBuffer *buffer;
1043
1044   if (!avimux->audio_buffer_queue &&
1045        GST_PAD_IS_USABLE(avimux->audiosinkpad) &&
1046       !avimux->audio_pad_eos)
1047   {
1048     while (1)
1049     {
1050       buffer = gst_pad_pull(avimux->audiosinkpad);
1051       if (GST_IS_EVENT(buffer))
1052       {
1053         gst_avimux_handle_event(avimux->audiosinkpad, GST_EVENT(buffer));
1054       }
1055       else
1056       {
1057         avimux->audio_buffer_queue = buffer;
1058         break;
1059       }
1060     }
1061   }
1062
1063   if (!avimux->video_buffer_queue &&
1064        GST_PAD_IS_USABLE(avimux->videosinkpad) &&
1065       !avimux->video_pad_eos)
1066   {
1067     while (1)
1068     {
1069       buffer = gst_pad_pull(avimux->videosinkpad);
1070       if (GST_IS_EVENT(buffer))
1071       {
1072         gst_avimux_handle_event(avimux->videosinkpad, GST_EVENT(buffer));
1073       }
1074       else
1075       {
1076         avimux->video_buffer_queue = buffer;
1077         if (avimux->framerate < 0)
1078           avimux->framerate = gst_video_frame_rate(GST_PAD_PEER(avimux->videosinkpad));
1079         break;
1080       }
1081     }
1082   }
1083 }
1084
1085
1086 /* send extra 'padding' data */
1087 static void
1088 gst_avimux_send_pad_data (GstAviMux *avimux,
1089                           gulong     num_bytes)
1090 {
1091   GstBuffer *buffer;
1092
1093   buffer = gst_buffer_new();
1094   GST_BUFFER_SIZE(buffer) = num_bytes;
1095   GST_BUFFER_DATA(buffer) = g_malloc(num_bytes);
1096   memset(GST_BUFFER_DATA(buffer), 0, num_bytes);
1097
1098   gst_pad_push(avimux->srcpad, buffer);
1099 }
1100
1101 /* do audio buffer */
1102 static void
1103 gst_avimux_do_audio_buffer (GstAviMux *avimux)
1104 {
1105   GstBuffer *data = avimux->audio_buffer_queue, *header;
1106   gulong total_size, pad_bytes;
1107
1108   /* write a audio header + index entry */
1109   header = gst_avimux_riff_get_audio_header((GST_BUFFER_SIZE(data)+3)&~3);
1110   total_size = GST_BUFFER_SIZE(header) + GST_BUFFER_SIZE(data);
1111   avimux->total_data += total_size;
1112
1113   if (avimux->is_bigfile)
1114   {
1115     avimux->datax_size += total_size;
1116   }
1117   else
1118   {
1119     avimux->data_size += total_size;
1120     avimux->audio_size += GST_BUFFER_SIZE(data);
1121     gst_avimux_add_index(avimux, "01wb", 0x0, total_size);
1122   }
1123
1124   gst_pad_push(avimux->srcpad, header);
1125   pad_bytes = ((GST_BUFFER_SIZE(data)+3)&~3) - GST_BUFFER_SIZE(data);
1126   if (pad_bytes)
1127     if (GST_BUFFER_MAXSIZE(data) >= GST_BUFFER_SIZE(data) + pad_bytes)
1128     {
1129       GST_BUFFER_SIZE(data) += pad_bytes;
1130       pad_bytes = 0;
1131     }
1132   gst_pad_push(avimux->srcpad, data);
1133   if (pad_bytes)
1134     gst_avimux_send_pad_data(avimux, pad_bytes);
1135   avimux->audio_buffer_queue = NULL;
1136 }
1137
1138
1139 /* do video buffer */
1140 static void
1141 gst_avimux_do_video_buffer (GstAviMux *avimux)
1142 {
1143   GstBuffer *data = avimux->video_buffer_queue, *header;
1144   gulong total_size, pad_bytes;
1145
1146   if (avimux->restart)
1147     gst_avimux_restart_file(avimux);
1148
1149   /* write a video header + index entry */
1150   if ((avimux->is_bigfile?avimux->datax_size:avimux->data_size)+GST_BUFFER_SIZE(data)>1024*1024*2000)
1151   {
1152     if (avimux->enable_large_avi)
1153       gst_avimux_bigfile(avimux, FALSE);
1154     else
1155       gst_avimux_restart_file(avimux);
1156   }
1157
1158   header = gst_avimux_riff_get_video_header((GST_BUFFER_SIZE(data)+3)&~3);
1159   total_size = GST_BUFFER_SIZE(header) + GST_BUFFER_SIZE(data);
1160   avimux->total_data += total_size;
1161   avimux->total_frames++;
1162
1163   if (avimux->is_bigfile)
1164   {
1165     avimux->datax_size += total_size;
1166     avimux->numx_frames++;
1167   }
1168   else
1169   {
1170     avimux->data_size += total_size;
1171     avimux->num_frames++;
1172     gst_avimux_add_index(avimux, "00db", 0x12, total_size);
1173   }
1174
1175   gst_pad_push(avimux->srcpad, header);
1176   pad_bytes = ((GST_BUFFER_SIZE(data)+3)&~3) - GST_BUFFER_SIZE(data);
1177   if (pad_bytes)
1178     if (GST_BUFFER_MAXSIZE(data) >= GST_BUFFER_SIZE(data) + pad_bytes)
1179     {
1180       GST_BUFFER_SIZE(data) += pad_bytes;
1181       pad_bytes = 0;
1182     }
1183   gst_pad_push(avimux->srcpad, data);
1184   if (pad_bytes)
1185     gst_avimux_send_pad_data(avimux, pad_bytes);
1186   avimux->video_buffer_queue = NULL;
1187 }
1188
1189
1190 /* take the oldest buffer in our internal queue and push-it */
1191 static gboolean
1192 gst_avimux_do_one_buffer (GstAviMux *avimux)
1193 {
1194   if (avimux->video_buffer_queue &&
1195       avimux->audio_buffer_queue)
1196   {
1197     if (GST_BUFFER_TIMESTAMP(avimux->video_buffer_queue) <=
1198         GST_BUFFER_TIMESTAMP(avimux->audio_buffer_queue))
1199       gst_avimux_do_video_buffer(avimux);
1200     else
1201       gst_avimux_do_audio_buffer(avimux);
1202   }
1203   else if (avimux->video_buffer_queue ||
1204            avimux->audio_buffer_queue)
1205   {
1206     if (avimux->video_buffer_queue)
1207       gst_avimux_do_video_buffer(avimux);
1208     else
1209       gst_avimux_do_audio_buffer(avimux);
1210   }
1211   else
1212     return FALSE;
1213
1214   return TRUE;
1215 }
1216
1217
1218 static void
1219 gst_avimux_loop (GstElement *element)
1220 {
1221   GstAviMux *avimux;
1222
1223   avimux = GST_AVIMUX(element);
1224
1225   /* first fill queue (some elements only set caps when
1226    * flowing data), then write header */
1227   gst_avimux_fill_queue(avimux);
1228   
1229   if (avimux->write_header)
1230     gst_avimux_start_file(avimux);
1231
1232   gst_avimux_do_one_buffer(avimux);
1233 }
1234
1235 static void
1236 gst_avimux_get_property (GObject    *object,
1237                          guint      prop_id,
1238                          GValue     *value,
1239                          GParamSpec *pspec)
1240 {
1241   GstAviMux *avimux;
1242
1243   /* it's not null if we got it, but it might not be ours */
1244   g_return_if_fail(GST_IS_AVIMUX(object));
1245   avimux = GST_AVIMUX(object);
1246
1247   switch (prop_id)
1248   {
1249     case ARG_BIGFILE:
1250       g_value_set_boolean(value, avimux->enable_large_avi);
1251       break;
1252     default:
1253       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1254       break;
1255   }
1256 }
1257
1258 static void
1259 gst_avimux_set_property (GObject      *object,
1260                          guint         prop_id,
1261                          const GValue *value,
1262                          GParamSpec   *pspec)
1263 {
1264   GstAviMux *avimux;
1265
1266   /* it's not null if we got it, but it might not be ours */
1267   g_return_if_fail(GST_IS_AVIMUX(object));
1268   avimux = GST_AVIMUX(object);
1269
1270   switch (prop_id)
1271   {
1272     case ARG_BIGFILE:
1273       avimux->enable_large_avi = g_value_get_boolean(value);
1274       break;
1275     default:
1276       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1277       break;
1278   }
1279 }
1280
1281 static GstElementStateReturn
1282 gst_avimux_change_state (GstElement *element)
1283 {
1284   GstAviMux *avimux;
1285   gint transition = GST_STATE_TRANSITION (element);
1286
1287   g_return_val_if_fail(GST_IS_AVIMUX(element), GST_STATE_FAILURE);
1288   
1289   avimux = GST_AVIMUX(element);
1290
1291   switch (transition) {
1292     case GST_STATE_READY_TO_PAUSED:
1293       break;
1294     case GST_STATE_PAUSED_TO_PLAYING:
1295       avimux->framerate = -1; /* means that we fill it in later */
1296       avimux->video_pad_eos = avimux->audio_pad_eos = FALSE;
1297       break;
1298     case GST_STATE_PLAYING_TO_PAUSED:
1299       /* this function returns TRUE while it handles buffers */
1300       while (gst_avimux_do_one_buffer(avimux));
1301       gst_avimux_stop_file(avimux);
1302       break;
1303     case GST_STATE_PAUSED_TO_READY:
1304       break;
1305   }
1306
1307   if (GST_ELEMENT_CLASS (parent_class)->change_state)
1308     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1309
1310   return GST_STATE_SUCCESS;
1311 }
1312
1313 static gboolean
1314 plugin_init (GModule *module, GstPlugin *plugin)
1315 {
1316   GstElementFactory *factory;
1317
1318   if (!gst_library_load("gstvideo"))
1319     return FALSE;
1320
1321   /* create an elementfactory for the avimux element */
1322   factory = gst_element_factory_new ("avimux", GST_TYPE_AVIMUX,
1323                                      &gst_avimux_details);
1324   g_return_val_if_fail (factory != NULL, FALSE);
1325
1326   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_factory));
1327   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (audio_sink_factory));
1328   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (video_sink_factory));
1329   
1330   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
1331
1332   return TRUE;
1333 }
1334
1335 GstPluginDesc plugin_desc = {
1336   GST_VERSION_MAJOR,
1337   GST_VERSION_MINOR,
1338   "avimux",
1339   plugin_init
1340 };