gstavimux.c 61.6 KB
Newer Older
1 2
/* AVI muxer plugin for GStreamer
 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
3
 *           (C) 2006 Mark Nauwelaerts <manauw@skynet.be>
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 */

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

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/**
 * SECTION:element-avimux
 *
 * <refsect2>
 * <para>
 * Muxes raw or compressed audio and/or video streams into an AVI file.
 * </para>
 * <title>Example launch line</title>
 * <para>
 * (write everything in one line, without the backslash characters)
 * <programlisting>
 * gst-launch-0.10 videotestsrc num-buffers=250 \
 * ! 'video/x-raw-yuv,format=(fourcc)I420,width=320,height=240,framerate=(fraction)25/1' \
 * ! queue ! mux. \
 * audiotestsrc num-buffers=440 ! audioconvert \
 * ! 'audio/x-raw-int,rate=44100,channels=2' ! queue ! mux. \
 * avimux name=mux ! filesink location=test.avi
 * </programlisting>
 * This will create an .AVI file containing an uncompressed video stream
 * with a test picture and an uncompressed audio stream containing a 
 * test sound.
 * </para>
 * <title>Another example launch line</title>
 * <para>
 * (write everything in one line, without the backslash characters)
 * <programlisting>
 * gst-launch-0.10 videotestsrc num-buffers=250 \
 * ! 'video/x-raw-yuv,format=(fourcc)I420,width=320,height=240,framerate=(fraction)25/1' \
 * ! xvidenc ! queue ! mux. \
 * audiotestsrc num-buffers=440 ! audioconvert ! 'audio/x-raw-int,rate=44100,channels=2' \
 * ! lame ! queue ! mux. \
 * avimux name=mux ! filesink location=test.avi
 * </programlisting>
 * This will create an .AVI file containing the same test video and sound
 * as above, only that both streams will be compressed this time. This will
 * only work if you have the necessary encoder elements installed of course.
 * </para>
 * </refsect2>
 *
 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
68

69 70 71
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
72

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73
#include "gst/gst-i18n-plugin.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
74 75 76
#include <stdlib.h>
#include <string.h>

77 78
#include <gst/video/video.h>

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
79 80
#include "gstavimux.h"

81 82
GST_DEBUG_CATEGORY_STATIC (avimux_debug);
#define GST_CAT_DEFAULT avimux_debug
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
83

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84 85
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
86
  ARG_0,
87
  ARG_BIGFILE
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
88 89
};

90 91 92 93 94 95 96 97
#define DEFAULT_BIGFILE TRUE

static const GstElementDetails gst_avi_mux_details =
GST_ELEMENT_DETAILS ("Avi muxer",
    "Codec/Muxer",
    "Muxes audio and video into an avi stream",
    "Ronald Bultje <rbultje@ronald.bitfreak.net>");

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98 99 100 101 102 103
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
104
static GstStaticPadTemplate video_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105 106 107 108
    GST_STATIC_PAD_TEMPLATE ("video_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("video/x-raw-yuv, "
109 110
        "format = (fourcc) { YUY2, I420 }, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
111
        "height = (int) [ 16, 4096 ], "
112
        "framerate = (fraction) [ 0, MAX ]; "
113
        "image/jpeg, "
114
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
115
        "height = (int) [ 16, 4096 ], "
116
        "framerate = (fraction) [ 0, MAX ]; "
117 118 119
        "video/x-divx, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
120
        "framerate = (fraction) [ 0, MAX ], "
121 122 123
        "divxversion = (int) [ 3, 5 ]; "
        "video/x-xvid, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
124
        "height = (int) [ 16, 4096 ], "
125
        "framerate = (fraction) [ 0, MAX ]; "
126 127
        "video/x-3ivx, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
128
        "height = (int) [ 16, 4096 ], "
129
        "framerate = (fraction) [ 0, MAX ]; "
130 131 132
        "video/x-msmpeg, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
133
        "framerate = (fraction) [ 0, MAX ], "
134 135 136 137
        "msmpegversion = (int) [ 41, 43 ]; "
        "video/mpeg, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
138
        "framerate = (fraction) [ 0, MAX ], "
139
        "mpegversion = (int) { 1, 2, 4}, "
140 141 142
        "systemstream = (boolean) FALSE; "
        "video/x-h263, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
143
        "height = (int) [ 16, 4096 ], "
144
        "framerate = (fraction) [ 0, MAX ]; "
145 146 147
        "video/x-h264, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
148
        "framerate = (fraction) [ 0, MAX ]; "
149 150 151
        "video/x-dv, "
        "width = (int) 720, "
        "height = (int) { 576, 480 }, "
152
        "framerate = (fraction) [ 0, MAX ], "
153 154
        "systemstream = (boolean) FALSE; "
        "video/x-huffyuv, "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
155
        "width = (int) [ 16, 4096 ], "
156 157 158
        "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ];"
        "video/x-dirac, "
        "width = (int) [ 16, 4096 ], "
159
        "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
160 161
    );

David Schleef's avatar
David Schleef committed
162
static GstStaticPadTemplate audio_sink_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
163 164 165 166
    GST_STATIC_PAD_TEMPLATE ("audio_%d",
    GST_PAD_SINK,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS ("audio/x-raw-int, "
167 168 169 170 171 172 173 174 175
        "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 ], "
176
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
177 178 179
        "audio/mpeg, "
        "mpegversion = (int) 4, "
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
180
/*#if 0 VC6 doesn't support #if here ...
181
        "audio/x-vorbis, "
182
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
183
#endif*/
184
        "audio/x-ac3, "
185 186 187 188 189
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
        "audio/x-alaw, "
        "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; "
        "audio/x-mulaw, "
        "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; ")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
190 191
    );

192 193 194
static void gst_avi_mux_base_init (gpointer g_class);
static void gst_avi_mux_class_init (GstAviMuxClass * klass);
static void gst_avi_mux_init (GstAviMux * avimux);
195
static void gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
196

197 198 199 200
static GstFlowReturn gst_avi_mux_collect_pads (GstCollectPads * pads,
    GstAviMux * avimux);
static gboolean gst_avi_mux_handle_event (GstPad * pad, GstEvent * event);
static GstPad *gst_avi_mux_request_new_pad (GstElement * element,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
201
    GstPadTemplate * templ, const gchar * name);
202 203
static void gst_avi_mux_release_pad (GstElement * element, GstPad * pad);
static void gst_avi_mux_set_property (GObject * object,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
204
    guint prop_id, const GValue * value, GParamSpec * pspec);
205
static void gst_avi_mux_get_property (GObject * object,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206
    guint prop_id, GValue * value, GParamSpec * pspec);
207
static GstStateChangeReturn gst_avi_mux_change_state (GstElement * element,
208
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
209 210

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
212
GType
213
gst_avi_mux_get_type (void)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
214 215 216 217 218
{
  static GType avimux_type = 0;

  if (!avimux_type) {
    static const GTypeInfo avimux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
219
      sizeof (GstAviMuxClass),
220
      gst_avi_mux_base_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
221
      NULL,
222
      (GClassInitFunc) gst_avi_mux_class_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
223 224
      NULL,
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225
      sizeof (GstAviMux),
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
226
      0,
227
      (GInstanceInitFunc) gst_avi_mux_init,
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
228
    };
229 230 231 232 233
    static const GInterfaceInfo tag_setter_info = {
      NULL,
      NULL,
      NULL
    };
234

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
235
    avimux_type =
236
        g_type_register_static (GST_TYPE_ELEMENT, "GstAviMux", &avimux_info, 0);
237 238
    g_type_add_interface_static (avimux_type, GST_TYPE_TAG_SETTER,
        &tag_setter_info);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
239 240 241 242
  }
  return avimux_type;
}

243
static void
244
gst_avi_mux_base_init (gpointer g_class)
245 246 247 248
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
249
      gst_static_pad_template_get (&src_factory));
250
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
251
      gst_static_pad_template_get (&audio_sink_factory));
252
  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
253
      gst_static_pad_template_get (&video_sink_factory));
254

255 256 257
  gst_element_class_set_details (element_class, &gst_avi_mux_details);

  GST_DEBUG_CATEGORY_INIT (avimux_debug, "avimux", 0, "Muxer for AVI streams");
258 259
}

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
260
static void
261 262 263
gst_avi_mux_finalize (GObject * object)
{
  GstAviMux *mux = GST_AVI_MUX (object);
264 265 266 267 268 269 270 271 272 273 274 275 276 277
  GSList *node;

  /* completely free each sinkpad */
  node = mux->sinkpads;
  while (node) {
    GstAviPad *avipad = (GstAviPad *) node->data;

    node = node->next;

    gst_avi_mux_pad_reset (avipad, TRUE);
    g_free (avipad);
  }
  g_slist_free (mux->sinkpads);
  mux->sinkpads = NULL;
278 279 280 281 282 283 284 285 286 287 288

  g_free (mux->idx);
  mux->idx = NULL;

  gst_object_unref (mux->collect);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gst_avi_mux_class_init (GstAviMuxClass * klass)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
289 290 291 292
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293 294
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
295

296
  parent_class = g_type_class_peek_parent (klass);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
297

298 299 300
  gobject_class->get_property = gst_avi_mux_get_property;
  gobject_class->set_property = gst_avi_mux_set_property;
  gobject_class->finalize = gst_avi_mux_finalize;
301

302 303 304 305
  g_object_class_install_property (gobject_class, ARG_BIGFILE,
      g_param_spec_boolean ("bigfile", "Bigfile Support (>2GB)",
          "Support for openDML-2.0 (big) AVI files", DEFAULT_BIGFILE,
          G_PARAM_READWRITE));
306

307 308 309 310
  gstelement_class->request_new_pad =
      GST_DEBUG_FUNCPTR (gst_avi_mux_request_new_pad);
  gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_avi_mux_release_pad);
  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_avi_mux_change_state);
311 312
}

313 314
/* reset pad to initial state
 * free - if true, release all, not only stream related, data */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315
static void
316
gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
317
{
318 319 320 321 322 323 324 325 326 327 328
  /* generic part */
  memset (&(avipad->hdr), 0, sizeof (gst_riff_strh));

  memset (&(avipad->idx[0]), 0, sizeof (avipad->idx));

  if (free) {
    g_free (avipad->tag);
    avipad->tag = NULL;
    g_free (avipad->idx_tag);
    avipad->idx_tag = NULL;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
329

330 331
  if (avipad->is_video) {
    GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
332

333
    avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
334 335 336 337 338 339
    if (vidpad->vids_codec_data) {
      gst_buffer_unref (vidpad->vids_codec_data);
      vidpad->vids_codec_data = NULL;
    }

    memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids));
340
    memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
341 342 343
  } else {
    GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;

344
    avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
345 346 347 348 349
    if (audpad->auds_codec_data) {
      gst_buffer_unref (audpad->auds_codec_data);
      audpad->auds_codec_data = NULL;
    }

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
    memset (&(audpad->auds), 0, sizeof (gst_riff_strf_auds));
  }
}

static void
gst_avi_mux_reset (GstAviMux * avimux)
{
  GSList *node, *newlist = NULL;

  /* free and reset each sinkpad */
  node = avimux->sinkpads;
  while (node) {
    GstAviPad *avipad = (GstAviPad *) node->data;

    node = node->next;

    gst_avi_mux_pad_reset (avipad, FALSE);
    /* if this pad has collectdata, keep it, otherwise dump it completely */
    if (avipad->collect)
      newlist = g_slist_append (newlist, avipad);
    else {
      gst_avi_mux_pad_reset (avipad, TRUE);
      g_free (avipad);
    }
  }

  /* free the old list of sinkpads, only keep the real collecting ones */
  g_slist_free (avimux->sinkpads);
  avimux->sinkpads = newlist;
379

380 381
  /* avi data */
  avimux->num_frames = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
382
  memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
383
  avimux->avi_hdr.max_bps = 10000000;
384
  avimux->codec_data_size = 0;
385

386 387 388 389 390 391 392 393 394 395
  if (avimux->tags) {
    gst_tag_list_free (avimux->tags);
    avimux->tags = NULL;
  }
  if (avimux->tags_snap) {
    gst_tag_list_free (avimux->tags_snap);
    avimux->tags_snap = NULL;
  }

  g_free (avimux->idx);
396 397
  avimux->idx = NULL;

398
  /* state info */
399
  avimux->write_header = TRUE;
400 401 402 403 404 405 406 407
}

static void
gst_avi_mux_init (GstAviMux * avimux)
{
  avimux->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
  gst_pad_use_fixed_caps (avimux->srcpad);
  gst_element_add_pad (GST_ELEMENT (avimux), avimux->srcpad);
408

409
  /* property */
410 411 412 413 414 415
  avimux->enable_large_avi = DEFAULT_BIGFILE;

  avimux->collect = gst_collect_pads_new ();
  gst_collect_pads_set_function (avimux->collect,
      (GstCollectPadsFunction) (GST_DEBUG_FUNCPTR (gst_avi_mux_collect_pads)),
      avimux);
416 417 418

  /* set to clean state */
  gst_avi_mux_reset (avimux);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
419 420
}

421 422
static gboolean
gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
423 424
{
  GstAviMux *avimux;
425 426
  GstAviVideoPad *avipad;
  GstAviCollectData *collect_pad;
David Schleef's avatar
David Schleef committed
427
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
428
  const gchar *mimetype;
429
  const GValue *fps, *par;
430
  const GValue *codec_data;
431
  gint width, height;
432
  gint par_n, par_d;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433

434
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
435

436 437 438 439 440 441 442 443
  /* find stream data */
  collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad);
  g_assert (collect_pad);
  avipad = (GstAviVideoPad *) collect_pad->avipad;
  g_assert (avipad);
  g_assert (avipad->parent.is_video);
  g_assert (avipad->parent.hdr.type == GST_MAKE_FOURCC ('v', 'i', 'd', 's'));

444 445
  GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
      GST_DEBUG_PAD_NAME (pad), vscaps);
446

David Schleef's avatar
David Schleef committed
447 448
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
449

David Schleef's avatar
David Schleef committed
450
  /* global */
451 452
  avipad->vids.size = sizeof (gst_riff_strf_vids);
  avipad->vids.planes = 1;
453 454 455
  if (!gst_structure_get_int (structure, "width", &width) ||
      !gst_structure_get_int (structure, "height", &height)) {
    goto refuse_caps;
456
  }
David Schleef's avatar
David Schleef committed
457

458 459
  avipad->vids.width = width;
  avipad->vids.height = height;
460 461 462 463 464

  fps = gst_structure_get_value (structure, "framerate");
  if (fps == NULL || !GST_VALUE_HOLDS_FRACTION (fps))
    goto refuse_caps;

465 466
  avipad->parent.hdr.rate = gst_value_get_fraction_numerator (fps);
  avipad->parent.hdr.scale = gst_value_get_fraction_denominator (fps);
David Schleef's avatar
David Schleef committed
467

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
  /* (pixel) aspect ratio data, if any */
  par = gst_structure_get_value (structure, "pixel-aspect-ratio");
  /* only use video properties header if there is non-trivial aspect info */
  if (par && GST_VALUE_HOLDS_FRACTION (par) &&
      ((par_n = gst_value_get_fraction_numerator (par)) !=
          (par_d = gst_value_get_fraction_denominator (par)))) {
    GValue to_ratio = { 0, };
    guint ratio_n, ratio_d;

    /* some fraction voodoo to obtain simplest possible ratio */
    g_value_init (&to_ratio, GST_TYPE_FRACTION);
    gst_value_set_fraction (&to_ratio, width * par_n, height * par_d);
    ratio_n = gst_value_get_fraction_numerator (&to_ratio);
    ratio_d = gst_value_get_fraction_denominator (&to_ratio);
    GST_DEBUG_OBJECT (avimux, "generating vprp data with aspect ratio %d/%d",
        ratio_n, ratio_d);
    /* simply fill in */
    avipad->vprp.vert_rate = avipad->parent.hdr.rate / avipad->parent.hdr.scale;
    avipad->vprp.hor_t_total = width;
    avipad->vprp.vert_lines = height;
    avipad->vprp.aspect = (ratio_n) << 16 | (ratio_d & 0xffff);
    avipad->vprp.width = width;
    avipad->vprp.height = height;
    avipad->vprp.fields = 1;
    avipad->vprp.field_info[0].compressed_bm_height = height;
    avipad->vprp.field_info[0].compressed_bm_width = width;
    avipad->vprp.field_info[0].valid_bm_height = height;
    avipad->vprp.field_info[0].valid_bm_width = width;
  }

498 499 500
  /* codec initialization data, if any */
  codec_data = gst_structure_get_value (structure, "codec_data");
  if (codec_data) {
501 502 503 504
    avipad->vids_codec_data = gst_value_get_buffer (codec_data);
    gst_buffer_ref (avipad->vids_codec_data);
    /* keep global track of size */
    avimux->codec_data_size += GST_BUFFER_SIZE (avipad->vids_codec_data);
505 506
  }

David Schleef's avatar
David Schleef committed
507 508 509 510
  if (!strcmp (mimetype, "video/x-raw-yuv")) {
    guint32 format;

    gst_structure_get_fourcc (structure, "format", &format);
511
    avipad->vids.compression = format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
512 513
    switch (format) {
      case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
514
        avipad->vids.bit_cnt = 16;
515
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
516
      case GST_MAKE_FOURCC ('I', '4', '2', '0'):
517
        avipad->vids.bit_cnt = 12;
518
        break;
David Schleef's avatar
David Schleef committed
519 520
    }
  } else {
521 522
    avipad->vids.bit_cnt = 24;
    avipad->vids.compression = 0;
David Schleef's avatar
David Schleef committed
523 524 525

    /* find format */
    if (!strcmp (mimetype, "video/x-huffyuv")) {
526
      avipad->vids.compression = GST_MAKE_FOURCC ('H', 'F', 'Y', 'U');
527
    } else if (!strcmp (mimetype, "image/jpeg")) {
528
      avipad->vids.compression = GST_MAKE_FOURCC ('M', 'J', 'P', 'G');
David Schleef's avatar
David Schleef committed
529 530
    } else if (!strcmp (mimetype, "video/x-divx")) {
      gint divxversion;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
531

David Schleef's avatar
David Schleef committed
532 533
      gst_structure_get_int (structure, "divxversion", &divxversion);
      switch (divxversion) {
534
        case 3:
535
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
536 537
          break;
        case 4:
538
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
539 540
          break;
        case 5:
541
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
542
          break;
543
      }
David Schleef's avatar
David Schleef committed
544
    } else if (!strcmp (mimetype, "video/x-xvid")) {
545
      avipad->vids.compression = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
David Schleef's avatar
David Schleef committed
546
    } else if (!strcmp (mimetype, "video/x-3ivx")) {
547
      avipad->vids.compression = GST_MAKE_FOURCC ('3', 'I', 'V', '2');
548
    } else if (gst_structure_has_name (structure, "video/x-msmpeg")) {
David Schleef's avatar
David Schleef committed
549
      gint msmpegversion;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550

David Schleef's avatar
David Schleef committed
551 552
      gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
      switch (msmpegversion) {
553
        case 41:
554
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
555 556
          break;
        case 42:
557
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
558 559
          break;
        case 43:
560
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
561
          break;
562 563 564
        default:
          GST_INFO ("unhandled msmpegversion : %d, fall back to fourcc=MPEG",
              msmpegversion);
565
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
566
          break;
567
      }
David Schleef's avatar
David Schleef committed
568
    } else if (!strcmp (mimetype, "video/x-dv")) {
569
      avipad->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
David Schleef's avatar
David Schleef committed
570
    } else if (!strcmp (mimetype, "video/x-h263")) {
571
      avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
572 573
    } else if (!strcmp (mimetype, "video/x-h264")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '4');
David Schleef's avatar
David Schleef committed
574
    } else if (!strcmp (mimetype, "video/mpeg")) {
575 576 577 578 579 580
      gint mpegversion;

      gst_structure_get_int (structure, "mpegversion", &mpegversion);

      switch (mpegversion) {
        case 2:
581
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '2');
582 583 584
          break;
        case 4:
          /* mplayer/ffmpeg might not work with DIVX, but with FMP4 */
585
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
586 587 588 589
          break;
        default:
          GST_INFO ("unhandled mpegversion : %d, fall back to fourcc=MPEG",
              mpegversion);
590
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
591 592
          break;
      }
593 594
    } else if (!strcmp (mimetype, "video/x-dirac")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('d', 'r', 'a', 'c');
David Schleef's avatar
David Schleef committed
595
    }
596

597
    if (!avipad->vids.compression)
598
      goto refuse_caps;
599 600
  }

601 602 603 604 605 606 607
  avipad->parent.hdr.fcc_handler = avipad->vids.compression;
  avipad->vids.image_size = avipad->vids.height * avipad->vids.width;
  /* hm, maybe why avi only handles one stream well ... */
  avimux->avi_hdr.width = avipad->vids.width;
  avimux->avi_hdr.height = avipad->vids.height;
  avimux->avi_hdr.us_frame = 1000000. * avipad->parent.hdr.scale /
      avipad->parent.hdr.rate;
608 609

  gst_object_unref (avimux);
610
  return TRUE;
611

612 613 614 615 616 617
refuse_caps:
  {
    GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
    gst_object_unref (avimux);
    return FALSE;
  }
618
}
Wim Taymans's avatar
Wim Taymans committed
619

620 621
static gboolean
gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps)
622 623
{
  GstAviMux *avimux;
624 625
  GstAviAudioPad *avipad;
  GstAviCollectData *collect_pad;
David Schleef's avatar
David Schleef committed
626
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
627
  const gchar *mimetype;
628
  const GValue *codec_data;
629
  gint channels, rate;
630

631
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
632

633 634 635 636 637 638 639 640
  /* find stream data */
  collect_pad = (GstAviCollectData *) gst_pad_get_element_private (pad);
  g_assert (collect_pad);
  avipad = (GstAviAudioPad *) collect_pad->avipad;
  g_assert (avipad);
  g_assert (!avipad->parent.is_video);
  g_assert (avipad->parent.hdr.type == GST_MAKE_FOURCC ('a', 'u', 'd', 's'));

641 642
  GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
      GST_DEBUG_PAD_NAME (pad), vscaps);
643

David Schleef's avatar
David Schleef committed
644 645
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
646

David Schleef's avatar
David Schleef committed
647
  /* we want these for all */
648 649 650 651 652
  if (!gst_structure_get_int (structure, "channels", &channels) ||
      !gst_structure_get_int (structure, "rate", &rate)) {
    goto refuse_caps;
  }

653 654
  avipad->auds.channels = channels;
  avipad->auds.rate = rate;
655

656 657 658 659 660 661 662 663 664
  /* codec initialization data, if any */
  codec_data = gst_structure_get_value (structure, "codec_data");
  if (codec_data) {
    avipad->auds_codec_data = gst_value_get_buffer (codec_data);
    gst_buffer_ref (avipad->auds_codec_data);
    /* keep global track of size */
    avimux->codec_data_size += GST_BUFFER_SIZE (avipad->auds_codec_data);
  }

David Schleef's avatar
David Schleef committed
665
  if (!strcmp (mimetype, "audio/x-raw-int")) {
666
    gint width, depth;
667
    gboolean signedness;
668

669
    avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
670

671
    if (!gst_structure_get_int (structure, "width", &width) ||
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
        !gst_structure_get_int (structure, "depth", &depth) ||
        !gst_structure_get_boolean (structure, "signed", &signedness)) {
      GST_DEBUG_OBJECT (avimux,
          "broken caps, width/depth/signed field missing");
      goto refuse_caps;
    }

    /* no clear place to put different values for these while keeping to spec */
    if (width != depth) {
      GST_DEBUG_OBJECT (avimux, "width must be same as depth!");
      goto refuse_caps;
    }

    /* because that's the way the caps will be recreated from riff data */
    if ((width == 8 && signedness) || (width == 16 && !signedness)) {
      GST_DEBUG_OBJECT (avimux,
          "8-bit PCM must be unsigned, 16-bit PCM signed");
689 690 691
      goto refuse_caps;
    }

692 693
    avipad->auds.blockalign = width;
    avipad->auds.size = (width == 8) ? 8 : depth;
694

David Schleef's avatar
David Schleef committed
695
    /* set some more info straight */
696 697 698
    avipad->auds.blockalign /= 8;
    avipad->auds.blockalign *= avipad->auds.channels;
    avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
699
  } else {
700
    avipad->auds.format = 0;
701 702 703 704
    /* set some defaults */
    avipad->auds.blockalign = 1;
    avipad->auds.av_bps = 0;
    avipad->auds.size = 16;
David Schleef's avatar
David Schleef committed
705 706

    if (!strcmp (mimetype, "audio/mpeg")) {
707
      gint mpegversion;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
708

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
      gst_structure_get_int (structure, "mpegversion", &mpegversion);
      switch (mpegversion) {
        case 1:{
          gint layer = 3;

          gst_structure_get_int (structure, "layer", &layer);
          switch (layer) {
            case 3:
              avipad->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL3;
              break;
            case 1:
            case 2:
              avipad->auds.format = GST_RIFF_WAVE_FORMAT_MPEGL12;
              break;
          }
724
          break;
725 726
        }
        case 4:
727
          GST_WARNING ("AAC");
728
          avipad->auds.format = GST_RIFF_WAVE_FORMAT_AAC;
729
          break;
730
      }
David Schleef's avatar
David Schleef committed
731
    } else if (!strcmp (mimetype, "audio/x-vorbis")) {
732
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
David Schleef's avatar
David Schleef committed
733
    } else if (!strcmp (mimetype, "audio/x-ac3")) {
734
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_A52;
735 736 737 738 739 740 741 742 743 744 745
    } else if (!strcmp (mimetype, "audio/x-alaw")) {
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_ALAW;
      avipad->auds.size = 8;
      avipad->auds.blockalign = avipad->auds.channels;
      avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
    } else if (!strcmp (mimetype, "audio/x-mulaw")) {
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_MULAW;
      avipad->auds.av_bps = 8;
      avipad->auds.size = 8;
      avipad->auds.blockalign = avipad->auds.channels;
      avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
David Schleef's avatar
David Schleef committed
746
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
747 748
  }

749 750 751 752
  if (!avipad->auds.format)
    goto refuse_caps;

  avipad->parent.hdr.rate = avipad->auds.rate;
753 754
  avipad->parent.hdr.samplesize = avipad->auds.blockalign;
  avipad->parent.hdr.scale = 1;
755

756 757
  gst_object_unref (avimux);
  return TRUE;
758

759 760 761 762 763 764
refuse_caps:
  {
    GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
    gst_object_unref (avimux);
    return FALSE;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
765 766
}

767

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
768
static GstPad *
769
gst_avi_mux_request_new_pad (GstElement * element,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
770
    GstPadTemplate * templ, const gchar * req_name)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
771 772 773
{
  GstAviMux *avimux;
  GstPad *newpad;
774
  GstAviPad *avipad;
775
  GstElementClass *klass;
776

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
777 778
  g_return_val_if_fail (templ != NULL, NULL);

779 780
  if (templ->direction != GST_PAD_SINK)
    goto wrong_direction;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
781

782 783
  g_return_val_if_fail (GST_IS_AVI_MUX (element), NULL);
  avimux = GST_AVI_MUX (element);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
784

785 786 787 788
  if (!avimux->write_header)
    goto too_late;

  klass = GST_ELEMENT_GET_CLASS (element);
789

790
  if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
791 792 793 794
    gchar *name;

    /* setup pad */
    name = g_strdup_printf ("audio_%02d", avimux->audio_pads);
795
    GST_DEBUG_OBJECT (avimux, "adding new pad: %s", name);
796 797
    newpad = gst_pad_new_from_template (templ, name);
    g_free (name);
798 799
    gst_pad_set_setcaps_function (newpad,
        GST_DEBUG_FUNCPTR (gst_avi_mux_audsink_set_caps));
800 801 802 803 804 805 806 807

    /* init pad specific data */
    avipad = g_malloc0 (sizeof (GstAviAudioPad));
    avipad->is_video = FALSE;
    avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
    avimux->audio_pads++;
    /* audio goes last */
    avimux->sinkpads = g_slist_append (avimux->sinkpads, avipad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
808
  } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
809 810 811 812
    /* though streams are pretty generic and relatively self-contained,
     * some video info goes in a single avi header -and therefore mux struct-
     * so video restricted to one stream */
    if (avimux->video_pads > 0)
813
      return NULL;
814
    /* setup pad */
815
    GST_DEBUG_OBJECT (avimux, "adding new pad: video_00");
816
    newpad = gst_pad_new_from_template (templ, "video_00");
817 818
    gst_pad_set_setcaps_function (newpad,
        GST_DEBUG_FUNCPTR (gst_avi_mux_vidsink_set_caps));
819 820 821 822 823 824 825 826
    avipad = g_malloc0 (sizeof (GstAviVideoPad));

    /* init pad specific data */
    avipad->is_video = TRUE;
    avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
    avimux->video_pads++;
    /* video goes first */
    avimux->sinkpads = g_slist_prepend (avimux->sinkpads, avipad);
827 828
  } else
    goto wrong_template;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
829

830 831 832
  avipad->collect = gst_collect_pads_add_pad (avimux->collect,
      newpad, sizeof (GstAviCollectData));
  ((GstAviCollectData *) (avipad->collect))->avipad = avipad;
833 834 835 836 837 838 839
  /* FIXME: hacked way to override/extend the event function of
   * GstCollectPads; because it sets its own event function giving the
   * element no access to events */
  avimux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
  gst_pad_set_event_function (newpad,
      GST_DEBUG_FUNCPTR (gst_avi_mux_handle_event));

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
840
  gst_element_add_pad (element, newpad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
841

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
842
  return newpad;
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859

  /* ERRORS */
wrong_direction:
  {
    g_warning ("avimux: request pad that is not a SINK pad\n");
    return NULL;
  }
too_late:
  {
    g_warning ("avimux: request pad cannot be added after streaming started\n");
    return NULL;
  }
wrong_template:
  {
    g_warning ("avimuxx: this is not our template!\n");
    return NULL;
  }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
860 861
}

862
static void
863
gst_avi_mux_release_pad (GstElement * element, GstPad * pad)
864
{
865
  GstAviMux *avimux = GST_AVI_MUX (element);
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
  GSList *node;

  node = avimux->sinkpads;
  while (node) {
    GstAviPad *avipad = (GstAviPad *) node->data;

    if (avipad->collect->pad == pad) {
      /* pad count should not be adjusted,
       * as it also represent number of streams present */
      avipad->collect = NULL;
      GST_DEBUG_OBJECT (avimux, "removed pad '%s'", GST_PAD_NAME (pad));
      gst_collect_pads_remove_pad (avimux->collect, pad);
      gst_element_remove_pad (element, pad);
      /* if not started yet, we can remove any sign this pad ever existed */
      /* in this case _start will take care of the real pad count */
      if (avimux->write_header) {
        avimux->sinkpads = g_slist_remove (avimux->sinkpads, avipad);
        gst_avi_mux_pad_reset (avipad, TRUE);
        g_free (avipad);
      }
      return;
    }
888

889
    node = node->next;
890 891
  }

892
  g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
893 894
}

895 896
/* maybe some of these functions should be moved to riff.h? */

897 898
/* DISCLAIMER: this function is fairly ugly. So be it (i.e. it makes the rest easier)
 * so is this struct */
899 900 901 902 903 904

typedef struct _GstMarkedBuffer
{
  guint *highmark;
  GstBuffer *buffer;
} GstMarkedBuffer;
905

906
static void