gstffmpegenc.c 20 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 36
#include "gstffmpegcodecmap.h"

37 38
typedef struct _GstFFMpegEnc GstFFMpegEnc;

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

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

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

typedef struct _GstFFMpegEncClass GstFFMpegEncClass;

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

  AVCodec *in_plugin;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
66
  GstPadTemplate *srctempl, *sinktempl;
67 68
};

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

75 76 77 78 79 80 81 82 83 84
#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))
85

86 87
#define VIDEO_BUFFER_SIZE (1024*1024)

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

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

#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
110 111 112 113 114 115 116
    {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},
117 118
  };
  if (!ffmpegenc_me_method_type) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
119 120
    ffmpegenc_me_method_type =
        g_enum_register_static ("GstFFMpegEncMeMethod", ffmpegenc_me_methods);
121 122 123 124 125 126 127
  }
  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
128 129 130 131
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
132 133

static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
134 135 136
gst_ffmpegenc_connect (GstPad * pad, const GstCaps * caps);
static void gst_ffmpegenc_chain_video (GstPad * pad, GstData * _data);
static void gst_ffmpegenc_chain_audio (GstPad * pad, GstData * _data);
137

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
138 139 140 141 142 143
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);

static GstElementStateReturn gst_ffmpegenc_change_state (GstElement * element);
144 145 146

static GstElementClass *parent_class = NULL;

147
/*static guint gst_ffmpegenc_signals[LAST_SIGNAL] = { 0 }; */
148

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

  params = g_hash_table_lookup (enc_global_plugins,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
159
      GINT_TO_POINTER (G_OBJECT_CLASS_TYPE (gobject_class)));
160 161
  /* 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
162
    params = g_hash_table_lookup (enc_global_plugins, GINT_TO_POINTER (0));
163 164
  }
  g_assert (params);
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
165 166

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

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

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

194
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
195
gst_ffmpegenc_class_init (GstFFMpegEncClass * klass)
196 197 198 199
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
200 201
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
202

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
203
  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
204 205

  if (klass->in_plugin->type == CODEC_TYPE_VIDEO) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
    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));
224 225 226 227 228
  }

  gobject_class->set_property = gst_ffmpegenc_set_property;
  gobject_class->get_property = gst_ffmpegenc_get_property;

Ronald S. Bultje's avatar
Ronald S. Bultje committed
229
  gstelement_class->change_state = gst_ffmpegenc_change_state;
230

Ronald S. Bultje's avatar
Ronald S. Bultje committed
231
  gobject_class->dispose = gst_ffmpegenc_dispose;
232 233 234
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
235
gst_ffmpegenc_init (GstFFMpegEnc * ffmpegenc)
236
{
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
237 238
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
239

Ronald S. Bultje's avatar
Ronald S. Bultje committed
240 241 242 243
  /* setup pads */
  ffmpegenc->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
  gst_pad_set_link_function (ffmpegenc->sinkpad, gst_ffmpegenc_connect);
  ffmpegenc->srcpad = gst_pad_new_from_template (oclass->srctempl, "src");
244
  gst_pad_use_explicit_caps (ffmpegenc->srcpad);
245 246 247 248

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

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

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

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

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

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
269
gst_ffmpegenc_dispose (GObject * object)
270
{
Ronald S. Bultje's avatar
Ronald S. Bultje committed
271
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) object;
272

Ronald S. Bultje's avatar
Ronald S. Bultje committed
273 274 275 276 277
  /* close old session */
  if (ffmpegenc->opened) {
    avcodec_close (ffmpegenc->context);
    ffmpegenc->opened = FALSE;
  }
278

Ronald S. Bultje's avatar
Ronald S. Bultje committed
279 280 281 282
  /* clean up remaining allocated data */
  av_free (ffmpegenc->context);
  av_free (ffmpegenc->picture);
}
283

Ronald S. Bultje's avatar
Ronald S. Bultje committed
284
static GstPadLinkReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
285
gst_ffmpegenc_connect (GstPad * pad, const GstCaps * caps)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
286
{
David Schleef's avatar
David Schleef committed
287
  GstCaps *other_caps;
288 289
  GstCaps *allowed_caps;
  GstCaps *icaps;
David Schleef's avatar
David Schleef committed
290 291
  enum PixelFormat pix_fmt;
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) gst_pad_get_parent (pad);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
292 293
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
294

Ronald S. Bultje's avatar
Ronald S. Bultje committed
295 296 297 298 299
  /* close old session */
  if (ffmpegenc->opened) {
    avcodec_close (ffmpegenc->context);
    ffmpegenc->opened = FALSE;
  }
300

Ronald S. Bultje's avatar
Ronald S. Bultje committed
301 302 303 304 305 306 307 308
  /* set defaults */
  avcodec_get_context_defaults (ffmpegenc->context);

  /* 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;
309

Ronald S. Bultje's avatar
Ronald S. Bultje committed
310 311 312 313
  /* general properties */
  ffmpegenc->context->qmin = 3;
  ffmpegenc->context->qmax = 15;
  ffmpegenc->context->max_qdiff = 3;
314

Ronald S. Bultje's avatar
Ronald S. Bultje committed
315 316
  /* no edges */
  ffmpegenc->context->flags |= CODEC_FLAG_EMU_EDGE;
317

David Schleef's avatar
David Schleef committed
318 319
  /* fetch pix_fmt and so on */
  gst_ffmpeg_caps_to_codectype (oclass->in_plugin->type,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
320
      caps, ffmpegenc->context);
321

David Schleef's avatar
David Schleef committed
322
  pix_fmt = ffmpegenc->context->pix_fmt;
323

David Schleef's avatar
David Schleef committed
324 325
  /* open codec */
  if (avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) {
326
    avcodec_close (ffmpegenc->context);
David Schleef's avatar
David Schleef committed
327
    GST_DEBUG ("ffenc_%s: Failed to open FFMPEG codec",
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
328
        oclass->in_plugin->name);
David Schleef's avatar
David Schleef committed
329
    return GST_PAD_LINK_REFUSED;
330 331
  }

David Schleef's avatar
David Schleef committed
332 333 334 335
  /* 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
336
        oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
337 338
    return GST_PAD_LINK_REFUSED;
  }
339

Ronald S. Bultje's avatar
Ronald S. Bultje committed
340
  /* try to set this caps on the other side */
David Schleef's avatar
David Schleef committed
341
  other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
342
      ffmpegenc->context, TRUE);
David Schleef's avatar
David Schleef committed
343
  if (!other_caps) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
344
    avcodec_close (ffmpegenc->context);
345
    GST_DEBUG ("Unsupported codec - no caps found");
Ronald S. Bultje's avatar
Ronald S. Bultje committed
346
    return GST_PAD_LINK_REFUSED;
347
  }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
348

349 350 351 352 353 354 355 356 357 358 359 360 361
  allowed_caps = gst_pad_get_allowed_caps (ffmpegenc->srcpad);
  icaps = gst_caps_intersect (allowed_caps, other_caps);
  gst_caps_free (allowed_caps);
  gst_caps_free (other_caps);

  if (gst_caps_is_empty (icaps)) {
    gst_caps_free (icaps);
    return GST_PAD_LINK_REFUSED;
  }

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

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
362 363 364
    newcaps =
        gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (icaps,
                0)), NULL);
365 366 367 368
    gst_caps_free (icaps);
    icaps = newcaps;
  }

369 370
  /* FIXME set_explicit_caps is not supposed to be used in a pad link
   * function. */
371
  if (!gst_pad_set_explicit_caps (ffmpegenc->srcpad, icaps)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
372
    avcodec_close (ffmpegenc->context);
373
    gst_caps_free (icaps);
374
    return GST_PAD_LINK_REFUSED;
375
  }
376
  gst_caps_free (icaps);
377

Ronald S. Bultje's avatar
Ronald S. Bultje committed
378 379 380 381
  /* success! */
  ffmpegenc->opened = TRUE;

  return GST_PAD_LINK_OK;
382 383 384
}

static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
385
gst_ffmpegenc_chain_video (GstPad * pad, GstData * _data)
386
{
387
  GstBuffer *inbuf = GST_BUFFER (_data);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
388
  GstBuffer *outbuf = NULL;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
389 390 391
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (gst_pad_get_parent (pad));
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
392
  gint ret_size = 0;
393

Ronald S. Bultje's avatar
Ronald S. Bultje committed
394 395
  /* FIXME: events (discont (flush!) and eos (close down) etc.) */

396 397
  outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
  avpicture_fill ((AVPicture *) ffmpegenc->picture,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
398 399 400
      GST_BUFFER_DATA (inbuf),
      ffmpegenc->context->pix_fmt,
      ffmpegenc->context->width, ffmpegenc->context->height);
401 402
  ffmpegenc->picture->pts = GST_BUFFER_TIMESTAMP (inbuf) / 1000;
  ret_size = avcodec_encode_video (ffmpegenc->context,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
403 404
      GST_BUFFER_DATA (outbuf),
      GST_BUFFER_MAXSIZE (outbuf), ffmpegenc->picture);
405

406
  if (ret_size < 0) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
407
    g_warning ("ffenc_%s: failed to encode buffer", oclass->in_plugin->name);
408 409 410 411
    gst_buffer_unref (inbuf);
    return;
  }

Ronald S. Bultje's avatar
Ronald S. Bultje committed
412 413 414
  GST_BUFFER_SIZE (outbuf) = ret_size;
  GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf);
  GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf);
415
  gst_pad_push (ffmpegenc->srcpad, GST_DATA (outbuf));
416 417 418 419

  gst_buffer_unref (inbuf);
}

420
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
421
gst_ffmpegenc_chain_audio (GstPad * pad, GstData * _data)
422 423 424
{
  GstBuffer *inbuf = GST_BUFFER (_data);
  GstBuffer *outbuf = NULL, *subbuf;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
425 426 427
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (gst_pad_get_parent (pad));
  GstFFMpegEncClass *oclass =
      (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
428 429 430 431 432 433 434
  gint size, ret_size = 0, in_size, frame_size;

  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
435
      ffmpegenc->context->channels;
436 437 438 439 440 441 442
  in_size = size;
  if (ffmpegenc->cache)
    in_size += GST_BUFFER_SIZE (ffmpegenc->cache);

  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
443
        ffmpegenc->context->frame_size) {
444 445 446 447 448 449 450 451 452
      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... */
        ffmpegenc->cache = gst_buffer_merge (ffmpegenc->cache, inbuf);
      } 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
453
            in_size);
454
        GST_BUFFER_DURATION (ffmpegenc->cache) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
455 456
            GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (ffmpegenc->cache) /
            size;
457
        GST_BUFFER_TIMESTAMP (ffmpegenc->cache) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
458 459
            GST_BUFFER_TIMESTAMP (inbuf) +
            (GST_BUFFER_DURATION (inbuf) * (size - in_size) / size);
460 461 462 463
        gst_buffer_unref (inbuf);
      } else {
        gst_buffer_unref (inbuf);
      }
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
464

465 466 467 468 469 470 471 472
      return;
    }

    /* 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
473
          GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
474 475 476 477 478
      subbuf = gst_buffer_merge (ffmpegenc->cache, subbuf);
      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
479
          GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
480
      GST_BUFFER_TIMESTAMP (subbuf) =
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
481 482
          GST_BUFFER_TIMESTAMP (inbuf) + (GST_BUFFER_DURATION (inbuf) *
          (size - in_size) / size);
483 484 485 486
    }

    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
487 488 489
        GST_BUFFER_DATA (outbuf),
        GST_BUFFER_MAXSIZE (outbuf), (const short int *)
        GST_BUFFER_DATA (subbuf));
490 491

    if (ret_size < 0) {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
492
      g_warning ("ffenc_%s: failed to encode buffer", oclass->in_plugin->name);
493
      gst_buffer_unref (inbuf);
494
      gst_buffer_unref (outbuf);
495
      gst_buffer_unref (subbuf);
496 497 498 499 500 501 502 503 504
      return;
    }

    GST_BUFFER_SIZE (outbuf) = ret_size;
    GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (subbuf);
    GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (subbuf);
    gst_pad_push (ffmpegenc->srcpad, GST_DATA (outbuf));

    in_size -= frame_size;
505
    gst_buffer_unref (subbuf);
506 507 508
  }
}

509
static void
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
510 511
gst_ffmpegenc_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
512 513 514 515
{
  GstFFMpegEnc *ffmpegenc;

  /* Get a pointer of the right type. */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
516
  ffmpegenc = (GstFFMpegEnc *) (object);
517 518 519 520

  /* 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
521
      ffmpegenc->bitrate = g_value_get_ulong (value);
522 523
      break;
    case ARG_GOP_SIZE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
524
      ffmpegenc->gop_size = g_value_get_int (value);
525 526
      break;
    case ARG_ME_METHOD:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
527 528 529
      ffmpegenc->me_method = g_value_get_enum (value);
      break;
    case ARG_BUFSIZE:
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
530
      ffmpegenc->buffer_size = g_value_get_ulong (value);
531 532 533 534 535 536 537 538 539
      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
540 541
gst_ffmpegenc_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
542 543 544 545
{
  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
546
  ffmpegenc = (GstFFMpegEnc *) (object);
547 548 549

  switch (prop_id) {
    case ARG_BIT_RATE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
550
      g_value_set_ulong (value, ffmpegenc->bitrate);
551 552
      break;
    case ARG_GOP_SIZE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
553
      g_value_set_int (value, ffmpegenc->gop_size);
554 555
      break;
    case ARG_ME_METHOD:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
556 557 558 559
      g_value_set_enum (value, ffmpegenc->me_method);
      break;
    case ARG_BUFSIZE:
      g_value_set_ulong (value, ffmpegenc->buffer_size);
560 561 562 563 564 565 566
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Ronald S. Bultje's avatar
Ronald S. Bultje committed
567
static GstElementStateReturn
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
568
gst_ffmpegenc_change_state (GstElement * element)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
569 570 571 572 573 574 575 576 577 578
{
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) element;
  gint transition = GST_STATE_TRANSITION (element);

  switch (transition) {
    case GST_STATE_PAUSED_TO_READY:
      if (ffmpegenc->opened) {
        avcodec_close (ffmpegenc->context);
        ffmpegenc->opened = FALSE;
      }
579 580 581 582
      if (ffmpegenc->cache) {
        gst_buffer_unref (ffmpegenc->cache);
        ffmpegenc->cache = NULL;
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
583 584 585 586 587 588 589 590 591
      break;
  }

  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);

  return GST_STATE_SUCCESS;
}

592
gboolean
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
593
gst_ffmpegenc_register (GstPlugin * plugin)
594 595
{
  GTypeInfo typeinfo = {
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
596 597
    sizeof (GstFFMpegEncClass),
    (GBaseInitFunc) gst_ffmpegenc_base_init,
598
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
599
    (GClassInitFunc) gst_ffmpegenc_class_init,
600 601
    NULL,
    NULL,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
602
    sizeof (GstFFMpegEnc),
603
    0,
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
604
    (GInstanceInitFunc) gst_ffmpegenc_init,
605 606 607
  };
  GType type;
  AVCodec *in_plugin;
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
608

609 610 611 612 613 614
  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
615 616
    GstCaps *srccaps, *sinkcaps;
    GstFFMpegEncClassParams *params;
617

Ronald S. Bultje's avatar
Ronald S. Bultje committed
618 619
    /* no quasi codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
620 621
        (in_plugin->id >= CODEC_ID_PCM_S16LE &&
            in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
622
      goto next;
623
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
624 625 626

    /* only encoders */
    if (!in_plugin->encode) {
627 628
      goto next;
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
629 630

    /* first make sure we've got a supported type */
631
    srccaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, TRUE);
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
632
    sinkcaps = gst_ffmpeg_codectype_to_caps (in_plugin->type, NULL);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
633 634 635
    if (!sinkcaps || !srccaps)
      goto next;

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

639
    /* if it's already registered, drop it */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
640 641
    if (g_type_from_name (type_name)) {
      g_free (type_name);
642 643 644
      goto next;
    }

Ronald S. Bultje's avatar
Ronald S. Bultje committed
645 646
    params = g_new0 (GstFFMpegEncClassParams, 1);
    params->in_plugin = in_plugin;
Ronald S. Bultje's avatar
Bla  
Ronald S. Bultje committed
647 648
    params->srccaps = srccaps;
    params->sinkcaps = sinkcaps;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
649

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
650 651
    g_hash_table_insert (enc_global_plugins,
        GINT_TO_POINTER (0), (gpointer) params);
652 653

    /* create the glib type now */
Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
654
    type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
David Schleef's avatar
David Schleef committed
655 656
    if (!gst_element_register (plugin, type_name, GST_RANK_NONE, type)) {
      g_free (type_name);
657
      return FALSE;
David Schleef's avatar
David Schleef committed
658 659 660
    }

    g_free (type_name);
661

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
662 663
    g_hash_table_insert (enc_global_plugins,
        GINT_TO_POINTER (type), (gpointer) params);
664

Thomas Vander Stichele's avatar
indent  
Thomas Vander Stichele committed
665
  next:
666 667
    in_plugin = in_plugin->next;
  }
668
  g_hash_table_remove (enc_global_plugins, GINT_TO_POINTER (0));
669 670 671

  return TRUE;
}