gstffmpegdec.c 18.5 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 222 223 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
static void
gst_ffmpegdec_close (GstFFMpegDec *ffmpegdec)
{
  if (!ffmpegdec->opened)
    return;

  avcodec_close (ffmpegdec->context);
  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
253
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
254
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
255
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
256 257 258
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
259 260
  GstStructure *structure;
  const GValue *par;
261

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

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

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

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

278 279 280 281 282 283 284 285 286
  /* 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);
  }

287 288 289 290
  /* 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
291
          ffmpegdec->context->codec_id == CODEC_ID_MPEG2VIDEO))
292 293
    ffmpegdec->context->flags |= CODEC_FLAG_TRUNCATED;

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

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

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

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
313
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
314
          context->width, context->height);
315 316
      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
317
          context->pix_fmt, context->width, context->height);
318 319 320 321 322 323
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
324 325
  }

326 327 328 329 330 331 332 333 334 335 336 337 338
  /* 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;
}
339

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

346 347 348
  gst_buffer_unref (buf);

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

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
357
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
358
{
359
  GstBuffer *inbuf = GST_BUFFER (_data);
360
  GstBuffer *outbuf = NULL;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
361 362 363
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
364
  guchar *data;
365 366 367
  gint size, len = 0;
  gint have_data;

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

375 376 377 378
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
379 380 381 382 383 384 385

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

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

386 387
    switch (oclass->in_plugin->type) {
      case CODEC_TYPE_VIDEO:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
388 389
        /* workarounds, functions write to buffers:
         *  libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
390 391
         *  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
392
        if (oclass->in_plugin->id == CODEC_ID_SVQ1 ||
393
            oclass->in_plugin->id == CODEC_ID_SVQ3) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
394 395 396 397
          inbuf = gst_buffer_copy_on_write (inbuf);
          data = GST_BUFFER_DATA (inbuf);
          size = GST_BUFFER_SIZE (inbuf);
        }
398
        len = avcodec_decode_video (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
399
            ffmpegdec->picture, &have_data, data, size);
400
        if (have_data) {
401 402 403 404
          /* 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;
405 406 407 408
          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
409 410 411
              ffmpegdec->context->width,
              ffmpegdec->context->height);

412
          outbuf = gst_buffer_new_and_alloc (size);
413
          avpicture_fill (&pic, GST_BUFFER_DATA (outbuf), to_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
414
              ffmpegdec->context->width, ffmpegdec->context->height);
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
          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];
              }
            }
          }
439

440 441 442
          /* this isn't necessarily true, but it's better than nothing */
          GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
        }
443
        break;
444

445
      case CODEC_TYPE_AUDIO:
446
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
447
        len = avcodec_decode_audio (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
448
            (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
449 450 451
        if (have_data < 0) {
          GST_WARNING_OBJECT (ffmpegdec,
              "FFmpeg error: len %d, have_data: %d < 0 !",
452
              len, have_data);
453 454 455
          gst_buffer_unref (inbuf);
          return;
        }
456

457 458 459
        if (have_data) {
          GST_BUFFER_SIZE (outbuf) = have_data;
          GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
460
              (ffmpegdec->context->channels * ffmpegdec->context->sample_rate);
461 462
        } else {
          gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
463
        }
464 465
        break;
      default:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
466
        g_assert (0);
467 468
        break;
    }
469 470

    if (len < 0) {
471
      GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
472
          oclass->in_plugin->name);
473 474 475
      break;
    }

476
    if (have_data) {
477
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
478
        GstCaps *caps;
479
        enum PixelFormat orig_fmt = ffmpegdec->context->pix_fmt;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
480

481 482
        ffmpegdec->context->pix_fmt = (orig_fmt == PIX_FMT_PAL8) ?
	    PIX_FMT_RGBA32 : orig_fmt;
483
        caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
484
            ffmpegdec->context);
485
        ffmpegdec->context->pix_fmt = orig_fmt;
486 487 488 489 490 491 492 493 494 495

        /* 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);
        }
496
        if (caps == NULL ||
497
            !gst_pad_set_explicit_caps (ffmpegdec->srcpad, caps)) {
498
          GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
499 500
              ("Failed to link ffmpeg decoder (%s) to next element",
                  oclass->in_plugin->name));
501 502
	  if (caps != NULL)
	    gst_caps_free (caps);
503 504
          return;
        }
505
	gst_caps_free (caps);
506 507 508
      }

      GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
509

510
      gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
511
    }
512 513 514

    size -= len;
    data += len;
515
  } while (size > 0);
516 517 518 519

  gst_buffer_unref (inbuf);
}

520
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
521
gst_ffmpegdec_change_state (GstElement * element)
522
{
523 524 525 526 527
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
528
      gst_ffmpegdec_close (ffmpegdec);
529 530 531
      break;
  }

532 533
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
534

535
  return GST_STATE_SUCCESS;
536 537 538
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
539
gst_ffmpegdec_register (GstPlugin * plugin)
540 541
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
542 543
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
544
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
545
    (GClassInitFunc) gst_ffmpegdec_class_init,
546 547
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
548
    sizeof (GstFFMpegDec),
549
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
550
    (GInstanceInitFunc) gst_ffmpegdec_init,
551 552 553
  };
  GType type;
  AVCodec *in_plugin;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
554

555 556 557 558 559
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
560
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
561 562
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
563

Ronald S. Bultje's avatar
Ronald S. Bultje committed
564 565
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
566 567
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
568
      goto next;
569
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
570 571 572

    /* only decoders */
    if (!in_plugin->decode) {
573 574
      goto next;
    }
575 576

    /* first make sure we've got a supported type */
577
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
578
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
579 580 581
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
582
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
583
    }
584

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

588
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
589 590
    if (g_type_from_name (type_name)) {
      g_free (type_name);
591 592 593
      goto next;
    }

594 595 596 597
    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
598 599 600
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

601 602 603
    /* create the gtype now
     * (Ronald) MPEG-4 gets a higher priority because it has been well-
     * tested and by far outperforms divxdec/xviddec - so we prefer it. */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
604
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
605
    if (!gst_element_register (plugin, type_name,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
606 607
            (in_plugin->id == CODEC_ID_MPEG4) ?
            GST_RANK_PRIMARY : GST_RANK_MARGINAL, type)) {
David Schleef's avatar
David Schleef committed
608
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
609
      return FALSE;
David Schleef's avatar
David Schleef committed
610 611 612
    }

    g_free (type_name);
613

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
614 615
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
616

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
617
  next:
618 619
    in_plugin = in_plugin->next;
  }
620
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
621 622 623

  return TRUE;
}