gstffmpegdec.c 14.3 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 21
#include "config.h"

22
#include <assert.h>
23
#include <string.h>
24

25 26 27 28 29
#ifdef HAVE_FFMPEG_UNINSTALLED
#include <avcodec.h>
#else
#include <ffmpeg/avcodec.h>
#endif
30 31 32

#include <gst/gst.h>

33
#include "gstffmpegcodecmap.h"
34

35 36 37 38 39 40 41 42 43 44
typedef struct _GstFFMpegDec GstFFMpegDec;

struct _GstFFMpegDec {
  GstElement element;

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

  AVCodecContext *context;
45
  AVFrame *picture;
46
  gboolean opened;
47 48 49 50 51 52 53 54
};

typedef struct _GstFFMpegDecClass GstFFMpegDecClass;

struct _GstFFMpegDecClass {
  GstElementClass parent_class;

  AVCodec *in_plugin;
55
  GstPadTemplate *srctempl, *sinktempl;
56 57
};

58 59
typedef struct {
  AVCodec *in_plugin;
60
  GstPadTemplate *srctempl, *sinktempl;
61
} GstFFMpegDecClassParams;
62

63 64 65 66 67 68 69 70 71 72
#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))
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

enum {
  /* FILL ME */
  LAST_SIGNAL
};

enum {
  ARG_0,
  /* FILL ME */
};

static GHashTable *global_plugins;

/* A number of functon prototypes are given so we can refer to them later. */
static void	gst_ffmpegdec_class_init	(GstFFMpegDecClass *klass);
static void	gst_ffmpegdec_init		(GstFFMpegDec *ffmpegdec);
89 90 91 92 93
static void	gst_ffmpegdec_dispose		(GObject      *object);

static GstPadLinkReturn	gst_ffmpegdec_connect	(GstPad    *pad,
						 GstCaps   *caps);
static void	gst_ffmpegdec_chain		(GstPad    *pad,
94
						 GstData   *data);
95

96 97
static GstElementStateReturn
		gst_ffmpegdec_change_state	(GstElement *element);
98

99
#if 0
100 101 102 103 104
/* some sort of bufferpool handling, but different */
static int	gst_ffmpegdec_get_buffer	(AVCodecContext *context,
						 AVFrame        *picture);
static void	gst_ffmpegdec_release_buffer	(AVCodecContext *context,
						 AVFrame        *picture);
105
#endif
106 107 108

static GstElementClass *parent_class = NULL;

109
/*static guint gst_ffmpegdec_signals[LAST_SIGNAL] = { 0 }; */
110 111 112 113 114 115

static void
gst_ffmpegdec_class_init (GstFFMpegDecClass *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
116
  GstFFMpegDecClassParams *params;
117 118 119 120 121 122

  gobject_class = (GObjectClass*)klass;
  gstelement_class = (GstElementClass*)klass;

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);

123
  params = g_hash_table_lookup (global_plugins,
124 125
		  GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));

126
  klass->in_plugin = params->in_plugin;
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
  klass->srctempl = params->srctempl;
  klass->sinktempl = params->sinktempl;

  gobject_class->dispose = gst_ffmpegdec_dispose;
  gstelement_class->change_state = gst_ffmpegdec_change_state;
}

static void
gst_ffmpegdec_init (GstFFMpegDec *ffmpegdec)
{
  GstFFMpegDecClass *oclass = (GstFFMpegDecClass*)(G_OBJECT_GET_CLASS (ffmpegdec));

  /* 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");

  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->sinkpad);
  gst_element_add_pad (GST_ELEMENT (ffmpegdec), ffmpegdec->srcpad);

  /* some ffmpeg data */
  ffmpegdec->context = avcodec_alloc_context();
  ffmpegdec->picture = avcodec_alloc_frame();

  ffmpegdec->opened = FALSE;
}

static void
gst_ffmpegdec_dispose (GObject *object)
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) object;
  /* close old session */
  if (ffmpegdec->opened) {
    avcodec_close (ffmpegdec->context);
    ffmpegdec->opened = FALSE;
  }
164

165 166 167
  /* clean up remaining allocated data */
  av_free (ffmpegdec->context);
  av_free (ffmpegdec->picture);
168 169
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
170
static GstPadLinkReturn
171 172
gst_ffmpegdec_connect (GstPad  *pad,
		       GstCaps *caps)
173 174 175 176
{
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *)(gst_pad_get_parent (pad));
  GstFFMpegDecClass *oclass = (GstFFMpegDecClass*)(G_OBJECT_GET_CLASS (ffmpegdec));

177
  /* we want fixed caps */
178
  if (!GST_CAPS_IS_FIXED (caps))
179
    return GST_PAD_LINK_DELAYED;
180

181 182 183 184 185
  /* close old session */
  if (ffmpegdec->opened) {
    avcodec_close (ffmpegdec->context);
    ffmpegdec->opened = FALSE;
  }
186

187 188
  /* set defaults */
  avcodec_get_context_defaults (ffmpegdec->context);
189

190
#if 0
191 192 193
  /* set buffer functions */
  ffmpegdec->context->get_buffer = gst_ffmpegdec_get_buffer;
  ffmpegdec->context->release_buffer = gst_ffmpegdec_release_buffer;
194
#endif
195

196 197 198
  /* get size and so */
  gst_ffmpeg_caps_to_codectype (oclass->in_plugin->type,
				caps, ffmpegdec->context);
199

200 201 202 203
  /* we dont send complete frames */
  if (oclass->in_plugin->capabilities & CODEC_CAP_TRUNCATED)
    ffmpegdec->context->flags |= CODEC_FLAG_TRUNCATED;

204 205 206
  /* do *not* draw edges */
  ffmpegdec->context->flags |= CODEC_FLAG_EMU_EDGE;

207 208 209
  /* open codec - we don't select an output pix_fmt yet,
   * simply because we don't know! We only get it
   * during playback... */
210
  if (avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) {
211
    GST_DEBUG (
212 213
		"ffdec_%s: Failed to open FFMPEG codec",
		oclass->in_plugin->name);
214
    return GST_PAD_LINK_REFUSED;
215
  }
216 217 218 219

  /* done! */
  ffmpegdec->opened = TRUE;

220
  return GST_PAD_LINK_OK;
221 222
}

223
#if 0
224 225 226 227 228 229 230 231 232
static int
gst_ffmpegdec_get_buffer (AVCodecContext *context,
			  AVFrame        *picture)
{
  GstBuffer *buf = NULL;
  gulong bufsize = 0;

  switch (context->codec_type) {
    case CODEC_TYPE_VIDEO:
233
      bufsize = avpicture_get_size (context->pix_fmt,
234 235 236 237 238 239
				    context->width,
				    context->height);
      buf = gst_buffer_new_and_alloc (bufsize);
      avpicture_fill ((AVPicture *) picture, GST_BUFFER_DATA (buf),
		      context->pix_fmt,
		      context->width, context->height);
240 241 242 243 244 245
      break;

    case CODEC_TYPE_AUDIO:
    default:
      g_assert (0);
      break;
246 247
  }

248 249 250 251 252 253 254 255 256 257 258 259 260
  /* 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;
}
261

262 263 264 265 266 267 268 269 270 271 272 273 274
static void
gst_ffmpegdec_release_buffer (AVCodecContext *context,
			      AVFrame        *picture)
{
  gint i;
  GstBuffer *buf = GST_BUFFER (picture->base[0]);
  gst_buffer_unref (buf);

  /* zero out the reference in ffmpeg */
  for (i=0;i<4;i++) {
    picture->data[i] = NULL;
    picture->linesize[i] = 0;
  }
275
}
276
#endif
277 278

static void
279
gst_ffmpegdec_chain (GstPad    *pad,
280
		     GstData *_data)
281
{
282
  GstBuffer *inbuf = GST_BUFFER (_data);
283
  GstBuffer *outbuf = NULL;
284
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *)(gst_pad_get_parent (pad));
285
  GstFFMpegDecClass *oclass = (GstFFMpegDecClass*)(G_OBJECT_GET_CLASS (ffmpegdec));
286
  guchar *data;
287 288 289
  gint size, len = 0;
  gint have_data;

Ronald S. Bultje's avatar
Ronald S. Bultje committed
290
  if (!ffmpegdec->opened) {
291 292 293
    gst_element_error (GST_ELEMENT (ffmpegdec),
		       "ffdec_%s: input format was not set before data-start",
		       oclass->in_plugin->name);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
294 295 296
    return;
  }

297 298 299 300
  /* FIXME: implement event awareness (especially EOS
   * (av_close_codec ()) and FLUSH/DISCONT
   * (avcodec_flush_buffers ()))
   */
301 302 303 304 305 306 307

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

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

308 309 310 311 312 313
    switch (oclass->in_plugin->type) {
      case CODEC_TYPE_VIDEO:
        len = avcodec_decode_video (ffmpegdec->context,
				    ffmpegdec->picture,
				    &have_data,
				    data, size);
314
        if (have_data) {
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
          /* 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 size = avpicture_get_size (ffmpegdec->context->pix_fmt,
					  ffmpegdec->context->width,
					  ffmpegdec->context->height);
          outbuf = gst_buffer_new_and_alloc (size);
          avpicture_fill (&pic, GST_BUFFER_DATA (outbuf),
			  ffmpegdec->context->pix_fmt,
			  ffmpegdec->context->width,
			  ffmpegdec->context->height);
          img_convert (&pic, ffmpegdec->context->pix_fmt,
		       (AVPicture *) ffmpegdec->picture,
		       ffmpegdec->context->pix_fmt,
		       ffmpegdec->context->width,
		       ffmpegdec->context->height);

333 334 335
          /* this isn't necessarily true, but it's better than nothing */
          GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
        }
336
        break;
337

338
      case CODEC_TYPE_AUDIO:
339
        outbuf = gst_buffer_new_and_alloc (AVCODEC_MAX_AUDIO_FRAME_SIZE);
340
        len = avcodec_decode_audio (ffmpegdec->context,
341
				    (int16_t *) GST_BUFFER_DATA (outbuf),
342 343
				    &have_data,
				    data, size);
344 345 346 347 348 349 350 351
        if (have_data) {
          GST_BUFFER_SIZE (outbuf) = have_data;
          GST_BUFFER_DURATION (outbuf) = (have_data * GST_SECOND) /
					   (ffmpegdec->context->channels *
					    ffmpegdec->context->sample_rate);
        } else {
          gst_buffer_unref (outbuf);
        } 
352 353 354 355 356
        break;
      default:
	g_assert(0);
        break;
    }
357 358

    if (len < 0) {
359 360
      g_warning ("ffdec_%s: decoding error",
		 oclass->in_plugin->name);
361 362 363
      break;
    }

364
    if (have_data) {
365
      if (!GST_PAD_CAPS (ffmpegdec->srcpad)) {
366 367 368 369 370
        GstCaps *caps;
        caps = gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type,
					     ffmpegdec->context);
        if (caps == NULL ||
            gst_pad_try_set_caps (ffmpegdec->srcpad, caps) <= 0) {
371 372 373
          gst_element_error (GST_ELEMENT (ffmpegdec),
			     "Failed to link ffmpeg decoder (%s) to next element",
			     oclass->in_plugin->name);
374 375
          return;
        }
376 377 378
      }

      GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
379

380
      gst_pad_push (ffmpegdec->srcpad, GST_DATA (outbuf));
381 382 383 384
    } 

    size -= len;
    data += len;
385
  } while (size > 0);
386 387 388 389

  gst_buffer_unref (inbuf);
}

390 391
static GstElementStateReturn
gst_ffmpegdec_change_state (GstElement *element)
392
{
393 394 395 396 397 398 399 400 401
  GstFFMpegDec *ffmpegdec = (GstFFMpegDec *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
      if (ffmpegdec->opened) {
        avcodec_close (ffmpegdec->context);
        ffmpegdec->opened = FALSE;
      }
402 403 404
      break;
  }

405 406
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
407

408
  return GST_STATE_SUCCESS;
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
}

gboolean
gst_ffmpegdec_register (GstPlugin *plugin)
{
  GstElementFactory *factory;
  GTypeInfo typeinfo = {
    sizeof(GstFFMpegDecClass),      
    NULL,
    NULL,
    (GClassInitFunc)gst_ffmpegdec_class_init,
    NULL,
    NULL,
    sizeof(GstFFMpegDec),
    0,
    (GInstanceInitFunc)gst_ffmpegdec_init,
  };
  GType type;
  GstElementDetails *details;
  AVCodec *in_plugin;
  
  in_plugin = first_avcodec;

  global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
    gchar *type_name;
436 437
    GstPadTemplate *sinktempl, *srctempl;
    GstCaps *sinkcaps, *srccaps;
438
    GstFFMpegDecClassParams *params;
439

Ronald S. Bultje's avatar
Ronald S. Bultje committed
440 441 442
    /* no quasi-codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
	(in_plugin->id >= CODEC_ID_PCM_S16LE &&
Ronald S. Bultje's avatar
Ronald S. Bultje committed
443
	 in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
444
      goto next;
445
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
446 447 448

    /* only decoders */
    if (!in_plugin->decode) {
449 450
      goto next;
    }
451 452 453 454 455 456 457

    /* first make sure we've got a supported type */
    sinkcaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL);
    srccaps  = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
    if (!sinkcaps || !srccaps)
      goto next;

458
    /* construct the type */
Ronald S. Bultje's avatar
Ronald S. Bultje committed
459
    type_name = g_strdup_printf("ffdec_%s", in_plugin->name);
460

461
    /* if it's already registered, drop it */
462 463 464 465 466
    if (g_type_from_name(type_name)) {
      g_free(type_name);
      goto next;
    }

467
    /* create the gtk type now */
468 469
    type = g_type_register_static(GST_TYPE_ELEMENT, type_name , &typeinfo, 0);

470
    /* construct the element details struct */
471
    details = g_new0 (GstElementDetails, 1);
472
    details->longname = g_strdup_printf("FFMPEG %s decoder", in_plugin->name);
473
    details->klass = g_strdup_printf("Codec/%s/Decoder",
474
				     (in_plugin->type == CODEC_TYPE_VIDEO) ?
475
				     "Video" : "Audio");
476
    details->license = g_strdup("LGPL");
477 478
    details->description = g_strdup_printf("FFMPEG %s decoder",
					   in_plugin->name);
479
    details->version = g_strdup(VERSION);
480 481
    details->author = g_strdup("The FFMPEG crew, "
				"Wim Taymans <wim.taymans@chello.be>, "
482 483
				"Ronald Bultje <rbultje@ronald.bitfreak.net>");
    details->copyright = g_strdup("(c) 2001-2003");
484

485
    /* register the plugin with gstreamer */
486
    factory = gst_element_factory_new(type_name,type,details);
487 488
    g_return_val_if_fail(factory != NULL, FALSE);

489
    gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_MARGINAL);
490

491 492
    sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
				      GST_PAD_ALWAYS, sinkcaps, NULL);
493 494
    gst_element_factory_add_pad_template (factory, sinktempl);

495 496 497 498
    srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
				     GST_PAD_ALWAYS, srccaps, NULL);
    gst_element_factory_add_pad_template (factory, srctempl);

499
    params = g_new0 (GstFFMpegDecClassParams, 1);
500
    params->in_plugin = in_plugin;
501 502
    params->sinktempl = sinktempl;
    params->srctempl = srctempl;
503 504 505 506

    g_hash_table_insert (global_plugins, 
		         GINT_TO_POINTER (type), 
			 (gpointer) params);
507 508 509 510 511 512 513 514 515 516

    /* The very last thing is to register the elementfactory with the plugin. */
    gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));

next:
    in_plugin = in_plugin->next;
  }

  return TRUE;
}