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-libs/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 */
58 static GstStaticPadTemplate src_factory =
59 GST_STATIC_PAD_TEMPLATE (
63 GST_STATIC_CAPS ("video/x-msvideo")
66 static GstStaticPadTemplate video_sink_factory =
67 GST_STATIC_PAD_TEMPLATE (
73 "format = (fourcc) { YUY2, I420 }, "
74 "width = (int) [ 16, 4096 ], "
75 "height = (int) [ 16, 4096 ]; "
77 "width = (int) [ 16, 4096 ], "
78 "height = (int) [ 16, 4096 ]; "
80 "width = (int) [ 16, 4096 ], "
81 "height = (int) [ 16, 4096 ], "
82 "divxversion = (int) [ 3, 5 ]; "
84 "width = (int) [ 16, 4096 ], "
85 "height = (int) [ 16, 4096 ]; "
87 "width = (int) [ 16, 4096 ], "
88 "height = (int) [ 16, 4096 ]; "
90 "width = (int) [ 16, 4096 ], "
91 "height = (int) [ 16, 4096 ], "
92 "msmpegversion = (int) [ 41, 43 ]; "
94 "width = (int) [ 16, 4096 ], "
95 "height = (int) [ 16, 4096 ], "
96 "mpegversion = (int) 1, "
97 "systemstream = (boolean) FALSE; "
99 "width = (int) [ 16, 4096 ], "
100 "height = (int) [ 16, 4096 ]; "
102 "width = (int) 720, "
103 "height = (int) { 576, 480 }, "
104 "systemstream = (boolean) FALSE; "
106 "width = (int) [ 16, 4096 ], "
107 "height = (int) [ 16, 4096 ]"
111 static GstStaticPadTemplate audio_sink_factory =
112 GST_STATIC_PAD_TEMPLATE (
118 "endianness = (int) LITTLE_ENDIAN, "
119 "signed = (boolean) { TRUE, FALSE }, "
120 "width = (int) { 8, 16 }, "
121 "depth = (int) { 8, 16 }, "
122 "rate = (int) [ 1000, 96000 ], "
123 "channels = (int) [ 1, 2 ]; "
125 "mpegversion = (int) 1, "
126 "layer = (int) [ 1, 3 ], "
127 "rate = (int) [ 1000, 96000 ], "
128 "channels = (int) [ 1, 2 ]; "
130 "rate = (int) [ 1000, 96000 ], "
131 "channels = (int) [ 1, 2 ]; "
133 "rate = (int) [ 1000, 96000 ], "
134 "channels = (int) [ 1, 2 ]"
139 static void gst_avimux_base_init (gpointer g_class);
140 static void gst_avimux_class_init (GstAviMuxClass *klass);
141 static void gst_avimux_init (GstAviMux *avimux);
143 static void gst_avimux_loop (GstElement *element);
144 static gboolean gst_avimux_handle_event (GstPad *pad,
146 static GstPad* gst_avimux_request_new_pad (GstElement *element,
147 GstPadTemplate *templ,
149 static void gst_avimux_set_property (GObject *object,
153 static void gst_avimux_get_property (GObject *object,
157 static GstElementStateReturn gst_avimux_change_state (GstElement *element);
159 static GstElementClass *parent_class = NULL;
160 /*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */
163 gst_avimux_get_type (void)
165 static GType avimux_type = 0;
168 static const GTypeInfo avimux_info = {
169 sizeof(GstAviMuxClass),
170 gst_avimux_base_init,
172 (GClassInitFunc)gst_avimux_class_init,
177 (GInstanceInitFunc)gst_avimux_init,
179 avimux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
185 gst_avimux_base_init (gpointer g_class)
187 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
188 static GstElementDetails gst_avimux_details = GST_ELEMENT_DETAILS (
191 "Muxes audio and video into an avi stream",
192 "Ronald Bultje <rbultje@ronald.bitfreak.net>"
195 gst_element_class_add_pad_template (element_class,
196 gst_static_pad_template_get (&src_factory));
197 gst_element_class_add_pad_template (element_class,
198 gst_static_pad_template_get (&audio_sink_factory));
199 gst_element_class_add_pad_template (element_class,
200 gst_static_pad_template_get (&video_sink_factory));
202 gst_element_class_set_details (element_class, &gst_avimux_details);
206 gst_avimux_class_init (GstAviMuxClass *klass)
208 GObjectClass *gobject_class;
209 GstElementClass *gstelement_class;
211 gobject_class = (GObjectClass*)klass;
212 gstelement_class = (GstElementClass*)klass;
214 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
216 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BIGFILE,
217 g_param_spec_boolean("bigfile","Bigfile Support",
218 "Support for openDML-2.0 (big) AVI files",
219 0,G_PARAM_READWRITE));
221 gstelement_class->request_new_pad = gst_avimux_request_new_pad;
223 gstelement_class->change_state = gst_avimux_change_state;
225 gstelement_class->get_property = gst_avimux_get_property;
226 gstelement_class->set_property = gst_avimux_set_property;
229 static const GstEventMask *
230 gst_avimux_get_event_masks (GstPad *pad)
232 static const GstEventMask gst_avimux_sink_event_masks[] = {
233 { GST_EVENT_EOS, 0 },
237 return gst_avimux_sink_event_masks;
241 gst_avimux_init (GstAviMux *avimux)
243 GstElementClass *klass = GST_ELEMENT_GET_CLASS (avimux);
245 avimux->srcpad = gst_pad_new_from_template (
246 gst_element_class_get_pad_template (klass, "src"), "src");
247 gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
249 GST_FLAG_SET (GST_ELEMENT(avimux), GST_ELEMENT_EVENT_AWARE);
251 avimux->audiosinkpad = NULL;
252 avimux->audio_pad_connected = FALSE;
253 avimux->videosinkpad = NULL;
254 avimux->video_pad_connected = FALSE;
256 avimux->audio_buffer_queue = NULL;
257 avimux->video_buffer_queue = NULL;
259 avimux->num_frames = 0;
261 /* audio/video/AVI header initialisation */
262 memset(&(avimux->avi_hdr),0,sizeof(gst_riff_avih));
263 memset(&(avimux->vids_hdr),0,sizeof(gst_riff_strh));
264 memset(&(avimux->vids),0,sizeof(gst_riff_strf_vids));
265 memset(&(avimux->auds_hdr),0,sizeof(gst_riff_strh));
266 memset(&(avimux->auds),0,sizeof(gst_riff_strf_auds));
267 avimux->vids_hdr.type = GST_MAKE_FOURCC('v','i','d','s');
268 avimux->vids_hdr.rate = 1000000;
269 avimux->avi_hdr.max_bps = 10000000;
270 avimux->auds_hdr.type = GST_MAKE_FOURCC('a','u','d','s');
271 avimux->vids_hdr.quality = 0xFFFFFFFF;
272 avimux->auds_hdr.quality = 0xFFFFFFFF;
276 avimux->write_header = TRUE;
278 avimux->enable_large_avi = TRUE;
280 gst_element_set_loop_function(GST_ELEMENT(avimux), gst_avimux_loop);
283 static GstPadLinkReturn
284 gst_avimux_vidsinkconnect (GstPad *pad, const GstCaps *vscaps)
287 GstStructure *structure;
288 const gchar* mimetype;
292 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
294 GST_DEBUG ("avimux: video sinkconnect triggered on %s",
295 gst_pad_get_name (pad));
297 structure = gst_caps_get_structure (vscaps, 0);
298 mimetype = gst_structure_get_name (structure);
301 avimux->vids.size = sizeof(gst_riff_strf_vids);
302 avimux->vids.planes = 1;
303 ret = gst_structure_get_int (structure, "width", &avimux->vids.width);
304 ret &= gst_structure_get_int (structure, "height", &avimux->vids.height);
305 ret &= gst_structure_get_double (structure, "framerate", &fps);
306 if (!ret) return GST_PAD_LINK_REFUSED;
309 avimux->vids_hdr.scale = avimux->vids_hdr.rate / fps;
311 if (!strcmp (mimetype, "video/x-raw-yuv")) {
314 gst_structure_get_fourcc (structure, "format", &format);
315 avimux->vids.compression = format;
318 case GST_MAKE_FOURCC('Y','U','Y','2'):
319 avimux->vids.bit_cnt = 16;
321 case GST_MAKE_FOURCC('I','4','2','0'):
322 avimux->vids.bit_cnt = 12;
326 avimux->vids.bit_cnt = 24;
327 avimux->vids.compression = 0;
330 if (!strcmp (mimetype, "video/x-huffyuv")) {
331 avimux->vids.compression = GST_MAKE_FOURCC('H','F','Y','U');
332 } else if (!strcmp (mimetype, "video/x-jpeg")) {
333 avimux->vids.compression = GST_MAKE_FOURCC('M','J','P','G');
334 } else if (!strcmp (mimetype, "video/x-divx")) {
336 gst_structure_get_int (structure, "divxversion", &divxversion);
337 switch (divxversion) {
339 avimux->vids.compression = GST_MAKE_FOURCC('D','I','V','3');
342 avimux->vids.compression = GST_MAKE_FOURCC('D','I','V','X');
345 avimux->vids.compression = GST_MAKE_FOURCC('D','X','5','0');
348 } else if (!strcmp (mimetype, "video/x-xvid")) {
349 avimux->vids.compression = GST_MAKE_FOURCC('X','V','I','D');
350 } else if (!strcmp (mimetype, "video/x-3ivx")) {
351 avimux->vids.compression = GST_MAKE_FOURCC('3','I','V','2');
352 } else if (!strcmp (mimetype, "video/x-msmpeg")) {
354 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
355 switch (msmpegversion) {
357 avimux->vids.compression = GST_MAKE_FOURCC('M','P','G','4');
360 avimux->vids.compression = GST_MAKE_FOURCC('M','P','4','2');
363 avimux->vids.compression = GST_MAKE_FOURCC('M','P','4','3');
366 } else if (!strcmp (mimetype, "video/x-dv")) {
367 avimux->vids.compression = GST_MAKE_FOURCC('D','V','S','D');
368 } else if (!strcmp (mimetype, "video/x-h263")) {
369 avimux->vids.compression = GST_MAKE_FOURCC('H','2','6','3');
370 } else if (!strcmp (mimetype, "video/mpeg")) {
371 avimux->vids.compression = GST_MAKE_FOURCC('M','P','E','G');
374 if (!avimux->vids.compression) {
375 return GST_PAD_LINK_DELAYED;
379 avimux->vids_hdr.fcc_handler = avimux->vids.compression;
380 avimux->vids.image_size = avimux->vids.height * avimux->vids.width;
381 avimux->avi_hdr.width = avimux->vids.width;
382 avimux->avi_hdr.height = avimux->vids.height;
383 avimux->avi_hdr.us_frame = avimux->vids_hdr.scale;
384 return GST_PAD_LINK_OK;
387 static GstPadLinkReturn
388 gst_avimux_audsinkconnect (GstPad *pad, const GstCaps *vscaps)
391 GstStructure *structure;
392 const gchar* mimetype;
395 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
397 GST_DEBUG ("avimux: audio sinkconnect triggered on %s",
398 gst_pad_get_name (pad));
400 structure = gst_caps_get_structure (vscaps, 0);
401 mimetype = gst_structure_get_name (structure);
403 /* we want these for all */
404 gst_structure_get_int (structure, "channels", &i);
405 avimux->auds.channels = i;
406 gst_structure_get_int (structure, "rate", &i);
407 avimux->auds.rate = i;
409 if (!strcmp (mimetype, "audio/x-raw-int")) {
410 avimux->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
412 gst_structure_get_int (structure, "width", &i);
413 avimux->auds.blockalign = i;
414 gst_structure_get_int (structure, "depth", &i);
415 avimux->auds.size = i;
417 /* set some more info straight */
418 avimux->auds.blockalign /= 8;
419 avimux->auds.blockalign *= avimux->auds.channels;
420 avimux->auds.av_bps = avimux->auds.blockalign * avimux->auds.rate;
421 } else if (!strcmp (mimetype, "audio/mpeg") ||
422 !strcmp (mimetype, "audio/x-vorbis") ||
423 !strcmp (mimetype, "audio/x-ac3")) {
424 avimux->auds.format = 0;
426 if (!strcmp (mimetype, "audio/mpeg")) {
428 gst_structure_get_int (structure, "layer", &layer);
431 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
434 avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
437 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
438 avimux->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
439 } else if (!strcmp (mimetype, "audio/x-ac3")) {
440 avimux->auds.format = GST_RIFF_WAVE_FORMAT_A52;
443 avimux->auds.blockalign = 1;
444 avimux->auds.av_bps = 0;
445 avimux->auds.size = 16;
447 if (!avimux->auds.format) {
448 return GST_PAD_LINK_REFUSED;
452 avimux->auds_hdr.rate = avimux->auds.blockalign * avimux->auds.rate;
453 avimux->auds_hdr.samplesize = avimux->auds.blockalign;
454 avimux->auds_hdr.scale = avimux->auds.blockalign;
455 return GST_PAD_LINK_OK;
459 gst_avimux_pad_link (GstPad *pad,
463 GstAviMux *avimux = GST_AVIMUX(data);
464 const gchar *padname = gst_pad_get_name (pad);
466 if (pad == avimux->audiosinkpad)
468 avimux->audio_pad_connected = TRUE;
470 else if (pad == avimux->videosinkpad)
472 avimux->video_pad_connected = TRUE;
476 g_warning("Unknown padname '%s'", padname);
480 GST_DEBUG ("pad '%s' connected", padname);
484 gst_avimux_pad_unlink (GstPad *pad,
488 GstAviMux *avimux = GST_AVIMUX(data);
489 const gchar *padname = gst_pad_get_name (pad);
491 if (pad == avimux->audiosinkpad)
493 avimux->audio_pad_connected = FALSE;
494 avimux->audiosinkpad = NULL;
496 else if (pad == avimux->videosinkpad)
498 avimux->video_pad_connected = FALSE;
499 avimux->videosinkpad = NULL;
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,
513 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;
536 else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
537 g_return_val_if_fail(avimux->videosinkpad == NULL, NULL);
538 newpad = gst_pad_new_from_template (templ, "video_00");
539 gst_pad_set_link_function (newpad, gst_avimux_vidsinkconnect);
540 avimux->videosinkpad = newpad;
543 g_warning ("avimux: this is not our template!\n");
547 g_signal_connect(newpad, "linked",
548 G_CALLBACK(gst_avimux_pad_link), (gpointer)avimux);
549 g_signal_connect(newpad, "unlinked",
550 G_CALLBACK(gst_avimux_pad_unlink), (gpointer)avimux);
551 gst_element_add_pad (element, newpad);
552 gst_pad_set_event_mask_function(newpad, gst_avimux_get_event_masks);
557 /* maybe some of these functions should be moved to riff.h? */
559 /* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */
562 gst_avimux_riff_get_avi_header (GstAviMux *avimux)
569 buffer = gst_buffer_new();
571 /* first, let's see what actually needs to be in the buffer */
572 GST_BUFFER_SIZE(buffer) = 0;
573 GST_BUFFER_SIZE(buffer) += 32 + sizeof(gst_riff_avih); /* avi header */
574 if (avimux->video_pad_connected)
575 { /* we have video */
576 GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids); /* vid hdr */
577 GST_BUFFER_SIZE(buffer) += 24; /* odml header */
579 if (avimux->audio_pad_connected)
580 { /* we have audio */
581 GST_BUFFER_SIZE(buffer) += 28 + sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds); /* aud hdr */
583 /* this is the "riff size" */
584 avimux->header_size = GST_BUFFER_SIZE(buffer);
585 GST_BUFFER_SIZE(buffer) += 12; /* avi data header */
587 /* allocate the buffer */
588 buffdata = GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(buffer));
590 /* avi header metadata */
591 memcpy(buffdata, "RIFF", 4); buffdata += 4;
592 temp32 = LE_FROM_GUINT32(avimux->header_size + avimux->idx_size + avimux->data_size);
593 memcpy(buffdata, &temp32, 4); buffdata += 4;
594 memcpy(buffdata, "AVI ", 4); buffdata += 4;
595 memcpy(buffdata, "LIST", 4); buffdata += 4;
596 temp32 = LE_FROM_GUINT32(avimux->header_size - 4*5);
597 memcpy(buffdata, &temp32, 4); buffdata += 4;
598 memcpy(buffdata, "hdrl", 4); buffdata += 4;
599 memcpy(buffdata, "avih", 4); buffdata += 4;
600 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_avih));
601 memcpy(buffdata, &temp32, 4); buffdata += 4;
602 /* the AVI header itself */
603 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.us_frame);
604 memcpy(buffdata, &temp32, 4); buffdata += 4;
605 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.max_bps);
606 memcpy(buffdata, &temp32, 4); buffdata += 4;
607 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.pad_gran);
608 memcpy(buffdata, &temp32, 4); buffdata += 4;
609 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.flags);
610 memcpy(buffdata, &temp32, 4); buffdata += 4;
611 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.tot_frames);
612 memcpy(buffdata, &temp32, 4); buffdata += 4;
613 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.init_frames);
614 memcpy(buffdata, &temp32, 4); buffdata += 4;
615 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.streams);
616 memcpy(buffdata, &temp32, 4); buffdata += 4;
617 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.bufsize);
618 memcpy(buffdata, &temp32, 4); buffdata += 4;
619 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.width);
620 memcpy(buffdata, &temp32, 4); buffdata += 4;
621 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.height);
622 memcpy(buffdata, &temp32, 4); buffdata += 4;
623 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.scale);
624 memcpy(buffdata, &temp32, 4); buffdata += 4;
625 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.rate);
626 memcpy(buffdata, &temp32, 4); buffdata += 4;
627 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.start);
628 memcpy(buffdata, &temp32, 4); buffdata += 4;
629 temp32 = LE_FROM_GUINT32(avimux->avi_hdr.length);
630 memcpy(buffdata, &temp32, 4); buffdata += 4;
632 if (avimux->video_pad_connected)
634 /* video header metadata */
635 memcpy(buffdata, "LIST", 4); buffdata += 4;
636 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh) + sizeof(gst_riff_strf_vids) + 4*5);
637 memcpy(buffdata, &temp32, 4); buffdata += 4;
638 memcpy(buffdata, "strl", 4); buffdata += 4;
640 memcpy(buffdata, "strh", 4); buffdata += 4;
641 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh));
642 memcpy(buffdata, &temp32, 4); buffdata += 4;
643 /* the actual header */
644 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.type);
645 memcpy(buffdata, &temp32, 4); buffdata += 4;
646 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.fcc_handler);
647 memcpy(buffdata, &temp32, 4); buffdata += 4;
648 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.flags);
649 memcpy(buffdata, &temp32, 4); buffdata += 4;
650 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.priority);
651 memcpy(buffdata, &temp32, 4); buffdata += 4;
652 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.init_frames);
653 memcpy(buffdata, &temp32, 4); buffdata += 4;
654 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.scale);
655 memcpy(buffdata, &temp32, 4); buffdata += 4;
656 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.rate);
657 memcpy(buffdata, &temp32, 4); buffdata += 4;
658 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.start);
659 memcpy(buffdata, &temp32, 4); buffdata += 4;
660 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.length);
661 memcpy(buffdata, &temp32, 4); buffdata += 4;
662 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.bufsize);
663 memcpy(buffdata, &temp32, 4); buffdata += 4;
664 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.quality);
665 memcpy(buffdata, &temp32, 4); buffdata += 4;
666 temp32 = LE_FROM_GUINT32(avimux->vids_hdr.samplesize);
667 memcpy(buffdata, &temp32, 4); buffdata += 4;
668 /* the video header */
669 memcpy(buffdata, "strf", 4); buffdata += 4;
670 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_vids));
671 memcpy(buffdata, &temp32, 4); buffdata += 4;
672 /* the actual header */
673 temp32 = LE_FROM_GUINT32(avimux->vids.size);
674 memcpy(buffdata, &temp32, 4); buffdata += 4;
675 temp32 = LE_FROM_GUINT32(avimux->vids.width);
676 memcpy(buffdata, &temp32, 4); buffdata += 4;
677 temp32 = LE_FROM_GUINT32(avimux->vids.height);
678 memcpy(buffdata, &temp32, 4); buffdata += 4;
679 temp16 = LE_FROM_GUINT16(avimux->vids.planes);
680 memcpy(buffdata, &temp16, 2); buffdata += 2;
681 temp16 = LE_FROM_GUINT16(avimux->vids.bit_cnt);
682 memcpy(buffdata, &temp16, 2); buffdata += 2;
683 temp32 = LE_FROM_GUINT32(avimux->vids.compression);
684 memcpy(buffdata, &temp32, 4); buffdata += 4;
685 temp32 = LE_FROM_GUINT32(avimux->vids.image_size);
686 memcpy(buffdata, &temp32, 4); buffdata += 4;
687 temp32 = LE_FROM_GUINT32(avimux->vids.xpels_meter);
688 memcpy(buffdata, &temp32, 4); buffdata += 4;
689 temp32 = LE_FROM_GUINT32(avimux->vids.ypels_meter);
690 memcpy(buffdata, &temp32, 4); buffdata += 4;
691 temp32 = LE_FROM_GUINT32(avimux->vids.num_colors);
692 memcpy(buffdata, &temp32, 4); buffdata += 4;
693 temp32 = LE_FROM_GUINT32(avimux->vids.imp_colors);
694 memcpy(buffdata, &temp32, 4); buffdata += 4;
697 if (avimux->audio_pad_connected)
700 memcpy(buffdata, "LIST", 4); buffdata += 4;
701 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh) + sizeof(gst_riff_strf_auds) + 4*5);
702 memcpy(buffdata, &temp32, 4); buffdata += 4;
703 memcpy(buffdata, "strl", 4); buffdata += 4;
705 memcpy(buffdata, "strh", 4); buffdata += 4;
706 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strh));
707 memcpy(buffdata, &temp32, 4); buffdata += 4;
708 /* the actual header */
709 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.type);
710 memcpy(buffdata, &temp32, 4); buffdata += 4;
711 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.fcc_handler);
712 memcpy(buffdata, &temp32, 4); buffdata += 4;
713 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.flags);
714 memcpy(buffdata, &temp32, 4); buffdata += 4;
715 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.priority);
716 memcpy(buffdata, &temp32, 4); buffdata += 4;
717 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.init_frames);
718 memcpy(buffdata, &temp32, 4); buffdata += 4;
719 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.scale);
720 memcpy(buffdata, &temp32, 4); buffdata += 4;
721 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.rate);
722 memcpy(buffdata, &temp32, 4); buffdata += 4;
723 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.start);
724 memcpy(buffdata, &temp32, 4); buffdata += 4;
725 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.length);
726 memcpy(buffdata, &temp32, 4); buffdata += 4;
727 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.bufsize);
728 memcpy(buffdata, &temp32, 4); buffdata += 4;
729 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.quality);
730 memcpy(buffdata, &temp32, 4); buffdata += 4;
731 temp32 = LE_FROM_GUINT32(avimux->auds_hdr.samplesize);
732 memcpy(buffdata, &temp32, 4); buffdata += 4;
733 /* the audio header */
734 memcpy(buffdata, "strf", 4); buffdata += 4;
735 temp32 = LE_FROM_GUINT32(sizeof(gst_riff_strf_auds));
736 memcpy(buffdata, &temp32, 4); buffdata += 4;
737 /* the actual header */
738 temp16 = LE_FROM_GUINT16(avimux->auds.format);
739 memcpy(buffdata, &temp16, 2); buffdata += 2;
740 temp16 = LE_FROM_GUINT16(avimux->auds.channels);
741 memcpy(buffdata, &temp16, 2); buffdata += 2;
742 temp32 = LE_FROM_GUINT32(avimux->auds.rate);
743 memcpy(buffdata, &temp32, 4); buffdata += 4;
744 temp32 = LE_FROM_GUINT32(avimux->auds.av_bps);
745 memcpy(buffdata, &temp32, 4); buffdata += 4;
746 temp16 = LE_FROM_GUINT16(avimux->auds.blockalign);
747 memcpy(buffdata, &temp16, 2); buffdata += 2;
748 temp16 = LE_FROM_GUINT16(avimux->auds.size);
749 memcpy(buffdata, &temp16, 2); buffdata += 2;
752 if (avimux->video_pad_connected)
755 memcpy(buffdata, "LIST", 4); buffdata += 4;
756 temp32 = LE_FROM_GUINT32(sizeof(guint32)+4*3);
757 memcpy(buffdata, &temp32, 4); buffdata += 4;
758 memcpy(buffdata, "odml", 4); buffdata += 4;
759 memcpy(buffdata, "dmlh", 4); buffdata += 4;
760 temp32 = LE_FROM_GUINT32(sizeof(guint32));
761 memcpy(buffdata, &temp32, 4); buffdata += 4;
762 temp32 = LE_FROM_GUINT32(avimux->total_frames);
763 memcpy(buffdata, &temp32, 4); buffdata += 4;
766 /* avi data header */
767 memcpy(buffdata, "LIST", 4); buffdata += 4;
768 temp32 = LE_FROM_GUINT32(avimux->data_size);
769 memcpy(buffdata, &temp32, 4); buffdata += 4;
770 memcpy(buffdata, "movi", 4);
776 gst_avimux_riff_get_avix_header (guint32 datax_size)
782 buffer = gst_buffer_new();
783 GST_BUFFER_SIZE(buffer) = 24;
784 buffdata = GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(buffer));
786 memcpy(buffdata, "LIST", 4); buffdata += 4;
787 temp32 = LE_FROM_GUINT32(datax_size+4*4);
788 memcpy(buffdata, &temp32, 4); buffdata += 4;
789 memcpy(buffdata, "AVIX", 4); buffdata += 4;
790 memcpy(buffdata, "LIST", 4); buffdata += 4;
791 temp32 = LE_FROM_GUINT32(datax_size);
792 memcpy(buffdata, &temp32, 4); buffdata += 4;
793 memcpy(buffdata, "movi", 4);
799 gst_avimux_riff_get_video_header (guint32 video_frame_size)
804 buffer = gst_buffer_new();
805 GST_BUFFER_DATA(buffer) = g_malloc(8);
806 GST_BUFFER_SIZE(buffer) = 8;
807 memcpy(GST_BUFFER_DATA(buffer), "00db", 4);
808 temp32 = LE_FROM_GUINT32(video_frame_size);
809 memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4);
815 gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
820 buffer = gst_buffer_new();
821 GST_BUFFER_DATA(buffer) = g_malloc(8);
822 GST_BUFFER_SIZE(buffer) = 8;
823 memcpy(GST_BUFFER_DATA(buffer), "01wb", 4);
824 temp32 = LE_FROM_GUINT32(audio_sample_size);
825 memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4);
830 /* some other usable functions (thankyou xawtv ;-) ) */
833 gst_avimux_add_index (GstAviMux *avimux, guchar *code, guint32 flags, guint32 size)
835 if (avimux->idx_index == avimux->idx_count)
837 avimux->idx_count += 256;
838 avimux->idx = realloc(avimux->idx, avimux->idx_count*sizeof(gst_riff_index_entry));
840 memcpy(&(avimux->idx[avimux->idx_index].id), code, 4);
841 avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32(flags);
842 avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32(avimux->idx_offset);
843 avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32(size);
848 gst_avimux_write_index (GstAviMux *avimux)
853 buffer = gst_buffer_new();
854 GST_BUFFER_SIZE(buffer) = 8;
855 GST_BUFFER_DATA(buffer) = g_malloc(8);
856 memcpy(GST_BUFFER_DATA(buffer), "idx1", 4);
857 temp32 = LE_FROM_GUINT32(avimux->idx_index * sizeof(gst_riff_index_entry));
858 memcpy(GST_BUFFER_DATA(buffer)+4, &temp32, 4);
859 gst_pad_push(avimux->srcpad, GST_DATA (buffer));
861 buffer = gst_buffer_new();
862 GST_BUFFER_SIZE(buffer) = avimux->idx_index * sizeof(gst_riff_index_entry);
863 GST_BUFFER_DATA(buffer) = (unsigned char*) avimux->idx;
864 avimux->idx = NULL; /* will be free()'ed by gst_buffer_unref() */
865 avimux->total_data += GST_BUFFER_SIZE(buffer);
866 gst_pad_push(avimux->srcpad, GST_DATA (buffer));
868 avimux->idx_size += avimux->idx_index * sizeof(gst_riff_index_entry) + 8;
871 avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
875 gst_avimux_bigfile(GstAviMux *avimux, gboolean last)
880 if (avimux->is_bigfile)
883 event = gst_event_new_seek (GST_FORMAT_BYTES |
884 GST_SEEK_METHOD_SET |
887 /* if the event succeeds */
888 gst_pad_push(avimux->srcpad, GST_DATA(event));
890 /* rewrite AVIX header */
891 header = gst_avimux_riff_get_avix_header(avimux->datax_size);
892 gst_pad_push(avimux->srcpad, GST_DATA (header));
894 /* go back to current location */
895 event = gst_event_new_seek (GST_FORMAT_BYTES |
896 GST_SEEK_METHOD_SET |
899 gst_pad_push(avimux->srcpad, GST_DATA(event));
901 avimux->avix_start = avimux->total_data;
906 avimux->is_bigfile = TRUE;
907 avimux->numx_frames = 0;
908 avimux->datax_size = 0;
910 header = gst_avimux_riff_get_avix_header(0);
911 avimux->total_data += GST_BUFFER_SIZE(header);
912 gst_pad_push(avimux->srcpad, GST_DATA (header));
915 /* enough header blabla now, let's go on to actually writing the headers */
918 gst_avimux_start_file (GstAviMux *avimux)
922 avimux->total_data = 0;
923 avimux->total_frames = 0;
924 avimux->data_size = 4; /* ? */
925 avimux->datax_size = 0;
926 avimux->num_frames = 0;
927 avimux->numx_frames = 0;
928 avimux->audio_size = 0;
929 avimux->audio_time = 0;
930 avimux->avix_start = 0;
932 avimux->idx_index = 0;
933 avimux->idx_offset = 0; /* see 10 lines below */
934 avimux->idx_size = 0;
935 avimux->idx_count = 0;
939 avimux->avi_hdr.streams = (avimux->video_pad_connected?1:0) + (avimux->audio_pad_connected?1:0);
940 avimux->is_bigfile = FALSE;
942 header = gst_avimux_riff_get_avi_header(avimux);
943 avimux->total_data += GST_BUFFER_SIZE(header);
944 avimux->idx_offset = avimux->total_data;
945 gst_pad_push(avimux->srcpad, GST_DATA (header));
947 avimux->write_header = FALSE;
948 avimux->restart = FALSE;
952 gst_avimux_stop_file (GstAviMux *avimux)
957 /* if bigfile, rewrite header, else write indexes */
958 if (avimux->video_pad_connected)
960 if (avimux->is_bigfile)
962 gst_avimux_bigfile(avimux, TRUE);
963 avimux->idx_size = 0;
967 gst_avimux_write_index(avimux);
971 /* statistics/total_frames/... */
972 avimux->avi_hdr.tot_frames = avimux->num_frames;
973 if (avimux->video_pad_connected) {
974 avimux->vids_hdr.length = avimux->num_frames;
976 if (avimux->audio_pad_connected) {
977 avimux->auds_hdr.length = (avimux->audio_time * avimux->auds.rate)/GST_SECOND;
980 /* set rate and everything having to do with that */
981 avimux->avi_hdr.max_bps = 0;
982 if (avimux->audio_pad_connected) {
983 /* calculate bps if needed */
984 if (!avimux->auds.av_bps) {
985 if (avimux->audio_time) {
986 avimux->auds_hdr.rate = (GST_SECOND * avimux->audio_size) / avimux->audio_time;
988 gst_element_error (avimux, STREAM, MUX,
989 (_("No or invalid input audio, AVI stream will be corrupt")), NULL);
990 avimux->auds_hdr.rate = 0;
992 avimux->auds.av_bps = avimux->auds_hdr.rate * avimux->auds_hdr.scale;
994 avimux->avi_hdr.max_bps += avimux->auds.av_bps;
996 if (avimux->video_pad_connected) {
997 avimux->avi_hdr.max_bps += ((avimux->vids.bit_cnt+7)/8) *
998 (1000000. / avimux->avi_hdr.us_frame) *
999 avimux->vids.image_size;
1002 /* seek and rewrite the header */
1003 header = gst_avimux_riff_get_avi_header(avimux);
1004 event = gst_event_new_seek (GST_FORMAT_BYTES |
1005 GST_SEEK_METHOD_SET, 0);
1006 gst_pad_push(avimux->srcpad, GST_DATA(event));
1007 gst_pad_push(avimux->srcpad, GST_DATA (header));
1008 event = gst_event_new_seek (GST_FORMAT_BYTES |
1009 GST_SEEK_METHOD_SET, avimux->total_data);
1010 gst_pad_push(avimux->srcpad, GST_DATA(event));
1012 avimux->write_header = TRUE;
1016 gst_avimux_restart_file (GstAviMux *avimux)
1020 gst_avimux_stop_file(avimux);
1022 event = gst_event_new(GST_EVENT_EOS);
1023 gst_pad_push(avimux->srcpad, GST_DATA(event));
1025 gst_avimux_start_file(avimux);
1028 /* handle events (search) */
1030 gst_avimux_handle_event (GstPad *pad, GstEvent *event)
1035 avimux = GST_AVIMUX (gst_pad_get_parent (pad));
1037 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
1041 /* is this allright? */
1042 if (pad == avimux->videosinkpad) {
1043 avimux->video_pad_eos = TRUE;
1044 } else if (pad == avimux->audiosinkpad) {
1045 avimux->audio_pad_eos = TRUE;
1047 g_warning("Unknown pad for EOS!");
1058 /* fill the internal queue for each available pad */
1060 gst_avimux_fill_queue (GstAviMux *avimux)
1064 while (!avimux->audio_buffer_queue &&
1065 avimux->audiosinkpad &&
1066 avimux->audio_pad_connected &&
1067 GST_PAD_IS_USABLE(avimux->audiosinkpad) &&
1068 !avimux->audio_pad_eos)
1070 buffer = GST_BUFFER (gst_pad_pull(avimux->audiosinkpad));
1071 if (GST_IS_EVENT(buffer)) {
1072 gst_avimux_handle_event(avimux->audiosinkpad, GST_EVENT(buffer));
1074 avimux->audio_buffer_queue = buffer;
1079 while (!avimux->video_buffer_queue &&
1080 avimux->videosinkpad &&
1081 avimux->video_pad_connected &&
1082 GST_PAD_IS_USABLE(avimux->videosinkpad) &&
1083 !avimux->video_pad_eos)
1085 buffer = GST_BUFFER (gst_pad_pull(avimux->videosinkpad));
1086 if (GST_IS_EVENT(buffer)) {
1087 gst_avimux_handle_event(avimux->videosinkpad, GST_EVENT(buffer));
1089 avimux->video_buffer_queue = buffer;
1096 /* send extra 'padding' data */
1098 gst_avimux_send_pad_data (GstAviMux *avimux,
1103 buffer = gst_buffer_new();
1104 GST_BUFFER_SIZE(buffer) = num_bytes;
1105 GST_BUFFER_DATA(buffer) = g_malloc(num_bytes);
1106 memset(GST_BUFFER_DATA(buffer), 0, num_bytes);
1108 gst_pad_push(avimux->srcpad, GST_DATA (buffer));
1111 /* do audio buffer */
1113 gst_avimux_do_audio_buffer (GstAviMux *avimux)
1115 GstBuffer *data = avimux->audio_buffer_queue, *header;
1116 gulong total_size, pad_bytes = 0;
1118 /* write a audio header + index entry */
1119 if (GST_BUFFER_SIZE(data) & 1) {
1120 pad_bytes = 2 - (GST_BUFFER_SIZE(data) & 1);
1122 header = gst_avimux_riff_get_audio_header(GST_BUFFER_SIZE(data));
1123 total_size = GST_BUFFER_SIZE(header) + GST_BUFFER_SIZE(data) + pad_bytes;
1125 if (avimux->is_bigfile)
1127 avimux->datax_size += total_size;
1131 avimux->data_size += total_size;
1132 avimux->audio_size += GST_BUFFER_SIZE(data);
1133 avimux->audio_time += GST_BUFFER_DURATION(data);
1134 gst_avimux_add_index(avimux, "01wb", 0x0, GST_BUFFER_SIZE(data));
1137 gst_pad_push(avimux->srcpad, GST_DATA (header));
1138 gst_pad_push(avimux->srcpad, GST_DATA (data));
1140 gst_avimux_send_pad_data(avimux, pad_bytes);
1142 avimux->total_data += total_size;
1143 avimux->idx_offset += total_size;
1145 avimux->audio_buffer_queue = NULL;
1149 /* do video buffer */
1151 gst_avimux_do_video_buffer (GstAviMux *avimux)
1153 GstBuffer *data = avimux->video_buffer_queue, *header;
1154 gulong total_size, pad_bytes = 0;
1156 if (avimux->restart)
1157 gst_avimux_restart_file(avimux);
1159 /* write a video header + index entry */
1160 if ((avimux->is_bigfile?avimux->datax_size:avimux->data_size)+GST_BUFFER_SIZE(data)>1024*1024*2000)
1162 if (avimux->enable_large_avi)
1163 gst_avimux_bigfile(avimux, FALSE);
1165 gst_avimux_restart_file(avimux);
1168 if (GST_BUFFER_SIZE(data) & 1) {
1169 pad_bytes = 2 - (GST_BUFFER_SIZE(data) & 1);
1171 header = gst_avimux_riff_get_video_header(GST_BUFFER_SIZE(data));
1172 total_size = GST_BUFFER_SIZE(header) + GST_BUFFER_SIZE(data) + pad_bytes;
1173 avimux->total_frames++;
1175 if (avimux->is_bigfile)
1177 avimux->datax_size += total_size;
1178 avimux->numx_frames++;
1183 if (GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_KEY_UNIT))
1185 avimux->data_size += total_size;
1186 avimux->num_frames++;
1187 gst_avimux_add_index(avimux, "00db", flags, GST_BUFFER_SIZE(data));
1190 gst_pad_push(avimux->srcpad, GST_DATA (header));
1191 gst_pad_push(avimux->srcpad, GST_DATA (data));
1193 gst_avimux_send_pad_data(avimux, pad_bytes);
1195 avimux->total_data += total_size;
1196 avimux->idx_offset += total_size;
1198 avimux->video_buffer_queue = NULL;
1202 /* take the oldest buffer in our internal queue and push-it */
1204 gst_avimux_do_one_buffer (GstAviMux *avimux)
1206 if (avimux->video_buffer_queue &&
1207 avimux->audio_buffer_queue)
1209 if (GST_BUFFER_TIMESTAMP(avimux->video_buffer_queue) <=
1210 GST_BUFFER_TIMESTAMP(avimux->audio_buffer_queue))
1211 gst_avimux_do_video_buffer(avimux);
1213 gst_avimux_do_audio_buffer(avimux);
1215 else if (avimux->video_buffer_queue ||
1216 avimux->audio_buffer_queue)
1218 if (avimux->video_buffer_queue)
1219 gst_avimux_do_video_buffer(avimux);
1221 gst_avimux_do_audio_buffer(avimux);
1224 /* simply finish off the file and send EOS */
1225 gst_avimux_stop_file(avimux);
1226 gst_pad_push(avimux->srcpad,
1227 GST_DATA(gst_event_new(GST_EVENT_EOS)));
1228 gst_element_set_eos(GST_ELEMENT(avimux));
1237 gst_avimux_loop (GstElement *element)
1241 avimux = GST_AVIMUX(element);
1243 /* first fill queue (some elements only set caps when
1244 * flowing data), then write header */
1245 gst_avimux_fill_queue(avimux);
1247 if (avimux->write_header)
1248 gst_avimux_start_file(avimux);
1250 gst_avimux_do_one_buffer(avimux);
1254 gst_avimux_get_property (GObject *object,
1261 /* it's not null if we got it, but it might not be ours */
1262 g_return_if_fail(GST_IS_AVIMUX(object));
1263 avimux = GST_AVIMUX(object);
1268 g_value_set_boolean(value, avimux->enable_large_avi);
1271 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1277 gst_avimux_set_property (GObject *object,
1279 const GValue *value,
1284 /* it's not null if we got it, but it might not be ours */
1285 g_return_if_fail(GST_IS_AVIMUX(object));
1286 avimux = GST_AVIMUX(object);
1291 avimux->enable_large_avi = g_value_get_boolean(value);
1294 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1299 static GstElementStateReturn
1300 gst_avimux_change_state (GstElement *element)
1303 gint transition = GST_STATE_TRANSITION (element);
1305 g_return_val_if_fail(GST_IS_AVIMUX(element), GST_STATE_FAILURE);
1307 avimux = GST_AVIMUX(element);
1309 switch (transition) {
1310 case GST_STATE_PAUSED_TO_PLAYING:
1311 avimux->video_pad_eos = avimux->audio_pad_eos = FALSE;
1315 if (GST_ELEMENT_CLASS (parent_class)->change_state)
1316 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1318 return GST_STATE_SUCCESS;