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 = GST_ELEMENT_DETAILS ("Crop",
63 "Filter/Effect/Video",
64 "Crops video into a user defined region",
65 "Wim Taymans <wim.taymans@chello.be>");
79 static GstStaticPadTemplate gst_video_crop_src_template =
80 GST_STATIC_PAD_TEMPLATE ("src",
83 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
86 static GstStaticPadTemplate gst_video_crop_sink_template =
87 GST_STATIC_PAD_TEMPLATE ("sink",
90 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
94 static void gst_video_crop_base_init (gpointer g_class);
95 static void gst_video_crop_class_init (GstVideoCropClass * klass);
96 static void gst_video_crop_init (GstVideoCrop * video_crop);
98 static void gst_video_crop_set_property (GObject * object, guint prop_id,
99 const GValue * value, GParamSpec * pspec);
100 static void gst_video_crop_get_property (GObject * object, guint prop_id,
101 GValue * value, GParamSpec * pspec);
103 static GstCaps *gst_video_crop_getcaps (GstPad * pad);
105 static GstPadLinkReturn
106 gst_video_crop_link (GstPad * pad, const GstCaps * caps);
107 static void gst_video_crop_chain (GstPad * pad, GstData * _data);
109 static GstStateChangeReturn gst_video_crop_change_state (GstElement * element,
110 GstStateChange transition);
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 g_return_if_fail (GST_IS_VIDEO_CROP (object));
218 video_crop = GST_VIDEO_CROP (object);
222 video_crop->crop_left = g_value_get_int (value);
225 video_crop->crop_right = g_value_get_int (value);
228 video_crop->crop_top = g_value_get_int (value);
231 video_crop->crop_bottom = g_value_get_int (value);
234 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
239 gst_video_crop_get_property (GObject * object, guint prop_id, GValue * value,
242 GstVideoCrop *video_crop;
244 g_return_if_fail (GST_IS_VIDEO_CROP (object));
246 video_crop = GST_VIDEO_CROP (object);
250 g_value_set_int (value, video_crop->crop_left);
253 g_value_set_int (value, video_crop->crop_right);
256 g_value_set_int (value, video_crop->crop_top);
259 g_value_set_int (value, video_crop->crop_bottom);
262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266 if (gst_pad_is_negotiated (video_crop->srcpad))
267 video_crop->renegotiate_src_caps = TRUE;
271 gst_video_crop_add_to_struct_val (GstStructure * s, const gchar * field_name,
276 val = gst_structure_get_value (s, field_name);
278 if (G_VALUE_HOLDS_INT (val)) {
279 gint ival = g_value_get_int (val);
281 gst_structure_set (s, field_name, G_TYPE_INT, ival + addval, NULL);
285 if (GST_VALUE_HOLDS_INT_RANGE (val)) {
286 gint min = gst_value_get_int_range_min (val);
287 gint max = gst_value_get_int_range_max (val);
289 gst_structure_set (s, field_name, GST_TYPE_INT_RANGE, min + addval,
294 if (GST_VALUE_HOLDS_LIST (val)) {
295 GValue newlist = { 0, };
298 g_value_init (&newlist, GST_TYPE_LIST);
299 for (i = 0; i < gst_value_list_get_size (val); ++i) {
300 GValue newval = { 0, };
301 g_value_init (&newval, G_VALUE_TYPE (val));
302 g_value_copy (val, &newval);
303 if (G_VALUE_HOLDS_INT (val)) {
304 gint ival = g_value_get_int (val);
306 g_value_set_int (&newval, ival + addval);
307 } else if (GST_VALUE_HOLDS_INT_RANGE (val)) {
308 gint min = gst_value_get_int_range_min (val);
309 gint max = gst_value_get_int_range_max (val);
311 gst_value_set_int_range (&newval, min + addval, max + addval);
313 g_return_if_reached ();
315 gst_value_list_append_value (&newlist, &newval);
316 g_value_unset (&newval);
318 gst_structure_set_value (s, field_name, &newlist);
319 g_value_unset (&newlist);
323 g_return_if_reached ();
327 gst_video_crop_getcaps (GstPad * pad)
330 GstCaps *othercaps, *caps;
332 gint i, delta_w, delta_h;
334 vc = GST_VIDEO_CROP (gst_pad_get_parent (pad));
335 otherpad = (pad == vc->srcpad) ? vc->sinkpad : vc->srcpad;
336 othercaps = gst_pad_get_allowed_caps (otherpad);
338 GST_DEBUG_OBJECT (pad, "othercaps of otherpad %s:%s are: %" GST_PTR_FORMAT,
339 GST_DEBUG_PAD_NAME (otherpad), othercaps);
341 if (pad == vc->srcpad) {
342 delta_w = 0 - vc->crop_left - vc->crop_right;
343 delta_h = 0 - vc->crop_top - vc->crop_bottom;
345 delta_w = vc->crop_left + vc->crop_right;
346 delta_h = vc->crop_top + vc->crop_bottom;
349 for (i = 0; i < gst_caps_get_size (othercaps); i++) {
350 GstStructure *s = gst_caps_get_structure (othercaps, i);
352 gst_video_crop_add_to_struct_val (s, "width", delta_w);
353 gst_video_crop_add_to_struct_val (s, "height", delta_h);
356 caps = gst_caps_intersect (othercaps, gst_pad_get_pad_template_caps (pad));
357 gst_caps_free (othercaps);
359 GST_DEBUG_OBJECT (pad, "returning caps: %" GST_PTR_FORMAT, caps);
363 static GstPadLinkReturn
364 gst_video_crop_link (GstPad * pad, const GstCaps * caps)
366 GstPadLinkReturn ret;
367 GstStructure *structure;
371 gint w, h, other_w, other_h;
373 vc = GST_VIDEO_CROP (gst_pad_get_parent (pad));
375 structure = gst_caps_get_structure (caps, 0);
376 if (!gst_structure_get_int (structure, "width", &w)
377 || !gst_structure_get_int (structure, "height", &h))
378 return GST_PAD_LINK_DELAYED;
380 if (pad == vc->srcpad) {
381 other_w = w + vc->crop_left + vc->crop_right;
382 other_h = h + vc->crop_top + vc->crop_bottom;
383 otherpad = vc->sinkpad;
385 vc->height = other_h;
387 other_w = w - vc->crop_left - vc->crop_right;
388 other_h = h - vc->crop_top - vc->crop_bottom;
391 otherpad = vc->srcpad;
394 newcaps = gst_caps_copy (caps);
396 gst_caps_set_simple (newcaps,
397 "width", G_TYPE_INT, other_w, "height", G_TYPE_INT, other_h, NULL);
399 ret = gst_pad_try_set_caps (otherpad, newcaps);
400 gst_caps_free (newcaps);
402 if (ret == GST_PAD_LINK_REFUSED)
403 return GST_PAD_LINK_REFUSED;
405 return GST_PAD_LINK_OK;
408 /* these macros are adapted from videotestsrc.c, paint_setup_I420() */
409 #define ROUND_UP_2(x) (((x)+1)&~1)
410 #define ROUND_UP_4(x) (((x)+3)&~3)
411 #define ROUND_UP_8(x) (((x)+7)&~7)
413 #define GST_VIDEO_I420_Y_ROWSTRIDE(width) (ROUND_UP_4(width))
414 #define GST_VIDEO_I420_U_ROWSTRIDE(width) (ROUND_UP_8(width)/2)
415 #define GST_VIDEO_I420_V_ROWSTRIDE(width) ((ROUND_UP_8(GST_VIDEO_I420_Y_ROWSTRIDE(width)))/2)
417 #define GST_VIDEO_I420_Y_OFFSET(w,h) (0)
418 #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)))
419 #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))
421 #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))
424 gst_video_crop_i420 (GstVideoCrop * video_crop, GstBuffer * src_buffer,
425 GstBuffer * dest_buffer)
429 guint8 *srcY, *srcU, *srcV;
430 guint8 *destY, *destU, *destV;
431 gint out_width = video_crop->width -
432 (video_crop->crop_left + video_crop->crop_right);
433 gint out_height = video_crop->height -
434 (video_crop->crop_top + video_crop->crop_bottom);
437 src = GST_BUFFER_DATA (src_buffer);
438 dest = GST_BUFFER_DATA (dest_buffer);
440 srcY = src + GST_VIDEO_I420_Y_OFFSET (video_crop->width, video_crop->height);
441 destY = dest + GST_VIDEO_I420_Y_OFFSET (out_width, out_height);
443 /* copy Y plane first */
445 (GST_VIDEO_I420_Y_ROWSTRIDE (video_crop->width) * video_crop->crop_top) +
446 video_crop->crop_left;
447 for (j = 0; j < out_height; j++) {
448 memcpy (destY, srcY, out_width);
449 srcY += GST_VIDEO_I420_Y_ROWSTRIDE (video_crop->width);
450 destY += GST_VIDEO_I420_Y_ROWSTRIDE (out_width);
453 destU = dest + GST_VIDEO_I420_U_OFFSET (out_width, out_height);
454 destV = dest + GST_VIDEO_I420_V_OFFSET (out_width, out_height);
456 srcU = src + GST_VIDEO_I420_U_OFFSET (video_crop->width, video_crop->height);
457 srcV = src + GST_VIDEO_I420_V_OFFSET (video_crop->width, video_crop->height);
460 (GST_VIDEO_I420_U_ROWSTRIDE (video_crop->width) * (video_crop->crop_top /
461 2)) + (video_crop->crop_left / 2);
463 (GST_VIDEO_I420_V_ROWSTRIDE (video_crop->width) * (video_crop->crop_top /
464 2)) + (video_crop->crop_left / 2);
466 for (j = 0; j < out_height / 2; j++) {
468 memcpy (destU, srcU, out_width / 2);
469 srcU += GST_VIDEO_I420_U_ROWSTRIDE (video_crop->width);
470 destU += GST_VIDEO_I420_U_ROWSTRIDE (out_width);
473 memcpy (destV, srcV, out_width / 2);
474 srcV += GST_VIDEO_I420_V_ROWSTRIDE (video_crop->width);
475 destV += GST_VIDEO_I420_V_ROWSTRIDE (out_width);
480 gst_video_crop_chain (GstPad * pad, GstData * _data)
482 GstBuffer *buffer = GST_BUFFER (_data);
483 GstVideoCrop *video_crop;
485 gint new_width, new_height;
487 video_crop = GST_VIDEO_CROP (gst_pad_get_parent (pad));
489 new_width = video_crop->width -
490 (video_crop->crop_left + video_crop->crop_right);
491 new_height = video_crop->height -
492 (video_crop->crop_top + video_crop->crop_bottom);
494 if (video_crop->renegotiate_src_caps || !GST_PAD_CAPS (video_crop->srcpad)) {
497 newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (video_crop->sinkpad));
499 gst_caps_set_simple (newcaps,
500 "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
502 if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (video_crop->srcpad,
504 GST_ELEMENT_ERROR (video_crop, CORE, NEGOTIATION, (NULL), (NULL));
505 gst_caps_free (newcaps);
509 gst_caps_free (newcaps);
511 video_crop->renegotiate_src_caps = FALSE;
514 /* passthrough if nothing to do */
515 if (new_width == video_crop->width && new_height == video_crop->height) {
516 gst_pad_push (video_crop->srcpad, GST_DATA (buffer));
520 g_return_if_fail (GST_BUFFER_SIZE (buffer) >=
521 GST_VIDEO_I420_SIZE (video_crop->width, video_crop->height));
524 gst_pad_alloc_buffer_and_set_caps (video_crop->srcpad,
525 GST_BUFFER_OFFSET (buffer), 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 GstStateChangeReturn
536 gst_video_crop_change_state (GstElement * element, GstStateChange transition)
538 GstVideoCrop *video_crop;
540 video_crop = GST_VIDEO_CROP (element);
542 switch (transition) {
543 case GST_STATE_CHANGE_NULL_TO_READY:
544 video_crop->renegotiate_src_caps = TRUE;
546 case GST_STATE_CHANGE_READY_TO_PAUSED:
548 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
550 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
552 case GST_STATE_CHANGE_PAUSED_TO_READY:
554 case GST_STATE_CHANGE_READY_TO_NULL:
558 if (parent_class->change_state != NULL)
559 return parent_class->change_state (element, transition);
561 return GST_STATE_CHANGE_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_NAME, GST_PACKAGE_ORIGIN)