Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / gst / videocrop / gstaspectratiocrop.c
1 /* GStreamer video frame cropping to aspect-ratio
2  * Copyright (C) 2009 Thijs Vermeir <thijsvermeir@gmail.com>
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 /**
21  * SECTION:element-aspectratiocrop
22  * @see_also: #GstVideoCrop
23  *
24  * This element crops video frames to a specified #GstAspectRatioCrop:aspect-ratio.
25  *
26  * If the aspect-ratio is already correct, the element will operate
27  * in pass-through mode.
28  *
29  * <refsect2>
30  * <title>Example launch line</title>
31  * |[
32  * gst-launch -v videotestsrc ! video/x-raw-rgb,height=640,width=480 ! aspectratiocrop aspect-ratio=16/9 ! ximagesink
33  * ]| This pipeline generates a videostream in 4/3 and crops it to 16/9.
34  * </refsect2>
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <gst/gst.h>
42 #include <gst/video/video.h>
43
44 #include "gstaspectratiocrop.h"
45
46 #include "gst/glib-compat-private.h"
47
48 GST_DEBUG_CATEGORY_STATIC (aspect_ratio_crop_debug);
49 #define GST_CAT_DEFAULT aspect_ratio_crop_debug
50
51 enum
52 {
53   ARG_0,
54   ARG_ASPECT_RATIO_CROP,
55 };
56
57 /* we support the same caps as videocrop */
58 #define ASPECT_RATIO_CROP_CAPS                          \
59   GST_VIDEO_CAPS_RGBx ";"                        \
60   GST_VIDEO_CAPS_xRGB ";"                        \
61   GST_VIDEO_CAPS_BGRx ";"                        \
62   GST_VIDEO_CAPS_xBGR ";"                        \
63   GST_VIDEO_CAPS_RGBA ";"                        \
64   GST_VIDEO_CAPS_ARGB ";"                        \
65   GST_VIDEO_CAPS_BGRA ";"                        \
66   GST_VIDEO_CAPS_ABGR ";"                        \
67   GST_VIDEO_CAPS_RGB ";"                         \
68   GST_VIDEO_CAPS_BGR ";"                         \
69   GST_VIDEO_CAPS_YUV ("AYUV") ";"                \
70   GST_VIDEO_CAPS_YUV ("YUY2") ";"                \
71   GST_VIDEO_CAPS_YUV ("YVYU") ";"                \
72   GST_VIDEO_CAPS_YUV ("UYVY") ";"                \
73   GST_VIDEO_CAPS_YUV ("Y800") ";"                \
74   GST_VIDEO_CAPS_YUV ("I420") ";"                \
75   GST_VIDEO_CAPS_YUV ("YV12") ";"                \
76   GST_VIDEO_CAPS_RGB_16 ";"                      \
77   GST_VIDEO_CAPS_RGB_15
78
79 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
80     GST_PAD_SRC,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS (ASPECT_RATIO_CROP_CAPS)
83     );
84
85 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
86     GST_PAD_SINK,
87     GST_PAD_ALWAYS,
88     GST_STATIC_CAPS (ASPECT_RATIO_CROP_CAPS)
89     );
90
91 GST_BOILERPLATE (GstAspectRatioCrop, gst_aspect_ratio_crop, GstBin,
92     GST_TYPE_BIN);
93
94 static void gst_aspect_ratio_crop_set_property (GObject * object, guint prop_id,
95     const GValue * value, GParamSpec * pspec);
96 static void gst_aspect_ratio_crop_get_property (GObject * object, guint prop_id,
97     GValue * value, GParamSpec * pspec);
98 static void gst_aspect_ratio_crop_set_cropping (GstAspectRatioCrop *
99     aspect_ratio_crop, gint top, gint right, gint bottom, gint left);
100 static GstCaps *gst_aspect_ratio_crop_get_caps (GstPad * pad);
101 static gboolean gst_aspect_ratio_crop_set_caps (GstPad * pad, GstCaps * caps);
102 static void gst_aspect_ratio_crop_finalize (GObject * object);
103 static void gst_aspect_ratio_transform_structure (GstAspectRatioCrop *
104     aspect_ratio_crop, GstStructure * structure, GstStructure ** new_structure,
105     gboolean set_videocrop);
106
107 static void
108 gst_aspect_ratio_crop_set_cropping (GstAspectRatioCrop * aspect_ratio_crop,
109     gint top, gint right, gint bottom, gint left)
110 {
111   GValue value = { 0 };
112   if (G_UNLIKELY (!aspect_ratio_crop->videocrop)) {
113     GST_WARNING_OBJECT (aspect_ratio_crop,
114         "Can't set the settings if there is no cropping element");
115     return;
116   }
117
118   g_value_init (&value, G_TYPE_INT);
119   g_value_set_int (&value, top);
120   GST_DEBUG_OBJECT (aspect_ratio_crop, "set top cropping to: %d", top);
121   g_object_set_property (G_OBJECT (aspect_ratio_crop->videocrop), "top",
122       &value);
123   g_value_set_int (&value, right);
124   GST_DEBUG_OBJECT (aspect_ratio_crop, "set right cropping to: %d", right);
125   g_object_set_property (G_OBJECT (aspect_ratio_crop->videocrop), "right",
126       &value);
127   g_value_set_int (&value, bottom);
128   GST_DEBUG_OBJECT (aspect_ratio_crop, "set bottom cropping to: %d", bottom);
129   g_object_set_property (G_OBJECT (aspect_ratio_crop->videocrop), "bottom",
130       &value);
131   g_value_set_int (&value, left);
132   GST_DEBUG_OBJECT (aspect_ratio_crop, "set left cropping to: %d", left);
133   g_object_set_property (G_OBJECT (aspect_ratio_crop->videocrop), "left",
134       &value);
135
136   g_value_unset (&value);
137 }
138
139 static gboolean
140 gst_aspect_ratio_crop_set_caps (GstPad * pad, GstCaps * caps)
141 {
142   GstAspectRatioCrop *aspect_ratio_crop;
143   GstPad *peer_pad;
144   GstStructure *structure;
145   gboolean ret;
146
147   aspect_ratio_crop = GST_ASPECT_RATIO_CROP (gst_pad_get_parent (pad));
148
149   g_mutex_lock (aspect_ratio_crop->crop_lock);
150
151   structure = gst_caps_get_structure (caps, 0);
152   gst_aspect_ratio_transform_structure (aspect_ratio_crop, structure, NULL,
153       TRUE);
154   peer_pad =
155       gst_element_get_static_pad (GST_ELEMENT (aspect_ratio_crop->videocrop),
156       "sink");
157   ret = gst_pad_set_caps (peer_pad, caps);
158   gst_object_unref (peer_pad);
159   gst_object_unref (aspect_ratio_crop);
160   g_mutex_unlock (aspect_ratio_crop->crop_lock);
161   return ret;
162 }
163
164 static void
165 gst_aspect_ratio_crop_base_init (gpointer g_class)
166 {
167   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
168
169   gst_element_class_set_details_simple (element_class, "aspectratiocrop",
170       "Filter/Effect/Video",
171       "Crops video into a user-defined aspect-ratio",
172       "Thijs Vermeir <thijsvermeir@gmail.com>");
173
174   gst_element_class_add_static_pad_template (element_class, &sink_template);
175   gst_element_class_add_static_pad_template (element_class, &src_template);
176 }
177
178 static void
179 gst_aspect_ratio_crop_class_init (GstAspectRatioCropClass * klass)
180 {
181   GObjectClass *gobject_class;
182
183   gobject_class = (GObjectClass *) klass;
184
185   gobject_class->set_property = gst_aspect_ratio_crop_set_property;
186   gobject_class->get_property = gst_aspect_ratio_crop_get_property;
187   gobject_class->finalize = gst_aspect_ratio_crop_finalize;
188
189   g_object_class_install_property (gobject_class, ARG_ASPECT_RATIO_CROP,
190       gst_param_spec_fraction ("aspect-ratio", "aspect-ratio",
191           "Target aspect-ratio of video", 0, 1, G_MAXINT, 1, 0, 1,
192           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
193 }
194
195 static void
196 gst_aspect_ratio_crop_finalize (GObject * object)
197 {
198   GstAspectRatioCrop *aspect_ratio_crop;
199
200   aspect_ratio_crop = GST_ASPECT_RATIO_CROP (object);
201
202   if (aspect_ratio_crop->crop_lock)
203     g_mutex_free (aspect_ratio_crop->crop_lock);
204
205   G_OBJECT_CLASS (parent_class)->finalize (object);
206 }
207
208 static void
209 gst_aspect_ratio_crop_init (GstAspectRatioCrop * aspect_ratio_crop,
210     GstAspectRatioCropClass * klass)
211 {
212   GstPad *link_pad;
213   GstPad *src_pad;
214
215   GST_DEBUG_CATEGORY_INIT (aspect_ratio_crop_debug, "aspectratiocrop", 0,
216       "aspectratiocrop");
217
218   aspect_ratio_crop->ar_num = 0;
219   aspect_ratio_crop->ar_denom = 1;
220
221   aspect_ratio_crop->crop_lock = g_mutex_new ();
222
223   /* add the transform element */
224   aspect_ratio_crop->videocrop = gst_element_factory_make ("videocrop", NULL);
225   gst_bin_add (GST_BIN (aspect_ratio_crop), aspect_ratio_crop->videocrop);
226
227   /* create ghost pad src */
228   link_pad =
229       gst_element_get_static_pad (GST_ELEMENT (aspect_ratio_crop->videocrop),
230       "src");
231   src_pad = gst_ghost_pad_new ("src", link_pad);
232   gst_pad_set_getcaps_function (src_pad,
233       GST_DEBUG_FUNCPTR (gst_aspect_ratio_crop_get_caps));
234   gst_element_add_pad (GST_ELEMENT (aspect_ratio_crop), src_pad);
235   gst_object_unref (link_pad);
236   /* create ghost pad sink */
237   link_pad =
238       gst_element_get_static_pad (GST_ELEMENT (aspect_ratio_crop->videocrop),
239       "sink");
240   aspect_ratio_crop->sink = gst_ghost_pad_new ("sink", link_pad);
241   gst_element_add_pad (GST_ELEMENT (aspect_ratio_crop),
242       aspect_ratio_crop->sink);
243   gst_object_unref (link_pad);
244   gst_pad_set_setcaps_function (aspect_ratio_crop->sink,
245       GST_DEBUG_FUNCPTR (gst_aspect_ratio_crop_set_caps));
246 }
247
248 static void
249 gst_aspect_ratio_transform_structure (GstAspectRatioCrop * aspect_ratio_crop,
250     GstStructure * structure, GstStructure ** new_structure,
251     gboolean set_videocrop)
252 {
253   gdouble incoming_ar;
254   gdouble requested_ar;
255   gint width, height;
256   gint cropvalue;
257   gint par_d, par_n;
258
259   /* Check if we need to change the aspect ratio */
260   if (aspect_ratio_crop->ar_num < 1) {
261     GST_DEBUG_OBJECT (aspect_ratio_crop, "No cropping requested");
262     goto beach;
263   }
264
265   /* get the information from the caps */
266   if (!gst_structure_get_int (structure, "width", &width) ||
267       !gst_structure_get_int (structure, "height", &height))
268     goto beach;
269
270   if (!gst_structure_get_fraction (structure, "pixel-aspect-ratio",
271           &par_n, &par_d)) {
272     par_d = par_n = 1;
273   }
274
275   incoming_ar = ((gdouble) (width * par_n)) / (height * par_d);
276   GST_LOG_OBJECT (aspect_ratio_crop,
277       "incoming caps width(%d), height(%d), par (%d/%d) : ar = %f", width,
278       height, par_n, par_d, incoming_ar);
279
280   requested_ar =
281       (gdouble) aspect_ratio_crop->ar_num / aspect_ratio_crop->ar_denom;
282
283   /* check if the original aspect-ratio is the aspect-ratio that we want */
284   if (requested_ar == incoming_ar) {
285     GST_DEBUG_OBJECT (aspect_ratio_crop,
286         "Input video already has the correct aspect ratio (%.3f == %.3f)",
287         incoming_ar, requested_ar);
288     goto beach;
289   } else if (requested_ar > incoming_ar) {
290     /* fix aspect ratio with cropping on top and bottom */
291     cropvalue =
292         ((((double) aspect_ratio_crop->ar_denom /
293                 (double) (aspect_ratio_crop->ar_num)) * ((double) par_n /
294                 (double) par_d) * width) - height) / 2;
295     if (cropvalue < 0) {
296       cropvalue *= -1;
297     }
298     if (cropvalue >= (height / 2))
299       goto crop_failed;
300     if (set_videocrop) {
301       gst_aspect_ratio_crop_set_cropping (aspect_ratio_crop, cropvalue, 0,
302           cropvalue, 0);
303     }
304     if (new_structure) {
305       *new_structure = gst_structure_copy (structure);
306       gst_structure_set (*new_structure,
307           "height", G_TYPE_INT, (int) (height - (cropvalue * 2)), NULL);
308     }
309   } else {
310     /* fix aspect ratio with cropping on left and right */
311     cropvalue =
312         ((((double) aspect_ratio_crop->ar_num /
313                 (double) (aspect_ratio_crop->ar_denom)) * ((double) par_d /
314                 (double) par_n) * height) - width) / 2;
315     if (cropvalue < 0) {
316       cropvalue *= -1;
317     }
318     if (cropvalue >= (width / 2))
319       goto crop_failed;
320     if (set_videocrop) {
321       gst_aspect_ratio_crop_set_cropping (aspect_ratio_crop, 0, cropvalue,
322           0, cropvalue);
323     }
324     if (new_structure) {
325       *new_structure = gst_structure_copy (structure);
326       gst_structure_set (*new_structure,
327           "width", G_TYPE_INT, (int) (width - (cropvalue * 2)), NULL);
328     }
329   }
330
331   return;
332
333 crop_failed:
334   GST_WARNING_OBJECT (aspect_ratio_crop,
335       "can't crop to aspect ratio requested");
336   goto beach;
337 beach:
338   if (set_videocrop) {
339     gst_aspect_ratio_crop_set_cropping (aspect_ratio_crop, 0, 0, 0, 0);
340   }
341
342   if (new_structure) {
343     *new_structure = gst_structure_copy (structure);
344   }
345 }
346
347 static GstCaps *
348 gst_aspect_ratio_crop_transform_caps (GstAspectRatioCrop * aspect_ratio_crop,
349     GstCaps * caps)
350 {
351   GstCaps *transform;
352   gint size, i;
353
354   transform = gst_caps_new_empty ();
355
356   size = gst_caps_get_size (caps);
357
358   for (i = 0; i < size; i++) {
359     GstStructure *s;
360     GstStructure *trans_s;
361
362     s = gst_caps_get_structure (caps, i);
363
364     gst_aspect_ratio_transform_structure (aspect_ratio_crop, s, &trans_s,
365         FALSE);
366     gst_caps_append_structure (transform, trans_s);
367   }
368
369   return transform;
370 }
371
372 static GstCaps *
373 gst_aspect_ratio_crop_get_caps (GstPad * pad)
374 {
375   GstPad *peer;
376   GstAspectRatioCrop *aspect_ratio_crop;
377   GstCaps *return_caps;
378
379   aspect_ratio_crop = GST_ASPECT_RATIO_CROP (gst_pad_get_parent (pad));
380
381   g_mutex_lock (aspect_ratio_crop->crop_lock);
382
383   peer = gst_pad_get_peer (aspect_ratio_crop->sink);
384   if (peer == NULL) {
385     return_caps = gst_static_pad_template_get_caps (&src_template);
386     gst_caps_ref (return_caps);
387   } else {
388     GstCaps *peer_caps;
389
390     peer_caps = gst_pad_get_caps (peer);
391     return_caps =
392         gst_aspect_ratio_crop_transform_caps (aspect_ratio_crop, peer_caps);
393     gst_caps_unref (peer_caps);
394     gst_object_unref (peer);
395   }
396
397   g_mutex_unlock (aspect_ratio_crop->crop_lock);
398   gst_object_unref (aspect_ratio_crop);
399
400   return return_caps;
401 }
402
403 static void
404 gst_aspect_ratio_crop_set_property (GObject * object, guint prop_id,
405     const GValue * value, GParamSpec * pspec)
406 {
407   GstAspectRatioCrop *aspect_ratio_crop;
408   gboolean recheck = FALSE;
409
410   aspect_ratio_crop = GST_ASPECT_RATIO_CROP (object);
411
412   GST_OBJECT_LOCK (aspect_ratio_crop);
413   switch (prop_id) {
414     case ARG_ASPECT_RATIO_CROP:
415       if (GST_VALUE_HOLDS_FRACTION (value)) {
416         aspect_ratio_crop->ar_num = gst_value_get_fraction_numerator (value);
417         aspect_ratio_crop->ar_denom =
418             gst_value_get_fraction_denominator (value);
419         recheck = (GST_PAD_CAPS (aspect_ratio_crop->sink) != NULL);
420       }
421       break;
422     default:
423       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
424       break;
425   }
426   GST_OBJECT_UNLOCK (aspect_ratio_crop);
427
428   if (recheck) {
429     gst_aspect_ratio_crop_set_caps (aspect_ratio_crop->sink,
430         GST_PAD_CAPS (aspect_ratio_crop->sink));
431   }
432 }
433
434 static void
435 gst_aspect_ratio_crop_get_property (GObject * object, guint prop_id,
436     GValue * value, GParamSpec * pspec)
437 {
438   GstAspectRatioCrop *aspect_ratio_crop;
439
440   aspect_ratio_crop = GST_ASPECT_RATIO_CROP (object);
441
442   GST_OBJECT_LOCK (aspect_ratio_crop);
443   switch (prop_id) {
444     case ARG_ASPECT_RATIO_CROP:
445       gst_value_set_fraction (value, aspect_ratio_crop->ar_num,
446           aspect_ratio_crop->ar_denom);
447       break;
448     default:
449       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
450       break;
451   }
452   GST_OBJECT_UNLOCK (aspect_ratio_crop);
453 }