Imported Upstream version 0.10.23
[profile/ivi/gst-plugins-bad.git] / ext / divx / gstdivxdec.c
1 /* GStreamer divx decoder 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 #include "config.h"
22 #endif
23
24 #include <string.h>
25 #include "gstdivxdec.h"
26 #include <gst/video/video.h>
27
28 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
29     GST_PAD_SINK,
30     GST_PAD_ALWAYS,
31     GST_STATIC_CAPS ("video/x-divx, "
32         "divxversion = (int) [ 3, 5 ], "
33         "width = (int) [ 16, 4096 ], "
34         "height = (int) [ 16, 4096 ], " "framerate = (fraction) [0/1, MAX]")
35     );
36
37 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
38     GST_PAD_SRC,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ I420, YUY2, YV12, UYVY }")
41         /* FIXME: 15/16/24/32bpp RGB */
42     )
43     );
44
45
46 /* DivxDec signals and args */
47 enum
48 {
49   /* FILL ME */
50   LAST_SIGNAL
51 };
52
53 enum
54 {
55   ARG_0
56       /* FILL ME */
57 };
58
59
60 static void gst_divxdec_base_init (GstDivxDecClass * klass);
61 static void gst_divxdec_class_init (GstDivxDecClass * klass);
62 static void gst_divxdec_init (GstDivxDec * divxdec);
63 static void gst_divxdec_dispose (GObject * object);
64 static GstFlowReturn gst_divxdec_chain (GstPad * pad, GstBuffer * buf);
65 static gboolean gst_divxdec_connect (GstPad * pad, GstCaps * vscapslist);
66 static gboolean gst_divxdec_negotiate (GstDivxDec * divxdec);
67 static GstStateChangeReturn
68 gst_divxdec_change_state (GstElement * element, GstStateChange transition);
69 static GstElementClass *parent_class = NULL;
70
71 /* static guint gst_divxdec_signals[LAST_SIGNAL] = { 0 }; */
72
73
74 static const gchar *
75 gst_divxdec_error (int errorcode)
76 {
77   const gchar *error;
78
79   switch (errorcode) {
80     case DEC_OK:
81       error = "No error";
82       break;
83     case DEC_MEMORY:
84       error = "Invalid memory";
85       break;
86     case DEC_BAD_FORMAT:
87       error = "Invalid format";
88       break;
89     case DEC_INVALID_ARGUMENT:
90       error = "Invalid argument";
91       break;
92     case DEC_NOT_IMPLEMENTED:
93       error = "Not implemented";
94       break;
95     default:
96       error = "Unknown error";
97       break;
98   }
99
100   return error;
101 }
102
103 GType
104 gst_divxdec_get_type (void)
105 {
106   static GType divxdec_type = 0;
107
108   if (!divxdec_type) {
109     static const GTypeInfo divxdec_info = {
110       sizeof (GstDivxDecClass),
111       (GBaseInitFunc) gst_divxdec_base_init,
112       NULL,
113       (GClassInitFunc) gst_divxdec_class_init,
114       NULL,
115       NULL,
116       sizeof (GstDivxDec),
117       0,
118       (GInstanceInitFunc) gst_divxdec_init,
119     };
120
121     divxdec_type = g_type_register_static (GST_TYPE_ELEMENT,
122         "GstDivxDec", &divxdec_info, 0);
123   }
124   return divxdec_type;
125 }
126
127
128 static void
129 gst_divxdec_base_init (GstDivxDecClass * klass)
130 {
131   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
132
133   gst_element_class_add_static_pad_template (element_class,
134       &sink_template);
135   gst_element_class_add_static_pad_template (element_class, &src_template);
136
137   gst_element_class_set_details_simple (element_class,
138       "Divx4linux video decoder", "Codec/Decoder/Video",
139       "Divx decoder based on divxdecore",
140       "Ronald Bultje <rbultje@ronald.bitfreak.net>");
141 }
142
143
144 static void
145 gst_divxdec_class_init (GstDivxDecClass * klass)
146 {
147   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
148   GObjectClass *gobject_class = (GObjectClass *) klass;
149
150   parent_class = g_type_class_peek_parent (klass);
151
152   gstelement_class->change_state = gst_divxdec_change_state;
153   gobject_class->dispose = gst_divxdec_dispose;
154 }
155
156
157 static void
158 gst_divxdec_init (GstDivxDec * divxdec)
159 {
160   /* create the sink pad */
161   divxdec->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
162   gst_element_add_pad (GST_ELEMENT (divxdec), divxdec->sinkpad);
163   gst_pad_set_chain_function (divxdec->sinkpad, gst_divxdec_chain);
164   gst_pad_set_setcaps_function (divxdec->sinkpad, gst_divxdec_connect);
165
166   /* create the src pad */
167   divxdec->srcpad = gst_pad_new_from_static_template (&src_template, "src");
168   gst_element_add_pad (GST_ELEMENT (divxdec), divxdec->srcpad);
169   gst_pad_use_fixed_caps (divxdec->srcpad);
170
171   /* bitrate, etc. */
172   divxdec->width = divxdec->height = divxdec->csp = divxdec->bitcnt = -1;
173   divxdec->version = 0;
174
175   /* set divx handle to NULL */
176   divxdec->handle = NULL;
177 }
178
179
180 static void
181 gst_divxdec_unset (GstDivxDec * divxdec)
182 {
183   if (divxdec->handle) {
184     /* unref this instance */
185     decore (divxdec->handle, DEC_OPT_RELEASE, NULL, NULL);
186     divxdec->handle = NULL;
187   }
188 }
189
190
191 static gboolean
192 gst_divxdec_setup (GstDivxDec * divxdec)
193 {
194   void *handle;
195   DEC_INIT xinit;
196   DivXBitmapInfoHeader output;
197   int ret;
198
199   /* initialize the handle */
200   memset (&xinit, 0, sizeof (DEC_INIT));
201   xinit.smooth_playback = 0;
202   switch (divxdec->version) {
203     case 3:
204       xinit.codec_version = 311;
205       break;
206     case 4:
207       xinit.codec_version = 400;
208       break;
209     case 5:
210       xinit.codec_version = 500;
211       break;
212     default:
213       xinit.codec_version = 0;
214       break;
215   }
216   if ((ret = decore (&handle, DEC_OPT_INIT, &xinit, NULL)) != 0) {
217     GST_ELEMENT_ERROR (divxdec, LIBRARY, INIT, (NULL),
218         ("divx library error: %s (%d)", gst_divxdec_error (ret), ret));
219     return FALSE;
220   }
221
222   /* we've got a handle now */
223   divxdec->handle = handle;
224
225   /* initialise parameters, see divx documentation */
226   memset (&output, 0, sizeof (DivXBitmapInfoHeader));
227   output.biSize = sizeof (DivXBitmapInfoHeader);
228   output.biWidth = divxdec->width;
229   output.biHeight = divxdec->height;
230   output.biBitCount = divxdec->bitcnt;
231   output.biCompression = divxdec->csp;
232
233   if ((ret = decore (divxdec->handle, DEC_OPT_SETOUT, &output, NULL)) != 0) {
234     GST_ELEMENT_ERROR (divxdec, LIBRARY, SETTINGS, (NULL),
235         ("error setting output: %s (%d)", gst_divxdec_error (ret), ret));
236     gst_divxdec_unset (divxdec);
237     return FALSE;
238   }
239   return TRUE;
240 }
241
242 static void
243 gst_divxdec_dispose (GObject * object)
244 {
245   GstDivxDec *divxdec = GST_DIVXDEC (object);
246
247   gst_divxdec_unset (divxdec);
248   G_OBJECT_CLASS (parent_class)->dispose (object);
249 }
250
251 static GstFlowReturn
252 gst_divxdec_chain (GstPad * pad, GstBuffer * buf)
253 {
254   GstDivxDec *divxdec;
255   GstBuffer *outbuf;
256   DEC_FRAME xframe;
257   int res;
258   GstFlowReturn ret;
259
260   divxdec = GST_DIVXDEC (gst_pad_get_parent (pad));
261   if (!divxdec->handle) {
262     if (gst_divxdec_negotiate (divxdec) <= 0) {
263       goto not_negotiated;
264     }
265   }
266
267   outbuf = gst_buffer_new_and_alloc (divxdec->width *
268       divxdec->height * divxdec->bpp / 8);
269   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
270   GST_BUFFER_SIZE (outbuf) = divxdec->width *
271       divxdec->height * divxdec->bpp / 8;
272
273   /* encode and so ... */
274   xframe.bitstream = (void *) GST_BUFFER_DATA (buf);
275   xframe.bmp = (void *) GST_BUFFER_DATA (outbuf);
276   xframe.length = GST_BUFFER_SIZE (buf);
277   xframe.stride = 0;
278   xframe.render_flag = 1;
279
280   if ((res = decore (divxdec->handle, DEC_OPT_FRAME, &xframe, NULL))) {
281     goto not_decoding;
282   }
283
284   gst_buffer_set_caps (outbuf, GST_PAD_CAPS (divxdec->srcpad));
285   ret = gst_pad_push (divxdec->srcpad, outbuf);
286   goto cleanup;
287
288 not_negotiated:
289   {
290     GST_ELEMENT_ERROR (divxdec, CORE, TOO_LAZY, (NULL),
291         ("No format set - aborting"));
292     ret = GST_FLOW_NOT_NEGOTIATED;
293     goto cleanup;
294   }
295
296 not_decoding:
297   {
298     GST_ELEMENT_ERROR (divxdec, STREAM, DECODE, (NULL),
299         ("Error decoding divx frame: %s (%d)", gst_divxdec_error (res), res));
300     gst_buffer_unref (outbuf);
301     ret = GST_FLOW_ERROR;
302     goto cleanup;
303   }
304
305 cleanup:
306
307   gst_buffer_unref (buf);
308   gst_object_unref (divxdec);
309   return ret;
310
311 }
312
313
314 /* FIXME: moved all the bits out here that are broken so the syntax
315  * stays clear */
316
317 /*
318   {
319   GST_MAKE_FOURCC ('R', 'G', 'B', ' '), 32, 32,
320   #if (G_BYTE_ORDER == G_BIG_ENDIAN)
321   GST_MAKE_FOURCC ('A', 'B', 'G', 'R'), 32}
322
323   ,
324   #else
325   0, 32}
326
327   ,
328   #endif
329   {
330   GST_MAKE_FOURCC ('R', 'G', 'B', ' '), 24, 24,
331   #if (G_BYTE_ORDER == G_BIG_ENDIAN)
332   GST_MAKE_FOURCC ('A', 'B', 'G', 'R'), 24}
333
334   ,
335   #else
336   0, 24}
337
338   ,
339   #endif
340   {
341   GST_MAKE_FOURCC ('R', 'G', 'B', ' '), 16, 16, 3, 16}
342
343   , {
344   GST_MAKE_FOURCC ('R', 'G', 'B', ' '), 15, 16, 0, 16}
345
346   ,
347   #endif
348   if (fmt_list[i].fourcc == GST_MAKE_FOURCC ('R', 'G', 'B', ' ')) {
349   guint32 r_mask = 0, b_mask = 0, g_mask = 0;
350   gint endianness = 0;
351
352   switch (fmt_list[i].depth) {
353   case 15:
354   endianness = G_BYTE_ORDER;
355   r_mask = 0xf800;
356   g_mask = 0x07c0;
357   b_mask = 0x003e;
358   break;
359   case 16:
360   endianness = G_BYTE_ORDER;
361   r_mask = 0xf800;
362   g_mask = 0x07e0;
363   b_mask = 0x001f;
364   break;
365   case 24:
366   endianness = G_BIG_ENDIAN;
367   r_mask = GST_VIDEO_BYTE1_MASK_24_INT;
368   g_mask = GST_VIDEO_BYTE2_MASK_24_INT;
369   b_mask = GST_VIDEO_BYTE3_MASK_24_INT break;
370   case 32:
371   endianness = G_BIG_ENDIAN;
372   r_mask = GST_VIDEO_BYTE1_MASK_32_INT;
373   g_mask = GST_VIDEO_BYTE2_MASK_32_INT;
374   b_mask = GST_VIDEO_BYTE3_MASK_32_INT break;
375   }
376   caps = GST_CAPS_NEW ("divxdec_src_pad_rgb",
377   "video/x-raw-rgb",
378   "width", GST_PROPS_INT (divxdec->width),
379   "height", GST_PROPS_INT (divxdec->height),
380   "framerate", GST_PROPS_FLOAT (divxdec->fps),
381   "depth", GST_PROPS_INT (fmt_list[i].depth),
382   "bpp", GST_PROPS_INT (fmt_list[i].bpp),
383   "endianness", GST_PROPS_INT (endianness),
384   "red_mask", GST_PROPS_INT (r_mask),
385   "green_mask", GST_PROPS_INT (g_mask),
386   "blue_mask", GST_PROPS_INT (b_mask));
387   } else {
388   #endif
389
390   #endif
391 */
392
393 static gboolean
394 gst_divxdec_negotiate (GstDivxDec * divxdec)
395 {
396   GstCaps *caps = NULL;
397   gint i;
398   gint par_num, par_den;
399   gboolean ret = FALSE;
400
401   struct
402   {
403     guint32 fourcc;
404     gint depth, bpp;
405     guint32 csp;
406     gint bitcnt;
407   }
408
409   fmt_list[] = {
410     {
411     GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), 16, 16,
412           GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), 0}
413     , {
414     GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'), 16, 16,
415           GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'), 0}
416     , {
417     GST_MAKE_FOURCC ('I', '4', '2', '0'), 12, 12,
418           GST_MAKE_FOURCC ('I', '4', '2', '0'), 0}
419     , {
420     GST_MAKE_FOURCC ('Y', 'V', '1', '2'), 12, 12,
421           GST_MAKE_FOURCC ('Y', 'V', '1', '2'), 0}
422     , {
423     0, 0, 0, 0, 0}
424   };
425
426   GST_DEBUG_OBJECT (divxdec, "fps %d/%d, PAR %d/%d",
427       divxdec->fps_n, divxdec->fps_d, divxdec->par_n, divxdec->par_d);
428
429   /* calculate par
430    * the info.aspect_* values reflect PAR;
431    * 0:0 is allowed and can be interpreted as 1:1, so correct for it */
432   par_num = divxdec->par_n;
433   par_den = divxdec->par_d;
434   if (par_num == 0 && par_den == 0) {
435     par_num = par_den = 1;
436   }
437
438   for (i = 0; fmt_list[i].fourcc != 0; i++) {
439     divxdec->csp = fmt_list[i].csp;
440
441     caps = gst_caps_new_simple ("video/x-raw-yuv",
442         "width", G_TYPE_INT, divxdec->width,
443         "height", G_TYPE_INT, divxdec->height,
444         "framerate", GST_TYPE_FRACTION, divxdec->fps_n, divxdec->fps_d,
445         "pixel-aspect-ratio", GST_TYPE_FRACTION, par_num, par_den,
446         "format", GST_TYPE_FOURCC, fmt_list[i].fourcc, NULL);
447
448     if (caps) {
449
450       if (gst_divxdec_setup (divxdec) &&
451           gst_pad_set_caps (divxdec->srcpad, caps)) {
452         divxdec->csp = fmt_list[i].csp;
453         divxdec->bpp = fmt_list[i].bpp;
454         divxdec->bitcnt = fmt_list[i].bitcnt;
455         ret = TRUE;
456         goto done;
457       }
458
459       gst_caps_unref (caps);
460       caps = NULL;
461
462     }
463
464   }
465
466   /* if we got here - it's not good */
467
468 done:
469
470   if (caps) {
471     gst_caps_unref (caps);
472   }
473
474   return ret;
475 }
476
477
478 static gboolean
479 gst_divxdec_connect (GstPad * pad, GstCaps * caps)
480 {
481   GstDivxDec *divxdec;
482   const GValue *par;
483   const GValue *fps;
484   gboolean ret = FALSE;
485
486   GstStructure *structure = gst_caps_get_structure (caps, 0);
487
488   divxdec = GST_DIVXDEC (gst_pad_get_parent (pad));
489
490   /* if there's something old around, remove it */
491   if (divxdec->handle) {
492     gst_divxdec_unset (divxdec);
493   }
494
495   /* if we get here, we know the input is divx. we
496    * only need to bother with the output colorspace */
497   gst_structure_get_int (structure, "width", &divxdec->width);
498   gst_structure_get_int (structure, "height", &divxdec->height);
499   gst_structure_get_int (structure, "divxversion", &divxdec->version);
500
501   /* get pixel aspect ratio if it's set */
502   par = gst_structure_get_value (structure, "pixel-aspect-ratio");
503   if (par) {
504     divxdec->par_n = gst_value_get_fraction_numerator (par),
505         divxdec->par_d = gst_value_get_fraction_denominator (par);
506   }
507
508   fps = gst_structure_get_value (structure, "framerate");
509   if (fps != NULL) {
510     divxdec->fps_n = gst_value_get_fraction_numerator (fps);
511     divxdec->fps_d = gst_value_get_fraction_denominator (fps);
512   } else {
513     divxdec->fps_n = -1;
514   }
515
516   ret = gst_divxdec_negotiate (divxdec);
517   gst_object_unref (divxdec);
518
519   return ret;
520 }
521
522 static GstStateChangeReturn
523 gst_divxdec_change_state (GstElement * element, GstStateChange transition)
524 {
525   GstStateChangeReturn ret;
526
527   switch (transition) {
528     case GST_STATE_CHANGE_NULL_TO_READY:
529       break;
530     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
531       break;
532     default:
533       break;
534   }
535
536   ret = parent_class->change_state (element, transition);
537
538   switch (transition) {
539     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
540       break;
541     case GST_STATE_CHANGE_PAUSED_TO_READY:
542       break;
543     case GST_STATE_CHANGE_READY_TO_NULL:
544       break;
545     default:
546       break;
547   }
548
549   return ret;
550 }
551
552
553 static gboolean
554 plugin_init (GstPlugin * plugin)
555 {
556   int lib_version;
557
558   lib_version = decore (NULL, DEC_OPT_VERSION, 0, 0);
559   if (lib_version != DECORE_VERSION) {
560     g_warning ("Version mismatch! This plugin was compiled for "
561         "DivX version %d, while your library has version %d!",
562         DECORE_VERSION, lib_version);
563     return FALSE;
564   }
565
566   return gst_element_register (plugin, "divxdec",
567       GST_RANK_SECONDARY, GST_TYPE_DIVXDEC);
568 }
569
570
571 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
572     GST_VERSION_MINOR,
573     "divxdec",
574     "DivX decoder",
575     plugin_init,
576     "5.03", GST_LICENSE_UNKNOWN, "divx4linux", "http://www.divx.com/");