gstavimux.c 68 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
/**
 * SECTION:element-avimux
 *
 * Muxes raw or compressed audio and/or video streams into an AVI file.
32 33 34 35 36
 *
 * <refsect2>
 * <title>Example launch lines</title>
 * <para>(write everything in one line, without the backslash characters)</para>
 * |[
37
 * gst-launch videotestsrc num-buffers=250 \
38 39 40 41 42
 * ! '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
43
 * ]| This will create an .AVI file containing an uncompressed video stream
44 45
 * with a test picture and an uncompressed audio stream containing a 
 * test sound.
46
 * |[
47
 * gst-launch videotestsrc num-buffers=250 \
48 49 50 51 52
 * ! '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
53
 * ]| This will create an .AVI file containing the same test video and sound
54 55 56 57
 * 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.
 * </refsect2>
 */
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
58

59 60 61
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
62

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
63
#include "gst/gst-i18n-plugin.h"
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
64 65 66
#include <stdlib.h>
#include <string.h>

67 68
#include <gst/video/video.h>

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
69 70
#include "gstavimux.h"

71 72
GST_DEBUG_CATEGORY_STATIC (avimux_debug);
#define GST_CAT_DEFAULT avimux_debug
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
73

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
74 75
enum
{
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
76
  ARG_0,
77
  ARG_BIGFILE
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
78 79
};

80 81 82 83 84 85
#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",
86
    "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
87

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

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

193 194 195
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);
196
static void gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
197

198 199 200 201
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
202
    GstPadTemplate * templ, const gchar * name);
203 204
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
205
    guint prop_id, const GValue * value, GParamSpec * pspec);
206
static void gst_avi_mux_get_property (GObject * object,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
207
    guint prop_id, GValue * value, GParamSpec * pspec);
208
static GstStateChangeReturn gst_avi_mux_change_state (GstElement * element,
209
    GstStateChange transition);
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
210 211

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

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

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

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

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

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

256 257 258
  gst_element_class_set_details (element_class, &gst_avi_mux_details);

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

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
261
static void
262 263 264
gst_avi_mux_finalize (GObject * object)
{
  GstAviMux *mux = GST_AVI_MUX (object);
265 266 267 268 269 270 271 272 273 274 275 276 277 278
  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;
279 280 281 282 283 284 285 286 287 288 289

  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
290 291 292 293
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

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

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

299 300 301
  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;
302

303 304 305 306
  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));
307

308 309 310 311
  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);
312 313
}

314 315
/* reset pad to initial state
 * free - if true, release all, not only stream related, data */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
316
static void
317
gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
318
{
319 320 321 322 323 324 325 326 327 328 329
  /* 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
330

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

334
    avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
335 336 337 338 339 340
    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));
341
    memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
342 343 344
  } else {
    GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;

345 346
    audpad->samples = 0;

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

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 379 380 381
    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;
382

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

389 390 391 392 393
  if (avimux->tags_snap) {
    gst_tag_list_free (avimux->tags_snap);
    avimux->tags_snap = NULL;
  }

394
  g_free (avimux->idx);
395 396
  avimux->idx = NULL;

397
  /* state info */
398
  avimux->write_header = TRUE;
399 400

  /* tags */
401
  gst_tag_setter_reset_tags (GST_TAG_SETTER (avimux));
402 403 404 405 406 407 408 409
}

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

411
  /* property */
412 413 414 415 416 417
  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);
418 419 420

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

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

436
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
437

438 439 440 441 442 443 444 445
  /* 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'));

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

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

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

460 461
  avipad->vids.width = width;
  avipad->vids.height = height;
462 463 464 465 466

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

467 468
  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
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 498 499
  /* (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;
  }

500 501 502
  /* codec initialization data, if any */
  codec_data = gst_structure_get_value (structure, "codec_data");
  if (codec_data) {
503 504 505 506
    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);
507 508
  }

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

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

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

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

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

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

      switch (mpegversion) {
        case 2:
583
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '2');
584 585 586
          break;
        case 4:
          /* mplayer/ffmpeg might not work with DIVX, but with FMP4 */
587
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
588 589 590 591
          break;
        default:
          GST_INFO ("unhandled mpegversion : %d, fall back to fourcc=MPEG",
              mpegversion);
592
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
593 594
          break;
      }
595 596
    } else if (!strcmp (mimetype, "video/x-dirac")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('d', 'r', 'a', 'c');
Thiago Santos's avatar
Thiago Santos committed
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
    } else if (!strcmp (mimetype, "video/x-wmv")) {
      gint wmvversion;

      if (gst_structure_get_int (structure, "wmvversion", &wmvversion)) {
        switch (wmvversion) {
          case 1:
            avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '1');
            break;
          case 2:
            avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '2');
            break;
          case 3:
            avipad->vids.compression = GST_MAKE_FOURCC ('W', 'M', 'V', '3');
          default:
            break;
        }
      }
614 615
    } else if (!strcmp (mimetype, "image/x-jpc")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('M', 'J', '2', 'C');
David Schleef's avatar
David Schleef committed
616
    }
617

618
    if (!avipad->vids.compression)
619
      goto refuse_caps;
620 621
  }

622 623 624 625 626 627 628
  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;
629 630

  gst_object_unref (avimux);
631
  return TRUE;
632

633 634 635 636 637 638
refuse_caps:
  {
    GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
    gst_object_unref (avimux);
    return FALSE;
  }
639
}
Wim Taymans's avatar
Wim Taymans committed
640

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
static GstFlowReturn
gst_avi_mux_audsink_scan_mpeg_audio (GstAviMux * avimux, GstAviPad * avipad,
    GstBuffer * buffer)
{
  guint8 *data;
  guint size;
  guint spf;
  guint32 header;
  gulong layer;
  gulong version;
  gint lsf, mpg25;

  data = GST_BUFFER_DATA (buffer);
  size = GST_BUFFER_SIZE (buffer);

  if (size < 4)
    goto not_parsed;

  header = GST_READ_UINT32_BE (data);

  if ((header & 0xffe00000) != 0xffe00000)
    goto not_parsed;

  /* thanks go to mp3parse */
  if (header & (1 << 20)) {
    lsf = (header & (1 << 19)) ? 0 : 1;
    mpg25 = 0;
  } else {
    lsf = 1;
    mpg25 = 1;
  }

  version = 1 + lsf + mpg25;
  layer = 4 - ((header >> 17) & 0x3);

  /* see http://www.codeproject.com/audio/MPEGAudioInfo.asp */
  if (layer == 1)
    spf = 384;
  else if (layer == 2)
    spf = 1152;
  else if (version == 1) {
    spf = 1152;
  } else {
    /* MPEG-2 or "2.5" */
    spf = 576;
  }

  if (G_UNLIKELY (avipad->hdr.scale <= 1))
    avipad->hdr.scale = spf;
  else if (G_UNLIKELY (avipad->hdr.scale != spf)) {
    GST_WARNING_OBJECT (avimux, "input mpeg audio has varying frame size");
    goto cbr_fallback;
  }

  return GST_FLOW_OK;

  /* EXITS */
not_parsed:
  {
    GST_WARNING_OBJECT (avimux, "input mpeg audio is not parsed");
    /* fall-through */
  }
cbr_fallback:
  {
    GST_WARNING_OBJECT (avimux, "falling back to CBR muxing");
    avipad->hdr.scale = 1;
    /* no need to check further */
    avipad->hook = NULL;
    return GST_FLOW_OK;
  }
}

static void
gst_avi_mux_audsink_set_fields (GstAviMux * avimux, GstAviAudioPad * avipad)
{
  if (avipad->parent.hdr.scale > 1) {
    /* vbr case: fixed duration per frame/chunk */
    avipad->parent.hdr.rate = avipad->auds.rate;
    avipad->parent.hdr.samplesize = 0;
    /* FIXME ?? some rumours say this should be largest audio chunk size */
    avipad->auds.blockalign = avipad->parent.hdr.scale;
  } else {
    /* by spec, hdr.rate is av_bps related, is calculated that way in stop_file,
     * and reduces to sample rate in PCM like cases */
    avipad->parent.hdr.rate = avipad->auds.av_bps / avipad->auds.blockalign;
    avipad->parent.hdr.samplesize = avipad->auds.blockalign;
    avipad->parent.hdr.scale = 1;
  }
}

731 732
static gboolean
gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps)
733 734
{
  GstAviMux *avimux;
735 736
  GstAviAudioPad *avipad;
  GstAviCollectData *collect_pad;
David Schleef's avatar
David Schleef committed
737
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
738
  const gchar *mimetype;
739
  const GValue *codec_data;
740
  gint channels, rate;
741

742
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
743

744 745 746 747 748 749 750 751
  /* 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'));

752 753
  GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
      GST_DEBUG_PAD_NAME (pad), vscaps);
754

David Schleef's avatar
David Schleef committed
755 756
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
757

David Schleef's avatar
David Schleef committed
758
  /* we want these for all */
759 760 761 762 763
  if (!gst_structure_get_int (structure, "channels", &channels) ||
      !gst_structure_get_int (structure, "rate", &rate)) {
    goto refuse_caps;
  }

764 765
  avipad->auds.channels = channels;
  avipad->auds.rate = rate;
766

767 768 769 770 771 772 773 774 775
  /* 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
776
  if (!strcmp (mimetype, "audio/x-raw-int")) {
777
    gint width, depth;
778
    gboolean signedness;
779

780
    avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
781

782
    if (!gst_structure_get_int (structure, "width", &width) ||
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
        !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");
800 801 802
      goto refuse_caps;
    }

803 804
    avipad->auds.blockalign = width;
    avipad->auds.size = (width == 8) ? 8 : depth;
805

David Schleef's avatar
David Schleef committed
806
    /* set some more info straight */
807 808 809
    avipad->auds.blockalign /= 8;
    avipad->auds.blockalign *= avipad->auds.channels;
    avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
810
  } else {
811
    avipad->auds.format = 0;
812 813 814 815
    /* set some defaults */
    avipad->auds.blockalign = 1;
    avipad->auds.av_bps = 0;
    avipad->auds.size = 16;
David Schleef's avatar
David Schleef committed
816 817

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

820 821 822 823
      gst_structure_get_int (structure, "mpegversion", &mpegversion);
      switch (mpegversion) {
        case 1:{
          gint layer = 3;
824
          gboolean parsed = FALSE;
825 826

          gst_structure_get_int (structure, "layer", &layer);
827
          gst_structure_get_boolean (structure, "parsed", &parsed);
828 829 830 831 832 833 834 835 836
          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;
          }
837 838 839 840 841 842 843 844
          if (parsed) {
            /* treat as VBR, should also cover CBR case;
             * setup hook to parse frame header and determine spf */
            avipad->parent.hook = gst_avi_mux_audsink_scan_mpeg_audio;
          } else {
            GST_WARNING_OBJECT (avimux, "unparsed MPEG audio input (?), "
                "doing CBR muxing");
          }
845
          break;
846 847
        }
        case 4:
848 849 850 851 852 853 854 855 856
        {
          GstBuffer *codec_data_buf = avipad->auds_codec_data;
          guint codec;

          /* vbr case needs some special handling */
          if (!codec_data_buf || GST_BUFFER_SIZE (codec_data_buf) < 2) {
            GST_WARNING_OBJECT (avimux, "no (valid) codec_data for AAC audio");
            break;
          }
857
          avipad->auds.format = GST_RIFF_WAVE_FORMAT_AAC;
858 859 860
          /* need to determine frame length */
          codec = GST_READ_UINT16_BE (GST_BUFFER_DATA (codec_data_buf));
          avipad->parent.hdr.scale = (codec & 0x4) ? 960 : 1024;
861
          break;
862
        }
863
      }
David Schleef's avatar
David Schleef committed
864
    } else if (!strcmp (mimetype, "audio/x-vorbis")) {
865
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
David Schleef's avatar
David Schleef committed
866
    } else if (!strcmp (mimetype, "audio/x-ac3")) {
867
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_A52;
868 869 870 871 872 873 874 875 876 877
    } 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.size = 8;
      avipad->auds.blockalign = avipad->auds.channels;
      avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
Thiago Santos's avatar
Thiago Santos committed
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
    } else if (!strcmp (mimetype, "audio/x-wma")) {
      gint version;
      gint bitrate;
      gint block_align;

      if (gst_structure_get_int (structure, "wmaversion", &version)) {
        switch (version) {
          case 1:
            avipad->auds.format = GST_RIFF_WAVE_FORMAT_WMAV1;
            break;
          case 2:
            avipad->auds.format = GST_RIFF_WAVE_FORMAT_WMAV2;
            break;
          default:
            break;
        }
      }

      if (avipad->auds.format != 0) {
        if (gst_structure_get_int (structure, "block_align", &block_align)) {
          avipad->auds.blockalign = block_align;
        }
        if (gst_structure_get_int (structure, "bitrate", &bitrate)) {
          avipad->auds.av_bps = bitrate / 8;
        }
      }
David Schleef's avatar
David Schleef committed
904
    }
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
905 906
  }

907 908 909
  if (!avipad->auds.format)
    goto refuse_caps;

910
  gst_avi_mux_audsink_set_fields (avimux, avipad);
911

912 913
  gst_object_unref (avimux);
  return TRUE;
914

915 916 917 918 919 920
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
921 922
}

923

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
924
static GstPad *
925
gst_avi_mux_request_new_pad (GstElement * element,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
926
    GstPadTemplate * templ, const gchar * req_name)
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
927 928 929
{
  GstAviMux *avimux;
  GstPad *newpad;
930
  GstAviPad *avipad;
931
  GstElementClass *klass;
932

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
933 934
  g_return_val_if_fail (templ != NULL, NULL);

935 936
  if (templ->direction != GST_PAD_SINK)
    goto wrong_direction;
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
937

938 939
  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
940