1 /* AVI muxer plugin for GStreamer
2 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
3 * (C) 2006 Mark Nauwelaerts <manauw@skynet.be>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /* based on:
22 * - the old avimuxer (by Wim Taymans)
23 * - xawtv's aviwriter (by Gerd Knorr)
24 * - mjpegtools' avilib (by Rainer Johanni)
25 * - openDML large-AVI docs
26 */
27
28 /**
29 * SECTION:element-avimux
30 * @title: avimux
31 *
32 * Muxes raw or compressed audio and/or video streams into an AVI file.
33 *
34 * ## Example launch lines
35 * (write everything in one line, without the backslash characters)
36 * |[
37 * gst-launch-1.0 videotestsrc num-buffers=250 \
38 * ! 'video/x-raw,format=(string)I420,width=320,height=240,framerate=(fraction)25/1' \
39 * ! queue ! mux. \
40 * audiotestsrc num-buffers=440 ! audioconvert \
41 * ! 'audio/x-raw,rate=44100,channels=2' ! queue ! mux. \
42 * avimux name=mux ! filesink location=test.avi
43 * ]| This will create an .AVI file containing an uncompressed video stream
44 * with a test picture and an uncompressed audio stream containing a
45 * test sound.
46 * |[
47 * gst-launch-1.0 videotestsrc num-buffers=250 \
48 * ! 'video/x-raw,format=(string)I420,width=320,height=240,framerate=(fraction)25/1' \
49 * ! xvidenc ! queue ! mux. \
50 * audiotestsrc num-buffers=440 ! audioconvert ! 'audio/x-raw,rate=44100,channels=2' \
51 * ! lame ! queue ! mux. \
52 * avimux name=mux ! filesink location=test.avi
53 * ]| This will create an .AVI file containing the same test video and sound
54 * as above, only that both streams will be compressed this time. This will
55 * only work if you have the necessary encoder elements installed of course.
56 *
57 */
58
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62
63 #include "gst/gst-i18n-plugin.h"
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67
68 #include <gst/video/video.h>
69 #include <gst/audio/audio.h>
70 #include <gst/base/gstbytewriter.h>
71
72 #include "gstavielements.h"
73 #include "gstavimux.h"
74
75 GST_DEBUG_CATEGORY_STATIC (avimux_debug);
76 #define GST_CAT_DEFAULT avimux_debug
77
78 enum
79 {
80 PROP_0,
81 PROP_BIGFILE
82 };
83
84 #define DEFAULT_BIGFILE TRUE
85
86 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
87 GST_PAD_SRC,
88 GST_PAD_ALWAYS,
89 GST_STATIC_CAPS ("video/x-msvideo")
90 );
91
92 static GstStaticPadTemplate video_sink_factory =
93 GST_STATIC_PAD_TEMPLATE ("video_%u",
94 GST_PAD_SINK,
95 GST_PAD_REQUEST,
96 GST_STATIC_CAPS ("video/x-raw, "
97 "format = (string) { YUY2, I420, BGR, BGRx, BGRA, GRAY8, UYVY, v210 }, "
98 "width = (int) [ 16, 4096 ], "
99 "height = (int) [ 16, 4096 ], "
100 "framerate = (fraction) [ 0, MAX ]; "
101 "image/jpeg, "
102 "width = (int) [ 16, 4096 ], "
103 "height = (int) [ 16, 4096 ], "
104 "framerate = (fraction) [ 0, MAX ]; "
105 "video/x-divx, "
106 "width = (int) [ 16, 4096 ], "
107 "height = (int) [ 16, 4096 ], "
108 "framerate = (fraction) [ 0, MAX ], "
109 "divxversion = (int) [ 3, 5 ]; "
110 "video/x-msmpeg, "
111 "width = (int) [ 16, 4096 ], "
112 "height = (int) [ 16, 4096 ], "
113 "framerate = (fraction) [ 0, MAX ], "
114 "msmpegversion = (int) [ 41, 43 ]; "
115 "video/mpeg, "
116 "width = (int) [ 16, 4096 ], "
117 "height = (int) [ 16, 4096 ], "
118 "framerate = (fraction) [ 0, MAX ], "
119 "mpegversion = (int) { 1, 2, 4}, "
120 "systemstream = (boolean) FALSE; "
121 "video/x-h263, "
122 "width = (int) [ 16, 4096 ], "
123 "height = (int) [ 16, 4096 ], "
124 "framerate = (fraction) [ 0, MAX ]; "
125 "video/x-h264, "
126 "stream-format = (string) byte-stream, "
127 "alignment = (string) au, "
128 "width = (int) [ 16, 4096 ], "
129 "height = (int) [ 16, 4096 ], "
130 "framerate = (fraction) [ 0, MAX ]; "
131 "video/x-dv, "
132 "width = (int) 720, "
133 "height = (int) { 576, 480 }, "
134 "framerate = (fraction) [ 0, MAX ], "
135 "systemstream = (boolean) FALSE; "
136 "video/x-huffyuv, "
137 "width = (int) [ 16, 4096 ], "
138 "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ];"
139 "video/x-wmv, "
140 "width = (int) [ 16, 4096 ], "
141 "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ], "
142 "wmvversion = (int) [ 1, 3];"
143 "image/x-jpc, "
144 "width = (int) [ 1, 2147483647 ], "
145 "height = (int) [ 1, 2147483647 ], "
146 "framerate = (fraction) [ 0, MAX ];"
147 "video/x-vp8, "
148 "width = (int) [ 1, 2147483647 ], "
149 "height = (int) [ 1, 2147483647 ], "
150 "framerate = (fraction) [ 0, MAX ];"
151 "image/png, "
152 "width = (int) [ 16, 4096 ], "
153 "height = (int) [ 16, 4096 ], framerate = (fraction) [ 0, MAX ]")
154 );
155
156 static GstStaticPadTemplate audio_sink_factory =
157 GST_STATIC_PAD_TEMPLATE ("audio_%u",
158 GST_PAD_SINK,
159 GST_PAD_REQUEST,
160 GST_STATIC_CAPS ("audio/x-raw, "
161 "format = (string) { U8, S16LE, S24LE, S32LE }, "
162 "rate = (int) [ 1000, 96000 ], "
163 "channels = (int) [ 1, 65535 ]; "
164 "audio/mpeg, "
165 "mpegversion = (int) 1, "
166 "layer = (int) [ 1, 3 ], "
167 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
168 "audio/mpeg, "
169 "mpegversion = (int) 4, "
170 "stream-format = (string) raw, "
171 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
172 /*#if 0 VC6 doesn't support #if here ...
173 "audio/x-vorbis, "
174 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
175 #endif*/
176 "audio/x-ac3, "
177 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 6 ]; "
178 "audio/x-alaw, "
179 "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; "
180 "audio/x-mulaw, "
181 "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; "
182 "audio/x-wma, "
183 "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ], "
184 "wmaversion = (int) [ 1, 2 ] ")
185 );
186
187 static void gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free);
188
189 static GstFlowReturn gst_avi_mux_collect_pads (GstCollectPads * pads,
190 GstAviMux * avimux);
191 static gboolean gst_avi_mux_handle_event (GstCollectPads * pad,
192 GstCollectData * data, GstEvent * event, gpointer user_data);
193 static GstPad *gst_avi_mux_request_new_pad (GstElement * element,
194 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
195 static void gst_avi_mux_release_pad (GstElement * element, GstPad * pad);
196 static void gst_avi_mux_set_property (GObject * object,
197 guint prop_id, const GValue * value, GParamSpec * pspec);
198 static void gst_avi_mux_get_property (GObject * object,
199 guint prop_id, GValue * value, GParamSpec * pspec);
200 static GstStateChangeReturn gst_avi_mux_change_state (GstElement * element,
201 GstStateChange transition);
202
203 #define gst_avi_mux_parent_class parent_class
204 G_DEFINE_TYPE_WITH_CODE (GstAviMux, gst_avi_mux, GST_TYPE_ELEMENT,
205 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
206 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (avimux, "avimux", GST_RANK_PRIMARY,
207 GST_TYPE_AVI_MUX, avi_element_init (plugin));
208
209 static void
gst_avi_mux_finalize(GObject * object)210 gst_avi_mux_finalize (GObject * object)
211 {
212 GstAviMux *mux = GST_AVI_MUX (object);
213 GSList *node;
214
215 /* completely free each sinkpad */
216 node = mux->sinkpads;
217 while (node) {
218 GstAviPad *avipad = (GstAviPad *) node->data;
219
220 node = node->next;
221
222 gst_avi_mux_pad_reset (avipad, TRUE);
223 g_free (avipad);
224 }
225 g_slist_free (mux->sinkpads);
226 mux->sinkpads = NULL;
227
228 g_free (mux->idx);
229 mux->idx = NULL;
230
231 gst_object_unref (mux->collect);
232
233 G_OBJECT_CLASS (parent_class)->finalize (object);
234 }
235
236 static void
gst_avi_mux_class_init(GstAviMuxClass * klass)237 gst_avi_mux_class_init (GstAviMuxClass * klass)
238 {
239 GObjectClass *gobject_class;
240 GstElementClass *gstelement_class;
241
242 gobject_class = (GObjectClass *) klass;
243 gstelement_class = (GstElementClass *) klass;
244
245 GST_DEBUG_CATEGORY_INIT (avimux_debug, "avimux", 0, "Muxer for AVI streams");
246
247 gobject_class->get_property = gst_avi_mux_get_property;
248 gobject_class->set_property = gst_avi_mux_set_property;
249 gobject_class->finalize = gst_avi_mux_finalize;
250
251 g_object_class_install_property (gobject_class, PROP_BIGFILE,
252 g_param_spec_boolean ("bigfile", "Bigfile Support (>2GB)",
253 "Support for openDML-2.0 (big) AVI files", DEFAULT_BIGFILE,
254 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
255
256 gstelement_class->request_new_pad =
257 GST_DEBUG_FUNCPTR (gst_avi_mux_request_new_pad);
258 gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_avi_mux_release_pad);
259 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_avi_mux_change_state);
260
261 gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
262 gst_element_class_add_static_pad_template (gstelement_class,
263 &audio_sink_factory);
264 gst_element_class_add_static_pad_template (gstelement_class,
265 &video_sink_factory);
266
267 gst_element_class_set_static_metadata (gstelement_class, "Avi muxer",
268 "Codec/Muxer",
269 "Muxes audio and video into an avi stream",
270 "GStreamer maintainers <gstreamer-devel@lists.freedesktop.org>");
271 }
272
273 /* reset pad to initial state
274 * free - if true, release all, not only stream related, data */
275 static void
gst_avi_mux_pad_reset(GstAviPad * avipad,gboolean free)276 gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
277 {
278 /* generic part */
279 memset (&(avipad->hdr), 0, sizeof (gst_riff_strh));
280
281 memset (&(avipad->idx[0]), 0, sizeof (avipad->idx));
282
283 if (free) {
284 g_free (avipad->tag);
285 avipad->tag = NULL;
286 g_free (avipad->idx_tag);
287 avipad->idx_tag = NULL;
288 }
289
290 if (avipad->is_video) {
291 GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
292
293 avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
294 if (vidpad->vids_codec_data) {
295 gst_buffer_unref (vidpad->vids_codec_data);
296 vidpad->vids_codec_data = NULL;
297 }
298
299 if (vidpad->prepend_buffer) {
300 gst_buffer_unref (vidpad->prepend_buffer);
301 vidpad->prepend_buffer = NULL;
302 }
303
304 memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids));
305 memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
306 } else {
307 GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
308
309 audpad->samples = 0;
310
311 avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
312 if (audpad->auds_codec_data) {
313 gst_buffer_unref (audpad->auds_codec_data);
314 audpad->auds_codec_data = NULL;
315 }
316
317 memset (&(audpad->auds), 0, sizeof (gst_riff_strf_auds));
318
319 audpad->audio_size = 0;
320 audpad->audio_time = 0;
321 audpad->max_audio_chunk = 0;
322 }
323 }
324
325 static void
gst_avi_mux_reset(GstAviMux * avimux)326 gst_avi_mux_reset (GstAviMux * avimux)
327 {
328 GSList *node, *newlist = NULL;
329
330 /* free and reset each sinkpad */
331 node = avimux->sinkpads;
332 while (node) {
333 GstAviPad *avipad = (GstAviPad *) node->data;
334
335 node = node->next;
336
337 gst_avi_mux_pad_reset (avipad, FALSE);
338 /* if this pad has collectdata, keep it, otherwise dump it completely */
339 if (avipad->collect)
340 newlist = g_slist_append (newlist, avipad);
341 else {
342 gst_avi_mux_pad_reset (avipad, TRUE);
343 g_free (avipad);
344 }
345 }
346
347 /* free the old list of sinkpads, only keep the real collecting ones */
348 g_slist_free (avimux->sinkpads);
349 avimux->sinkpads = newlist;
350
351 /* avi data */
352 avimux->num_frames = 0;
353 memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
354 avimux->avi_hdr.max_bps = 10000000;
355 avimux->codec_data_size = 0;
356
357 if (avimux->tags_snap) {
358 gst_tag_list_unref (avimux->tags_snap);
359 avimux->tags_snap = NULL;
360 }
361
362 g_free (avimux->idx);
363 avimux->idx = NULL;
364
365 /* state info */
366 avimux->write_header = TRUE;
367
368 /* tags */
369 gst_tag_setter_reset_tags (GST_TAG_SETTER (avimux));
370 }
371
372 static void
gst_avi_mux_init(GstAviMux * avimux)373 gst_avi_mux_init (GstAviMux * avimux)
374 {
375 avimux->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
376 gst_pad_use_fixed_caps (avimux->srcpad);
377 gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
378
379 /* property */
380 avimux->enable_large_avi = DEFAULT_BIGFILE;
381
382 avimux->collect = gst_collect_pads_new ();
383 gst_collect_pads_set_function (avimux->collect,
384 (GstCollectPadsFunction) (GST_DEBUG_FUNCPTR (gst_avi_mux_collect_pads)),
385 avimux);
386 gst_collect_pads_set_event_function (avimux->collect,
387 (GstCollectPadsEventFunction) (GST_DEBUG_FUNCPTR
388 (gst_avi_mux_handle_event)), avimux);
389
390 /* set to clean state */
391 gst_avi_mux_reset (avimux);
392 }
393
394 static gboolean
gst_avi_mux_vidsink_set_caps(GstPad * pad,GstCaps * vscaps)395 gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
396 {
397 GstAviMux *avimux;
398 GstAviVideoPad *avipad;
399 GstAviCollectData *collect_pad;
400 GstStructure *structure;
401 const gchar *mimetype;
402 const GValue *fps, *par;
403 const GValue *codec_data;
404 gint width, height;
405 gint par_n, par_d;
406 gboolean codec_data_in_headers = TRUE;
407 gboolean valid_caps = TRUE;
408
409 avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
410
411 /* find stream data */
412 collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad);
413 g_assert (collect_pad);
414 avipad = (GstAviVideoPad *) collect_pad->avipad;
415 g_assert (avipad);
416 g_assert (avipad->parent.is_video);
417 g_assert (avipad->parent.hdr.type == GST_MAKE_FOURCC ('v', 'i', 'd', 's'));
418
419 GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
420 GST_DEBUG_PAD_NAME (pad), vscaps);
421
422 structure = gst_caps_get_structure (vscaps, 0);
423 mimetype = gst_structure_get_name (structure);
424
425 /* global */
426 avipad->vids.size = sizeof (gst_riff_strf_vids);
427 avipad->vids.planes = 1;
428 if (!gst_structure_get_int (structure, "width", &width) ||
429 !gst_structure_get_int (structure, "height", &height)) {
430 goto refuse_caps;
431 }
432
433 avipad->vids.width = width;
434 avipad->vids.height = height;
435
436 fps = gst_structure_get_value (structure, "framerate");
437 if (fps == NULL || !GST_VALUE_HOLDS_FRACTION (fps))
438 goto refuse_caps;
439
440 avipad->parent.hdr.rate = gst_value_get_fraction_numerator (fps);
441 avipad->parent.hdr.scale = gst_value_get_fraction_denominator (fps);
442 if (avipad->parent.hdr.rate <= 0 || avipad->parent.hdr.scale <= 0)
443 goto refuse_caps;
444
445 /* (pixel) aspect ratio data, if any */
446 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
447 /* only use video properties header if there is non-trivial aspect info */
448 if (par && GST_VALUE_HOLDS_FRACTION (par) &&
449 ((par_n = gst_value_get_fraction_numerator (par)) !=
450 (par_d = gst_value_get_fraction_denominator (par)))) {
451 GValue to_ratio = { 0, };
452 guint ratio_n, ratio_d;
453
454 /* some fraction voodoo to obtain simplest possible ratio */
455 g_value_init (&to_ratio, GST_TYPE_FRACTION);
456 gst_value_set_fraction (&to_ratio, width * par_n, height * par_d);
457 ratio_n = gst_value_get_fraction_numerator (&to_ratio);
458 ratio_d = gst_value_get_fraction_denominator (&to_ratio);
459 GST_DEBUG_OBJECT (avimux, "generating vprp data with aspect ratio %d/%d",
460 ratio_n, ratio_d);
461 /* simply fill in */
462 avipad->vprp.vert_rate = avipad->parent.hdr.rate / avipad->parent.hdr.scale;
463 avipad->vprp.hor_t_total = width;
464 avipad->vprp.vert_lines = height;
465 avipad->vprp.aspect = (ratio_n) << 16 | (ratio_d & 0xffff);
466 avipad->vprp.width = width;
467 avipad->vprp.height = height;
468 avipad->vprp.fields = 1;
469 avipad->vprp.field_info[0].compressed_bm_height = height;
470 avipad->vprp.field_info[0].compressed_bm_width = width;
471 avipad->vprp.field_info[0].valid_bm_height = height;
472 avipad->vprp.field_info[0].valid_bm_width = width;
473 }
474
475 if (!strcmp (mimetype, "video/x-raw")) {
476 const gchar *format;
477 GstVideoFormat fmt;
478
479 format = gst_structure_get_string (structure, "format");
480 fmt = gst_video_format_from_string (format);
481
482 switch (fmt) {
483 case GST_VIDEO_FORMAT_YUY2:
484 avipad->vids.compression = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
485 avipad->vids.bit_cnt = 16;
486 break;
487 case GST_VIDEO_FORMAT_UYVY:
488 avipad->vids.compression = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
489 avipad->vids.bit_cnt = 16;
490 break;
491 case GST_VIDEO_FORMAT_I420:
492 avipad->vids.compression = GST_MAKE_FOURCC ('I', '4', '2', '0');
493 avipad->vids.bit_cnt = 12;
494 break;
495 case GST_VIDEO_FORMAT_GRAY8:
496 avipad->vids.compression = GST_MAKE_FOURCC ('Y', '8', '0', '0');
497 avipad->vids.bit_cnt = 8;
498 break;
499 case GST_VIDEO_FORMAT_v210:
500 avipad->vids.compression = GST_MAKE_FOURCC ('v', '2', '1', '0');
501 avipad->vids.bit_cnt = 20;
502 break;
503 case GST_VIDEO_FORMAT_BGR:
504 avipad->vids.compression = GST_MAKE_FOURCC (0x00, 0x00, 0x00, 0x00);
505 avipad->vids.bit_cnt = 24;
506 break;
507 case GST_VIDEO_FORMAT_BGRx:
508 case GST_VIDEO_FORMAT_BGRA:
509 avipad->vids.compression = GST_MAKE_FOURCC (0x00, 0x00, 0x00, 0x00);
510 avipad->vids.bit_cnt = 32;
511 break;
512 default:
513 valid_caps = FALSE;
514 break;
515 }
516 } else {
517 avipad->vids.bit_cnt = 24;
518 avipad->vids.compression = 0;
519
520 /* find format */
521 if (!strcmp (mimetype, "video/x-huffyuv")) {
522 avipad->vids.compression = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
523 } else if (!strcmp (mimetype, "image/jpeg")) {
524 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
525 } else if (!strcmp (mimetype, "video/x-divx")) {
526 gint divxversion;
527
528 gst_structure_get_int (structure, "divxversion", &divxversion);
529 switch (divxversion) {
530 case 3:
531 avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
532 break;
533 case 4:
534 avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
535 break;
536 case 5:
537 avipad->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
538 break;
539 default:
540 valid_caps = FALSE;
541 }
542 } else if (gst_structure_has_name (structure, "video/x-msmpeg")) {
543 gint msmpegversion;
544
545 gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
546 switch (msmpegversion) {
547 case 41:
548 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
549 break;
550 case 42:
551 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
552 break;
553 case 43:
554 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
555 break;
556 default:
557 GST_INFO ("unhandled msmpegversion : %d, fall back to fourcc=MPEG",
558 msmpegversion);
559 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
560 break;
561 }
562 } else if (!strcmp (mimetype, "video/x-dv")) {
563 avipad->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
564 } else if (!strcmp (mimetype, "video/x-h263")) {
565 avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
566 } else if (!strcmp (mimetype, "video/x-h264")) {
567 avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '4');
568 } else if (!strcmp (mimetype, "video/mpeg")) {
569 gint mpegversion;
570
571 gst_structure_get_int (structure, "mpegversion", &mpegversion);
572
573 switch (mpegversion) {
574 case 2:
575 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '2');
576 break;
577 case 4:
578 /* mplayer/ffmpeg might not work with DIVX, but with FMP4 */
579 avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
580
581 /* DIVX/XVID in AVI store the codec_data chunk as part of the
582 first data buffer. So for this case, we prepend the codec_data
583 blob (if any) to that first buffer */
584 codec_data_in_headers = FALSE;
585 break;
586 default:
587 GST_INFO ("unhandled mpegversion : %d, fall back to fourcc=MPEG",
588 mpegversion);
589 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
590 break;
591 }
592 } else if (!strcmp (mimetype, "video/x-wmv")) {
593 gint wmvversion;
594
595 if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
596 switch (wmvversion) {
597 case 1:
598 avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
599 break;
600 case 2:
601 avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
602 break;
603 case 3:
604 avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
605 break;
606 default:
607 valid_caps = FALSE;
608 break;
609 }
610 }
611 } else if (!strcmp (mimetype, "image/x-jpc")) {
612 avipad->vids.compression = GST_MAKE_FOURCC ('M', 'J', '2', 'C');
613 } else if (!strcmp (mimetype, "video/x-vp8")) {
614 avipad->vids.compression = GST_MAKE_FOURCC ('V', 'P', '8', '0');
615 } else if (!strcmp (mimetype, "image/png")) {
616 avipad->vids.compression = GST_MAKE_FOURCC ('p', 'n', 'g', ' ');
617 } else {
618 valid_caps = FALSE;
619 }
620
621 if (!valid_caps)
622 goto refuse_caps;
623 }
624
625 /* codec initialization data, if any */
626 codec_data = gst_structure_get_value (structure, "codec_data");
627 if (codec_data) {
628 if (codec_data_in_headers) {
629 avipad->vids_codec_data = gst_value_get_buffer (codec_data);
630 gst_buffer_ref (avipad->vids_codec_data);
631 /* keep global track of size */
632 avimux->codec_data_size += gst_buffer_get_size (avipad->vids_codec_data);
633 } else {
634 avipad->prepend_buffer =
635 gst_buffer_ref (gst_value_get_buffer (codec_data));
636 }
637 }
638
639 avipad->parent.hdr.fcc_handler = avipad->vids.compression;
640 avipad->vids.image_size = avipad->vids.height * avipad->vids.width;
641 /* hm, maybe why avi only handles one stream well ... */
642 avimux->avi_hdr.width = avipad->vids.width;
643 avimux->avi_hdr.height = avipad->vids.height;
644 avimux->avi_hdr.us_frame = 1000000. * avipad->parent.hdr.scale /
645 avipad->parent.hdr.rate;
646
647 gst_object_unref (avimux);
648 return TRUE;
649
650 refuse_caps:
651 {
652 GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
653 gst_object_unref (avimux);
654 return FALSE;
655 }
656 }
657
658 static void gst_avi_mux_audsink_set_fields (GstAviMux * avimux,
659 GstAviAudioPad * avipad);
660
661 static GstFlowReturn
gst_avi_mux_audsink_scan_mpeg_audio(GstAviMux * avimux,GstAviPad * avipad,GstBuffer * buffer)662 gst_avi_mux_audsink_scan_mpeg_audio (GstAviMux * avimux, GstAviPad * avipad,
663 GstBuffer * buffer)
664 {
665 GstMapInfo map;
666 guint spf;
667 guint32 header;
668 gulong layer;
669 gulong version;
670 gint lsf, mpg25;
671
672 gst_buffer_map (buffer, &map, GST_MAP_READ);
673 if (map.size < 4)
674 goto not_parsed;
675
676 header = GST_READ_UINT32_BE (map.data);
677
678 if ((header & 0xffe00000) != 0xffe00000)
679 goto not_parsed;
680
681 /* thanks go to mp3parse */
682 if (header & (1 << 20)) {
683 lsf = (header & (1 << 19)) ? 0 : 1;
684 mpg25 = 0;
685 } else {
686 lsf = 1;
687 mpg25 = 1;
688 }
689
690 version = 1 + lsf + mpg25;
691 layer = 4 - ((header >> 17) & 0x3);
692
693 /* see http://www.codeproject.com/audio/MPEGAudioInfo.asp */
694 if (layer == 1)
695 spf = 384;
696 else if (layer == 2)
697 spf = 1152;
698 else if (version == 1) {
699 spf = 1152;
700 } else {
701 /* MPEG-2 or "2.5" */
702 spf = 576;
703 }
704
705 if (G_UNLIKELY (avipad->hdr.scale <= 1)) {
706 avipad->hdr.scale = spf;
707 gst_avi_mux_audsink_set_fields (avimux, (GstAviAudioPad *) avipad);
708 } else if (G_UNLIKELY (avipad->hdr.scale != spf)) {
709 GST_WARNING_OBJECT (avimux, "input mpeg audio has varying frame size");
710 goto cbr_fallback;
711 }
712 done:
713 gst_buffer_unmap (buffer, &map);
714
715 return GST_FLOW_OK;
716
717 /* EXITS */
718 not_parsed:
719 {
720 GST_WARNING_OBJECT (avimux, "input mpeg audio is not parsed");
721 /* fall-through */
722 }
723 cbr_fallback:
724 {
725 GST_WARNING_OBJECT (avimux, "falling back to CBR muxing");
726 avipad->hdr.scale = 1;
727 gst_avi_mux_audsink_set_fields (avimux, (GstAviAudioPad *) avipad);
728 /* no need to check further */
729 avipad->hook = NULL;
730 goto done;
731 }
732 }
733
734 static void
gst_avi_mux_audsink_set_fields(GstAviMux * avimux,GstAviAudioPad * avipad)735 gst_avi_mux_audsink_set_fields (GstAviMux * avimux, GstAviAudioPad * avipad)
736 {
737 if (avipad->parent.hdr.scale > 1) {
738 /* vbr case: fixed duration per frame/chunk */
739 avipad->parent.hdr.rate = avipad->auds.rate;
740 avipad->parent.hdr.samplesize = 0;
741 /* this triggers determining largest audio chunk size to write at end */
742 avipad->max_audio_chunk = avipad->auds.blockalign =
743 avipad->parent.hdr.scale;
744 } else {
745 /* by spec, hdr.rate is av_bps related, is calculated that way in stop_file,
746 * and reduces to sample rate in PCM like cases */
747 avipad->parent.hdr.rate = avipad->auds.av_bps / avipad->auds.blockalign;
748 avipad->parent.hdr.samplesize = avipad->auds.blockalign;
749 avipad->parent.hdr.scale = 1;
750 }
751 }
752
753 /* Taken from wavenc */
754 static guint64
gstmask_to_wavmask(guint64 gstmask,GstAudioChannelPosition * pos)755 gstmask_to_wavmask (guint64 gstmask, GstAudioChannelPosition * pos)
756 {
757 const GstAudioChannelPosition valid_pos =
758 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT |
759 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT |
760 GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER |
761 GST_AUDIO_CHANNEL_POSITION_LFE1 |
762 GST_AUDIO_CHANNEL_POSITION_REAR_LEFT |
763 GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT |
764 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER |
765 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER |
766 GST_AUDIO_CHANNEL_POSITION_REAR_CENTER |
767 GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT |
768 GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT |
769 GST_AUDIO_CHANNEL_POSITION_TOP_CENTER |
770 GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT |
771 GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER |
772 GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT |
773 GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT |
774 GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER |
775 GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT;
776
777 const GstAudioChannelPosition wav_pos[] = {
778 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
779 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
780 GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
781 GST_AUDIO_CHANNEL_POSITION_LFE1,
782 GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
783 GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
784 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
785 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
786 GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
787 GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
788 GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
789 GST_AUDIO_CHANNEL_POSITION_TOP_CENTER,
790 GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT,
791 GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER,
792 GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT,
793 GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT,
794 GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER,
795 GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT,
796 };
797 int k;
798 int chan = 0;
799 guint64 ret = 0;
800 guint64 mask = 1;
801
802 if (gstmask == 0 || ((gstmask & ~valid_pos) != 0))
803 return 0;
804
805 for (k = 0; k < G_N_ELEMENTS (wav_pos); ++k) {
806 if (gstmask & (G_GUINT64_CONSTANT (1) << wav_pos[k])) {
807 ret |= mask;
808 pos[chan++] = wav_pos[k];
809 }
810 mask <<= 1;
811 }
812
813 return ret;
814 }
815
816 static gboolean
gst_avi_mux_audsink_set_caps(GstPad * pad,GstCaps * vscaps)817 gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps)
818 {
819 GstAviMux *avimux;
820 GstAviAudioPad *avipad;
821 GstAviCollectData *collect_pad;
822 GstStructure *structure;
823 const gchar *mimetype;
824 const GValue *codec_data;
825 gint channels, rate;
826
827 avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
828
829 /* find stream data */
830 collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad);
831 g_assert (collect_pad);
832 avipad = (GstAviAudioPad *) collect_pad->avipad;
833 g_assert (avipad);
834 g_assert (!avipad->parent.is_video);
835 g_assert (avipad->parent.hdr.type == GST_MAKE_FOURCC ('a', 'u', 'd', 's'));
836
837 GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
838 GST_DEBUG_PAD_NAME (pad), vscaps);
839
840 structure = gst_caps_get_structure (vscaps, 0);
841 mimetype = gst_structure_get_name (structure);
842
843 /* we want these for all */
844 if (!gst_structure_get_int (structure, "channels", &channels) ||
845 !gst_structure_get_int (structure, "rate", &rate)) {
846 goto refuse_caps;
847 }
848
849 avipad->auds.channels = channels;
850 avipad->auds.rate = rate;
851
852 /* codec initialization data, if any */
853 codec_data = gst_structure_get_value (structure, "codec_data");
854 if (codec_data) {
855 avipad->auds_codec_data = gst_value_get_buffer (codec_data);
856 gst_buffer_ref (avipad->auds_codec_data);
857 /* keep global track of size */
858 avimux->codec_data_size += gst_buffer_get_size (avipad->auds_codec_data);
859 }
860
861 if (!strcmp (mimetype, "audio/x-raw")) {
862 const gchar *format;
863 GstAudioFormat fmt;
864 guint64 channel_mask;
865
866 format = gst_structure_get_string (structure, "format");
867 fmt = gst_audio_format_from_string (format);
868
869 if (!gst_structure_get (structure, "channel-mask", GST_TYPE_BITMASK,
870 &channel_mask, NULL))
871 channel_mask = gst_audio_channel_get_fallback_mask (channels);
872
873 switch (fmt) {
874 case GST_AUDIO_FORMAT_U8:
875 avipad->auds.blockalign = 8;
876 avipad->auds.bits_per_sample = 8;
877 break;
878 case GST_AUDIO_FORMAT_S16LE:
879 avipad->auds.blockalign = 16;
880 avipad->auds.bits_per_sample = 16;
881 break;
882 case GST_AUDIO_FORMAT_S24LE:
883 avipad->auds.blockalign = 24;
884 avipad->auds.bits_per_sample = 24;
885 break;
886 case GST_AUDIO_FORMAT_S32LE:
887 avipad->auds.blockalign = 32;
888 avipad->auds.bits_per_sample = 32;
889 break;
890 default:
891 goto refuse_caps;
892 }
893
894 avipad->audio_format = fmt;
895
896 avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
897 /* set some more info straight */
898 avipad->auds.blockalign /= 8;
899 avipad->auds.blockalign *= avipad->auds.channels;
900 avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
901
902 if (channels > 2 || (channels == 1 && channel_mask != 0x0)
903 || (channels == 2 && channel_mask != 0x3)) {
904 avipad->write_waveformatex = TRUE;
905 /* The same for now as we don't support e.g. S24_32 */
906 avipad->valid_bits_per_sample = avipad->auds.bits_per_sample;
907 avipad->channel_mask =
908 gstmask_to_wavmask (channel_mask, avipad->wav_positions);
909
910 gst_audio_channel_positions_from_mask (channels, channel_mask,
911 avipad->gst_positions);
912 avipad->needs_reorder =
913 memcmp (avipad->gst_positions, avipad->wav_positions,
914 channels * sizeof (*avipad->gst_positions)) != 0;
915 }
916 } else {
917 avipad->auds.format = 0;
918 /* set some defaults */
919 avipad->auds.blockalign = 1;
920 avipad->auds.av_bps = 0;
921 avipad->auds.bits_per_sample = 16;
922
923 if (!strcmp (mimetype, "audio/mpeg")) {
924 gint mpegversion;
925
926 gst_structure_get_int (structure, "mpegversion", &mpegversion);
927 switch (mpegversion) {
928 case 1:{
929 gint layer = 3;
930 gboolean parsed = FALSE;
931
932 gst_structure_get_int (structure, "layer", &layer);
933 gst_structure_get_boolean (structure, "parsed", &parsed);
934 switch (layer) {
935 case 3:
936 avipad->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
937 break;
938 case 1:
939 case 2:
940 avipad->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
941 break;
942 }
943 if (parsed) {
944 /* treat as VBR, should also cover CBR case;
945 * setup hook to parse frame header and determine spf */
946 avipad->parent.hook = gst_avi_mux_audsink_scan_mpeg_audio;
947 } else {
948 GST_WARNING_OBJECT (avimux, "unparsed MPEG audio input (?), "
949 "doing CBR muxing");
950 }
951 break;
952 }
953 case 4:
954 {
955 GstBuffer *codec_data_buf = avipad->auds_codec_data;
956 const gchar *stream_format;
957 guint codec;
958 guint8 data[2];
959
960 stream_format = gst_structure_get_string (structure, "stream-format");
961 if (stream_format) {
962 if (strcmp (stream_format, "raw") != 0) {
963 GST_WARNING_OBJECT (avimux, "AAC's stream format '%s' is not "
964 "supported, please use 'raw'", stream_format);
965 break;
966 }
967 } else {
968 GST_WARNING_OBJECT (avimux, "AAC's stream-format not specified, "
969 "assuming 'raw'");
970 }
971
972 /* vbr case needs some special handling */
973 if (!codec_data_buf || gst_buffer_get_size (codec_data_buf) < 2) {
974 GST_WARNING_OBJECT (avimux, "no (valid) codec_data for AAC audio");
975 break;
976 }
977 avipad->auds.format = GST_RIFF_WAVE_FORMAT_AAC;
978 /* need to determine frame length */
979 gst_buffer_extract (codec_data_buf, 0, data, 2);
980 codec = GST_READ_UINT16_BE (data);
981 avipad->parent.hdr.scale = (codec & 0x4) ? 960 : 1024;
982 break;
983 }
984 }
985 } else if (!strcmp (mimetype, "audio/x-vorbis")) {
986 avipad->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
987 } else if (!strcmp (mimetype, "audio/x-ac3")) {
988 avipad->auds.format = GST_RIFF_WAVE_FORMAT_A52;
989 } else if (!strcmp (mimetype, "audio/x-alaw")) {
990 avipad->auds.format = GST_RIFF_WAVE_FORMAT_ALAW;
991 avipad->auds.bits_per_sample = 8;
992 avipad->auds.blockalign = avipad->auds.channels;
993 avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
994 } else if (!strcmp (mimetype, "audio/x-mulaw")) {
995 avipad->auds.format = GST_RIFF_WAVE_FORMAT_MULAW;
996 avipad->auds.bits_per_sample = 8;
997 avipad->auds.blockalign = avipad->auds.channels;
998 avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
999 } else if (!strcmp (mimetype, "audio/x-wma")) {
1000 gint version;
1001 gint bitrate;
1002 gint block_align;
1003
1004 if (gst_structure_get_int (structure, "wmaversion", &version)) {
1005 switch (version) {
1006 case 1:
1007 avipad->auds.format = GST_RIFF_WAVE_FORMAT_WMAV1;
1008 break;
1009 case 2:
1010 avipad->auds.format = GST_RIFF_WAVE_FORMAT_WMAV2;
1011 break;
1012 default:
1013 break;
1014 }
1015 }
1016
1017 if (avipad->auds.format != 0) {
1018 if (gst_structure_get_int (structure, "block_align", &block_align)) {
1019 avipad->auds.blockalign = block_align;
1020 }
1021 if (gst_structure_get_int (structure, "bitrate", &bitrate)) {
1022 avipad->auds.av_bps = bitrate / 8;
1023 }
1024 }
1025 }
1026 }
1027
1028 if (!avipad->auds.format)
1029 goto refuse_caps;
1030
1031 avipad->parent.hdr.fcc_handler = avipad->auds.format;
1032 gst_avi_mux_audsink_set_fields (avimux, avipad);
1033
1034 gst_object_unref (avimux);
1035 return TRUE;
1036
1037 refuse_caps:
1038 {
1039 GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
1040 gst_object_unref (avimux);
1041 return FALSE;
1042 }
1043 }
1044
1045
1046 static GstPad *
gst_avi_mux_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * req_name,const GstCaps * caps)1047 gst_avi_mux_request_new_pad (GstElement * element,
1048 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
1049 {
1050 GstAviMux *avimux;
1051 GstPad *newpad;
1052 GstAviPad *avipad;
1053 GstElementClass *klass;
1054 gchar *name = NULL;
1055 const gchar *pad_name = NULL;
1056 gint pad_id;
1057
1058 g_return_val_if_fail (templ != NULL, NULL);
1059
1060 if (templ->direction != GST_PAD_SINK)
1061 goto wrong_direction;
1062
1063 g_return_val_if_fail (GST_IS_AVI_MUX (element), NULL);
1064 avimux = GST_AVI_MUX (element);
1065
1066 if (!avimux->write_header)
1067 goto too_late;
1068
1069 klass = GST_ELEMENT_GET_CLASS (element);
1070
1071 if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
1072 /* don't mix named and unnamed pads, if the pad already exists we fail when
1073 * trying to add it */
1074 if (req_name != NULL && sscanf (req_name, "audio_%u", &pad_id) == 1) {
1075 pad_name = req_name;
1076 } else {
1077 name = g_strdup_printf ("audio_%u", avimux->audio_pads++);
1078 pad_name = name;
1079 }
1080
1081 /* init pad specific data */
1082 avipad = g_malloc0 (sizeof (GstAviAudioPad));
1083 avipad->is_video = FALSE;
1084 avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
1085 /* audio goes last */
1086 avimux->sinkpads = g_slist_append (avimux->sinkpads, avipad);
1087 } else if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
1088 /* though streams are pretty generic and relatively self-contained,
1089 * some video info goes in a single avi header -and therefore mux struct-
1090 * so video restricted to one stream */
1091 if (avimux->video_pads > 0)
1092 goto too_many_video_pads;
1093
1094 /* setup pad */
1095 pad_name = "video_0";
1096 avimux->video_pads++;
1097
1098 /* init pad specific data */
1099 avipad = g_malloc0 (sizeof (GstAviVideoPad));
1100 avipad->is_video = TRUE;
1101 avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
1102 /* video goes first */
1103 avimux->sinkpads = g_slist_prepend (avimux->sinkpads, avipad);
1104 } else
1105 goto wrong_template;
1106
1107 newpad = gst_pad_new_from_template (templ, pad_name);
1108
1109 avipad->collect = gst_collect_pads_add_pad (avimux->collect,
1110 newpad, sizeof (GstAviCollectData), NULL, TRUE);
1111 ((GstAviCollectData *) (avipad->collect))->avipad = avipad;
1112
1113 if (!gst_element_add_pad (element, newpad))
1114 goto pad_add_failed;
1115
1116 g_free (name);
1117
1118 GST_DEBUG_OBJECT (newpad, "Added new request pad");
1119
1120 return newpad;
1121
1122 /* ERRORS */
1123 wrong_direction:
1124 {
1125 g_warning ("avimux: request pad that is not a SINK pad\n");
1126 return NULL;
1127 }
1128 too_late:
1129 {
1130 g_warning ("avimux: request pad cannot be added after streaming started\n");
1131 return NULL;
1132 }
1133 wrong_template:
1134 {
1135 g_warning ("avimux: this is not our template!\n");
1136 return NULL;
1137 }
1138 too_many_video_pads:
1139 {
1140 GST_WARNING_OBJECT (avimux, "Can only have one video stream");
1141 return NULL;
1142 }
1143 pad_add_failed:
1144 {
1145 GST_WARNING_OBJECT (avimux, "Adding the new pad '%s' failed", pad_name);
1146 g_free (name);
1147 gst_object_unref (newpad);
1148 return NULL;
1149 }
1150 }
1151
1152 static void
gst_avi_mux_release_pad(GstElement * element,GstPad * pad)1153 gst_avi_mux_release_pad (GstElement * element, GstPad * pad)
1154 {
1155 GstAviMux *avimux = GST_AVI_MUX (element);
1156 GSList *node;
1157
1158 node = avimux->sinkpads;
1159 while (node) {
1160 GstAviPad *avipad = (GstAviPad *) node->data;
1161
1162 if (avipad->collect->pad == pad) {
1163 /* pad count should not be adjusted,
1164 * as it also represent number of streams present */
1165 avipad->collect = NULL;
1166 GST_DEBUG_OBJECT (avimux, "removed pad '%s'", GST_PAD_NAME (pad));
1167 gst_collect_pads_remove_pad (avimux->collect, pad);
1168 gst_element_remove_pad (element, pad);
1169 /* if not started yet, we can remove any sign this pad ever existed */
1170 /* in this case _start will take care of the real pad count */
1171 if (avimux->write_header) {
1172 avimux->sinkpads = g_slist_remove (avimux->sinkpads, avipad);
1173 gst_avi_mux_pad_reset (avipad, TRUE);
1174 g_free (avipad);
1175 }
1176 return;
1177 }
1178
1179 node = node->next;
1180 }
1181
1182 g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
1183 }
1184
1185 static inline guint
gst_avi_mux_start_chunk(GstByteWriter * bw,const gchar * tag,guint32 fourcc)1186 gst_avi_mux_start_chunk (GstByteWriter * bw, const gchar * tag, guint32 fourcc)
1187 {
1188 guint chunk_offset;
1189
1190 if (tag)
1191 gst_byte_writer_put_data (bw, (const guint8 *) tag, 4);
1192 else
1193 gst_byte_writer_put_uint32_le (bw, fourcc);
1194
1195 chunk_offset = gst_byte_writer_get_pos (bw);
1196 /* real chunk size comes later */
1197 gst_byte_writer_put_uint32_le (bw, 0);
1198
1199 return chunk_offset;
1200 }
1201
1202 static inline void
gst_avi_mux_end_chunk(GstByteWriter * bw,guint chunk_offset)1203 gst_avi_mux_end_chunk (GstByteWriter * bw, guint chunk_offset)
1204 {
1205 guint size;
1206
1207 size = gst_byte_writer_get_pos (bw);
1208
1209 gst_byte_writer_set_pos (bw, chunk_offset);
1210 gst_byte_writer_put_uint32_le (bw, size - chunk_offset - 4);
1211 gst_byte_writer_set_pos (bw, size);
1212
1213 /* arrange for even padding */
1214 if (size & 1)
1215 gst_byte_writer_put_uint8 (bw, 0);
1216 }
1217
1218 /* maybe some of these functions should be moved to riff.h? */
1219
1220 static void
gst_avi_mux_write_tag(const GstTagList * list,const gchar * tag,gpointer data)1221 gst_avi_mux_write_tag (const GstTagList * list, const gchar * tag,
1222 gpointer data)
1223 {
1224 const struct
1225 {
1226 guint32 fcc;
1227 const gchar *tag;
1228 } rifftags[] = {
1229 {
1230 GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
1231 GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
1232 GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
1233 GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
1234 GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
1235 GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
1236 GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
1237 GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
1238 GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
1239 GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
1240 0, NULL}
1241 };
1242 gint n;
1243 gchar *str = NULL;
1244 GstByteWriter *bw = data;
1245 guint chunk;
1246
1247 for (n = 0; rifftags[n].fcc != 0; n++) {
1248 if (!strcmp (rifftags[n].tag, tag)) {
1249 if (rifftags[n].fcc == GST_RIFF_INFO_ICRD) {
1250 GDate *date;
1251 /* special case for the date tag */
1252 if (gst_tag_list_get_date (list, tag, &date)) {
1253 str =
1254 g_strdup_printf ("%04d:%02d:%02d", g_date_get_year (date),
1255 g_date_get_month (date), g_date_get_day (date));
1256 g_date_free (date);
1257 }
1258 } else {
1259 gst_tag_list_get_string (list, tag, &str);
1260 }
1261 if (str) {
1262 chunk = gst_avi_mux_start_chunk (bw, NULL, rifftags[n].fcc);
1263 gst_byte_writer_put_string (bw, str);
1264 gst_avi_mux_end_chunk (bw, chunk);
1265 g_free (str);
1266 str = NULL;
1267 break;
1268 }
1269 }
1270 }
1271 }
1272
1273 static GstBuffer *
gst_avi_mux_riff_get_avi_header(GstAviMux * avimux)1274 gst_avi_mux_riff_get_avi_header (GstAviMux * avimux)
1275 {
1276 const GstTagList *tags;
1277 GstBuffer *buffer = NULL;
1278 gint size = 0;
1279 GstByteWriter bw;
1280 GSList *node;
1281 guint avih, riff, hdrl;
1282 GstMapInfo map;
1283 gboolean hdl = TRUE;
1284
1285 GST_DEBUG_OBJECT (avimux, "creating avi header, data_size %u, idx_size %u",
1286 avimux->data_size, avimux->idx_size);
1287
1288 if (avimux->tags_snap)
1289 tags = avimux->tags_snap;
1290 else {
1291 /* need to make snapshot of current state of tags to ensure the same set
1292 * is used next time around during header rewrite at the end */
1293 tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (avimux));
1294 if (tags)
1295 tags = avimux->tags_snap = gst_tag_list_copy (tags);
1296 }
1297
1298 gst_byte_writer_init_with_size (&bw, 1024, FALSE);
1299
1300 /* avi header metadata */
1301 riff = gst_avi_mux_start_chunk (&bw, "RIFF", 0);
1302 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "AVI ", 4);
1303 hdrl = gst_avi_mux_start_chunk (&bw, "LIST", 0);
1304 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "hdrl", 4);
1305
1306 avih = gst_avi_mux_start_chunk (&bw, "avih", 0);
1307 /* the AVI header itself */
1308 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.us_frame);
1309 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.max_bps);
1310 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.pad_gran);
1311 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.flags);
1312 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.tot_frames);
1313 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.init_frames);
1314 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.streams);
1315 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.bufsize);
1316 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.width);
1317 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.height);
1318 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.scale);
1319 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.rate);
1320 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.start);
1321 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->avi_hdr.length);
1322 gst_avi_mux_end_chunk (&bw, avih);
1323
1324 /* stream data */
1325 node = avimux->sinkpads;
1326 while (node) {
1327 GstAviPad *avipad = (GstAviPad *) node->data;
1328 GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
1329 GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
1330 gint codec_size = 0;
1331 guint strh, strl, strf, indx;
1332
1333 /* stream list metadata */
1334 strl = gst_avi_mux_start_chunk (&bw, "LIST", 0);
1335 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "strl", 4);
1336
1337 /* generic header */
1338 strh = gst_avi_mux_start_chunk (&bw, "strh", 0);
1339 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.type);
1340 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.fcc_handler);
1341 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.flags);
1342 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.priority);
1343 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.init_frames);
1344 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.scale);
1345 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.rate);
1346 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.start);
1347 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.length);
1348 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.bufsize);
1349 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.quality);
1350 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->hdr.samplesize);
1351 hdl &= gst_byte_writer_put_uint16_le (&bw, 0);
1352 hdl &= gst_byte_writer_put_uint16_le (&bw, 0);
1353 hdl &= gst_byte_writer_put_uint16_le (&bw, 0);
1354 hdl &= gst_byte_writer_put_uint16_le (&bw, 0);
1355 gst_avi_mux_end_chunk (&bw, strh);
1356
1357 if (avipad->is_video) {
1358 codec_size = vidpad->vids_codec_data ?
1359 gst_buffer_get_size (vidpad->vids_codec_data) : 0;
1360 /* the video header */
1361 strf = gst_avi_mux_start_chunk (&bw, "strf", 0);
1362 /* the actual header */
1363 hdl &=
1364 gst_byte_writer_put_uint32_le (&bw, vidpad->vids.size + codec_size);
1365 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.width);
1366 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.height);
1367 hdl &= gst_byte_writer_put_uint16_le (&bw, vidpad->vids.planes);
1368 hdl &= gst_byte_writer_put_uint16_le (&bw, vidpad->vids.bit_cnt);
1369 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.compression);
1370 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.image_size);
1371 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.xpels_meter);
1372 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.ypels_meter);
1373 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.num_colors);
1374 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vids.imp_colors);
1375 if (vidpad->vids_codec_data) {
1376 gst_buffer_map (vidpad->vids_codec_data, &map, GST_MAP_READ);
1377 hdl &= gst_byte_writer_put_data (&bw, map.data, map.size);
1378 gst_buffer_unmap (vidpad->vids_codec_data, &map);
1379 }
1380 gst_avi_mux_end_chunk (&bw, strf);
1381
1382 /* add video property data, mainly for aspect ratio, if any */
1383 if (vidpad->vprp.aspect) {
1384 gint f;
1385 guint vprp;
1386
1387 /* let's be on the safe side */
1388 vidpad->vprp.fields = MIN (vidpad->vprp.fields,
1389 GST_RIFF_VPRP_VIDEO_FIELDS);
1390 /* the vprp header */
1391 vprp = gst_avi_mux_start_chunk (&bw, "vprp", 0);
1392 /* the actual data */
1393 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.format_token);
1394 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.standard);
1395 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.vert_rate);
1396 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.hor_t_total);
1397 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.vert_lines);
1398 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.aspect);
1399 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.width);
1400 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.height);
1401 hdl &= gst_byte_writer_put_uint32_le (&bw, vidpad->vprp.fields);
1402
1403 for (f = 0; f < vidpad->vprp.fields; ++f) {
1404 gst_riff_vprp_video_field_desc *fd;
1405
1406 fd = &(vidpad->vprp.field_info[f]);
1407 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->compressed_bm_height);
1408 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->compressed_bm_width);
1409 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->valid_bm_height);
1410 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->valid_bm_width);
1411 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->valid_bm_x_offset);
1412 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->valid_bm_y_offset);
1413 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->video_x_t_offset);
1414 hdl &= gst_byte_writer_put_uint32_le (&bw, fd->video_y_start);
1415 }
1416 gst_avi_mux_end_chunk (&bw, vprp);
1417 }
1418 } else {
1419 codec_size = audpad->auds_codec_data ?
1420 gst_buffer_get_size (audpad->auds_codec_data) : 0;
1421 /* the audio header */
1422 strf = gst_avi_mux_start_chunk (&bw, "strf", 0);
1423 /* the actual header */
1424 if (audpad->write_waveformatex)
1425 hdl &= gst_byte_writer_put_uint16_le (&bw, 0xfffe);
1426 else
1427 hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.format);
1428 hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.channels);
1429 hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->auds.rate);
1430 hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->auds.av_bps);
1431 hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.blockalign);
1432 hdl &= gst_byte_writer_put_uint16_le (&bw, audpad->auds.bits_per_sample);
1433 if (audpad->write_waveformatex) {
1434 hdl &= gst_byte_writer_put_uint16_le (&bw, codec_size + 22);
1435 hdl &=
1436 gst_byte_writer_put_uint16_le (&bw, audpad->valid_bits_per_sample);
1437 hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->channel_mask);
1438 hdl &= gst_byte_writer_put_uint32_le (&bw, audpad->auds.format);
1439 hdl &= gst_byte_writer_put_uint32_le (&bw, 0x00100000);
1440 hdl &= gst_byte_writer_put_uint32_le (&bw, 0xAA000080);
1441 hdl &= gst_byte_writer_put_uint32_le (&bw, 0x719B3800);
1442 } else {
1443 hdl &= gst_byte_writer_put_uint16_le (&bw, codec_size);
1444 }
1445 if (audpad->auds_codec_data) {
1446 gst_buffer_map (audpad->auds_codec_data, &map, GST_MAP_READ);
1447 hdl &= gst_byte_writer_put_data (&bw, map.data, map.size);
1448 gst_buffer_unmap (audpad->auds_codec_data, &map);
1449 }
1450 gst_avi_mux_end_chunk (&bw, strf);
1451 }
1452
1453 /* odml superindex chunk */
1454 if (avipad->idx_index > 0)
1455 indx = gst_avi_mux_start_chunk (&bw, "indx", 0);
1456 else
1457 indx = gst_avi_mux_start_chunk (&bw, "JUNK", 0);
1458 hdl &= gst_byte_writer_put_uint16_le (&bw, 4); /* bytes per entry */
1459 hdl &= gst_byte_writer_put_uint8 (&bw, 0); /* index subtype */
1460 hdl &= gst_byte_writer_put_uint8 (&bw, GST_AVI_INDEX_OF_INDEXES); /* index type */
1461 hdl &= gst_byte_writer_put_uint32_le (&bw, avipad->idx_index); /* entries in use */
1462 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) avipad->tag, 4); /* stream id */
1463 hdl &= gst_byte_writer_put_uint32_le (&bw, 0); /* reserved */
1464 hdl &= gst_byte_writer_put_uint32_le (&bw, 0); /* reserved */
1465 hdl &= gst_byte_writer_put_uint32_le (&bw, 0); /* reserved */
1466 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) avipad->idx,
1467 GST_AVI_SUPERINDEX_COUNT * sizeof (gst_avi_superindex_entry));
1468 gst_avi_mux_end_chunk (&bw, indx);
1469
1470 /* end strl for this stream */
1471 gst_avi_mux_end_chunk (&bw, strl);
1472
1473 node = node->next;
1474 }
1475
1476 if (avimux->video_pads > 0) {
1477 guint odml, dmlh;
1478 /* odml header */
1479 odml = gst_avi_mux_start_chunk (&bw, "LIST", 0);
1480 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "odml", 4);
1481 dmlh = gst_avi_mux_start_chunk (&bw, "dmlh", 0);
1482 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->total_frames);
1483 gst_avi_mux_end_chunk (&bw, dmlh);
1484 gst_avi_mux_end_chunk (&bw, odml);
1485 }
1486
1487 /* end hdrl */
1488 gst_avi_mux_end_chunk (&bw, hdrl);
1489
1490 /* tags */
1491 if (tags) {
1492 guint info;
1493
1494 info = gst_avi_mux_start_chunk (&bw, "LIST", 0);
1495 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "INFO", 4);
1496
1497 gst_tag_list_foreach (tags, gst_avi_mux_write_tag, &bw);
1498 if (info + 8 == gst_byte_writer_get_pos (&bw)) {
1499 /* no tags written, remove the empty INFO LIST as it is useless
1500 * and prevents playback in vlc */
1501 gst_byte_writer_set_pos (&bw, info - 4);
1502 } else {
1503 gst_avi_mux_end_chunk (&bw, info);
1504 }
1505 }
1506
1507 /* pop RIFF */
1508 gst_avi_mux_end_chunk (&bw, riff);
1509
1510 /* avi data header */
1511 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "LIST", 4);
1512 hdl &= gst_byte_writer_put_uint32_le (&bw, avimux->data_size);
1513 hdl &= gst_byte_writer_put_data (&bw, (guint8 *) "movi", 4);
1514
1515 if (!hdl)
1516 goto beach;
1517
1518 /* now get the data */
1519 buffer = gst_byte_writer_reset_and_get_buffer (&bw);
1520
1521 /* ... but RIFF includes more than just header */
1522 gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
1523 size = GST_READ_UINT32_LE (map.data + 4);
1524 size += 8 + avimux->data_size + avimux->idx_size;
1525 GST_WRITE_UINT32_LE (map.data + 4, size);
1526
1527 GST_MEMDUMP_OBJECT (avimux, "avi header", map.data, map.size);
1528 gst_buffer_unmap (buffer, &map);
1529
1530 beach:
1531 return buffer;
1532 }
1533
1534 static GstBuffer *
gst_avi_mux_riff_get_avix_header(guint32 datax_size)1535 gst_avi_mux_riff_get_avix_header (guint32 datax_size)
1536 {
1537 GstBuffer *buffer;
1538 GstMapInfo map;
1539
1540 buffer = gst_buffer_new_and_alloc (24);
1541
1542 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
1543 memcpy (map.data + 0, "RIFF", 4);
1544 GST_WRITE_UINT32_LE (map.data + 4, datax_size + 3 * 4);
1545 memcpy (map.data + 8, "AVIX", 4);
1546 memcpy (map.data + 12, "LIST", 4);
1547 GST_WRITE_UINT32_LE (map.data + 16, datax_size);
1548 memcpy (map.data + 20, "movi", 4);
1549 gst_buffer_unmap (buffer, &map);
1550
1551 return buffer;
1552 }
1553
1554 static inline GstBuffer *
gst_avi_mux_riff_get_header(GstAviPad * avipad,guint32 video_frame_size)1555 gst_avi_mux_riff_get_header (GstAviPad * avipad, guint32 video_frame_size)
1556 {
1557 GstBuffer *buffer;
1558 GstMapInfo map;
1559
1560 buffer = gst_buffer_new_and_alloc (8);
1561
1562 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
1563 memcpy (map.data + 0, avipad->tag, 4);
1564 GST_WRITE_UINT32_LE (map.data + 4, video_frame_size);
1565 gst_buffer_unmap (buffer, &map);
1566
1567 return buffer;
1568 }
1569
1570 /* write an odml index chunk in the movi list */
1571 static GstFlowReturn
gst_avi_mux_write_avix_index(GstAviMux * avimux,GstAviPad * avipad,gchar * code,gchar * chunk,gst_avi_superindex_entry * super_index,gint * super_index_count)1572 gst_avi_mux_write_avix_index (GstAviMux * avimux, GstAviPad * avipad,
1573 gchar * code, gchar * chunk, gst_avi_superindex_entry * super_index,
1574 gint * super_index_count)
1575 {
1576 GstFlowReturn res;
1577 GstBuffer *buffer;
1578 guint8 *data;
1579 gst_riff_index_entry *entry;
1580 gint i;
1581 guint32 size, entry_count;
1582 gboolean is_pcm = FALSE;
1583 guint32 pcm_samples = 0;
1584 GstMapInfo map;
1585
1586 /* check if it is pcm */
1587 if (avipad && !avipad->is_video) {
1588 GstAviAudioPad *audiopad = (GstAviAudioPad *) avipad;
1589 if (audiopad->auds.format == GST_RIFF_WAVE_FORMAT_PCM) {
1590 pcm_samples = audiopad->samples;
1591 is_pcm = TRUE;
1592 }
1593 }
1594
1595 /* allocate the maximum possible */
1596 buffer = gst_buffer_new_and_alloc (32 + 8 * avimux->idx_index);
1597
1598 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
1599 data = map.data;
1600
1601 /* general index chunk info */
1602 memcpy (map.data + 0, chunk, 4); /* chunk id */
1603 GST_WRITE_UINT32_LE (map.data + 4, 0); /* chunk size; fill later */
1604 GST_WRITE_UINT16_LE (map.data + 8, 2); /* index entry is 2 words */
1605 map.data[10] = 0; /* index subtype */
1606 map.data[11] = GST_AVI_INDEX_OF_CHUNKS; /* index type: AVI_INDEX_OF_CHUNKS */
1607 GST_WRITE_UINT32_LE (map.data + 12, 0); /* entries in use; fill later */
1608 memcpy (map.data + 16, code, 4); /* stream to which index refers */
1609 GST_WRITE_UINT64_LE (map.data + 20, avimux->avix_start); /* base offset */
1610 GST_WRITE_UINT32_LE (map.data + 28, 0); /* reserved */
1611 map.data += 32;
1612
1613 /* now the actual index entries */
1614 i = avimux->idx_index;
1615 entry = avimux->idx;
1616 while (i > 0) {
1617 if (memcmp (&entry->id, code, 4) == 0) {
1618 /* enter relative offset to the data (!) */
1619 GST_WRITE_UINT32_LE (map.data, GUINT32_FROM_LE (entry->offset) + 8);
1620 /* msb is set if not (!) keyframe */
1621 GST_WRITE_UINT32_LE (map.data + 4, GUINT32_FROM_LE (entry->size)
1622 | (GUINT32_FROM_LE (entry->flags)
1623 & GST_RIFF_IF_KEYFRAME ? 0 : 1U << 31));
1624 map.data += 8;
1625 }
1626 i--;
1627 entry++;
1628 }
1629
1630 /* ok, now we know the size and no of entries, fill in where needed */
1631 size = map.data - data;
1632 GST_WRITE_UINT32_LE (data + 4, size - 8);
1633 entry_count = (size - 32) / 8;
1634 GST_WRITE_UINT32_LE (data + 12, entry_count);
1635 gst_buffer_unmap (buffer, &map);
1636 gst_buffer_resize (buffer, 0, size);
1637
1638 /* send */
1639 if ((res = gst_pad_push (avimux->srcpad, buffer)) != GST_FLOW_OK)
1640 return res;
1641
1642 /* keep track of this in superindex (if room) ... */
1643 if (*super_index_count < GST_AVI_SUPERINDEX_COUNT) {
1644 i = *super_index_count;
1645 super_index[i].offset = GUINT64_TO_LE (avimux->total_data);
1646 super_index[i].size = GUINT32_TO_LE (size);
1647 if (is_pcm) {
1648 super_index[i].duration = GUINT32_TO_LE (pcm_samples);
1649 } else {
1650 super_index[i].duration = GUINT32_TO_LE (entry_count);
1651 }
1652 (*super_index_count)++;
1653 } else
1654 GST_WARNING_OBJECT (avimux, "No more room in superindex of stream %s",
1655 code);
1656
1657 /* ... and in size */
1658 avimux->total_data += size;
1659 if (avimux->is_bigfile)
1660 avimux->datax_size += size;
1661 else
1662 avimux->data_size += size;
1663
1664 return GST_FLOW_OK;
1665 }
1666
1667 /* some other usable functions (thankyou xawtv ;-) ) */
1668
1669 static void
gst_avi_mux_add_index(GstAviMux * avimux,GstAviPad * avipad,guint32 flags,guint32 size)1670 gst_avi_mux_add_index (GstAviMux * avimux, GstAviPad * avipad, guint32 flags,
1671 guint32 size)
1672 {
1673 gchar *code = avipad->tag;
1674 if (avimux->idx_index == avimux->idx_count) {
1675 avimux->idx_count += 256;
1676 avimux->idx =
1677 g_realloc (avimux->idx,
1678 avimux->idx_count * sizeof (gst_riff_index_entry));
1679 }
1680
1681 /* in case of pcm audio, we need to count the number of samples for
1682 * putting in the indx entries */
1683 if (!avipad->is_video) {
1684 GstAviAudioPad *audiopad = (GstAviAudioPad *) avipad;
1685 if (audiopad->auds.format == GST_RIFF_WAVE_FORMAT_PCM) {
1686 audiopad->samples += size / audiopad->auds.blockalign;
1687 }
1688 }
1689
1690 memcpy (&(avimux->idx[avimux->idx_index].id), code, 4);
1691 avimux->idx[avimux->idx_index].flags = GUINT32_TO_LE (flags);
1692 avimux->idx[avimux->idx_index].offset = GUINT32_TO_LE (avimux->idx_offset);
1693 avimux->idx[avimux->idx_index].size = GUINT32_TO_LE (size);
1694 avimux->idx_index++;
1695 }
1696
1697 static GstFlowReturn
gst_avi_mux_write_index(GstAviMux * avimux)1698 gst_avi_mux_write_index (GstAviMux * avimux)
1699 {
1700 GstFlowReturn res;
1701 GstBuffer *buffer;
1702 GstMapInfo map;
1703 guint8 *data;
1704 gsize size;
1705
1706 buffer = gst_buffer_new_and_alloc (8);
1707
1708 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
1709 memcpy (map.data + 0, "idx1", 4);
1710 GST_WRITE_UINT32_LE (map.data + 4,
1711 avimux->idx_index * sizeof (gst_riff_index_entry));
1712 gst_buffer_unmap (buffer, &map);
1713
1714 res = gst_pad_push (avimux->srcpad, buffer);
1715 if (res != GST_FLOW_OK)
1716 return res;
1717
1718 buffer = gst_buffer_new ();
1719
1720 size = avimux->idx_index * sizeof (gst_riff_index_entry);
1721 data = (guint8 *) avimux->idx;
1722 avimux->idx = NULL; /* will be free()'ed by gst_buffer_unref() */
1723
1724 gst_buffer_append_memory (buffer,
1725 gst_memory_new_wrapped (0, data, size, 0, size, data, g_free));
1726
1727 avimux->total_data += size + 8;
1728
1729 res = gst_pad_push (avimux->srcpad, buffer);
1730 if (res != GST_FLOW_OK)
1731 return res;
1732
1733 avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8;
1734
1735 /* update header */
1736 avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
1737 return GST_FLOW_OK;
1738 }
1739
1740 static GstFlowReturn
gst_avi_mux_bigfile(GstAviMux * avimux,gboolean last)1741 gst_avi_mux_bigfile (GstAviMux * avimux, gboolean last)
1742 {
1743 GstFlowReturn res = GST_FLOW_OK;
1744 GstBuffer *header;
1745 GSList *node;
1746
1747 /* first some odml standard index chunks in the movi list */
1748 node = avimux->sinkpads;
1749 while (node) {
1750 GstAviPad *avipad = (GstAviPad *) node->data;
1751
1752 node = node->next;
1753
1754 res = gst_avi_mux_write_avix_index (avimux, avipad, avipad->tag,
1755 avipad->idx_tag, avipad->idx, &avipad->idx_index);
1756 if (res != GST_FLOW_OK)
1757 return res;
1758 }
1759
1760 if (avimux->is_bigfile) {
1761 GstSegment segment;
1762
1763 gst_segment_init (&segment, GST_FORMAT_BYTES);
1764
1765 /* search back */
1766 segment.start = avimux->avix_start;
1767 segment.time = avimux->avix_start;
1768 gst_pad_push_event (avimux->srcpad, gst_event_new_segment (&segment));
1769
1770 /* rewrite AVIX header */
1771 header = gst_avi_mux_riff_get_avix_header (avimux->datax_size);
1772 res = gst_pad_push (avimux->srcpad, header);
1773
1774 /* go back to current location, at least try */
1775 segment.start = avimux->total_data;
1776 segment.time = avimux->total_data;
1777 gst_pad_push_event (avimux->srcpad, gst_event_new_segment (&segment));
1778
1779 if (res != GST_FLOW_OK)
1780 return res;
1781 } else { /* write a standard index in the first riff chunk */
1782 res = gst_avi_mux_write_index (avimux);
1783 /* the index data/buffer is freed by pushing it */
1784 avimux->idx_count = 0;
1785 if (res != GST_FLOW_OK)
1786 return res;
1787 }
1788
1789 avimux->avix_start = avimux->total_data;
1790
1791 if (last)
1792 return res;
1793
1794 avimux->is_bigfile = TRUE;
1795 avimux->numx_frames = 0;
1796 avimux->datax_size = 4; /* movi tag */
1797 avimux->idx_index = 0;
1798 node = avimux->sinkpads;
1799 while (node) {
1800 GstAviPad *avipad = (GstAviPad *) node->data;
1801 node = node->next;
1802 if (!avipad->is_video) {
1803 GstAviAudioPad *audiopad = (GstAviAudioPad *) avipad;
1804 audiopad->samples = 0;
1805 }
1806 }
1807
1808 header = gst_avi_mux_riff_get_avix_header (0);
1809 avimux->total_data += gst_buffer_get_size (header);
1810 /* avix_start is used as base offset for the odml index chunk */
1811 avimux->idx_offset = avimux->total_data - avimux->avix_start;
1812
1813 return gst_pad_push (avimux->srcpad, header);
1814 }
1815
1816 /* enough header blabla now, let's go on to actually writing the headers */
1817
1818 static GstFlowReturn
gst_avi_mux_start_file(GstAviMux * avimux)1819 gst_avi_mux_start_file (GstAviMux * avimux)
1820 {
1821 GstFlowReturn res;
1822 GstBuffer *header;
1823 GSList *node;
1824 GstCaps *caps;
1825 GstSegment segment;
1826
1827 avimux->total_data = 0;
1828 avimux->total_frames = 0;
1829 avimux->data_size = 4; /* movi tag */
1830 avimux->datax_size = 0;
1831 avimux->num_frames = 0;
1832 avimux->numx_frames = 0;
1833 avimux->avix_start = 0;
1834
1835 avimux->idx_index = 0;
1836 avimux->idx_offset = 0; /* see 10 lines below */
1837 avimux->idx_size = 0;
1838 avimux->idx_count = 0;
1839 avimux->idx = NULL;
1840
1841 /* state */
1842 avimux->write_header = FALSE;
1843 avimux->restart = FALSE;
1844
1845 /* init streams, see what we've got */
1846 node = avimux->sinkpads;
1847 avimux->audio_pads = avimux->video_pads = 0;
1848 while (node) {
1849 GstAviPad *avipad = (GstAviPad *) node->data;
1850
1851 node = node->next;
1852
1853 if (!avipad->is_video) {
1854 /* audio stream numbers must start at 1 iff there is a video stream 0;
1855 * request_pad inserts video pad at head of list, so this test suffices */
1856 if (avimux->video_pads)
1857 avimux->audio_pads++;
1858 avipad->tag = g_strdup_printf ("%02uwb", avimux->audio_pads);
1859 avipad->idx_tag = g_strdup_printf ("ix%02u", avimux->audio_pads);
1860 if (!avimux->video_pads)
1861 avimux->audio_pads++;
1862 } else {
1863 avipad->tag = g_strdup_printf ("%02udb", avimux->video_pads);
1864 avipad->idx_tag = g_strdup_printf ("ix%02u", avimux->video_pads++);
1865 }
1866 }
1867
1868 /* stream-start (FIXME: create id based on input ids) */
1869 {
1870 gchar s_id[32];
1871
1872 g_snprintf (s_id, sizeof (s_id), "avimux-%08x", g_random_int ());
1873 gst_pad_push_event (avimux->srcpad, gst_event_new_stream_start (s_id));
1874 }
1875
1876 caps = gst_pad_get_pad_template_caps (avimux->srcpad);
1877 gst_pad_set_caps (avimux->srcpad, caps);
1878 gst_caps_unref (caps);
1879
1880 /* let downstream know we think in BYTES and expect to do seeking later on */
1881 gst_segment_init (&segment, GST_FORMAT_BYTES);
1882 gst_pad_push_event (avimux->srcpad, gst_event_new_segment (&segment));
1883
1884 /* header */
1885 avimux->avi_hdr.streams = g_slist_length (avimux->sinkpads);
1886 avimux->is_bigfile = FALSE;
1887
1888 header = gst_avi_mux_riff_get_avi_header (avimux);
1889 avimux->total_data += gst_buffer_get_size (header);
1890
1891 res = gst_pad_push (avimux->srcpad, header);
1892
1893 avimux->idx_offset = avimux->total_data;
1894
1895 return res;
1896 }
1897
1898 static GstFlowReturn
gst_avi_mux_stop_file(GstAviMux * avimux)1899 gst_avi_mux_stop_file (GstAviMux * avimux)
1900 {
1901 GstFlowReturn res = GST_FLOW_OK;
1902 GstBuffer *header;
1903 GSList *node;
1904 GstSegment segment;
1905
1906 /* Do not write index and header, if the index has no data */
1907 if (avimux->idx == NULL)
1908 return GST_FLOW_OK;
1909
1910 /* if bigfile, rewrite header, else write indexes */
1911 /* don't bail out at once if error, still try to re-write header */
1912 if (avimux->video_pads > 0) {
1913 if (avimux->is_bigfile) {
1914 res = gst_avi_mux_bigfile (avimux, TRUE);
1915 } else {
1916 res = gst_avi_mux_write_index (avimux);
1917 }
1918 }
1919
1920 /* we do our best to make it interleaved at least ... */
1921 if (avimux->audio_pads > 0 && avimux->video_pads > 0)
1922 avimux->avi_hdr.flags |= GST_RIFF_AVIH_ISINTERLEAVED;
1923
1924 /* set rate and everything having to do with that */
1925 avimux->avi_hdr.max_bps = 0;
1926 node = avimux->sinkpads;
1927 while (node) {
1928 GstAviPad *avipad = (GstAviPad *) node->data;
1929
1930 node = node->next;
1931
1932 if (!avipad->is_video) {
1933 GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
1934
1935 /* calculate bps if needed */
1936 if (!audpad->auds.av_bps) {
1937 if (audpad->audio_time) {
1938 audpad->auds.av_bps =
1939 (GST_SECOND * audpad->audio_size) / audpad->audio_time;
1940 /* round bps to nearest multiple of 8;
1941 * which is much more likely to be the (cbr) bitrate in use;
1942 * which in turn results in better timestamp calculation on playback */
1943 audpad->auds.av_bps = GST_ROUND_UP_8 (audpad->auds.av_bps - 4);
1944 } else {
1945 GST_ELEMENT_WARNING (avimux, STREAM, MUX,
1946 (_("No or invalid input audio, AVI stream will be corrupt.")),
1947 (NULL));
1948 audpad->auds.av_bps = 0;
1949 }
1950 }
1951 /* housekeeping for vbr case */
1952 if (audpad->max_audio_chunk)
1953 audpad->auds.blockalign = audpad->max_audio_chunk;
1954 if (audpad->auds.blockalign == 0)
1955 audpad->auds.blockalign = 1;
1956 /* note that hdr.rate is actually used by demux in cbr case */
1957 if (avipad->hdr.scale <= 1)
1958 avipad->hdr.rate = audpad->auds.av_bps / audpad->auds.blockalign;
1959 avimux->avi_hdr.max_bps += audpad->auds.av_bps;
1960 avipad->hdr.length = gst_util_uint64_scale (audpad->audio_time,
1961 avipad->hdr.rate, avipad->hdr.scale * GST_SECOND);
1962 } else {
1963 GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
1964
1965 avimux->avi_hdr.max_bps += ((vidpad->vids.bit_cnt + 7) / 8) *
1966 (1000000. / avimux->avi_hdr.us_frame) * vidpad->vids.image_size;
1967 avipad->hdr.length = avimux->total_frames;
1968 }
1969 }
1970
1971 /* statistics/total_frames/... */
1972 avimux->avi_hdr.tot_frames = avimux->num_frames;
1973
1974 /* seek and rewrite the header */
1975 gst_segment_init (&segment, GST_FORMAT_BYTES);
1976 gst_pad_push_event (avimux->srcpad, gst_event_new_segment (&segment));
1977
1978 /* the first error survives */
1979 header = gst_avi_mux_riff_get_avi_header (avimux);
1980 if (res == GST_FLOW_OK)
1981 res = gst_pad_push (avimux->srcpad, header);
1982 else
1983 gst_pad_push (avimux->srcpad, header);
1984
1985 segment.start = avimux->total_data;
1986 segment.time = avimux->total_data;
1987 gst_pad_push_event (avimux->srcpad, gst_event_new_segment (&segment));
1988
1989 avimux->write_header = TRUE;
1990
1991 return res;
1992 }
1993
1994 static GstFlowReturn
gst_avi_mux_restart_file(GstAviMux * avimux)1995 gst_avi_mux_restart_file (GstAviMux * avimux)
1996 {
1997 GstFlowReturn res;
1998
1999 if ((res = gst_avi_mux_stop_file (avimux)) != GST_FLOW_OK)
2000 return res;
2001
2002 gst_pad_push_event (avimux->srcpad, gst_event_new_eos ());
2003
2004 return gst_avi_mux_start_file (avimux);
2005 }
2006
2007 /* handle events (search) */
2008 static gboolean
gst_avi_mux_handle_event(GstCollectPads * pads,GstCollectData * data,GstEvent * event,gpointer user_data)2009 gst_avi_mux_handle_event (GstCollectPads * pads, GstCollectData * data,
2010 GstEvent * event, gpointer user_data)
2011 {
2012 GstAviMux *avimux;
2013 gboolean ret = TRUE;
2014
2015 avimux = GST_AVI_MUX (user_data);
2016
2017 switch (GST_EVENT_TYPE (event)) {
2018 case GST_EVENT_CAPS:
2019 {
2020 GstCaps *caps;
2021 GstAviCollectData *collect_pad;
2022 GstAviVideoPad *avipad;
2023
2024 gst_event_parse_caps (event, &caps);
2025
2026 /* find stream data */
2027 collect_pad = (GstAviCollectData *) data;
2028 g_assert (collect_pad);
2029 avipad = (GstAviVideoPad *) collect_pad->avipad;
2030 g_assert (avipad);
2031
2032 if (avipad->parent.is_video) {
2033 ret = gst_avi_mux_vidsink_set_caps (data->pad, caps);
2034 } else {
2035 ret = gst_avi_mux_audsink_set_caps (data->pad, caps);
2036 }
2037 gst_event_unref (event);
2038 event = NULL;
2039 break;
2040 }
2041 case GST_EVENT_TAG:{
2042 GstTagList *list;
2043 GstTagSetter *setter = GST_TAG_SETTER (avimux);
2044 const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter);
2045
2046 gst_event_parse_tag (event, &list);
2047 gst_tag_setter_merge_tags (setter, list, mode);
2048 gst_event_unref (event);
2049 event = NULL;
2050 break;
2051 }
2052 default:
2053 break;
2054 }
2055
2056 if (event != NULL)
2057 return gst_collect_pads_event_default (pads, data, event, FALSE);
2058
2059 return ret;
2060 }
2061
2062 /* send extra 'padding' data */
2063 static GstFlowReturn
gst_avi_mux_send_pad_data(GstAviMux * avimux,gulong num_bytes)2064 gst_avi_mux_send_pad_data (GstAviMux * avimux, gulong num_bytes)
2065 {
2066 GstBuffer *buffer;
2067
2068 buffer = gst_buffer_new_and_alloc (num_bytes);
2069 gst_buffer_memset (buffer, 0, 0, num_bytes);
2070
2071 return gst_pad_push (avimux->srcpad, buffer);
2072 }
2073
2074 #define gst_avi_mux_is_uncompressed(fourcc) \
2075 (fourcc == GST_RIFF_DIB || \
2076 fourcc == GST_RIFF_rgb || \
2077 fourcc == GST_RIFF_RGB || fourcc == GST_RIFF_RAW)
2078
2079 /*
2080 * Helper for gst_avi_demux_invert()
2081 */
2082 static inline void
swap_line(guint8 * d1,guint8 * d2,guint8 * tmp,gint bytes)2083 swap_line (guint8 * d1, guint8 * d2, guint8 * tmp, gint bytes)
2084 {
2085 memcpy (tmp, d1, bytes);
2086 memcpy (d1, d2, bytes);
2087 memcpy (d2, tmp, bytes);
2088 }
2089
2090 /*
2091 * Invert DIB buffers... Takes existing buffer and
2092 * returns either the buffer or a new one (with old
2093 * one dereferenced).
2094 * FFMPEG does this by simply negating the height in the header. Should we?
2095 * FIXME: can't we preallocate tmp? and remember stride, bpp?
2096 * this could be done in do_one_buffer() I suppose
2097 */
2098 static GstBuffer *
gst_avi_mux_invert(GstAviPad * avipad,GstBuffer * buf)2099 gst_avi_mux_invert (GstAviPad * avipad, GstBuffer * buf)
2100 {
2101 gint y, w, h;
2102 gint bpp, stride;
2103 guint8 *tmp = NULL;
2104 GstMapInfo map;
2105
2106 GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
2107
2108 h = vidpad->vids.height;
2109 w = vidpad->vids.width;
2110 bpp = vidpad->vids.bit_cnt ? vidpad->vids.bit_cnt : 8;
2111 stride = GST_ROUND_UP_4 (w * (bpp / 8));
2112
2113 buf = gst_buffer_make_writable (buf);
2114
2115 gst_buffer_map (buf, &map, GST_MAP_READWRITE);
2116 if (map.size < (stride * h)) {
2117 GST_WARNING ("Buffer is smaller than reported Width x Height x Depth");
2118 gst_buffer_unmap (buf, &map);
2119 return buf;
2120 }
2121
2122 tmp = g_malloc (stride);
2123
2124 for (y = 0; y < h / 2; y++) {
2125 swap_line (map.data + stride * y, map.data + stride * (h - 1 - y), tmp,
2126 stride);
2127 }
2128
2129 g_free (tmp);
2130
2131 gst_buffer_unmap (buf, &map);
2132
2133 return buf;
2134 }
2135
2136 /* do buffer */
2137 static GstFlowReturn
gst_avi_mux_do_buffer(GstAviMux * avimux,GstAviPad * avipad)2138 gst_avi_mux_do_buffer (GstAviMux * avimux, GstAviPad * avipad)
2139 {
2140 GstFlowReturn res;
2141 GstBuffer *data, *header;
2142 gulong total_size, pad_bytes = 0;
2143 guint flags;
2144 gsize datasize;
2145 GstClockTime time;
2146
2147 data = gst_collect_pads_pop (avimux->collect, avipad->collect);
2148 /* arrange downstream running time */
2149 time = gst_segment_to_running_time (&avipad->collect->segment,
2150 GST_FORMAT_TIME, GST_BUFFER_TIMESTAMP (data));
2151 if (time != GST_BUFFER_TIMESTAMP (data)) {
2152 data = gst_buffer_make_writable (data);
2153 GST_BUFFER_TIMESTAMP (data) = time;
2154 }
2155
2156 /* Prepend a special buffer to the first one for some formats */
2157 if (avipad->is_video) {
2158 GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
2159
2160 if (vidpad->prepend_buffer) {
2161 /* Keep a reference to data until we copy the timestamps, then release it */
2162 GstBuffer *newdata =
2163 gst_buffer_append (vidpad->prepend_buffer, gst_buffer_ref (data));
2164 gst_buffer_copy_into (newdata, data, GST_BUFFER_COPY_TIMESTAMPS, 0, -1);
2165 gst_buffer_unref (data);
2166
2167 data = newdata;
2168 vidpad->prepend_buffer = NULL;
2169 }
2170
2171 /* DIB buffers are stored topdown (I don't know why) */
2172 if (gst_avi_mux_is_uncompressed (avipad->hdr.fcc_handler)) {
2173 data = gst_avi_mux_invert (avipad, data);
2174 }
2175 } else {
2176 GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
2177
2178 if (audpad->needs_reorder) {
2179 data = gst_buffer_make_writable (data);
2180 if (!gst_audio_buffer_reorder_channels (data, audpad->audio_format,
2181 audpad->auds.channels, audpad->gst_positions,
2182 audpad->wav_positions)) {
2183 GST_WARNING_OBJECT (avimux, "Could not reorder channels");
2184 }
2185 }
2186 }
2187
2188 if (avimux->restart) {
2189 if ((res = gst_avi_mux_restart_file (avimux)) != GST_FLOW_OK)
2190 goto done;
2191 }
2192
2193 datasize = gst_buffer_get_size (data);
2194
2195 /* need to restart or start a next avix chunk ? */
2196 if ((avimux->is_bigfile ? avimux->datax_size : avimux->data_size) +
2197 datasize > GST_AVI_MAX_SIZE) {
2198 if (avimux->enable_large_avi) {
2199 if ((res = gst_avi_mux_bigfile (avimux, FALSE)) != GST_FLOW_OK)
2200 goto done;
2201 } else {
2202 if ((res = gst_avi_mux_restart_file (avimux)) != GST_FLOW_OK)
2203 goto done;
2204 }
2205 }
2206
2207 /* get header and record some stats */
2208 if (datasize & 1) {
2209 pad_bytes = 2 - (datasize & 1);
2210 }
2211 header = gst_avi_mux_riff_get_header (avipad, datasize);
2212 total_size = gst_buffer_get_size (header) + datasize + pad_bytes;
2213
2214 if (avimux->is_bigfile) {
2215 avimux->datax_size += total_size;
2216 } else {
2217 avimux->data_size += total_size;
2218 }
2219
2220 if (G_UNLIKELY (avipad->hook)) {
2221 gst_buffer_ref (data);
2222 avipad->hook (avimux, avipad, data);
2223 }
2224
2225 /* the suggested buffer size is the max frame size */
2226 if (avipad->hdr.bufsize < datasize)
2227 avipad->hdr.bufsize = datasize;
2228
2229 if (avipad->is_video) {
2230 avimux->total_frames++;
2231
2232 if (avimux->is_bigfile) {
2233 avimux->numx_frames++;
2234 } else {
2235 avimux->num_frames++;
2236 }
2237
2238 flags = 0x02;
2239 if (!GST_BUFFER_FLAG_IS_SET (data, GST_BUFFER_FLAG_DELTA_UNIT))
2240 flags |= 0x10;
2241 } else {
2242 GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
2243
2244 flags = 0;
2245 audpad->audio_size += datasize;
2246 audpad->audio_time += GST_BUFFER_DURATION (data);
2247 if (audpad->max_audio_chunk && datasize > audpad->max_audio_chunk)
2248 audpad->max_audio_chunk = datasize;
2249 }
2250
2251 gst_avi_mux_add_index (avimux, avipad, flags, datasize);
2252
2253 /* send buffers */
2254 GST_LOG_OBJECT (avimux, "pushing buffers: head, data");
2255
2256 if ((res = gst_pad_push (avimux->srcpad, header)) != GST_FLOW_OK)
2257 goto done;
2258
2259 gst_buffer_ref (data);
2260 if ((res = gst_pad_push (avimux->srcpad, data)) != GST_FLOW_OK)
2261 goto done;
2262
2263 if (pad_bytes) {
2264 if ((res = gst_avi_mux_send_pad_data (avimux, pad_bytes)) != GST_FLOW_OK)
2265 goto done;
2266 }
2267
2268 /* if any push above fails, we're in trouble with file consistency anyway */
2269 avimux->total_data += total_size;
2270 avimux->idx_offset += total_size;
2271
2272 done:
2273 gst_buffer_unref (data);
2274 return res;
2275 }
2276
2277 /* pick the oldest buffer from the pads and push it */
2278 static GstFlowReturn
gst_avi_mux_do_one_buffer(GstAviMux * avimux)2279 gst_avi_mux_do_one_buffer (GstAviMux * avimux)
2280 {
2281 GstAviPad *avipad, *best_pad;
2282 GSList *node;
2283 GstBuffer *buffer;
2284 GstClockTime time, best_time, delay;
2285
2286 node = avimux->sinkpads;
2287 best_pad = NULL;
2288 best_time = GST_CLOCK_TIME_NONE;
2289 for (; node; node = node->next) {
2290 avipad = (GstAviPad *) node->data;
2291
2292 if (!avipad->collect)
2293 continue;
2294
2295 buffer = gst_collect_pads_peek (avimux->collect, avipad->collect);
2296 if (!buffer)
2297 continue;
2298 time = GST_BUFFER_TIMESTAMP (buffer);
2299 gst_buffer_unref (buffer);
2300
2301 /* invalid should pass */
2302 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
2303 time = gst_segment_to_running_time (&avipad->collect->segment,
2304 GST_FORMAT_TIME, time);
2305 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
2306 GST_DEBUG_OBJECT (avimux, "clipping buffer on pad %s outside segment",
2307 GST_PAD_NAME (avipad->collect->pad));
2308 buffer = gst_collect_pads_pop (avimux->collect, avipad->collect);
2309 gst_buffer_unref (buffer);
2310 return GST_FLOW_OK;
2311 }
2312 }
2313
2314 delay = avipad->is_video ? GST_SECOND / 2 : 0;
2315
2316 /* invalid timestamp buffers pass first,
2317 * these are probably initialization buffers */
2318 if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time)
2319 || (GST_CLOCK_TIME_IS_VALID (best_time) && time + delay < best_time)) {
2320 best_pad = avipad;
2321 best_time = time + delay;
2322 }
2323 }
2324
2325 if (best_pad) {
2326 GST_LOG_OBJECT (avimux, "selected pad %s with time %" GST_TIME_FORMAT,
2327 GST_PAD_NAME (best_pad->collect->pad), GST_TIME_ARGS (best_time));
2328
2329 return gst_avi_mux_do_buffer (avimux, best_pad);
2330 } else {
2331 /* simply finish off the file and send EOS */
2332 gst_avi_mux_stop_file (avimux);
2333 gst_pad_push_event (avimux->srcpad, gst_event_new_eos ());
2334 return GST_FLOW_EOS;
2335 }
2336
2337 }
2338
2339 static GstFlowReturn
gst_avi_mux_collect_pads(GstCollectPads * pads,GstAviMux * avimux)2340 gst_avi_mux_collect_pads (GstCollectPads * pads, GstAviMux * avimux)
2341 {
2342 GstFlowReturn res;
2343
2344 if (G_UNLIKELY (avimux->write_header)) {
2345 if ((res = gst_avi_mux_start_file (avimux)) != GST_FLOW_OK)
2346 return res;
2347 }
2348
2349 return gst_avi_mux_do_one_buffer (avimux);
2350 }
2351
2352
2353 static void
gst_avi_mux_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2354 gst_avi_mux_get_property (GObject * object,
2355 guint prop_id, GValue * value, GParamSpec * pspec)
2356 {
2357 GstAviMux *avimux;
2358
2359 avimux = GST_AVI_MUX (object);
2360
2361 switch (prop_id) {
2362 case PROP_BIGFILE:
2363 g_value_set_boolean (value, avimux->enable_large_avi);
2364 break;
2365 default:
2366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2367 break;
2368 }
2369 }
2370
2371 static void
gst_avi_mux_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2372 gst_avi_mux_set_property (GObject * object,
2373 guint prop_id, const GValue * value, GParamSpec * pspec)
2374 {
2375 GstAviMux *avimux;
2376
2377 avimux = GST_AVI_MUX (object);
2378
2379 switch (prop_id) {
2380 case PROP_BIGFILE:
2381 avimux->enable_large_avi = g_value_get_boolean (value);
2382 break;
2383 default:
2384 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2385 break;
2386 }
2387 }
2388
2389 static GstStateChangeReturn
gst_avi_mux_change_state(GstElement * element,GstStateChange transition)2390 gst_avi_mux_change_state (GstElement * element, GstStateChange transition)
2391 {
2392 GstAviMux *avimux;
2393 GstStateChangeReturn ret;
2394
2395 avimux = GST_AVI_MUX (element);
2396
2397 switch (transition) {
2398 case GST_STATE_CHANGE_READY_TO_PAUSED:
2399 gst_collect_pads_start (avimux->collect);
2400 break;
2401 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2402 break;
2403 case GST_STATE_CHANGE_PAUSED_TO_READY:
2404 gst_collect_pads_stop (avimux->collect);
2405 break;
2406 default:
2407 break;
2408 }
2409
2410 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2411 if (ret == GST_STATE_CHANGE_FAILURE)
2412 goto done;
2413
2414 switch (transition) {
2415 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2416 break;
2417 case GST_STATE_CHANGE_PAUSED_TO_READY:
2418 gst_avi_mux_reset (avimux);
2419 break;
2420 case GST_STATE_CHANGE_READY_TO_NULL:
2421 break;
2422 default:
2423 break;
2424 }
2425
2426 done:
2427 return ret;
2428 }
2429