gstavimux.c 67.3 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's avatar
Andy 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 \
Wim Taymans's avatar
Wim Taymans committed
38
 * ! 'video/x-raw,format=(string)I420,width=320,height=240,framerate=(fraction)25/1' \
39 40
 * ! queue ! mux. \
 * audiotestsrc num-buffers=440 ! audioconvert \
Wim Taymans's avatar
Wim Taymans committed
41
 * ! 'audio/x-raw,rate=44100,channels=2' ! queue ! mux. \
42
 * avimux name=mux ! filesink location=test.avi
43
 * ]| This will create an .AVI file containing an uncompressed video stream
44
 * with a test picture and an uncompressed audio stream containing a
45
 * test sound.
46
 * |[
47
 * gst-launch videotestsrc num-buffers=250 \
Wim Taymans's avatar
Wim Taymans committed
48
 * ! 'video/x-raw,format=(string)I420,width=320,height=240,framerate=(fraction)25/1' \
49
 * ! xvidenc ! queue ! mux. \
Wim Taymans's avatar
Wim Taymans committed
50
 * audiotestsrc num-buffers=440 ! audioconvert ! 'audio/x-raw,rate=44100,channels=2' \
51 52
 * ! 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's avatar
Andy Wingo committed
58

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

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

68
#include <gst/video/video.h>
Wim Taymans's avatar
Wim Taymans committed
69
#include <gst/audio/audio.h>
70
#include <gst/base/gstbytewriter.h>
71

Andy Wingo's avatar
Andy Wingo committed
72 73
#include "gstavimux.h"

74 75
GST_DEBUG_CATEGORY_STATIC (avimux_debug);
#define GST_CAT_DEFAULT avimux_debug
Andy Wingo's avatar
Andy Wingo committed
76

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
77 78
enum
{
Andy Wingo's avatar
Andy Wingo committed
79
  ARG_0,
80
  ARG_BIGFILE
Andy Wingo's avatar
Andy Wingo committed
81 82
};

83 84
#define DEFAULT_BIGFILE TRUE

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
85 86 87 88 89 90
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
91
static GstStaticPadTemplate video_sink_factory =
92
    GST_STATIC_PAD_TEMPLATE ("video_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93 94
    GST_PAD_SINK,
    GST_PAD_REQUEST,
Wim Taymans's avatar
Wim Taymans committed
95
    GST_STATIC_CAPS ("video/x-raw, "
96
        "format = (string) { YUY2, I420 }, "
97
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
98
        "height = (int) [ 16, 4096 ], "
99
        "framerate = (fraction) [ 0, MAX ]; "
100
        "image/jpeg, "
101
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
102
        "height = (int) [ 16, 4096 ], "
103
        "framerate = (fraction) [ 0, MAX ]; "
104 105 106
        "video/x-divx, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
107
        "framerate = (fraction) [ 0, MAX ], "
108 109 110
        "divxversion = (int) [ 3, 5 ]; "
        "video/x-xvid, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
111
        "height = (int) [ 16, 4096 ], "
112
        "framerate = (fraction) [ 0, MAX ]; "
113 114
        "video/x-3ivx, "
        "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-msmpeg, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
120
        "framerate = (fraction) [ 0, MAX ], "
121 122 123 124
        "msmpegversion = (int) [ 41, 43 ]; "
        "video/mpeg, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
125
        "framerate = (fraction) [ 0, MAX ], "
126
        "mpegversion = (int) { 1, 2, 4}, "
127 128 129
        "systemstream = (boolean) FALSE; "
        "video/x-h263, "
        "width = (int) [ 16, 4096 ], "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
130
        "height = (int) [ 16, 4096 ], "
131
        "framerate = (fraction) [ 0, MAX ]; "
132
        "video/x-h264, "
133 134
        "stream-format = (string) byte-stream, "
        "alignment = (string) au, "
135 136
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], "
137
        "framerate = (fraction) [ 0, MAX ]; "
138 139 140
        "video/x-dv, "
        "width = (int) 720, "
        "height = (int) { 576, 480 }, "
141
        "framerate = (fraction) [ 0, MAX ], "
142 143
        "systemstream = (boolean) FALSE; "
        "video/x-huffyuv, "
Ronald S. Bultje's avatar
Ronald S. Bultje committed
144
        "width = (int) [ 16, 4096 ], "
145
        "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ];"
Thiago Santos's avatar
Thiago Santos committed
146 147 148
        "video/x-wmv, "
        "width = (int) [ 16, 4096 ], "
        "height = (int) [ 16, 4096 ], " "framerate = (fraction) [ 0, MAX ], "
149 150 151 152
        "wmvversion = (int) [ 1, 3];"
        "image/x-jpc, "
        "width = (int) [ 1, 2147483647 ], "
        "height = (int) [ 1, 2147483647 ], "
153 154 155 156
        "framerate = (fraction) [ 0, MAX ];"
        "video/x-vp8, "
        "width = (int) [ 1, 2147483647 ], "
        "height = (int) [ 1, 2147483647 ], "
157
        "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 =
161
    GST_STATIC_PAD_TEMPLATE ("audio_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162 163
    GST_PAD_SINK,
    GST_PAD_REQUEST,
Wim Taymans's avatar
Wim Taymans committed
164 165
    GST_STATIC_CAPS ("audio/x-raw, "
        "format = (string) { U8, S16LE }, "
166 167 168 169 170
        "rate = (int) [ 1000, 96000 ], "
        "channels = (int) [ 1, 2 ]; "
        "audio/mpeg, "
        "mpegversion = (int) 1, "
        "layer = (int) [ 1, 3 ], "
171
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
172 173
        "audio/mpeg, "
        "mpegversion = (int) 4, "
174
        "stream-format = (string) raw, "
175
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
176
/*#if 0 VC6 doesn't support #if here ...
177
        "audio/x-vorbis, "
178
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 2 ]; "
179
#endif*/
180
        "audio/x-ac3, "
181
        "rate = (int) [ 1000, 96000 ], " "channels = (int) [ 1, 6 ]; "
182 183 184
        "audio/x-alaw, "
        "rate = (int) [ 1000, 48000 ], " "channels = (int) [ 1, 2 ]; "
        "audio/x-mulaw, "
Thiago Santos's avatar
Thiago Santos committed
185 186 187 188
        "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
189 190
    );

191
static void gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
192

193
static GstFlowReturn gst_avi_mux_collect_pads (GstCollectPads2 * pads,
194
    GstAviMux * avimux);
195 196
static gboolean gst_avi_mux_handle_event (GstCollectPads2 * pad,
    GstCollectData2 * data, GstEvent * event, gpointer user_data);
197
static GstPad *gst_avi_mux_request_new_pad (GstElement * element,
198
    GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
199 200
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
201
    guint prop_id, const GValue * value, GParamSpec * pspec);
202
static void gst_avi_mux_get_property (GObject * object,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
203
    guint prop_id, GValue * value, GParamSpec * pspec);
204
static GstStateChangeReturn gst_avi_mux_change_state (GstElement * element,
205
    GstStateChange transition);
Andy Wingo's avatar
Andy Wingo committed
206

Wim Taymans's avatar
Wim Taymans committed
207 208 209
#define gst_avi_mux_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstAviMux, gst_avi_mux, GST_TYPE_ELEMENT,
    G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
210

Andy Wingo's avatar
Andy Wingo committed
211
static void
212 213 214
gst_avi_mux_finalize (GObject * object)
{
  GstAviMux *mux = GST_AVI_MUX (object);
215 216 217 218 219 220 221 222 223 224 225 226 227 228
  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;
229 230 231 232 233 234 235 236 237 238 239

  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's avatar
Andy Wingo committed
240 241 242 243
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
244 245
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Andy Wingo's avatar
Andy Wingo committed
246

Wim Taymans's avatar
Wim Taymans committed
247
  GST_DEBUG_CATEGORY_INIT (avimux_debug, "avimux", 0, "Muxer for AVI streams");
Andy Wingo's avatar
Andy Wingo committed
248

249 250 251
  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;
252

253 254 255
  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,
256
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
257

258 259 260 261
  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);
Wim Taymans's avatar
Wim Taymans committed
262 263 264 265 266 267 268 269 270 271 272 273

  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&audio_sink_factory));
  gst_element_class_add_pad_template (gstelement_class,
      gst_static_pad_template_get (&video_sink_factory));

  gst_element_class_set_details_simple (gstelement_class, "Avi muxer",
      "Codec/Muxer",
      "Muxes audio and video into an avi stream",
      "GStreamer maintainers <gstreamer-devel@lists.sourceforge.net>");
274 275
}

276 277
/* reset pad to initial state
 * free - if true, release all, not only stream related, data */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
278
static void
279
gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
Andy Wingo's avatar
Andy Wingo committed
280
{
281 282 283 284 285 286 287 288 289 290 291
  /* 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's avatar
Andy Wingo committed
292

293 294
  if (avipad->is_video) {
    GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
295

296
    avipad->hdr.type = GST_MAKE_FOURCC ('v', 'i', 'd', 's');
297 298 299 300 301
    if (vidpad->vids_codec_data) {
      gst_buffer_unref (vidpad->vids_codec_data);
      vidpad->vids_codec_data = NULL;
    }

302 303 304 305 306
    if (vidpad->prepend_buffer) {
      gst_buffer_unref (vidpad->prepend_buffer);
      vidpad->prepend_buffer = NULL;
    }

307
    memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids));
308
    memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
309 310 311
  } else {
    GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;

312 313
    audpad->samples = 0;

314
    avipad->hdr.type = GST_MAKE_FOURCC ('a', 'u', 'd', 's');
315 316 317 318 319
    if (audpad->auds_codec_data) {
      gst_buffer_unref (audpad->auds_codec_data);
      audpad->auds_codec_data = NULL;
    }

320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
    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;
349

350 351
  /* avi data */
  avimux->num_frames = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
352
  memset (&(avimux->avi_hdr), 0, sizeof (gst_riff_avih));
353
  avimux->avi_hdr.max_bps = 10000000;
354
  avimux->codec_data_size = 0;
355

356 357 358 359 360
  if (avimux->tags_snap) {
    gst_tag_list_free (avimux->tags_snap);
    avimux->tags_snap = NULL;
  }

361
  g_free (avimux->idx);
362 363
  avimux->idx = NULL;

364
  /* state info */
365
  avimux->write_header = TRUE;
366 367

  /* tags */
368
  gst_tag_setter_reset_tags (GST_TAG_SETTER (avimux));
369 370 371 372 373 374 375 376
}

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

378
  /* property */
379 380
  avimux->enable_large_avi = DEFAULT_BIGFILE;

381 382 383
  avimux->collect = gst_collect_pads2_new ();
  gst_collect_pads2_set_function (avimux->collect,
      (GstCollectPads2Function) (GST_DEBUG_FUNCPTR (gst_avi_mux_collect_pads)),
384
      avimux);
385 386 387
  gst_collect_pads2_set_event_function (avimux->collect,
      (GstCollectPads2EventFunction) (GST_DEBUG_FUNCPTR
          (gst_avi_mux_handle_event)), avimux);
388 389 390

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

393 394
static gboolean
gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
Andy Wingo's avatar
Andy Wingo committed
395 396
{
  GstAviMux *avimux;
397 398
  GstAviVideoPad *avipad;
  GstAviCollectData *collect_pad;
David Schleef's avatar
David Schleef committed
399
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
400
  const gchar *mimetype;
401
  const GValue *fps, *par;
402
  const GValue *codec_data;
403
  gint width, height;
404
  gint par_n, par_d;
405
  gboolean codec_data_in_headers = TRUE;
Andy Wingo's avatar
Andy Wingo committed
406

407
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
Andy Wingo's avatar
Andy Wingo committed
408

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

417 418
  GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
      GST_DEBUG_PAD_NAME (pad), vscaps);
419

David Schleef's avatar
David Schleef committed
420 421
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
422

David Schleef's avatar
David Schleef committed
423
  /* global */
424 425
  avipad->vids.size = sizeof (gst_riff_strf_vids);
  avipad->vids.planes = 1;
426 427 428
  if (!gst_structure_get_int (structure, "width", &width) ||
      !gst_structure_get_int (structure, "height", &height)) {
    goto refuse_caps;
429
  }
David Schleef's avatar
David Schleef committed
430

431 432
  avipad->vids.width = width;
  avipad->vids.height = height;
433 434 435 436 437

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

438 439
  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
440

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
  /* (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;
  }

Wim Taymans's avatar
Wim Taymans committed
471 472 473
  if (!strcmp (mimetype, "video/x-raw")) {
    const gchar *format;
    GstVideoFormat fmt;
David Schleef's avatar
David Schleef committed
474

Wim Taymans's avatar
Wim Taymans committed
475 476 477 478 479 480
    format = gst_structure_get_string (structure, "format");
    fmt = gst_video_format_from_string (format);

    switch (fmt) {
      case GST_VIDEO_FORMAT_YUY2:
        avipad->vids.compression = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
481
        avipad->vids.bit_cnt = 16;
482
        break;
Wim Taymans's avatar
Wim Taymans committed
483 484
      case GST_VIDEO_FORMAT_I420:
        avipad->vids.compression = GST_MAKE_FOURCC ('I', '4', '2', '0');
485
        avipad->vids.bit_cnt = 12;
486
        break;
Wim Taymans's avatar
Wim Taymans committed
487 488
      default:
        break;
David Schleef's avatar
David Schleef committed
489 490
    }
  } else {
491 492
    avipad->vids.bit_cnt = 24;
    avipad->vids.compression = 0;
David Schleef's avatar
David Schleef committed
493 494 495

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

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

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

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

      switch (mpegversion) {
        case 2:
551
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'G', '2');
552 553 554
          break;
        case 4:
          /* mplayer/ffmpeg might not work with DIVX, but with FMP4 */
555
          avipad->vids.compression = GST_MAKE_FOURCC ('D', 'I', 'V', 'X');
556 557 558 559 560

          /* DIVX/XVID in AVI store the codec_data chunk as part of the
             first data buffer. So for this case, we prepend the codec_data
             blob (if any) to that first buffer */
          codec_data_in_headers = FALSE;
561 562 563 564
          break;
        default:
          GST_INFO ("unhandled mpegversion : %d, fall back to fourcc=MPEG",
              mpegversion);
565
          avipad->vids.compression = GST_MAKE_FOURCC ('M', 'P', 'E', 'G');
566 567
          break;
      }
Thiago Santos's avatar
Thiago Santos committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
    } 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;
        }
      }
585 586
    } else if (!strcmp (mimetype, "image/x-jpc")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('M', 'J', '2', 'C');
587 588
    } else if (!strcmp (mimetype, "video/x-vp8")) {
      avipad->vids.compression = GST_MAKE_FOURCC ('V', 'P', '8', '0');
David Schleef's avatar
David Schleef committed
589
    }
590

591
    if (!avipad->vids.compression)
592
      goto refuse_caps;
593 594
  }

595 596 597 598 599 600 601
  /* codec initialization data, if any */
  codec_data = gst_structure_get_value (structure, "codec_data");
  if (codec_data) {
    if (codec_data_in_headers) {
      avipad->vids_codec_data = gst_value_get_buffer (codec_data);
      gst_buffer_ref (avipad->vids_codec_data);
      /* keep global track of size */
Wim Taymans's avatar
Wim Taymans committed
602
      avimux->codec_data_size += gst_buffer_get_size (avipad->vids_codec_data);
603 604 605 606 607 608
    } else {
      avipad->prepend_buffer =
          gst_buffer_ref (gst_value_get_buffer (codec_data));
    }
  }

609 610 611 612 613 614 615
  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;
616 617

  gst_object_unref (avimux);
618
  return TRUE;
619

620 621 622 623 624 625
refuse_caps:
  {
    GST_WARNING_OBJECT (avimux, "refused caps %" GST_PTR_FORMAT, vscaps);
    gst_object_unref (avimux);
    return FALSE;
  }
626
}
Wim Taymans's avatar
Wim Taymans committed
627

628 629 630 631
static GstFlowReturn
gst_avi_mux_audsink_scan_mpeg_audio (GstAviMux * avimux, GstAviPad * avipad,
    GstBuffer * buffer)
{
Wim Taymans's avatar
Wim Taymans committed
632
  GstMapInfo map;
633 634 635 636 637 638
  guint spf;
  guint32 header;
  gulong layer;
  gulong version;
  gint lsf, mpg25;

Wim Taymans's avatar
Wim Taymans committed
639 640
  gst_buffer_map (buffer, &map, GST_MAP_READ);
  if (map.size < 4)
641 642
    goto not_parsed;

Wim Taymans's avatar
Wim Taymans committed
643
  header = GST_READ_UINT32_BE (map.data);
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

  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;
  }
Wim Taymans's avatar
Wim Taymans committed
678
done:
Wim Taymans's avatar
Wim Taymans committed
679
  gst_buffer_unmap (buffer, &map);
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694

  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;
Wim Taymans's avatar
Wim Taymans committed
695
    goto done;
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
  }
}

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;
  }
}

717 718
static gboolean
gst_avi_mux_audsink_set_caps (GstPad * pad, GstCaps * vscaps)
719 720
{
  GstAviMux *avimux;
721 722
  GstAviAudioPad *avipad;
  GstAviCollectData *collect_pad;
David Schleef's avatar
David Schleef committed
723
  GstStructure *structure;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
724
  const gchar *mimetype;
725
  const GValue *codec_data;
726
  gint channels, rate;
727

728
  avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
729

730 731 732 733 734 735 736 737
  /* 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'));

738 739
  GST_DEBUG_OBJECT (avimux, "%s:%s, caps=%" GST_PTR_FORMAT,
      GST_DEBUG_PAD_NAME (pad), vscaps);
740

David Schleef's avatar
David Schleef committed
741 742
  structure = gst_caps_get_structure (vscaps, 0);
  mimetype = gst_structure_get_name (structure);
743

David Schleef's avatar
David Schleef committed
744
  /* we want these for all */
745 746 747 748 749
  if (!gst_structure_get_int (structure, "channels", &channels) ||
      !gst_structure_get_int (structure, "rate", &rate)) {
    goto refuse_caps;
  }

750 751
  avipad->auds.channels = channels;
  avipad->auds.rate = rate;
752

753 754 755 756 757 758
  /* 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 */
Wim Taymans's avatar
Wim Taymans committed
759
    avimux->codec_data_size += gst_buffer_get_size (avipad->auds_codec_data);
760 761
  }

Wim Taymans's avatar
Wim Taymans committed
762 763 764
  if (!strcmp (mimetype, "audio/x-raw")) {
    const gchar *format;
    GstAudioFormat fmt;
765

Wim Taymans's avatar
Wim Taymans committed
766 767
    format = gst_structure_get_string (structure, "format");
    fmt = gst_audio_format_from_string (format);
768

Wim Taymans's avatar
Wim Taymans committed
769 770 771 772 773 774 775 776 777 778 779
    switch (fmt) {
      case GST_AUDIO_FORMAT_U8:
        avipad->auds.blockalign = 8;
        avipad->auds.size = 8;
        break;
      case GST_AUDIO_FORMAT_S16:
        avipad->auds.blockalign = 16;
        avipad->auds.size = 16;
        break;
      default:
        goto refuse_caps;
780 781
    }

Wim Taymans's avatar
Wim Taymans committed
782
    avipad->auds.format = GST_RIFF_WAVE_FORMAT_PCM;
David Schleef's avatar
David Schleef committed
783
    /* set some more info straight */
784 785 786
    avipad->auds.blockalign /= 8;
    avipad->auds.blockalign *= avipad->auds.channels;
    avipad->auds.av_bps = avipad->auds.blockalign * avipad->auds.rate;
787
  } else {
788
    avipad->auds.format = 0;
789 790 791 792
    /* set some defaults */
    avipad->auds.blockalign = 1;
    avipad->auds.av_bps = 0;
    avipad->auds.size = 16;
David Schleef's avatar
David Schleef committed
793 794

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

797 798 799 800
      gst_structure_get_int (structure, "mpegversion", &mpegversion);
      switch (mpegversion) {
        case 1:{
          gint layer = 3;
801
          gboolean parsed = FALSE;
802 803

          gst_structure_get_int (structure, "layer", &layer);
804
          gst_structure_get_boolean (structure, "parsed", &parsed);
805 806 807 808 809 810 811 812 813
          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;
          }
814 815 816 817 818 819 820 821
          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");
          }
822
          break;
823 824
        }
        case 4:
825 826
        {
          GstBuffer *codec_data_buf = avipad->auds_codec_data;
Thiago Santos's avatar
Thiago Santos committed
827
          const gchar *stream_format;
828
          guint codec;
Wim Taymans's avatar
Wim Taymans committed
829
          guint8 data[2];
830

Thiago Santos's avatar
Thiago Santos committed
831 832
          stream_format = gst_structure_get_string (structure, "stream-format");
          if (stream_format) {
833
            if (strcmp (stream_format, "raw") != 0) {
Thiago Santos's avatar
Thiago Santos committed
834
              GST_WARNING_OBJECT (avimux, "AAC's stream format '%s' is not "
835
                  "supported, please use 'raw'", stream_format);
Thiago Santos's avatar
Thiago Santos committed
836 837 838 839
              break;
            }
          } else {
            GST_WARNING_OBJECT (avimux, "AAC's stream-format not specified, "
840
                "assuming 'raw'");
Thiago Santos's avatar
Thiago Santos committed
841 842
          }

843
          /* vbr case needs some special handling */
Wim Taymans's avatar
Wim Taymans committed
844
          if (!codec_data_buf || gst_buffer_get_size (codec_data_buf) < 2) {
845 846 847
            GST_WARNING_OBJECT (avimux, "no (valid) codec_data for AAC audio");
            break;
          }
848
          avipad->auds.format = GST_RIFF_WAVE_FORMAT_AAC;
849
          /* need to determine frame length */
Wim Taymans's avatar
Wim Taymans committed
850 851
          gst_buffer_extract (codec_data_buf, 0, data, 2);
          codec = GST_READ_UINT16_BE (data);
852
          avipad->parent.hdr.scale = (codec & 0x4) ? 960 : 1024;
853
          break;
854
        }
855
      }
David Schleef's avatar
David Schleef committed
856
    } else if (!strcmp (mimetype, "audio/x-vorbis")) {
857
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_VORBIS3;
David Schleef's avatar
David Schleef committed
858
    } else if (!strcmp (mimetype, "audio/x-ac3")) {
859
      avipad->auds.format = GST_RIFF_WAVE_FORMAT_A52;
860 861 862 863 864 865 866 867 868 869
    } 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
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
    } 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
896
    }
Andy Wingo's avatar
Andy Wingo committed
897 898
  }

899 900 901
  if (!avipad->auds.format)
    goto refuse_caps;

902
  avipad->parent.hdr.fcc_handler = avipad->auds.format;
903
  gst_avi_mux_audsink_set_fields (avimux, avipad);
904