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 GstElementStateReturn gst_avimux_change_state (GstElement * element);
160 static GstElementClass *parent_class = NULL;
162 /*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */
165 gst_avimux_get_type (void)
167 static GType avimux_type = 0;
170 static const GTypeInfo avimux_info = {
171 sizeof (GstAviMuxClass),
172 gst_avimux_base_init,
174 (GClassInitFunc) gst_avimux_class_init,
179 (GInstanceInitFunc) gst_avimux_init,
181 static const GInterfaceInfo tag_setter_info = {
188 g_type_register_static (GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
189 g_type_add_interface_static (avimux_type, GST_TYPE_TAG_SETTER,
196 gst_avimux_base_init (gpointer g_class)
198 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
199 static GstElementDetails gst_avimux_details =
200 GST_ELEMENT_DETAILS ("Avi multiplexer",
202 "Muxes audio and video into an avi stream",
203 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
205 gst_element_class_add_pad_template (element_class,
206 gst_static_pad_template_get (&src_factory));
207 gst_element_class_add_pad_template (element_class,
208 gst_static_pad_template_get (&audio_sink_factory));
209 gst_element_class_add_pad_template (element_class,
210 gst_static_pad_template_get (&video_sink_factory));
212 gst_element_class_set_details (element_class, &gst_avimux_details);
216 gst_avimux_class_init (GstAviMuxClass * klass)
218 GObjectClass *gobject_class;
219 GstElementClass *gstelement_class;
221 gobject_class = (GObjectClass *) klass;
222 gstelement_class = (GstElementClass *) klass;
224 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
226 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIGFILE,
227 g_param_spec_boolean ("bigfile", "Bigfile Support",
228 "Support for openDML-2.0 (big) AVI files", 0, G_PARAM_READWRITE));
230 gstelement_class->request_new_pad = gst_avimux_request_new_pad;
231 gstelement_class->release_pad = gst_avimux_release_pad;
233 gstelement_class->change_state = gst_avimux_change_state;
235 gstelement_class->get_property = gst_avimux_get_property;
236 gstelement_class->set_property = gst_avimux_set_property;
239 static const GstEventMask *
240 gst_avimux_get_event_masks (GstPad * pad)
242 static const GstEventMask gst_avimux_sink_event_masks[] = {
247 return gst_avimux_sink_event_masks;
251 gst_avimux_init (GstAviMux * avimux)
253 GstElementClass *klass = GST_ELEMENT_GET_CLASS (avimux);
256 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
258 gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
260 GST_FLAG_SET (GST_ELEMENT (avimux), GST_ELEMENT_EVENT_AWARE);
262 avimux->audiosinkpad = NULL;
263 avimux->audio_pad_connected = FALSE;
264 avimux->videosinkpad = NULL;
265 avimux->video_pad_connected = FALSE;
267 avimux->audio_buffer_queue = NULL;
268 avimux->video_buffer_queue = NULL;
270 avimux->num_frames = 0;
272 /* audio/video/AVI header initialisation */
273 memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
274 memset (&(avimux->vids_hdr), 0, sizeof (gst_riff_strh));
275 memset (&(avimux->vids), 0, sizeof (gst_riff_strf_vids));
276 memset (&(avimux->auds_hdr), 0, sizeof (gst_riff_strh));
277 memset (&(avimux->auds), 0, sizeof (gst_riff_strf_auds));
278 avimux->vids_hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
279 avimux->vids_hdr.rate = 1000000;
280 avimux->avi_hdr.max_bps = 10000000;
281 avimux->auds_hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
282 avimux->vids_hdr.quality = 0xFFFFFFFF;
283 avimux->auds_hdr.quality = 0xFFFFFFFF;
288 avimux->write_header = TRUE;
290 avimux->enable_large_avi = TRUE;
292 gst_element_set_loop_function (GST_ELEMENT (avimux), gst_avimux_loop);
295 static GstPadLinkReturn
296 gst_avimux_vidsinkconnect (GstPad * pad, const GstCaps * vscaps)
299 GstStructure *structure;
300 const gchar *mimetype;
304 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
306 GST_DEBUG ("avimux: video sinkconnect triggered on %s",
307 gst_pad_get_name (pad));
309 structure = gst_caps_get_structure (vscaps, 0);
310 mimetype = gst_structure_get_name (structure);
313 avimux->vids.size = sizeof (gst_riff_strf_vids);
314 avimux->vids.planes = 1;
315 ret = gst_structure_get_int (structure, "width", &avimux->vids.width);
316 ret &= gst_structure_get_int (structure, "height", &avimux->vids.height);
317 ret &= gst_structure_get_double (structure, "framerate", &fps);
319 return GST_PAD_LINK_REFUSED;
322 avimux->vids_hdr.scale = avimux->vids_hdr.rate / fps;
324 if (!strcmp (mimetype, "video/x-raw-yuv")) {
327 gst_structure_get_fourcc (structure, "format", &format);
328 avimux->vids.compression = format;
330 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
331 avimux->vids.bit_cnt = 16;
333 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
334 avimux->vids.bit_cnt = 12;
338 avimux->vids.bit_cnt = 24;
339 avimux->vids.compression = 0;
342 if (!strcmp (mimetype, "video/x-huffyuv")) {
343 avimux->vids.compression = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
344 } else if (!strcmp (mimetype, "image/jpeg")) {
345 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
346 } else if (!strcmp (mimetype, "video/x-divx")) {
349 gst_structure_get_int (structure, "divxversion", &divxversion);
350 switch (divxversion) {
352 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
355 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
358 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
361 } else if (!strcmp (mimetype, "video/x-xvid")) {
362 avimux->vids.compression = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
363 } else if (!strcmp (mimetype, "video/x-3ivx")) {
364 avimux->vids.compression = GST_MAKE_FOURCC ('3', 'I', 'V', '2');
365 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
368 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
369 switch (msmpegversion) {
371 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
374 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
377 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
380 } else if (!strcmp (mimetype, "video/x-dv")) {
381 avimux->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
382 } else if (!strcmp (mimetype, "video/x-h263")) {
383 avimux->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
384 } else if (!strcmp (mimetype, "video/mpeg")) {
385 avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
388 if (!avimux->vids.compression) {
389 return GST_PAD_LINK_DELAYED;
393 avimux->vids_hdr.fcc_handler = avimux->vids.compression;
394 avimux->vids.image_size = avimux->vids.height * avimux->vids.width;
395 avimux->avi_hdr.width = avimux->vids.width;
396 avimux->avi_hdr.height = avimux->vids.height;
397 avimux->avi_hdr.us_frame = avimux->vids_hdr.scale;
398 return GST_PAD_LINK_OK;
401 static GstPadLinkReturn
402 gst_avimux_audsinkconnect (GstPad * pad, const GstCaps * vscaps)
405 GstStructure *structure;
406 const gchar *mimetype;
409 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
411 GST_DEBUG ("avimux: audio sinkconnect triggered on %s",
412 gst_pad_get_name (pad));
414 structure = gst_caps_get_structure (vscaps, 0);
415 mimetype = gst_structure_get_name (structure);
417 /* we want these for all */
418 gst_structure_get_int (structure, "channels", &i);
419 avimux->auds.channels = i;
420 gst_structure_get_int (structure, "rate", &i);
421 avimux->auds.rate = i;
423 if (!strcmp (mimetype, "audio/x-raw-int")) {
424 avimux->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
426 gst_structure_get_int (structure, "width", &i);
427 avimux->auds.blockalign = i;
428 gst_structure_get_int (structure, "depth", &i);
429 avimux->auds.size = i;
431 /* set some more info straight */
432 avimux->auds.blockalign /= 8;
433 avimux->auds.blockalign *= avimux->auds.channels;
434 avimux->auds.av_bps = avimux->auds.blockalign * avimux->auds.rate;
435 } else if (!strcmp (mimetype, "audio/mpeg") ||
436 !strcmp (mimetype, "audio/x-vorbis") ||
437 !strcmp (mimetype, "audio/x-ac3")) {
438 avimux->auds.format = 0;
440 if (!strcmp (mimetype, "audio/mpeg")) {
443 gst_structure_get_int (structure, "layer", &layer);
446 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
450 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
453 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
454 avimux->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
455 } else if (!strcmp (mimetype, "audio/x-ac3")) {
456 avimux->auds.format = GST_RIFF_WAVE_FORMAT_A52;
459 avimux->auds.blockalign = 1;
460 avimux->auds.av_bps = 0;
461 avimux->auds.size = 16;
463 if (!avimux->auds.format) {
464 return GST_PAD_LINK_REFUSED;
468 avimux->auds_hdr.rate = avimux->auds.blockalign * avimux->auds.rate;
469 avimux->auds_hdr.samplesize = avimux->auds.blockalign;
470 avimux->auds_hdr.scale = 1;
471 return GST_PAD_LINK_OK;
475 gst_avimux_pad_link (GstPad * pad, GstPad * peer, gpointer data)
477 GstAviMux *avimux = GST_AVIMUX (data);
478 const gchar *padname = gst_pad_get_name (pad);
480 if (pad == avimux->audiosinkpad) {
481 avimux->audio_pad_connected = TRUE;
482 } else if (pad == avimux->videosinkpad) {
483 avimux->video_pad_connected = TRUE;
485 g_warning ("Unknown padname '%s'", padname);
489 GST_DEBUG ("pad '%s' connected", padname);
493 gst_avimux_pad_unlink (GstPad * pad, GstPad * peer, gpointer data)
495 GstAviMux *avimux = GST_AVIMUX (data);
496 const gchar *padname = gst_pad_get_name (pad);
498 if (pad == avimux->audiosinkpad) {
499 avimux->audio_pad_connected = FALSE;
500 } else if (pad == avimux->videosinkpad) {
501 avimux->video_pad_connected = FALSE;
503 g_warning ("Unknown padname '%s'", padname);
507 GST_DEBUG ("pad '%s' unlinked", padname);
511 gst_avimux_request_new_pad (GstElement * element,
512 GstPadTemplate * templ, const gchar * req_name)
516 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
518 g_return_val_if_fail (templ != NULL, NULL);
520 if (templ->direction != GST_PAD_SINK) {
521 g_warning ("avimux: request pad that is not a SINK pad\n");
525 g_return_val_if_fail (GST_IS_AVIMUX (element), NULL);
527 avimux = GST_AVIMUX (element);
529 if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
530 g_return_val_if_fail (avimux->audiosinkpad == NULL, NULL);
531 newpad = gst_pad_new_from_template (templ, "audio_00");
532 gst_pad_set_link_function (newpad, gst_avimux_audsinkconnect);
533 avimux->audiosinkpad = newpad;
534 } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
535 g_return_val_if_fail (avimux->videosinkpad == NULL, NULL);
536 newpad = gst_pad_new_from_template (templ, "video_00");
537 gst_pad_set_link_function (newpad, gst_avimux_vidsinkconnect);
538 avimux->videosinkpad = newpad;
540 g_warning ("avimux: this is not our template!\n");
544 g_signal_connect (newpad, "linked",
545 G_CALLBACK (gst_avimux_pad_link), (gpointer) avimux);
546 g_signal_connect (newpad, "unlinked",
547 G_CALLBACK (gst_avimux_pad_unlink), (gpointer) avimux);
548 gst_element_add_pad (element, newpad);
549 gst_pad_set_event_mask_function (newpad, gst_avimux_get_event_masks);
555 gst_avimux_release_pad (GstElement * element, GstPad * pad)
557 GstAviMux *avimux = GST_AVIMUX (element);
559 if (pad == avimux->videosinkpad) {
560 avimux->videosinkpad = NULL;
561 } else if (pad == avimux->audiosinkpad) {
562 avimux->audiosinkpad = NULL;
564 g_warning ("Unknown pad %s", gst_pad_get_name (pad));
568 GST_DEBUG ("Removed pad '%s'", gst_pad_get_name (pad));
569 gst_element_remove_pad (element, pad);
572 /* maybe some of these functions should be moved to riff.h? */
574 /* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */
577 gst_avimux_write_tag (const GstTagList * list, const gchar * tag, gpointer data)
585 GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
586 GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
587 GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
588 GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
589 GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
590 GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
591 GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
595 GstBuffer *buf = data;
596 guint8 *buffdata = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf);
599 for (n = 0; rifftags[n].fcc != 0; n++) {
600 if (!strcmp (rifftags[n].tag, tag) &&
601 gst_tag_list_get_string (list, tag, &str)) {
606 if (GST_BUFFER_MAXSIZE (buf) >= GST_BUFFER_SIZE (buf) + 8 + plen) {
607 GST_WRITE_UINT32_LE (buffdata, rifftags[n].fcc);
608 GST_WRITE_UINT32_LE (buffdata + 4, len + 1);
609 memcpy (buffdata + 8, str, len);
610 buffdata[8 + len] = 0;
611 GST_BUFFER_SIZE (buf) += 8 + plen;
619 gst_avimux_riff_get_avi_header (GstAviMux * avimux)
622 const GstTagList *iface_tags;
627 /* first, let's see what actually needs to be in the buffer */
628 size += 32 + sizeof (gst_riff_avih); /* avi header */
629 if (avimux->video_pad_connected) { /* we have video */
630 size += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_vids); /* vid hdr */
631 size += 24; /* odml header */
633 if (avimux->audio_pad_connected) { /* we have audio */
634 size += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds); /* aud hdr */
636 /* this is the "riff size" */
637 avimux->header_size = size;
638 size += 12; /* avi data header */
641 iface_tags = gst_tag_setter_get_list (GST_TAG_SETTER (avimux));
642 if (iface_tags || avimux->tags) {
644 if (iface_tags && avimux->tags) {
645 tags = gst_tag_list_merge (iface_tags, avimux->tags,
646 GST_TAG_MERGE_APPEND);
647 } else if (iface_tags) {
648 tags = gst_tag_list_copy (iface_tags);
650 tags = gst_tag_list_copy (avimux->tags);
656 /* allocate the buffer */
657 buffer = gst_buffer_new_and_alloc (size);
658 buffdata = GST_BUFFER_DATA (buffer);
659 GST_BUFFER_SIZE (buffer) = 0;
661 /* avi header metadata */
662 memcpy (buffdata + 0, "RIFF", 4);
663 GST_WRITE_UINT32_LE (buffdata + 4,
664 avimux->header_size + avimux->idx_size + avimux->data_size);
665 memcpy (buffdata + 8, "AVI ", 4);
666 memcpy (buffdata + 12, "LIST", 4);
667 GST_WRITE_UINT32_LE (buffdata + 16, avimux->header_size - 4 * 5);
668 memcpy (buffdata + 20, "hdrl", 4);
669 memcpy (buffdata + 24, "avih", 4);
670 GST_WRITE_UINT32_LE (buffdata + 28, sizeof (gst_riff_avih));
672 GST_BUFFER_SIZE (buffer) += 32;
674 /* the AVI header itself */
675 GST_WRITE_UINT32_LE (buffdata + 0, avimux->avi_hdr.us_frame);
676 GST_WRITE_UINT32_LE (buffdata + 4, avimux->avi_hdr.max_bps);
677 GST_WRITE_UINT32_LE (buffdata + 8, avimux->avi_hdr.pad_gran);
678 GST_WRITE_UINT32_LE (buffdata + 12, avimux->avi_hdr.flags);
679 GST_WRITE_UINT32_LE (buffdata + 16, avimux->avi_hdr.tot_frames);
680 GST_WRITE_UINT32_LE (buffdata + 20, avimux->avi_hdr.init_frames);
681 GST_WRITE_UINT32_LE (buffdata + 24, avimux->avi_hdr.streams);
682 GST_WRITE_UINT32_LE (buffdata + 28, avimux->avi_hdr.bufsize);
683 GST_WRITE_UINT32_LE (buffdata + 32, avimux->avi_hdr.width);
684 GST_WRITE_UINT32_LE (buffdata + 36, avimux->avi_hdr.height);
685 GST_WRITE_UINT32_LE (buffdata + 40, avimux->avi_hdr.scale);
686 GST_WRITE_UINT32_LE (buffdata + 44, avimux->avi_hdr.rate);
687 GST_WRITE_UINT32_LE (buffdata + 48, avimux->avi_hdr.start);
688 GST_WRITE_UINT32_LE (buffdata + 52, avimux->avi_hdr.length);
690 GST_BUFFER_SIZE (buffer) += 56;
692 if (avimux->video_pad_connected) {
693 /* video header metadata */
694 memcpy (buffdata + 0, "LIST", 4);
695 GST_WRITE_UINT32_LE (buffdata + 4,
696 sizeof (gst_riff_strh) + sizeof (gst_riff_strf_vids) + 4 * 5);
697 memcpy (buffdata + 8, "strl", 4);
699 memcpy (buffdata + 12, "strh", 4);
700 GST_WRITE_UINT32_LE (buffdata + 16, sizeof (gst_riff_strh));
701 /* the actual header */
702 GST_WRITE_UINT32_LE (buffdata + 20, avimux->vids_hdr.type);
703 GST_WRITE_UINT32_LE (buffdata + 24, avimux->vids_hdr.fcc_handler);
704 GST_WRITE_UINT32_LE (buffdata + 28, avimux->vids_hdr.flags);
705 GST_WRITE_UINT32_LE (buffdata + 32, avimux->vids_hdr.priority);
706 GST_WRITE_UINT32_LE (buffdata + 36, avimux->vids_hdr.init_frames);
707 GST_WRITE_UINT32_LE (buffdata + 40, avimux->vids_hdr.scale);
708 GST_WRITE_UINT32_LE (buffdata + 44, avimux->vids_hdr.rate);
709 GST_WRITE_UINT32_LE (buffdata + 48, avimux->vids_hdr.start);
710 GST_WRITE_UINT32_LE (buffdata + 52, avimux->vids_hdr.length);
711 GST_WRITE_UINT32_LE (buffdata + 56, avimux->vids_hdr.bufsize);
712 GST_WRITE_UINT32_LE (buffdata + 60, avimux->vids_hdr.quality);
713 GST_WRITE_UINT32_LE (buffdata + 64, avimux->vids_hdr.samplesize);
714 /* the video header */
715 memcpy (buffdata + 68, "strf", 4);
716 GST_WRITE_UINT32_LE (buffdata + 72, sizeof (gst_riff_strf_vids));
717 /* the actual header */
718 GST_WRITE_UINT32_LE (buffdata + 76, avimux->vids.size);
719 GST_WRITE_UINT32_LE (buffdata + 80, avimux->vids.width);
720 GST_WRITE_UINT32_LE (buffdata + 84, avimux->vids.height);
721 GST_WRITE_UINT16_LE (buffdata + 88, avimux->vids.planes);
722 GST_WRITE_UINT16_LE (buffdata + 90, avimux->vids.bit_cnt);
723 GST_WRITE_UINT32_LE (buffdata + 92, avimux->vids.compression);
724 GST_WRITE_UINT32_LE (buffdata + 96, avimux->vids.image_size);
725 GST_WRITE_UINT32_LE (buffdata + 100, avimux->vids.xpels_meter);
726 GST_WRITE_UINT32_LE (buffdata + 104, avimux->vids.ypels_meter);
727 GST_WRITE_UINT32_LE (buffdata + 108, avimux->vids.num_colors);
728 GST_WRITE_UINT32_LE (buffdata + 112, avimux->vids.imp_colors);
730 GST_BUFFER_SIZE (buffer) += 116;
733 if (avimux->audio_pad_connected) {
735 memcpy (buffdata + 0, "LIST", 4);
736 GST_WRITE_UINT32_LE (buffdata + 4,
737 sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds) + 4 * 5);
738 memcpy (buffdata + 8, "strl", 4);
740 memcpy (buffdata + 12, "strh", 4);
741 GST_WRITE_UINT32_LE (buffdata + 16, sizeof (gst_riff_strh));
742 /* the actual header */
743 GST_WRITE_UINT32_LE (buffdata + 20, avimux->auds_hdr.type);
744 GST_WRITE_UINT32_LE (buffdata + 24, avimux->auds_hdr.fcc_handler);
745 GST_WRITE_UINT32_LE (buffdata + 28, avimux->auds_hdr.flags);
746 GST_WRITE_UINT32_LE (buffdata + 32, avimux->auds_hdr.priority);
747 GST_WRITE_UINT32_LE (buffdata + 36, avimux->auds_hdr.init_frames);
748 GST_WRITE_UINT32_LE (buffdata + 40, avimux->auds_hdr.scale);
749 GST_WRITE_UINT32_LE (buffdata + 44, avimux->auds_hdr.rate);
750 GST_WRITE_UINT32_LE (buffdata + 48, avimux->auds_hdr.start);
751 GST_WRITE_UINT32_LE (buffdata + 52, avimux->auds_hdr.length);
752 GST_WRITE_UINT32_LE (buffdata + 56, avimux->auds_hdr.bufsize);
753 GST_WRITE_UINT32_LE (buffdata + 60, avimux->auds_hdr.quality);
754 GST_WRITE_UINT32_LE (buffdata + 64, avimux->auds_hdr.samplesize);
755 /* the audio header */
756 memcpy (buffdata + 68, "strf", 4);
757 GST_WRITE_UINT32_LE (buffdata + 72, sizeof (gst_riff_strf_auds));
758 /* the actual header */
759 GST_WRITE_UINT16_LE (buffdata + 76, avimux->auds.format);
760 GST_WRITE_UINT16_LE (buffdata + 78, avimux->auds.channels);
761 GST_WRITE_UINT32_LE (buffdata + 80, avimux->auds.rate);
762 GST_WRITE_UINT32_LE (buffdata + 84, avimux->auds.av_bps);
763 GST_WRITE_UINT16_LE (buffdata + 88, avimux->auds.blockalign);
764 GST_WRITE_UINT16_LE (buffdata + 90, avimux->auds.size);
766 GST_BUFFER_SIZE (buffer) += 92;
769 if (avimux->video_pad_connected) {
771 memcpy (buffdata + 0, "LIST", 4);
772 GST_WRITE_UINT32_LE (buffdata + 4, sizeof (guint32) + 4 * 3);
773 memcpy (buffdata + 8, "odml", 4);
774 memcpy (buffdata + 12, "dmlh", 4);
775 GST_WRITE_UINT32_LE (buffdata + 16, sizeof (guint32));
776 GST_WRITE_UINT32_LE (buffdata + 20, avimux->total_frames);
778 GST_BUFFER_SIZE (buffer) += 24;
786 memcpy (buffdata + 0, "LIST", 4);
787 ptr = buffdata + 4; /* fill in later */
788 startsize = GST_BUFFER_SIZE (buffer) + 4;
789 memcpy (buffdata + 8, "INFO", 4);
791 GST_BUFFER_SIZE (buffer) += 12;
793 /* 12 bytes is needed for data header */
794 GST_BUFFER_MAXSIZE (buffer) -= 12;
795 gst_tag_list_foreach (tags, gst_avimux_write_tag, buffer);
796 gst_tag_list_free (tags);
797 GST_BUFFER_MAXSIZE (buffer) += 12;
798 buffdata = GST_BUFFER_DATA (buffer) + GST_BUFFER_SIZE (buffer);
800 /* update list size */
801 GST_WRITE_UINT32_LE (ptr, GST_BUFFER_SIZE (buffer) - startsize - 4);
804 /* avi data header */
805 memcpy (buffdata + 0, "LIST", 4);
806 GST_WRITE_UINT32_LE (buffdata + 4, avimux->data_size);
807 memcpy (buffdata + 8, "movi", 4);
809 GST_BUFFER_SIZE (buffer) += 12;
815 gst_avimux_riff_get_avix_header (guint32 datax_size)
820 buffer = gst_buffer_new_and_alloc (24);
821 buffdata = GST_BUFFER_DATA (buffer);
823 memcpy (buffdata + 0, "LIST", 4);
824 GST_WRITE_UINT32_LE (buffdata + 4, datax_size + 4 * 4);
825 memcpy (buffdata + 8, "AVIX", 4);
826 memcpy (buffdata + 12, "LIST", 4);
827 GST_WRITE_UINT32_LE (buffdata + 16, datax_size);
828 memcpy (buffdata + 20, "movi", 4);
834 gst_avimux_riff_get_video_header (guint32 video_frame_size)
839 buffer = gst_buffer_new_and_alloc (8);
840 buffdata = GST_BUFFER_DATA (buffer);
841 memcpy (buffdata + 0, "00db", 4);
842 GST_WRITE_UINT32_LE (buffdata + 4, video_frame_size);
848 gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
853 buffer = gst_buffer_new_and_alloc (8);
854 buffdata = GST_BUFFER_DATA (buffer);
855 memcpy (buffdata + 0, "01wb", 4);
856 GST_WRITE_UINT32_LE (buffdata + 4, audio_sample_size);
861 /* some other usable functions (thankyou xawtv ;-) ) */
864 gst_avimux_add_index (GstAviMux * avimux, guchar * code, guint32 flags,
867 if (avimux->idx_index == avimux->idx_count) {
868 avimux->idx_count += 256;
870 realloc (avimux->idx,
871 avimux->idx_count * sizeof (gst_riff_index_entry));
873 memcpy (&(avimux->idx[avimux->idx_index].id), code, 4);
874 avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32 (flags);
875 avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32 (avimux->idx_offset);
876 avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32 (size);
881 gst_avimux_write_index (GstAviMux * avimux)
886 buffer = gst_buffer_new_and_alloc (8);
887 buffdata = GST_BUFFER_DATA (buffer);
888 memcpy (buffdata + 0, "idx1", 4);
889 GST_WRITE_UINT32_LE (buffdata + 4,
890 avimux->idx_index * sizeof (gst_riff_index_entry));
891 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
893 buffer = gst_buffer_new ();
894 GST_BUFFER_SIZE (buffer) = avimux->idx_index * sizeof (gst_riff_index_entry);
895 GST_BUFFER_DATA (buffer) = (guint8 *) avimux->idx;
896 avimux->idx = NULL; /* will be free()'ed by gst_buffer_unref() */
897 avimux->total_data += GST_BUFFER_SIZE (buffer) + 8;
898 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
900 avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8;
903 avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
907 gst_avimux_bigfile (GstAviMux * avimux, gboolean last)
912 if (avimux->is_bigfile) {
914 event = gst_event_new_seek (GST_FORMAT_BYTES |
915 GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->avix_start);
916 /* if the event succeeds */
917 gst_pad_push (avimux->srcpad, GST_DATA (event));
919 /* rewrite AVIX header */
920 header = gst_avimux_riff_get_avix_header (avimux->datax_size);
921 gst_pad_push (avimux->srcpad, GST_DATA (header));
923 /* go back to current location */
924 event = gst_event_new_seek (GST_FORMAT_BYTES |
925 GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->total_data);
926 gst_pad_push (avimux->srcpad, GST_DATA (event));
928 avimux->avix_start = avimux->total_data;
933 avimux->is_bigfile = TRUE;
934 avimux->numx_frames = 0;
935 avimux->datax_size = 0;
937 header = gst_avimux_riff_get_avix_header (0);
938 avimux->total_data += GST_BUFFER_SIZE (header);
939 gst_pad_push (avimux->srcpad, GST_DATA (header));
942 /* enough header blabla now, let's go on to actually writing the headers */
945 gst_avimux_start_file (GstAviMux * avimux)
949 avimux->total_data = 0;
950 avimux->total_frames = 0;
951 avimux->data_size = 4; /* ? */
952 avimux->datax_size = 0;
953 avimux->num_frames = 0;
954 avimux->numx_frames = 0;
955 avimux->audio_size = 0;
956 avimux->audio_time = 0;
957 avimux->avix_start = 0;
959 avimux->idx_index = 0;
960 avimux->idx_offset = 0; /* see 10 lines below */
961 avimux->idx_size = 0;
962 avimux->idx_count = 0;
966 avimux->avi_hdr.streams =
967 (avimux->video_pad_connected ? 1 : 0) +
968 (avimux->audio_pad_connected ? 1 : 0);
969 avimux->is_bigfile = FALSE;
971 header = gst_avimux_riff_get_avi_header (avimux);
972 avimux->total_data += GST_BUFFER_SIZE (header);
973 gst_pad_push (avimux->srcpad, GST_DATA (header));
975 avimux->idx_offset = avimux->total_data;
977 avimux->write_header = FALSE;
978 avimux->restart = FALSE;
982 gst_avimux_stop_file (GstAviMux * avimux)
987 /* if bigfile, rewrite header, else write indexes */
988 if (avimux->video_pad_connected) {
989 if (avimux->is_bigfile) {
990 gst_avimux_bigfile (avimux, TRUE);
991 avimux->idx_size = 0;
993 gst_avimux_write_index (avimux);
997 /* set rate and everything having to do with that */
998 avimux->avi_hdr.max_bps = 0;
999 if (avimux->audio_pad_connected) {
1000 /* calculate bps if needed */
1001 if (!avimux->auds.av_bps) {
1002 if (avimux->audio_time) {
1003 avimux->auds.av_bps =
1004 (GST_SECOND * avimux->audio_size) / avimux->audio_time;
1006 GST_ELEMENT_ERROR (avimux, STREAM, MUX,
1007 (_("No or invalid input audio, AVI stream will be corrupt.")),
1009 avimux->auds.av_bps = 0;
1011 avimux->auds_hdr.rate = avimux->auds.av_bps * avimux->auds_hdr.scale;
1013 avimux->avi_hdr.max_bps += avimux->auds.av_bps;
1015 if (avimux->video_pad_connected) {
1016 avimux->avi_hdr.max_bps += ((avimux->vids.bit_cnt + 7) / 8) *
1017 (1000000. / avimux->avi_hdr.us_frame) * avimux->vids.image_size;
1020 /* statistics/total_frames/... */
1021 avimux->avi_hdr.tot_frames = avimux->num_frames;
1022 if (avimux->video_pad_connected) {
1023 avimux->vids_hdr.length = avimux->num_frames;
1025 if (avimux->audio_pad_connected) {
1026 avimux->auds_hdr.length =
1027 (avimux->audio_time * avimux->auds_hdr.rate) / GST_SECOND;
1030 /* seek and rewrite the header */
1031 header = gst_avimux_riff_get_avi_header (avimux);
1032 event = gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_SET, 0);
1033 gst_pad_push (avimux->srcpad, GST_DATA (event));
1034 gst_pad_push (avimux->srcpad, GST_DATA (header));
1035 event = gst_event_new_seek (GST_FORMAT_BYTES |
1036 GST_SEEK_METHOD_SET, avimux->total_data);
1037 gst_pad_push (avimux->srcpad, GST_DATA (event));
1039 avimux->write_header = TRUE;
1043 gst_avimux_restart_file (GstAviMux * avimux)
1047 gst_avimux_stop_file (avimux);
1049 event = gst_event_new (GST_EVENT_EOS);
1050 gst_pad_push (avimux->srcpad, GST_DATA (event));
1052 gst_avimux_start_file (avimux);
1055 /* handle events (search) */
1057 gst_avimux_handle_event (GstPad * pad, GstEvent * event)
1062 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
1064 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
1068 /* is this allright? */
1069 if (pad == avimux->videosinkpad) {
1070 avimux->video_pad_eos = TRUE;
1071 } else if (pad == avimux->audiosinkpad) {
1072 avimux->audio_pad_eos = TRUE;
1074 g_warning ("Unknown pad for EOS!");
1079 gst_tag_list_insert (avimux->tags, gst_event_tag_get_list (event),
1080 GST_TAG_MERGE_PREPEND);
1082 avimux->tags = gst_tag_list_copy (gst_event_tag_get_list (event));
1089 gst_event_unref (event);
1095 /* fill the internal queue for each available pad */
1097 gst_avimux_fill_queue (GstAviMux * avimux)
1101 while (!avimux->audio_buffer_queue &&
1102 avimux->audiosinkpad &&
1103 avimux->audio_pad_connected &&
1104 GST_PAD_IS_USABLE (avimux->audiosinkpad) && !avimux->audio_pad_eos) {
1105 buffer = GST_BUFFER (gst_pad_pull (avimux->audiosinkpad));
1106 if (GST_IS_EVENT (buffer)) {
1107 gst_avimux_handle_event (avimux->audiosinkpad, GST_EVENT (buffer));
1109 avimux->audio_buffer_queue = buffer;
1114 while (!avimux->video_buffer_queue &&
1115 avimux->videosinkpad &&
1116 avimux->video_pad_connected &&
1117 GST_PAD_IS_USABLE (avimux->videosinkpad) && !avimux->video_pad_eos) {
1118 buffer = GST_BUFFER (gst_pad_pull (avimux->videosinkpad));
1119 if (GST_IS_EVENT (buffer)) {
1120 gst_avimux_handle_event (avimux->videosinkpad, GST_EVENT (buffer));
1122 avimux->video_buffer_queue = buffer;
1129 /* send extra 'padding' data */
1131 gst_avimux_send_pad_data (GstAviMux * avimux, gulong num_bytes)
1135 buffer = gst_buffer_new ();
1136 GST_BUFFER_SIZE (buffer) = num_bytes;
1137 GST_BUFFER_DATA (buffer) = g_malloc (num_bytes);
1138 memset (GST_BUFFER_DATA (buffer), 0, num_bytes);
1140 gst_pad_push (avimux->srcpad, GST_DATA (buffer));
1143 /* do audio buffer */
1145 gst_avimux_do_audio_buffer (GstAviMux * avimux)
1147 GstBuffer *data = avimux->audio_buffer_queue, *header;
1148 gulong total_size, pad_bytes = 0;
1150 /* write a audio header + index entry */
1151 if (GST_BUFFER_SIZE (data) & 1) {
1152 pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
1154 header = gst_avimux_riff_get_audio_header (GST_BUFFER_SIZE (data));
1155 total_size = GST_BUFFER_SIZE (header) + GST_BUFFER_SIZE (data) + pad_bytes;
1157 if (avimux->is_bigfile) {
1158 avimux->datax_size += total_size;
1160 avimux->data_size += total_size;
1161 avimux->audio_size += GST_BUFFER_SIZE (data);
1162 avimux->audio_time += GST_BUFFER_DURATION (data);
1163 gst_avimux_add_index (avimux, "01wb", 0x0, GST_BUFFER_SIZE (data));
1166 gst_pad_push (avimux->srcpad, GST_DATA (header));
1167 gst_pad_push (avimux->srcpad, GST_DATA (data));
1169 gst_avimux_send_pad_data (avimux, pad_bytes);
1171 avimux->total_data += total_size;
1172 avimux->idx_offset += total_size;
1174 avimux->audio_buffer_queue = NULL;
1178 /* do video buffer */
1180 gst_avimux_do_video_buffer (GstAviMux * avimux)
1182 GstBuffer *data = avimux->video_buffer_queue, *header;
1183 gulong total_size, pad_bytes = 0;
1185 if (avimux->restart)
1186 gst_avimux_restart_file (avimux);
1188 /* write a video header + index entry */
1189 if ((avimux->is_bigfile ? avimux->datax_size : avimux->data_size) +
1190 GST_BUFFER_SIZE (data) > 1024 * 1024 * 2000) {
1191 if (avimux->enable_large_avi)
1192 gst_avimux_bigfile (avimux, FALSE);
1194 gst_avimux_restart_file (avimux);
1197 if (GST_BUFFER_SIZE (data) & 1) {
1198 pad_bytes = 2 - (GST_BUFFER_SIZE (data) & 1);
1200 header = gst_avimux_riff_get_video_header (GST_BUFFER_SIZE (data));
1201 total_size = GST_BUFFER_SIZE (header) + GST_BUFFER_SIZE (data) + pad_bytes;
1202 avimux->total_frames++;
1204 if (avimux->is_bigfile) {
1205 avimux->datax_size += total_size;
1206 avimux->numx_frames++;
1210 if (GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_KEY_UNIT))
1212 avimux->data_size += total_size;
1213 avimux->num_frames++;
1214 gst_avimux_add_index (avimux, "00db", flags, GST_BUFFER_SIZE (data));
1217 gst_pad_push (avimux->srcpad, GST_DATA (header));
1218 gst_pad_push (avimux->srcpad, GST_DATA (data));
1220 gst_avimux_send_pad_data (avimux, pad_bytes);
1222 avimux->total_data += total_size;
1223 avimux->idx_offset += total_size;
1225 avimux->video_buffer_queue = NULL;
1229 /* take the oldest buffer in our internal queue and push-it */
1231 gst_avimux_do_one_buffer (GstAviMux * avimux)
1233 if (avimux->video_buffer_queue && avimux->audio_buffer_queue) {
1234 if (GST_BUFFER_TIMESTAMP (avimux->video_buffer_queue) <=
1235 GST_BUFFER_TIMESTAMP (avimux->audio_buffer_queue))
1236 gst_avimux_do_video_buffer (avimux);
1238 gst_avimux_do_audio_buffer (avimux);
1239 } else if (avimux->video_buffer_queue || avimux->audio_buffer_queue) {
1240 if (avimux->video_buffer_queue)
1241 gst_avimux_do_video_buffer (avimux);
1243 gst_avimux_do_audio_buffer (avimux);
1245 /* simply finish off the file and send EOS */
1246 gst_avimux_stop_file (avimux);
1247 gst_pad_push (avimux->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
1248 gst_element_set_eos (GST_ELEMENT (avimux));
1257 gst_avimux_loop (GstElement * element)
1261 avimux = GST_AVIMUX (element);
1263 /* first fill queue (some elements only set caps when
1264 * flowing data), then write header */
1265 gst_avimux_fill_queue (avimux);
1267 if (avimux->write_header)
1268 gst_avimux_start_file (avimux);
1270 gst_avimux_do_one_buffer (avimux);
1274 gst_avimux_get_property (GObject * object,
1275 guint prop_id, GValue * value, GParamSpec * pspec)
1279 g_return_if_fail (GST_IS_AVIMUX (object));
1280 avimux = GST_AVIMUX (object);
1284 g_value_set_boolean (value, avimux->enable_large_avi);
1287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293 gst_avimux_set_property (GObject * object,
1294 guint prop_id, const GValue * value, GParamSpec * pspec)
1298 g_return_if_fail (GST_IS_AVIMUX (object));
1299 avimux = GST_AVIMUX (object);
1303 avimux->enable_large_avi = g_value_get_boolean (value);
1306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1311 static GstElementStateReturn
1312 gst_avimux_change_state (GstElement * element)
1315 gint transition = GST_STATE_TRANSITION (element);
1317 g_return_val_if_fail (GST_IS_AVIMUX (element), GST_STATE_FAILURE);
1319 avimux = GST_AVIMUX (element);
1321 switch (transition) {
1322 case GST_STATE_PAUSED_TO_PLAYING:
1323 avimux->video_pad_eos = avimux->audio_pad_eos = FALSE;
1325 case GST_STATE_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);
1336 return GST_STATE_SUCCESS;