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