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