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

  gint hurry_up, lowres;
69 70 71 72
};

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

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

  AVCodec *in_plugin;
78
  GstPadTemplate *srctempl, *sinktempl;
79 80
};

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
81 82
typedef struct _GstFFMpegDecClassParams GstFFMpegDecClassParams;

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

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

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
100 101
enum
{
102
  ARG_0,
103 104
  ARG_LOWRES,
  ARG_SKIPFRAME
105 106 107 108 109
};

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
110 111 112 113
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);
114

115 116 117 118
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
119 120 121
static GstPadLinkReturn gst_ffmpegdec_connect (GstPad * pad,
    const GstCaps * caps);
static void gst_ffmpegdec_chain (GstPad * pad, GstData * data);
122

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

125 126 127 128 129
static void gst_ffmpegdec_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_ffmpegdec_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);

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

static GstElementClass *parent_class = NULL;

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
#define GST_FFMPEGDEC_TYPE_LOWRES (gst_ffmpegdec_lowres_get_type())
static GType
gst_ffmpegdec_lowres_get_type (void)
{
  static GType ffmpegdec_lowres_type = 0;

  if (!ffmpegdec_lowres_type) {
    static GEnumValue ffmpegdec_lowres[] = {
      {0, "0", "full"},
      {1, "1", "1/2-size"},
      {2, "2", "1/4-size"},
      {0, NULL, NULL},
    };

    ffmpegdec_lowres_type =
        g_enum_register_static ("GstFFMpegDecLowres", ffmpegdec_lowres);
  }

  return ffmpegdec_lowres_type;
}

#define GST_FFMPEGDEC_TYPE_SKIPFRAME (gst_ffmpegdec_skipframe_get_type())
static GType
gst_ffmpegdec_skipframe_get_type (void)
{
  static GType ffmpegdec_skipframe_type = 0;

  if (!ffmpegdec_skipframe_type) {
    static GEnumValue ffmpegdec_skipframe[] = {
      {0, "0", "Skip nothing"},
      {1, "1", "Skip B-frames"},
      {2, "2", "Skip IDCT/Dequantization"},
      {5, "5", "Skip everything"},
      {0, NULL, NULL},
    };

    ffmpegdec_skipframe_type =
        g_enum_register_static ("GstFFMpegDecSkipFrame", ffmpegdec_skipframe);
  }

  return ffmpegdec_skipframe_type;
}
182

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
183
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
184
gst_ffmpegdec_base_init (GstFFMpegDecClass * klass)
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
185 186 187 188
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstFFMpegDecClassParams *params;
Benjamin Otte's avatar
Benjamin Otte committed
189
  GstElementDetails details;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
190 191 192
  GstPadTemplate *sinktempl, *srctempl;

  params = g_hash_table_lookup (global_plugins,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
193
      GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
194
  if (!params)
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
195
    params = g_hash_table_lookup (global_plugins, GINT_TO_POINTER (0));
196
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
197 198

  /* construct the element details struct */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
199
  details.longname = g_strdup_printf ("FFMPEG %s decoder",
200
      gst_ffmpeg_get_codecid_longname (params->in_plugin->id));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
201
  details.klass = g_strdup_printf ("Codec/Decoder/%s",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
202 203 204
      (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
205
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
206
      "Ronald Bultje <rbultje@ronald.bitfreak.net>";
Benjamin Otte's avatar
Benjamin Otte committed
207 208 209 210
  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
211 212 213

  /* pad templates */
  sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
214
      GST_PAD_ALWAYS, params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
215
  srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
216
      GST_PAD_ALWAYS, params->srccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
217 218 219 220 221 222 223 224 225

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

226
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
227
gst_ffmpegdec_class_init (GstFFMpegDecClass * klass)
228
{
229 230
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
231

232
  parent_class = g_type_class_peek_parent (klass);
233

234 235 236 237 238 239 240 241 242
  g_object_class_install_property (gobject_class, ARG_SKIPFRAME,
      g_param_spec_enum ("skip-frame", "Skip frames",
          "Which types of frames to skip during decoding",
          GST_FFMPEGDEC_TYPE_SKIPFRAME, 0, G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class, ARG_LOWRES,
      g_param_spec_enum ("lowres", "Low resolution",
          "At which resolution to decode images",
          GST_FFMPEGDEC_TYPE_LOWRES, 0, G_PARAM_READWRITE));

243
  gobject_class->dispose = gst_ffmpegdec_dispose;
244 245
  gobject_class->set_property = gst_ffmpegdec_set_property;
  gobject_class->get_property = gst_ffmpegdec_get_property;
246 247 248 249
  gstelement_class->change_state = gst_ffmpegdec_change_state;
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
250
gst_ffmpegdec_init (GstFFMpegDec * ffmpegdec)
251
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
252 253
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
254 255 256 257 258

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

261
  ffmpegdec->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
262
  gst_pad_use_explicit_caps (ffmpegdec->srcpad);
263 264 265 266
  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));
267 268 269
  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad);

  /* some ffmpeg data */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
270 271
  ffmpegdec->context = avcodec_alloc_context ();
  ffmpegdec->picture = avcodec_alloc_frame ();
272 273
  ffmpegdec->pctx = NULL;
  ffmpegdec->pcache = NULL;
274
  ffmpegdec->par = NULL;
275
  ffmpegdec->opened = FALSE;
276
  ffmpegdec->hurry_up = ffmpegdec->lowres = 0;
277 278

  GST_FLAG_SET (ffmpegdec, GST_ELEMENT_EVENT_AWARE);
279 280 281
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
282
gst_ffmpegdec_dispose (GObject * object)
283 284
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;
285 286 287 288

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

290 291 292
  /* clean up remaining allocated data */
  av_free (ffmpegdec->context);
  av_free (ffmpegdec->picture);
293 294
}

295 296 297 298 299 300 301 302 303 304
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;
305 306

  if (gst_pad_query (peer, type, fmt, value))
307
    return TRUE;
308

309
  /* ok, do bitrate calc... */
310
  if ((type != GST_QUERY_POSITION && type != GST_QUERY_TOTAL) ||
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
           *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;
330 331 332 333

  gst_event_ref (event);
  if (gst_pad_send_event (peer, event)) {
    gst_event_unref (event);
334
    return TRUE;
335 336 337 338 339
  }

  gst_event_unref (event);

  return FALSE; /* .. */
340 341
}

342 343 344 345 346 347
static void
gst_ffmpegdec_close (GstFFMpegDec *ffmpegdec)
{
  if (!ffmpegdec->opened)
    return;

348 349 350 351 352
  if (ffmpegdec->par) {
    g_free (ffmpegdec->par);
    ffmpegdec->par = NULL;
  }

353 354
  if (ffmpegdec->context->priv_data)
    avcodec_close (ffmpegdec->context);
355 356 357 358 359 360 361 362 363 364 365
  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;
  }
366 367 368 369 370 371 372 373 374

  if (ffmpegdec->pctx) {
    if (ffmpegdec->pcache) {
      gst_buffer_unref (ffmpegdec->pcache);
      ffmpegdec->pcache = NULL;
    }
    av_parser_close (ffmpegdec->pctx);
    ffmpegdec->pctx = NULL;
  }
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
}

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

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

393 394 395 396 397
  /* 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 &&
398
      oclass->in_plugin->id != CODEC_ID_MP3) {
399
    ffmpegdec->pctx = av_parser_init (oclass->in_plugin->id);
400
  }
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415
  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;
  }
416
  ffmpegdec->next_ts = 0;
417

418 419 420
  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
422
gst_ffmpegdec_connect (GstPad * pad, const GstCaps * caps)
423
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
424 425 426
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
427 428
  GstStructure *structure;
  const GValue *par;
429

430
  /* close old session */
431
  gst_ffmpegdec_close (ffmpegdec);
432

433 434
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
435

436
#if 0
437 438 439
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
440
#endif
441

442
  /* get size and so */
443 444
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, caps, ffmpegdec->context);
445

446 447 448 449 450 451 452 453 454
  /* 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);
  }

455 456 457
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

458 459 460
  /* workaround encoder bugs */
  ffmpegdec->context->workaround_bugs |= FF_BUG_AUTODETECT;

461 462 463 464
  /* for slow cpus */
  ffmpegdec->context->lowres = ffmpegdec->lowres;
  ffmpegdec->context->hurry_up = ffmpegdec->hurry_up;

465 466 467
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
468 469 470 471 472 473 474 475 476
  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;
477 478
}

479
#if 0
480
static int
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
481
gst_ffmpegdec_get_buffer (AVCodecContext * context, AVFrame * picture)
482 483 484 485 486 487
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
488
      bufsize = avpicture_get_size (context->pix_fmt,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
489
          context->width, context->height);
490
      buf = gst_buffer_new_and_alloc (bufsize);
491 492
      gst_ffmpeg_avpicture_fill ((AVPicture *) picture,
          GST_BUFFER_DATA (buf),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
493
          context->pix_fmt, context->width, context->height);
494 495 496 497 498 499
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
500 501
  }

502 503 504 505 506 507 508 509 510 511 512 513 514
  /* 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;
}
515

516
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
517
gst_ffmpegdec_release_buffer (AVCodecContext * context, AVFrame * picture)
518 519 520
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
521

522 523 524
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
525
  for (i = 0; i < 4; i++) {
526 527 528
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
529
}
530
#endif
531

532 533 534 535 536 537 538
static gboolean
gst_ffmpegdec_negotiate (GstFFMpegDec * ffmpegdec)
{
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
  GstCaps *caps;

539 540 541 542 543 544 545 546
  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;
547 548 549 550 551 552 553 554 555
      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;
556 557 558 559 560 561
      break;
    case CODEC_TYPE_AUDIO:
      if (ffmpegdec->format.audio.samplerate ==
              ffmpegdec->context->sample_rate &&
          ffmpegdec->format.audio.channels == ffmpegdec->context->channels)
        return TRUE;
562 563 564 565
      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;
566
      ffmpegdec->format.audio.channels = ffmpegdec->context->channels;
567 568 569 570 571
      break;
    default:
      break;
  }

572 573 574 575 576 577 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 604 605 606 607 608 609 610 611 612 613
  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;
}
614

615 616
static gint
gst_ffmpegdec_frame (GstFFMpegDec * ffmpegdec,
617
    guint8 * data, guint size, gint * got_data, guint64 * in_ts)
618 619 620 621 622 623 624 625 626 627
{
  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:
628
      ffmpegdec->picture->pict_type = -1; /* in case we skip frames */
629 630
      len = avcodec_decode_video (ffmpegdec->context,
          ffmpegdec->picture, &have_data, data, size);
631 632
      GST_DEBUG_OBJECT (ffmpegdec,
          "Decode video: len=%d, have_data=%d", len, have_data);
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658

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

659 660 661 662
        /* 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. */
663 664 665
        if ((ffmpegdec->picture->pict_type == FF_I_TYPE ||
             !GST_CLOCK_TIME_IS_VALID (ffmpegdec->next_ts)) &&
            GST_CLOCK_TIME_IS_VALID (*in_ts)) {
666
          ffmpegdec->next_ts = *in_ts;
667
        }
668

669
        GST_BUFFER_TIMESTAMP (outbuf) = ffmpegdec->next_ts;
670 671 672 673 674
        if (ffmpegdec->context->frame_rate_base != 0 &&
            ffmpegdec->context->frame_rate != 0) {
          GST_BUFFER_DURATION (outbuf) = GST_SECOND *
              ffmpegdec->context->frame_rate_base /
              ffmpegdec->context->frame_rate;
675 676 677 678 679

          /* Take repeat_pict into account */
          GST_BUFFER_DURATION (outbuf) += GST_BUFFER_DURATION (outbuf)
              * ffmpegdec->picture->repeat_pict / 2;
	  
680 681 682 683
          ffmpegdec->next_ts += GST_BUFFER_DURATION (outbuf);
        } else {
          ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
        }
684 685
      } else if (ffmpegdec->picture->pict_type != -1) {
        /* update time for skip-frame */
686 687 688
        if ((ffmpegdec->picture->pict_type == FF_I_TYPE ||
             !GST_CLOCK_TIME_IS_VALID (ffmpegdec->next_ts)) &&
            GST_CLOCK_TIME_IS_VALID (*in_ts)) {
689
          ffmpegdec->next_ts = *in_ts;
690
        }
691
        
692 693
        if (ffmpegdec->context->frame_rate_base != 0 &&
            ffmpegdec->context->frame_rate != 0) {
694
          guint64 dur = GST_SECOND *  
695 696
            ffmpegdec->context->frame_rate_base /
            ffmpegdec->context->frame_rate;
697 698 699 700 701

          /* Take repeat_pict into account */
          dur += dur * ffmpegdec->picture->repeat_pict / 2;
	  
          ffmpegdec->next_ts += dur;
702 703
        } else {
          ffmpegdec->next_ts = GST_CLOCK_TIME_NONE;
704
        }
705 706 707 708 709 710 711
      }
      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);
712 713
      GST_DEBUG_OBJECT (ffmpegdec,
          "Decode audio: len=%d, have_data=%d", len, have_data);
714 715 716

      if (len >= 0 && have_data > 0) {
        GST_BUFFER_SIZE (outbuf) = have_data;
717 718
        if (GST_CLOCK_TIME_IS_VALID (*in_ts)) {
          ffmpegdec->next_ts = *in_ts;
719
        }
720 721 722 723 724 725
        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);
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
      } 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 {
745 746
    /* this is where I lost my last clue on ffmpeg... */
    *got_data = 1; //(ffmpegdec->pctx || have_data) ? 1 : 0;
747 748 749
  }

  if (have_data) {
750 751
    GST_DEBUG_OBJECT (ffmpegdec, "Decoded data, now pushing (%"
        GST_TIME_FORMAT ")", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)));
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766

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

767 768 769
static void
gst_ffmpegdec_handle_event (GstFFMpegDec * ffmpegdec, GstEvent * event)
{
770 771
  GST_DEBUG_OBJECT (ffmpegdec,
      "Handling event of type %d", GST_EVENT_TYPE (event));
772

773
  switch (GST_EVENT_TYPE (event)) {
774 775 776 777 778 779 780 781 782 783 784
    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;
    }
785
    case GST_EVENT_FLUSH:
786 787 788
      if (ffmpegdec->opened) {
        avcodec_flush_buffers (ffmpegdec->context);
      }
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
      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...");
      }
813 814 815
      if (ffmpegdec->opened) {
        avcodec_flush_buffers (ffmpegdec->context);
      }
816 817 818 819 820 821 822 823 824
      /* fall-through */
    }
    default:
    forward:
      gst_pad_event_default (ffmpegdec->sinkpad, event);
      return;
  }
}

825
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
826
gst_ffmpegdec_chain (GstPad * pad, GstData * _data)
827
{
828
  GstBuffer *inbuf;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
829 830 831
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) (gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass =
      (GstFFMpegDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
832
  guint8 *bdata, *data;
833
  gint bsize, size, len, have_data;
834 835 836 837 838 839 840 841 842 843
  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);
844

Ronald S. Bultje's avatar
Ronald S. Bultje committed
845
  if (!ffmpegdec->opened) {
846
    GST_ELEMENT_ERROR (ffmpegdec, CORE, NEGOTIATION, (NULL),
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
847 848
        ("ffdec_%s: input format was not set before data start",
            oclass->in_plugin->name));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
849 850 851
    return;
  }

852 853
  GST_DEBUG_OBJECT (ffmpegdec,
      "Received new data of size %d, time %" GST_TIME_FORMAT,
854
      GST_BUFFER_SIZE (inbuf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)));
855

856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
  /* 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);
  }
876 877

  do {
878
    /* parse, if at all possible */
879
    if (ffmpegdec->pctx) {
880
      gint res;
881
      gint64 ffpts = AV_NOPTS_VALUE;
882

883 884 885
      if (GST_CLOCK_TIME_IS_VALID (in_ts))
	ffpts = in_ts / (GST_SECOND / AV_TIME_BASE);
    
886 887
      res = av_parser_parse (ffmpegdec->pctx, ffmpegdec->context,
          &data, &size, bdata, bsize,
888
          ffpts, ffpts);
889

890 891
      GST_DEBUG_OBJECT (ffmpegdec, "Parsed video frame, res=%d, size=%d",
          res, size);
892 893 894 895
      
      if (ffmpegdec->pctx->pts != AV_NOPTS_VALUE)
        in_ts = ffmpegdec->pctx->pts * (GST_SECOND / AV_TIME_BASE);

896
      if (res == 0 || size == 0)
897
        break;
898
      else {
899 900 901 902 903 904 905 906
        bsize -= res;
        bdata += res;
      }
    } else {
      data = bdata;
      size = bsize;
    }

907
    if ((len = gst_ffmpegdec_frame (ffmpegdec, data, size,
908
             &have_data, &in_ts)) < 0)
909 910
      break;

911
    if (!ffmpegdec->pctx) {
912 913 914
      bsize -= len;
      bdata += len;
    }
915 916 917 918

    if (!have_data) {
      break;
    }
919
  } while (bsize > 0);
920

921 922
  if ((ffmpegdec->pctx || oclass->in_plugin->id == CODEC_ID_MP3) &&
      bsize > 0) {
923
    GST_DEBUG_OBJECT (ffmpegdec, "Keeping %d bytes of data", bsize);
924

925 926 927
    ffmpegdec->pcache = gst_buffer_create_sub (inbuf,
        GST_BUFFER_SIZE (inbuf) - bsize, bsize);
  }
928 929 930
  gst_buffer_unref (inbuf);
}

931
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
932
gst_ffmpegdec_change_state (GstElement * element)
933
{
934 935 936 937 938
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
939
      gst_ffmpegdec_close (ffmpegdec);
940 941 942
      break;
  }

943 944
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
945

946
  return GST_STATE_SUCCESS;
947 948
}

949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
static void
gst_ffmpegdec_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;

  switch (prop_id) {
    case ARG_LOWRES:
      ffmpegdec->lowres = ffmpegdec->context->lowres =
          g_value_get_enum (value);
      break;
    case ARG_SKIPFRAME:
      ffmpegdec->hurry_up = ffmpegdec->context->hurry_up =
          g_value_get_enum (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_ffmpegdec_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;

  switch (prop_id) {
    case ARG_LOWRES:
      g_value_set_enum (value, ffmpegdec->context->lowres);
      break;
    case ARG_SKIPFRAME:
      g_value_set_enum (value, ffmpegdec->context->hurry_up);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

989
gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
990
gst_ffmpegdec_register (GstPlugin * plugin)
991 992
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
993 994
    sizeof (GstFFMpegDecClass),
    (GBaseInitFunc) gst_ffmpegdec_base_init,
995
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
996
    (GClassInitFunc) gst_ffmpegdec_class_init,
997 998
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
999
    sizeof (GstFFMpegDec),
1000
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1001
    (GInstanceInitFunc) gst_ffmpegdec_init,
1002 1003 1004
  };
  GType type;
  AVCodec *in_plugin;
1005
  gint rank;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1006

1007 1008 1009 1010 1011
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
1012
    GstFFMpegDecClassParams *params;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
1013 1014
    GstCaps *srccaps, *sinkcaps;
    gchar *type_name;
1015

Ronald S. Bultje's avatar
Ronald S. Bultje committed
1016 1017
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1018
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
1019
            in_plugin->id <= CODEC_ID_PCM_U8)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
1020
      goto next;
1021
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
1022 1023 1024

    /* only decoders */
    if (!in_plugin->decode) {
1025 1026
      goto next;
    }
1027

1028 1029 1030 1031
    /* name */
    if (!gst_ffmpeg_get_codecid_longname (in_plugin->id))
      goto next;

1032
    /* first make sure we've got a supported type */
1033
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, FALSE);
1034 1035 1036 1037 1038
    if (in_plugin->type == CODEC_TYPE_VIDEO) {
      srccaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv");
    } else {
      srccaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
    }
Benjamin Otte's avatar
Benjamin Otte committed
1039 1040 1041
    if (!sinkcaps || !srccaps) {
      if (sinkcaps) gst_caps_free (sinkcaps);
      if (srccaps) gst_caps_free (srccaps);
1042
      goto next;
Benjamin Otte's avatar
Benjamin Otte committed
1043
    }
1044

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

1048
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1049 1050
    if (g_type_from_name (type_name)) {
      g_free (type_name);
1051 1052 1053
      goto next;
    }

1054 1055 1056 1057
    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
1058 1059 1060
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);

1061
    /* create the gtype now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1062
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
1063 1064 1065

    /* (Ronald) MPEG-4 gets a higher priority because it has been well-
     * tested and by far outperforms divxdec/xviddec - so we prefer it.
1066 1067 1068 1069 1070
     * 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. */
1071 1072 1073 1074 1075 1076 1077 1078
    switch (in_plugin->id) {
      case CODEC_ID_MPEG4:
      case CODEC_ID_MSMPEG4V3:
        rank = GST_RANK_PRIMARY;
        break;
      default:
        rank = GST_RANK_MARGINAL;
        break;
1079 1080 1081 1082
      /* what's that? */
      case CODEC_ID_SP5X:
        rank = GST_RANK_NONE;
        break;
1083 1084
    }
    if (!gst_element_register (plugin, type_name, rank, type)) {
David Schleef's avatar
David Schleef committed
1085
      g_free (type_name);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
1086
      return FALSE;
David Schleef's avatar
David Schleef committed
1087 1088 1089
    }

    g_free (type_name);
1090

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1091 1092
    g_hash_table_insert (global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
1093

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
1094
  next:
1095 1096
    in_plugin = in_plugin->next;
  }
1097
  g_hash_table_remove (global_plugins, GINT_TO_POINTER (0));
1098 1099 1100

  return TRUE;
}