gstffmpegdec.c 22.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 "gstffmpeg.h"
36
#include "gstffmpegcodecmap.h"
37

38 39
typedef struct _GstFFMpegDec GstFFMpegDec;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
40 41
struct _GstFFMpegDec
{
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;

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

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

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

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

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

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

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

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

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

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

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

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

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

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

static GstElementClass *parent_class = NULL;

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

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

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

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

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

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

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

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

179
  parent_class = g_type_class_peek_parent (klass);
180

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

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

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

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

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

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

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

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

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

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

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

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

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

275 276 277 278
  /* 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);

279 280 281
  return TRUE;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

386 387 388
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
389
  for (i = 0; i < 4; i++) {
390 391 392
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
393
}
394
#endif
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 444
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;
}
445

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

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

466 467
  GST_DEBUG ("Received new data of size %d", GST_BUFFER_SIZE (inbuf));

468 469 470 471
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
472

473 474
  /* parse cache joining */
  if (ffmpegdec->pcache) {
475 476 477 478
GST_LOG ("Joining %p[%lld/%d]&&%p[%lld/%d]",
	 ffmpegdec->pcache, GST_BUFFER_OFFSET (ffmpegdec->pcache),
	 GST_BUFFER_SIZE (ffmpegdec->pcache), inbuf,
	 GST_BUFFER_OFFSET (inbuf), GST_BUFFER_SIZE (inbuf));
479
    inbuf = gst_buffer_join (ffmpegdec->pcache, inbuf);
480
GST_LOG ("done");
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    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);
  }
498 499

  do {
500
    /* parse, if at all possible */
501 502
    if (ffmpegdec->pctx && ffmpegdec->context->codec_id != CODEC_ID_MP3 && 
	ffmpegdec->context->codec_id != CODEC_ID_MJPEG) {
503 504 505 506 507 508 509
      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));

510
      if (res == 0 || size == 0)
511
        break;
512
      else {
513 514 515 516 517 518 519 520
        bsize -= res;
        bdata += res;
      }
    } else {
      data = bdata;
      size = bsize;
    }

521 522
    ffmpegdec->context->frame_number++;

523 524 525
    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
526
            ffmpegdec->picture, &have_data, data, size);
527
        GST_DEBUG ("Decode video: len=%d, have_data=%d", len, have_data);
528 529

        if (len >= 0 && have_data) {
530 531 532 533
          /* 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;
534
          gint fsize = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
535
              ffmpegdec->context->width, ffmpegdec->context->height);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
536

537
          outbuf = gst_buffer_new_and_alloc (fsize);
538 539
	  /* original ffmpeg code does not handle odd sizes correctly. This patched
	   * up version does */
540
          gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
541 542
              ffmpegdec->context->pix_fmt,
              ffmpegdec->context->width, ffmpegdec->context->height);
543 544 545 546 547

	  /* 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,
548 549
              (AVPicture *) ffmpegdec->picture,
              ffmpegdec->context->pix_fmt,
550 551
              ffmpegdec->context->width, 
	      ffmpegdec->context->height);
552

553
          /* note that ffmpeg sometimes gets the FPS wrong */
554 555
          if (GST_CLOCK_TIME_IS_VALID (expected_ts) &&
              ffmpegdec->context->frame_rate > 0) {
556 557 558 559
            GST_BUFFER_TIMESTAMP (outbuf) = expected_ts;
            GST_BUFFER_DURATION (outbuf) = GST_SECOND *
                ffmpegdec->context->frame_rate_base /
                ffmpegdec->context->frame_rate;
560 561
          } else {
            GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
562
          }
563
        }
564
        break;
565

566
      case CODEC_TYPE_AUDIO:
567
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
568
        len = avcodec_decode_audio (ffmpegdec->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
569
            (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
570 571
        GST_DEBUG ("Decode audio: len=%d, have_data=%d", len, have_data);

572 573 574
        if (have_data < 0) {
          GST_WARNING_OBJECT (ffmpegdec,
              "FFmpeg error: len %d, have_data: %d < 0 !",
575
              len, have_data);
576 577 578
          gst_buffer_unref (inbuf);
          return;
        }
579

580
        if (len >= 0 && have_data) {
581
          GST_BUFFER_SIZE (outbuf) = have_data;
582 583 584 585 586 587 588
          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);
          }
589 590
        } else {
          gst_buffer_unref (outbuf);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
591
        }
592 593
        break;
      default:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
594
        g_assert (0);
595 596
        break;
    }
597 598

    if (len < 0) {
599
      GST_ERROR_OBJECT (ffmpegdec, "ffdec_%s: decoding error",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
600
          oclass->in_plugin->name);
601
      break;
602 603
    } else if (len == 0) {
      break;
604 605
    }

606
    if (have_data) {
607 608
      GST_DEBUG ("Decoded data, now pushing");

609
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
610
        if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
611
          gst_buffer_unref (outbuf);
612 613
          return;
        }
614 615
      }

616 617 618 619
      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
620
    }
621

622 623
    if (!ffmpegdec->pctx || ffmpegdec->context->codec_id == CODEC_ID_MP3 || 
	ffmpegdec->context->codec_id == CODEC_ID_MJPEG) {
624 625 626 627
      bsize -= len;
      bdata += len;
    }
  } while (bsize > 0);
628

629
  if (ffmpegdec->pctx && bsize > 0) {
630 631
    GST_DEBUG ("Keeping %d bytes of data", bsize);

632 633 634
    ffmpegdec->pcache = gst_buffer_create_sub (inbuf,
        GST_BUFFER_SIZE (inbuf) - bsize, bsize);
  }
635 636 637
  gst_buffer_unref (inbuf);
}

638
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
639
gst_ffmpegdec_change_state (GstElement * element)
640
{
641 642 643 644 645
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
646
      gst_ffmpegdec_close (ffmpegdec);
647 648 649
      break;
  }

650 651
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
652

653
  return GST_STATE_SUCCESS;
654 655 656
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
657
gst_ffmpegdec_register (GstPlugin * plugin)
658 659
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
660 661
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
662
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
663
    (GClassInitFunc) gst_ffmpegdec_class_init,
664 665
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
666
    sizeof (GstFFMpegDec),
667
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
668
    (GInstanceInitFunc) gst_ffmpegdec_init,
669 670 671
  };
  GType type;
  AVCodec *in_plugin;
672
  gint rank;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
673

674 675 676 677 678
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
679
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
680 681
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
682

Ronald S. Bultje's avatar
Ronald S. Bultje committed
683 684
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
685 686
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
687
      goto next;
688
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
689 690 691

    /* only decoders */
    if (!in_plugin->decode) {
692 693
      goto next;
    }
694

695 696 697 698
    /* name */
    if (!gst_ffmpeg_get_codecid_longname (in_plugin->id))
      goto next;

699
    /* first make sure we've got a supported type */
700
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
701
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
702 703 704
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
705
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
706
    }
707

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

711
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
712 713
    if (g_type_from_name (type_name)) {
      g_free (type_name);
714 715 716
      goto next;
    }

717 718 719 720
    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
721 722 723
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

724
    /* create the gtype now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
725
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
726 727 728

    /* (Ronald) MPEG-4 gets a higher priority because it has been well-
     * tested and by far outperforms divxdec/xviddec - so we prefer it.
729 730 731 732 733
     * msmpeg4v3 same, as it outperforms divxdec for divx3 playback.
     * H263 has the same mimetype as H263I and since H263 works for the
     * few streams that I've tried (see, e.g., #155163), I'll use that
     * and use rank=none for H263I for now, until I know what the diff
     * is. */
734 735 736 737 738
    switch (in_plugin->id) {
      case CODEC_ID_MPEG4:
      case CODEC_ID_MSMPEG4V3:
        rank = GST_RANK_PRIMARY;
        break;
739 740 741
      case CODEC_ID_H263I:
        rank = GST_RANK_NONE;
        break;
742 743 744 745 746
      default:
        rank = GST_RANK_MARGINAL;
        break;
    }
    if (!gst_element_register (plugin, type_name, rank, type)) {
David Schleef's avatar
David Schleef committed
747
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
748
      return FALSE;
David Schleef's avatar
David Schleef committed
749 750 751
    }

    g_free (type_name);
752

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
753 754
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
755

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
756
  next:
757 758
    in_plugin = in_plugin->next;
  }
759
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
760 761 762

  return TRUE;
}