compatibility fix for new GST_DEBUG stuff.
[platform/upstream/gstreamer.git] / ext / xvid / gstxvidenc.c
1 /* GStreamer xvid encoder plugin
2  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include "config.h"
25 #endif
26
27 #include <string.h>
28 #include "gstxvidenc.h"
29 #include <gst/video/video.h>
30 #include <xvid.h>
31
32 /* elementfactory information */
33 GstElementDetails gst_xvidenc_details = {
34   "Xvid encoder",
35   "Codec/Video/Encoder",
36   "GPL",
37   "Xvid encoder based on xvidencore",
38   VERSION,
39   "Ronald Bultje <rbultje@ronald.bitfreak.net>",
40   "(C) 2003",
41 };
42
43 GST_PAD_TEMPLATE_FACTORY(sink_template,
44   "sink",
45   GST_PAD_SINK,
46   GST_PAD_ALWAYS,
47   GST_CAPS_NEW("xvidenc_sink",
48                "video/raw",
49                  "format", GST_PROPS_LIST(
50                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')),
51                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','4','2','0')),
52                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','Y','U','V')),
53                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','U','Y','2')),
54                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','V','1','2')),
55                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','V','Y','U')),
56                              GST_PROPS_FOURCC(GST_MAKE_FOURCC('U','Y','V','Y'))
57                            ),
58                  "width",  GST_PROPS_INT_RANGE(0, G_MAXINT),
59                  "height", GST_PROPS_INT_RANGE(0, G_MAXINT),
60                  NULL)
61 )
62
63 GST_PAD_TEMPLATE_FACTORY(src_template,
64   "src",
65   GST_PAD_SRC,
66   GST_PAD_ALWAYS,
67   GST_CAPS_NEW("xvidenc_src",
68                "video/xvid",
69                  NULL)
70 )
71
72
73 /* XvidEnc signals and args */
74 enum {
75   FRAME_ENCODED,
76   LAST_SIGNAL
77 };
78
79 enum {
80   ARG_0,
81   ARG_BITRATE,
82   ARG_MAXKEYINTERVAL,
83   ARG_BUFSIZE
84 };
85
86
87 static void             gst_xvidenc_class_init   (GstXvidEncClass *klass);
88 static void             gst_xvidenc_init         (GstXvidEnc      *xvidenc);
89 static void             gst_xvidenc_chain        (GstPad          *pad,
90                                                   GstBuffer       *buf);
91 static GstPadLinkReturn gst_xvidenc_connect      (GstPad          *pad,
92                                                   GstCaps         *vscapslist);
93
94 /* properties */
95 static void             gst_xvidenc_set_property (GObject         *object,
96                                                   guint            prop_id,
97                                                   const GValue    *value,
98                                                   GParamSpec      *pspec);
99 static void             gst_xvidenc_get_property (GObject         *object,
100                                                   guint            prop_id,
101                                                   GValue          *value,
102                                                   GParamSpec      *pspec);
103
104 static GstElementClass *parent_class = NULL;
105 static guint gst_xvidenc_signals[LAST_SIGNAL] = { 0 };
106
107
108 GType
109 gst_xvidenc_get_type(void)
110 {
111   static GType xvidenc_type = 0;
112
113   if (!xvidenc_type)
114   {
115     static const GTypeInfo xvidenc_info = {
116       sizeof(GstXvidEncClass),
117       NULL,
118       NULL,
119       (GClassInitFunc) gst_xvidenc_class_init,
120       NULL,
121       NULL,
122       sizeof(GstXvidEnc),
123       0,
124       (GInstanceInitFunc) gst_xvidenc_init,
125     };
126     xvidenc_type = g_type_register_static(GST_TYPE_ELEMENT,
127                                           "GstXvidEnc",
128                                           &xvidenc_info, 0);
129   }
130   return xvidenc_type;
131 }
132
133
134 static void
135 gst_xvidenc_class_init (GstXvidEncClass *klass)
136 {
137   GstElementClass *gstelement_class;
138   GObjectClass *gobject_class;
139
140   gobject_class = (GObjectClass *) klass;
141   gstelement_class = (GstElementClass *) klass;
142
143   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
144
145   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BITRATE,
146     g_param_spec_ulong("bitrate","Bitrate",
147                        "Target video bitrate",
148                        0,G_MAXULONG,0,G_PARAM_READWRITE));
149
150   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MAXKEYINTERVAL,
151     g_param_spec_int("max_key_interval","Max. Key Interval",
152                      "Maximum number of frames between two keyframes",
153                      0,G_MAXINT,0,G_PARAM_READWRITE));
154
155   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFSIZE,
156     g_param_spec_ulong("buffer_size", "Buffer Size",
157                        "Size of the video buffers",
158                        0,G_MAXULONG,0,G_PARAM_READWRITE));
159
160   gobject_class->set_property = gst_xvidenc_set_property;
161   gobject_class->get_property = gst_xvidenc_get_property;
162
163   gst_xvidenc_signals[FRAME_ENCODED] =
164     g_signal_new ("frame_encoded", G_TYPE_FROM_CLASS(klass),
165                   G_SIGNAL_RUN_LAST,
166                   G_STRUCT_OFFSET (GstXvidEncClass, frame_encoded),
167                   NULL, NULL,
168                   g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
169 }
170
171
172 static void
173 gst_xvidenc_init (GstXvidEnc *xvidenc)
174 {
175   /* create the sink pad */
176   xvidenc->sinkpad = gst_pad_new_from_template(
177                        GST_PAD_TEMPLATE_GET(sink_template),
178                        "sink");
179   gst_element_add_pad(GST_ELEMENT(xvidenc), xvidenc->sinkpad);
180
181   gst_pad_set_chain_function(xvidenc->sinkpad, gst_xvidenc_chain);
182   gst_pad_set_link_function(xvidenc->sinkpad, gst_xvidenc_connect);
183
184   /* create the src pad */
185   xvidenc->srcpad = gst_pad_new_from_template(
186                       GST_PAD_TEMPLATE_GET(src_template),
187                       "src");
188   gst_element_add_pad(GST_ELEMENT(xvidenc), xvidenc->srcpad);
189
190   /* bitrate, etc. */
191   xvidenc->width = xvidenc->height = xvidenc->csp = -1;
192   xvidenc->bitrate = 512 * 1024;
193   xvidenc->max_key_interval = -1; /* default - 2*fps */
194   xvidenc->buffer_size = 512 * 1024;
195
196   /* set xvid handle to NULL */
197   xvidenc->handle = NULL;
198 }
199
200
201 static gboolean
202 gst_xvidenc_setup (GstXvidEnc *xvidenc)
203 {
204   XVID_ENC_PARAM xenc;
205   gdouble fps;
206   int ret;
207
208   fps = gst_video_frame_rate(GST_PAD_PEER(xvidenc->sinkpad));
209
210   /* set up xvid codec parameters - grab docs from
211    * xvid.org for more info */
212   memset(&xenc, 0, sizeof(XVID_ENC_PARAM));
213   xenc.width = xvidenc->width;
214   xenc.height = xvidenc->height;
215   xenc.fincr = (int)(fps * 1000);
216   xenc.fbase = 1000;
217   xenc.rc_bitrate = xvidenc->bitrate;
218   xenc.rc_reaction_delay_factor = -1;
219   xenc.rc_averaging_period = -1;
220   xenc.rc_buffer = -1;
221   xenc.min_quantizer = 1;
222   xenc.max_quantizer = 31;
223   xenc.max_key_interval = (xvidenc->max_key_interval == -1) ?
224                             (2 * xenc.fincr / xenc.fbase) :
225                               xvidenc->max_key_interval;
226   xenc.handle = NULL;
227
228   if ((ret = xvid_encore(NULL, XVID_ENC_CREATE,
229                          &xenc, NULL)) != XVID_ERR_OK) {
230     gst_element_error(GST_ELEMENT(xvidenc),
231                       "Error setting up xvid encoder: %s (%d)",
232                       gst_xvid_error(ret), ret);
233     return FALSE;
234   }
235
236   xvidenc->handle = xenc.handle;
237
238   return TRUE;
239 }
240
241
242 static void
243 gst_xvidenc_chain (GstPad    *pad,
244                    GstBuffer *buf)
245 {
246   GstXvidEnc *xvidenc;
247   GstBuffer *outbuf;
248   XVID_ENC_FRAME xframe;
249   int ret;
250
251   g_return_if_fail(pad != NULL);
252   g_return_if_fail(GST_IS_PAD(pad));
253   g_return_if_fail(buf != NULL);
254
255   xvidenc = GST_XVIDENC(GST_OBJECT_PARENT(pad));
256
257   if (!xvidenc->handle) {
258     if (!gst_xvidenc_setup(xvidenc)) {
259       gst_buffer_unref(buf);
260       return;
261     }
262   }
263
264   outbuf = gst_buffer_new_and_alloc(xvidenc->buffer_size);
265   GST_BUFFER_TIMESTAMP(outbuf) = GST_BUFFER_TIMESTAMP(buf);
266
267   /* encode and so ... */
268   xframe.image = GST_BUFFER_DATA(buf);
269   xframe.bitstream = (void *) GST_BUFFER_DATA(outbuf);
270   xframe.length = GST_BUFFER_MAXSIZE(outbuf);
271   xframe.intra = -1;
272   xframe.quant = 0;
273   xframe.colorspace = xvidenc->csp;
274   xframe.general = XVID_H263QUANT |
275                    XVID_INTER4V |
276                    XVID_HALFPEL;
277   xframe.motion = PMV_EARLYSTOP16 |
278                   PMV_HALFPELREFINE16 |
279                   PMV_EXTSEARCH16 |
280                   PMV_EARLYSTOP8 |
281                   PMV_HALFPELREFINE8;
282
283   if ((ret = xvid_encore(xvidenc->handle, XVID_ENC_ENCODE,
284                          &xframe, NULL)) != XVID_ERR_OK) {
285     gst_element_error(GST_ELEMENT(xvidenc),
286                       "Error encoding xvid frame: %s (%d)",
287                       gst_xvid_error(ret), ret);
288     gst_buffer_unref(buf);
289     return;
290   }
291
292   GST_BUFFER_SIZE(outbuf) = xframe.length;
293   if (xframe.intra)
294     GST_BUFFER_FLAG_SET(outbuf, GST_BUFFER_KEY_UNIT);
295
296   /* go out, multiply! */
297   gst_pad_push(xvidenc->srcpad, outbuf);
298
299   /* proclaim destiny */
300   g_signal_emit(G_OBJECT(xvidenc),gst_xvidenc_signals[FRAME_ENCODED], 0);
301
302   /* until the final judgement */
303   gst_buffer_unref(buf);
304 }
305
306
307 static GstPadLinkReturn
308 gst_xvidenc_connect (GstPad  *pad,
309                      GstCaps *vscaps)
310 {
311   GstXvidEnc *xvidenc;
312   GstCaps *caps;
313
314   xvidenc = GST_XVIDENC(gst_pad_get_parent (pad));
315
316   /* if there's something old around, remove it */
317   if (xvidenc->handle) {
318     xvid_encore(xvidenc->handle, XVID_ENC_DESTROY, NULL, NULL);
319     xvidenc->handle = NULL;
320   }
321
322   /* we are not going to act on variable caps */
323   if (!GST_CAPS_IS_FIXED(vscaps))
324     return GST_PAD_LINK_DELAYED;
325
326   for (caps = vscaps; caps != NULL; caps = caps->next)
327   {
328     int w,h,d;
329     guint32 fourcc;
330     gint xvid_cs;
331     gst_caps_get_int(caps, "width", &w);
332     gst_caps_get_int(caps, "height", &h);
333     gst_caps_get_fourcc_int(caps, "format", &fourcc);
334
335     switch (fourcc)
336     {
337       case GST_MAKE_FOURCC('I','4','2','0'):
338       case GST_MAKE_FOURCC('I','Y','U','V'):
339         xvid_cs = XVID_CSP_I420;
340         break;
341       case GST_MAKE_FOURCC('Y','U','Y','2'):
342         xvid_cs = XVID_CSP_YUY2;
343         break;
344       case GST_MAKE_FOURCC('Y','V','1','2'):
345         xvid_cs = XVID_CSP_YV12;
346         break;
347       case GST_MAKE_FOURCC('U','Y','V','Y'):
348         xvid_cs = XVID_CSP_UYVY;
349         break;
350       case GST_MAKE_FOURCC('Y','V','Y','U'):
351         xvid_cs = XVID_CSP_YVYU;
352         break;
353       case GST_MAKE_FOURCC('R','G','B',' '):
354         gst_caps_get_int(caps, "depth", &d);
355         switch (d) {
356           case 15:
357             xvid_cs = XVID_CSP_RGB555;
358             break;
359           case 16:
360             xvid_cs = XVID_CSP_RGB565;
361             break;
362           case 24:
363             xvid_cs = XVID_CSP_RGB24;
364             break;
365           case 32:
366             xvid_cs = XVID_CSP_RGB32;
367             break;
368           default:
369             goto trynext;
370         }
371         break;
372       default:
373         goto trynext;
374     }
375
376     /* grmbl, we only know the peer pad *after*
377      * linking, so we accept here, get the fps on
378      * the first cycle and set it all up then */
379     xvidenc->csp = xvid_cs;
380     xvidenc->width = w;
381     xvidenc->height = h;
382     return gst_pad_try_set_caps(xvidenc->srcpad,
383                                 GST_CAPS_NEW("xvidenc_src_caps",
384                                              "video/xvid",
385                                                "width",  GST_PROPS_INT(w),
386                                                "height", GST_PROPS_INT(h)));
387
388 trynext:
389     continue;
390   }
391
392   /* if we got here - it's not good */
393   return GST_PAD_LINK_REFUSED;
394 }
395
396
397 static void
398 gst_xvidenc_set_property (GObject      *object,
399                           guint         prop_id,
400                           const GValue *value,
401                           GParamSpec   *pspec)
402 {
403   GstXvidEnc *xvidenc;
404
405   /* it's not null if we got it, but it might not be ours */
406   g_return_if_fail (GST_IS_XVIDENC (object));
407   xvidenc = GST_XVIDENC(object);
408
409   switch (prop_id)
410   {
411     case ARG_BITRATE:
412       xvidenc->bitrate = g_value_get_ulong(value);
413       break;
414     case ARG_BUFSIZE:
415       xvidenc->buffer_size = g_value_get_ulong(value);
416       break;
417     case ARG_MAXKEYINTERVAL:
418       xvidenc->max_key_interval = g_value_get_int(value);
419       break;
420     default:
421       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
422       break;
423   }
424 }
425
426
427 static void
428 gst_xvidenc_get_property (GObject    *object,
429                           guint       prop_id,
430                           GValue     *value,
431                           GParamSpec *pspec)
432 {
433   GstXvidEnc *xvidenc;
434
435   /* it's not null if we got it, but it might not be ours */
436   g_return_if_fail (GST_IS_XVIDENC (object));
437   xvidenc = GST_XVIDENC(object);
438
439   switch (prop_id) {
440     case ARG_BITRATE:
441       g_value_set_ulong(value, xvidenc->bitrate);
442       break;
443     case ARG_BUFSIZE:
444       g_value_set_ulong(value, xvidenc->buffer_size);
445       break;
446     case ARG_MAXKEYINTERVAL:
447       g_value_set_int(value, xvidenc->max_key_interval);
448       break;
449     default:
450       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
451       break;
452   }
453 }
454
455
456 gboolean
457 gst_xvidenc_plugin_init (GModule   *module,
458                          GstPlugin *plugin)
459 {
460   GstElementFactory *factory;
461
462   if (!gst_library_load("gstvideo"))
463     return FALSE;
464
465   /* create an elementfactory for the v4lmjpegsrcparse element */
466   factory = gst_element_factory_new("xvidenc", GST_TYPE_XVIDENC,
467                                     &gst_xvidenc_details);
468   g_return_val_if_fail(factory != NULL, FALSE);
469
470   /* add pad templates */
471   gst_element_factory_add_pad_template(factory,
472     GST_PAD_TEMPLATE_GET(sink_template));
473   gst_element_factory_add_pad_template(factory,
474     GST_PAD_TEMPLATE_GET(src_template));
475
476   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
477
478   return TRUE;
479 }