gstffmpegdec.c 28.2 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 57 58
  union {
    struct {
      gint width, height, fps, fps_base;
    } video;
    struct {
      gint channels, samplerate;
    } audio;
59 60
  } format;
  guint64 next_ts;
61

62 63 64 65
  /* parsing */
  AVCodecParserContext *pctx;
  GstBuffer *pcache;

66
  GValue *par;		/* pixel aspect ratio of incoming data */
67 68 69 70
};

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
71 72
struct _GstFFMpegDecClass
{
73 74 75
  GstElementClass parent_class;

  AVCodec *in_plugin;
76
  GstPadTemplate *srctempl, *sinktempl;
77 78
};

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
79 80
typedef struct _GstFFMpegDecClassParams GstFFMpegDecClassParams;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
81 82
struct _GstFFMpegDecClassParams
{
83
  AVCodec *in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
84 85
  GstCaps *srccaps, *sinkcaps;
};
86

87 88 89 90 91 92 93 94 95 96
#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))
97

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
98 99
enum
{
100 101 102 103
  /* FILL ME */
  LAST_SIGNAL
};

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
104 105
enum
{
106 107 108 109 110 111 112
  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
113 114 115 116
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);
117

118 119 120 121
static gboolean gst_ffmpegdec_query (GstPad * pad, GstQueryType type,
    GstFormat * fmt, gint64 * value);
static gboolean gst_ffmpegdec_event (GstPad * pad, GstEvent * event);

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
122 123 124
static GstPadLinkReturn gst_ffmpegdec_connect (GstPad * pad,
    const GstCaps * caps);
static void gst_ffmpegdec_chain (GstPad * pad, GstData * data);
125

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

128
#if 0
129
/* some sort of bufferpool handling, but different */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
130 131 132 133
static int gst_ffmpegdec_get_buffer (AVCodecContext * context,
    AVFrame * picture);
static void gst_ffmpegdec_release_buffer (AVCodecContext * context,
    AVFrame * picture);
134
#endif
135 136 137

static GstElementClass *parent_class = NULL;

138
/*static guint gst_ffmpegdec_signals[LAST_SIGNAL] = { 0 }; */
139

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
140
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
141
gst_ffmpegdec_base_init (GstFFMpegDecClass * klass)
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
142 143 144 145
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstFFMpegDecClassParams *params;
Benjamin Otte's avatar
Benjamin Otte committed
146
  GstElementDetails details;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
147 148 149
  GstPadTemplate *sinktempl, *srctempl;

  params = g_hash_table_lookup (global_plugins,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
150
      GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
151
  if (!params)
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
152
    params = g_hash_table_lookup (global_plugins, GINT_TO_POINTER (0));
153
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
154 155

  /* construct the element details struct */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
156
  details.longname = g_strdup_printf ("FFMPEG %s decoder",
157
      gst_ffmpeg_get_codecid_longname (params->in_plugin->id));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
158
  details.klass = g_strdup_printf ("Codec/Decoder/%s",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
159 160 161
      (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
162
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
163
      "Ronald Bultje <rbultje@ronald.bitfreak.net>";
Benjamin Otte's avatar
Benjamin Otte committed
164 165 166 167
  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
168 169 170

  /* pad templates */
  sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
171
      GST_PAD_ALWAYS, params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
172
  srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
173
      GST_PAD_ALWAYS, params->srccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
174 175 176 177 178 179 180 181 182

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

183
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
184
gst_ffmpegdec_class_init (GstFFMpegDecClass * klass)
185 186 187 188
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
189 190
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
191

192
  parent_class = g_type_class_peek_parent (klass);
193

194 195 196 197 198
  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
199
gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec)
200
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
201 202
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
203 204 205 206 207

  /* 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);
208 209
  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad);

210
  ffmpegdec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
211
  gst_pad_use_explicit_caps (ffmpegdec->srcpad);
212 213 214 215
  gst_pad_set_event_function (ffmpegdec->srcpad,
      GST_DEBUG_FUNCPTR (gst_ffmpegdec_event));
  gst_pad_set_query_function (ffmpegdec->srcpad,
      GST_DEBUG_FUNCPTR (gst_ffmpegdec_query));
216 217 218
  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad);

  /* some ffmpeg data */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
219 220
  ffmpegdec->context = avcodec_alloc_context ();
  ffmpegdec->picture = avcodec_alloc_frame ();
221

222 223 224
  ffmpegdec->pctx = NULL;
  ffmpegdec->pcache = NULL;

225
  ffmpegdec->par = NULL;
226
  ffmpegdec->opened = FALSE;
227 228

  GST_FLAG_SET (ffmpegdec, GST_ELEMENT_EVENT_AWARE);
229 230 231
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
232
gst_ffmpegdec_dispose (GObject * object)
233 234
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;
235 236 237 238

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

240 241 242
  /* clean up remaining allocated data */
  av_free (ffmpegdec->context);
  av_free (ffmpegdec->picture);
243 244
}

245 246 247 248 249 250 251 252 253 254
static gboolean
gst_ffmpegdec_query (GstPad * pad, GstQueryType type,
    GstFormat * fmt, gint64 * value)
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) gst_pad_get_parent (pad);
  GstPad *peer = GST_PAD_PEER (ffmpegdec->sinkpad);
  GstFormat bfmt = GST_FORMAT_BYTES;

  if (!peer)
    return FALSE;
255 256

  if (gst_pad_query (peer, type, fmt, value))
257
    return TRUE;
258

259
  /* ok, do bitrate calc... */
260
  if ((type != GST_QUERY_POSITION && type != GST_QUERY_TOTAL) ||
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
           *fmt != GST_FORMAT_TIME || ffmpegdec->context->bit_rate == 0 ||
           !gst_pad_query (peer, type, &bfmt, value))
    return FALSE;

  if (ffmpegdec->pcache && type == GST_QUERY_POSITION)
    *value -= GST_BUFFER_SIZE (ffmpegdec->pcache);
  *value *= GST_SECOND / ffmpegdec->context->bit_rate;

  return TRUE;
}

static gboolean
gst_ffmpegdec_event (GstPad * pad, GstEvent * event)
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) gst_pad_get_parent (pad);
  GstPad *peer = GST_PAD_PEER (ffmpegdec->sinkpad);

  if (!peer)
    return FALSE;
280 281 282 283

  gst_event_ref (event);
  if (gst_pad_send_event (peer, event)) {
    gst_event_unref (event);
284
    return TRUE;
285 286 287 288 289
  }

  gst_event_unref (event);

  return FALSE; /* .. */
290 291
}

292 293 294 295 296 297
static void
gst_ffmpegdec_close (GstFFMpegDec *ffmpegdec)
{
  if (!ffmpegdec->opened)
    return;

298 299 300 301 302
  if (ffmpegdec->par) {
    g_free (ffmpegdec->par);
    ffmpegdec->par = NULL;
  }

303 304
  if (ffmpegdec->context->priv_data)
    avcodec_close (ffmpegdec->context);
305 306 307 308 309 310 311 312 313 314 315
  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;
  }
316 317 318 319 320 321 322 323 324

  if (ffmpegdec->pctx) {
    if (ffmpegdec->pcache) {
      gst_buffer_unref (ffmpegdec->pcache);
      ffmpegdec->pcache = NULL;
    }
    av_parser_close (ffmpegdec->pctx);
    ffmpegdec->pctx = NULL;
  }
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
}

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

341 342
  GST_LOG ("Opened ffmpeg codec %s", oclass->in_plugin->name);

343 344 345 346 347
  /* open a parser if we can - exclude mpeg4, because it is already
   * framed (divx), mp3 because it doesn't work (?) and mjpeg because
   * of $(see mpeg4)... */
  if (oclass->in_plugin->id != CODEC_ID_MPEG4 &&
      oclass->in_plugin->id != CODEC_ID_MJPEG &&
348
      oclass->in_plugin->id != CODEC_ID_MP3) {
349
    ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id);
350
  }
351

352 353 354 355 356 357 358 359 360 361 362 363 364 365
  switch (oclass->in_plugin->type) {
    case CODEC_TYPE_VIDEO:
      ffmpegdec->format.video.width = 0;
      ffmpegdec->format.video.height = 0;
      ffmpegdec->format.video.fps = 0;
      ffmpegdec->format.video.fps_base = 0;
      break;
    case CODEC_TYPE_AUDIO:
      ffmpegdec->format.audio.samplerate = 0;
      ffmpegdec->format.audio.channels = 0;
      break;
    default:
      break;
  }
366
  ffmpegdec->next_ts = 0;
367

368 369 370
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
372
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
373
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
374 375 376
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
377 378
  GstStructure *structure;
  const GValue *par;
379

380
  /* close old session */
381
  gst_ffmpegdec_close (ffmpegdec);
382

383 384
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
385

386
#if 0
387 388 389
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
390
#endif
391

392
  /* get size and so */
393 394
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, caps, ffmpegdec->context);
395

396 397 398 399 400 401 402 403 404
  /* 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);
  }

405 406 407
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

408 409 410
  /* workaround encoder bugs */
  ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT;

411 412 413
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
414 415 416 417 418 419 420 421 422
  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;
423 424
}

425
#if 0
426
static int
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
427
gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
428 429 430 431 432 433
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
434
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
435
          context->width, context->height);
436
      buf = gst_buffer_new_and_alloc (bufsize);
437 438
      gst_ffmpeg_avpicture_fill ((AVPicture *) picture,
          GST_BUFFER_DATA (buf),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
439
          context->pix_fmt, context->width, context->height);
440 441 442 443 444 445
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
446 447
  }

448 449 450 451 452 453 454 455 456 457 458 459 460
  /* 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;
}
461

462
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
463
gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture)
464 465 466
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
467

468 469 470
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
471
  for (i = 0; i < 4; i++) {
472 473 474
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
475
}
476
#endif
477

478 479 480 481 482 483 484
static gboolean
gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
{
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
  GstCaps *caps;

485 486 487 488 489 490 491 492
  switch (oclass->in_plugin->type) {
    case CODEC_TYPE_VIDEO:
      if (ffmpegdec->format.video.width == ffmpegdec->context->width &&
          ffmpegdec->format.video.height == ffmpegdec->context->height &&
          ffmpegdec->format.video.fps == ffmpegdec->context->frame_rate &&
          ffmpegdec->format.video.fps_base ==
              ffmpegdec->context->frame_rate_base)
        return TRUE;
493 494 495 496 497 498 499 500 501
      GST_DEBUG ("Renegotiating video from %dx%d@%d/%dfps to %dx%d@%d/%dfps",
          ffmpegdec->format.video.width, ffmpegdec->format.video.height,
          ffmpegdec->format.video.fps, ffmpegdec->format.video.fps_base,
          ffmpegdec->context->width, ffmpegdec->context->height,
          ffmpegdec->context->frame_rate, ffmpegdec->context->frame_rate_base);
      ffmpegdec->format.video.width = ffmpegdec->context->width;
      ffmpegdec->format.video.height = ffmpegdec->context->height;
      ffmpegdec->format.video.fps = ffmpegdec->context->frame_rate;
      ffmpegdec->format.video.fps_base = ffmpegdec->context->frame_rate_base;
502 503 504 505 506 507
      break;
    case CODEC_TYPE_AUDIO:
      if (ffmpegdec->format.audio.samplerate ==
              ffmpegdec->context->sample_rate &&
          ffmpegdec->format.audio.channels == ffmpegdec->context->channels)
        return TRUE;
508 509 510 511
      GST_DEBUG ("Renegotiating audio from %dHz@%dchannels to %dHz@%dchannels",
          ffmpegdec->format.audio.samplerate, ffmpegdec->format.audio.channels,
          ffmpegdec->context->sample_rate, ffmpegdec->context->channels);
      ffmpegdec->format.audio.samplerate = ffmpegdec->context->sample_rate;
512
      ffmpegdec->format.audio.channels = ffmpegdec->context->channels;
513 514 515 516 517
      break;
    default:
      break;
  }

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
  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;
}
560

561 562
static gint
gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
563
    guint8 * data, guint size, gint * got_data, guint64 * in_ts)
564 565 566 567 568 569 570 571 572 573 574 575
{
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
  GstBuffer *outbuf = NULL;
  gint have_data, len = 0;

  ffmpegdec->context->frame_number++;

  switch (oclass->in_plugin->type) {
    case CODEC_TYPE_VIDEO:
      len = avcodec_decode_video (ffmpegdec->context,
          ffmpegdec->picture, &have_data, data, size);
576 577
      GST_DEBUG_OBJECT (ffmpegdec,
          "Decode video: len=%d, have_data=%d", len, have_data);
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

      if (len >= 0 && have_data > 0) {
        /* 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;
        gint fsize = gst_ffmpeg_avpicture_get_size (ffmpegdec->context->pix_fmt,
            ffmpegdec->context->width, ffmpegdec->context->height);

        outbuf = gst_buffer_new_and_alloc (fsize);

        /* original ffmpeg code does not handle odd sizes correctly.
         * This patched up version does */
        gst_ffmpeg_avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
            ffmpegdec->context->pix_fmt,
            ffmpegdec->context->width, ffmpegdec->context->height);

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

604 605 606 607 608 609
        /* note that ffmpeg sometimes gets the FPS wrong.
         * For B-frame containing movies, we get all pictures delayed
         * except for the I frames, so we synchronize only on I frames
         * and keep an internal counter based on FPS for the others. */
        if (ffmpegdec->picture->pict_type == FF_I_TYPE &&
            GST_CLOCK_TIME_IS_VALID (*in_ts) &&
610
            ffmpegdec->context->frame_rate > 0) {
611
          ffmpegdec->next_ts = *in_ts;
612
        }
613 614 615 616 617
        GST_BUFFER_TIMESTAMP (outbuf) = ffmpegdec->next_ts;
        GST_BUFFER_DURATION (outbuf) = GST_SECOND *
            ffmpegdec->context->frame_rate_base /
            ffmpegdec->context->frame_rate;
        ffmpegdec->next_ts += GST_BUFFER_DURATION (outbuf);
618 619 620 621 622 623 624
      }
      break;

    case CODEC_TYPE_AUDIO:
      outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
      len = avcodec_decode_audio (ffmpegdec->context,
          (int16_t *) GST_BUFFER_DATA (outbuf), &have_data, data, size);
625 626
      GST_DEBUG_OBJECT (ffmpegdec,
          "Decode audio: len=%d, have_data=%d", len, have_data);
627 628 629

      if (len >= 0 && have_data > 0) {
        GST_BUFFER_SIZE (outbuf) = have_data;
630 631
        if (GST_CLOCK_TIME_IS_VALID (*in_ts)) {
          ffmpegdec->next_ts = *in_ts;
632
        }
633 634 635 636 637 638
        GST_BUFFER_TIMESTAMP (outbuf) = ffmpegdec->next_ts;
        GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
            (2 * ffmpegdec->context->channels *
            ffmpegdec->context->sample_rate);
        ffmpegdec->next_ts += GST_BUFFER_DURATION (outbuf);
        *in_ts += GST_BUFFER_DURATION (outbuf);
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
      } else {
        gst_buffer_unref (outbuf);
      }
      break;
    default:
      g_assert (0);
      break;
  }

  if (len < 0 || have_data < 0) {
    GST_ERROR_OBJECT (ffmpegdec,
        "ffdec_%s: decoding error (len: %d, have_data: %d)",
        oclass->in_plugin->name, len, have_data);
    *got_data = 0;
    return len;
  } else if (len == 0 && have_data == 0) {
    *got_data = 0;
    return 0;
  } else {
658 659
    /* this is where I lost my last clue on ffmpeg... */
    *got_data = 1; //(ffmpegdec->pctx || have_data) ? 1 : 0;
660 661 662
  }

  if (have_data) {
663
    GST_DEBUG_OBJECT (ffmpegdec, "Decoded data, now pushing");
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

    if (!gst_ffmpegdec_negotiate (ffmpegdec)) {
      gst_buffer_unref (outbuf);
      return -1;
    }

    if (GST_PAD_IS_USABLE (ffmpegdec->srcpad))
      gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
    else
      gst_buffer_unref (outbuf);
  }

  return len;
}

679 680 681
static void
gst_ffmpegdec_handle_event (GstFFMpegDec * ffmpegdec, GstEvent * event)
{
682 683
  GST_DEBUG_OBJECT (ffmpegdec,
      "Handling event of type %d", GST_EVENT_TYPE (event));
684

685
  switch (GST_EVENT_TYPE (event)) {
686 687 688 689 690 691 692 693 694 695 696
    case GST_EVENT_EOS: {
      gint have_data, len;

      do {
        len = gst_ffmpegdec_frame (ffmpegdec, NULL, 0, &have_data,
            &ffmpegdec->next_ts);
        if (len < 0 || have_data == 0)
          break;
      } while (1);
      goto forward;
    }
697
    case GST_EVENT_FLUSH:
698 699 700
      if (ffmpegdec->opened) {
        avcodec_flush_buffers (ffmpegdec->context);
      }
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
      goto forward;
    case GST_EVENT_DISCONTINUOUS: {
      gint64 value;

      if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
        ffmpegdec->next_ts = value;
        GST_DEBUG_OBJECT (ffmpegdec, "Discont to time %" GST_TIME_FORMAT,
            GST_TIME_ARGS (value));
      } else if (ffmpegdec->context->bit_rate &&
          gst_event_discont_get_value (event, GST_FORMAT_BYTES, &value)) {
        gboolean new_media;

        ffmpegdec->next_ts = value * GST_SECOND / ffmpegdec->context->bit_rate;
        GST_DEBUG_OBJECT (ffmpegdec,
            "Discont to byte %lld, time %" GST_TIME_FORMAT,
            value, GST_TIME_ARGS (ffmpegdec->next_ts));
        new_media = GST_EVENT_DISCONT_NEW_MEDIA (event);
        gst_event_unref (event);
        event = gst_event_new_discontinuous (new_media,
            GST_FORMAT_TIME, ffmpegdec->next_ts, GST_FORMAT_UNDEFINED);
      } else {
        GST_WARNING_OBJECT (ffmpegdec,
            "Received discont with no useful value...");
      }
725 726 727
      if (ffmpegdec->opened) {
        avcodec_flush_buffers (ffmpegdec->context);
      }
728 729 730 731 732 733 734 735 736
      /* fall-through */
    }
    default:
    forward:
      gst_pad_event_default (ffmpegdec->sinkpad, event);
      return;
  }
}

737
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
738
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
739
{
740
  GstBuffer *inbuf;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
741 742 743
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
744
  guint8 *bdata, *data;
745
  gint bsize, size, len, have_data;
746 747 748 749 750 751 752 753 754 755
  guint64 in_ts;

  /* event handling */
  if (GST_IS_EVENT (_data)) {
    gst_ffmpegdec_handle_event (ffmpegdec, GST_EVENT (_data));
    return;
  }

  inbuf = GST_BUFFER (_data);
  in_ts = GST_BUFFER_TIMESTAMP (inbuf);
756

Ronald S. Bultje's avatar
Ronald S. Bultje committed
757
  if (!ffmpegdec->opened) {
758
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
759 760
        ("ffdec_%s: input format was not set before data start",
            oclass->in_plugin->name));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
761 762 763
    return;
  }

764 765
  GST_DEBUG_OBJECT (ffmpegdec,
      "Received new data of size %d, time %" GST_TIME_FORMAT,
766
      GST_BUFFER_SIZE (inbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)));
767

768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
  /* 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);
  }
788 789

  do {
790
    /* parse, if at all possible */
791
    if (ffmpegdec->pctx) {
792 793 794 795
      gint res;

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

799 800
      GST_DEBUG_OBJECT (ffmpegdec, "Parsed video frame, res=%d, size=%d",
          res, size);
801
      if (res == 0 || size == 0)
802
        break;
803
      else {
804 805 806 807 808 809 810 811
        bsize -= res;
        bdata += res;
      }
    } else {
      data = bdata;
      size = bsize;
    }

812
    if ((len = gst_ffmpegdec_frame (ffmpegdec, data, size,
813
             &have_data, &in_ts)) < 0)
814 815
      break;

816
    if (!ffmpegdec->pctx) {
817 818 819
      bsize -= len;
      bdata += len;
    }
820 821 822 823

    if (!have_data) {
      break;
    }
824
  } while (bsize > 0);
825

826 827
  if ((ffmpegdec->pctx || oclass->in_plugin->id == CODEC_ID_MP3) &&
      bsize > 0) {
828
    GST_DEBUG_OBJECT (ffmpegdec, "Keeping %d bytes of data", bsize);
829

830 831 832
    ffmpegdec->pcache = gst_buffer_create_sub (inbuf,
        GST_BUFFER_SIZE (inbuf) - bsize, bsize);
  }
833 834 835
  gst_buffer_unref (inbuf);
}

836
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
837
gst_ffmpegdec_change_state (GstElement * element)
838
{
839 840 841 842 843
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
844
      gst_ffmpegdec_close (ffmpegdec);
845 846 847
      break;
  }

848 849
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
850

851
  return GST_STATE_SUCCESS;
852 853 854
}

gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
855
gst_ffmpegdec_register (GstPlugin * plugin)
856 857
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
858 859
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
860
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
861
    (GClassInitFunc) gst_ffmpegdec_class_init,
862 863
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
864
    sizeof (GstFFMpegDec),
865
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
866
    (GInstanceInitFunc) gst_ffmpegdec_init,
867 868 869
  };
  GType type;
  AVCodec *in_plugin;
870
  gint rank;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
871

872 873 874 875 876
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
877
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
878 879
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
880

Ronald S. Bultje's avatar
Ronald S. Bultje committed
881 882
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
883 884
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
885
      goto next;
886
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
887 888 889

    /* only decoders */
    if (!in_plugin->decode) {
890 891
      goto next;
    }
892

893 894 895 896
    /* name */
    if (!gst_ffmpeg_get_codecid_longname (in_plugin->id))
      goto next;

897
    /* first make sure we've got a supported type */
898
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
899
    srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
900 901 902
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
903
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
904
    }
905

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

909
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
910 911
    if (g_type_from_name (type_name)) {
      g_free (type_name);
912 913 914
      goto next;
    }

915 916 917 918
    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
919 920 921
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

922
    /* create the gtype now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
923
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
924 925 926

    /* (Ronald) MPEG-4 gets a higher priority because it has been well-
     * tested and by far outperforms divxdec/xviddec - so we prefer it.
927 928 929 930 931
     * 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. */
932
    switch (in_plugin->id) {
933
      case CODEC_ID_MPEG2VIDEO:
934 935 936 937 938 939 940 941 942
      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
943
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
944
      return FALSE;
David Schleef's avatar
David Schleef committed
945 946 947
    }

    g_free (type_name);
948

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
949 950
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
951

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
952
  next:
953 954
    in_plugin = in_plugin->next;
  }
955
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
956 957 958

  return TRUE;
}