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 ], "
74 "framerate = (double) [ 0, MAX ]; "
76 "width = (int) [ 16, 4096 ], "
77 "height = (int) [ 16, 4096 ], "
78 "framerate = (double) [ 0, MAX ]; "
80 "width = (int) [ 16, 4096 ], "
81 "height = (int) [ 16, 4096 ], "
82 "framerate = (double) [ 0, MAX ], "
83 "divxversion = (int) [ 3, 5 ]; "
85 "width = (int) [ 16, 4096 ], "
86 "height = (int) [ 16, 4096 ], "
87 "framerate = (double) [ 0, MAX ]; "
89 "width = (int) [ 16, 4096 ], "
90 "height = (int) [ 16, 4096 ], "
91 "framerate = (double) [ 0, MAX ]; "
93 "width = (int) [ 16, 4096 ], "
94 "height = (int) [ 16, 4096 ], "
95 "framerate = (double) [ 0, MAX ], "
96 "msmpegversion = (int) [ 41, 43 ]; "
98 "width = (int) [ 16, 4096 ], "
99 "height = (int) [ 16, 4096 ], "
100 "framerate = (double) [ 0, MAX ], "
101 "mpegversion = (int) 1, "
102 "systemstream = (boolean) FALSE; "
104 "width = (int) [ 16, 4096 ], "
105 "height = (int) [ 16, 4096 ], "
106 "framerate = (double) [ 0, MAX ]; "
108 "width = (int) [ 16, 4096 ], "
109 "height = (int) [ 16, 4096 ], "
110 "framerate = (double) [ 0, MAX ]; "
112 "width = (int) 720, "
113 "height = (int) { 576, 480 }, "
114 "framerate = (double) [ 0, MAX ], "
115 "systemstream = (boolean) FALSE; "
117 "width = (int) [ 16, 4096 ], "
118 "height = (int) [ 16, 4096 ], " "framerate = (double) [ 0, MAX ]")
121 static GstStaticPadTemplate audio_sink_factory =
122 GST_STATIC_PAD_TEMPLATE ("audio_%d",
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 ]; "
133 "mpegversion = (int) 1, "
134 "layer = (int) [ 1, 3 ], "
135 "rate = (int) [ 1000, 96000 ], "
136 "channels = (int) [ 1, 2 ]; "
138 "rate = (int) [ 1000, 96000 ], "
139 "channels = (int) [ 1, 2 ]; "
141 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]")
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);
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);
161 static GstElementClass *parent_class = NULL;
163 /*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */
166 gst_avimux_get_type (void)
168 static GType avimux_type = 0;
171 static const GTypeInfo avimux_info = {
172 sizeof (GstAviMuxClass),
173 gst_avimux_base_init,
175 (GClassInitFunc) gst_avimux_class_init,
180 (GInstanceInitFunc) gst_avimux_init,
182 static const GInterfaceInfo tag_setter_info = {
189 g_type_register_static (GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
190 g_type_add_interface_static (avimux_type, GST_TYPE_TAG_SETTER,
197 gst_avimux_base_init (gpointer g_class)
199 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
200 static GstElementDetails gst_avimux_details =
201 GST_ELEMENT_DETAILS ("Avi multiplexer",
203 "Muxes audio and video into an avi stream",
204 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
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));
213 gst_element_class_set_details (element_class, &gst_avimux_details);
217 gst_avimux_class_init (GstAviMuxClass * klass)
219 GObjectClass *gobject_class;
220 GstElementClass *gstelement_class;
222 gobject_class = (GObjectClass *) klass;
223 gstelement_class = (GstElementClass *) klass;
225 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
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));
231 gstelement_class->request_new_pad = gst_avimux_request_new_pad;
232 gstelement_class->release_pad = gst_avimux_release_pad;
234 gstelement_class->change_state = gst_avimux_change_state;
236 gstelement_class->get_property = gst_avimux_get_property;
237 gstelement_class->set_property = gst_avimux_set_property;
240 static const GstEventMask *
241 gst_avimux_get_event_masks (GstPad * pad)
243 static const GstEventMask gst_avimux_sink_event_masks[] = {
248 return gst_avimux_sink_event_masks;
252 gst_avimux_init (GstAviMux * avimux)
254 GstElementClass *klass = GST_ELEMENT_GET_CLASS (avimux);
257 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
259 gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
261 GST_OBJECT_FLAG_SET (GST_ELEMENT (avimux), GST_ELEMENT_EVENT_AWARE);
263 avimux->audiosinkpad = NULL;
264 avimux->audio_pad_connected = FALSE;
265 avimux->videosinkpad = NULL;
266 avimux->video_pad_connected = FALSE;
268 avimux->audio_buffer_queue = NULL;
269 avimux->video_buffer_queue = NULL;
271 avimux->num_frames = 0;
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;
289 avimux->write_header = TRUE;
291 avimux->enable_large_avi = TRUE;
293 gst_element_set_loop_function (GST_ELEMENT (avimux), gst_avimux_loop);
296 static GstPadLinkReturn
297 gst_avimux_vidsinkconnect (GstPad * pad, const GstCaps * vscaps)
300 GstStructure *structure;
301 const gchar *mimetype;
305 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
307 GST_DEBUG ("avimux: video sinkconnect triggered on %s",
308 gst_pad_get_name (pad));
310 structure = gst_caps_get_structure (vscaps, 0);
311 mimetype = gst_structure_get_name (structure);
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);
320 return GST_PAD_LINK_REFUSED;
323 avimux->vids_hdr.scale = avimux->vids_hdr.rate / fps;
325 if (!strcmp (mimetype, "video/x-raw-yuv")) {
328 gst_structure_get_fourcc (structure, "format", &format);
329 avimux->vids.compression = format;
331 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
332 avimux->vids.bit_cnt = 16;
334 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
335 avimux->vids.bit_cnt = 12;
339 avimux->vids.bit_cnt = 24;
340 avimux->vids.compression = 0;
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")) {
350 gst_structure_get_int (structure, "divxversion", &divxversion);
351 switch (divxversion) {
353 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
356 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
359 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
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")) {
369 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
370 switch (msmpegversion) {
372 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
375 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
378 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
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');
389 if (!avimux->vids.compression) {
390 return GST_PAD_LINK_DELAYED;
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;
402 static GstPadLinkReturn
403 gst_avimux_audsinkconnect (GstPad * pad, const GstCaps * vscaps)
406 GstStructure *structure;
407 const gchar *mimetype;
410 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
412 GST_DEBUG ("avimux: audio sinkconnect triggered on %s",
413 gst_pad_get_name (pad));
415 structure = gst_caps_get_structure (vscaps, 0);
416 mimetype = gst_structure_get_name (structure);
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;
424 if (!strcmp (mimetype, "audio/x-raw-int")) {
425 avimux->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
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;
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;
441 if (!strcmp (mimetype, "audio/mpeg")) {
444 gst_structure_get_int (structure, "layer", &layer);
447 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
451 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
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;
460 avimux->auds.blockalign = 1;
461 avimux->auds.av_bps = 0;
462 avimux->auds.size = 16;
464 if (!avimux->auds.format) {
465 return GST_PAD_LINK_REFUSED;
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;
476 gst_avimux_pad_link (GstPad * pad, GstPad * peer, gpointer data)
478 GstAviMux *avimux = GST_AVIMUX (data);
479 const gchar *padname = gst_pad_get_name (pad);
481 if (pad == avimux->audiosinkpad) {
482 avimux->audio_pad_connected = TRUE;
483 } else if (pad == avimux->videosinkpad) {
484 avimux->video_pad_connected = TRUE;
486 g_warning ("Unknown padname '%s'", padname);
490 GST_DEBUG ("pad '%s' connected", padname);
494 gst_avimux_pad_unlink (GstPad * pad, GstPad * peer, gpointer data)
496 GstAviMux *avimux = GST_AVIMUX (data);
497 const gchar *padname = gst_pad_get_name (pad);
499 if (pad == avimux->audiosinkpad) {
500 avimux->audio_pad_connected = FALSE;
501 } else if (pad == avimux->videosinkpad) {
502 avimux->video_pad_connected = FALSE;
504 g_warning ("Unknown padname '%s'", padname);
508 GST_DEBUG ("pad '%s' unlinked", padname);
512 gst_avimux_request_new_pad (GstElement * element,
513 GstPadTemplate * templ, const gchar * req_name)
517 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
519 g_return_val_if_fail (templ != NULL, NULL);
521 if (templ->direction != GST_PAD_SINK) {
522 g_warning ("avimux: request pad that is not a SINK pad\n");
526 g_return_val_if_fail (GST_IS_AVIMUX (element), NULL);
528 avimux = GST_AVIMUX (element);
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;
541 g_warning ("avimux: this is not our template!\n");
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);
556 gst_avimux_release_pad (GstElement * element, GstPad * pad)
558 GstAviMux *avimux = GST_AVIMUX (element);
560 if (pad == avimux->videosinkpad) {
561 avimux->videosinkpad = NULL;
562 } else if (pad == avimux->audiosinkpad) {
563 avimux->audiosinkpad = NULL;
565 g_warning ("Unknown pad %s", gst_pad_get_name (pad));
569 GST_DEBUG ("Removed pad '%s'", gst_pad_get_name (pad));
570 gst_element_remove_pad (element, pad);
573 /* maybe some of these functions should be moved to riff.h? */
575 /* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */
578 gst_avimux_write_tag (const GstTagList * list, const gchar * tag, gpointer data)
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}, {
596 GstBuffer *buf = data;
597 guint8 *buffdata = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf);
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)) {
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;
620 gst_avimux_riff_get_avi_header (GstAviMux * avimux)
623 const GstTagList *iface_tags;
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 */
634 if (avimux->audio_pad_connected) { /* we have audio */
635 size += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds); /* aud hdr */
637 /* this is the "riff size" */
638 avimux->header_size = size;
639 size += 12; /* avi data header */
642 iface_tags = gst_tag_setter_get_list (GST_TAG_SETTER (avimux));
643 if (iface_tags || avimux->tags) {
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);
651 tags = gst_tag_list_copy (avimux->tags);
657 /* allocate the buffer */
658 buffer = gst_buffer_new_and_alloc (size);
659 buffdata = GST_BUFFER_DATA (buffer);
660 GST_BUFFER_SIZE (buffer) = 0;
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));
673 GST_BUFFER_SIZE (buffer) += 32;
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);
691 GST_BUFFER_SIZE (buffer) += 56;
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);
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);
731 GST_BUFFER_SIZE (buffer) += 116;
734 if (avimux->audio_pad_connected) {
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);
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);
767 GST_BUFFER_SIZE (buffer) += 92;
770 if (avimux->video_pad_connected) {
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);
779 GST_BUFFER_SIZE (buffer) += 24;
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);
792 GST_BUFFER_SIZE (buffer) += 12;
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);
801 /* update list size */
802 GST_WRITE_UINT32_LE (ptr, GST_BUFFER_SIZE (buffer) - startsize - 4);
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);
810 GST_BUFFER_SIZE (buffer) += 12;
816 gst_avimux_riff_get_avix_header (guint32 datax_size)
821 buffer = gst_buffer_new_and_alloc (24);
822 buffdata = GST_BUFFER_DATA (buffer);
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);
835 gst_avimux_riff_get_video_header (guint32 video_frame_size)
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);
849 gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
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);
862 /* some other usable functions (thankyou xawtv ;-) ) */
865 gst_avimux_add_index (GstAviMux * avimux, guchar * code, guint32 flags,
868 if (avimux->idx_index == avimux->idx_count) {
869 avimux->idx_count += 256;
871 realloc (avimux->idx,
872 avimux->idx_count * sizeof (gst_riff_index_entry));
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);
882 gst_avimux_write_index (GstAviMux * avimux)
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));
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));
901 avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8;
904 avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
908 gst_avimux_bigfile (GstAviMux * avimux, gboolean last)
913 if (avimux->is_bigfile) {
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));
920 /* rewrite AVIX header */
921 header = gst_avimux_riff_get_avix_header (avimux->datax_size);
922 gst_pad_push (avimux->srcpad, GST_DATA (header));
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));
929 avimux->avix_start = avimux->total_data;
934 avimux->is_bigfile = TRUE;
935 avimux->numx_frames = 0;
936 avimux->datax_size = 0;
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));
943 /* enough header blabla now, let's go on to actually writing the headers */
946 gst_avimux_start_file (GstAviMux * avimux)
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;
960 avimux->idx_index = 0;
961 avimux->idx_offset = 0; /* see 10 lines below */
962 avimux->idx_size = 0;
963 avimux->idx_count = 0;
967 avimux->avi_hdr.streams =
968 (avimux->video_pad_connected ? 1 : 0) +
969 (avimux->audio_pad_connected ? 1 : 0);
970 avimux->is_bigfile = FALSE;
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));
976 avimux->idx_offset = avimux->total_data;
978 avimux->write_header = FALSE;
979 avimux->restart = FALSE;
983 gst_avimux_stop_file (GstAviMux * avimux)
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;
994 gst_avimux_write_index (avimux);
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;
1007 GST_ELEMENT_ERROR (avimux, STREAM, MUX,
1008 (_("No or invalid input audio, AVI stream will be corrupt.")),
1010 avimux->auds.av_bps = 0;
1012 avimux->auds_hdr.rate = avimux->auds.av_bps * avimux->auds_hdr.scale;
1014 avimux->avi_hdr.max_bps += avimux->auds.av_bps;
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;
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;
1026 if (avimux->audio_pad_connected) {
1027 avimux->auds_hdr.length =
1028 (avimux->audio_time * avimux->auds_hdr.rate) / GST_SECOND;
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));
1040 avimux->write_header = TRUE;
1044 gst_avimux_restart_file (GstAviMux * avimux)
1048 gst_avimux_stop_file (avimux);
1050 event = gst_event_new (GST_EVENT_EOS);
1051 gst_pad_push (avimux->srcpad, GST_DATA (event));
1053 gst_avimux_start_file (avimux);
1056 /* handle events (search) */
1058 gst_avimux_handle_event (GstPad * pad, GstEvent * event)
1063 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
1065 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
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;
1075 g_warning ("Unknown pad for EOS!");
1080 gst_tag_list_insert (avimux->tags, gst_event_tag_get_list (event),
1081 GST_TAG_MERGE_PREPEND);
1083 avimux->tags = gst_tag_list_copy (gst_event_tag_get_list (event));
1090 gst_event_unref (event);
1096 /* fill the internal queue for each available pad */
1098 gst_avimux_fill_queue (GstAviMux * avimux)
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));
1110 avimux->audio_buffer_queue = buffer;
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));
1123 avimux->video_buffer_queue = buffer;
1130 /* send extra 'padding' data */
1132 gst_avimux_send_pad_data (GstAviMux * avimux, gulong num_bytes)
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);
1141 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
1144 /* do audio buffer */
1146 gst_avimux_do_audio_buffer (GstAviMux * avimux)
1148 GstBuffer *data = avimux->audio_buffer_queue, *header;
1149 gulong total_size, pad_bytes = 0;
1151 /* write a audio header + index entry */
1152 if (GST_BUFFER_SIZE (data) & 1) {
1153 pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
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;
1158 if (avimux->is_bigfile) {
1159 avimux->datax_size += total_size;
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));
1167 gst_pad_push (avimux->srcpad, GST_DATA (header));
1168 gst_pad_push (avimux->srcpad, GST_DATA (data));
1170 gst_avimux_send_pad_data (avimux, pad_bytes);
1172 avimux->total_data += total_size;
1173 avimux->idx_offset += total_size;
1175 avimux->audio_buffer_queue = NULL;
1179 /* do video buffer */
1181 gst_avimux_do_video_buffer (GstAviMux * avimux)
1183 GstBuffer *data = avimux->video_buffer_queue, *header;
1184 gulong total_size, pad_bytes = 0;
1186 if (avimux->restart)
1187 gst_avimux_restart_file (avimux);
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);
1195 gst_avimux_restart_file (avimux);
1198 if (GST_BUFFER_SIZE (data) & 1) {
1199 pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
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++;
1205 if (avimux->is_bigfile) {
1206 avimux->datax_size += total_size;
1207 avimux->numx_frames++;
1211 if (GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_KEY_UNIT))
1213 avimux->data_size += total_size;
1214 avimux->num_frames++;
1215 gst_avimux_add_index (avimux, "00db", flags, GST_BUFFER_SIZE (data));
1218 gst_pad_push (avimux->srcpad, GST_DATA (header));
1219 gst_pad_push (avimux->srcpad, GST_DATA (data));
1221 gst_avimux_send_pad_data (avimux, pad_bytes);
1223 avimux->total_data += total_size;
1224 avimux->idx_offset += total_size;
1226 avimux->video_buffer_queue = NULL;
1230 /* take the oldest buffer in our internal queue and push-it */
1232 gst_avimux_do_one_buffer (GstAviMux * avimux)
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);
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);
1244 gst_avimux_do_audio_buffer (avimux);
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));
1258 gst_avimux_loop (GstElement * element)
1262 avimux = GST_AVIMUX (element);
1264 /* first fill queue (some elements only set caps when
1265 * flowing data), then write header */
1266 gst_avimux_fill_queue (avimux);
1268 if (avimux->write_header)
1269 gst_avimux_start_file (avimux);
1271 gst_avimux_do_one_buffer (avimux);
1275 gst_avimux_get_property (GObject * object,
1276 guint prop_id, GValue * value, GParamSpec * pspec)
1280 g_return_if_fail (GST_IS_AVIMUX (object));
1281 avimux = GST_AVIMUX (object);
1285 g_value_set_boolean (value, avimux->enable_large_avi);
1288 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1294 gst_avimux_set_property (GObject * object,
1295 guint prop_id, const GValue * value, GParamSpec * pspec)
1299 g_return_if_fail (GST_IS_AVIMUX (object));
1300 avimux = GST_AVIMUX (object);
1304 avimux->enable_large_avi = g_value_get_boolean (value);
1307 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1312 static GstStateChangeReturn
1313 gst_avimux_change_state (GstElement * element, GstStateChange transition)
1317 g_return_val_if_fail (GST_IS_AVIMUX (element), GST_STATE_CHANGE_FAILURE);
1319 avimux = GST_AVIMUX (element);
1321 switch (transition) {
1322 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1323 avimux->video_pad_eos = avimux->audio_pad_eos = FALSE;
1325 case GST_STATE_CHANGE_PAUSED_TO_READY:
1327 gst_tag_list_free (avimux->tags);
1328 avimux->tags = NULL;
1333 if (GST_ELEMENT_CLASS (parent_class)->change_state)
1334 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1336 return GST_STATE_CHANGE_SUCCESS;