gstffmpegenc.c 24 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 25 26

#include <assert.h>
#include <string.h>

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 37
#include "gstffmpegcodecmap.h"

38 39
typedef struct _GstFFMpegEnc GstFFMpegEnc;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
40 41
struct _GstFFMpegEnc
{
42 43 44 45 46 47 48
  GstElement element;

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

  AVCodecContext *context;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
49 50
  AVFrame *picture;
  gboolean opened;
51
  GstBuffer *cache;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
52 53 54 55 56 57

  /* cache */
  gulong bitrate;
  gint me_method;
  gint gop_size;
  gulong buffer_size;
58 59 60 61
};

typedef struct _GstFFMpegEncClass GstFFMpegEncClass;

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

  AVCodec *in_plugin;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
67
  GstPadTemplate *srctempl, *sinktempl;
68
  GstCaps *sinkcaps;
69 70
};

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

77 78 79 80 81 82 83 84 85 86
#define GST_TYPE_FFMPEGENC \
  (gst_ffmpegenc_get_type())
#define GST_FFMPEGENC(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGENC,GstFFMpegEnc))
#define GST_FFMPEGENC_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGENC,GstFFMpegEncClass))
#define GST_IS_FFMPEGENC(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGENC))
#define GST_IS_FFMPEGENC_CLASS(obj) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGENC))
87

88 89
#define VIDEO_BUFFER_SIZE (1024*1024)

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

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
96 97
enum
{
98 99 100 101
  ARG_0,
  ARG_BIT_RATE,
  ARG_GOP_SIZE,
  ARG_ME_METHOD,
Ronald S. Bultje's avatar
Ronald S. Bultje committed
102
  ARG_BUFSIZE
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
103
      /* FILL ME */
104 105 106 107 108 109 110 111
};

#define GST_TYPE_ME_METHOD (gst_ffmpegenc_me_method_get_type())
static GType
gst_ffmpegenc_me_method_get_type (void)
{
  static GType ffmpegenc_me_method_type = 0;
  static GEnumValue ffmpegenc_me_methods[] = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
112 113 114 115 116 117 118
    {ME_ZERO, "0", "zero"},
    {ME_FULL, "1", "full"},
    {ME_LOG, "2", "logarithmic"},
    {ME_PHODS, "3", "phods"},
    {ME_EPZS, "4", "epzs"},
    {ME_X1, "5", "x1"},
    {0, NULL, NULL},
119 120
  };
  if (!ffmpegenc_me_method_type) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
121 122
    ffmpegenc_me_method_type =
        g_enum_register_static ("GstFFMpegEncMeMethod", ffmpegenc_me_methods);
123 124 125 126 127 128 129
  }
  return ffmpegenc_me_method_type;
}

static GHashTable *enc_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
130 131 132 133
static void gst_ffmpegenc_class_init (GstFFMpegEncClass * klass);
static void gst_ffmpegenc_base_init (GstFFMpegEncClass * klass);
static void gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc);
static void gst_ffmpegenc_dispose (GObject * object);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
134

135
static gboolean gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps);
136
static GstCaps * gst_ffmpegenc_getcaps (GstPad * pad);
137 138
static GstFlowReturn gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer *buffer);
static GstFlowReturn gst_ffmpegenc_chain_audio (GstPad * pad, GstBuffer *buffer);
139

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
140 141 142 143 144
static void gst_ffmpegenc_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_ffmpegenc_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec);

145 146
static GstStateChangeReturn gst_ffmpegenc_change_state (GstElement * element,
    GstStateChange transition);
147 148 149

static GstElementClass *parent_class = NULL;

150
/*static guint gst_ffmpegenc_signals[LAST_SIGNAL] = { 0 }; */
151

Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
152
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
153
gst_ffmpegenc_base_init (GstFFMpegEncClass * klass)
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
154 155 156 157
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
  GstFFMpegEncClassParams *params;
Benjamin Otte's avatar
Benjamin Otte committed
158
  GstElementDetails details;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
159 160 161
  GstPadTemplate *srctempl, *sinktempl;

  params = g_hash_table_lookup (enc_global_plugins,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
162
      GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
163 164
  /* HACK: if we don't have a GType yet, our params are stored at position 0 */
  if (!params) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
165
    params = g_hash_table_lookup (enc_global_plugins, GINT_TO_POINTER (0));
166 167
  }
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
168 169

  /* construct the element details struct */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
170
  details.longname = g_strdup_printf ("FFMPEG %s encoder",
171
      gst_ffmpeg_get_codecid_longname (params->in_plugin->id));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
172
  details.klass = g_strdup_printf ("Codec/Encoder/%s",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
173 174 175
      (params->in_plugin->type == CODEC_TYPE_VIDEO) ? "Video" : "Audio");
  details.description = g_strdup_printf ("FFMPEG %s encoder",
      params->in_plugin->name);
Benjamin Otte's avatar
Benjamin Otte committed
176
  details.author = "Wim Taymans <wim.taymans@chello.be>, "
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
177
      "Ronald Bultje <rbultje@ronald.bitfreak.net>";
Benjamin Otte's avatar
Benjamin Otte committed
178 179 180 181
  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
182 183 184

  /* pad templates */
  sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
185
      GST_PAD_ALWAYS, params->sinkcaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
186
  srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
187
      GST_PAD_ALWAYS, params->srccaps);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
188 189 190 191 192 193 194

  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;
195
  klass->sinkcaps = NULL;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
196 197
}

198
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
199
gst_ffmpegenc_class_init (GstFFMpegEncClass * klass)
200 201 202 203
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
204 205
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
206

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
207
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
208

209 210 211
  gobject_class->set_property = gst_ffmpegenc_set_property;
  gobject_class->get_property = gst_ffmpegenc_get_property;

212
  if (klass->in_plugin->type == CODEC_TYPE_VIDEO) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE,
        g_param_spec_ulong ("bitrate", "Bit Rate",
            "Target Video Bitrate", 0, G_MAXULONG, 300000, G_PARAM_READWRITE));
    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GOP_SIZE,
        g_param_spec_int ("gop_size", "GOP Size",
            "Number of frames within one GOP",
            0, G_MAXINT, 15, G_PARAM_READWRITE));
    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ME_METHOD,
        g_param_spec_enum ("me_method", "ME Method",
            "Motion Estimation Method",
            GST_TYPE_ME_METHOD, ME_LOG, G_PARAM_READWRITE));
    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFSIZE,
        g_param_spec_ulong ("buffer_size", "Buffer Size",
            "Size of the video buffers", 0, G_MAXULONG, 0, G_PARAM_READWRITE));
  } else if (klass->in_plugin->type == CODEC_TYPE_AUDIO) {
    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BIT_RATE,
        g_param_spec_ulong ("bitrate", "Bit Rate",
            "Target Audio Bitrate", 0, G_MAXULONG, 128000, G_PARAM_READWRITE));
231 232
  }

Ronald S. Bultje's avatar
Ronald S. Bultje committed
233
  gstelement_class->change_state = gst_ffmpegenc_change_state;
234

Ronald S. Bultje's avatar
Ronald S. Bultje committed
235
  gobject_class->dispose = gst_ffmpegenc_dispose;
236 237 238
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
239
gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc)
240
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
241 242
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
243

Ronald S. Bultje's avatar
Ronald S. Bultje committed
244 245
  /* setup pads */
  ffmpegenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
246
  gst_pad_set_setcaps_function (ffmpegenc->sinkpad, gst_ffmpegenc_setcaps);
247
  gst_pad_set_getcaps_function (ffmpegenc->sinkpad, gst_ffmpegenc_getcaps);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
248
  ffmpegenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
249
  gst_pad_use_fixed_caps (ffmpegenc->srcpad);
250

Ronald S. Bultje's avatar
Ronald S. Bultje committed
251
  /* ffmpeg objects */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
252 253
  ffmpegenc->context = avcodec_alloc_context ();
  ffmpegenc->picture = avcodec_alloc_frame ();
Ronald S. Bultje's avatar
Ronald S. Bultje committed
254 255 256
  ffmpegenc->opened = FALSE;

  if (oclass->in_plugin->type == CODEC_TYPE_VIDEO) {
257 258
    gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_video);

Ronald S. Bultje's avatar
Ronald S. Bultje committed
259 260 261 262
    ffmpegenc->bitrate = 300000;
    ffmpegenc->buffer_size = 512 * 1024;
    ffmpegenc->gop_size = 15;
  } else if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) {
263 264
    gst_pad_set_chain_function (ffmpegenc->sinkpad, gst_ffmpegenc_chain_audio);

Ronald S. Bultje's avatar
Ronald S. Bultje committed
265 266
    ffmpegenc->bitrate = 128000;
  }
267 268 269

  gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->sinkpad);
  gst_element_add_pad (GST_ELEMENT (ffmpegenc), ffmpegenc->srcpad);
270 271 272
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
273
gst_ffmpegenc_dispose (GObject * object)
274
{
Ronald S. Bultje's avatar
Ronald S. Bultje committed
275
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) object;
276

Ronald S. Bultje's avatar
Ronald S. Bultje committed
277 278 279 280 281
  /* close old session */
  if (ffmpegenc->opened) {
    avcodec_close (ffmpegenc->context);
    ffmpegenc->opened = FALSE;
  }
282

Ronald S. Bultje's avatar
Ronald S. Bultje committed
283 284 285
  /* clean up remaining allocated data */
  av_free (ffmpegenc->context);
  av_free (ffmpegenc->picture);
286 287

  G_OBJECT_CLASS (parent_class)->dispose (object);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
288
}
289

290 291 292
static GstCaps *
gst_ffmpegenc_getcaps (GstPad * pad)
{
293
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) GST_PAD_PARENT (pad);
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
  AVCodecContext *ctx;
  enum PixelFormat pixfmt;
  GstCaps *caps = NULL;

  /* audio needs no special care */
  if (oclass->in_plugin->type == CODEC_TYPE_AUDIO) {
    return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
  }

  /* cached */
  if (oclass->sinkcaps) {
    return gst_caps_copy (oclass->sinkcaps);
  }

  /* create cache etc. */
  ctx = avcodec_alloc_context ();
  if (!ctx) {
    return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
  }

  /* set some default properties */
  ctx->width = 384;
  ctx->height = 288;
319 320
  ctx->time_base.num = DEFAULT_FRAME_RATE_BASE;
  ctx->time_base.den = 25 * DEFAULT_FRAME_RATE_BASE;
321 322 323 324
  ctx->bit_rate = 350 * 1000;
  /* makes it silent */
  ctx->strict_std_compliance = -1;

325 326 327 328 329 330 331
   /* shut up the logging while we autoprobe; we don't want warnings and
    * errors about unsupported formats */
   /* FIXME: if someone cares about this disabling the logging for other
    * instances/threads/..., one could investigate if there is a way to
    * set this as a struct member on the av context, and check it from the
    * log handler */
  _shut_up_I_am_probing = TRUE;
332 333 334 335 336 337 338 339 340
  for (pixfmt = 0; pixfmt < PIX_FMT_NB; pixfmt++) {
    ctx->pix_fmt = pixfmt;
    if (avcodec_open (ctx, oclass->in_plugin) >= 0 &&
        ctx->pix_fmt == pixfmt) {
      ctx->width = -1;
      if (!caps)
        caps = gst_caps_new_empty ();
      gst_caps_append (caps,
          gst_ffmpeg_codectype_to_caps (oclass->in_plugin->type, ctx));
341
      avcodec_close (ctx);
342
    }
343 344
    if (ctx->priv_data)
      avcodec_close (ctx);
345 346
  }
  av_free (ctx);
347
  _shut_up_I_am_probing = FALSE;
348 349 350 351 352 353 354 355 356 357 358

  /* make sure we have something */
  if (!caps) {
    return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
  }

  oclass->sinkcaps = gst_caps_copy (caps);

  return caps;
}

359 360
static gboolean
gst_ffmpegenc_setcaps (GstPad * pad, GstCaps * caps)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
361
{
David Schleef's avatar
David Schleef committed
362
  GstCaps *other_caps;
363 364
  GstCaps *allowed_caps;
  GstCaps *icaps;
David Schleef's avatar
David Schleef committed
365
  enum PixelFormat pix_fmt;
366
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) GST_PAD_PARENT (pad);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
367 368
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
369

Ronald S. Bultje's avatar
Ronald S. Bultje committed
370 371 372 373 374
  /* close old session */
  if (ffmpegenc->opened) {
    avcodec_close (ffmpegenc->context);
    ffmpegenc->opened = FALSE;
  }
375

Ronald S. Bultje's avatar
Ronald S. Bultje committed
376 377 378
  /* set defaults */
  avcodec_get_context_defaults (ffmpegenc->context);

379 380 381
  /* if we set it in _getcaps we should set it also in _link */
  ffmpegenc->context->strict_std_compliance = -1;

Ronald S. Bultje's avatar
Ronald S. Bultje committed
382 383 384 385 386
  /* user defined properties */
  ffmpegenc->context->bit_rate = ffmpegenc->bitrate;
  ffmpegenc->context->bit_rate_tolerance = ffmpegenc->bitrate;
  ffmpegenc->context->gop_size = ffmpegenc->gop_size;
  ffmpegenc->context->me_method = ffmpegenc->me_method;
387

Ronald S. Bultje's avatar
Ronald S. Bultje committed
388
  /* general properties */
389 390 391
  ffmpegenc->context->qmin = 1;
  ffmpegenc->context->qmax = 31;
  ffmpegenc->context->max_qdiff = 15;
392

David Schleef's avatar
David Schleef committed
393
  /* fetch pix_fmt and so on */
394
  gst_ffmpeg_caps_with_codectype (oclass->in_plugin->type,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
395
      caps, ffmpegenc->context);
396 397 398 399
  if (!ffmpegenc->context->time_base.den) {
    ffmpegenc->context->time_base.den = 25;
    ffmpegenc->context->time_base.num = 1;
  }
400

David Schleef's avatar
David Schleef committed
401
  pix_fmt = ffmpegenc->context->pix_fmt;
402

David Schleef's avatar
David Schleef committed
403 404
  /* open codec */
  if (avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) {
405 406
    if (ffmpegenc->context->priv_data)
      avcodec_close (ffmpegenc->context);
David Schleef's avatar
David Schleef committed
407
    GST_DEBUG ("ffenc_%s: Failed to open FFMPEG codec",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
408
        oclass->in_plugin->name);
409
    return FALSE;
410 411
  }

David Schleef's avatar
David Schleef committed
412 413 414 415
  /* is the colourspace correct? */
  if (pix_fmt != ffmpegenc->context->pix_fmt) {
    avcodec_close (ffmpegenc->context);
    GST_DEBUG ("ffenc_%s: AV wants different colourspace (%d given, %d wanted)",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
416
        oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt);
417
    return FALSE;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
418
  }
419

420
  /* some codecs support more than one format, first auto-choose one */
421
  GST_DEBUG_OBJECT (ffmpegenc, "picking an output format ...");
422
  allowed_caps = gst_pad_get_allowed_caps (ffmpegenc->srcpad);
423
  if (!allowed_caps) {
424 425 426 427 428
    GST_DEBUG_OBJECT (ffmpegenc, "... but no peer, using template caps");
    /* we need to copy because get_allowed_caps returns a ref, and
     * get_pad_template_caps doesn't */
    allowed_caps = gst_caps_copy (
        gst_pad_get_pad_template_caps (ffmpegenc->srcpad));
429
  }
430
  GST_DEBUG_OBJECT (ffmpegenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
431 432 433
  gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
      oclass->in_plugin->type, allowed_caps, ffmpegenc->context);

Ronald S. Bultje's avatar
Ronald S. Bultje committed
434
  /* try to set this caps on the other side */
David Schleef's avatar
David Schleef committed
435
  other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
436
      ffmpegenc->context, TRUE);
437

David Schleef's avatar
David Schleef committed
438
  if (!other_caps) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
439
    avcodec_close (ffmpegenc->context);
440
    GST_DEBUG ("Unsupported codec - no caps found");
441
    return FALSE;
442
  }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
443

444
  icaps = gst_caps_intersect (allowed_caps, other_caps);
445 446
  gst_caps_unref (allowed_caps);
  gst_caps_unref (other_caps);
447
  if (gst_caps_is_empty (icaps)) {
448
    gst_caps_unref (icaps);
449
    return FALSE;
450 451 452 453 454
  }

  if (gst_caps_get_size (icaps) > 1) {
    GstCaps *newcaps;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
455 456
    newcaps =
        gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
457
            0)), NULL);
458
    gst_caps_unref (icaps);
459 460 461
    icaps = newcaps;
  }

462
  if (!gst_pad_set_caps (ffmpegenc->srcpad, icaps)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
463
    avcodec_close (ffmpegenc->context);
464
    gst_caps_unref (icaps);
465
    return FALSE;
466
  }
467
  gst_caps_unref (icaps);
468

Ronald S. Bultje's avatar
Ronald S. Bultje committed
469 470 471
  /* success! */
  ffmpegenc->opened = TRUE;

472
  return TRUE;
473 474
}

475 476
static GstFlowReturn
gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer * inbuf)
477
{
478 479
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (GST_PAD_PARENT (pad));
  GstBuffer *outbuf;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
480 481
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
482
  gint ret_size = 0, frame_size;
483

484 485 486 487
  GST_DEBUG_OBJECT (ffmpegenc,
      "Received buffer of time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)));

488
  frame_size = gst_ffmpeg_avpicture_fill ((AVPicture *) ffmpegenc->picture,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
489 490 491
      GST_BUFFER_DATA (inbuf),
      ffmpegenc->context->pix_fmt,
      ffmpegenc->context->width, ffmpegenc->context->height);
492
  g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (inbuf), GST_FLOW_ERROR);
493

494
  ffmpegenc->picture->pts =
495 496
      gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (inbuf),
          ffmpegenc->context->time_base);
497

498
  outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
499
  ret_size = avcodec_encode_video (ffmpegenc->context,
500
      GST_BUFFER_DATA (outbuf),
501
      GST_BUFFER_SIZE (outbuf), ffmpegenc->picture);
502

503
  if (ret_size < 0) {
504 505
    GST_ERROR_OBJECT (ffmpegenc,
        "ffenc_%s: failed to encode buffer", oclass->in_plugin->name);
506
    gst_buffer_unref (inbuf);
507
    gst_buffer_unref (outbuf);
508
    return GST_FLOW_OK;
509 510
  }

511 512 513
  GST_BUFFER_SIZE (outbuf) = ret_size;
  GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
  GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
514 515 516
  if (!ffmpegenc->context->coded_frame->key_frame)
    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
  gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegenc->srcpad));
517

518 519
  gst_buffer_unref (inbuf);

520
  return gst_pad_push (ffmpegenc->srcpad, outbuf);
521 522
}

523 524
static GstFlowReturn
gst_ffmpegenc_chain_audio (GstPad * pad, GstBuffer * inbuf)
525 526
{
  GstBuffer *outbuf = NULL, *subbuf;
527
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (GST_OBJECT_PARENT (pad));
528
  gint size, ret_size = 0, in_size, frame_size;
529
  GstFlowReturn ret;
530 531 532 533 534 535

  size = GST_BUFFER_SIZE (inbuf);

  /* FIXME: events (discont (flush!) and eos (close down) etc.) */

  frame_size = ffmpegenc->context->frame_size * 2 *
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
536
      ffmpegenc->context->channels;
537 538 539 540
  in_size = size;
  if (ffmpegenc->cache)
    in_size += GST_BUFFER_SIZE (ffmpegenc->cache);

541 542 543 544
  GST_DEBUG_OBJECT (ffmpegenc,
      "Received buffer of time %" GST_TIME_FORMAT " and size %d (cache: %d)",
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)), size, in_size - size);

545 546 547
  while (1) {
    /* do we have enough data for one frame? */
    if (in_size / (2 * ffmpegenc->context->channels) <
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
548
        ffmpegenc->context->frame_size) {
549 550 551
      if (in_size > size) {
        /* this is panic! we got a buffer, but still don't have enough
         * data. Merge them and retry in the next cycle... */
552 553
        ffmpegenc->cache = gst_buffer_span (ffmpegenc->cache, 0, inbuf,
		GST_BUFFER_SIZE (ffmpegenc->cache) + GST_BUFFER_SIZE (inbuf));
554 555 556 557 558
      } else if (in_size == size) {
        /* exactly the same! how wonderful */
        ffmpegenc->cache = inbuf;
      } else if (in_size > 0) {
        ffmpegenc->cache = gst_buffer_create_sub (inbuf, size - in_size,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
559
            in_size);
560
        GST_BUFFER_DURATION (ffmpegenc->cache) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
561 562
            GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (ffmpegenc->cache) /
            size;
563
        GST_BUFFER_TIMESTAMP (ffmpegenc->cache) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
564 565
            GST_BUFFER_TIMESTAMP (inbuf) +
            (GST_BUFFER_DURATION (inbuf) * (size - in_size) / size);
566 567 568 569
        gst_buffer_unref (inbuf);
      } else {
        gst_buffer_unref (inbuf);
      }
570
      return GST_FLOW_OK;
571 572 573 574 575 576 577
    }

    /* create the frame */
    if (in_size > size) {
      /* merge */
      subbuf = gst_buffer_create_sub (inbuf, 0, frame_size - (in_size - size));
      GST_BUFFER_DURATION (subbuf) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
578
          GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
579 580
      subbuf = gst_buffer_span (ffmpegenc->cache, 0, subbuf,
	      GST_BUFFER_SIZE (ffmpegenc->cache) + GST_BUFFER_SIZE (subbuf));
581 582 583 584
      ffmpegenc->cache = NULL;
    } else {
      subbuf = gst_buffer_create_sub (inbuf, size - in_size, frame_size);
      GST_BUFFER_DURATION (subbuf) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
585
          GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
586
      GST_BUFFER_TIMESTAMP (subbuf) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
587 588
          GST_BUFFER_TIMESTAMP (inbuf) + (GST_BUFFER_DURATION (inbuf) *
          (size - in_size) / size);
589 590 591 592
    }

    outbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (inbuf));
    ret_size = avcodec_encode_audio (ffmpegenc->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
593
        GST_BUFFER_DATA (outbuf),
594
        GST_BUFFER_SIZE (outbuf), (const short int *)
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
595
        GST_BUFFER_DATA (subbuf));
596 597

    if (ret_size < 0) {
598
      GST_ERROR_OBJECT (ffmpegenc, "Failed to encode buffer");
599
      gst_buffer_unref (inbuf);
600
      gst_buffer_unref (outbuf);
601
      gst_buffer_unref (subbuf);
602
      return GST_FLOW_OK;
603 604 605 606 607
    }

    GST_BUFFER_SIZE (outbuf) = ret_size;
    GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (subbuf);
    GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (subbuf);
608 609 610
    gst_buffer_unref (subbuf);

    ret = gst_pad_push (ffmpegenc->srcpad, outbuf);
611 612 613

    in_size -= frame_size;
  }
614 615

  return ret;
616 617
}

618
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
619 620
gst_ffmpegenc_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
621 622 623 624
{
  GstFFMpegEnc *ffmpegenc;

  /* Get a pointer of the right type. */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
625
  ffmpegenc = (GstFFMpegEnc *) (object);
626 627 628 629

  /* Check the argument id to see which argument we're setting. */
  switch (prop_id) {
    case ARG_BIT_RATE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
630
      ffmpegenc->bitrate = g_value_get_ulong (value);
631 632
      break;
    case ARG_GOP_SIZE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
633
      ffmpegenc->gop_size = g_value_get_int (value);
634 635
      break;
    case ARG_ME_METHOD:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
636 637 638
      ffmpegenc->me_method = g_value_get_enum (value);
      break;
    case ARG_BUFSIZE:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
639
      ffmpegenc->buffer_size = g_value_get_ulong (value);
640 641 642 643 644 645 646 647 648
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

/* The set function is simply the inverse of the get fuction. */
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
649 650
gst_ffmpegenc_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
651 652 653 654
{
  GstFFMpegEnc *ffmpegenc;

  /* It's not null if we got it, but it might not be ours */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
655
  ffmpegenc = (GstFFMpegEnc *) (object);
656 657 658

  switch (prop_id) {
    case ARG_BIT_RATE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
659
      g_value_set_ulong (value, ffmpegenc->bitrate);
660 661
      break;
    case ARG_GOP_SIZE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
662
      g_value_set_int (value, ffmpegenc->gop_size);
663 664
      break;
    case ARG_ME_METHOD:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
665 666 667 668
      g_value_set_enum (value, ffmpegenc->me_method);
      break;
    case ARG_BUFSIZE:
      g_value_set_ulong (value, ffmpegenc->buffer_size);
669 670 671 672 673 674 675
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

676 677
static GstStateChangeReturn
gst_ffmpegenc_change_state (GstElement * element, GstStateChange transition)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
678 679
{
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) element;
680
  GstStateChangeReturn result;
681 682 683 684 685 686

  switch (transition) {
    default:
      break;
  }

687
  result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
688 689

  switch (transition) {
690
    case GST_STATE_CHANGE_PAUSED_TO_READY:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
691 692 693 694
      if (ffmpegenc->opened) {
        avcodec_close (ffmpegenc->context);
        ffmpegenc->opened = FALSE;
      }
695 696 697 698
      if (ffmpegenc->cache) {
        gst_buffer_unref (ffmpegenc->cache);
        ffmpegenc->cache = NULL;
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
699
      break;
700 701
    default:
      break;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
702
  }
703
  return result;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
704 705
}

706
gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
707
gst_ffmpegenc_register (GstPlugin * plugin)
708 709
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
710 711
    sizeof (GstFFMpegEncClass),
    (GBaseInitFunc) gst_ffmpegenc_base_init,
712
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
713
    (GClassInitFunc) gst_ffmpegenc_class_init,
714 715
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
716
    sizeof (GstFFMpegEnc),
717
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
718
    (GInstanceInitFunc) gst_ffmpegenc_init,
719 720 721
  };
  GType type;
  AVCodec *in_plugin;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
722

723 724 725 726 727 728
  in_plugin = first_avcodec;

  enc_global_plugins = g_hash_table_new (NULL, NULL);

  while (in_plugin) {
    gchar *type_name;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
729 730
    GstCaps *srccaps, *sinkcaps;
    GstFFMpegEncClassParams *params;
731

Ronald S. Bultje's avatar
Ronald S. Bultje committed
732 733
    /* no quasi codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
734
        in_plugin->id == CODEC_ID_ZLIB ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
735 736
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
737
      goto next;
738
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
739 740 741

    /* only encoders */
    if (!in_plugin->encode) {
742 743
      goto next;
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
744

745
    /* name */
746 747 748
    if (!gst_ffmpeg_get_codecid_longname (in_plugin->id)) {
      g_warning ("Add encoder %s (%d) please",
          in_plugin->name, in_plugin->id);
749
      goto next;
750
    }
751

Ronald S. Bultje's avatar
Ronald S. Bultje committed
752
    /* first make sure we've got a supported type */
753
    srccaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, TRUE);
754 755 756 757 758
    if (in_plugin->type == CODEC_TYPE_VIDEO) {
      sinkcaps = gst_caps_from_string ("video/x-raw-rgb; video/x-raw-yuv");
    } else {
      sinkcaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
759 760 761
    if (!sinkcaps || !srccaps)
      goto next;

762
    /* construct the type */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
763
    type_name = g_strdup_printf ("ffenc_%s", in_plugin->name);
764

765
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
766 767
    if (g_type_from_name (type_name)) {
      g_free (type_name);
768 769 770
      goto next;
    }

Ronald S. Bultje's avatar
Ronald S. Bultje committed
771 772
    params = g_new0 (GstFFMpegEncClassParams, 1);
    params->in_plugin = in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
773 774
    params->srccaps = srccaps;
    params->sinkcaps = sinkcaps;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
775

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
776 777
    g_hash_table_insert (enc_global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);
778 779

    /* create the glib type now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
780
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
David Schleef's avatar
David Schleef committed
781 782
    if (!gst_element_register (plugin, type_name, GST_RANK_NONE, type)) {
      g_free (type_name);
783
      return FALSE;
David Schleef's avatar
David Schleef committed
784 785 786
    }

    g_free (type_name);
787

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
788 789
    g_hash_table_insert (enc_global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
790

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
791
  next:
792 793
    in_plugin = in_plugin->next;
  }
794
  g_hash_table_remove (enc_global_plugins, GINT_TO_POINTER (0));
795 796 797

  return TRUE;
}