Add .gbs.conf needed to submit from tizen_ivi_genivi branch
[profile/ivi/gst-ffmpeg0.10.git] / ext / libswscale / gstffmpegscale.c
1 /* GStreamer libswscale wrapper
2  * Copyright (C) 2005 Luca Ognibene <luogni@tin.it>
3  * Copyright (C) 2006 Martin Zlomek <martin.zlomek@itonis.tv>
4  * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #ifdef HAVE_FFMPEG_UNINSTALLED
27 #include <swscale.h>
28 #else
29 #include <libswscale/swscale.h>
30 #endif
31
32 #include <gst/gst.h>
33 #include <gst/base/gstbasetransform.h>
34 #include <gst/video/video.h>
35
36 #ifdef HAVE_ORC
37 #include <orc/orc.h>
38 #endif
39
40 #include <string.h>
41
42 typedef struct _GstFFMpegScale
43 {
44   GstBaseTransform element;
45
46   /* pads */
47   GstPad *sinkpad, *srcpad;
48
49   /* state */
50   gint in_width, in_height;
51   gint out_width, out_height;
52
53   enum PixelFormat in_pixfmt, out_pixfmt;
54   struct SwsContext *ctx;
55
56   /* cached auxiliary data */
57   gint in_stride[3], in_offset[3];
58   gint out_stride[3], out_offset[3];
59
60   /* property */
61   gint method;
62 } GstFFMpegScale;
63
64 typedef struct _GstFFMpegScaleClass
65 {
66   GstBaseTransformClass parent_class;
67 } GstFFMpegScaleClass;
68
69 #define GST_TYPE_FFMPEGSCALE \
70         (gst_ffmpegscale_get_type())
71 #define GST_FFMPEGSCALE(obj) \
72         (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGSCALE,GstFFMpegScale))
73 #define GST_FFMPEGSCALE_CLASS(klass) \
74         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGSCALE,GstFFMpegScaleClass))
75 #define GST_IS_FFMPEGSCALE(obj) \
76         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGSCALE))
77 #define GST_IS_FFMPEGSCALE_CLASS(klass) \
78         (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGSCALE))
79
80 GType gst_ffmpegscale_get_type (void);
81
82 GST_DEBUG_CATEGORY (ffmpegscale_debug);
83 #define GST_CAT_DEFAULT ffmpegscale_debug
84
85 /* libswscale supported formats depend on endianness */
86 #if G_BYTE_ORDER == G_BIG_ENDIAN
87 #define VIDEO_CAPS \
88         GST_VIDEO_CAPS_RGB "; " GST_VIDEO_CAPS_BGR "; " \
89         GST_VIDEO_CAPS_xRGB "; " GST_VIDEO_CAPS_xBGR "; " \
90         GST_VIDEO_CAPS_ARGB "; " GST_VIDEO_CAPS_ABGR "; " \
91         GST_VIDEO_CAPS_YUV ("{ I420, YUY2, Y41B, Y42B }")
92 #else
93 #define VIDEO_CAPS \
94         GST_VIDEO_CAPS_RGB "; " GST_VIDEO_CAPS_BGR "; " \
95         GST_VIDEO_CAPS_RGBx "; " GST_VIDEO_CAPS_BGRx "; " \
96         GST_VIDEO_CAPS_RGBA "; " GST_VIDEO_CAPS_BGRA "; " \
97         GST_VIDEO_CAPS_YUV ("{ I420, YUY2, Y41B, Y42B }")
98 #endif
99
100 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
101     GST_PAD_SRC,
102     GST_PAD_ALWAYS,
103     GST_STATIC_CAPS (VIDEO_CAPS)
104     );
105
106 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
107     GST_PAD_SINK,
108     GST_PAD_ALWAYS,
109     GST_STATIC_CAPS (VIDEO_CAPS)
110     );
111
112 static gint gst_ffmpegscale_method_flags[] = {
113   SWS_FAST_BILINEAR,
114   SWS_BILINEAR,
115   SWS_BICUBIC,
116   SWS_X,
117   SWS_POINT,
118   SWS_AREA,
119   SWS_BICUBLIN,
120   SWS_GAUSS,
121   SWS_SINC,
122   SWS_LANCZOS,
123   SWS_SPLINE,
124 };
125
126 #define GST_TYPE_FFMPEGSCALE_METHOD (gst_ffmpegscale_method_get_type())
127 static GType
128 gst_ffmpegscale_method_get_type (void)
129 {
130   static GType ffmpegscale_method_type = 0;
131
132   static const GEnumValue ffmpegscale_methods[] = {
133     {0, "Fast Bilinear", "fast-bilinear"},
134     {1, "Bilinear", "bilinear"},
135     {2, "Bicubic", "bicubic"},
136     {3, "Experimental", "experimental"},
137     {4, "Nearest Neighbour", "nearest-neighbour"},
138     {5, "Area", "area"},
139     {6, "Luma Bicubic / Chroma Linear", "bicubic-lin"},
140     {7, "Gauss", "gauss"},
141     {8, "SincR", "sincr"},
142     {9, "Lanczos", "lanczos"},
143     {10, "Natural Bicubic Spline", "bicubic-spline"},
144     {0, NULL, NULL},
145   };
146
147   if (!ffmpegscale_method_type) {
148     ffmpegscale_method_type =
149         g_enum_register_static ("GstFFMpegVideoScaleMethod",
150         ffmpegscale_methods);
151   }
152   return ffmpegscale_method_type;
153 }
154
155 #define DEFAULT_PROP_METHOD    2
156
157 enum
158 {
159   PROP_0,
160   PROP_METHOD
161       /* FILL ME */
162 };
163
164 GST_BOILERPLATE (GstFFMpegScale, gst_ffmpegscale, GstBaseTransform,
165     GST_TYPE_BASE_TRANSFORM);
166
167 static void gst_ffmpegscale_finalize (GObject * object);
168 static void gst_ffmpegscale_set_property (GObject * object, guint prop_id,
169     const GValue * value, GParamSpec * pspec);
170 static void gst_ffmpegscale_get_property (GObject * object, guint prop_id,
171     GValue * value, GParamSpec * pspec);
172
173 static gboolean gst_ffmpegscale_stop (GstBaseTransform * trans);
174 static GstCaps *gst_ffmpegscale_transform_caps (GstBaseTransform * trans,
175     GstPadDirection direction, GstCaps * caps);
176 static void gst_ffmpegscale_fixate_caps (GstBaseTransform * trans,
177     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
178 static gboolean gst_ffmpegscale_get_unit_size (GstBaseTransform * trans,
179     GstCaps * caps, guint * size);
180 static gboolean gst_ffmpegscale_set_caps (GstBaseTransform * trans,
181     GstCaps * incaps, GstCaps * outcaps);
182 static GstFlowReturn gst_ffmpegscale_transform (GstBaseTransform * trans,
183     GstBuffer * inbuf, GstBuffer * outbuf);
184
185 static gboolean gst_ffmpegscale_handle_src_event (GstPad * pad,
186     GstEvent * event);
187
188 static void
189 gst_ffmpegscale_base_init (gpointer g_class)
190 {
191   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
192
193   gst_element_class_add_pad_template (element_class,
194       gst_static_pad_template_get (&src_factory));
195   gst_element_class_add_pad_template (element_class,
196       gst_static_pad_template_get (&sink_factory));
197   gst_element_class_set_details_simple (element_class, "FFMPEG Scale element",
198       "Filter/Converter/Video",
199       "Converts video from one resolution to another",
200       "Luca Ognibene <luogni@tin.it>, Mark Nauwelaerts <mnauw@users.sf.net>");
201 }
202
203 static void
204 gst_ffmpegscale_class_init (GstFFMpegScaleClass * klass)
205 {
206   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
207   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
208
209   gobject_class->finalize = gst_ffmpegscale_finalize;
210   gobject_class->set_property = gst_ffmpegscale_set_property;
211   gobject_class->get_property = gst_ffmpegscale_get_property;
212
213   g_object_class_install_property (gobject_class, PROP_METHOD,
214       g_param_spec_enum ("method", "method", "method",
215           GST_TYPE_FFMPEGSCALE_METHOD, DEFAULT_PROP_METHOD,
216           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217
218   trans_class->stop = GST_DEBUG_FUNCPTR (gst_ffmpegscale_stop);
219   trans_class->transform_caps =
220       GST_DEBUG_FUNCPTR (gst_ffmpegscale_transform_caps);
221   trans_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_ffmpegscale_fixate_caps);
222   trans_class->get_unit_size =
223       GST_DEBUG_FUNCPTR (gst_ffmpegscale_get_unit_size);
224   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_ffmpegscale_set_caps);
225   trans_class->transform = GST_DEBUG_FUNCPTR (gst_ffmpegscale_transform);
226
227   trans_class->passthrough_on_same_caps = TRUE;
228 }
229
230 static void
231 gst_ffmpegscale_init (GstFFMpegScale * scale, GstFFMpegScaleClass * klass)
232 {
233   GstBaseTransform *trans = GST_BASE_TRANSFORM (scale);
234
235   gst_pad_set_event_function (trans->srcpad, gst_ffmpegscale_handle_src_event);
236
237   scale->method = DEFAULT_PROP_METHOD;
238   scale->ctx = NULL;
239   scale->in_pixfmt = PIX_FMT_NONE;
240   scale->out_pixfmt = PIX_FMT_NONE;
241 }
242
243 static void
244 gst_ffmpegscale_reset (GstFFMpegScale * scale)
245 {
246   if (scale->ctx != NULL) {
247     sws_freeContext (scale->ctx);
248     scale->ctx = NULL;
249   }
250
251   scale->in_pixfmt = PIX_FMT_NONE;
252   scale->out_pixfmt = PIX_FMT_NONE;
253 }
254
255 static void
256 gst_ffmpegscale_finalize (GObject * object)
257 {
258   GstFFMpegScale *scale = GST_FFMPEGSCALE (object);
259
260   gst_ffmpegscale_reset (scale);
261
262   G_OBJECT_CLASS (parent_class)->finalize (object);
263 }
264
265 /* copies the given caps */
266 static GstCaps *
267 gst_ffmpegscale_caps_remove_format_info (GstCaps * caps)
268 {
269   int i;
270   GstStructure *structure;
271   GstCaps *rgbcaps;
272   GstCaps *graycaps;
273
274   caps = gst_caps_copy (caps);
275
276   for (i = 0; i < gst_caps_get_size (caps); i++) {
277     structure = gst_caps_get_structure (caps, i);
278
279     gst_structure_set_name (structure, "video/x-raw-yuv");
280     gst_structure_remove_field (structure, "format");
281     gst_structure_remove_field (structure, "endianness");
282     gst_structure_remove_field (structure, "depth");
283     gst_structure_remove_field (structure, "bpp");
284     gst_structure_remove_field (structure, "red_mask");
285     gst_structure_remove_field (structure, "green_mask");
286     gst_structure_remove_field (structure, "blue_mask");
287     gst_structure_remove_field (structure, "alpha_mask");
288     gst_structure_remove_field (structure, "palette_data");
289   }
290
291   rgbcaps = gst_caps_copy (caps);
292
293   for (i = 0; i < gst_caps_get_size (rgbcaps); i++) {
294     structure = gst_caps_get_structure (rgbcaps, i);
295
296     gst_structure_set_name (structure, "video/x-raw-rgb");
297   }
298   graycaps = gst_caps_copy (caps);
299
300   for (i = 0; i < gst_caps_get_size (graycaps); i++) {
301     structure = gst_caps_get_structure (graycaps, i);
302
303     gst_structure_set_name (structure, "video/x-raw-gray");
304   }
305
306   gst_caps_append (caps, graycaps);
307   gst_caps_append (caps, rgbcaps);
308
309   return caps;
310 }
311
312 static GstCaps *
313 gst_ffmpegscale_transform_caps (GstBaseTransform * trans,
314     GstPadDirection direction, GstCaps * caps)
315 {
316   GstFFMpegScale *scale;
317   GstCaps *ret;
318   GstStructure *structure;
319   const GValue *par;
320
321   /* this function is always called with a simple caps */
322   g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL);
323
324   scale = GST_FFMPEGSCALE (trans);
325
326   structure = gst_caps_get_structure (caps, 0);
327
328   ret = gst_caps_copy (caps);
329   structure = gst_structure_copy (gst_caps_get_structure (ret, 0));
330
331   gst_structure_set (structure,
332       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
333       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
334
335   gst_caps_merge_structure (ret, gst_structure_copy (structure));
336
337   /* if pixel aspect ratio, make a range of it */
338   if ((par = gst_structure_get_value (structure, "pixel-aspect-ratio"))) {
339     gst_structure_set (structure,
340         "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
341
342     gst_caps_merge_structure (ret, structure);
343   } else {
344     gst_structure_free (structure);
345   }
346
347   /* now also unfix colour space format */
348   gst_caps_append (ret, gst_ffmpegscale_caps_remove_format_info (ret));
349
350   GST_DEBUG_OBJECT (trans, "returning caps: %" GST_PTR_FORMAT, ret);
351
352   return ret;
353 }
354
355 static void
356 gst_ffmpegscale_fixate_caps (GstBaseTransform * trans,
357     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
358 {
359   GstStructure *ins, *outs;
360   const GValue *from_par, *to_par;
361
362   g_return_if_fail (gst_caps_is_fixed (caps));
363
364   GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
365       " based on caps %" GST_PTR_FORMAT, othercaps, caps);
366
367   ins = gst_caps_get_structure (caps, 0);
368   outs = gst_caps_get_structure (othercaps, 0);
369
370   from_par = gst_structure_get_value (ins, "pixel-aspect-ratio");
371   to_par = gst_structure_get_value (outs, "pixel-aspect-ratio");
372
373   /* we have both PAR but they might not be fixated */
374   if (from_par && to_par) {
375     gint from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d;
376     gint count = 0, w = 0, h = 0;
377     guint num, den;
378
379     /* from_par should be fixed */
380     g_return_if_fail (gst_value_is_fixed (from_par));
381
382     from_par_n = gst_value_get_fraction_numerator (from_par);
383     from_par_d = gst_value_get_fraction_denominator (from_par);
384
385     /* fixate the out PAR */
386     if (!gst_value_is_fixed (to_par)) {
387       GST_DEBUG_OBJECT (trans, "fixating to_par to %dx%d", from_par_n,
388           from_par_d);
389       gst_structure_fixate_field_nearest_fraction (outs, "pixel-aspect-ratio",
390           from_par_n, from_par_d);
391     }
392
393     to_par_n = gst_value_get_fraction_numerator (to_par);
394     to_par_d = gst_value_get_fraction_denominator (to_par);
395
396     /* if both width and height are already fixed, we can't do anything
397      * about it anymore */
398     if (gst_structure_get_int (outs, "width", &w))
399       ++count;
400     if (gst_structure_get_int (outs, "height", &h))
401       ++count;
402     if (count == 2) {
403       GST_DEBUG_OBJECT (trans, "dimensions already set to %dx%d, not fixating",
404           w, h);
405       return;
406     }
407
408     gst_structure_get_int (ins, "width", &from_w);
409     gst_structure_get_int (ins, "height", &from_h);
410
411     if (!gst_video_calculate_display_ratio (&num, &den, from_w, from_h,
412             from_par_n, from_par_d, to_par_n, to_par_d)) {
413       GST_ELEMENT_ERROR (trans, CORE, NEGOTIATION, (NULL),
414           ("Error calculating the output scaled size - integer overflow"));
415       return;
416     }
417
418     GST_DEBUG_OBJECT (trans,
419         "scaling input with %dx%d and PAR %d/%d to output PAR %d/%d",
420         from_w, from_h, from_par_n, from_par_d, to_par_n, to_par_d);
421     GST_DEBUG_OBJECT (trans, "resulting output should respect ratio of %d/%d",
422         num, den);
423
424     /* now find a width x height that respects this display ratio.
425      * prefer those that have one of w/h the same as the incoming video
426      * using wd / hd = num / den */
427
428     /* if one of the output width or height is fixed, we work from there */
429     if (h) {
430       GST_DEBUG_OBJECT (trans, "height is fixed,scaling width");
431       w = (guint) gst_util_uint64_scale_int (h, num, den);
432     } else if (w) {
433       GST_DEBUG_OBJECT (trans, "width is fixed, scaling height");
434       h = (guint) gst_util_uint64_scale_int (w, den, num);
435     } else {
436       /* none of width or height is fixed, figure out both of them based only on
437        * the input width and height */
438       /* check hd / den is an integer scale factor, and scale wd with the PAR */
439       if (from_h % den == 0) {
440         GST_DEBUG_OBJECT (trans, "keeping video height");
441         h = from_h;
442         w = (guint) gst_util_uint64_scale_int (h, num, den);
443       } else if (from_w % num == 0) {
444         GST_DEBUG_OBJECT (trans, "keeping video width");
445         w = from_w;
446         h = (guint) gst_util_uint64_scale_int (w, den, num);
447       } else {
448         GST_DEBUG_OBJECT (trans, "approximating but keeping video height");
449         h = from_h;
450         w = (guint) gst_util_uint64_scale_int (h, num, den);
451       }
452     }
453     GST_DEBUG_OBJECT (trans, "scaling to %dx%d", w, h);
454
455     /* now fixate */
456     gst_structure_fixate_field_nearest_int (outs, "width", w);
457     gst_structure_fixate_field_nearest_int (outs, "height", h);
458   } else {
459     gint width, height;
460
461     if (gst_structure_get_int (ins, "width", &width)) {
462       if (gst_structure_has_field (outs, "width")) {
463         gst_structure_fixate_field_nearest_int (outs, "width", width);
464       }
465     }
466     if (gst_structure_get_int (ins, "height", &height)) {
467       if (gst_structure_has_field (outs, "height")) {
468         gst_structure_fixate_field_nearest_int (outs, "height", height);
469       }
470     }
471   }
472
473   GST_DEBUG_OBJECT (trans, "fixated othercaps to %" GST_PTR_FORMAT, othercaps);
474 }
475
476 static gboolean
477 gst_ffmpegscale_get_unit_size (GstBaseTransform * trans, GstCaps * caps,
478     guint * size)
479 {
480   gint width, height;
481   GstVideoFormat format;
482
483   if (!gst_video_format_parse_caps (caps, &format, &width, &height))
484     return FALSE;
485
486   *size = gst_video_format_get_size (format, width, height);
487
488   GST_DEBUG_OBJECT (trans, "unit size = %d for format %d w %d height %d",
489       *size, format, width, height);
490
491   return TRUE;
492 }
493
494 /* Convert a GstCaps (video/raw) to a FFMPEG PixFmt
495  */
496 static enum PixelFormat
497 gst_ffmpeg_caps_to_pixfmt (const GstCaps * caps)
498 {
499   GstStructure *structure;
500   enum PixelFormat pix_fmt = PIX_FMT_NONE;
501
502   GST_DEBUG ("converting caps %" GST_PTR_FORMAT, caps);
503   g_return_val_if_fail (gst_caps_get_size (caps) == 1, PIX_FMT_NONE);
504   structure = gst_caps_get_structure (caps, 0);
505
506   if (strcmp (gst_structure_get_name (structure), "video/x-raw-yuv") == 0) {
507     guint32 fourcc;
508
509     if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
510       switch (fourcc) {
511         case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
512           pix_fmt = PIX_FMT_YUYV422;
513           break;
514         case GST_MAKE_FOURCC ('I', '4', '2', '0'):
515           pix_fmt = PIX_FMT_YUV420P;
516           break;
517         case GST_MAKE_FOURCC ('Y', '4', '1', 'B'):
518           pix_fmt = PIX_FMT_YUV411P;
519           break;
520         case GST_MAKE_FOURCC ('Y', '4', '2', 'B'):
521           pix_fmt = PIX_FMT_YUV422P;
522           break;
523         case GST_MAKE_FOURCC ('Y', 'U', 'V', '9'):
524           pix_fmt = PIX_FMT_YUV410P;
525           break;
526       }
527     }
528   } else if (strcmp (gst_structure_get_name (structure),
529           "video/x-raw-rgb") == 0) {
530     gint bpp = 0, rmask = 0, endianness = 0;
531
532     if (gst_structure_get_int (structure, "bpp", &bpp) &&
533         gst_structure_get_int (structure, "endianness", &endianness) &&
534         endianness == G_BIG_ENDIAN) {
535       if (gst_structure_get_int (structure, "red_mask", &rmask)) {
536         switch (bpp) {
537           case 32:
538             if (rmask == 0x00ff0000)
539               pix_fmt = PIX_FMT_ARGB;
540             else if (rmask == 0xff000000)
541               pix_fmt = PIX_FMT_RGBA;
542             else if (rmask == 0xff00)
543               pix_fmt = PIX_FMT_BGRA;
544             else if (rmask == 0xff)
545               pix_fmt = PIX_FMT_ABGR;
546             break;
547           case 24:
548             if (rmask == 0x0000FF)
549               pix_fmt = PIX_FMT_BGR24;
550             else
551               pix_fmt = PIX_FMT_RGB24;
552             break;
553           case 16:
554             if (endianness == G_BYTE_ORDER)
555               pix_fmt = PIX_FMT_RGB565;
556             break;
557           case 15:
558             if (endianness == G_BYTE_ORDER)
559               pix_fmt = PIX_FMT_RGB555;
560             break;
561           default:
562             /* nothing */
563             break;
564         }
565       } else {
566         if (bpp == 8) {
567           pix_fmt = PIX_FMT_PAL8;
568         }
569       }
570     }
571   }
572
573   return pix_fmt;
574 }
575
576 static void
577 gst_ffmpegscale_fill_info (GstFFMpegScale * scale, GstVideoFormat format,
578     guint width, guint height, gint stride[], gint offset[])
579 {
580   gint i;
581
582   for (i = 0; i < 3; i++) {
583     stride[i] = gst_video_format_get_row_stride (format, i, width);
584     offset[i] = gst_video_format_get_component_offset (format, i, width,
585         height);
586     /* stay close to the ffmpeg offset way */
587     if (offset[i] < 3)
588       offset[i] = 0;
589     GST_DEBUG_OBJECT (scale, "format %d, component %d; stride %d, offset %d",
590         format, i, stride[i], offset[i]);
591   }
592 }
593
594 static gboolean
595 gst_ffmpegscale_set_caps (GstBaseTransform * trans, GstCaps * incaps,
596     GstCaps * outcaps)
597 {
598   GstFFMpegScale *scale = GST_FFMPEGSCALE (trans);
599   guint mmx_flags, altivec_flags;
600   gint swsflags;
601   GstVideoFormat in_format, out_format;
602   gboolean ok;
603
604   g_return_val_if_fail (scale->method <
605       G_N_ELEMENTS (gst_ffmpegscale_method_flags), FALSE);
606
607   if (scale->ctx) {
608     sws_freeContext (scale->ctx);
609     scale->ctx = NULL;
610   }
611
612   ok = gst_video_format_parse_caps (incaps, &in_format, &scale->in_width,
613       &scale->in_height);
614   ok &= gst_video_format_parse_caps (outcaps, &out_format, &scale->out_width,
615       &scale->out_height);
616   scale->in_pixfmt = gst_ffmpeg_caps_to_pixfmt (incaps);
617   scale->out_pixfmt = gst_ffmpeg_caps_to_pixfmt (outcaps);
618
619   if (!ok || scale->in_pixfmt == PIX_FMT_NONE ||
620       scale->out_pixfmt == PIX_FMT_NONE ||
621       in_format == GST_VIDEO_FORMAT_UNKNOWN ||
622       out_format == GST_VIDEO_FORMAT_UNKNOWN)
623     goto refuse_caps;
624
625   GST_DEBUG_OBJECT (scale, "format %d => %d, from=%dx%d -> to=%dx%d", in_format,
626       out_format, scale->in_width, scale->in_height, scale->out_width,
627       scale->out_height);
628
629   gst_ffmpegscale_fill_info (scale, in_format, scale->in_width,
630       scale->in_height, scale->in_stride, scale->in_offset);
631   gst_ffmpegscale_fill_info (scale, out_format, scale->out_width,
632       scale->out_height, scale->out_stride, scale->out_offset);
633
634 #ifdef HAVE_ORC
635   mmx_flags = orc_target_get_default_flags (orc_target_get_by_name ("mmx"));
636   altivec_flags =
637       orc_target_get_default_flags (orc_target_get_by_name ("altivec"));
638   swsflags = (mmx_flags & ORC_TARGET_MMX_MMX ? SWS_CPU_CAPS_MMX : 0)
639       | (mmx_flags & ORC_TARGET_MMX_MMXEXT ? SWS_CPU_CAPS_MMX2 : 0)
640       | (mmx_flags & ORC_TARGET_MMX_3DNOW ? SWS_CPU_CAPS_3DNOW : 0)
641       | (altivec_flags & ORC_TARGET_ALTIVEC_ALTIVEC ? SWS_CPU_CAPS_ALTIVEC : 0);
642 #else
643   mmx_flags = 0;
644   altivec_flags = 0;
645   swsflags = 0;
646 #endif
647
648
649   scale->ctx = sws_getContext (scale->in_width, scale->in_height,
650       scale->in_pixfmt, scale->out_width, scale->out_height, scale->out_pixfmt,
651       swsflags | gst_ffmpegscale_method_flags[scale->method], NULL, NULL, NULL);
652   if (!scale->ctx)
653     goto setup_failed;
654
655   return TRUE;
656
657   /* ERRORS */
658 setup_failed:
659   {
660     GST_ELEMENT_ERROR (trans, LIBRARY, INIT, (NULL), (NULL));
661     return FALSE;
662   }
663 refuse_caps:
664   {
665     GST_DEBUG_OBJECT (trans, "refused caps %" GST_PTR_FORMAT, incaps);
666     return FALSE;
667   }
668 }
669
670 static GstFlowReturn
671 gst_ffmpegscale_transform (GstBaseTransform * trans, GstBuffer * inbuf,
672     GstBuffer * outbuf)
673 {
674   GstFFMpegScale *scale = GST_FFMPEGSCALE (trans);
675   guint8 *in_data[3] = { NULL, NULL, NULL };
676   guint8 *out_data[3] = { NULL, NULL, NULL };
677   gint i;
678
679   for (i = 0; i < 3; i++) {
680     /* again; stay close to the ffmpeg offset way */
681     if (!i || scale->in_offset[i])
682       in_data[i] = GST_BUFFER_DATA (inbuf) + scale->in_offset[i];
683     if (!i || scale->out_offset[i])
684       out_data[i] = GST_BUFFER_DATA (outbuf) + scale->out_offset[i];
685   }
686
687   sws_scale (scale->ctx, (const guint8 **) in_data, scale->in_stride, 0,
688       scale->in_height, out_data, scale->out_stride);
689
690   return GST_FLOW_OK;
691 }
692
693 static gboolean
694 gst_ffmpegscale_handle_src_event (GstPad * pad, GstEvent * event)
695 {
696   GstFFMpegScale *scale;
697   GstStructure *structure;
698   gdouble pointer;
699   gboolean res;
700
701   scale = GST_FFMPEGSCALE (gst_pad_get_parent (pad));
702
703   switch (GST_EVENT_TYPE (event)) {
704     case GST_EVENT_NAVIGATION:
705       event =
706           GST_EVENT (gst_mini_object_make_writable (GST_MINI_OBJECT (event)));
707
708       structure = (GstStructure *) gst_event_get_structure (event);
709       if (gst_structure_get_double (structure, "pointer_x", &pointer)) {
710         gst_structure_set (structure,
711             "pointer_x", G_TYPE_DOUBLE,
712             pointer * scale->in_width / scale->out_width, NULL);
713       }
714       if (gst_structure_get_double (structure, "pointer_y", &pointer)) {
715         gst_structure_set (structure,
716             "pointer_y", G_TYPE_DOUBLE,
717             pointer * scale->in_height / scale->out_height, NULL);
718       }
719       break;
720     default:
721       break;
722   }
723
724   res = gst_pad_event_default (pad, event);
725
726   gst_object_unref (scale);
727
728   return res;
729 }
730
731 static gboolean
732 gst_ffmpegscale_stop (GstBaseTransform * trans)
733 {
734   GstFFMpegScale *scale = GST_FFMPEGSCALE (trans);
735
736   gst_ffmpegscale_reset (scale);
737
738   return TRUE;
739 }
740
741 static void
742 gst_ffmpegscale_set_property (GObject * object, guint prop_id,
743     const GValue * value, GParamSpec * pspec)
744 {
745   GstFFMpegScale *scale = GST_FFMPEGSCALE (object);
746
747   switch (prop_id) {
748     case PROP_METHOD:
749       scale->method = g_value_get_enum (value);
750       break;
751     default:
752       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
753       break;
754   }
755 }
756
757 static void
758 gst_ffmpegscale_get_property (GObject * object, guint prop_id, GValue * value,
759     GParamSpec * pspec)
760 {
761   GstFFMpegScale *scale = GST_FFMPEGSCALE (object);
762
763   switch (prop_id) {
764     case PROP_METHOD:
765       g_value_set_enum (value, scale->method);
766       break;
767     default:
768       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
769       break;
770   }
771 }
772
773 #ifndef GST_DISABLE_GST_DEBUG
774 static void
775 gst_ffmpeg_log_callback (void *ptr, int level, const char *fmt, va_list vl)
776 {
777   GstDebugLevel gst_level;
778
779   switch (level) {
780     case AV_LOG_QUIET:
781       gst_level = GST_LEVEL_NONE;
782       break;
783     case AV_LOG_ERROR:
784       gst_level = GST_LEVEL_ERROR;
785       break;
786     case AV_LOG_INFO:
787       gst_level = GST_LEVEL_INFO;
788       break;
789     case AV_LOG_DEBUG:
790       gst_level = GST_LEVEL_DEBUG;
791       break;
792     default:
793       gst_level = GST_LEVEL_INFO;
794       break;
795   }
796
797   gst_debug_log_valist (ffmpegscale_debug, gst_level, "", "", 0, NULL, fmt, vl);
798 }
799 #endif
800
801 static gboolean
802 plugin_init (GstPlugin * plugin)
803 {
804   GST_DEBUG_CATEGORY_INIT (ffmpegscale_debug, "ffvideoscale", 0,
805       "video scaling element");
806
807 #ifdef HAVE_ORC
808   orc_init ();
809 #endif
810
811 #ifndef GST_DISABLE_GST_DEBUG
812   av_log_set_callback (gst_ffmpeg_log_callback);
813 #endif
814
815   return gst_element_register (plugin, "ffvideoscale",
816       GST_RANK_NONE, GST_TYPE_FFMPEGSCALE);
817 }
818
819 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
820     GST_VERSION_MINOR,
821     "ffvideoscale",
822     "videoscaling element (" FFMPEG_SOURCE ")",
823     plugin_init,
824     PACKAGE_VERSION, "GPL", "FFMpeg", "http://ffmpeg.sourceforge.net/")