gstavimux.c 40.8 KB
Newer Older
1
2
/* AVI muxer plugin for GStreamer
 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
21
22
23
24
25
26
/* based on:
 * - the old avimuxer (by Wim Taymans)
 * - xawtv's aviwriter (by Gerd Knorr)
 * - mjpegtools' avilib (by Rainer Johanni)
 * - openDML large-AVI docs
 */

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
27

28
29
30
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
31

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
32
#include "gst/gst-i18n-plugin.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
33
34
35
#include <stdlib.h>
#include <string.h>

36
37
#include <gst/video/video.h>

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
38
39
#include "gstavimux.h"

40
41
42
43
44
45
#ifndef LE_FROM_GUINT16
#define LE_FROM_GUINT16 GUINT16_FROM_LE
#endif
#ifndef LE_FROM_GUINT32
#define LE_FROM_GUINT32 GUINT32_FROM_LE
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
46
47

/* AviMux signals and args */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48
49
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
50
51
52
53
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
54
55
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
56
  ARG_0,
57
  ARG_BIGFILE
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
58
59
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
60
61
62
63
64
65
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-msvideo")
    );

David Schleef's avatar
David Schleef committed
66
static GstStaticPadTemplate video_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
67
68
69
70
    GST_STATIC_PAD_TEMPLATE ("video_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("video/x-raw-yuv, "
71
72
        "format = (fourcc) { YUY2, I420 }, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
73
74
        "height = (int) [ 16, 4096 ], "
        "framerate = (double) [ 0, MAX ]; "
75
        "image/jpeg, "
76
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
77
78
        "height = (int) [ 16, 4096 ], "
        "framerate = (double) [ 0, MAX ]; "
79
80
81
        "video/x-divx, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
82
        "framerate = (double) [ 0, MAX ], "
83
84
85
        "divxversion = (int) [ 3, 5 ]; "
        "video/x-xvid, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
86
87
        "height = (int) [ 16, 4096 ], "
        "framerate = (double) [ 0, MAX ]; "
88
89
        "video/x-3ivx, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
90
91
        "height = (int) [ 16, 4096 ], "
        "framerate = (double) [ 0, MAX ]; "
92
93
94
        "video/x-msmpeg, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
95
        "framerate = (double) [ 0, MAX ], "
96
97
98
99
        "msmpegversion = (int) [ 41, 43 ]; "
        "video/mpeg, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
100
        "framerate = (double) [ 0, MAX ], "
101
102
103
104
        "mpegversion = (int) 1, "
        "systemstream = (boolean) FALSE; "
        "video/x-h263, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
105
106
        "height = (int) [ 16, 4096 ], "
        "framerate = (double) [ 0, MAX ]; "
107
108
109
110
        "video/x-h264, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
        "framerate = (double) [ 0, MAX ]; "
111
112
113
        "video/x-dv, "
        "width = (int) 720, "
        "height = (int) { 576, 480 }, "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
114
        "framerate = (double) [ 0, MAX ], "
115
116
        "systemstream = (boolean) FALSE; "
        "video/x-huffyuv, "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
117
118
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], " "framerate = (double) [ 0, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
119
120
    );

David Schleef's avatar
David Schleef committed
121
static GstStaticPadTemplate audio_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122
123
124
125
    GST_STATIC_PAD_TEMPLATE ("audio_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("audio/x-raw-int, "
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
        "endianness = (int) LITTLE_ENDIAN, "
        "signed = (boolean) { TRUE, FALSE }, "
        "width = (int) { 8, 16 }, "
        "depth = (int) { 8, 16 }, "
        "rate = (int) [ 1000, 96000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/mpeg, "
        "mpegversion = (int) 1, "
        "layer = (int) [ 1, 3 ], "
        "rate = (int) [ 1000, 96000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/x-vorbis, "
        "rate = (int) [ 1000, 96000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/x-ac3, "
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142
143
144
145
146
147
148
149
150
151
152
    );


static void gst_avimux_base_init (gpointer g_class);
static void gst_avimux_class_init (GstAviMuxClass * klass);
static void gst_avimux_init (GstAviMux * avimux);

static void gst_avimux_loop (GstElement * element);
static gboolean gst_avimux_handle_event (GstPad * pad, GstEvent * event);
static GstPad *gst_avimux_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * name);
153
static void gst_avimux_release_pad (GstElement * element, GstPad * pad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
154
155
156
157
static void gst_avimux_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_avimux_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);
158
159
static GstStateChangeReturn gst_avimux_change_state (GstElement * element,
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
160
161

static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162

163
/*static guint gst_avimux_signals[LAST_SIGNAL] = { 0 }; */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
164
165

GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
166
gst_avimux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
167
168
169
170
171
{
  static GType avimux_type = 0;

  if (!avimux_type) {
    static const GTypeInfo avimux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
172
      sizeof (GstAviMuxClass),
173
      gst_avimux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
174
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
175
      (GClassInitFunc) gst_avimux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
176
177
      NULL,
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178
      sizeof (GstAviMux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
179
      0,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
180
      (GInstanceInitFunc) gst_avimux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
181
    };
182
183
184
185
186
    static const GInterfaceInfo tag_setter_info = {
      NULL,
      NULL,
      NULL
    };
187

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
    avimux_type =
189
        g_type_register_static (GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
190
191
    g_type_add_interface_static (avimux_type, GST_TYPE_TAG_SETTER,
        &tag_setter_info);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
192
193
194
195
  }
  return avimux_type;
}

196
197
198
199
static void
gst_avimux_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
200
201
202
203
204
  static GstElementDetails gst_avimux_details =
      GST_ELEMENT_DETAILS ("Avi multiplexer",
      "Codec/Muxer",
      "Muxes audio and video into an avi stream",
      "Ronald Bultje <rbultje@ronald.bitfreak.net>");
205
206

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
207
      gst_static_pad_template_get (&src_factory));
208
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
209
      gst_static_pad_template_get (&audio_sink_factory));
210
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
211
      gst_static_pad_template_get (&video_sink_factory));
212
213
214
215

  gst_element_class_set_details (element_class, &gst_avimux_details);
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
216
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
217
gst_avimux_class_init (GstAviMuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
218
219
220
221
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
222
223
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
224
225
226

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
227
228
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIGFILE,
      g_param_spec_boolean ("bigfile", "Bigfile Support",
229
          "Support for openDML-2.0 (big) AVI files", 0, G_PARAM_READWRITE));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
230
231

  gstelement_class->request_new_pad = gst_avimux_request_new_pad;
232
  gstelement_class->release_pad = gst_avimux_release_pad;
233
234
235
236
237

  gstelement_class->change_state = gst_avimux_change_state;

  gstelement_class->get_property = gst_avimux_get_property;
  gstelement_class->set_property = gst_avimux_set_property;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
238
239
}

240
static const GstEventMask *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
241
gst_avimux_get_event_masks (GstPad * pad)
242
{
243
  static const GstEventMask gst_avimux_sink_event_masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
244
245
    {GST_EVENT_EOS, 0},
    {0,}
246
  };
247

248
  return gst_avimux_sink_event_masks;
249
250
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
251
252
static void
gst_avimux_init (GstAviMux * avimux)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
253
{
254
255
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (avimux);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
256
257
  avimux->srcpad =
      gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
258
          "src"), "src");
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
259
260
  gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
261
  GST_FLAG_SET (GST_ELEMENT (avimux), GST_ELEMENT_EVENT_AWARE);
262

263
264
265
266
267
268
269
  avimux->audiosinkpad = NULL;
  avimux->audio_pad_connected = FALSE;
  avimux->videosinkpad = NULL;
  avimux->video_pad_connected = FALSE;

  avimux->audio_buffer_queue = NULL;
  avimux->video_buffer_queue = NULL;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
270

271
272
273
  avimux->num_frames = 0;

  /* audio/video/AVI header initialisation */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
274
275
276
277
278
279
  memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
  memset (&(avimux->vids_hdr), 0, sizeof (gst_riff_strh));
  memset (&(avimux->vids), 0, sizeof (gst_riff_strf_vids));
  memset (&(avimux->auds_hdr), 0, sizeof (gst_riff_strh));
  memset (&(avimux->auds), 0, sizeof (gst_riff_strf_auds));
  avimux->vids_hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
280
  avimux->vids_hdr.rate = 1000000;
281
  avimux->avi_hdr.max_bps = 10000000;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
282
  avimux->auds_hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
283
284
  avimux->vids_hdr.quality = 0xFFFFFFFF;
  avimux->auds_hdr.quality = 0xFFFFFFFF;
285
  avimux->tags = NULL;
286
287
288
289
290
291

  avimux->idx = NULL;

  avimux->write_header = TRUE;

  avimux->enable_large_avi = TRUE;
292

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
  gst_element_set_loop_function (GST_ELEMENT (avimux), gst_avimux_loop);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
294
295
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
296
static GstPadLinkReturn
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
297
gst_avimux_vidsinkconnect (GstPad * pad, const GstCaps * vscaps)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
298
299
{
  GstAviMux *avimux;
David Schleef's avatar
David Schleef committed
300
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
301
  const gchar *mimetype;
David Schleef's avatar
David Schleef committed
302
303
  gdouble fps = 0.;
  gboolean ret;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
304
305
306

  avimux = GST_AVIMUX (gst_pad_get_parent (pad));

307
  GST_DEBUG ("avimux: video sinkconnect triggered on %s",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
308
      gst_pad_get_name (pad));
309

David Schleef's avatar
David Schleef committed
310
311
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
312

David Schleef's avatar
David Schleef committed
313
  /* global */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
315
  avimux->vids.size = sizeof (gst_riff_strf_vids);
  avimux->vids.planes = 1;
David Schleef's avatar
David Schleef committed
316
317
318
  ret = gst_structure_get_int (structure, "width", &avimux->vids.width);
  ret &= gst_structure_get_int (structure, "height", &avimux->vids.height);
  ret &= gst_structure_get_double (structure, "framerate", &fps);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
319
320
  if (!ret)
    return GST_PAD_LINK_REFUSED;
David Schleef's avatar
David Schleef committed
321
322
323
324
325
326
327
328
329

  if (fps != 0.)
    avimux->vids_hdr.scale = avimux->vids_hdr.rate / fps;

  if (!strcmp (mimetype, "video/x-raw-yuv")) {
    guint32 format;

    gst_structure_get_fourcc (structure, "format", &format);
    avimux->vids.compression = format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
330
331
    switch (format) {
      case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
332
333
        avimux->vids.bit_cnt = 16;
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
334
      case GST_MAKE_FOURCC ('I', '4', '2', '0'):
335
336
        avimux->vids.bit_cnt = 12;
        break;
David Schleef's avatar
David Schleef committed
337
338
339
340
341
342
343
    }
  } else {
    avimux->vids.bit_cnt = 24;
    avimux->vids.compression = 0;

    /* find format */
    if (!strcmp (mimetype, "video/x-huffyuv")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
344
      avimux->vids.compression = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
345
    } else if (!strcmp (mimetype, "image/jpeg")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
346
      avimux->vids.compression = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
David Schleef's avatar
David Schleef committed
347
348
    } else if (!strcmp (mimetype, "video/x-divx")) {
      gint divxversion;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
349

David Schleef's avatar
David Schleef committed
350
351
      gst_structure_get_int (structure, "divxversion", &divxversion);
      switch (divxversion) {
352
353
354
355
356
357
358
359
360
        case 3:
          avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
          break;
        case 4:
          avimux->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
          break;
        case 5:
          avimux->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
          break;
361
      }
David Schleef's avatar
David Schleef committed
362
    } else if (!strcmp (mimetype, "video/x-xvid")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
363
      avimux->vids.compression = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
David Schleef's avatar
David Schleef committed
364
    } else if (!strcmp (mimetype, "video/x-3ivx")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
365
      avimux->vids.compression = GST_MAKE_FOURCC ('3', 'I', 'V', '2');
David Schleef's avatar
David Schleef committed
366
367
    } else if (!strcmp (mimetype, "video/x-msmpeg")) {
      gint msmpegversion;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
368

David Schleef's avatar
David Schleef committed
369
370
      gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
      switch (msmpegversion) {
371
372
373
374
375
376
377
378
379
        case 41:
          avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
          break;
        case 42:
          avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
          break;
        case 43:
          avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
          break;
380
      }
David Schleef's avatar
David Schleef committed
381
    } else if (!strcmp (mimetype, "video/x-dv")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
382
      avimux->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
David Schleef's avatar
David Schleef committed
383
    } else if (!strcmp (mimetype, "video/x-h263")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
384
      avimux->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
David Schleef's avatar
David Schleef committed
385
    } else if (!strcmp (mimetype, "video/mpeg")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386
      avimux->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
David Schleef's avatar
David Schleef committed
387
    }
388

David Schleef's avatar
David Schleef committed
389
390
    if (!avimux->vids.compression) {
      return GST_PAD_LINK_DELAYED;
391
    }
392
393
394
395
396
397
398
399
400
  }

  avimux->vids_hdr.fcc_handler = avimux->vids.compression;
  avimux->vids.image_size = avimux->vids.height * avimux->vids.width;
  avimux->avi_hdr.width = avimux->vids.width;
  avimux->avi_hdr.height = avimux->vids.height;
  avimux->avi_hdr.us_frame = avimux->vids_hdr.scale;
  return GST_PAD_LINK_OK;
}
Wim Taymans's avatar
Wim Taymans committed
401

402
static GstPadLinkReturn
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403
gst_avimux_audsinkconnect (GstPad * pad, const GstCaps * vscaps)
404
405
{
  GstAviMux *avimux;
David Schleef's avatar
David Schleef committed
406
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407
  const gchar *mimetype;
David Schleef's avatar
David Schleef committed
408
  int i;
409
410
411
412

  avimux = GST_AVIMUX (gst_pad_get_parent (pad));

  GST_DEBUG ("avimux: audio sinkconnect triggered on %s",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
413
      gst_pad_get_name (pad));
414

David Schleef's avatar
David Schleef committed
415
416
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
417

David Schleef's avatar
David Schleef committed
418
419
420
421
422
  /* we want these for all */
  gst_structure_get_int (structure, "channels", &i);
  avimux->auds.channels = i;
  gst_structure_get_int (structure, "rate", &i);
  avimux->auds.rate = i;
423

David Schleef's avatar
David Schleef committed
424
  if (!strcmp (mimetype, "audio/x-raw-int")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
425
    avimux->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
426

David Schleef's avatar
David Schleef committed
427
428
429
430
    gst_structure_get_int (structure, "width", &i);
    avimux->auds.blockalign = i;
    gst_structure_get_int (structure, "depth", &i);
    avimux->auds.size = i;
431

David Schleef's avatar
David Schleef committed
432
433
434
435
436
    /* set some more info straight */
    avimux->auds.blockalign /= 8;
    avimux->auds.blockalign *= avimux->auds.channels;
    avimux->auds.av_bps = avimux->auds.blockalign * avimux->auds.rate;
  } else if (!strcmp (mimetype, "audio/mpeg") ||
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
437
438
      !strcmp (mimetype, "audio/x-vorbis") ||
      !strcmp (mimetype, "audio/x-ac3")) {
David Schleef's avatar
David Schleef committed
439
440
441
442
    avimux->auds.format = 0;

    if (!strcmp (mimetype, "audio/mpeg")) {
      gint layer = 3;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443

David Schleef's avatar
David Schleef committed
444
445
      gst_structure_get_int (structure, "layer", &layer);
      switch (layer) {
446
447
448
449
450
451
452
        case 3:
          avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
          break;
        case 1:
        case 2:
          avimux->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
          break;
453
      }
David Schleef's avatar
David Schleef committed
454
455
456
457
458
    } else if (!strcmp (mimetype, "audio/x-vorbis")) {
      avimux->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
    } else if (!strcmp (mimetype, "audio/x-ac3")) {
      avimux->auds.format = GST_RIFF_WAVE_FORMAT_A52;
    }
459

David Schleef's avatar
David Schleef committed
460
461
462
    avimux->auds.blockalign = 1;
    avimux->auds.av_bps = 0;
    avimux->auds.size = 16;
463

David Schleef's avatar
David Schleef committed
464
465
    if (!avimux->auds.format) {
      return GST_PAD_LINK_REFUSED;
466
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
467
468
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
469
  avimux->auds_hdr.rate = avimux->auds.blockalign * avimux->auds.rate;
470
  avimux->auds_hdr.samplesize = avimux->auds.blockalign;
471
  avimux->auds_hdr.scale = 1;
472
  return GST_PAD_LINK_OK;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
473
474
}

475
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
476
gst_avimux_pad_link (GstPad * pad, GstPad * peer, gpointer data)
477
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
478
  GstAviMux *avimux = GST_AVIMUX (data);
479
480
  const gchar *padname = gst_pad_get_name (pad);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
481
  if (pad == avimux->audiosinkpad) {
482
    avimux->audio_pad_connected = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
  } else if (pad == avimux->videosinkpad) {
484
    avimux->video_pad_connected = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
485
486
  } else {
    g_warning ("Unknown padname '%s'", padname);
487
488
489
    return;
  }

490
  GST_DEBUG ("pad '%s' connected", padname);
491
492
493
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
494
gst_avimux_pad_unlink (GstPad * pad, GstPad * peer, gpointer data)
495
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
496
  GstAviMux *avimux = GST_AVIMUX (data);
497
498
  const gchar *padname = gst_pad_get_name (pad);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
499
  if (pad == avimux->audiosinkpad) {
500
    avimux->audio_pad_connected = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
501
  } else if (pad == avimux->videosinkpad) {
502
    avimux->video_pad_connected = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
503
504
  } else {
    g_warning ("Unknown padname '%s'", padname);
505
506
507
    return;
  }

508
  GST_DEBUG ("pad '%s' unlinked", padname);
509
510
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
511
512
513
static GstPad *
gst_avimux_request_new_pad (GstElement * element,
    GstPadTemplate * templ, const gchar * req_name)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
514
515
516
{
  GstAviMux *avimux;
  GstPad *newpad;
517
  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
518

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
519
520
521
522
523
524
525
526
527
528
529
  g_return_val_if_fail (templ != NULL, NULL);

  if (templ->direction != GST_PAD_SINK) {
    g_warning ("avimux: request pad that is not a SINK pad\n");
    return NULL;
  }

  g_return_val_if_fail (GST_IS_AVIMUX (element), NULL);

  avimux = GST_AVIMUX (element);

530
  if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
531
    g_return_val_if_fail (avimux->audiosinkpad == NULL, NULL);
532
    newpad = gst_pad_new_from_template (templ, "audio_00");
533
    gst_pad_set_link_function (newpad, gst_avimux_audsinkconnect);
534
    avimux->audiosinkpad = newpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
535
536
  } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
    g_return_val_if_fail (avimux->videosinkpad == NULL, NULL);
537
    newpad = gst_pad_new_from_template (templ, "video_00");
538
    gst_pad_set_link_function (newpad, gst_avimux_vidsinkconnect);
539
    avimux->videosinkpad = newpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
540
  } else {
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
541
542
543
544
    g_warning ("avimux: this is not our template!\n");
    return NULL;
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
545
546
547
548
  g_signal_connect (newpad, "linked",
      G_CALLBACK (gst_avimux_pad_link), (gpointer) avimux);
  g_signal_connect (newpad, "unlinked",
      G_CALLBACK (gst_avimux_pad_unlink), (gpointer) avimux);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
549
  gst_element_add_pad (element, newpad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550
551
  gst_pad_set_event_mask_function (newpad, gst_avimux_get_event_masks);

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
552
553
554
  return newpad;
}

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
static void
gst_avimux_release_pad (GstElement * element, GstPad * pad)
{
  GstAviMux *avimux = GST_AVIMUX (element);

  if (pad == avimux->videosinkpad) {
    avimux->videosinkpad = NULL;
  } else if (pad == avimux->audiosinkpad) {
    avimux->audiosinkpad = NULL;
  } else {
    g_warning ("Unknown pad %s", gst_pad_get_name (pad));
    return;
  }

  GST_DEBUG ("Removed pad '%s'", gst_pad_get_name (pad));
  gst_element_remove_pad (element, pad);
}

573
574
575
576
/* maybe some of these functions should be moved to riff.h? */

/* DISCLAIMER: this function is ugly. So be it (i.e. it makes the rest easier) */

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
static void
gst_avimux_write_tag (const GstTagList * list, const gchar * tag, gpointer data)
{
  const struct
  {
    guint32 fcc;
    gchar *tag;
  } rifftags[] = {
    {
    GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
    GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
    GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
    GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
    GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
    GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
    GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
    0, NULL}
  };
595
  gint n, len, plen;
596
  GstBuffer *buf = data;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
597
  guint8 *buffdata = GST_BUFFER_DATA (buf) + GST_BUFFER_SIZE (buf);
598
599
600
601
602
603
  gchar *str;

  for (n = 0; rifftags[n].fcc != 0; n++) {
    if (!strcmp (rifftags[n].tag, tag) &&
        gst_tag_list_get_string (list, tag, &str)) {
      len = strlen (str);
604
605
606
607
      plen = len + 1;
      if (plen & 1)
        plen++;
      if (GST_BUFFER_MAXSIZE (buf) >= GST_BUFFER_SIZE (buf) + 8 + plen) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
608
609
610
611
        GST_WRITE_UINT32_LE (buffdata, rifftags[n].fcc);
        GST_WRITE_UINT32_LE (buffdata + 4, len + 1);
        memcpy (buffdata + 8, str, len);
        buffdata[8 + len] = 0;
612
        GST_BUFFER_SIZE (buf) += 8 + plen;
613
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
614
      break;
615
616
617
618
    }
  }
}

619
static GstBuffer *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
620
gst_avimux_riff_get_avi_header (GstAviMux * avimux)
621
{
622
623
  GstTagList *tags;
  const GstTagList *iface_tags;
624
625
  GstBuffer *buffer;
  guint8 *buffdata;
626
  guint size = 0;
627
628

  /* first, let's see what actually needs to be in the buffer */
629
  size += 32 + sizeof (gst_riff_avih);  /* avi header */
630
  if (avimux->video_pad_connected) {    /* we have video */
631
632
    size += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_vids);  /* vid hdr */
    size += 24;                 /* odml header */
633
  }
634
  if (avimux->audio_pad_connected) {    /* we have audio */
635
    size += 28 + sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds);  /* aud hdr */
636
637
  }
  /* this is the "riff size" */
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  avimux->header_size = size;
  size += 12;                   /* avi data header */

  /* tags */
  iface_tags = gst_tag_setter_get_list (GST_TAG_SETTER (avimux));
  if (iface_tags || avimux->tags) {
    size += 1024;
    if (iface_tags && avimux->tags) {
      tags = gst_tag_list_merge (iface_tags, avimux->tags,
          GST_TAG_MERGE_APPEND);
    } else if (iface_tags) {
      tags = gst_tag_list_copy (iface_tags);
    } else {
      tags = gst_tag_list_copy (avimux->tags);
    }
  } else {
    tags = NULL;
  }
656
657

  /* allocate the buffer */
658
659
660
  buffer = gst_buffer_new_and_alloc (size);
  buffdata = GST_BUFFER_DATA (buffer);
  GST_BUFFER_SIZE (buffer) = 0;
661
662

  /* avi header metadata */
663
664
665
666
667
668
669
670
671
672
673
674
  memcpy (buffdata + 0, "RIFF", 4);
  GST_WRITE_UINT32_LE (buffdata + 4,
      avimux->header_size + avimux->idx_size + avimux->data_size);
  memcpy (buffdata + 8, "AVI ", 4);
  memcpy (buffdata + 12, "LIST", 4);
  GST_WRITE_UINT32_LE (buffdata + 16, avimux->header_size - 4 * 5);
  memcpy (buffdata + 20, "hdrl", 4);
  memcpy (buffdata + 24, "avih", 4);
  GST_WRITE_UINT32_LE (buffdata + 28, sizeof (gst_riff_avih));
  buffdata += 32;
  GST_BUFFER_SIZE (buffer) += 32;

675
  /* the AVI header itself */
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  GST_WRITE_UINT32_LE (buffdata + 0, avimux->avi_hdr.us_frame);
  GST_WRITE_UINT32_LE (buffdata + 4, avimux->avi_hdr.max_bps);
  GST_WRITE_UINT32_LE (buffdata + 8, avimux->avi_hdr.pad_gran);
  GST_WRITE_UINT32_LE (buffdata + 12, avimux->avi_hdr.flags);
  GST_WRITE_UINT32_LE (buffdata + 16, avimux->avi_hdr.tot_frames);
  GST_WRITE_UINT32_LE (buffdata + 20, avimux->avi_hdr.init_frames);
  GST_WRITE_UINT32_LE (buffdata + 24, avimux->avi_hdr.streams);
  GST_WRITE_UINT32_LE (buffdata + 28, avimux->avi_hdr.bufsize);
  GST_WRITE_UINT32_LE (buffdata + 32, avimux->avi_hdr.width);
  GST_WRITE_UINT32_LE (buffdata + 36, avimux->avi_hdr.height);
  GST_WRITE_UINT32_LE (buffdata + 40, avimux->avi_hdr.scale);
  GST_WRITE_UINT32_LE (buffdata + 44, avimux->avi_hdr.rate);
  GST_WRITE_UINT32_LE (buffdata + 48, avimux->avi_hdr.start);
  GST_WRITE_UINT32_LE (buffdata + 52, avimux->avi_hdr.length);
  buffdata += 56;
  GST_BUFFER_SIZE (buffer) += 56;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
692
693

  if (avimux->video_pad_connected) {
694
    /* video header metadata */
695
696
697
698
    memcpy (buffdata + 0, "LIST", 4);
    GST_WRITE_UINT32_LE (buffdata + 4,
        sizeof (gst_riff_strh) + sizeof (gst_riff_strf_vids) + 4 * 5);
    memcpy (buffdata + 8, "strl", 4);
699
    /* generic header */
700
701
    memcpy (buffdata + 12, "strh", 4);
    GST_WRITE_UINT32_LE (buffdata + 16, sizeof (gst_riff_strh));
702
    /* the actual header */
703
704
705
706
707
708
709
710
711
712
713
714
    GST_WRITE_UINT32_LE (buffdata + 20, avimux->vids_hdr.type);
    GST_WRITE_UINT32_LE (buffdata + 24, avimux->vids_hdr.fcc_handler);
    GST_WRITE_UINT32_LE (buffdata + 28, avimux->vids_hdr.flags);
    GST_WRITE_UINT32_LE (buffdata + 32, avimux->vids_hdr.priority);
    GST_WRITE_UINT32_LE (buffdata + 36, avimux->vids_hdr.init_frames);
    GST_WRITE_UINT32_LE (buffdata + 40, avimux->vids_hdr.scale);
    GST_WRITE_UINT32_LE (buffdata + 44, avimux->vids_hdr.rate);
    GST_WRITE_UINT32_LE (buffdata + 48, avimux->vids_hdr.start);
    GST_WRITE_UINT32_LE (buffdata + 52, avimux->vids_hdr.length);
    GST_WRITE_UINT32_LE (buffdata + 56, avimux->vids_hdr.bufsize);
    GST_WRITE_UINT32_LE (buffdata + 60, avimux->vids_hdr.quality);
    GST_WRITE_UINT32_LE (buffdata + 64, avimux->vids_hdr.samplesize);
715
    /* the video header */
716
717
    memcpy (buffdata + 68, "strf", 4);
    GST_WRITE_UINT32_LE (buffdata + 72, sizeof (gst_riff_strf_vids));
718
    /* the actual header */
719
720
721
722
723
724
725
726
727
728
729
730
731
    GST_WRITE_UINT32_LE (buffdata + 76, avimux->vids.size);
    GST_WRITE_UINT32_LE (buffdata + 80, avimux->vids.width);
    GST_WRITE_UINT32_LE (buffdata + 84, avimux->vids.height);
    GST_WRITE_UINT16_LE (buffdata + 88, avimux->vids.planes);
    GST_WRITE_UINT16_LE (buffdata + 90, avimux->vids.bit_cnt);
    GST_WRITE_UINT32_LE (buffdata + 92, avimux->vids.compression);
    GST_WRITE_UINT32_LE (buffdata + 96, avimux->vids.image_size);
    GST_WRITE_UINT32_LE (buffdata + 100, avimux->vids.xpels_meter);
    GST_WRITE_UINT32_LE (buffdata + 104, avimux->vids.ypels_meter);
    GST_WRITE_UINT32_LE (buffdata + 108, avimux->vids.num_colors);
    GST_WRITE_UINT32_LE (buffdata + 112, avimux->vids.imp_colors);
    buffdata += 116;
    GST_BUFFER_SIZE (buffer) += 116;
732
  }
733

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
734
  if (avimux->audio_pad_connected) {
735
    /* audio header */
736
737
738
739
    memcpy (buffdata + 0, "LIST", 4);
    GST_WRITE_UINT32_LE (buffdata + 4,
        sizeof (gst_riff_strh) + sizeof (gst_riff_strf_auds) + 4 * 5);
    memcpy (buffdata + 8, "strl", 4);
740
    /* generic header */
741
742
    memcpy (buffdata + 12, "strh", 4);
    GST_WRITE_UINT32_LE (buffdata + 16, sizeof (gst_riff_strh));
743
    /* the actual header */
744
745
746
747
748
749
750
751
752
753
754
755
    GST_WRITE_UINT32_LE (buffdata + 20, avimux->auds_hdr.type);
    GST_WRITE_UINT32_LE (buffdata + 24, avimux->auds_hdr.fcc_handler);
    GST_WRITE_UINT32_LE (buffdata + 28, avimux->auds_hdr.flags);
    GST_WRITE_UINT32_LE (buffdata + 32, avimux->auds_hdr.priority);
    GST_WRITE_UINT32_LE (buffdata + 36, avimux->auds_hdr.init_frames);
    GST_WRITE_UINT32_LE (buffdata + 40, avimux->auds_hdr.scale);
    GST_WRITE_UINT32_LE (buffdata + 44, avimux->auds_hdr.rate);
    GST_WRITE_UINT32_LE (buffdata + 48, avimux->auds_hdr.start);
    GST_WRITE_UINT32_LE (buffdata + 52, avimux->auds_hdr.length);
    GST_WRITE_UINT32_LE (buffdata + 56, avimux->auds_hdr.bufsize);
    GST_WRITE_UINT32_LE (buffdata + 60, avimux->auds_hdr.quality);
    GST_WRITE_UINT32_LE (buffdata + 64, avimux->auds_hdr.samplesize);
756
    /* the audio header */
757
758
    memcpy (buffdata + 68, "strf", 4);
    GST_WRITE_UINT32_LE (buffdata + 72, sizeof (gst_riff_strf_auds));
759
    /* the actual header */
760
761
762
763
764
765
766
767
    GST_WRITE_UINT16_LE (buffdata + 76, avimux->auds.format);
    GST_WRITE_UINT16_LE (buffdata + 78, avimux->auds.channels);
    GST_WRITE_UINT32_LE (buffdata + 80, avimux->auds.rate);
    GST_WRITE_UINT32_LE (buffdata + 84, avimux->auds.av_bps);
    GST_WRITE_UINT16_LE (buffdata + 88, avimux->auds.blockalign);
    GST_WRITE_UINT16_LE (buffdata + 90, avimux->auds.size);
    buffdata += 92;
    GST_BUFFER_SIZE (buffer) += 92;
768
  }
769

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
770
  if (avimux->video_pad_connected) {
771
    /* odml header */
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
    memcpy (buffdata + 0, "LIST", 4);
    GST_WRITE_UINT32_LE (buffdata + 4, sizeof (guint32) + 4 * 3);
    memcpy (buffdata + 8, "odml", 4);
    memcpy (buffdata + 12, "dmlh", 4);
    GST_WRITE_UINT32_LE (buffdata + 16, sizeof (guint32));
    GST_WRITE_UINT32_LE (buffdata + 20, avimux->total_frames);
    buffdata += 24;
    GST_BUFFER_SIZE (buffer) += 24;
  }

  /* tags */
  if (tags) {
    guint8 *ptr;
    guint startsize;

    memcpy (buffdata + 0, "LIST", 4);
    ptr = buffdata + 4;         /* fill in later */
    startsize = GST_BUFFER_SIZE (buffer) + 4;
    memcpy (buffdata + 8, "INFO", 4);
    buffdata += 12;
    GST_BUFFER_SIZE (buffer) += 12;

    /* 12 bytes is needed for data header */
    GST_BUFFER_MAXSIZE (buffer) -= 12;
    gst_tag_list_foreach (tags, gst_avimux_write_tag, buffer);
    gst_tag_list_free (tags);
    GST_BUFFER_MAXSIZE (buffer) += 12;
    buffdata = GST_BUFFER_DATA (buffer) + GST_BUFFER_SIZE (buffer);

    /* update list size */
Ronald S. Bultje's avatar
Ronald S. Bultje committed
802
    GST_WRITE_UINT32_LE (ptr, GST_BUFFER_SIZE (buffer) - startsize - 4);
803
  }
804
805

  /* avi data header */
806
807
  memcpy (buffdata + 0, "LIST", 4);
  GST_WRITE_UINT32_LE (buffdata + 4, avimux->data_size);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
808
  memcpy (buffdata + 8, "movi", 4);
809
810
  buffdata += 12;
  GST_BUFFER_SIZE (buffer) += 12;
811
812
813
814
815
816
817
818
819
820

  return buffer;
}

static GstBuffer *
gst_avimux_riff_get_avix_header (guint32 datax_size)
{
  GstBuffer *buffer;
  guint8 *buffdata;

821
822
823
824
825
826
827
828
829
  buffer = gst_buffer_new_and_alloc (24);
  buffdata = GST_BUFFER_DATA (buffer);

  memcpy (buffdata + 0, "LIST", 4);
  GST_WRITE_UINT32_LE (buffdata + 4, datax_size + 4 * 4);
  memcpy (buffdata + 8, "AVIX", 4);
  memcpy (buffdata + 12, "LIST", 4);
  GST_WRITE_UINT32_LE (buffdata + 16, datax_size);
  memcpy (buffdata + 20, "movi", 4);
830
831
832
833
834
835
836
837

  return buffer;
}

static GstBuffer *
gst_avimux_riff_get_video_header (guint32 video_frame_size)
{
  GstBuffer *buffer;
838
  guint8 *buffdata;
839

840
841
842
843
  buffer = gst_buffer_new_and_alloc (8);
  buffdata = GST_BUFFER_DATA (buffer);
  memcpy (buffdata + 0, "00db", 4);
  GST_WRITE_UINT32_LE (buffdata + 4, video_frame_size);
844
845
846
847
848
849
850
851

  return buffer;
}

static GstBuffer *
gst_avimux_riff_get_audio_header (guint32 audio_sample_size)
{
  GstBuffer *buffer;
852
  guint8 *buffdata;
853

854
855
856
857
  buffer = gst_buffer_new_and_alloc (8);
  buffdata = GST_BUFFER_DATA (buffer);
  memcpy (buffdata + 0, "01wb", 4);
  GST_WRITE_UINT32_LE (buffdata + 4, audio_sample_size);
858
859
860
861
862
863

  return buffer;
}

/* some other usable functions (thankyou xawtv ;-) ) */

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
864
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
865
866
gst_avimux_add_index (GstAviMux * avimux, guchar * code, guint32 flags,
    guint32 size)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
867
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
868
  if (avimux->idx_index == avimux->idx_count) {
869
    avimux->idx_count += 256;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
870
    avimux->idx =
871
872
        realloc (avimux->idx,
        avimux->idx_count * sizeof (gst_riff_index_entry));
873
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
874
875
876
877
  memcpy (&(avimux->idx[avimux->idx_index].id), code, 4);
  avimux->idx[avimux->idx_index].flags = LE_FROM_GUINT32 (flags);
  avimux->idx[avimux->idx_index].offset = LE_FROM_GUINT32 (avimux->idx_offset);
  avimux->idx[avimux->idx_index].size = LE_FROM_GUINT32 (size);
878
879
880
881
  avimux->idx_index++;
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
882
gst_avimux_write_index (GstAviMux * avimux)
883
884
{
  GstBuffer *buffer;
885
  guint8 *buffdata;
886

887
888
889
890
891
  buffer = gst_buffer_new_and_alloc (8);
  buffdata = GST_BUFFER_DATA (buffer);
  memcpy (buffdata + 0, "idx1", 4);
  GST_WRITE_UINT32_LE (buffdata + 4,
      avimux->idx_index * sizeof (gst_riff_index_entry));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
892
  gst_pad_push (avimux->srcpad, GST_DATA (buffer));
893

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
894
895
  buffer = gst_buffer_new ();
  GST_BUFFER_SIZE (buffer) = avimux->idx_index * sizeof (gst_riff_index_entry);
896
  GST_BUFFER_DATA (buffer) = (guint8 *) avimux->idx;
897
  avimux->idx = NULL;           /* will be free()'ed by gst_buffer_unref() */
898
  avimux->total_data += GST_BUFFER_SIZE (buffer) + 8;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
899
  gst_pad_push (avimux->srcpad, GST_DATA (buffer));
900

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
901
  avimux->idx_size += avimux->idx_index * sizeof (gst_riff_index_entry) + 8;
902
903
904
905
906
907

  /* update header */
  avimux->avi_hdr.flags |= GST_RIFF_AVIH_HASINDEX;
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
908
gst_avimux_bigfile (GstAviMux * avimux, gboolean last)
909
910
911
{
  GstBuffer *header;
  GstEvent *event;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
912
913

  if (avimux->is_bigfile) {
914
    /* sarch back */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
915
    event = gst_event_new_seek (GST_FORMAT_BYTES |
916
        GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->avix_start);
Wim Taymans's avatar
Wim Taymans committed
917
    /* if the event succeeds */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
918
    gst_pad_push (avimux->srcpad, GST_DATA (event));
919
920

    /* rewrite AVIX header */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
921
922
    header = gst_avimux_riff_get_avix_header (avimux->datax_size);
    gst_pad_push (avimux->srcpad, GST_DATA (header));
923
924

    /* go back to current location */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
925
    event = gst_event_new_seek (GST_FORMAT_BYTES |
926
        GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH, avimux->total_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
927
    gst_pad_push (avimux->srcpad, GST_DATA (event));
928
929
930
931
932
933
934
935
936
937
  }
  avimux->avix_start = avimux->total_data;

  if (last)
    return;

  avimux->is_bigfile = TRUE;
  avimux->numx_frames = 0;
  avimux->datax_size = 0;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
938
939
940
  header = gst_avimux_riff_get_avix_header (0);
  avimux->total_data += GST_BUFFER_SIZE (header);
  gst_pad_push (avimux->srcpad, GST_DATA (header));
941
942
943
944
945
}

/* enough header blabla now, let's go on to actually writing the headers */

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
946
gst_avimux_start_file (GstAviMux * avimux)
947
948
949
950
951
{
  GstBuffer *header;

  avimux->total_data = 0;
  avimux->total_frames = 0;
952
  avimux->data_size = 4;        /* ? */
953
954
955
956
  avimux->datax_size = 0;
  avimux->num_frames = 0;
  avimux->numx_frames = 0;
  avimux->audio_size = 0;
957
  avimux->audio_time = 0;
958
  avimux->avix_start = 0;
959
960

  avimux->idx_index = 0;
961
  avimux->idx_offset = 0;       /* see 10 lines below */
962
963
964
965
  avimux->idx_size = 0;
  avimux->idx_count = 0;
  avimux->idx = NULL;

966
  /* header */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
967
968
969
  avimux->avi_hdr.streams =
      (avimux->video_pad_connected ? 1 : 0) +
      (avimux->audio_pad_connected ? 1 : 0);
970
  avimux->is_bigfile = FALSE;
971

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
972
973
974
  header = gst_avimux_riff_get_avi_header (avimux);
  avimux->total_data += GST_BUFFER_SIZE (header);
  gst_pad_push (avimux->srcpad, GST_DATA (header));
975

976
977
  avimux->idx_offset = avimux->total_data;

978
  avimux->write_header = FALSE;
979
  avimux->restart = FALSE;
980
981
982
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
983
gst_avimux_stop_file (GstAviMux * avimux)
984
985
986
987
988
{
  GstEvent *event;
  GstBuffer *header;

  /* if bigfile, rewrite header, else write indexes */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
989
990
991
  if (avimux->video_pad_connected) {
    if (avimux->is_bigfile) {
      gst_avimux_bigfile (avimux, TRUE);
992
      avimux->idx_size = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
993
994
    } else {
      gst_avimux_write_index (avimux);
995
996
997
    }
  }

998
  /* set rate and everything having to do with that */
999
  avimux->avi_hdr.max_bps = 0;
1000
1001
1002
  if (avimux->audio_pad_connected) {
    /* calculate bps if needed */
    if (!avimux->auds.av_bps) {
1003
      if (avimux->audio_time) {
1004
        avimux->auds.av_bps =
1005
            (GST_SECOND * avimux->audio_size) / avimux->audio_time;
1006
      } else {
1007
1008
1009
        GST_ELEMENT_ERROR (avimux, STREAM, MUX,
            (_("No or invalid input audio, AVI stream will be corrupt.")),
            (NULL));
1010
        avimux->auds.av_bps = 0;
1011
      }
1012
      avimux->auds_hdr.rate = avimux->auds.av_bps * avimux->auds_hdr.scale;
1013
    }
1014
    avimux->avi_hdr.max_bps += avimux->auds.av_bps;
1015
  }
1016
  if (avimux->video_pad_connected) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1017
    avimux->avi_hdr.max_bps += ((avimux->vids.bit_cnt + 7) / 8) *
1018
        (1000000. / avimux->avi_hdr.us_frame) * avimux->vids.image_size;
1019
  }
1020

1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
  /* statistics/total_frames/... */
  avimux->avi_hdr.tot_frames = avimux->num_frames;
  if (avimux->video_pad_connected) {
    avimux->vids_hdr.length = avimux->num_frames;
  }
  if (avimux->audio_pad_connected) {
    avimux->auds_hdr.length =
        (avimux->audio_time * avimux->auds_hdr.rate) / GST_SECOND;
  }

1031
  /* seek and rewrite the header */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1032
1033
1034
1035
  header = gst_avimux_riff_get_avi_header (avimux