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

20
#ifdef HAVE_CONFIG_H
21
#include "config.h"
22
#endif
23

24
#include <assert.h>
25
#include <string.h>
26

27 28 29 30 31
#ifdef HAVE_FFMPEG_UNINSTALLED
#include <avcodec.h>
#else
#include <ffmpeg/avcodec.h>
#endif
32 33 34

#include <gst/gst.h>

35
#include "gstffmpegcodecmap.h"
36

37 38
typedef struct _GstFFMpegDec GstFFMpegDec;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
39 40
struct _GstFFMpegDec
{
41 42 43 44 45 46 47
  GstElement element;

  /* We need to keep track of our pads, so we do so here. */
  GstPad *srcpad;
  GstPad *sinkpad;

  AVCodecContext *context;
48
  AVFrame *picture;
49
  gboolean opened;
50 51

  GValue *par;		/* pixel aspect ratio of incoming data */
52 53 54 55
};

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
56 57
struct _GstFFMpegDecClass
{
58 59 60
  GstElementClass parent_class;

  AVCodec *in_plugin;
61
  GstPadTemplate *srctempl, *sinktempl;
62 63
};

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
64 65
typedef struct _GstFFMpegDecClassParams GstFFMpegDecClassParams;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
66 67
struct _GstFFMpegDecClassParams
{
68
  AVCodec *in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
69 70
  GstCaps *srccaps, *sinkcaps;
};
71

72 73 74 75 76 77 78 79 80 81
#define GST_TYPE_FFMPEGDEC \
  (gst_ffmpegdec_get_type())
#define GST_FFMPEGDEC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegDec))
#define GST_FFMPEGDEC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegDecClass))
#define GST_IS_FFMPEGDEC(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC))
#define GST_IS_FFMPEGDEC_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC))
82

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
83 84
enum
{
85 86 87 88
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
89 90
enum
{
91 92 93 94 95 96 97
  ARG_0,
  /* FILL ME */
};

static GHashTable *global_plugins;

/* A number of functon prototypes are given so we can refer to them later. */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
98 99 100 101
static void gst_ffmpegdec_base_init (GstFFMpegDecClass * klass);
static void gst_ffmpegdec_class_init (GstFFMpegDecClass * klass);
static void gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec);
static void gst_ffmpegdec_dispose (GObject * object);
102

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
103 104 105
static GstPadLinkReturn gst_ffmpegdec_connect (GstPad * pad,
    const GstCaps * caps);
static void gst_ffmpegdec_chain (GstPad * pad, GstData * data);
106

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
107
static GstElementStateReturn gst_ffmpegdec_change_state (GstElement * element);
108

109
#if 0
110
/* some sort of bufferpool handling, but different */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
111 112 113 114
static int gst_ffmpegdec_get_buffer (AVCodecContext * context,
    AVFrame * picture);
static void gst_ffmpegdec_release_buffer (AVCodecContext * context,
    AVFrame * picture);
115
#endif
116 117 118

static GstElementClass *parent_class = NULL;

119
/*static guint gst_ffmpegdec_signals[LAST_SIGNAL] = { 0 }; */
120

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
121
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
122
gst_ffmpegdec_base_init (GstFFMpegDecClass * klass)
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
123 124 125 126
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstFFMpegDecClassParams *params;
Benjamin Otte's avatar
Benjamin Otte committed
127
  GstElementDetails details;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
128 129 130
  GstPadTemplate *sinktempl, *srctempl;

  params = g_hash_table_lookup (global_plugins,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
131
      GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
132
  if (!params)
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
133
    params = g_hash_table_lookup (global_plugins, GINT_TO_POINTER (0));
134
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
135 136

  /* construct the element details struct */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
137 138
  details.longname = g_strdup_printf ("FFMPEG %s decoder",
      params->in_plugin->name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
139
  details.klass = g_strdup_printf ("Codec/Decoder/%s",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
140 141 142
      (params->in_plugin->type == CODEC_TYPE_VIDEO) ? "Video" : "Audio");
  details.description = g_strdup_printf ("FFMPEG %s decoder",
      params->in_plugin->name);
Benjamin Otte's avatar
Benjamin Otte committed
143
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
144
      "Ronald Bultje <rbultje@ronald.bitfreak.net>";
Benjamin Otte's avatar
Benjamin Otte committed
145 146 147 148
  gst_element_class_set_details (element_class, &details);
  g_free (details.longname);
  g_free (details.klass);
  g_free (details.description);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
149 150 151

  /* pad templates */
  sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
152
      GST_PAD_ALWAYS, params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
153
  srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
154
      GST_PAD_ALWAYS, params->srccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
155 156 157 158 159 160 161 162 163

  gst_element_class_add_pad_template (element_class, srctempl);
  gst_element_class_add_pad_template (element_class, sinktempl);

  klass->in_plugin = params->in_plugin;
  klass->srctempl = srctempl;
  klass->sinktempl = sinktempl;
}

164
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
165
gst_ffmpegdec_class_init (GstFFMpegDecClass * klass)
166 167 168 169
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
170 171
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
172

173
  parent_class = g_type_class_peek_parent (klass);
174

175 176 177 178 179
  gobject_class->dispose = gst_ffmpegdec_dispose;
  gstelement_class->change_state = gst_ffmpegdec_change_state;
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
180
gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec)
181
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
182 183
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
184 185 186 187 188 189

  /* setup pads */
  ffmpegdec->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
  gst_pad_set_link_function (ffmpegdec->sinkpad, gst_ffmpegdec_connect);
  gst_pad_set_chain_function (ffmpegdec->sinkpad, gst_ffmpegdec_chain);
  ffmpegdec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
190
  gst_pad_use_explicit_caps (ffmpegdec->srcpad);
191 192 193 194 195

  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad);
  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad);

  /* some ffmpeg data */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
196 197
  ffmpegdec->context = avcodec_alloc_context ();
  ffmpegdec->picture = avcodec_alloc_frame ();
198 199 200 201 202

  ffmpegdec->opened = FALSE;
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
203
gst_ffmpegdec_dispose (GObject * object)
204 205
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;
206 207 208 209

  G_OBJECT_CLASS (parent_class)->dispose (object);
  /* old session should have been closed in element_class->dispose */
  g_assert (!ffmpegdec->opened);
210

211 212 213
  /* clean up remaining allocated data */
  av_free (ffmpegdec->context);
  av_free (ffmpegdec->picture);
214 215
}

216 217 218 219 220 221
static void
gst_ffmpegdec_close (GstFFMpegDec *ffmpegdec)
{
  if (!ffmpegdec->opened)
    return;

222 223
  if (ffmpegdec->context->priv_data)
    avcodec_close (ffmpegdec->context);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
  ffmpegdec->opened = FALSE;

  if (ffmpegdec->context->palctrl) {
    av_free (ffmpegdec->context->palctrl);
    ffmpegdec->context->palctrl = NULL;
  }

  if (ffmpegdec->context->extradata) {
    av_free (ffmpegdec->context->extradata);
    ffmpegdec->context->extradata = NULL;
  }
}

static gboolean
gst_ffmpegdec_open (GstFFMpegDec *ffmpegdec)
{
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));

  ffmpegdec->opened = TRUE;
  if (avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) {
    gst_ffmpegdec_close (ffmpegdec);
    GST_DEBUG ("ffdec_%s: Failed to open FFMPEG codec",
        oclass->in_plugin->name);
    return FALSE;
  }

  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
254
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
255
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
256
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
257 258 259
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
260 261
  GstStructure *structure;
  const GValue *par;
262

263
  /* close old session */
264
  gst_ffmpegdec_close (ffmpegdec);
265

266 267
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
268

269
#if 0
270 271 272
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
273
#endif
274

275
  /* get size and so */
276 277
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, caps, ffmpegdec->context);
278

279 280 281 282 283 284 285 286 287
  /* get pixel aspect ratio if it's set */
  structure = gst_caps_get_structure (caps, 0);
  par = gst_structure_get_value (structure, "pixel-aspect-ratio");
  if (par) {
    GST_DEBUG_OBJECT (ffmpegdec, "sink caps have pixel-aspect-ratio");
    ffmpegdec->par = g_new0 (GValue, 1);
    gst_value_init_and_copy (ffmpegdec->par, par);
  }

288 289 290 291
  /* we dont send complete frames - FIXME: we need a 'framed' property
   * in caps */
  if (oclass->in_plugin->capabilities & CODEC_CAP_TRUNCATED &&
      (ffmpegdec->context->codec_id == CODEC_ID_MPEG1VIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
292
          ffmpegdec->context->codec_id == CODEC_ID_MPEG2VIDEO))
293 294
    ffmpegdec->context->flags |= CODEC_FLAG_TRUNCATED;

295 296 297
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

298 299 300
  /* workaround encoder bugs */
  ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT;

301 302 303
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
304 305
  return gst_ffmpegdec_open (ffmpegdec) ?
      GST_PAD_LINK_OK : GST_PAD_LINK_REFUSED;
306 307
}

308
#if 0
309
static int
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
310
gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
311 312 313 314 315 316
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
317
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
318
          context->width, context->height);
319 320
      buf = gst_buffer_new_and_alloc (bufsize);
      avpicture_fill ((AVPicture *) picture, GST_BUFFER_DATA (buf),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
321
          context->pix_fmt, context->width, context->height);
322 323 324 325 326 327
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
328 329
  }

330 331 332 333 334 335 336 337 338 339 340 341 342
  /* tell ffmpeg we own this buffer
   *
   * we also use an evil hack (keep buffer in base[0])
   * to keep a reference to the buffer in release_buffer(),
   * so that we can ref() it here and unref() it there
   * so that we don't need to copy data */
  picture->type = FF_BUFFER_TYPE_USER;
  picture->age = G_MAXINT;
  picture->base[0] = (int8_t *) buf;
  gst_buffer_ref (buf);

  return 0;
}
343

344
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
345
gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture)
346 347 348
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
349

350 351 352
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
353
  for (i = 0; i < 4; i++) {
354 355 356
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
357
}
358
#endif
359 360

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
361
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
362
{
363
  GstBuffer *inbuf = GST_BUFFER (_data);
364
  GstBuffer *outbuf = NULL;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
365 366 367
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
368
  guchar *data;
369 370
  gint size, len = 0;
  gint have_data;
371
  guint64 expected_ts = GST_BUFFER_TIMESTAMP (inbuf);
372

Ronald S. Bultje's avatar
Ronald S. Bultje committed
373
  if (!ffmpegdec->opened) {
374
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
375 376
        ("ffdec_%s: input format was not set before data start",
            oclass->in_plugin->name));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
377 378 379
    return;
  }

380 381 382 383
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
384 385 386 387 388 389 390

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

  do {
    ffmpegdec->context->frame_number++;

391 392
    switch (oclass->in_plugin->type) {
      case CODEC_TYPE_VIDEO:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
393 394
        /* workarounds, functions write to buffers:
         *  libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
395 396
         *  libavcodec/svq3.c:svq3_decode_slice_header too.
         * ffmpeg devs know about it and will fix it (they said). */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
397
        if (oclass->in_plugin->id == CODEC_ID_SVQ1 ||
398
            oclass->in_plugin->id == CODEC_ID_SVQ3) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
399 400 401 402
          inbuf = gst_buffer_copy_on_write (inbuf);
          data = GST_BUFFER_DATA (inbuf);
          size = GST_BUFFER_SIZE (inbuf);
        }
403

404
        len = avcodec_decode_video (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
405
            ffmpegdec->picture, &have_data, data, size);
406 407

        if (len >= 0 && have_data) {
408 409 410 411
          /* libavcodec constantly crashes on stupid buffer allocation
           * errors inside. This drives me crazy, so we let it allocate
           * it's own buffers and copy to our own buffer afterwards... */
          AVPicture pic;
412 413 414 415
          enum PixelFormat to_fmt =
	      (ffmpegdec->context->pix_fmt == PIX_FMT_PAL8) ?
	      PIX_FMT_RGBA32 : ffmpegdec->context->pix_fmt;
          gint size = avpicture_get_size (to_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
416 417 418
              ffmpegdec->context->width,
              ffmpegdec->context->height);

419
          outbuf = gst_buffer_new_and_alloc (size);
420
          avpicture_fill (&pic, GST_BUFFER_DATA (outbuf), to_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
421
              ffmpegdec->context->width, ffmpegdec->context->height);
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
          if (to_fmt == ffmpegdec->context->pix_fmt) {
            img_convert (&pic, ffmpegdec->context->pix_fmt,
                (AVPicture *) ffmpegdec->picture,
                ffmpegdec->context->pix_fmt,
                ffmpegdec->context->width, ffmpegdec->context->height);
          } else {
            /* manual conversion from palette to RGBA32 */
            gint x, y, pix, ws = ffmpegdec->picture->linesize[0],
		wd = ffmpegdec->context->width;
            guint8 *dest = GST_BUFFER_DATA (outbuf);
            guint32 conv;
            AVPaletteControl *pal = ffmpegdec->context->palctrl;

            for (y = 0; y < ffmpegdec->context->height; y++) {
              for (x = 0; x < ffmpegdec->context->width; x++) {
                pix = ffmpegdec->picture->data[0][y * ws + x];
                conv = pal->palette[pix];
                dest[(y * wd + x) * 4]     = ((guint8 *) &conv)[0];
                dest[(y * wd + x) * 4 + 1] = ((guint8 *) &conv)[1];
                dest[(y * wd + x) * 4 + 2] = ((guint8 *) &conv)[2];
                dest[(y * wd + x) * 4 + 3] = ((guint8 *) &conv)[3];
              }
            }
          }
446

447 448 449 450 451 452 453 454
          /* note that ffmpeg sometimes gets the FPS wrong */
          if (GST_CLOCK_TIME_IS_VALID (expected_ts)) {
            GST_BUFFER_TIMESTAMP (outbuf) = expected_ts;
            GST_BUFFER_DURATION (outbuf) = GST_SECOND *
                ffmpegdec->context->frame_rate_base /
                ffmpegdec->context->frame_rate;
            expected_ts += GST_BUFFER_DURATION (outbuf);
          }
455
        }
456
        break;
457

458
      case CODEC_TYPE_AUDIO:
459
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
460
        len = avcodec_decode_audio (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
461
            (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
462 463 464
        if (have_data < 0) {
          GST_WARNING_OBJECT (ffmpegdec,
              "FFmpeg error: len %d, have_data: %d < 0 !",
465
              len, have_data);
466 467 468
          gst_buffer_unref (inbuf);
          return;
        }
469

470 471
        if (have_data) {
          GST_BUFFER_SIZE (outbuf) = have_data;
472 473 474 475 476 477 478
          if (GST_CLOCK_TIME_IS_VALID (expected_ts)) {
            GST_BUFFER_TIMESTAMP (outbuf) = expected_ts;
            GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
                (2 * ffmpegdec->context->channels *
                ffmpegdec->context->sample_rate);
            expected_ts += GST_BUFFER_DURATION (outbuf);
          }
479 480
        } else {
          gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
481
        }
482 483
        break;
      default:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
484
        g_assert (0);
485 486
        break;
    }
487 488

    if (len < 0) {
489
      GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
490
          oclass->in_plugin->name);
491 492 493
      break;
    }

494
    if (have_data) {
495
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
496
        GstCaps *caps;
497
        enum PixelFormat orig_fmt = ffmpegdec->context->pix_fmt;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
498

499 500
        ffmpegdec->context->pix_fmt = (orig_fmt == PIX_FMT_PAL8) ?
	    PIX_FMT_RGBA32 : orig_fmt;
501
        caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
502
            ffmpegdec->context);
503
        ffmpegdec->context->pix_fmt = orig_fmt;
504 505 506 507 508 509 510 511 512 513

        /* add in pixel-aspect-ratio if we have it */
        if (caps && ffmpegdec->par) {
          GST_DEBUG_OBJECT (ffmpegdec, "setting pixel-aspect-ratio");
	  gst_structure_set (gst_caps_get_structure (caps, 0),
              "pixel-aspect-ratio", GST_TYPE_FRACTION,
              gst_value_get_fraction_numerator (ffmpegdec->par),
              gst_value_get_fraction_denominator (ffmpegdec->par),
              NULL);
        }
514
        if (caps == NULL ||
515
            !gst_pad_set_explicit_caps (ffmpegdec->srcpad, caps)) {
516
          GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
517 518
              ("Failed to link ffmpeg decoder (%s) to next element",
                  oclass->in_plugin->name));
519 520
	  if (caps != NULL)
	    gst_caps_free (caps);
521 522
          return;
        }
523
	gst_caps_free (caps);
524 525
      }

526
      gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
527
    }
528 529 530

    size -= len;
    data += len;
531
  } while (size > 0);
532 533 534 535

  gst_buffer_unref (inbuf);
}

536
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
537
gst_ffmpegdec_change_state (GstElement * element)
538
{
539 540 541 542 543
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
544
      gst_ffmpegdec_close (ffmpegdec);
545 546 547
      break;
  }

548 549
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
550

551
  return GST_STATE_SUCCESS;
552 553 554
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
555
gst_ffmpegdec_register (GstPlugin * plugin)
556 557
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
558 559
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
560
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
561
    (GClassInitFunc) gst_ffmpegdec_class_init,
562 563
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
564
    sizeof (GstFFMpegDec),
565
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
566
    (GInstanceInitFunc) gst_ffmpegdec_init,
567 568 569
  };
  GType type;
  AVCodec *in_plugin;
570
  gint rank;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
571

572 573 574 575 576
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
577
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
578 579
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
580

Ronald S. Bultje's avatar
Ronald S. Bultje committed
581 582
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
583 584
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
585
      goto next;
586
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
587 588 589

    /* only decoders */
    if (!in_plugin->decode) {
590 591
      goto next;
    }
592 593

    /* first make sure we've got a supported type */
594
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
595
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
596 597 598
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
599
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
600
    }
601

602
    /* construct the type */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
603
    type_name = g_strdup_printf ("ffdec_%s", in_plugin->name);
604

605
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
606 607
    if (g_type_from_name (type_name)) {
      g_free (type_name);
608 609 610
      goto next;
    }

611 612 613 614
    params = g_new0 (GstFFMpegDecClassParams, 1);
    params->in_plugin = in_plugin;
    params->srccaps = srccaps;
    params->sinkcaps = sinkcaps;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
615 616 617
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

618
    /* create the gtype now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
619
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
620 621 622 623 624 625 626 627 628 629 630 631 632 633

    /* (Ronald) MPEG-4 gets a higher priority because it has been well-
     * tested and by far outperforms divxdec/xviddec - so we prefer it.
     * msmpeg4v3 same, as it outperforms divxdec for divx3 playback. */
    switch (in_plugin->id) {
      case CODEC_ID_MPEG4:
      case CODEC_ID_MSMPEG4V3:
        rank = GST_RANK_PRIMARY;
        break;
      default:
        rank = GST_RANK_MARGINAL;
        break;
    }
    if (!gst_element_register (plugin, type_name, rank, type)) {
David Schleef's avatar
David Schleef committed
634
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
635
      return FALSE;
David Schleef's avatar
David Schleef committed
636 637 638
    }

    g_free (type_name);
639

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
640 641
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
642

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
643
  next:
644 645
    in_plugin = in_plugin->next;
  }
646
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
647 648 649

  return TRUE;
}