5ac3020636adc2f7d87b93f60820c839d3d73628
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2xoverlay.c
1 /* GStreamer
2  *
3  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
4  *               2006 Edgard Lima <edgard.lima@indt.org.br>
5  *
6  * gstv4l2xoverlay.c: X-based overlay interface implementation for V4L2
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <string.h>
29 #include <gst/gst.h>
30 #include <X11/X.h>
31 #include <X11/Xlib.h>
32 #include <X11/extensions/Xv.h>
33 #include <X11/extensions/Xvlib.h>
34 #include <sys/stat.h>
35
36 #include "gstv4l2xoverlay.h"
37 #include "gstv4l2object.h"
38 #include "v4l2_calls.h"
39
40 struct _GstV4l2Xv
41 {
42   Display *dpy;
43   gint port, idle_id;
44   GMutex *mutex;
45 };
46
47 GST_DEBUG_CATEGORY_STATIC (v4l2xv_debug);
48 #define GST_CAT_DEFAULT v4l2xv_debug
49
50 void
51 gst_v4l2_xoverlay_interface_init (GstXOverlayClass * klass)
52 {
53   GST_DEBUG_CATEGORY_INIT (v4l2xv_debug, "v4l2xv", 0,
54       "V4L2 XOverlay interface debugging");
55 }
56
57 static void
58 gst_v4l2_xoverlay_open (GstV4l2Object * v4l2object)
59 {
60   struct stat s;
61   GstV4l2Xv *v4l2xv;
62   const gchar *name = g_getenv ("DISPLAY");
63   unsigned int ver, rel, req, ev, err, anum;
64   int i, id = 0, first_id = 0, min;
65   XvAdaptorInfo *ai;
66   Display *dpy;
67
68   /* we need a display, obviously */
69   if (!name || !(dpy = XOpenDisplay (name))) {
70     GST_WARNING_OBJECT (v4l2object->element,
71         "No $DISPLAY set or failed to open - no overlay");
72     return;
73   }
74
75   /* First let's check that XVideo extension is available */
76   if (!XQueryExtension (dpy, "XVideo", &i, &i, &i)) {
77     GST_WARNING_OBJECT (v4l2object->element,
78         "Xv extension not available - no overlay");
79     XCloseDisplay (dpy);
80     return;
81   }
82
83   /* find port that belongs to this device */
84   if (XvQueryExtension (dpy, &ver, &rel, &req, &ev, &err) != Success) {
85     GST_WARNING_OBJECT (v4l2object->element,
86         "Xv extension not supported - no overlay");
87     XCloseDisplay (dpy);
88     return;
89   }
90   if (XvQueryAdaptors (dpy, DefaultRootWindow (dpy), &anum, &ai) != Success) {
91     GST_WARNING_OBJECT (v4l2object->element, "Failed to query Xv adaptors");
92     XCloseDisplay (dpy);
93     return;
94   }
95   if (fstat (v4l2object->video_fd, &s) < 0) {
96     GST_ELEMENT_ERROR (v4l2object->element, RESOURCE, NOT_FOUND,
97         (_("Cannot identify device '%s'."), v4l2object->videodev),
98         GST_ERROR_SYSTEM);
99     XCloseDisplay (dpy);
100     return;
101   }
102   min = s.st_rdev & 0xff;
103   for (i = 0; i < anum; i++) {
104     if (!strcmp (ai[i].name, "video4linux2")) {
105       if (first_id == 0)
106         first_id = ai[i].base_id;
107
108       /* hmm... */
109       if (first_id != 0 && ai[i].base_id == first_id + min)
110         id = ai[i].base_id;
111     }
112   }
113   XvFreeAdaptorInfo (ai);
114
115   if (id == 0) {
116     GST_WARNING_OBJECT (v4l2object->element,
117         "Did not find XvPortID for device - no overlay");
118     XCloseDisplay (dpy);
119     return;
120   }
121
122   v4l2xv = g_new0 (GstV4l2Xv, 1);
123   v4l2xv->dpy = dpy;
124   v4l2xv->port = id;
125   v4l2xv->mutex = g_mutex_new ();
126   v4l2xv->idle_id = 0;
127   v4l2object->xv = v4l2xv;
128
129   if (v4l2object->xwindow_id) {
130     gst_v4l2_xoverlay_set_xwindow_id (v4l2object, v4l2object->xwindow_id);
131   }
132 }
133
134 static void
135 gst_v4l2_xoverlay_close (GstV4l2Object * v4l2object)
136 {
137   GstV4l2Xv *v4l2xv = v4l2object->xv;
138
139   if (!v4l2object->xv)
140     return;
141
142   if (v4l2object->xwindow_id) {
143     gst_v4l2_xoverlay_set_xwindow_id (v4l2object, 0);
144   }
145
146   XCloseDisplay (v4l2xv->dpy);
147   g_mutex_free (v4l2xv->mutex);
148   if (v4l2xv->idle_id)
149     g_source_remove (v4l2xv->idle_id);
150   g_free (v4l2xv);
151   v4l2object->xv = NULL;
152 }
153
154 void
155 gst_v4l2_xoverlay_start (GstV4l2Object * v4l2object)
156 {
157   if (v4l2object->xwindow_id) {
158     gst_v4l2_xoverlay_open (v4l2object);
159   }
160 }
161
162 void
163 gst_v4l2_xoverlay_stop (GstV4l2Object * v4l2object)
164 {
165   gst_v4l2_xoverlay_close (v4l2object);
166 }
167
168 static gboolean
169 idle_refresh (gpointer data)
170 {
171   GstV4l2Object *v4l2object = GST_V4L2_OBJECT (data);
172   GstV4l2Xv *v4l2xv = v4l2object->xv;
173   XWindowAttributes attr;
174
175   if (v4l2xv) {
176     g_mutex_lock (v4l2xv->mutex);
177
178     XGetWindowAttributes (v4l2xv->dpy, v4l2object->xwindow_id, &attr);
179     XvPutVideo (v4l2xv->dpy, v4l2xv->port, v4l2object->xwindow_id,
180         DefaultGC (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)),
181         0, 0, attr.width, attr.height, 0, 0, attr.width, attr.height);
182
183     v4l2xv->idle_id = 0;
184     g_mutex_unlock (v4l2xv->mutex);
185   }
186
187   /* once */
188   return FALSE;
189 }
190
191 void
192 gst_v4l2_xoverlay_set_xwindow_id (GstV4l2Object * v4l2object, XID xwindow_id)
193 {
194   GstV4l2Xv *v4l2xv;
195   XWindowAttributes attr;
196   gboolean change = (v4l2object->xwindow_id != xwindow_id);
197
198   GST_LOG_OBJECT (v4l2object->element, "Setting XID to %lx",
199       (gulong) xwindow_id);
200
201   if (!v4l2object->xv && GST_V4L2_IS_OPEN (v4l2object))
202     gst_v4l2_xoverlay_open (v4l2object);
203
204   v4l2xv = v4l2object->xv;
205
206   if (v4l2xv)
207     g_mutex_lock (v4l2xv->mutex);
208
209   if (change) {
210     if (v4l2object->xwindow_id && v4l2xv) {
211       GST_DEBUG_OBJECT (v4l2object->element,
212           "Deactivating old port %lx", v4l2object->xwindow_id);
213
214       XvSelectPortNotify (v4l2xv->dpy, v4l2xv->port, 0);
215       XvSelectVideoNotify (v4l2xv->dpy, v4l2object->xwindow_id, 0);
216       XvStopVideo (v4l2xv->dpy, v4l2xv->port, v4l2object->xwindow_id);
217     }
218
219     v4l2object->xwindow_id = xwindow_id;
220   }
221
222   if (!v4l2xv || xwindow_id == 0) {
223     if (v4l2xv)
224       g_mutex_unlock (v4l2xv->mutex);
225     return;
226   }
227
228   if (change) {
229     GST_DEBUG_OBJECT (v4l2object->element, "Activating new port %lx",
230         xwindow_id);
231
232     /* draw */
233     XvSelectPortNotify (v4l2xv->dpy, v4l2xv->port, 1);
234     XvSelectVideoNotify (v4l2xv->dpy, v4l2object->xwindow_id, 1);
235   }
236
237   XGetWindowAttributes (v4l2xv->dpy, v4l2object->xwindow_id, &attr);
238   XvPutVideo (v4l2xv->dpy, v4l2xv->port, v4l2object->xwindow_id,
239       DefaultGC (v4l2xv->dpy, DefaultScreen (v4l2xv->dpy)),
240       0, 0, attr.width, attr.height, 0, 0, attr.width, attr.height);
241
242   if (v4l2xv->idle_id)
243     g_source_remove (v4l2xv->idle_id);
244   v4l2xv->idle_id = g_idle_add (idle_refresh, v4l2object);
245   g_mutex_unlock (v4l2xv->mutex);
246 }