gstffmpegenc.c 19.9 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
39
40
41
42
43
44
45
46
typedef struct _GstFFMpegEnc GstFFMpegEnc;

struct _GstFFMpegEnc {
  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
47
48
  AVFrame *picture;
  gboolean opened;
49
  GstBuffer *cache;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
50
51
52
53
54
55

  /* cache */
  gulong bitrate;
  gint me_method;
  gint gop_size;
  gulong buffer_size;
56
57
58
59
60
61
62
63
};

typedef struct _GstFFMpegEncClass GstFFMpegEncClass;

struct _GstFFMpegEncClass {
  GstElementClass parent_class;

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

Ronald S. Bultje's avatar
Ronald S. Bultje committed
67
68
typedef struct {
  AVCodec *in_plugin;
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
69
  GstCaps *srccaps, *sinkcaps;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
70
71
} GstFFMpegEncClassParams;

72
73
74
75
76
77
78
79
80
81
#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))
82

83
84
#define VIDEO_BUFFER_SIZE (1024*1024)

85
86
87
88
89
90
91
92
93
94
enum {
  /* FILL ME */
  LAST_SIGNAL
};

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

#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[] = {
    { ME_ZERO,  "0", "zero" },
    { ME_FULL,  "1", "full" },
    { ME_LOG,   "2", "logarithmic" },
    { ME_PHODS, "3", "phods" },
109
110
    { ME_EPZS,  "4", "epzs" },
    { ME_X1   , "5", "x1" },
111
112
113
114
115
116
117
118
119
120
121
122
    { 0, NULL, NULL },
  };
  if (!ffmpegenc_me_method_type) {
    ffmpegenc_me_method_type = g_enum_register_static ("GstFFMpegEncMeMethod", ffmpegenc_me_methods);
  }
  return ffmpegenc_me_method_type;
}

static GHashTable *enc_global_plugins;

/* A number of functon prototypes are given so we can refer to them later. */
static void	gst_ffmpegenc_class_init	(GstFFMpegEncClass *klass);
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
123
static void	gst_ffmpegenc_base_init		(GstFFMpegEncClass *klass);
124
static void	gst_ffmpegenc_init		(GstFFMpegEnc *ffmpegenc);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
125
126
127
static void	gst_ffmpegenc_dispose		(GObject *object);

static GstPadLinkReturn
David Schleef's avatar
David Schleef committed
128
		gst_ffmpegenc_connect		(GstPad *pad, const GstCaps *caps);
129
130
static void	gst_ffmpegenc_chain_video	(GstPad *pad, GstData *_data);
static void	gst_ffmpegenc_chain_audio	(GstPad *pad, GstData *_data);
131

Ronald S. Bultje's avatar
Ronald S. Bultje committed
132
133
134
135
136
137
138
139
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);
140

Ronald S. Bultje's avatar
Ronald S. Bultje committed
141
142
static GstElementStateReturn
		gst_ffmpegenc_change_state	(GstElement *element);
143
144
145

static GstElementClass *parent_class = NULL;

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

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

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

  /* construct the element details struct */
  details = g_new0 (GstElementDetails, 1);
  details->longname = g_strdup_printf("FFMPEG %s encoder",
				      params->in_plugin->name);
170
  details->klass = g_strdup_printf("Codec/Encoder/%s",
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
171
172
173
174
175
176
177
178
179
				   (params->in_plugin->type == CODEC_TYPE_VIDEO) ?
				   "Video" : "Audio");
  details->description = g_strdup_printf("FFMPEG %s encoder",
					 params->in_plugin->name);
  details->author = g_strdup("Wim Taymans <wim.taymans@chello.be>\n"
			     "Ronald Bultje <rbultje@ronald.bitfreak.net>");

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

  gst_element_class_add_pad_template (element_class, srctempl);
  gst_element_class_add_pad_template (element_class, sinktempl);
  gst_element_class_set_details (element_class, details);

  klass->in_plugin = params->in_plugin;
  klass->srctempl = srctempl;
  klass->sinktempl = sinktempl;
}

193
194
195
196
197
198
199
200
201
202
203
204
205
static void
gst_ffmpegenc_class_init (GstFFMpegEncClass *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

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

  parent_class = g_type_class_ref(GST_TYPE_ELEMENT);

  if (klass->in_plugin->type == CODEC_TYPE_VIDEO) {
    g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_BIT_RATE,
Ronald S. Bultje's avatar
Ronald S. Bultje committed
206
207
208
      g_param_spec_ulong ("bitrate","Bit Rate",
			  "Target Video Bitrate",
			  0, G_MAXULONG, 300000, G_PARAM_READWRITE)); 
209
    g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_GOP_SIZE,
Ronald S. Bultje's avatar
Ronald S. Bultje committed
210
211
212
      g_param_spec_int ("gop_size","GOP Size",
			"Number of frames within one GOP",
			0, G_MAXINT, 15, G_PARAM_READWRITE)); 
213
    g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_ME_METHOD,
Ronald S. Bultje's avatar
Ronald S. Bultje committed
214
215
216
217
218
219
220
      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));
221
222
223
  }
  else if (klass->in_plugin->type == CODEC_TYPE_AUDIO) {
    g_object_class_install_property(G_OBJECT_CLASS (klass), ARG_BIT_RATE,
Ronald S. Bultje's avatar
Ronald S. Bultje committed
224
225
226
      g_param_spec_ulong ("bitrate","Bit Rate",
			  "Target Audio Bitrate",
			  0, G_MAXULONG, 128000, G_PARAM_READWRITE)); 
227
228
229
230
231
  }

  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
232
  gstelement_class->change_state = gst_ffmpegenc_change_state;
233

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

static void
gst_ffmpegenc_init(GstFFMpegEnc *ffmpegenc)
{
  GstFFMpegEncClass *oclass = (GstFFMpegEncClass*)(G_OBJECT_GET_CLASS (ffmpegenc));

Ronald S. Bultje's avatar
Ronald S. Bultje committed
242
243
244
245
  /* 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");
246
  gst_pad_use_explicit_caps (ffmpegenc->srcpad);
247
248
249
250

  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
251
252
253
254
  /* ffmpeg objects */
  ffmpegenc->context = avcodec_alloc_context();
  ffmpegenc->picture = avcodec_alloc_frame();
  ffmpegenc->opened = FALSE;
255
  ffmpegenc->cache = NULL;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
256
257

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

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

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

static void
Ronald S. Bultje's avatar
Ronald S. Bultje committed
271
gst_ffmpegenc_dispose (GObject *object)
272
{
Ronald S. Bultje's avatar
Ronald S. Bultje committed
273
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) object;
274

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

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

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

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

Ronald S. Bultje's avatar
Ronald S. Bultje committed
303
304
305
306
307
308
309
310
  /* 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;
311

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

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

David Schleef's avatar
David Schleef committed
320
321
322
  /* fetch pix_fmt and so on */
  gst_ffmpeg_caps_to_codectype (oclass->in_plugin->type,
				caps, ffmpegenc->context);
323

David Schleef's avatar
David Schleef committed
324
  pix_fmt = ffmpegenc->context->pix_fmt;
325

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

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

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

351
352
353
354
355
356
357
358
359
360
361
362
363
  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;

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
385
386
gst_ffmpegenc_chain_video (GstPad  *pad,
			   GstData *_data)
387
{
388
  GstBuffer *inbuf = GST_BUFFER (_data);
Ronald S. Bultje's avatar
Ronald S. Bultje committed
389
  GstBuffer *outbuf = NULL;
390
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *)(gst_pad_get_parent (pad));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
391
  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
398
399
400
401
402
403
404
405
406
  outbuf = gst_buffer_new_and_alloc (ffmpegenc->buffer_size);
  avpicture_fill ((AVPicture *) ffmpegenc->picture,
		  GST_BUFFER_DATA (inbuf),
		  ffmpegenc->context->pix_fmt,
		  ffmpegenc->context->width,
		  ffmpegenc->context->height);
  ffmpegenc->picture->pts = GST_BUFFER_TIMESTAMP (inbuf) / 1000;
  ret_size = avcodec_encode_video (ffmpegenc->context,
				   GST_BUFFER_DATA (outbuf),
				   GST_BUFFER_MAXSIZE (outbuf),
				   ffmpegenc->picture);
407

408
409
410
411
412
413
414
  if (ret_size < 0) {
    g_warning("ffenc_%s: failed to encode buffer",
	      oclass->in_plugin->name);
    gst_buffer_unref (inbuf);
    return;
  }

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

  gst_buffer_unref (inbuf);
}

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
static void
gst_ffmpegenc_chain_audio (GstPad  *pad,
			   GstData *_data)
{
  GstBuffer *inbuf = GST_BUFFER (_data);
  GstBuffer *outbuf = NULL, *subbuf;
  GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *)(gst_pad_get_parent (pad));
  GstFFMpegEncClass *oclass = (GstFFMpegEncClass*)(G_OBJECT_GET_CLASS(ffmpegenc));
  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 *
			ffmpegenc->context->channels;
  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) <
	  ffmpegenc->context->frame_size) {
      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,
						  in_size);
        GST_BUFFER_DURATION (ffmpegenc->cache) =
	  GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (ffmpegenc->cache) / size;
        GST_BUFFER_TIMESTAMP (ffmpegenc->cache) =
	  GST_BUFFER_TIMESTAMP (inbuf) + (GST_BUFFER_DURATION (inbuf) *
	    (size - in_size) / size);
        gst_buffer_unref (inbuf);
      } else {
        gst_buffer_unref (inbuf);
      }
          
      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) =
	GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
      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) =
	GST_BUFFER_DURATION (inbuf) * GST_BUFFER_SIZE (subbuf) / size;
      GST_BUFFER_TIMESTAMP (subbuf) =
	GST_BUFFER_TIMESTAMP (inbuf) + (GST_BUFFER_DURATION (inbuf) *
	  (size - in_size) / size);
    }

    outbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (inbuf));
    ret_size = avcodec_encode_audio (ffmpegenc->context,
				     GST_BUFFER_DATA (outbuf),
				     GST_BUFFER_MAXSIZE (outbuf),
				     (const short int *)
				       GST_BUFFER_DATA (subbuf));

    if (ret_size < 0) {
      g_warning("ffenc_%s: failed to encode buffer",
		oclass->in_plugin->name);
      gst_buffer_unref (inbuf);
498
      gst_buffer_unref (outbuf);
499
      gst_buffer_unref (subbuf);
500
501
502
503
504
505
506
507
508
      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;
509
    gst_buffer_unref (subbuf);
510
511
512
  }
}

513
static void
Ronald S. Bultje's avatar
Ronald S. Bultje committed
514
515
516
517
gst_ffmpegenc_set_property (GObject      *object,
			    guint         prop_id,
			    const GValue *value,
			    GParamSpec   *pspec)
518
519
520
521
522
523
524
525
526
{
  GstFFMpegEnc *ffmpegenc;

  /* Get a pointer of the right type. */
  ffmpegenc = (GstFFMpegEnc *)(object);

  /* 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
527
      ffmpegenc->bitrate = g_value_get_ulong (value);
528
529
      break;
    case ARG_GOP_SIZE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
530
      ffmpegenc->gop_size = g_value_get_int (value);
531
532
      break;
    case ARG_ME_METHOD:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
533
534
535
536
      ffmpegenc->me_method = g_value_get_enum (value);
      break;
    case ARG_BUFSIZE:
      ffmpegenc->buffer_size = g_value_get_ulong(value);
537
538
539
540
541
542
543
544
545
      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
Ronald S. Bultje's avatar
Ronald S. Bultje committed
546
547
548
549
gst_ffmpegenc_get_property (GObject    *object,
			    guint       prop_id,
			    GValue     *value,
			    GParamSpec *pspec)
550
551
552
553
554
555
556
557
{
  GstFFMpegEnc *ffmpegenc;

  /* It's not null if we got it, but it might not be ours */
  ffmpegenc = (GstFFMpegEnc *)(object);

  switch (prop_id) {
    case ARG_BIT_RATE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
558
      g_value_set_ulong (value, ffmpegenc->bitrate);
559
560
      break;
    case ARG_GOP_SIZE:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
561
      g_value_set_int (value, ffmpegenc->gop_size);
562
563
      break;
    case ARG_ME_METHOD:
Ronald S. Bultje's avatar
Ronald S. Bultje committed
564
565
566
567
      g_value_set_enum (value, ffmpegenc->me_method);
      break;
    case ARG_BUFSIZE:
      g_value_set_ulong (value, ffmpegenc->buffer_size);
568
569
570
571
572
573
574
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

Ronald S. Bultje's avatar
Ronald S. Bultje committed
575
576
577
578
579
580
581
582
583
584
585
586
static GstElementStateReturn
gst_ffmpegenc_change_state (GstElement *element)
{
  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;
      }
587
588
589
590
      if (ffmpegenc->cache) {
        gst_buffer_unref (ffmpegenc->cache);
        ffmpegenc->cache = NULL;
      }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
591
592
593
594
595
596
597
598
599
      break;
  }

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

  return GST_STATE_SUCCESS;
}

600
601
602
603
604
gboolean
gst_ffmpegenc_register (GstPlugin *plugin)
{
  GTypeInfo typeinfo = {
    sizeof(GstFFMpegEncClass),      
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
605
    (GBaseInitFunc)gst_ffmpegenc_base_init,
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
    NULL,
    (GClassInitFunc)gst_ffmpegenc_class_init,
    NULL,
    NULL,
    sizeof(GstFFMpegEnc),
    0,
    (GInstanceInitFunc)gst_ffmpegenc_init,
  };
  GType type;
  AVCodec *in_plugin;
  
  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
623
624
    GstCaps *srccaps, *sinkcaps;
    GstFFMpegEncClassParams *params;
625

Ronald S. Bultje's avatar
Ronald S. Bultje committed
626
627
628
    /* no quasi codecs, please */
    if (in_plugin->id == CODEC_ID_RAWVIDEO ||
	(in_plugin->id >= CODEC_ID_PCM_S16LE &&
629
	 in_plugin->id <= CODEC_ID_PCM_ALAW)) {
Ronald S. Bultje's avatar
Ronald S. Bultje committed
630
      goto next;
631
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
632
633
634

    /* only encoders */
    if (!in_plugin->encode) {
635
636
      goto next;
    }
Ronald S. Bultje's avatar
Ronald S. Bultje committed
637
638
639
640
641
642
643

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

644
    /* construct the type */
Ronald S. Bultje's avatar
Ronald S. Bultje committed
645
    type_name = g_strdup_printf("ffenc_%s", in_plugin->name);
646

647
    /* if it's already registered, drop it */
648
649
650
651
652
    if (g_type_from_name(type_name)) {
      g_free(type_name);
      goto next;
    }

Ronald S. Bultje's avatar
Ronald S. Bultje committed
653
654
    params = g_new0 (GstFFMpegEncClassParams, 1);
    params->in_plugin = in_plugin;
Ronald S. Bultje's avatar
Bla    
Ronald S. Bultje committed
655
656
    params->srccaps = srccaps;
    params->sinkcaps = sinkcaps;
Ronald S. Bultje's avatar
Ronald S. Bultje committed
657

658
659
660
661
662
663
664
665
666
    g_hash_table_insert (enc_global_plugins, 
		         GINT_TO_POINTER (0), 
			 (gpointer) params);

    /* create the glib type now */
    type = g_type_register_static(GST_TYPE_ELEMENT, type_name , &typeinfo, 0);
    if (!gst_element_register (plugin, type_name, GST_RANK_NONE, type))
      return FALSE;

Ronald S. Bultje's avatar
Ronald S. Bultje committed
667
668
669
    g_hash_table_insert (enc_global_plugins, 
		         GINT_TO_POINTER (type), 
			 (gpointer) params);
670
671
672
673

next:
    in_plugin = in_plugin->next;
  }
674
  g_hash_table_remove (enc_global_plugins, GINT_TO_POINTER (0));
675
676
677

  return TRUE;
}