gstffmpegdec.c 21.1 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
  GstElement element;

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

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

52 53 54 55
  /* parsing */
  AVCodecParserContext *pctx;
  GstBuffer *pcache;

56
  GValue *par;		/* pixel aspect ratio of incoming data */
57 58 59 60
};

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
61 62
struct _GstFFMpegDecClass
{
63 64 65
  GstElementClass parent_class;

  AVCodec *in_plugin;
66
  GstPadTemplate *srctempl, *sinktempl;
67 68
};

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
69 70
typedef struct _GstFFMpegDecClassParams GstFFMpegDecClassParams;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
71 72
struct _GstFFMpegDecClassParams
{
73
  AVCodec *in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
74 75
  GstCaps *srccaps, *sinkcaps;
};
76

77 78 79 80 81 82 83 84 85 86
#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))
87

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
88 89
enum
{
90 91 92 93
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
94 95
enum
{
96 97 98 99 100 101 102
  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
103 104 105 106
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);
107

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
108 109 110
static GstPadLinkReturn gst_ffmpegdec_connect (GstPad * pad,
    const GstCaps * caps);
static void gst_ffmpegdec_chain (GstPad * pad, GstData * data);
111

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

114
#if 0
115
/* some sort of bufferpool handling, but different */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
116 117 118 119
static int gst_ffmpegdec_get_buffer (AVCodecContext * context,
    AVFrame * picture);
static void gst_ffmpegdec_release_buffer (AVCodecContext * context,
    AVFrame * picture);
120
#endif
121 122 123

static GstElementClass *parent_class = NULL;

124
/*static guint gst_ffmpegdec_signals[LAST_SIGNAL] = { 0 }; */
125

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

  params = g_hash_table_lookup (global_plugins,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
136
      GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
137
  if (!params)
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
138
    params = g_hash_table_lookup (global_plugins, GINT_TO_POINTER (0));
139
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
140 141

  /* construct the element details struct */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
142
  details.longname = g_strdup_printf ("FFMPEG %s decoder",
143
      gst_ffmpeg_get_codecid_longname (params->in_plugin->id));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
144
  details.klass = g_strdup_printf ("Codec/Decoder/%s",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
145 146 147
      (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
148
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
149
      "Ronald Bultje <rbultje@ronald.bitfreak.net>";
Benjamin Otte's avatar
Benjamin Otte committed
150 151 152 153
  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
154 155 156

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

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

169
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
170
gst_ffmpegdec_class_init (GstFFMpegDecClass * klass)
171 172 173 174
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
175 176
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
177

178
  parent_class = g_type_class_peek_parent (klass);
179

180 181 182 183 184
  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
185
gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec)
186
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
187 188
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
189 190 191 192 193 194

  /* 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");
195
  gst_pad_use_explicit_caps (ffmpegdec->srcpad);
196 197 198 199 200

  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
201 202
  ffmpegdec->context = avcodec_alloc_context ();
  ffmpegdec->picture = avcodec_alloc_frame ();
203

204 205 206
  ffmpegdec->pctx = NULL;
  ffmpegdec->pcache = NULL;

207
  ffmpegdec->par = NULL;
208 209 210 211
  ffmpegdec->opened = FALSE;
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
212
gst_ffmpegdec_dispose (GObject * object)
213 214
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;
215 216 217 218

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

220 221 222
  /* clean up remaining allocated data */
  av_free (ffmpegdec->context);
  av_free (ffmpegdec->picture);
223 224
}

225 226 227 228 229 230
static void
gst_ffmpegdec_close (GstFFMpegDec *ffmpegdec)
{
  if (!ffmpegdec->opened)
    return;

231 232 233 234 235
  if (ffmpegdec->par) {
    g_free (ffmpegdec->par);
    ffmpegdec->par = NULL;
  }

236 237
  if (ffmpegdec->context->priv_data)
    avcodec_close (ffmpegdec->context);
238 239 240 241 242 243 244 245 246 247 248
  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;
  }
249 250 251 252 253 254 255 256 257

  if (ffmpegdec->pctx) {
    if (ffmpegdec->pcache) {
      gst_buffer_unref (ffmpegdec->pcache);
      ffmpegdec->pcache = NULL;
    }
    av_parser_close (ffmpegdec->pctx);
    ffmpegdec->pctx = NULL;
  }
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
}

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

274 275 276 277
  /* open a parser if we can - exclude mpeg4 for now... */
  if (oclass->in_plugin->id != CODEC_ID_MPEG4)
    ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id);

278 279 280
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
282
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
283
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
284 285 286
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
287 288
  GstStructure *structure;
  const GValue *par;
289

290
  /* close old session */
291
  gst_ffmpegdec_close (ffmpegdec);
292

293 294
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
295

296
#if 0
297 298 299
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
300
#endif
301

302
  /* get size and so */
303 304
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, caps, ffmpegdec->context);
305

306 307 308 309 310 311 312 313 314
  /* 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);
  }

315 316 317 318
  /* 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
319
          ffmpegdec->context->codec_id == CODEC_ID_MPEG2VIDEO))
320 321
    ffmpegdec->context->flags |= CODEC_FLAG_TRUNCATED;

322 323 324
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

325 326 327
  /* workaround encoder bugs */
  ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT;

328 329 330
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
331 332 333 334 335 336 337 338 339
  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;
340 341
}

342
#if 0
343
static int
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
344
gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
345 346 347 348 349 350
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
351
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
352
          context->width, context->height);
353
      buf = gst_buffer_new_and_alloc (bufsize);
354 355
      gst_ffmpeg_avpicture_fill ((AVPicture *) picture,
          GST_BUFFER_DATA (buf),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
356
          context->pix_fmt, context->width, context->height);
357 358 359 360 361 362
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
363 364
  }

365 366 367 368 369 370 371 372 373 374 375 376 377
  /* 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;
}
378

379
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
380
gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture)
381 382 383
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
384

385 386 387
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
388
  for (i = 0; i < 4; i++) {
389 390 391
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
392
}
393
#endif
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 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
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;
}
444

445
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
446
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
447
{
448
  GstBuffer *inbuf = GST_BUFFER (_data);
449
  GstBuffer *outbuf = NULL;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
450 451 452
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
453 454
  guint8 *bdata, *data;
  gint bsize, size, len = 0;
455
  gint have_data;
456
  guint64 expected_ts = GST_BUFFER_TIMESTAMP (inbuf);
457

Ronald S. Bultje's avatar
Ronald S. Bultje committed
458
  if (!ffmpegdec->opened) {
459
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
460 461
        ("ffdec_%s: input format was not set before data start",
            oclass->in_plugin->name));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
462 463 464
    return;
  }

465 466 467 468
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
469

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
  /* parse cache joining */
  if (ffmpegdec->pcache) {
    inbuf = gst_buffer_join (ffmpegdec->pcache, inbuf);
    ffmpegdec->pcache = NULL;
    bdata = GST_BUFFER_DATA (inbuf);
    bsize = GST_BUFFER_SIZE (inbuf);
  }
  /* workarounds, functions write to buffers:
   *  libavcodec/svq1.c:svq1_decode_frame writes to the given buffer.
   *  libavcodec/svq3.c:svq3_decode_slice_header too.
   * ffmpeg devs know about it and will fix it (they said). */
  else if (oclass->in_plugin->id == CODEC_ID_SVQ1 ||
      oclass->in_plugin->id == CODEC_ID_SVQ3) {
    inbuf = gst_buffer_copy_on_write (inbuf);
    bdata = GST_BUFFER_DATA (inbuf);
    bsize = GST_BUFFER_SIZE (inbuf);
  } else {
    bdata = GST_BUFFER_DATA (inbuf);
    bsize = GST_BUFFER_SIZE (inbuf);
  }
490 491

  do {
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
    /* parse, if at all possible */
    if (ffmpegdec->pctx) {
      gint res;

      res = av_parser_parse (ffmpegdec->pctx, ffmpegdec->context,
          &data, &size, bdata, bsize,
          expected_ts / (GST_SECOND / AV_TIME_BASE),
          expected_ts / (GST_SECOND / AV_TIME_BASE));

      if (res == 0)
        break;
      else if (size == 0) {
        bsize = 0;
        break;
      } else {
        bsize -= res;
        bdata += res;
      }
    } else {
      data = bdata;
      size = bsize;
    }

515 516
    ffmpegdec->context->frame_number++;

517 518 519
    switch (oclass->in_plugin->type) {
      case CODEC_TYPE_VIDEO:
        len = avcodec_decode_video (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
520
            ffmpegdec->picture, &have_data, data, size);
521 522

        if (len >= 0 && have_data) {
523 524 525 526
          /* 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;
527
          gint fsize = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
528
              ffmpegdec->context->width, ffmpegdec->context->height);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
529

530
          outbuf = gst_buffer_new_and_alloc (fsize);
531 532
	  /* original ffmpeg code does not handle odd sizes correctly. This patched
	   * up version does */
533
          gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
534 535
              ffmpegdec->context->pix_fmt,
              ffmpegdec->context->width, ffmpegdec->context->height);
536 537 538 539 540

	  /* 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,
541 542
              (AVPicture *) ffmpegdec->picture,
              ffmpegdec->context->pix_fmt,
543 544
              ffmpegdec->context->width, 
	      ffmpegdec->context->height);
545

546
          /* note that ffmpeg sometimes gets the FPS wrong */
547 548
          if (GST_CLOCK_TIME_IS_VALID (expected_ts) &&
              ffmpegdec->context->frame_rate > 0) {
549 550 551 552
            GST_BUFFER_TIMESTAMP (outbuf) = expected_ts;
            GST_BUFFER_DURATION (outbuf) = GST_SECOND *
                ffmpegdec->context->frame_rate_base /
                ffmpegdec->context->frame_rate;
553 554
          } else {
            GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
555
          }
556
        }
557
        break;
558

559
      case CODEC_TYPE_AUDIO:
560
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
561
        len = avcodec_decode_audio (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
562
            (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
563 564 565
        if (have_data < 0) {
          GST_WARNING_OBJECT (ffmpegdec,
              "FFmpeg error: len %d, have_data: %d < 0 !",
566
              len, have_data);
567 568 569
          gst_buffer_unref (inbuf);
          return;
        }
570

571
        if (len >= 0 && have_data) {
572
          GST_BUFFER_SIZE (outbuf) = have_data;
573 574 575 576 577 578 579
          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);
          }
580 581
        } else {
          gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
582
        }
583 584
        break;
      default:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
585
        g_assert (0);
586 587
        break;
    }
588 589

    if (len < 0) {
590
      GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
591
          oclass->in_plugin->name);
592 593 594
      break;
    }

595
    if (have_data) {
596
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
597
        if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
598
          gst_buffer_unref (outbuf);
599 600
          return;
        }
601 602
      }

603 604 605 606
      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
607
    }
608

609 610 611 612 613
    if (!ffmpegdec->pctx) {
      bsize -= len;
      bdata += len;
    }
  } while (bsize > 0);
614

615 616 617 618
  if (ffmpegdec->pctx && bsize > 0) {
    ffmpegdec->pcache = gst_buffer_create_sub (inbuf,
        GST_BUFFER_SIZE (inbuf) - bsize, bsize);
  }
619 620 621
  gst_buffer_unref (inbuf);
}

622
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
623
gst_ffmpegdec_change_state (GstElement * element)
624
{
625 626 627 628 629
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
630
      gst_ffmpegdec_close (ffmpegdec);
631 632 633
      break;
  }

634 635
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
636

637
  return GST_STATE_SUCCESS;
638 639 640
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
641
gst_ffmpegdec_register (GstPlugin * plugin)
642 643
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
644 645
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
646
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
647
    (GClassInitFunc) gst_ffmpegdec_class_init,
648 649
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
650
    sizeof (GstFFMpegDec),
651
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
652
    (GInstanceInitFunc) gst_ffmpegdec_init,
653 654 655
  };
  GType type;
  AVCodec *in_plugin;
656
  gint rank;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
657

658 659 660 661 662
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
663
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
664 665
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
666

Ronald S. Bultje's avatar
Ronald S. Bultje committed
667 668
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
669 670
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
671
      goto next;
672
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
673 674 675

    /* only decoders */
    if (!in_plugin->decode) {
676 677
      goto next;
    }
678

679 680 681 682
    /* name */
    if (!gst_ffmpeg_get_codecid_longname (in_plugin->id))
      goto next;

683
    /* first make sure we've got a supported type */
684
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
685
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
686 687 688
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
689
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
690
    }
691

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

695
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
696 697
    if (g_type_from_name (type_name)) {
      g_free (type_name);
698 699 700
      goto next;
    }

701 702 703 704
    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
705 706 707
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

708
    /* create the gtype now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
709
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
710 711 712 713 714 715 716 717 718 719 720 721 722 723

    /* (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
724
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
725
      return FALSE;
David Schleef's avatar
David Schleef committed
726 727 728
    }

    g_free (type_name);
729

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
730 731
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
732

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
733
  next:
734 735
    in_plugin = in_plugin->next;
  }
736
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
737 738 739

  return TRUE;
}