v4l2sink: emit application message when cropping
[gstreamer-omap:gst-plugins-good.git] / sys / v4l2 / gstv4l2sink.c
1 /* GStreamer
2  *
3  * Copyright (C) 2009 Texas Instruments, Inc - http://www.ti.com/
4  *
5  * Description: V4L2 sink element
6  *  Created on: Jul 2, 2009
7  *      Author: Rob Clark <rob@ti.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /**
26  * SECTION:element-v4l2sink
27  *
28  * v4l2sink can be used to display video to v4l2 devices (screen overlays
29  * provided by the graphics hardware, tv-out, etc)
30  *
31  * <refsect2>
32  * <title>Example launch lines</title>
33  * |[
34  * gst-launch videotestsrc ! v4l2sink device=/dev/video1
35  * ]| This pipeline displays a test pattern on /dev/video1
36  * |[
37  * gst-launch -v videotestsrc ! navigationtest ! v4l2sink
38  * ]| A pipeline to test navigation events.
39  * While moving the mouse pointer over the test signal you will see a black box
40  * following the mouse pointer. If you press the mouse button somewhere on the
41  * video and release it somewhere else a green box will appear where you pressed
42  * the button and a red one where you released it. (The navigationtest element
43  * is part of gst-plugins-good.) You can observe here that even if the images
44  * are scaled through hardware the pointer coordinates are converted back to the
45  * original video frame geometry so that the box can be drawn to the correct
46  * position. This also handles borders correctly, limiting coordinates to the
47  * image area
48  * </refsect2>
49  */
50
51
52 #ifdef HAVE_CONFIG_H
53 #include <config.h>
54 #endif
55
56
57 #include "gstv4l2colorbalance.h"
58 #ifdef HAVE_XVIDEO
59 #include "gstv4l2xoverlay.h"
60 #endif
61 #include "gstv4l2vidorient.h"
62
63 #include "gstv4l2sink.h"
64 #include "gst/gst-i18n-plugin.h"
65 #include <gst/video/video.h>
66
67 #include <string.h>
68
69 GST_DEBUG_CATEGORY (v4l2sink_debug);
70 #define GST_CAT_DEFAULT v4l2sink_debug
71
72 #define PROP_DEF_QUEUE_SIZE         12
73 #define PROP_DEF_MIN_QUEUED_BUFS    1
74 #define DEFAULT_PROP_DEVICE   "/dev/video1"
75
76 #define MIN_ROTATION_ANGLE 0
77 #define MAX_ROTATION_ANGLE 360
78 #define DEFAULT_ROTATION_ANGLE 0
79
80 enum
81 {
82   PROP_0,
83   V4L2_STD_OBJECT_PROPS,
84   PROP_QUEUE_SIZE,
85   PROP_MIN_QUEUED_BUFS,
86   PROP_OVERLAY_TOP,
87   PROP_OVERLAY_LEFT,
88   PROP_OVERLAY_WIDTH,
89   PROP_OVERLAY_HEIGHT,
90   PROP_CROP_TOP,
91   PROP_CROP_LEFT,
92   PROP_CROP_WIDTH,
93   PROP_CROP_HEIGHT,
94   PROP_ROTATION,
95   PROP_FLIP,
96 };
97
98
99 GST_IMPLEMENT_V4L2_PROBE_METHODS (GstV4l2SinkClass, gst_v4l2sink);
100 GST_IMPLEMENT_V4L2_COLOR_BALANCE_METHODS (GstV4l2Sink, gst_v4l2sink);
101 #ifdef HAVE_XVIDEO
102 GST_IMPLEMENT_V4L2_XOVERLAY_METHODS (GstV4l2Sink, gst_v4l2sink);
103 #endif
104 GST_IMPLEMENT_V4L2_VIDORIENT_METHODS (GstV4l2Sink, gst_v4l2sink);
105 static gboolean gst_v4l2_update_geometry (GstV4l2Object * v4l2object,
106     GstVideoRectangle * rect);
107
108 static gboolean
109 gst_v4l2sink_iface_supported (GstImplementsInterface * iface, GType iface_type)
110 {
111   GstV4l2Object *v4l2object = GST_V4L2SINK (iface)->v4l2object;
112
113 #ifdef HAVE_XVIDEO
114   g_assert (iface_type == GST_TYPE_X_OVERLAY ||
115       iface_type == GST_TYPE_NAVIGATION ||
116       iface_type == GST_TYPE_COLOR_BALANCE ||
117       iface_type == GST_TYPE_VIDEO_ORIENTATION);
118 #else
119   g_assert (iface_type == GST_TYPE_COLOR_BALANCE ||
120       iface_type == GST_TYPE_VIDEO_ORIENTATION);
121 #endif
122
123   if (v4l2object->video_fd == -1)
124     return FALSE;
125
126 #ifdef HAVE_XVIDEO
127   if (!GST_V4L2_IS_OVERLAY (v4l2object)) {
128     if (iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_NAVIGATION)
129       return FALSE;
130   }
131 #endif
132
133   return TRUE;
134 }
135
136 /*
137  * Flip state
138  */
139 enum
140 {
141   FLIP_NONE = 0,
142   FLIP_HORIZONTAL = 1,
143   FLIP_VERTICAL = 2,
144 };
145
146 #define GST_TYPE_V4L2_FLIP (gst_v4l2_flip_get_type ())
147 static GType
148 gst_v4l2_flip_get_type (void)
149 {
150   static GType type = 0;
151
152   if (!type) {
153     static GEnumValue vals[] = {
154       {FLIP_NONE, "No Flip", "none"},
155       {FLIP_HORIZONTAL, "Horizontal Flip", "horiz"},
156       {FLIP_VERTICAL, "Vertical Flip", "vert"},
157       {0, NULL, NULL},
158     };
159     type = g_enum_register_static ("GstV4l2SinkFlip", vals);
160   }
161   return type;
162 }
163
164 static void
165 gst_v4l2sink_interface_init (GstImplementsInterfaceClass * klass)
166 {
167   /*
168    * default virtual functions
169    */
170   klass->supported = gst_v4l2sink_iface_supported;
171 }
172
173 #ifdef HAVE_XVIDEO
174 static void gst_v4l2sink_navigation_send_event (GstNavigation * navigation,
175     GstStructure * structure);
176 static void
177 gst_v4l2sink_navigation_init (GstNavigationInterface * iface)
178 {
179   iface->send_event = gst_v4l2sink_navigation_send_event;
180 }
181 #endif
182
183 static void
184 gst_v4l2sink_init_interfaces (GType type)
185 {
186   static const GInterfaceInfo v4l2iface_info = {
187     (GInterfaceInitFunc) gst_v4l2sink_interface_init,
188     NULL,
189     NULL,
190   };
191 #ifdef HAVE_XVIDEO
192   static const GInterfaceInfo v4l2_xoverlay_info = {
193     (GInterfaceInitFunc) gst_v4l2sink_xoverlay_interface_init,
194     NULL,
195     NULL,
196   };
197   static const GInterfaceInfo v4l2_navigation_info = {
198     (GInterfaceInitFunc) gst_v4l2sink_navigation_init,
199     NULL,
200     NULL,
201   };
202 #endif
203   static const GInterfaceInfo v4l2_colorbalance_info = {
204     (GInterfaceInitFunc) gst_v4l2sink_color_balance_interface_init,
205     NULL,
206     NULL,
207   };
208   static const GInterfaceInfo v4l2_videoorientation_info = {
209     (GInterfaceInitFunc) gst_v4l2sink_video_orientation_interface_init,
210     NULL,
211     NULL,
212   };
213   static const GInterfaceInfo v4l2_propertyprobe_info = {
214     (GInterfaceInitFunc) gst_v4l2sink_property_probe_interface_init,
215     NULL,
216     NULL,
217   };
218
219   g_type_add_interface_static (type,
220       GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info);
221 #ifdef HAVE_XVIDEO
222   g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info);
223   g_type_add_interface_static (type,
224       GST_TYPE_NAVIGATION, &v4l2_navigation_info);
225 #endif
226   g_type_add_interface_static (type,
227       GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info);
228   g_type_add_interface_static (type,
229       GST_TYPE_VIDEO_ORIENTATION, &v4l2_videoorientation_info);
230   g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
231       &v4l2_propertyprobe_info);
232 }
233
234
235 GST_BOILERPLATE_FULL (GstV4l2Sink, gst_v4l2sink, GstVideoSink,
236     GST_TYPE_VIDEO_SINK, gst_v4l2sink_init_interfaces);
237
238
239 static void gst_v4l2sink_dispose (GObject * object);
240 static void gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink);
241
242 /* GObject methods: */
243 static void gst_v4l2sink_set_property (GObject * object, guint prop_id,
244     const GValue * value, GParamSpec * pspec);
245 static void gst_v4l2sink_get_property (GObject * object, guint prop_id,
246     GValue * value, GParamSpec * pspec);
247
248
249 /* GstElement methods: */
250 static GstStateChangeReturn gst_v4l2sink_change_state (GstElement * element,
251     GstStateChange transition);
252
253 /* GstBaseSink methods: */
254 static GstCaps *gst_v4l2sink_get_caps (GstBaseSink * bsink);
255 static gboolean gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
256 static GstFlowReturn gst_v4l2sink_buffer_alloc (GstBaseSink * bsink,
257     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
258 static gboolean gst_v4l2sink_event (GstBaseSink * bsink, GstEvent * event);
259 static GstFlowReturn gst_v4l2sink_show_frame (GstBaseSink * bsink,
260     GstBuffer * buf);
261 static void gst_v4l2sink_sync_rotation (GstV4l2Sink * v4l2sink);
262
263 static void
264 gst_v4l2sink_base_init (gpointer g_class)
265 {
266   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
267   GstV4l2SinkClass *gstv4l2sink_class = GST_V4L2SINK_CLASS (g_class);
268
269   gstv4l2sink_class->v4l2_class_devices = NULL;
270
271   GST_DEBUG_CATEGORY_INIT (v4l2sink_debug, "v4l2sink", 0, "V4L2 sink element");
272
273   gst_element_class_set_details_simple (gstelement_class,
274       "Video (video4linux2) Sink", "Sink/Video",
275       "Displays frames on a video4linux2 device", "Rob Clark <rob@ti.com>,");
276
277   gst_element_class_add_pad_template
278       (gstelement_class,
279       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
280           gst_v4l2_object_get_all_caps ()));
281 }
282
283 static void
284 gst_v4l2sink_class_init (GstV4l2SinkClass * klass)
285 {
286   GObjectClass *gobject_class;
287   GstElementClass *element_class;
288   GstBaseSinkClass *basesink_class;
289
290   gobject_class = G_OBJECT_CLASS (klass);
291   element_class = GST_ELEMENT_CLASS (klass);
292   basesink_class = GST_BASE_SINK_CLASS (klass);
293
294   gobject_class->dispose = gst_v4l2sink_dispose;
295   gobject_class->finalize = (GObjectFinalizeFunc) gst_v4l2sink_finalize;
296   gobject_class->set_property = gst_v4l2sink_set_property;
297   gobject_class->get_property = gst_v4l2sink_get_property;
298
299   element_class->change_state = gst_v4l2sink_change_state;
300
301   gst_v4l2_object_install_properties_helper (gobject_class,
302       DEFAULT_PROP_DEVICE);
303   g_object_class_install_property (gobject_class, PROP_QUEUE_SIZE,
304       g_param_spec_uint ("queue-size", "Queue size",
305           "Number of buffers to be enqueud in the driver in streaming mode",
306           GST_V4L2_MIN_BUFFERS, GST_V4L2_MAX_BUFFERS, PROP_DEF_QUEUE_SIZE,
307           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
308   g_object_class_install_property (gobject_class, PROP_MIN_QUEUED_BUFS,
309       g_param_spec_uint ("min-queued-bufs", "Minimum queued bufs",
310           "Minimum number of queued bufs; v4l2sink won't dqbuf if the driver "
311           "doesn't have more than this number (which normally you shouldn't change)",
312           0, GST_V4L2_MAX_BUFFERS, PROP_DEF_MIN_QUEUED_BUFS,
313           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
314   g_object_class_install_property (gobject_class, PROP_OVERLAY_TOP,
315       g_param_spec_int ("overlay-top", "Overlay top",
316           "The topmost (y) coordinate of the video overlay; top left corner of screen is 0,0",
317           G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
318   g_object_class_install_property (gobject_class, PROP_OVERLAY_LEFT,
319       g_param_spec_int ("overlay-left", "Overlay left",
320           "The leftmost (x) coordinate of the video overlay; top left corner of screen is 0,0",
321           G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
322   g_object_class_install_property (gobject_class, PROP_OVERLAY_WIDTH,
323       g_param_spec_uint ("overlay-width", "Overlay width",
324           "The width of the video overlay; default is equal to negotiated image width",
325           0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
326   g_object_class_install_property (gobject_class, PROP_OVERLAY_HEIGHT,
327       g_param_spec_uint ("overlay-height", "Overlay height",
328           "The height of the video overlay; default is equal to negotiated image height",
329           0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
330
331   g_object_class_install_property (gobject_class, PROP_CROP_TOP,
332       g_param_spec_int ("crop-top", "Crop top",
333           "The topmost (y) coordinate of the video crop; top left corner of image is 0,0",
334           0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE));
335   g_object_class_install_property (gobject_class, PROP_CROP_LEFT,
336       g_param_spec_int ("crop-left", "Crop left",
337           "The leftmost (x) coordinate of the video crop; top left corner of image is 0,0",
338           0x80000000, 0x7fffffff, 0, G_PARAM_READWRITE));
339   g_object_class_install_property (gobject_class, PROP_CROP_WIDTH,
340       g_param_spec_uint ("crop-width", "Crop width",
341           "The width of the video crop; default is equal to negotiated image width",
342           0, 0xffffffff, 0, G_PARAM_READWRITE));
343   g_object_class_install_property (gobject_class, PROP_CROP_HEIGHT,
344       g_param_spec_uint ("crop-height", "Crop height",
345           "The height of the video crop; default is equal to negotiated image height",
346           0, 0xffffffff, 0, G_PARAM_READWRITE));
347
348   g_object_class_install_property (gobject_class, PROP_ROTATION,
349       g_param_spec_int ("rotation", "Rotation angle",
350           "Rotation angle for the image", MIN_ROTATION_ANGLE,
351           MAX_ROTATION_ANGLE, DEFAULT_ROTATION_ANGLE, G_PARAM_READWRITE));
352
353   g_object_class_install_property (gobject_class, PROP_FLIP,
354       g_param_spec_enum ("flip", "Flip State",
355           "Flip horizontal/vertical",
356           GST_TYPE_V4L2_FLIP, FLIP_NONE, G_PARAM_READWRITE));
357
358   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_get_caps);
359   basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_v4l2sink_set_caps);
360   basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR (gst_v4l2sink_buffer_alloc);
361   basesink_class->event = GST_DEBUG_FUNCPTR (gst_v4l2sink_event);
362   basesink_class->render = GST_DEBUG_FUNCPTR (gst_v4l2sink_show_frame);
363 }
364
365 static void
366 gst_v4l2sink_init (GstV4l2Sink * v4l2sink, GstV4l2SinkClass * klass)
367 {
368   v4l2sink->v4l2object = gst_v4l2_object_new (GST_ELEMENT (v4l2sink),
369       V4L2_BUF_TYPE_VIDEO_OUTPUT, DEFAULT_PROP_DEVICE,
370       gst_v4l2_get_output, gst_v4l2_set_output, NULL, gst_v4l2_update_geometry);
371
372
373   /* same default value for video output device as is used for
374    * v4l2src/capture is no good..  so lets set a saner default
375    * (which can be overridden by the one creating the v4l2sink
376    * after the constructor returns)
377    */
378   g_object_set (v4l2sink, "device", "/dev/video1", NULL);
379
380   /* number of buffers requested */
381   v4l2sink->num_buffers = PROP_DEF_QUEUE_SIZE;
382   v4l2sink->num_buffers_can_change = TRUE;
383   v4l2sink->min_queued_bufs = PROP_DEF_MIN_QUEUED_BUFS;
384
385   v4l2sink->probed_caps = NULL;
386   v4l2sink->current_caps = NULL;
387
388   v4l2sink->overlay_fields_set = 0;
389   v4l2sink->crop_fields_set = 0;
390   v4l2sink->state = 0;
391   v4l2sink->rotation = 0;
392   v4l2sink->flip = FLIP_NONE;
393
394   v4l2sink->video_par_n = 0;
395   v4l2sink->video_par_d = 0;
396   v4l2sink->display_par_n = 0;
397   v4l2sink->display_par_d = 0;
398 }
399
400 static void
401 gst_v4l2sink_sync_flip (GstV4l2Sink * v4l2sink)
402 {
403   if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
404     struct v4l2_control control;
405     gint fd = v4l2sink->v4l2object->video_fd;
406
407     memset (&control, 0x00, sizeof (struct v4l2_control));
408
409     switch (v4l2sink->flip) {
410       case FLIP_VERTICAL:
411         v4l2sink->rotation = 0;
412         control.value = 1;
413         break;
414       case FLIP_HORIZONTAL:
415         /* Horizontal Flip = Vertical Flip + 180 rotation */
416         v4l2sink->rotation = 180;
417         control.value = 1;
418         break;
419       case FLIP_NONE:
420         /* In the below switch case logic we need to handle FLIP_NONE
421          * case since the v4l2 driver holds on to the last configured
422          * flip value even after the device file is closed.
423          */
424         control.value = 0;
425         break;
426       default:
427         GST_WARNING_OBJECT (v4l2sink, "Invalid flip property");
428         control.value = 0;
429         break;
430     }
431
432     gst_v4l2sink_sync_rotation (v4l2sink);
433     control.id = V4L2_CID_VFLIP;
434     g_return_if_fail (v4l2_ioctl (fd, VIDIOC_S_CTRL, &control) >= 0);
435   }
436 }
437
438 static void
439 gst_v4l2sink_dispose (GObject * object)
440 {
441   GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);
442
443   if (v4l2sink->probed_caps) {
444     gst_caps_unref (v4l2sink->probed_caps);
445   }
446
447   if (v4l2sink->current_caps) {
448     gst_caps_unref (v4l2sink->current_caps);
449   }
450
451   G_OBJECT_CLASS (parent_class)->dispose (object);
452 }
453
454
455 static void
456 gst_v4l2sink_finalize (GstV4l2Sink * v4l2sink)
457 {
458   gst_v4l2_object_destroy (v4l2sink->v4l2object);
459
460   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) (v4l2sink));
461 }
462
463
464 /*
465  * State values
466  */
467 enum
468 {
469   STATE_OFF = 0,
470   STATE_PENDING_STREAMON,
471   STATE_STREAMING
472 };
473
474 /*
475  * flags to indicate which overlay/crop properties the user has set (and
476  * therefore which ones should override the defaults from the driver)
477  */
478 enum
479 {
480   RECT_TOP_SET = 0x01,
481   RECT_LEFT_SET = 0x02,
482   RECT_WIDTH_SET = 0x04,
483   RECT_HEIGHT_SET = 0x08
484 };
485
486 static void
487 gst_v4l2sink_sync_overlay_fields (GstV4l2Sink * v4l2sink)
488 {
489   if (!v4l2sink->overlay_fields_set)
490     return;
491
492   if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
493
494     gint fd = v4l2sink->v4l2object->video_fd;
495     struct v4l2_format format;
496
497     memset (&format, 0x00, sizeof (struct v4l2_format));
498     format.type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
499
500     if (v4l2_ioctl (fd, VIDIOC_G_FMT, &format) < 0) {
501       GST_WARNING_OBJECT (v4l2sink, "VIDIOC_G_FMT failed");
502       return;
503     }
504
505     GST_INFO_OBJECT (v4l2sink,
506         "setting overlay: overlay_fields_set=0x%02x, top=%d, left=%d, width=%d, height=%d",
507         v4l2sink->overlay_fields_set,
508         v4l2sink->overlay.top, v4l2sink->overlay.left,
509         v4l2sink->overlay.width, v4l2sink->overlay.height);
510
511     if (v4l2sink->overlay_fields_set & RECT_TOP_SET)
512       format.fmt.win.w.top = v4l2sink->overlay.top;
513     if (v4l2sink->overlay_fields_set & RECT_LEFT_SET)
514       format.fmt.win.w.left = v4l2sink->overlay.left;
515     if (v4l2sink->overlay_fields_set & RECT_WIDTH_SET)
516       format.fmt.win.w.width = v4l2sink->overlay.width;
517     if (v4l2sink->overlay_fields_set & RECT_HEIGHT_SET)
518       format.fmt.win.w.height = v4l2sink->overlay.height;
519
520     if (v4l2_ioctl (fd, VIDIOC_S_FMT, &format) < 0) {
521       GST_WARNING_OBJECT (v4l2sink, "VIDIOC_S_FMT failed");
522       return;
523     }
524
525     v4l2sink->overlay_fields_set = 0;
526     v4l2sink->overlay = format.fmt.win.w;
527   }
528 }
529
530 static void
531 gst_v4l2sink_sync_crop_fields (GstV4l2Sink * v4l2sink)
532 {
533   if (!v4l2sink->crop_fields_set)
534     return;
535
536   if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
537
538     gint fd = v4l2sink->v4l2object->video_fd;
539     struct v4l2_crop crop;
540
541     memset (&crop, 0x00, sizeof (struct v4l2_crop));
542     crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
543
544     if (v4l2_ioctl (fd, VIDIOC_G_CROP, &crop) < 0) {
545       GST_WARNING_OBJECT (v4l2sink, "VIDIOC_G_CROP failed");
546       return;
547     }
548
549     GST_INFO_OBJECT (v4l2sink,
550         "setting crop: crop_fields_set=0x%02x, top=%d, left=%d, width=%d, height=%d",
551         v4l2sink->crop_fields_set,
552         v4l2sink->crop.top, v4l2sink->crop.left,
553         v4l2sink->crop.width, v4l2sink->crop.height);
554
555     if (v4l2sink->crop_fields_set & RECT_TOP_SET)
556       crop.c.top = v4l2sink->crop.top;
557     if (v4l2sink->crop_fields_set & RECT_LEFT_SET)
558       crop.c.left = v4l2sink->crop.left;
559     if (v4l2sink->crop_fields_set & RECT_WIDTH_SET)
560       crop.c.width = v4l2sink->crop.width;
561     if (v4l2sink->crop_fields_set & RECT_HEIGHT_SET)
562       crop.c.height = v4l2sink->crop.height;
563
564     if (v4l2_ioctl (fd, VIDIOC_S_CROP, &crop) < 0) {
565       GST_WARNING_OBJECT (v4l2sink, "VIDIOC_S_CROP failed");
566       return;
567     }
568
569     v4l2sink->crop_fields_set = 0;
570     v4l2sink->crop = crop.c;
571   }
572 }
573
574 static void
575 gst_v4l2sink_sync_rotation (GstV4l2Sink * v4l2sink)
576 {
577   if (GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
578     struct v4l2_control control;
579     gint fd = v4l2sink->v4l2object->video_fd;
580
581     memset (&control, 0x00, sizeof (struct v4l2_control));
582     control.id = V4L2_CID_ROTATE;
583     control.value = v4l2sink->rotation;
584     g_return_if_fail (v4l2_ioctl (fd, VIDIOC_S_CTRL, &control) >= 0);
585   }
586 }
587
588
589 static void
590 gst_v4l2sink_set_property (GObject * object,
591     guint prop_id, const GValue * value, GParamSpec * pspec)
592 {
593   GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);
594
595   if (!gst_v4l2_object_set_property_helper (v4l2sink->v4l2object,
596           prop_id, value, pspec)) {
597     switch (prop_id) {
598       case PROP_QUEUE_SIZE:
599         v4l2sink->num_buffers = g_value_get_uint (value);
600         break;
601       case PROP_MIN_QUEUED_BUFS:
602         v4l2sink->min_queued_bufs = g_value_get_uint (value);
603         break;
604       case PROP_OVERLAY_TOP:
605         v4l2sink->overlay.top = g_value_get_int (value);
606         v4l2sink->overlay_fields_set |= RECT_TOP_SET;
607         gst_v4l2sink_sync_overlay_fields (v4l2sink);
608         break;
609       case PROP_OVERLAY_LEFT:
610         v4l2sink->overlay.left = g_value_get_int (value);
611         v4l2sink->overlay_fields_set |= RECT_LEFT_SET;
612         gst_v4l2sink_sync_overlay_fields (v4l2sink);
613         break;
614       case PROP_OVERLAY_WIDTH:
615         v4l2sink->overlay.width = g_value_get_uint (value);
616         v4l2sink->overlay_fields_set |= RECT_WIDTH_SET;
617         gst_v4l2sink_sync_overlay_fields (v4l2sink);
618         break;
619       case PROP_OVERLAY_HEIGHT:
620         v4l2sink->overlay.height = g_value_get_uint (value);
621         v4l2sink->overlay_fields_set |= RECT_HEIGHT_SET;
622         gst_v4l2sink_sync_overlay_fields (v4l2sink);
623         break;
624       case PROP_CROP_TOP:
625         v4l2sink->crop.top = g_value_get_int (value);
626         v4l2sink->crop_fields_set |= RECT_TOP_SET;
627         gst_v4l2sink_sync_crop_fields (v4l2sink);
628         break;
629       case PROP_CROP_LEFT:
630         v4l2sink->crop.left = g_value_get_int (value);
631         v4l2sink->crop_fields_set |= RECT_LEFT_SET;
632         gst_v4l2sink_sync_crop_fields (v4l2sink);
633         break;
634       case PROP_CROP_WIDTH:
635         v4l2sink->crop.width = g_value_get_uint (value);
636         v4l2sink->crop_fields_set |= RECT_WIDTH_SET;
637         gst_v4l2sink_sync_crop_fields (v4l2sink);
638         break;
639       case PROP_CROP_HEIGHT:
640         v4l2sink->crop.height = g_value_get_uint (value);
641         v4l2sink->crop_fields_set |= RECT_HEIGHT_SET;
642         gst_v4l2sink_sync_crop_fields (v4l2sink);
643         break;
644       case PROP_ROTATION:
645         v4l2sink->rotation = g_value_get_int (value);
646         gst_v4l2sink_sync_rotation (v4l2sink);
647         break;
648       case PROP_FLIP:
649         v4l2sink->flip = g_value_get_enum (value);
650         gst_v4l2sink_sync_flip (v4l2sink);
651         break;
652       default:
653         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
654         break;
655     }
656   }
657 }
658
659
660 static void
661 gst_v4l2sink_get_property (GObject * object,
662     guint prop_id, GValue * value, GParamSpec * pspec)
663 {
664   GstV4l2Sink *v4l2sink = GST_V4L2SINK (object);
665
666   if (!gst_v4l2_object_get_property_helper (v4l2sink->v4l2object,
667           prop_id, value, pspec)) {
668     switch (prop_id) {
669       case PROP_QUEUE_SIZE:
670         g_value_set_uint (value, v4l2sink->num_buffers);
671         break;
672       case PROP_MIN_QUEUED_BUFS:
673         g_value_set_uint (value, v4l2sink->min_queued_bufs);
674         break;
675       case PROP_OVERLAY_TOP:
676         g_value_set_int (value, v4l2sink->overlay.top);
677         break;
678       case PROP_OVERLAY_LEFT:
679         g_value_set_int (value, v4l2sink->overlay.left);
680         break;
681       case PROP_OVERLAY_WIDTH:
682         g_value_set_uint (value, v4l2sink->overlay.width);
683         break;
684       case PROP_OVERLAY_HEIGHT:
685         g_value_set_uint (value, v4l2sink->overlay.height);
686         break;
687       case PROP_CROP_TOP:
688         g_value_set_int (value, v4l2sink->crop.top);
689         break;
690       case PROP_CROP_LEFT:
691         g_value_set_int (value, v4l2sink->crop.left);
692         break;
693       case PROP_CROP_WIDTH:
694         g_value_set_uint (value, v4l2sink->crop.width);
695         break;
696       case PROP_CROP_HEIGHT:
697         g_value_set_uint (value, v4l2sink->crop.height);
698         break;
699       case PROP_ROTATION:
700         g_value_set_int (value, v4l2sink->rotation);
701         break;
702       case PROP_FLIP:
703         g_value_set_enum (value, v4l2sink->flip);
704         break;
705       default:
706         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
707         break;
708     }
709   }
710 }
711
712 static GstStateChangeReturn
713 gst_v4l2sink_change_state (GstElement * element, GstStateChange transition)
714 {
715   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
716   GstV4l2Sink *v4l2sink = GST_V4L2SINK (element);
717
718   GST_DEBUG_OBJECT (v4l2sink, "%d -> %d",
719       GST_STATE_TRANSITION_CURRENT (transition),
720       GST_STATE_TRANSITION_NEXT (transition));
721
722   switch (transition) {
723     case GST_STATE_CHANGE_NULL_TO_READY:
724       /* open the device */
725       if (!gst_v4l2_object_start (v4l2sink->v4l2object))
726         return GST_STATE_CHANGE_FAILURE;
727       gst_v4l2sink_sync_rotation (v4l2sink);
728       gst_v4l2sink_sync_flip (v4l2sink);
729       break;
730     default:
731       break;
732   }
733
734   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
735
736   switch (transition) {
737     case GST_STATE_CHANGE_PAUSED_TO_READY:
738       if (v4l2sink->state == STATE_STREAMING) {
739         if (!gst_v4l2_object_stop_streaming (v4l2sink->v4l2object)) {
740           return GST_STATE_CHANGE_FAILURE;
741         }
742         v4l2sink->state = STATE_PENDING_STREAMON;
743         v4l2sink->video_par_n = 0;
744         v4l2sink->video_par_d = 0;
745         v4l2sink->display_par_n = 0;
746         v4l2sink->display_par_d = 0;
747       }
748       break;
749     case GST_STATE_CHANGE_READY_TO_NULL:
750       if (v4l2sink->pool) {
751         gst_v4l2_buffer_pool_stop_running (v4l2sink->pool);
752         gst_mini_object_unref (GST_MINI_OBJECT (v4l2sink->pool));
753         v4l2sink->pool = NULL;
754       }
755       /* close the device */
756       if (!gst_v4l2_object_stop (v4l2sink->v4l2object))
757         return GST_STATE_CHANGE_FAILURE;
758       v4l2sink->state = STATE_OFF;
759       break;
760     default:
761       break;
762   }
763
764   return ret;
765 }
766
767
768 static GstCaps *
769 gst_v4l2sink_get_caps (GstBaseSink * bsink)
770 {
771   GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
772   GstCaps *ret;
773   GSList *walk;
774   GSList *formats;
775
776   if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
777     /* FIXME: copy? */
778     GST_DEBUG_OBJECT (v4l2sink, "device is not open");
779     return
780         gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
781             (v4l2sink)));
782   }
783
784   if (v4l2sink->probed_caps) {
785     LOG_CAPS (v4l2sink, v4l2sink->probed_caps);
786     return gst_caps_ref (v4l2sink->probed_caps);
787   }
788
789   formats = gst_v4l2_object_get_format_list (v4l2sink->v4l2object);
790
791   ret = gst_caps_new_empty ();
792
793   for (walk = formats; walk; walk = walk->next) {
794     struct v4l2_fmtdesc *format;
795
796     GstStructure *templates[MAX_STRUCTS_PER_FOURCC];
797     gint count, i;
798
799     format = (struct v4l2_fmtdesc *) walk->data;
800
801     count = gst_v4l2_object_v4l2fourcc_to_structures (format->pixelformat,
802         templates);
803
804     for (i = 0; i < count; i++) {
805       GstCaps *tmp;
806
807       tmp = gst_v4l2_object_probe_caps_for_format (v4l2sink->v4l2object,
808           format->pixelformat, templates[i]);
809       if (tmp)
810         gst_caps_append (ret, tmp);
811
812       gst_structure_free (templates[i]);
813     }
814   }
815
816   v4l2sink->probed_caps = gst_caps_ref (ret);
817
818   GST_INFO_OBJECT (v4l2sink, "probed caps: %p", ret);
819   LOG_CAPS (v4l2sink, ret);
820
821   return ret;
822 }
823
824 static gboolean
825 gst_v4l2sink_calculate_display_rectangle (GstV4l2Sink * v4l2sink, guint width,
826     guint height, gint video_par_n, gint video_par_d, gint display_par_n,
827     gint display_par_d, GstVideoRectangle * rectangle)
828 {
829   guint num, den;
830   guint res_width, res_height;
831   GstVideoRectangle src, dst, result;
832
833   if (!gst_video_calculate_display_ratio (&num, &den, width,
834           height, video_par_n, video_par_d, display_par_n, display_par_d))
835     goto no_disp_ratio;
836
837   /* start with same height, because of interlaced video */
838   /* check hd / den is an integer scale factor, and scale wd with the PAR */
839   if (height % den == 0) {
840     GST_DEBUG_OBJECT (v4l2sink, "keeping video height");
841     res_width = gst_util_uint64_scale_int (height, num, den);
842     res_height = height;
843   } else if (width % num == 0) {
844     GST_DEBUG_OBJECT (v4l2sink, "keeping video width");
845     res_width = width;
846     res_height = gst_util_uint64_scale_int (width, den, num);
847   } else {
848     GST_DEBUG_OBJECT (v4l2sink, "approximating while keeping video height");
849     res_width = gst_util_uint64_scale_int (width, num, den);
850     res_height = height;
851   }
852
853   /* set the input resolution (adjusted with PAR) as the source rectangle */
854   src.w = res_width;
855   src.h = res_height;
856
857   /* use the X window coordinates as the destination rectangle */
858   dst = *rectangle;
859
860   /* fit the source in the X window */
861   gst_video_sink_center_rect (src, dst, &result, TRUE);
862
863   GST_INFO_OBJECT (v4l2sink, "video resolution before %dx%d after PAR %dx%d, "
864       "PAR %d/%d, window size %dx%d, overlay x:%d y:%d resolution %dx%d",
865       width, height, res_width, res_height,
866       v4l2sink->video_par_n, v4l2sink->video_par_d,
867       dst.w, dst.h, result.x, result.y, result.w, result.h);
868
869   *rectangle = result;
870
871   return TRUE;
872
873 no_disp_ratio:
874   {
875     GST_ELEMENT_ERROR (v4l2sink, CORE, NEGOTIATION, (NULL),
876         ("Error calculating the output display ratio of the video."));
877     return FALSE;
878   }
879 }
880
881 static gboolean
882 gst_v4l2sink_configure_overlay (GstV4l2Sink * v4l2sink, guint width,
883     guint height, gint video_par_n, gint video_par_d, guint display_par_n,
884     gint display_par_d)
885 {
886   GstVideoRectangle rect;
887
888   if (!gst_v4l2_xoverlay_get_render_rect (v4l2sink->v4l2object, &rect))
889     goto no_xv;
890
891   gst_v4l2sink_calculate_display_rectangle (v4l2sink, width, height,
892       video_par_n, video_par_d, display_par_n, display_par_d, &rect);
893
894   /* the centered rectangle is how we want to configure the overlay */
895   v4l2sink->overlay.top = rect.y;
896   v4l2sink->overlay.left = rect.x;
897   v4l2sink->overlay.width = rect.w;
898   v4l2sink->overlay.height = rect.h;
899   v4l2sink->overlay_fields_set |=
900       RECT_WIDTH_SET | RECT_HEIGHT_SET | RECT_LEFT_SET | RECT_TOP_SET;
901
902   gst_v4l2sink_sync_overlay_fields (v4l2sink);
903
904   return TRUE;
905
906 no_xv:
907   GST_INFO_OBJECT (v4l2sink, "no xv, leaving overlay unconfigured");
908   return TRUE;
909 }
910
911 static gboolean
912 gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
913 {
914   GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
915   GstQuery *query;
916   gint w = 0, h = 0, rs = 0;
917   gboolean interlaced;
918   struct v4l2_fmtdesc *format;
919   guint fps_n, fps_d;
920   guint size;
921   GstStructure *structure;
922   gint video_par_n, video_par_d;
923   gint display_par_n, display_par_d;
924   const GValue *caps_par;
925
926   LOG_CAPS (v4l2sink, caps);
927
928   if (!GST_V4L2_IS_OPEN (v4l2sink->v4l2object)) {
929     GST_DEBUG_OBJECT (v4l2sink, "device is not open");
930     return FALSE;
931   }
932
933   /* we want our own v4l2 type of fourcc codes */
934   if (!gst_v4l2_object_get_caps_info (v4l2sink->v4l2object, caps,
935           &format, &w, &h, &rs, &interlaced, &fps_n, &fps_d, &size)) {
936     GST_DEBUG_OBJECT (v4l2sink, "can't get capture format from caps %p", caps);
937     return FALSE;
938   }
939
940   GST_VIDEO_SINK_WIDTH (v4l2sink) = w;
941   GST_VIDEO_SINK_HEIGHT (v4l2sink) = h;
942
943   if (!format) {
944     GST_DEBUG_OBJECT (v4l2sink, "unrecognized caps!!");
945     return FALSE;
946   }
947
948   /* we need to make our own ref before we potentially update the
949    * caps, to avoid that we release a ref that is not owned by us
950    * when we make the caps writable
951    */
952   caps = gst_caps_ref (caps);
953
954   /* if necessary, update caps for rowstride */
955   if (rs) {
956     caps = gst_v4l2_object_update_rowstride (v4l2sink->v4l2object, caps, rs);
957     GST_DEBUG_OBJECT (v4l2sink, "updated caps: %" GST_PTR_FORMAT, caps);
958   }
959
960   if (v4l2sink->current_caps) {
961     GST_DEBUG_OBJECT (v4l2sink, "already have caps set.. are they equal?");
962     LOG_CAPS (v4l2sink, v4l2sink->current_caps);
963     if (gst_caps_is_equal (v4l2sink->current_caps, caps)) {
964       GST_DEBUG_OBJECT (v4l2sink, "yes they are!");
965       gst_caps_unref (caps);
966       return TRUE;
967     }
968     GST_DEBUG_OBJECT (v4l2sink, "no they aren't!");
969   }
970
971   if (v4l2sink->pool) {
972     /* TODO: if we've already allocated buffers, we probably need to
973      * do something here to free and reallocate....
974      *
975      *   gst_v4l2_object_stop_streaming()
976      *   gst_v4l2_buffer_pool_destroy()
977      *
978      */
979     gst_v4l2_object_stop_streaming (v4l2sink->v4l2object);
980     gst_v4l2_buffer_pool_stop_running (v4l2sink->pool);
981     gst_mini_object_unref (GST_MINI_OBJECT (v4l2sink->pool));
982     v4l2sink->pool = NULL;
983     return TRUE;
984   }
985
986   /* query to find if anyone upstream using these buffers has any
987    * minimum requirements:
988    */
989   query = gst_query_new_buffers (caps);
990   if (gst_element_query (GST_ELEMENT (v4l2sink), query)) {
991     gint min_buffers, min_width, min_height;
992
993     gst_query_parse_buffers_count (query, &min_buffers);
994
995     GST_DEBUG_OBJECT (v4l2sink, "min_buffers=%d", min_buffers);
996
997     /* XXX need to account for some buffers used by queue, etc.. probably
998      * queue should handle query, pass on to sink pad, and then add some
999      * number of buffers to the min, so this value is dynamic depending
1000      * on the pipeline?
1001      */
1002     if (min_buffers != -1) {
1003       min_buffers += 3 + v4l2sink->min_queued_bufs;
1004       v4l2sink->num_buffers_can_change = FALSE;
1005     }
1006
1007     if (min_buffers > v4l2sink->num_buffers) {
1008       v4l2sink->num_buffers = min_buffers;
1009     }
1010
1011     gst_query_parse_buffers_dimensions (query, &min_width, &min_height);
1012     if (min_width > w)
1013       w = min_width;
1014     if (min_height > h)
1015       h = min_height;
1016   }
1017   gst_query_unref (query);
1018
1019   if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat,
1020           w, h, interlaced)) {
1021     /* error already posted */
1022     gst_caps_unref (caps);
1023     return FALSE;
1024   }
1025
1026   /* clear top/left crop values.. otherwise by default display will try
1027    * to center, rather than scale, the image if it is too big to fit on
1028    * display
1029    */
1030   v4l2sink->crop.top = v4l2sink->crop.left = 0;
1031   v4l2sink->crop_fields_set |= RECT_TOP_SET | RECT_LEFT_SET;
1032   v4l2sink->crop.width = w;
1033   v4l2sink->crop_fields_set |= RECT_WIDTH_SET;
1034   v4l2sink->crop.height = h;
1035   v4l2sink->crop_fields_set |= RECT_HEIGHT_SET;
1036
1037   /* this needs to go after gst_v4l2_object_set_format() to ensure driver
1038    * has proper width/height (so we don't confuse it's error checking by
1039    * setting a crop larger than the picture size)
1040    */
1041   gst_v4l2sink_sync_crop_fields (v4l2sink);
1042
1043   structure = gst_caps_get_structure (caps, 0);
1044   caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1045   if (caps_par) {
1046     video_par_n = gst_value_get_fraction_numerator (caps_par);
1047     video_par_d = gst_value_get_fraction_denominator (caps_par);
1048   } else {
1049     video_par_n = 1;
1050     video_par_d = 1;
1051   }
1052
1053   GST_INFO_OBJECT (v4l2sink, "PAR from caps %d/%d %s",
1054       video_par_n, video_par_d, gst_structure_to_string (structure));
1055
1056   /* FIXME: get these from v4l2/X */
1057   display_par_n = 1;
1058   display_par_d = 1;
1059
1060   v4l2sink->video_par_n = video_par_n;
1061   v4l2sink->video_par_d = video_par_d;
1062   v4l2sink->display_par_n = display_par_n;
1063   v4l2sink->display_par_d = display_par_d;
1064
1065 #ifdef HAVE_XVIDEO
1066   gst_v4l2_xoverlay_prepare_xwindow_id (v4l2sink->v4l2object, TRUE);
1067 #endif
1068
1069   v4l2sink->current_caps = caps;
1070
1071   return gst_v4l2sink_configure_overlay (v4l2sink,
1072       w, h, v4l2sink->video_par_n, v4l2sink->video_par_d,
1073       v4l2sink->display_par_n, v4l2sink->display_par_d);
1074 }
1075
1076 /* buffer alloc function to implement pad_alloc for upstream element */
1077 static GstFlowReturn
1078 gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1079     GstCaps * caps, GstBuffer ** buf)
1080 {
1081   GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
1082   GstV4l2Buffer *v4l2buf;
1083
1084   if (v4l2sink->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) {
1085
1086     /* initialize the buffer pool if not initialized yet (first buffer): */
1087     if (G_UNLIKELY (!v4l2sink->pool)) {
1088
1089       gboolean no_pending_streamon = FALSE;
1090       char *driver = (char *) v4l2sink->v4l2object->vcap.driver;
1091
1092       /* the omap24xxvout driver wants us to start streaming before we
1093        * queue the first buffer:
1094        */
1095       if (!strcmp ("omap24xxvout", driver)) {
1096         GST_DEBUG_OBJECT (v4l2sink,
1097             "enabling no_pending_streamon hack for omap24xxvout driver");
1098         no_pending_streamon = TRUE;
1099       }
1100
1101       /* workaround for bug in omap_vout driver, when we ask for more
1102        * than four buffers:
1103        */
1104       if (!strcmp ("omap_vout", driver)) {
1105         if (v4l2sink->num_buffers_can_change && v4l2sink->num_buffers > 4) {
1106           v4l2sink->num_buffers = 4;
1107           GST_DEBUG_OBJECT (v4l2sink,
1108               "limiting to 4 buffers to work-around omap_vout driver bug");
1109         }
1110       }
1111
1112       /* set_caps() might not be called yet.. so just to make sure: */
1113       if (!gst_v4l2sink_set_caps (bsink, caps)) {
1114         return GST_FLOW_ERROR;
1115       }
1116
1117       /* caps may have changed in _set_caps() if we need rowstride */
1118       caps = v4l2sink->current_caps;
1119
1120       GST_V4L2_CHECK_OPEN (v4l2sink->v4l2object);
1121
1122       if (!(v4l2sink->pool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2sink),
1123                   v4l2sink->v4l2object->video_fd,
1124                   v4l2sink->num_buffers, caps, FALSE,
1125                   V4L2_BUF_TYPE_VIDEO_OUTPUT))) {
1126         return GST_FLOW_ERROR;
1127       }
1128
1129       gst_v4l2sink_sync_overlay_fields (v4l2sink);
1130       gst_v4l2sink_sync_crop_fields (v4l2sink);
1131
1132       if (no_pending_streamon) {
1133         if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) {
1134           return GST_FLOW_ERROR;
1135         }
1136         v4l2sink->state = STATE_STREAMING;
1137       } else {
1138         v4l2sink->state = STATE_PENDING_STREAMON;
1139       }
1140
1141       GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()");
1142
1143       if (v4l2sink->num_buffers != v4l2sink->pool->buffer_count) {
1144         if (!v4l2sink->num_buffers_can_change) {
1145           GST_WARNING_OBJECT (v4l2sink,
1146               "I can't handle a differing number of buffers!!!!");
1147           return GST_FLOW_ERROR;
1148         }
1149         v4l2sink->num_buffers = v4l2sink->pool->buffer_count;
1150         g_object_notify (G_OBJECT (v4l2sink), "queue-size");
1151       }
1152     }
1153
1154     v4l2buf = gst_v4l2_buffer_pool_get (v4l2sink->pool, TRUE);
1155
1156     if (G_LIKELY (v4l2buf && v4l2buf != GST_V4L2_BUFFER_SENTINEL)) {
1157       GST_DEBUG_OBJECT (v4l2sink, "allocated buffer: %p size %d",
1158           v4l2buf, size);
1159       *buf = GST_BUFFER (v4l2buf);
1160       return GST_FLOW_OK;
1161     } else {
1162       GST_DEBUG_OBJECT (v4l2sink, "failed to allocate buffer");
1163       return GST_FLOW_ERROR;
1164     }
1165
1166   } else {
1167     GST_ERROR_OBJECT (v4l2sink, "only supporting streaming mode for now...");
1168     return GST_FLOW_ERROR;
1169   }
1170 }
1171
1172 /* called to handle events */
1173 static gboolean
1174 gst_v4l2sink_event (GstBaseSink * bsink, GstEvent * event)
1175 {
1176   GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
1177   GstEventType type = GST_EVENT_TYPE (event);
1178
1179   GST_DEBUG_OBJECT (v4l2sink, "event %" GST_PTR_FORMAT, event);
1180
1181   switch (type) {
1182     case GST_EVENT_CROP:{
1183       gint top, left, width, height;
1184       gst_event_parse_crop (event, &top, &left, &width, &height);
1185       if (top >= 0) {
1186         v4l2sink->crop.top = top;
1187         v4l2sink->crop_fields_set |= RECT_TOP_SET;
1188       }
1189       if (left >= 0) {
1190         v4l2sink->crop.left = left;
1191         v4l2sink->crop_fields_set |= RECT_LEFT_SET;
1192       }
1193       if (width >= 0) {
1194         v4l2sink->crop.width = width;
1195         v4l2sink->crop_fields_set |= RECT_WIDTH_SET;
1196       }
1197       if (height >= 0) {
1198         v4l2sink->crop.height = height;
1199         v4l2sink->crop_fields_set |= RECT_HEIGHT_SET;
1200       }
1201       gst_v4l2sink_sync_crop_fields (v4l2sink);
1202       if (width >= 0 && height > 0) {
1203         GstMessage *msg;
1204
1205         GST_VIDEO_SINK_WIDTH (v4l2sink) = width;
1206         GST_VIDEO_SINK_HEIGHT (v4l2sink) = height;
1207
1208         gst_v4l2sink_configure_overlay (v4l2sink,
1209             width, height, v4l2sink->video_par_n, v4l2sink->video_par_d,
1210             v4l2sink->display_par_n, v4l2sink->display_par_d);
1211         /* call set_window_handle with the current handle to update its geometry
1212          */
1213         gst_v4l2_xoverlay_set_window_handle (v4l2sink->v4l2object,
1214             v4l2sink->v4l2object->xwindow_id);
1215
1216         /* send a message to let totem know that we're cropping and it should
1217          * adjust the aspect ratio */
1218         msg = gst_message_new_application (GST_OBJECT (v4l2sink),
1219             gst_structure_new ("video-size-crop",
1220                 "width", G_TYPE_INT, GST_VIDEO_SINK_WIDTH (v4l2sink),
1221                 "height", G_TYPE_INT, GST_VIDEO_SINK_HEIGHT (v4l2sink), NULL));
1222         gst_element_post_message (GST_ELEMENT (v4l2sink), msg);
1223       }
1224       return TRUE;
1225     }
1226     case GST_EVENT_LIVE_FLUSH:
1227       g_async_queue_push (v4l2sink->pool->avail_buffers,
1228           GST_V4L2_BUFFER_SENTINEL);
1229       return TRUE;
1230     default:{
1231       if (GST_BASE_SINK_CLASS (parent_class)->event) {
1232         return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
1233       } else {
1234         return TRUE;
1235       }
1236     }
1237   }
1238 }
1239
1240 /* called after A/V sync to render frame */
1241 static GstFlowReturn
1242 gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1243 {
1244   GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
1245   GstBuffer *newbuf = NULL;
1246
1247   GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf);
1248
1249   if (!GST_IS_V4L2_BUFFER (buf)) {
1250     GstFlowReturn ret;
1251
1252     /* special case check for sub-buffers:  In certain cases, places like
1253      * GstBaseTransform, which might check that the buffer is writable
1254      * before copying metadata, timestamp, and such, will find that the
1255      * buffer has more than one reference to it.  In these cases, they
1256      * will create a sub-buffer with an offset=0 and length equal to the
1257      * original buffer size.
1258      *
1259      * This could happen in two scenarios: (1) a tee in the pipeline, and
1260      * (2) because the refcnt is incremented in gst_mini_object_free()
1261      * before the finalize function is called, and decremented after it
1262      * returns..  but returning this buffer to the buffer pool in the
1263      * finalize function, could wake up a thread blocked in _buffer_alloc()
1264      * which could run and get a buffer w/ refcnt==2 before the thread
1265      * originally unref'ing the buffer returns from finalize function and
1266      * decrements the refcnt back to 1!
1267      */
1268     if (buf->parent &&
1269         (GST_BUFFER_DATA (buf) == GST_BUFFER_DATA (buf->parent)) &&
1270         (GST_BUFFER_SIZE (buf) == GST_BUFFER_SIZE (buf->parent))) {
1271       GST_DEBUG_OBJECT (v4l2sink, "I have a sub-buffer!");
1272       return gst_v4l2sink_show_frame (bsink, buf->parent);
1273     }
1274
1275     GST_DEBUG_OBJECT (v4l2sink, "slow-path.. I got a %s so I need to memcpy",
1276         g_type_name (G_OBJECT_TYPE (buf)));
1277
1278     ret = gst_v4l2sink_buffer_alloc (bsink,
1279         GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf),
1280         &newbuf);
1281
1282     if (GST_FLOW_OK != ret) {
1283       GST_DEBUG_OBJECT (v4l2sink,
1284           "dropping frame!  Consider increasing 'queue-size' property!");
1285       return GST_FLOW_OK;
1286     }
1287
1288     memcpy (GST_BUFFER_DATA (newbuf),
1289         GST_BUFFER_DATA (buf),
1290         MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf)));
1291
1292     GST_DEBUG_OBJECT (v4l2sink, "render copied buffer: %p", newbuf);
1293
1294     buf = newbuf;
1295   }
1296
1297   if (!gst_v4l2_buffer_pool_qbuf (v4l2sink->pool, GST_V4L2_BUFFER (buf))) {
1298     return GST_FLOW_ERROR;
1299   }
1300
1301   if (v4l2sink->state == STATE_PENDING_STREAMON) {
1302     if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) {
1303       return GST_FLOW_ERROR;
1304     }
1305     v4l2sink->state = STATE_STREAMING;
1306   }
1307
1308   if (!newbuf) {
1309     gst_buffer_ref (buf);
1310   }
1311
1312   /* if the driver has more than one buffer, ie. more than just the one we
1313    * just queued, then dequeue one immediately to make it available via
1314    * _buffer_alloc():
1315    */
1316   if (gst_v4l2_buffer_pool_available_buffers (v4l2sink->pool) >
1317       v4l2sink->min_queued_bufs) {
1318     GstV4l2Buffer *v4l2buf = gst_v4l2_buffer_pool_dqbuf (v4l2sink->pool);
1319
1320     /* note: if we get a buf, we don't want to use it directly (because
1321      * someone else could still hold a ref).. but instead we release our
1322      * reference to it, and if no one else holds a ref it will be returned
1323      * to the pool of available buffers..  and if not, we keep looping.
1324      */
1325     if (v4l2buf) {
1326       gst_buffer_unref (GST_BUFFER (v4l2buf));
1327     }
1328   }
1329
1330   return GST_FLOW_OK;
1331 }
1332
1333 #ifdef HAVE_XVIDEO
1334 static void
1335 gst_v4l2sink_navigation_send_event (GstNavigation * navigation,
1336     GstStructure * structure)
1337 {
1338   GstV4l2Sink *v4l2sink = GST_V4L2SINK (navigation);
1339   GstV4l2Xv *xv = v4l2sink->v4l2object->xv;
1340   GstPad *peer;
1341
1342   if (!xv)
1343     return;
1344
1345   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (v4l2sink)))) {
1346     GstVideoRectangle rect;
1347     gdouble x, y, xscale = 1.0, yscale = 1.0;
1348
1349     gst_v4l2_xoverlay_get_render_rect (v4l2sink->v4l2object, &rect);
1350
1351     /* We calculate scaling using the original video frames geometry to
1352      * include pixel aspect ratio scaling.
1353      */
1354     xscale = (gdouble) GST_VIDEO_SINK_WIDTH (v4l2sink) / rect.w;
1355     yscale = (gdouble) GST_VIDEO_SINK_HEIGHT (v4l2sink) / rect.h;
1356
1357     /* Converting pointer coordinates to the non scaled geometry */
1358     if (gst_structure_get_double (structure, "pointer_x", &x)) {
1359       x = MIN (x, rect.x + rect.w);
1360       x = MAX (x - rect.x, 0);
1361       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1362           (gdouble) x * xscale, NULL);
1363     }
1364     if (gst_structure_get_double (structure, "pointer_y", &y)) {
1365       y = MIN (y, rect.y + rect.h);
1366       y = MAX (y - rect.y, 0);
1367       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1368           (gdouble) y * yscale, NULL);
1369     }
1370
1371     gst_pad_send_event (peer, gst_event_new_navigation (structure));
1372     gst_object_unref (peer);
1373   }
1374 }
1375 #endif
1376
1377 static gboolean
1378 gst_v4l2_update_geometry (GstV4l2Object * v4l2object, GstVideoRectangle * rect)
1379 {
1380   GstV4l2Sink *v4l2sink = GST_V4L2SINK (v4l2object->element);
1381   GST_INFO_OBJECT (v4l2sink, "geometry changed");
1382
1383   if (!GST_VIDEO_SINK_WIDTH (v4l2sink) || !GST_VIDEO_SINK_HEIGHT (v4l2sink))
1384     return FALSE;
1385
1386   gst_v4l2sink_calculate_display_rectangle (v4l2sink,
1387       GST_VIDEO_SINK_WIDTH (v4l2sink), GST_VIDEO_SINK_HEIGHT (v4l2sink),
1388       v4l2sink->video_par_n, v4l2sink->video_par_d,
1389       v4l2sink->display_par_n, v4l2sink->display_par_d, rect);
1390
1391   return TRUE;
1392 }