gstffmpegdec.c 19.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

  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
  details.longname = g_strdup_printf ("FFMPEG %s decoder",
138
      gst_ffmpeg_get_codecid_longname (params->in_plugin->id));
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
  ffmpegdec->par = NULL;
200 201 202 203
  ffmpegdec->opened = FALSE;
}

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

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

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

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

223 224 225 226 227
  if (ffmpegdec->par) {
    g_free (ffmpegdec->par);
    ffmpegdec->par = NULL;
  }

228 229
  if (ffmpegdec->context->priv_data)
    avcodec_close (ffmpegdec->context);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
  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
260
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
261
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
262
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
263 264 265
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
266 267
  GstStructure *structure;
  const GValue *par;
268

269
  /* close old session */
270
  gst_ffmpegdec_close (ffmpegdec);
271

272 273
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
274

275
#if 0
276 277 278
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
279
#endif
280

281
  /* get size and so */
282 283
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, caps, ffmpegdec->context);
284

285 286 287 288 289 290 291 292 293
  /* 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);
  }

294 295 296 297
  /* 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
298
          ffmpegdec->context->codec_id == CODEC_ID_MPEG2VIDEO))
299 300
    ffmpegdec->context->flags |= CODEC_FLAG_TRUNCATED;

301 302 303
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

304 305 306
  /* workaround encoder bugs */
  ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT;

307 308 309
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
310 311 312 313 314 315 316 317 318
  if (!gst_ffmpegdec_open (ffmpegdec)) {
    if (ffmpegdec->par) {
      g_free (ffmpegdec->par);
      ffmpegdec->par = NULL;
    }
    return GST_PAD_LINK_REFUSED;
  }

  return GST_PAD_LINK_OK;
319 320
}

321
#if 0
322
static int
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
323
gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
324 325 326 327 328 329
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
330
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
331
          context->width, context->height);
332
      buf = gst_buffer_new_and_alloc (bufsize);
333 334
      gst_ffmpeg_avpicture_fill ((AVPicture *) picture,
          GST_BUFFER_DATA (buf),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
335
          context->pix_fmt, context->width, context->height);
336 337 338 339 340 341
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
342 343
  }

344 345 346 347 348 349 350 351 352 353 354 355 356
  /* 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;
}
357

358
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
359
gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture)
360 361 362
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
363

364 365 366
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
367
  for (i = 0; i < 4; i++) {
368 369 370
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
371
}
372
#endif
373

374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
static gboolean
gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
{
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
  GstCaps *caps;

  caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type,
      ffmpegdec->context);

  /* add in pixel-aspect-ratio if we have it,
   * prefer ffmpeg par over sink par (since it's provided
   * by the codec, which is more often correct).
   */
 if (caps) {
   if (ffmpegdec->context->sample_aspect_ratio.num &&
       ffmpegdec->context->sample_aspect_ratio.den) {
     GST_DEBUG ("setting ffmpeg provided pixel-aspect-ratio");
     gst_structure_set (gst_caps_get_structure (caps, 0),
         "pixel-aspect-ratio", GST_TYPE_FRACTION,
         ffmpegdec->context->sample_aspect_ratio.num,
         ffmpegdec->context->sample_aspect_ratio.den,
         NULL);
    } else if (ffmpegdec->par) {
      GST_DEBUG ("passing on pixel-aspect-ratio from sink");
      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);
    }
  }

  if (caps == NULL ||
      !gst_pad_set_explicit_caps (ffmpegdec->srcpad, caps)) {
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
        ("Failed to link ffmpeg decoder (%s) to next element",
        oclass->in_plugin->name));

    if (caps != NULL)
      gst_caps_free (caps);

    return FALSE;
  }

  gst_caps_free (caps);

  return TRUE;
}
423

424
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
425
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
426
{
427
  GstBuffer *inbuf = GST_BUFFER (_data);
428
  GstBuffer *outbuf = NULL;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
429 430 431
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
432
  guchar *data;
433 434
  gint size, len = 0;
  gint have_data;
435
  guint64 expected_ts = GST_BUFFER_TIMESTAMP (inbuf);
436

Ronald S. Bultje's avatar
Ronald S. Bultje committed
437
  if (!ffmpegdec->opened) {
438
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
439 440
        ("ffdec_%s: input format was not set before data start",
            oclass->in_plugin->name));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
441 442 443
    return;
  }

444 445 446 447
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
448 449 450 451 452 453 454

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

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

455 456
    switch (oclass->in_plugin->type) {
      case CODEC_TYPE_VIDEO:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
457 458
        /* workarounds, functions write to buffers:
         *  libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
459 460
         *  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
461
        if (oclass->in_plugin->id == CODEC_ID_SVQ1 ||
462
            oclass->in_plugin->id == CODEC_ID_SVQ3) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
463 464 465 466
          inbuf = gst_buffer_copy_on_write (inbuf);
          data = GST_BUFFER_DATA (inbuf);
          size = GST_BUFFER_SIZE (inbuf);
        }
467

468
        len = avcodec_decode_video (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
469
            ffmpegdec->picture, &have_data, data, size);
470 471

        if (len >= 0 && have_data) {
472 473 474 475
          /* 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;
476 477
          gint size = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
              ffmpegdec->context->width, ffmpegdec->context->height);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
478

479
          outbuf = gst_buffer_new_and_alloc (size);
480 481
	  /* original ffmpeg code does not handle odd sizes correctly. This patched
	   * up version does */
482
          gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
483 484
              ffmpegdec->context->pix_fmt,
              ffmpegdec->context->width, ffmpegdec->context->height);
485 486 487 488 489

	  /* the original convert function did not do the right thing, this
	   * is a patched up version that adjust widht/height so that the
	   * ffmpeg one works correctly. */
          gst_ffmpeg_img_convert (&pic, ffmpegdec->context->pix_fmt,
490 491
              (AVPicture *) ffmpegdec->picture,
              ffmpegdec->context->pix_fmt,
492 493
              ffmpegdec->context->width, 
	      ffmpegdec->context->height);
494

495
          /* note that ffmpeg sometimes gets the FPS wrong */
496 497
          if (GST_CLOCK_TIME_IS_VALID (expected_ts) &&
              ffmpegdec->context->frame_rate > 0) {
498 499 500 501 502
            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);
503 504
          } else {
            GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
505
          }
506
        }
507
        break;
508

509
      case CODEC_TYPE_AUDIO:
510
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
511
        len = avcodec_decode_audio (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
512
            (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
513 514 515
        if (have_data < 0) {
          GST_WARNING_OBJECT (ffmpegdec,
              "FFmpeg error: len %d, have_data: %d < 0 !",
516
              len, have_data);
517 518 519
          gst_buffer_unref (inbuf);
          return;
        }
520

521
        if (len >= 0 && have_data) {
522
          GST_BUFFER_SIZE (outbuf) = have_data;
523 524 525 526 527 528 529
          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);
          }
530 531
        } else {
          gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
532
        }
533 534
        break;
      default:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
535
        g_assert (0);
536 537
        break;
    }
538 539

    if (len < 0) {
540
      GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
541
          oclass->in_plugin->name);
542 543 544
      break;
    }

545
    if (have_data) {
546
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
547
        if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
548
          gst_buffer_unref (outbuf);
549 550
          return;
        }
551 552
      }

553 554 555 556
      if (GST_PAD_IS_USABLE (ffmpegdec->srcpad))
        gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
      else
        gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
557
    }
558 559 560

    size -= len;
    data += len;
561
  } while (size > 0);
562 563 564 565

  gst_buffer_unref (inbuf);
}

566
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
567
gst_ffmpegdec_change_state (GstElement * element)
568
{
569 570 571 572 573
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
574
      gst_ffmpegdec_close (ffmpegdec);
575 576 577
      break;
  }

578 579
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
580

581
  return GST_STATE_SUCCESS;
582 583 584
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
585
gst_ffmpegdec_register (GstPlugin * plugin)
586 587
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
588 589
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
590
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
591
    (GClassInitFunc) gst_ffmpegdec_class_init,
592 593
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
594
    sizeof (GstFFMpegDec),
595
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
596
    (GInstanceInitFunc) gst_ffmpegdec_init,
597 598 599
  };
  GType type;
  AVCodec *in_plugin;
600
  gint rank;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
601

602 603 604 605 606
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
607
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
608 609
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
610

Ronald S. Bultje's avatar
Ronald S. Bultje committed
611 612
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
613 614
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
615
      goto next;
616
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
617 618 619

    /* only decoders */
    if (!in_plugin->decode) {
620 621
      goto next;
    }
622

623 624 625 626
    /* name */
    if (!gst_ffmpeg_get_codecid_longname (in_plugin->id))
      goto next;

627
    /* first make sure we've got a supported type */
628
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
629
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
630 631 632
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
633
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
634
    }
635

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

639
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
640 641
    if (g_type_from_name (type_name)) {
      g_free (type_name);
642 643 644
      goto next;
    }

645 646 647 648
    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
649 650 651
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

652
    /* create the gtype now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
653
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
654 655 656 657 658 659 660 661 662 663 664 665 666 667

    /* (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
668
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
669
      return FALSE;
David Schleef's avatar
David Schleef committed
670 671 672
    }

    g_free (type_name);
673

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
674 675
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
676

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
677
  next:
678 679
    in_plugin = in_plugin->next;
  }
680
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
681 682 683

  return TRUE;
}