1 /* AVI muxer plugin for GStreamer
2 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
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.
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.
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.
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
32 #include "gst/gst-i18n-plugin.h"
36 #include <gst/video/video.h>
38 #include "gstavimux.h"
40 #ifndef LE_FROM_GUINT16
41 #define LE_FROM_GUINT16 GUINT16_FROM_LE
43 #ifndef LE_FROM_GUINT32
44 #define LE_FROM_GUINT32 GUINT32_FROM_LE
47 /* AviMux signals and args */
60 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
63 GST_STATIC_CAPS ("video/x-msvideo")
66 static GstStaticPadTemplate video_sink_factory =
67 GST_STATIC_PAD_TEMPLATE ("video_%d",
70 GST_STATIC_CAPS ("video/x-raw-yuv, "
71 "format = (fourcc) { YUY2, I420 }, "
72 "width = (int) [ 16, 4096 ], "
73 "height = (int) [ 16, 4096 ]; "
75 "width = (int) [ 16, 4096 ], "
76 "height = (int) [ 16, 4096 ]; "
78 "width = (int) [ 16, 4096 ], "
79 "height = (int) [ 16, 4096 ], "
80 "divxversion = (int) [ 3, 5 ]; "
82 "width = (int) [ 16, 4096 ], "
83 "height = (int) [ 16, 4096 ]; "
85 "width = (int) [ 16, 4096 ], "
86 "height = (int) [ 16, 4096 ]; "
88 "width = (int) [ 16, 4096 ], "
89 "height = (int) [ 16, 4096 ], "
90 "msmpegversion = (int) [ 41, 43 ]; "
92 "width = (int) [ 16, 4096 ], "
93 "height = (int) [ 16, 4096 ], "
94 "mpegversion = (int) 1, "
95 "systemstream = (boolean) FALSE; "
97 "width = (int) [ 16, 4096 ], "
98 "height = (int) [ 16, 4096 ]; "
100 "width = (int) 720, "
101 "height = (int) { 576, 480 }, "
102 "systemstream = (boolean) FALSE; "
104 "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
107 static GstStaticPadTemplate audio_sink_factory =
108 GST_STATIC_PAD_TEMPLATE ("audio_%d",
111 GST_STATIC_CAPS ("audio/x-raw-int, "
112 "endianness = (int) LITTLE_ENDIAN, "
113 "signed = (boolean) { TRUE, FALSE }, "
114 "width = (int) { 8, 16 }, "
115 "depth = (int) { 8, 16 }, "
116 "rate = (int) [ 1000, 96000 ], "
117 "channels = (int) [ 1, 2 ]; "
119 "mpegversion = (int) 1, "
120 "layer = (int) [ 1, 3 ], "
121 "rate = (int) [ 1000, 96000 ], "
122 "channels = (int) [ 1, 2 ]; "
124 "rate = (int) [ 1000, 96000 ], "
125 "channels = (int) [ 1, 2 ]; "
127 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]")
131 static void gst_avimux_base_init (gpointer g_class);
132 static void gst_avimux_class_init (GstAviMuxClass * klass);
133 static void gst_avimux_init (GstAviMux * avimux);
135 static void gst_avimux_loop (GstElement * element);
136 static gboolean gst_avimux_handle_event (GstPad * pad, GstEvent * event);
137 static GstPad *gst_avimux_request_new_pad (GstElement * element,
138 GstPadTemplate * templ, const gchar * name);
139 static void gst_avimux_set_property (GObject * object,
140 guint prop_id, const GValue * value, GParamSpec * pspec);
141 static void gst_avimux_get_property (GObject * object,
142 guint prop_id, GValue * value, GParamSpec * pspec);
143 static GstElementStateReturn gst_avimux_change_state (GstElement * element);
145 static GstElementClass *parent_class = NULL;
147 /*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */
150 gst_avimux_get_type (void)
152 static GType avimux_type = 0;
155 static const GTypeInfo avimux_info = {
156 sizeof (GstAviMuxClass),
157 gst_avimux_base_init,
159 (GClassInitFunc) gst_avimux_class_init,
164 (GInstanceInitFunc) gst_avimux_init,
168 g_type_register_static (GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
174 gst_avimux_base_init (gpointer g_class)
176 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
177 static GstElementDetails gst_avimux_details =
178 GST_ELEMENT_DETAILS ("Avi multiplexer",
180 "Muxes audio and video into an avi stream",
181 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
183 gst_element_class_add_pad_template (element_class,
184 gst_static_pad_template_get (&src_factory));
185 gst_element_class_add_pad_template (element_class,
186 gst_static_pad_template_get (&audio_sink_factory));
187 gst_element_class_add_pad_template (element_class,
188 gst_static_pad_template_get (&video_sink_factory));
190 gst_element_class_set_details (element_class, &gst_avimux_details);
194 gst_avimux_class_init (GstAviMuxClass * klass)
196 GObjectClass *gobject_class;
197 GstElementClass *gstelement_class;
199 gobject_class = (GObjectClass *) klass;
200 gstelement_class = (GstElementClass *) klass;
202 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
204 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIGFILE,
205 g_param_spec_boolean ("bigfile", "Bigfile Support",
206 "Support for openDML-2.0 (big) AVI files", 0, G_PARAM_READWRITE));
208 gstelement_class->request_new_pad = gst_avimux_request_new_pad;
210 gstelement_class->change_state = gst_avimux_change_state;
212 gstelement_class->get_property = gst_avimux_get_property;
213 gstelement_class->set_property = gst_avimux_set_property;
216 static const GstEventMask *
217 gst_avimux_get_event_masks (GstPad * pad)
219 static const GstEventMask gst_avimux_sink_event_masks[] = {
224 return gst_avimux_sink_event_masks;
228 gst_avimux_init (GstAviMux * avimux)
230 GstElementClass *klass = GST_ELEMENT_GET_CLASS (avimux);
233 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
235 gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
237 GST_FLAG_SET (GST_ELEMENT (avimux), GST_ELEMENT_EVENT_AWARE);
239 avimux->audiosinkpad = NULL;
240 avimux->audio_pad_connected = FALSE;
241 avimux->videosinkpad = NULL;
242 avimux->video_pad_connected = FALSE;
244 avimux->audio_buffer_queue = NULL;
245 avimux->video_buffer_queue = NULL;
247 avimux->num_frames = 0;
249 /* audio/video/AVI header initialisation */
250 memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
251 memset (&(avimux->vids_hdr), 0, sizeof (gst_riff_strh));
252 memset (&(avimux->vids), 0, sizeof (gst_riff_strf_vids));
253 memset (&(avimux->auds_hdr), 0, sizeof (gst_riff_strh));
254 memset (&(avimux->auds), 0, sizeof (gst_riff_strf_auds));
255 avimux->vids_hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
256 avimux->vids_hdr.rate = 1000000;
257 avimux->avi_hdr.max_bps = 10000000;
258 avimux->auds_hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
259 avimux->vids_hdr.quality = 0xFFFFFFFF;
260 avimux->auds_hdr.quality = 0xFFFFFFFF;
264 avimux->write_header = TRUE;
266 avimux->enable_large_avi = TRUE;
268 gst_element_set_loop_function (GST_ELEMENT (avimux), gst_avimux_loop);
271 static GstPadLinkReturn
272 gst_avimux_vidsinkconnect (GstPad * pad, const GstCaps * vscaps)
275 GstStructure *structure;
276 const gchar *mimetype;
280 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
282 GST_DEBUG ("avimux: video sinkconnect triggered on %s",
283 gst_pad_get_name (pad));
285 structure = gst_caps_get_structure (vscaps, 0);
286 mimetype = gst_structure_get_name (structure);
289 avimux->vids.size = sizeof (gst_riff_strf_vids);
290 avimux->vids.planes = 1;
291 ret = gst_structure_get_int (structure, "width", &avimux->vids.width);
292 ret &= gst_structure_get_int (structure, "height", &avimux->vids.height);
293 ret &= gst_structure_get_double (structure, "framerate", &fps);
295 return GST_PAD_LINK_REFUSED;
298 avimux->vids_hdr.scale = avimux->vids_hdr.rate / fps;
300 if (!strcmp (mimetype, "video/x-raw-yuv")) {
303 gst_structure_get_fourcc (structure, "format", &format);
304 avimux->vids.compression = format;
306 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
307 avimux->vids.bit_cnt = 16;
309 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
310 avimux->vids.bit_cnt = 12;
314 avimux->vids.bit_cnt = 24;
315 avimux->vids.compression = 0;
318 if (!strcmp (mimetype, "video/x-huffyuv")) {
319 avimux->vids.compression = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
320 } else if (!strcmp (mimetype, "image/jpeg")) {
321 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
322 } else if (!strcmp (mimetype, "video/x-divx")) {
325 gst_structure_get_int (structure, "divxversion", &divxversion);
326 switch (divxversion) {
328 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
331 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
334 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
337 } else if (!strcmp (mimetype, "video/x-xvid")) {
338 avimux->vids.compression = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
339 } else if (!strcmp (mimetype, "video/x-3ivx")) {
340 avimux->vids.compression = GST_MAKE_FOURCC ('3', 'I', 'V', '2');
341 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
344 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
345 switch (msmpegversion) {
347 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
350 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
353 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
356 } else if (!strcmp (mimetype, "video/x-dv")) {
357 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
358 } else if (!strcmp (mimetype, "video/x-h263")) {
359 avimux->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
360 } else if (!strcmp (mimetype, "video/mpeg")) {
361 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
364 if (!avimux->vids.compression) {
365 return GST_PAD_LINK_DELAYED;
369 avimux->vids_hdr.fcc_handler = avimux->vids.compression;
370 avimux->vids.image_size = avimux->vids.height * avimux->vids.width;
371 avimux->avi_hdr.width = avimux->vids.width;
372 avimux->avi_hdr.height = avimux->vids.height;
373 avimux->avi_hdr.us_frame = avimux->vids_hdr.scale;
374 return GST_PAD_LINK_OK;
377 static GstPadLinkReturn
378 gst_avimux_audsinkconnect (GstPad * pad, const GstCaps * vscaps)
381 GstStructure *structure;
382 const gchar *mimetype;
385 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
387 GST_DEBUG ("avimux: audio sinkconnect triggered on %s",
388 gst_pad_get_name (pad));
390 structure = gst_caps_get_structure (vscaps, 0);
391 mimetype = gst_structure_get_name (structure);
393 /* we want these for all */
394 gst_structure_get_int (structure, "channels", &i);
395 avimux->auds.channels = i;
396 gst_structure_get_int (structure, "rate", &i);
397 avimux->auds.rate = i;
399 if (!strcmp (mimetype, "audio/x-raw-int")) {
400 avimux->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
402 gst_structure_get_int (structure, "width", &i);
403 avimux->auds.blockalign = i;
404 gst_structure_get_int (structure, "depth", &i);
405 avimux->auds.size = i;
407 /* set some more info straight */
408 avimux->auds.blockalign /= 8;
409 avimux->auds.blockalign *= avimux->auds.channels;
410 avimux->auds.av_bps = avimux->auds.blockalign * avimux->auds.rate;
411 } else if (!strcmp (mimetype, "audio/mpeg") ||
412 !strcmp (mimetype, "audio/x-vorbis") ||
413 !strcmp (mimetype, "audio/x-ac3")) {
414 avimux->auds.format = 0;
416 if (!strcmp (mimetype, "audio/mpeg")) {
419 gst_structure_get_int (structure, "layer", &layer);
422 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
426 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
429 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
430 avimux->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
431 } else if (!strcmp (mimetype, "audio/x-ac3")) {
432 avimux->auds.format = GST_RIFF_WAVE_FORMAT_A52;
435 avimux->auds.blockalign = 1;
436 avimux->auds.av_bps = 0;
437 avimux->auds.size = 16;
439 if (!avimux->auds.format) {
440 return GST_PAD_LINK_REFUSED;
444 avimux->auds_hdr.rate = avimux->auds.blockalign * avimux->auds.rate;
445 avimux->auds_hdr.samplesize = avimux->auds.blockalign;
446 avimux->auds_hdr.scale = avimux->auds.blockalign;
447 return GST_PAD_LINK_OK;
451 gst_avimux_pad_link (GstPad * pad, GstPad * peer, gpointer data)
453 GstAviMux *avimux = GST_AVIMUX (data);
454 const gchar *padname = gst_pad_get_name (pad);
456 if (pad == avimux->audiosinkpad) {
457 avimux->audio_pad_connected = TRUE;
458 } else if (pad == avimux->videosinkpad) {
459 avimux->video_pad_connected = TRUE;
461 g_warning ("Unknown padname '%s'", padname);
465 GST_DEBUG ("pad '%s' connected", padname);
469 gst_avimux_pad_unlink (GstPad * pad, GstPad * peer, gpointer data)
471 GstAviMux *avimux = GST_AVIMUX (data);
472 const gchar *padname = gst_pad_get_name (pad);
474 if (pad == avimux->audiosinkpad) {
475 avimux->audio_pad_connected = FALSE;
476 avimux->audiosinkpad = NULL;
477 } else if (pad == avimux->videosinkpad) {
478 avimux->video_pad_connected = FALSE;
479 avimux->videosinkpad = NULL;
481 g_warning ("Unknown padname '%s'", padname);
485 GST_DEBUG ("pad '%s' unlinked", padname);
489 gst_avimux_request_new_pad (GstElement * element,
490 GstPadTemplate * templ, const gchar * req_name)
494 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
496 g_return_val_if_fail (templ != NULL, NULL);
498 if (templ->direction != GST_PAD_SINK) {
499 g_warning ("avimux: request pad that is not a SINK pad\n");
503 g_return_val_if_fail (GST_IS_AVIMUX (element), NULL);
505 avimux = GST_AVIMUX (element);
507 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
508 g_return_val_if_fail (avimux->audiosinkpad == NULL, NULL);
509 newpad = gst_pad_new_from_template (templ, "audio_00");
510 gst_pad_set_link_function (newpad, gst_avimux_audsinkconnect);
511 avimux->audiosinkpad = newpad;
512 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
513 g_return_val_if_fail (avimux->videosinkpad == NULL, NULL);
514 newpad = gst_pad_new_from_template (templ, "video_00");
515 gst_pad_set_link_function (newpad, gst_avimux_vidsinkconnect);
516 avimux->videosinkpad = newpad;
518 g_warning ("avimux: this is not our template!\n");
522 g_signal_connect (newpad, "linked",
523 G_CALLBACK (gst_avimux_pad_link), (gpointer) avimux);
524 g_signal_connect (newpad, "unlinked",
525 G_CALLBACK (gst_avimux_pad_unlink), (gpointer) avimux);
526 gst_element_add_pad (element, newpad);
527 gst_pad_set_event_mask_function (newpad, gst_avimux_get_event_masks);
532 /* maybe some of these functions should be moved to riff.h? */
534 /* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */
537 gst_avimux_riff_get_avi_header (GstAviMux * avimux)
544 buffer = gst_buffer_new ();
546 /* first, let's see what actually needs to be in the buffer */
547 GST_BUFFER_SIZE (buffer) = 0;
548 GST_BUFFER_SIZE (buffer) += 32 + sizeof (gst_riff_avih); /* avi header */
549 if (avimux->video_pad_connected) { /* we have video */
550 GST_BUFFER_SIZE (buffer) += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_vids); /* vid hdr */
551 GST_BUFFER_SIZE (buffer) += 24; /* odml header */
553 if (avimux->audio_pad_connected) { /* we have audio */
554 GST_BUFFER_SIZE (buffer) += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds); /* aud hdr */
556 /* this is the "riff size" */
557 avimux->header_size = GST_BUFFER_SIZE (buffer);
558 GST_BUFFER_SIZE (buffer) += 12; /* avi data header */
560 /* allocate the buffer */
561 buffdata = GST_BUFFER_DATA (buffer) = g_malloc (GST_BUFFER_SIZE (buffer));
563 /* avi header metadata */
564 memcpy (buffdata, "RIFF", 4);
567 LE_FROM_GUINT32 (avimux->header_size + avimux->idx_size +
569 memcpy (buffdata, &temp32, 4);
571 memcpy (buffdata, "AVI ", 4);
573 memcpy (buffdata, "LIST", 4);
575 temp32 = LE_FROM_GUINT32 (avimux->header_size - 4 * 5);
576 memcpy (buffdata, &temp32, 4);
578 memcpy (buffdata, "hdrl", 4);
580 memcpy (buffdata, "avih", 4);
582 temp32 = LE_FROM_GUINT32 (sizeof (gst_riff_avih));
583 memcpy (buffdata, &temp32, 4);
585 /* the AVI header itself */
586 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.us_frame);
587 memcpy (buffdata, &temp32, 4);
589 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.max_bps);
590 memcpy (buffdata, &temp32, 4);
592 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.pad_gran);
593 memcpy (buffdata, &temp32, 4);
595 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.flags);
596 memcpy (buffdata, &temp32, 4);
598 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.tot_frames);
599 memcpy (buffdata, &temp32, 4);
601 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.init_frames);
602 memcpy (buffdata, &temp32, 4);
604 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.streams);
605 memcpy (buffdata, &temp32, 4);
607 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.bufsize);
608 memcpy (buffdata, &temp32, 4);
610 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.width);
611 memcpy (buffdata, &temp32, 4);
613 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.height);
614 memcpy (buffdata, &temp32, 4);
616 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.scale);
617 memcpy (buffdata, &temp32, 4);
619 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.rate);
620 memcpy (buffdata, &temp32, 4);
622 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.start);
623 memcpy (buffdata, &temp32, 4);
625 temp32 = LE_FROM_GUINT32 (avimux->avi_hdr.length);
626 memcpy (buffdata, &temp32, 4);
629 if (avimux->video_pad_connected) {
630 /* video header metadata */
631 memcpy (buffdata, "LIST", 4);
634 LE_FROM_GUINT32 (sizeof (gst_riff_strh) + sizeof (gst_riff_strf_vids) +
636 memcpy (buffdata, &temp32, 4);
638 memcpy (buffdata, "strl", 4);
641 memcpy (buffdata, "strh", 4);
643 temp32 = LE_FROM_GUINT32 (sizeof (gst_riff_strh));
644 memcpy (buffdata, &temp32, 4);
646 /* the actual header */
647 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.type);
648 memcpy (buffdata, &temp32, 4);
650 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.fcc_handler);
651 memcpy (buffdata, &temp32, 4);
653 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.flags);
654 memcpy (buffdata, &temp32, 4);
656 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.priority);
657 memcpy (buffdata, &temp32, 4);
659 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.init_frames);
660 memcpy (buffdata, &temp32, 4);
662 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.scale);
663 memcpy (buffdata, &temp32, 4);
665 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.rate);
666 memcpy (buffdata, &temp32, 4);
668 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.start);
669 memcpy (buffdata, &temp32, 4);
671 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.length);
672 memcpy (buffdata, &temp32, 4);
674 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.bufsize);
675 memcpy (buffdata, &temp32, 4);
677 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.quality);
678 memcpy (buffdata, &temp32, 4);
680 temp32 = LE_FROM_GUINT32 (avimux->vids_hdr.samplesize);
681 memcpy (buffdata, &temp32, 4);
683 /* the video header */
684 memcpy (buffdata, "strf", 4);
686 temp32 = LE_FROM_GUINT32 (sizeof (gst_riff_strf_vids));
687 memcpy (buffdata, &temp32, 4);
689 /* the actual header */
690 temp32 = LE_FROM_GUINT32 (avimux->vids.size);
691 memcpy (buffdata, &temp32, 4);
693 temp32 = LE_FROM_GUINT32 (avimux->vids.width);
694 memcpy (buffdata, &temp32, 4);
696 temp32 = LE_FROM_GUINT32 (avimux->vids.height);
697 memcpy (buffdata, &temp32, 4);
699 temp16 = LE_FROM_GUINT16 (avimux->vids.planes);
700 memcpy (buffdata, &temp16, 2);
702 temp16 = LE_FROM_GUINT16 (avimux->vids.bit_cnt);
703 memcpy (buffdata, &temp16, 2);
705 temp32 = LE_FROM_GUINT32 (avimux->vids.compression);
706 memcpy (buffdata, &temp32, 4);
708 temp32 = LE_FROM_GUINT32 (avimux->vids.image_size);
709 memcpy (buffdata, &temp32, 4);
711 temp32 = LE_FROM_GUINT32 (avimux->vids.xpels_meter);
712 memcpy (buffdata, &temp32, 4);
714 temp32 = LE_FROM_GUINT32 (avimux->vids.ypels_meter);
715 memcpy (buffdata, &temp32, 4);
717 temp32 = LE_FROM_GUINT32 (avimux->vids.num_colors);
718 memcpy (buffdata, &temp32, 4);
720 temp32 = LE_FROM_GUINT32 (avimux->vids.imp_colors);
721 memcpy (buffdata, &temp32, 4);
725 if (avimux->audio_pad_connected) {
727 memcpy (buffdata, "LIST", 4);
730 LE_FROM_GUINT32 (sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds) +
732 memcpy (buffdata, &temp32, 4);
734 memcpy (buffdata, "strl", 4);
737 memcpy (buffdata, "strh", 4);
739 temp32 = LE_FROM_GUINT32 (sizeof (gst_riff_strh));
740 memcpy (buffdata, &temp32, 4);
742 /* the actual header */
743 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.type);
744 memcpy (buffdata, &temp32, 4);
746 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.fcc_handler);
747 memcpy (buffdata, &temp32, 4);
749 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.flags);
750 memcpy (buffdata, &temp32, 4);
752 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.priority);
753 memcpy (buffdata, &temp32, 4);
755 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.init_frames);
756 memcpy (buffdata, &temp32, 4);
758 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.scale);
759 memcpy (buffdata, &temp32, 4);
761 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.rate);
762 memcpy (buffdata, &temp32, 4);
764 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.start);
765 memcpy (buffdata, &temp32, 4);
767 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.length);
768 memcpy (buffdata, &temp32, 4);
770 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.bufsize);
771 memcpy (buffdata, &temp32, 4);
773 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.quality);
774 memcpy (buffdata, &temp32, 4);
776 temp32 = LE_FROM_GUINT32 (avimux->auds_hdr.samplesize);
777 memcpy (buffdata, &temp32, 4);
779 /* the audio header */
780 memcpy (buffdata, "strf", 4);
782 temp32 = LE_FROM_GUINT32 (sizeof (gst_riff_strf_auds));
783 memcpy (buffdata, &temp32, 4);
785 /* the actual header */
786 temp16 = LE_FROM_GUINT16 (avimux->auds.format);
787 memcpy (buffdata, &temp16, 2);
789 temp16 = LE_FROM_GUINT16 (avimux->auds.channels);
790 memcpy (buffdata, &temp16, 2);
792 temp32 = LE_FROM_GUINT32 (avimux->auds.rate);
793 memcpy (buffdata, &temp32, 4);
795 temp32 = LE_FROM_GUINT32 (avimux->auds.av_bps);
796 memcpy (buffdata, &temp32, 4);
798 temp16 = LE_FROM_GUINT16 (avimux->auds.blockalign);
799 memcpy (buffdata, &temp16, 2);
801 temp16 = LE_FROM_GUINT16 (avimux->auds.size);
802 memcpy (buffdata, &temp16, 2);
806 if (avimux->video_pad_connected) {
808 memcpy (buffdata, "LIST", 4);
810 temp32 = LE_FROM_GUINT32 (sizeof (guint32) + 4 * 3);
811 memcpy (buffdata, &temp32, 4);
813 memcpy (buffdata, "odml", 4);
815 memcpy (buffdata, "dmlh", 4);
817 temp32 = LE_FROM_GUINT32 (sizeof (guint32));
818 memcpy (buffdata, &temp32, 4);
820 temp32 = LE_FROM_GUINT32 (avimux->total_frames);
821 memcpy (buffdata, &temp32, 4);
825 /* avi data header */
826 memcpy (buffdata, "LIST", 4);
828 temp32 = LE_FROM_GUINT32 (avimux->data_size);
829 memcpy (buffdata, &temp32, 4);
831 memcpy (buffdata, "movi", 4);
837 gst_avimux_riff_get_avix_header (guint32 datax_size)
843 buffer = gst_buffer_new ();
844 GST_BUFFER_SIZE (buffer) = 24;
845 buffdata = GST_BUFFER_DATA (buffer) = g_malloc (GST_BUFFER_SIZE (buffer));
847 memcpy (buffdata, "LIST", 4);
849 temp32 = LE_FROM_GUINT32 (datax_size + 4 * 4);
850 memcpy (buffdata, &temp32, 4);
852 memcpy (buffdata, "AVIX", 4);
854 memcpy (buffdata, "LIST", 4);
856 temp32 = LE_FROM_GUINT32 (datax_size);
857 memcpy (buffdata, &temp32, 4);
859 memcpy (buffdata, "movi", 4);
865 gst_avimux_riff_get_video_header (guint32 video_frame_size)
870 buffer = gst_buffer_new ();
871 GST_BUFFER_DATA (buffer) = g_malloc (8);
872 GST_BUFFER_SIZE (buffer) = 8;
873 memcpy (GST_BUFFER_DATA (buffer), "00db", 4);
874 temp32 = LE_FROM_GUINT32 (video_frame_size);
875 memcpy (GST_BUFFER_DATA (buffer) + 4, &temp32, 4);
881 gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
886 buffer = gst_buffer_new ();
887 GST_BUFFER_DATA (buffer) = g_malloc (8);
888 GST_BUFFER_SIZE (buffer) = 8;
889 memcpy (GST_BUFFER_DATA (buffer), "01wb", 4);
890 temp32 = LE_FROM_GUINT32 (audio_sample_size);
891 memcpy (GST_BUFFER_DATA (buffer) + 4, &temp32, 4);
896 /* some other usable functions (thankyou xawtv ;-) ) */
899 gst_avimux_add_index (GstAviMux * avimux, guchar * code, guint32 flags,
902 if (avimux->idx_index == avimux->idx_count) {
903 avimux->idx_count += 256;
905 realloc (avimux->idx,
906 avimux->idx_count * sizeof (gst_riff_index_entry));
908 memcpy (&(avimux->idx[avimux->idx_index].id), code, 4);
909 avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32 (flags);
910 avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32 (avimux->idx_offset);
911 avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32 (size);
916 gst_avimux_write_index (GstAviMux * avimux)
921 buffer = gst_buffer_new ();
922 GST_BUFFER_SIZE (buffer) = 8;
923 GST_BUFFER_DATA (buffer) = g_malloc (8);
924 memcpy (GST_BUFFER_DATA (buffer), "idx1", 4);
925 temp32 = LE_FROM_GUINT32 (avimux->idx_index * sizeof (gst_riff_index_entry));
926 memcpy (GST_BUFFER_DATA (buffer) + 4, &temp32, 4);
927 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
929 buffer = gst_buffer_new ();
930 GST_BUFFER_SIZE (buffer) = avimux->idx_index * sizeof (gst_riff_index_entry);
931 GST_BUFFER_DATA (buffer) = (unsigned char *) avimux->idx;
932 avimux->idx = NULL; /* will be free()'ed by gst_buffer_unref() */
933 avimux->total_data += GST_BUFFER_SIZE (buffer);
934 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
936 avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8;
939 avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
943 gst_avimux_bigfile (GstAviMux * avimux, gboolean last)
948 if (avimux->is_bigfile) {
950 event = gst_event_new_seek (GST_FORMAT_BYTES |
951 GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->avix_start);
952 /* if the event succeeds */
953 gst_pad_push (avimux->srcpad, GST_DATA (event));
955 /* rewrite AVIX header */
956 header = gst_avimux_riff_get_avix_header (avimux->datax_size);
957 gst_pad_push (avimux->srcpad, GST_DATA (header));
959 /* go back to current location */
960 event = gst_event_new_seek (GST_FORMAT_BYTES |
961 GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->total_data);
962 gst_pad_push (avimux->srcpad, GST_DATA (event));
964 avimux->avix_start = avimux->total_data;
969 avimux->is_bigfile = TRUE;
970 avimux->numx_frames = 0;
971 avimux->datax_size = 0;
973 header = gst_avimux_riff_get_avix_header (0);
974 avimux->total_data += GST_BUFFER_SIZE (header);
975 gst_pad_push (avimux->srcpad, GST_DATA (header));
978 /* enough header blabla now, let's go on to actually writing the headers */
981 gst_avimux_start_file (GstAviMux * avimux)
985 avimux->total_data = 0;
986 avimux->total_frames = 0;
987 avimux->data_size = 4; /* ? */
988 avimux->datax_size = 0;
989 avimux->num_frames = 0;
990 avimux->numx_frames = 0;
991 avimux->audio_size = 0;
992 avimux->audio_time = 0;
993 avimux->avix_start = 0;
995 avimux->idx_index = 0;
996 avimux->idx_offset = 0; /* see 10 lines below */
997 avimux->idx_size = 0;
998 avimux->idx_count = 0;
1002 avimux->avi_hdr.streams =
1003 (avimux->video_pad_connected ? 1 : 0) +
1004 (avimux->audio_pad_connected ? 1 : 0);
1005 avimux->is_bigfile = FALSE;
1007 header = gst_avimux_riff_get_avi_header (avimux);
1008 avimux->total_data += GST_BUFFER_SIZE (header);
1009 avimux->idx_offset = avimux->total_data;
1010 gst_pad_push (avimux->srcpad, GST_DATA (header));
1012 avimux->write_header = FALSE;
1013 avimux->restart = FALSE;
1017 gst_avimux_stop_file (GstAviMux * avimux)
1022 /* if bigfile, rewrite header, else write indexes */
1023 if (avimux->video_pad_connected) {
1024 if (avimux->is_bigfile) {
1025 gst_avimux_bigfile (avimux, TRUE);
1026 avimux->idx_size = 0;
1028 gst_avimux_write_index (avimux);
1032 /* statistics/total_frames/... */
1033 avimux->avi_hdr.tot_frames = avimux->num_frames;
1034 if (avimux->video_pad_connected) {
1035 avimux->vids_hdr.length = avimux->num_frames;
1037 if (avimux->audio_pad_connected) {
1038 avimux->auds_hdr.length =
1039 (avimux->audio_time * avimux->auds.rate) / GST_SECOND;
1042 /* set rate and everything having to do with that */
1043 avimux->avi_hdr.max_bps = 0;
1044 if (avimux->audio_pad_connected) {
1045 /* calculate bps if needed */
1046 if (!avimux->auds.av_bps) {
1047 if (avimux->audio_time) {
1048 avimux->auds_hdr.rate =
1049 (GST_SECOND * avimux->audio_size) / avimux->audio_time;
1051 GST_ELEMENT_ERROR (avimux, STREAM, MUX,
1052 (_("No or invalid input audio, AVI stream will be corrupt.")),
1054 avimux->auds_hdr.rate = 0;
1056 avimux->auds.av_bps = avimux->auds_hdr.rate * avimux->auds_hdr.scale;
1058 avimux->avi_hdr.max_bps += avimux->auds.av_bps;
1060 if (avimux->video_pad_connected) {
1061 avimux->avi_hdr.max_bps += ((avimux->vids.bit_cnt + 7) / 8) *
1062 (1000000. / avimux->avi_hdr.us_frame) * avimux->vids.image_size;
1065 /* seek and rewrite the header */
1066 header = gst_avimux_riff_get_avi_header (avimux);
1067 event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, 0);
1068 gst_pad_push (avimux->srcpad, GST_DATA (event));
1069 gst_pad_push (avimux->srcpad, GST_DATA (header));
1070 event = gst_event_new_seek (GST_FORMAT_BYTES |
1071 GST_SEEK_METHOD_SET, avimux->total_data);
1072 gst_pad_push (avimux->srcpad, GST_DATA (event));
1074 avimux->write_header = TRUE;
1078 gst_avimux_restart_file (GstAviMux * avimux)
1082 gst_avimux_stop_file (avimux);
1084 event = gst_event_new (GST_EVENT_EOS);
1085 gst_pad_push (avimux->srcpad, GST_DATA (event));
1087 gst_avimux_start_file (avimux);
1090 /* handle events (search) */
1092 gst_avimux_handle_event (GstPad * pad, GstEvent * event)
1097 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
1099 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
1103 /* is this allright? */
1104 if (pad == avimux->videosinkpad) {
1105 avimux->video_pad_eos = TRUE;
1106 } else if (pad == avimux->audiosinkpad) {
1107 avimux->audio_pad_eos = TRUE;
1109 g_warning ("Unknown pad for EOS!");
1120 /* fill the internal queue for each available pad */
1122 gst_avimux_fill_queue (GstAviMux * avimux)
1126 while (!avimux->audio_buffer_queue &&
1127 avimux->audiosinkpad &&
1128 avimux->audio_pad_connected &&
1129 GST_PAD_IS_USABLE (avimux->audiosinkpad) && !avimux->audio_pad_eos) {
1130 buffer = GST_BUFFER (gst_pad_pull (avimux->audiosinkpad));
1131 if (GST_IS_EVENT (buffer)) {
1132 gst_avimux_handle_event (avimux->audiosinkpad, GST_EVENT (buffer));
1134 avimux->audio_buffer_queue = buffer;
1139 while (!avimux->video_buffer_queue &&
1140 avimux->videosinkpad &&
1141 avimux->video_pad_connected &&
1142 GST_PAD_IS_USABLE (avimux->videosinkpad) && !avimux->video_pad_eos) {
1143 buffer = GST_BUFFER (gst_pad_pull (avimux->videosinkpad));
1144 if (GST_IS_EVENT (buffer)) {
1145 gst_avimux_handle_event (avimux->videosinkpad, GST_EVENT (buffer));
1147 avimux->video_buffer_queue = buffer;
1154 /* send extra 'padding' data */
1156 gst_avimux_send_pad_data (GstAviMux * avimux, gulong num_bytes)
1160 buffer = gst_buffer_new ();
1161 GST_BUFFER_SIZE (buffer) = num_bytes;
1162 GST_BUFFER_DATA (buffer) = g_malloc (num_bytes);
1163 memset (GST_BUFFER_DATA (buffer), 0, num_bytes);
1165 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
1168 /* do audio buffer */
1170 gst_avimux_do_audio_buffer (GstAviMux * avimux)
1172 GstBuffer *data = avimux->audio_buffer_queue, *header;
1173 gulong total_size, pad_bytes = 0;
1175 /* write a audio header + index entry */
1176 if (GST_BUFFER_SIZE (data) & 1) {
1177 pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
1179 header = gst_avimux_riff_get_audio_header (GST_BUFFER_SIZE (data));
1180 total_size = GST_BUFFER_SIZE (header) + GST_BUFFER_SIZE (data) + pad_bytes;
1182 if (avimux->is_bigfile) {
1183 avimux->datax_size += total_size;
1185 avimux->data_size += total_size;
1186 avimux->audio_size += GST_BUFFER_SIZE (data);
1187 avimux->audio_time += GST_BUFFER_DURATION (data);
1188 gst_avimux_add_index (avimux, "01wb", 0x0, GST_BUFFER_SIZE (data));
1191 gst_pad_push (avimux->srcpad, GST_DATA (header));
1192 gst_pad_push (avimux->srcpad, GST_DATA (data));
1194 gst_avimux_send_pad_data (avimux, pad_bytes);
1196 avimux->total_data += total_size;
1197 avimux->idx_offset += total_size;
1199 avimux->audio_buffer_queue = NULL;
1203 /* do video buffer */
1205 gst_avimux_do_video_buffer (GstAviMux * avimux)
1207 GstBuffer *data = avimux->video_buffer_queue, *header;
1208 gulong total_size, pad_bytes = 0;
1210 if (avimux->restart)
1211 gst_avimux_restart_file (avimux);
1213 /* write a video header + index entry */
1214 if ((avimux->is_bigfile ? avimux->datax_size : avimux->data_size) +
1215 GST_BUFFER_SIZE (data) > 1024 * 1024 * 2000) {
1216 if (avimux->enable_large_avi)
1217 gst_avimux_bigfile (avimux, FALSE);
1219 gst_avimux_restart_file (avimux);
1222 if (GST_BUFFER_SIZE (data) & 1) {
1223 pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
1225 header = gst_avimux_riff_get_video_header (GST_BUFFER_SIZE (data));
1226 total_size = GST_BUFFER_SIZE (header) + GST_BUFFER_SIZE (data) + pad_bytes;
1227 avimux->total_frames++;
1229 if (avimux->is_bigfile) {
1230 avimux->datax_size += total_size;
1231 avimux->numx_frames++;
1235 if (GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_KEY_UNIT))
1237 avimux->data_size += total_size;
1238 avimux->num_frames++;
1239 gst_avimux_add_index (avimux, "00db", flags, GST_BUFFER_SIZE (data));
1242 gst_pad_push (avimux->srcpad, GST_DATA (header));
1243 gst_pad_push (avimux->srcpad, GST_DATA (data));
1245 gst_avimux_send_pad_data (avimux, pad_bytes);
1247 avimux->total_data += total_size;
1248 avimux->idx_offset += total_size;
1250 avimux->video_buffer_queue = NULL;
1254 /* take the oldest buffer in our internal queue and push-it */
1256 gst_avimux_do_one_buffer (GstAviMux * avimux)
1258 if (avimux->video_buffer_queue && avimux->audio_buffer_queue) {
1259 if (GST_BUFFER_TIMESTAMP (avimux->video_buffer_queue) <=
1260 GST_BUFFER_TIMESTAMP (avimux->audio_buffer_queue))
1261 gst_avimux_do_video_buffer (avimux);
1263 gst_avimux_do_audio_buffer (avimux);
1264 } else if (avimux->video_buffer_queue || avimux->audio_buffer_queue) {
1265 if (avimux->video_buffer_queue)
1266 gst_avimux_do_video_buffer (avimux);
1268 gst_avimux_do_audio_buffer (avimux);
1270 /* simply finish off the file and send EOS */
1271 gst_avimux_stop_file (avimux);
1272 gst_pad_push (avimux->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
1273 gst_element_set_eos (GST_ELEMENT (avimux));
1282 gst_avimux_loop (GstElement * element)
1286 avimux = GST_AVIMUX (element);
1288 /* first fill queue (some elements only set caps when
1289 * flowing data), then write header */
1290 gst_avimux_fill_queue (avimux);
1292 if (avimux->write_header)
1293 gst_avimux_start_file (avimux);
1295 gst_avimux_do_one_buffer (avimux);
1299 gst_avimux_get_property (GObject * object,
1300 guint prop_id, GValue * value, GParamSpec * pspec)
1304 /* it's not null if we got it, but it might not be ours */
1305 g_return_if_fail (GST_IS_AVIMUX (object));
1306 avimux = GST_AVIMUX (object);
1310 g_value_set_boolean (value, avimux->enable_large_avi);
1313 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1319 gst_avimux_set_property (GObject * object,
1320 guint prop_id, const GValue * value, GParamSpec * pspec)
1324 /* it's not null if we got it, but it might not be ours */
1325 g_return_if_fail (GST_IS_AVIMUX (object));
1326 avimux = GST_AVIMUX (object);
1330 avimux->enable_large_avi = g_value_get_boolean (value);
1333 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1338 static GstElementStateReturn
1339 gst_avimux_change_state (GstElement * element)
1342 gint transition = GST_STATE_TRANSITION (element);
1344 g_return_val_if_fail (GST_IS_AVIMUX (element), GST_STATE_FAILURE);
1346 avimux = GST_AVIMUX (element);
1348 switch (transition) {
1349 case GST_STATE_PAUSED_TO_PLAYING:
1350 avimux->video_pad_eos = avimux->audio_pad_eos = FALSE;
1354 if (GST_ELEMENT_CLASS (parent_class)->change_state)
1355 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1357 return GST_STATE_SUCCESS;