gstffmpegdec.c 17.7 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 52 53
};

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

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

  AVCodec *in_plugin;
59
  GstPadTemplate *srctempl, *sinktempl;
60 61
};

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
62 63
typedef struct _GstFFMpegDecClassParams GstFFMpegDecClassParams;

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

70 71 72 73 74 75 76 77 78 79
#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))
80

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

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
87 88
enum
{
89 90 91 92 93 94 95
  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
96 97 98 99
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);
100

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

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

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

static GstElementClass *parent_class = NULL;

117
/*static guint gst_ffmpegdec_signals[LAST_SIGNAL] = { 0 }; */
118

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

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

  /* construct the element details struct */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
135 136
  details.longname = g_strdup_printf ("FFMPEG %s decoder",
      params->in_plugin->name);
137
  details.klass = g_strdup_printf ("Codec/%s/Decoder",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
138 139 140
      (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
141
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
142
      "Ronald Bultje <rbultje@ronald.bitfreak.net>";
Benjamin Otte's avatar
Benjamin Otte committed
143 144 145 146
  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
147 148 149

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

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

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

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

171
  parent_class = g_type_class_peek_parent (klass);
172

173 174 175 176 177
  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
178
gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec)
179
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
180 181
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
182 183 184 185 186 187

  /* 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");
188
  gst_pad_use_explicit_caps (ffmpegdec->srcpad);
189 190 191 192 193

  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
194 195
  ffmpegdec->context = avcodec_alloc_context ();
  ffmpegdec->picture = avcodec_alloc_frame ();
196 197 198 199 200

  ffmpegdec->opened = FALSE;
}

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

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

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

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
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
251
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
252
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
253
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
254 255 256
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
257

258
  /* close old session */
259
  gst_ffmpegdec_close (ffmpegdec);
260

261 262
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
263

264
#if 0
265 266 267
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
268
#endif
269

270
  /* get size and so */
271 272
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, caps, ffmpegdec->context);
273

274 275 276 277
  /* 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
278
          ffmpegdec->context->codec_id == CODEC_ID_MPEG2VIDEO))
279 280
    ffmpegdec->context->flags |= CODEC_FLAG_TRUNCATED;

281 282 283
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

284 285 286
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
287 288
  return gst_ffmpegdec_open (ffmpegdec) ?
      GST_PAD_LINK_OK : GST_PAD_LINK_REFUSED;
289 290
}

291
#if 0
292
static int
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
293
gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
294 295 296 297 298 299
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
300
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
301
          context->width, context->height);
302 303
      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
304
          context->pix_fmt, context->width, context->height);
305 306 307 308 309 310
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
311 312
  }

313 314 315 316 317 318 319 320 321 322 323 324 325
  /* 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;
}
326

327
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
328
gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture)
329 330 331
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
332

333 334 335
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
336
  for (i = 0; i < 4; i++) {
337 338 339
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
340
}
341
#endif
342 343

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
344
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
345
{
346
  GstBuffer *inbuf = GST_BUFFER (_data);
347
  GstBuffer *outbuf = NULL;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
348 349 350
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
351
  guchar *data;
352 353 354
  gint size, len = 0;
  gint have_data;

Ronald S. Bultje's avatar
Ronald S. Bultje committed
355
  if (!ffmpegdec->opened) {
356
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
357 358
        ("ffdec_%s: input format was not set before data start",
            oclass->in_plugin->name));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
359 360 361
    return;
  }

362 363 364 365
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
366 367 368 369 370 371 372

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

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

373 374
    switch (oclass->in_plugin->type) {
      case CODEC_TYPE_VIDEO:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
375 376
        /* workarounds, functions write to buffers:
         *  libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
377 378
         *  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
379
        if (oclass->in_plugin->id == CODEC_ID_SVQ1 ||
380
            oclass->in_plugin->id == CODEC_ID_SVQ3) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
381 382 383 384
          inbuf = gst_buffer_copy_on_write (inbuf);
          data = GST_BUFFER_DATA (inbuf);
          size = GST_BUFFER_SIZE (inbuf);
        }
385
        len = avcodec_decode_video (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
386
            ffmpegdec->picture, &have_data, data, size);
387
        if (have_data) {
388 389 390 391
          /* 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;
392 393 394 395
          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
396 397 398
              ffmpegdec->context->width,
              ffmpegdec->context->height);

399
          outbuf = gst_buffer_new_and_alloc (size);
400
          avpicture_fill (&pic, GST_BUFFER_DATA (outbuf), to_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
401
              ffmpegdec->context->width, ffmpegdec->context->height);
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
          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];
              }
            }
          }
426

427 428 429
          /* this isn't necessarily true, but it's better than nothing */
          GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
        }
430
        break;
431

432
      case CODEC_TYPE_AUDIO:
433
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
434
        len = avcodec_decode_audio (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
435
            (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
436 437 438
        if (have_data < 0) {
          GST_WARNING_OBJECT (ffmpegdec,
              "FFmpeg error: len %d, have_data: %d < 0 !",
439
              len, have_data);
440 441 442
          gst_buffer_unref (inbuf);
          return;
        }
443

444 445 446
        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
447
              (ffmpegdec->context->channels * ffmpegdec->context->sample_rate);
448 449
        } else {
          gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
450
        }
451 452
        break;
      default:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
453
        g_assert (0);
454 455
        break;
    }
456 457

    if (len < 0) {
458
      GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
459
          oclass->in_plugin->name);
460 461 462
      break;
    }

463
    if (have_data) {
464
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
465
        GstCaps *caps;
466
        enum PixelFormat orig_fmt = ffmpegdec->context->pix_fmt;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
467

468 469
        ffmpegdec->context->pix_fmt = (orig_fmt == PIX_FMT_PAL8) ?
	    PIX_FMT_RGBA32 : orig_fmt;
470
        caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
471
            ffmpegdec->context);
472
        ffmpegdec->context->pix_fmt = orig_fmt;
473
        if (caps == NULL ||
474
            !gst_pad_set_explicit_caps (ffmpegdec->srcpad, caps)) {
475
          GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
476 477
              ("Failed to link ffmpeg decoder (%s) to next element",
                  oclass->in_plugin->name));
478 479
	  if (caps != NULL)
	    gst_caps_free (caps);
480 481
          return;
        }
482
	gst_caps_free (caps);
483 484 485
      }

      GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
486

487
      gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
488
    }
489 490 491

    size -= len;
    data += len;
492
  } while (size > 0);
493 494 495 496

  gst_buffer_unref (inbuf);
}

497
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
498
gst_ffmpegdec_change_state (GstElement * element)
499
{
500 501 502 503 504
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
505
      gst_ffmpegdec_close (ffmpegdec);
506 507 508
      break;
  }

509 510
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
511

512
  return GST_STATE_SUCCESS;
513 514 515
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
516
gst_ffmpegdec_register (GstPlugin * plugin)
517 518
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
519 520
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
521
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
522
    (GClassInitFunc) gst_ffmpegdec_class_init,
523 524
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
525
    sizeof (GstFFMpegDec),
526
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
527
    (GInstanceInitFunc) gst_ffmpegdec_init,
528 529 530
  };
  GType type;
  AVCodec *in_plugin;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
531

532 533 534 535 536
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
537
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
538 539
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
540

Ronald S. Bultje's avatar
Ronald S. Bultje committed
541 542
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
543 544
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
545
      goto next;
546
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
547 548 549

    /* only decoders */
    if (!in_plugin->decode) {
550 551
      goto next;
    }
552 553

    /* first make sure we've got a supported type */
554
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
555
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
556 557 558
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
559
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
560
    }
561

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

565
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
566 567
    if (g_type_from_name (type_name)) {
      g_free (type_name);
568 569 570
      goto next;
    }

571 572 573 574
    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
575 576 577
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

578 579 580
    /* 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
581
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
582
    if (!gst_element_register (plugin, type_name,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
583 584
            (in_plugin->id == CODEC_ID_MPEG4) ?
            GST_RANK_PRIMARY : GST_RANK_MARGINAL, type)) {
David Schleef's avatar
David Schleef committed
585
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
586
      return FALSE;
David Schleef's avatar
David Schleef committed
587 588 589
    }

    g_free (type_name);
590

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
591 592
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
593

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
594
  next:
595 596
    in_plugin = in_plugin->next;
  }
597
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
598 599 600

  return TRUE;
}