gstavimux.c 57.5 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 340 341 342
    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));
  } else {
    GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;

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

349 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
    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;
378

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

385 386 387 388 389 390 391 392 393 394
  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);
395 396
  avimux->idx = NULL;

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

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);
407

408
  /* property */
409 410 411 412 413 414
  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);
415 416 417

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

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

432
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
433

434 435 436 437 438 439 440 441
  /* 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'));

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

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

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

456 457
  avipad->vids.width = width;
  avipad->vids.height = height;
458 459 460 461 462

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

463 464
  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
465

466 467 468
  /* codec initialization data, if any */
  codec_data = gst_structure_get_value (structure, "codec_data");
  if (codec_data) {
469 470 471 472
    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);
473 474
  }

David Schleef's avatar
David Schleef committed
475 476 477 478
  if (!strcmp (mimetype, "video/x-raw-yuv")) {
    guint32 format;

    gst_structure_get_fourcc (structure, "format", &format);
479
    avipad->vids.compression = format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
480 481
    switch (format) {
      case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
482
        avipad->vids.bit_cnt = 16;
483
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
484
      case GST_MAKE_FOURCC ('I', '4', '2', '0'):
485
        avipad->vids.bit_cnt = 12;
486
        break;
David Schleef's avatar
David Schleef committed
487 488
    }
  } else {
489 490
    avipad->vids.bit_cnt = 24;
    avipad->vids.compression = 0;
David Schleef's avatar
David Schleef committed
491 492 493

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

David Schleef's avatar
David Schleef committed
500 501
      gst_structure_get_int (structure, "divxversion", &divxversion);
      switch (divxversion) {
502
        case 3:
503
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', '3');
504 505
          break;
        case 4:
506
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
507 508
          break;
        case 5:
509
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'X', '5', '0');
510
          break;
511
      }
David Schleef's avatar
David Schleef committed
512
    } else if (!strcmp (mimetype, "video/x-xvid")) {
513
      avipad->vids.compression = GST_MAKE_FOURCC ('X', 'V', 'I', 'D');
David Schleef's avatar
David Schleef committed
514
    } else if (!strcmp (mimetype, "video/x-3ivx")) {
515
      avipad->vids.compression = GST_MAKE_FOURCC ('3', 'I', 'V', '2');
516
    } else if (gst_structure_has_name (structure, "video/x-msmpeg")) {
David Schleef's avatar
David Schleef committed
517
      gint msmpegversion;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
518

David Schleef's avatar
David Schleef committed
519 520
      gst_structure_get_int (structure, "msmpegversion", &msmpegversion);
      switch (msmpegversion) {
521
        case 41:
522
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '4');
523 524
          break;
        case 42:
525
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '2');
526 527
          break;
        case 43:
528
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', '4', '3');
529
          break;
530 531 532
        default:
          GST_INFO ("unhandled msmpegversion : %d, fall back to fourcc=MPEG",
              msmpegversion);
533
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
534
          break;
535
      }
David Schleef's avatar
David Schleef committed
536
    } else if (!strcmp (mimetype, "video/x-dv")) {
537
      avipad->vids.compression = GST_MAKE_FOURCC ('D', 'V', 'S', 'D');
David Schleef's avatar
David Schleef committed
538
    } else if (!strcmp (mimetype, "video/x-h263")) {
539
      avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '3');
540 541
    } else if (!strcmp (mimetype, "video/x-h264")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('H', '2', '6', '4');
David Schleef's avatar
David Schleef committed
542
    } else if (!strcmp (mimetype, "video/mpeg")) {
543 544 545 546 547 548
      gint mpegversion;

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

      switch (mpegversion) {
        case 2:
549
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '2');
550 551 552
          break;
        case 4:
          /* mplayer/ffmpeg might not work with DIVX, but with FMP4 */
553
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
554 555 556 557
          break;
        default:
          GST_INFO ("unhandled mpegversion : %d, fall back to fourcc=MPEG",
              mpegversion);
558
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
559 560
          break;
      }
561 562
    } else if (!strcmp (mimetype, "video/x-dirac")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('d', 'r', 'a', 'c');
David Schleef's avatar
David Schleef committed
563
    }
564

565
    if (!avipad->vids.compression)
566
      goto refuse_caps;
567 568
  }

569 570 571 572 573 574 575
  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;
576 577

  gst_object_unref (avimux);
578
  return TRUE;
579

580 581 582 583 584 585
refuse_caps:
  {
    GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
    gst_object_unref (avimux);
    return FALSE;
  }
586
}
Wim Taymans's avatar
Wim Taymans committed
587

588 589
static gboolean
gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps)
590 591
{
  GstAviMux *avimux;
592 593
  GstAviAudioPad *avipad;
  GstAviCollectData *collect_pad;
David Schleef's avatar
David Schleef committed
594
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
595
  const gchar *mimetype;
596
  const GValue *codec_data;
597
  gint channels, rate;
598

599
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
600

601 602 603 604 605 606 607 608
  /* 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'));

609 610
  GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
      GST_DEBUG_PAD_NAME (pad), vscaps);
611

David Schleef's avatar
David Schleef committed
612 613
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
614

David Schleef's avatar
David Schleef committed
615
  /* we want these for all */
616 617 618 619 620
  if (!gst_structure_get_int (structure, "channels", &channels) ||
      !gst_structure_get_int (structure, "rate", &rate)) {
    goto refuse_caps;
  }

621 622
  avipad->auds.channels = channels;
  avipad->auds.rate = rate;
623

624 625 626 627 628 629 630 631 632
  /* 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
633
  if (!strcmp (mimetype, "audio/x-raw-int")) {
634
    gint width, depth;
635
    gboolean signedness;
636

637
    avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
638

639
    if (!gst_structure_get_int (structure, "width", &width) ||
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
        !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");
657 658 659
      goto refuse_caps;
    }

660 661
    avipad->auds.blockalign = width;
    avipad->auds.size = (width == 8) ? 8 : depth;
662

David Schleef's avatar
David Schleef committed
663
    /* set some more info straight */
664 665 666
    avipad->auds.blockalign /= 8;
    avipad->auds.blockalign *= avipad->auds.channels;
    avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
667
  } else {
668
    avipad->auds.format = 0;
669 670 671 672
    /* set some defaults */
    avipad->auds.blockalign = 1;
    avipad->auds.av_bps = 0;
    avipad->auds.size = 16;
David Schleef's avatar
David Schleef committed
673 674

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

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
      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;
          }
692
          break;
693 694
        }
        case 4:
695
          GST_WARNING ("AAC");
696
          avipad->auds.format = GST_RIFF_WAVE_FORMAT_AAC;
697
          break;
698
      }
David Schleef's avatar
David Schleef committed
699
    } else if (!strcmp (mimetype, "audio/x-vorbis")) {
700
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
David Schleef's avatar
David Schleef committed
701
    } else if (!strcmp (mimetype, "audio/x-ac3")) {
702
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_A52;
703 704 705 706 707 708 709 710 711 712 713
    } 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
714
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
715 716
  }

717 718 719 720
  if (!avipad->auds.format)
    goto refuse_caps;

  avipad->parent.hdr.rate = avipad->auds.rate;
721 722
  avipad->parent.hdr.samplesize = avipad->auds.blockalign;
  avipad->parent.hdr.scale = 1;
723

724 725
  gst_object_unref (avimux);
  return TRUE;
726

727 728 729 730 731 732
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
733 734
}

735

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
736
static GstPad *
737
gst_avi_mux_request_new_pad (GstElement * element,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
738
    GstPadTemplate * templ, const gchar * req_name)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
739 740 741
{
  GstAviMux *avimux;
  GstPad *newpad;
742
  GstAviPad *avipad;
743
  GstElementClass *klass;
744

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
745 746
  g_return_val_if_fail (templ != NULL, NULL);

747 748
  if (templ->direction != GST_PAD_SINK)
    goto wrong_direction;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
749

750 751
  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
752

753 754 755 756
  if (!avimux->write_header)
    goto too_late;

  klass = GST_ELEMENT_GET_CLASS (element);
757

758
  if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
759 760 761 762
    gchar *name;

    /* setup pad */
    name = g_strdup_printf ("audio_%02d", avimux->audio_pads);
763
    GST_DEBUG_OBJECT (avimux, "adding new pad: %s", name);
764 765
    newpad = gst_pad_new_from_template (templ, name);
    g_free (name);
766 767
    gst_pad_set_setcaps_function (newpad,
        GST_DEBUG_FUNCPTR (gst_avi_mux_audsink_set_caps));
768 769 770 771 772 773 774 775

    /* 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
776
  } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
777 778 779 780
    /* 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)
781
      return NULL;
782
    /* setup pad */
783
    GST_DEBUG_OBJECT (avimux, "adding new pad: video_00");
784
    newpad = gst_pad_new_from_template (templ, "video_00");
785 786
    gst_pad_set_setcaps_function (newpad,
        GST_DEBUG_FUNCPTR (gst_avi_mux_vidsink_set_caps));
787 788 789 790 791 792 793 794
    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);
795 796
  } else
    goto wrong_template;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
797

798 799 800
  avipad->collect = gst_collect_pads_add_pad (avimux->collect,
      newpad, sizeof (GstAviCollectData));
  ((GstAviCollectData *) (avipad->collect))->avipad = avipad;
801 802 803 804 805 806 807
  /* 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
808
  gst_element_add_pad (element, newpad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
809

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
810
  return newpad;
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827

  /* 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
828 829
}

830
static void
831
gst_avi_mux_release_pad (GstElement * element, GstPad * pad)
832
{
833
  GstAviMux *avimux = GST_AVI_MUX (element);
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
  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;
    }
856

857
    node = node->next;
858 859
  }

860
  g_warning ("Unknown pad %s", GST_PAD_NAME (pad));
861 862
}

863 864
/* maybe some of these functions should be moved to riff.h? */

865 866
/* DISCLAIMER: this function is fairly ugly. So be it (i.e. it makes the rest easier)
 * so is this struct */
867 868 869 870 871 872

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

874
static void
875 876
gst_avi_mux_write_tag (const GstTagList * list, const gchar * tag,
    gpointer data)
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
{
  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}
  };
893
  gint n, len, plen;
894 895 896
  GstBuffer *buf = ((GstMarkedBuffer *) data)->buffer;
  guint *highmark = ((GstMarkedBuffer *) data)->highmark;
  guint8 *buffdata = GST_BUFFER_DATA (buf) + *highmark;
897 898 899 900 901 902
  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);
903 904 905
      plen = len + 1;
      if (plen & 1)
        plen++;