2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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.
24 #include <gst/video/video.h>
28 #define GST_TYPE_VIDEO_CROP \
29 (gst_video_crop_get_type())
30 #define GST_VIDEO_CROP(obj) \
31 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_CROP,GstVideoCrop))
32 #define GST_VIDEO_CROP_CLASS(klass) \
33 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_CROP,GstVideoCropClass))
34 #define GST_IS_VIDEO_CROP(obj) \
35 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_CROP))
36 #define GST_IS_VIDEO_CROP_CLASS(obj) \
37 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_CROP))
39 typedef struct _GstVideoCrop GstVideoCrop;
40 typedef struct _GstVideoCropClass GstVideoCropClass;
52 gint crop_left, crop_right, crop_top, crop_bottom;
53 gboolean renegotiate_src_caps;
56 struct _GstVideoCropClass
58 GstElementClass parent_class;
61 /* elementfactory information */
62 static GstElementDetails gst_video_crop_details =
63 GST_ELEMENT_DETAILS ("video crop filter",
64 "Filter/Effect/Video",
65 "Crops video into a user defined region",
66 "Wim Taymans <wim.taymans@chello.be>");
80 static GstStaticPadTemplate gst_video_crop_src_template =
81 GST_STATIC_PAD_TEMPLATE ("src",
84 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
87 static GstStaticPadTemplate gst_video_crop_sink_template =
88 GST_STATIC_PAD_TEMPLATE ("sink",
91 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
95 static void gst_video_crop_base_init (gpointer g_class);
96 static void gst_video_crop_class_init (GstVideoCropClass * klass);
97 static void gst_video_crop_init (GstVideoCrop * video_crop);
99 static void gst_video_crop_set_property (GObject * object, guint prop_id,
100 const GValue * value, GParamSpec * pspec);
101 static void gst_video_crop_get_property (GObject * object, guint prop_id,
102 GValue * value, GParamSpec * pspec);
104 static GstCaps *gst_video_crop_getcaps (GstPad * pad);
106 static GstPadLinkReturn
107 gst_video_crop_link (GstPad * pad, const GstCaps * caps);
108 static void gst_video_crop_chain (GstPad * pad, GstData * _data);
110 static GstElementStateReturn gst_video_crop_change_state (GstElement * element);
113 static GstElementClass *parent_class = NULL;
115 /* static guint gst_video_crop_signals[LAST_SIGNAL] = { 0 }; */
118 gst_video_crop_get_type (void)
120 static GType video_crop_type = 0;
122 if (!video_crop_type) {
123 static const GTypeInfo video_crop_info = {
124 sizeof (GstVideoCropClass),
125 gst_video_crop_base_init,
127 (GClassInitFunc) gst_video_crop_class_init,
130 sizeof (GstVideoCrop),
132 (GInstanceInitFunc) gst_video_crop_init,
136 g_type_register_static (GST_TYPE_ELEMENT, "GstVideoCrop",
137 &video_crop_info, 0);
139 return video_crop_type;
143 gst_video_crop_base_init (gpointer g_class)
145 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
147 gst_element_class_set_details (element_class, &gst_video_crop_details);
149 gst_element_class_add_pad_template (element_class,
150 gst_static_pad_template_get (&gst_video_crop_sink_template));
151 gst_element_class_add_pad_template (element_class,
152 gst_static_pad_template_get (&gst_video_crop_src_template));
155 gst_video_crop_class_init (GstVideoCropClass * klass)
157 GObjectClass *gobject_class;
158 GstElementClass *gstelement_class;
160 gobject_class = (GObjectClass *) klass;
161 gstelement_class = (GstElementClass *) klass;
163 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
165 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LEFT,
166 g_param_spec_int ("left", "Left", "Pixels to crop at left",
167 0, G_MAXINT, 0, G_PARAM_READWRITE));
168 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_RIGHT,
169 g_param_spec_int ("right", "Right", "Pixels to crop at right",
170 0, G_MAXINT, 0, G_PARAM_READWRITE));
171 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOP,
172 g_param_spec_int ("top", "Top", "Pixels to crop at top",
173 0, G_MAXINT, 0, G_PARAM_READWRITE));
174 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BOTTOM,
175 g_param_spec_int ("bottom", "Bottom", "Pixels to crop at bottom",
176 0, G_MAXINT, 0, G_PARAM_READWRITE));
178 gobject_class->set_property = gst_video_crop_set_property;
179 gobject_class->get_property = gst_video_crop_get_property;
181 gstelement_class->change_state = gst_video_crop_change_state;
185 gst_video_crop_init (GstVideoCrop * video_crop)
187 /* create the sink and src pads */
188 video_crop->sinkpad =
189 gst_pad_new_from_template (gst_static_pad_template_get
190 (&gst_video_crop_sink_template), "sink");
191 gst_element_add_pad (GST_ELEMENT (video_crop), video_crop->sinkpad);
192 gst_pad_set_chain_function (video_crop->sinkpad, gst_video_crop_chain);
193 gst_pad_set_getcaps_function (video_crop->sinkpad, gst_video_crop_getcaps);
194 gst_pad_set_link_function (video_crop->sinkpad, gst_video_crop_link);
197 gst_pad_new_from_template (gst_static_pad_template_get
198 (&gst_video_crop_src_template), "src");
199 gst_element_add_pad (GST_ELEMENT (video_crop), video_crop->srcpad);
200 gst_pad_set_getcaps_function (video_crop->srcpad, gst_video_crop_getcaps);
201 gst_pad_set_link_function (video_crop->srcpad, gst_video_crop_link);
203 video_crop->crop_right = 0;
204 video_crop->crop_left = 0;
205 video_crop->crop_top = 0;
206 video_crop->crop_bottom = 0;
209 /* do we need this function? */
211 gst_video_crop_set_property (GObject * object, guint prop_id,
212 const GValue * value, GParamSpec * pspec)
214 GstVideoCrop *video_crop;
216 /* it's not null if we got it, but it might not be ours */
217 g_return_if_fail (GST_IS_VIDEO_CROP (object));
219 video_crop = GST_VIDEO_CROP (object);
223 video_crop->crop_left = g_value_get_int (value);
226 video_crop->crop_right = g_value_get_int (value);
229 video_crop->crop_top = g_value_get_int (value);
232 video_crop->crop_bottom = g_value_get_int (value);
235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
240 gst_video_crop_get_property (GObject * object, guint prop_id, GValue * value,
243 GstVideoCrop *video_crop;
245 g_return_if_fail (GST_IS_VIDEO_CROP (object));
247 video_crop = GST_VIDEO_CROP (object);
251 g_value_set_int (value, video_crop->crop_left);
254 g_value_set_int (value, video_crop->crop_right);
257 g_value_set_int (value, video_crop->crop_top);
260 g_value_set_int (value, video_crop->crop_bottom);
263 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267 if (gst_pad_is_negotiated (video_crop->srcpad))
268 video_crop->renegotiate_src_caps = TRUE;
272 gst_video_crop_add_to_struct_val (GstStructure * s, const gchar * field_name,
277 val = gst_structure_get_value (s, field_name);
279 if (G_VALUE_HOLDS_INT (val)) {
280 gint ival = g_value_get_int (val);
282 gst_structure_set (s, field_name, G_TYPE_INT, ival + addval, NULL);
286 if (GST_VALUE_HOLDS_INT_RANGE (val)) {
287 gint min = gst_value_get_int_range_min (val);
288 gint max = gst_value_get_int_range_max (val);
290 gst_structure_set (s, field_name, GST_TYPE_INT_RANGE, min + addval,
295 if (GST_VALUE_HOLDS_LIST (val)) {
296 GValue newlist = { 0, };
299 g_value_init (&newlist, GST_TYPE_LIST);
300 for (i = 0; i < gst_value_list_get_size (val); ++i) {
301 GValue newval = { 0, };
302 g_value_init (&newval, G_VALUE_TYPE (val));
303 g_value_copy (val, &newval);
304 if (G_VALUE_HOLDS_INT (val)) {
305 gint ival = g_value_get_int (val);
307 g_value_set_int (&newval, ival + addval);
308 } else if (GST_VALUE_HOLDS_INT_RANGE (val)) {
309 gint min = gst_value_get_int_range_min (val);
310 gint max = gst_value_get_int_range_max (val);
312 gst_value_set_int_range (&newval, min + addval, max + addval);
314 g_return_if_reached ();
316 gst_value_list_append_value (&newlist, &newval);
317 g_value_unset (&newval);
319 gst_structure_set_value (s, field_name, &newlist);
320 g_value_unset (&newlist);
324 g_return_if_reached ();
328 gst_video_crop_getcaps (GstPad * pad)
331 GstCaps *othercaps, *caps;
333 gint i, delta_w, delta_h;
335 vc = GST_VIDEO_CROP (gst_pad_get_parent (pad));
336 otherpad = (pad == vc->srcpad) ? vc->sinkpad : vc->srcpad;
337 othercaps = gst_pad_get_allowed_caps (otherpad);
339 GST_DEBUG_OBJECT (pad, "othercaps of otherpad %s:%s are: %" GST_PTR_FORMAT,
340 GST_DEBUG_PAD_NAME (otherpad), othercaps);
342 if (pad == vc->srcpad) {
343 delta_w = 0 - vc->crop_left - vc->crop_right;
344 delta_h = 0 - vc->crop_top - vc->crop_bottom;
346 delta_w = vc->crop_left + vc->crop_right;
347 delta_h = vc->crop_top + vc->crop_bottom;
350 for (i = 0; i < gst_caps_get_size (othercaps); i++) {
351 GstStructure *s = gst_caps_get_structure (othercaps, i);
353 gst_video_crop_add_to_struct_val (s, "width", delta_w);
354 gst_video_crop_add_to_struct_val (s, "height", delta_h);
357 caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad));
358 gst_caps_free (othercaps);
360 GST_DEBUG_OBJECT (pad, "returning caps: %" GST_PTR_FORMAT, caps);
364 static GstPadLinkReturn
365 gst_video_crop_link (GstPad * pad, const GstCaps * caps)
367 GstPadLinkReturn ret;
368 GstStructure *structure;
372 gint w, h, other_w, other_h;
374 vc = GST_VIDEO_CROP (gst_pad_get_parent (pad));
376 structure = gst_caps_get_structure (caps, 0);
377 if (!gst_structure_get_int (structure, "width", &w)
378 || !gst_structure_get_int (structure, "height", &h))
379 return GST_PAD_LINK_DELAYED;
381 if (pad == vc->srcpad) {
382 other_w = w + vc->crop_left + vc->crop_right;
383 other_h = h + vc->crop_top + vc->crop_bottom;
384 otherpad = vc->sinkpad;
386 vc->height = other_h;
388 other_w = w - vc->crop_left - vc->crop_right;
389 other_h = h - vc->crop_top - vc->crop_bottom;
392 otherpad = vc->srcpad;
395 newcaps = gst_caps_copy (caps);
397 gst_caps_set_simple (newcaps,
398 "width", G_TYPE_INT, other_w, "height", G_TYPE_INT, other_h, NULL);
400 ret = gst_pad_try_set_caps (otherpad, newcaps);
401 gst_caps_free (newcaps);
403 if (ret == GST_PAD_LINK_REFUSED)
404 return GST_PAD_LINK_REFUSED;
406 return GST_PAD_LINK_OK;
409 /* these macros are adapted from videotestsrc.c, paint_setup_I420() */
410 #define ROUND_UP_2(x) (((x)+1)&~1)
411 #define ROUND_UP_4(x) (((x)+3)&~3)
412 #define ROUND_UP_8(x) (((x)+7)&~7)
414 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (ROUND_UP_4(width))
415 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (ROUND_UP_8(width)/2)
416 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
418 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
419 #define GST_VIDEO_I420_U_OFFSET(w,h) (GST_VIDEO_I420_Y_OFFSET(w,h)+(GST_VIDEO_I420_Y_ROWSTRIDE(w)*ROUND_UP_2(h)))
420 #define GST_VIDEO_I420_V_OFFSET(w,h) (GST_VIDEO_I420_U_OFFSET(w,h)+(GST_VIDEO_I420_U_ROWSTRIDE(w)*ROUND_UP_2(h)/2))
422 #define GST_VIDEO_I420_SIZE(w,h) (GST_VIDEO_I420_V_OFFSET(w,h)+(GST_VIDEO_I420_V_ROWSTRIDE(w)*ROUND_UP_2(h)/2))
425 gst_video_crop_i420 (GstVideoCrop * video_crop, GstBuffer * src_buffer,
426 GstBuffer * dest_buffer)
430 guint8 *srcY, *srcU, *srcV;
431 guint8 *destY, *destU, *destV;
432 gint out_width = video_crop->width -
433 (video_crop->crop_left + video_crop->crop_right);
434 gint out_height = video_crop->height -
435 (video_crop->crop_top + video_crop->crop_bottom);
438 src = GST_BUFFER_DATA (src_buffer);
439 dest = GST_BUFFER_DATA (dest_buffer);
441 srcY = src + GST_VIDEO_I420_Y_OFFSET (video_crop->width, video_crop->height);
442 destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
444 /* copy Y plane first */
446 (GST_VIDEO_I420_Y_ROWSTRIDE (video_crop->width) * video_crop->crop_top) +
447 video_crop->crop_left;
448 for (j = 0; j < out_height; j++) {
449 memcpy (destY, srcY, out_width);
450 srcY += GST_VIDEO_I420_Y_ROWSTRIDE (video_crop->width);
451 destY += GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
454 destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);
455 destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);
457 srcU = src + GST_VIDEO_I420_U_OFFSET (video_crop->width, video_crop->height);
458 srcV = src + GST_VIDEO_I420_V_OFFSET (video_crop->width, video_crop->height);
461 (GST_VIDEO_I420_U_ROWSTRIDE (video_crop->width) * (video_crop->crop_top /
462 2)) + (video_crop->crop_left / 2);
464 (GST_VIDEO_I420_V_ROWSTRIDE (video_crop->width) * (video_crop->crop_top /
465 2)) + (video_crop->crop_left / 2);
467 for (j = 0; j < out_height / 2; j++) {
469 memcpy (destU, srcU, out_width / 2);
470 srcU += GST_VIDEO_I420_U_ROWSTRIDE (video_crop->width);
471 destU += GST_VIDEO_I420_U_ROWSTRIDE (out_width);
474 memcpy (destV, srcV, out_width / 2);
475 srcV += GST_VIDEO_I420_V_ROWSTRIDE (video_crop->width);
476 destV += GST_VIDEO_I420_V_ROWSTRIDE (out_width);
481 gst_video_crop_chain (GstPad * pad, GstData * _data)
483 GstBuffer *buffer = GST_BUFFER (_data);
484 GstVideoCrop *video_crop;
486 gint new_width, new_height;
488 video_crop = GST_VIDEO_CROP (gst_pad_get_parent (pad));
490 new_width = video_crop->width -
491 (video_crop->crop_left + video_crop->crop_right);
492 new_height = video_crop->height -
493 (video_crop->crop_top + video_crop->crop_bottom);
495 if (video_crop->renegotiate_src_caps || !GST_PAD_CAPS (video_crop->srcpad)) {
498 newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (video_crop->sinkpad));
500 gst_caps_set_simple (newcaps,
501 "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
503 if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (video_crop->srcpad,
505 GST_ELEMENT_ERROR (video_crop, CORE, NEGOTIATION, (NULL), (NULL));
506 gst_caps_free (newcaps);
510 gst_caps_free (newcaps);
512 video_crop->renegotiate_src_caps = FALSE;
515 /* passthrough if nothing to do */
516 if (new_width == video_crop->width && new_height == video_crop->height) {
517 gst_pad_push (video_crop->srcpad, GST_DATA (buffer));
521 g_return_if_fail (GST_BUFFER_SIZE (buffer) >=
522 GST_VIDEO_I420_SIZE (video_crop->width, video_crop->height));
524 outbuf = gst_pad_alloc_buffer (video_crop->srcpad, GST_BUFFER_OFFSET (buffer),
525 GST_VIDEO_I420_SIZE (new_width, new_height));
527 gst_buffer_stamp (outbuf, buffer);
529 gst_video_crop_i420 (video_crop, buffer, outbuf);
530 gst_buffer_unref (buffer);
532 gst_pad_push (video_crop->srcpad, GST_DATA (outbuf));
535 static GstElementStateReturn
536 gst_video_crop_change_state (GstElement * element)
538 GstVideoCrop *video_crop;
540 video_crop = GST_VIDEO_CROP (element);
542 switch (GST_STATE_TRANSITION (element)) {
543 case GST_STATE_NULL_TO_READY:
544 video_crop->renegotiate_src_caps = TRUE;
546 case GST_STATE_READY_TO_PAUSED:
548 case GST_STATE_PAUSED_TO_PLAYING:
550 case GST_STATE_PLAYING_TO_PAUSED:
552 case GST_STATE_PAUSED_TO_READY:
554 case GST_STATE_READY_TO_NULL:
558 if (parent_class->change_state != NULL)
559 return parent_class->change_state (element);
561 return GST_STATE_SUCCESS;
565 plugin_init (GstPlugin * plugin)
567 return gst_element_register (plugin, "videocrop", GST_RANK_NONE,
568 GST_TYPE_VIDEO_CROP);
571 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
574 "Crops video into a user defined region",
575 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)