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