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