1 | /* GStreamer |
---|
2 | * Copyright (C) <2003> Julien Moutte <julien@moutte.net> |
---|
3 | * |
---|
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. |
---|
8 | * |
---|
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. |
---|
13 | * |
---|
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. |
---|
18 | */ |
---|
19 | |
---|
20 | #ifdef HAVE_CONFIG_H |
---|
21 | #include "config.h" |
---|
22 | #endif |
---|
23 | |
---|
24 | /* Our interfaces */ |
---|
25 | #include <gst/navigation/navigation.h> |
---|
26 | #include <gst/xoverlay/xoverlay.h> |
---|
27 | #include <gst/colorbalance/colorbalance.h> |
---|
28 | |
---|
29 | /* Object header */ |
---|
30 | #include "xvimagesink.h" |
---|
31 | |
---|
32 | /* Debugging category */ |
---|
33 | #include <gst/gstinfo.h> |
---|
34 | GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink); |
---|
35 | #define GST_CAT_DEFAULT gst_debug_xvimagesink |
---|
36 | |
---|
37 | typedef struct |
---|
38 | { |
---|
39 | unsigned long flags; |
---|
40 | unsigned long functions; |
---|
41 | unsigned long decorations; |
---|
42 | long input_mode; |
---|
43 | unsigned long status; |
---|
44 | } |
---|
45 | MotifWmHints, MwmHints; |
---|
46 | |
---|
47 | #define MWM_HINTS_DECORATIONS (1L << 1) |
---|
48 | |
---|
49 | static void gst_xvimagesink_buffer_free (GstBuffer * buffer); |
---|
50 | static void gst_xvimagesink_xvimage_destroy (GstXvImageSink * xvimagesink, |
---|
51 | GstXvImage * xvimage); |
---|
52 | |
---|
53 | |
---|
54 | /* ElementFactory information */ |
---|
55 | static GstElementDetails gst_xvimagesink_details = |
---|
56 | GST_ELEMENT_DETAILS ("Video sink", |
---|
57 | "Sink/Video", |
---|
58 | "A Xv based videosink", |
---|
59 | "Julien Moutte <julien@moutte.net>"); |
---|
60 | |
---|
61 | /* Default template - initiated with class struct to allow gst-register to work |
---|
62 | without X running */ |
---|
63 | static GstStaticPadTemplate gst_xvimagesink_sink_template_factory = |
---|
64 | GST_STATIC_PAD_TEMPLATE ("sink", |
---|
65 | GST_PAD_SINK, |
---|
66 | GST_PAD_ALWAYS, |
---|
67 | GST_STATIC_CAPS ("video/x-raw-rgb, " |
---|
68 | "framerate = (double) [ 1.0, 100.0 ], " |
---|
69 | "width = (int) [ 1, MAX ], " |
---|
70 | "height = (int) [ 1, MAX ]; " |
---|
71 | "video/x-raw-yuv, " |
---|
72 | "framerate = (double) [ 1.0, 100.0 ], " |
---|
73 | "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") |
---|
74 | ); |
---|
75 | |
---|
76 | enum |
---|
77 | { |
---|
78 | ARG_0, |
---|
79 | ARG_CONTRAST, |
---|
80 | ARG_BRIGHTNESS, |
---|
81 | ARG_HUE, |
---|
82 | ARG_SATURATION, |
---|
83 | ARG_DISPLAY, |
---|
84 | ARG_SYNCHRONOUS, |
---|
85 | ARG_PIXEL_ASPECT_RATIO |
---|
86 | /* FILL ME */ |
---|
87 | }; |
---|
88 | |
---|
89 | static GstVideoSinkClass *parent_class = NULL; |
---|
90 | static gboolean error_caught = FALSE; |
---|
91 | |
---|
92 | /* ============================================================= */ |
---|
93 | /* */ |
---|
94 | /* Private Methods */ |
---|
95 | /* */ |
---|
96 | /* ============================================================= */ |
---|
97 | |
---|
98 | /* X11 stuff */ |
---|
99 | |
---|
100 | static int |
---|
101 | gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent) |
---|
102 | { |
---|
103 | char error_msg[1024]; |
---|
104 | |
---|
105 | XGetErrorText (display, xevent->error_code, error_msg, 1024); |
---|
106 | GST_DEBUG ("xvimagesink failed to use XShm calls. error: %s", error_msg); |
---|
107 | error_caught = TRUE; |
---|
108 | return 0; |
---|
109 | } |
---|
110 | |
---|
111 | /* This function checks that it is actually really possible to create an image |
---|
112 | using XShm */ |
---|
113 | static gboolean |
---|
114 | gst_xvimagesink_check_xshm_calls (GstXContext * xcontext) |
---|
115 | { |
---|
116 | #ifndef HAVE_XSHM |
---|
117 | return FALSE; |
---|
118 | #else |
---|
119 | GstXvImage *xvimage = NULL; |
---|
120 | int (*handler) (Display *, XErrorEvent *); |
---|
121 | gboolean result = FALSE; |
---|
122 | |
---|
123 | g_return_val_if_fail (xcontext != NULL, FALSE); |
---|
124 | |
---|
125 | xvimage = g_new0 (GstXvImage, 1); |
---|
126 | g_return_val_if_fail (xvimage != NULL, FALSE); |
---|
127 | |
---|
128 | /* Setting an error handler to catch failure */ |
---|
129 | error_caught = FALSE; |
---|
130 | handler = XSetErrorHandler (gst_xvimagesink_handle_xerror); |
---|
131 | |
---|
132 | /* Trying to create a 1x1 picture */ |
---|
133 | GST_DEBUG ("XvShmCreateImage of 1x1"); |
---|
134 | xvimage->xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id, |
---|
135 | xcontext->im_format, NULL, 1, 1, &xvimage->SHMInfo); |
---|
136 | if (!xvimage->xvimage) { |
---|
137 | GST_WARNING ("could not XvShmCreateImage a 1x1 image"); |
---|
138 | goto beach; |
---|
139 | } |
---|
140 | xvimage->size = xvimage->xvimage->data_size; |
---|
141 | |
---|
142 | xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size, |
---|
143 | IPC_CREAT | 0777); |
---|
144 | if (xvimage->SHMInfo.shmid == -1) { |
---|
145 | GST_WARNING ("could not get shared memory of %d bytes", xvimage->size); |
---|
146 | goto beach; |
---|
147 | } |
---|
148 | |
---|
149 | xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0); |
---|
150 | if (xvimage->SHMInfo.shmaddr == ((void *) -1)) { |
---|
151 | GST_WARNING ("Failed to shmat: %s", g_strerror (errno)); |
---|
152 | goto beach; |
---|
153 | } |
---|
154 | |
---|
155 | xvimage->xvimage->data = xvimage->SHMInfo.shmaddr; |
---|
156 | xvimage->SHMInfo.readOnly = FALSE; |
---|
157 | |
---|
158 | if (XShmAttach (xcontext->disp, &xvimage->SHMInfo) == 0) { |
---|
159 | GST_WARNING ("Failed to XShmAttach"); |
---|
160 | goto beach; |
---|
161 | } |
---|
162 | |
---|
163 | XSync (xcontext->disp, 0); |
---|
164 | |
---|
165 | XShmDetach (xcontext->disp, &xvimage->SHMInfo); |
---|
166 | XSync (xcontext->disp, FALSE); |
---|
167 | |
---|
168 | shmdt (xvimage->SHMInfo.shmaddr); |
---|
169 | shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0); |
---|
170 | |
---|
171 | /* To be sure, reset the SHMInfo entry */ |
---|
172 | xvimage->SHMInfo.shmaddr = ((void *) -1); |
---|
173 | |
---|
174 | /* store whether we succeeded in result and reset error_caught */ |
---|
175 | result = !error_caught; |
---|
176 | error_caught = FALSE; |
---|
177 | |
---|
178 | beach: |
---|
179 | XSetErrorHandler (handler); |
---|
180 | if (xvimage->xvimage) |
---|
181 | XFree (xvimage->xvimage); |
---|
182 | g_free (xvimage); |
---|
183 | XSync (xcontext->disp, FALSE); |
---|
184 | return result; |
---|
185 | #endif /* HAVE_XSHM */ |
---|
186 | } |
---|
187 | |
---|
188 | /* This function handles GstXvImage creation depending on XShm availability */ |
---|
189 | static GstXvImage * |
---|
190 | gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, |
---|
191 | gint width, gint height) |
---|
192 | { |
---|
193 | GstXvImage *xvimage = NULL; |
---|
194 | gboolean succeeded = FALSE; |
---|
195 | |
---|
196 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
---|
197 | GST_DEBUG_OBJECT (xvimagesink, "creating %dx%d", width, height); |
---|
198 | |
---|
199 | xvimage = g_new0 (GstXvImage, 1); |
---|
200 | |
---|
201 | xvimage->width = width; |
---|
202 | xvimage->height = height; |
---|
203 | xvimage->im_format = xvimagesink->xcontext->im_format; |
---|
204 | xvimage->xvimagesink = xvimagesink; |
---|
205 | |
---|
206 | g_mutex_lock (xvimagesink->x_lock); |
---|
207 | |
---|
208 | #ifdef HAVE_XSHM |
---|
209 | if (xvimagesink->xcontext->use_xshm) { |
---|
210 | xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp, |
---|
211 | xvimagesink->xcontext->xv_port_id, |
---|
212 | xvimage->im_format, NULL, |
---|
213 | xvimage->width, xvimage->height, &xvimage->SHMInfo); |
---|
214 | if (!xvimage->xvimage) { |
---|
215 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL), |
---|
216 | ("could not XvShmCreateImage a %dx%d image")); |
---|
217 | goto beach; |
---|
218 | } |
---|
219 | |
---|
220 | /* we have to use the returned data_size for our shm size */ |
---|
221 | xvimage->size = xvimage->xvimage->data_size; |
---|
222 | GST_DEBUG_OBJECT (xvimagesink, "XShm image size is %d", xvimage->size); |
---|
223 | |
---|
224 | xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size, |
---|
225 | IPC_CREAT | 0777); |
---|
226 | if (xvimage->SHMInfo.shmid == -1) { |
---|
227 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL), |
---|
228 | ("could not get shared memory of %d bytes", xvimage->size)); |
---|
229 | goto beach; |
---|
230 | } |
---|
231 | |
---|
232 | xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0); |
---|
233 | if (xvimage->SHMInfo.shmaddr == ((void *) -1)) { |
---|
234 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL), |
---|
235 | ("Failed to shmat: %s", g_strerror (errno))); |
---|
236 | goto beach; |
---|
237 | } |
---|
238 | |
---|
239 | xvimage->xvimage->data = xvimage->SHMInfo.shmaddr; |
---|
240 | xvimage->SHMInfo.readOnly = FALSE; |
---|
241 | |
---|
242 | if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) { |
---|
243 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL), |
---|
244 | ("Failed to XShmAttach")); |
---|
245 | goto beach; |
---|
246 | } |
---|
247 | |
---|
248 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
249 | } else |
---|
250 | #endif /* HAVE_XSHM */ |
---|
251 | { |
---|
252 | xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp, |
---|
253 | xvimagesink->xcontext->xv_port_id, |
---|
254 | xvimage->im_format, NULL, xvimage->width, xvimage->height); |
---|
255 | if (!xvimage->xvimage) { |
---|
256 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL), |
---|
257 | ("could not XvCreateImage a %dx%d image")); |
---|
258 | goto beach; |
---|
259 | } |
---|
260 | |
---|
261 | /* we have to use the returned data_size for our image size */ |
---|
262 | xvimage->size = xvimage->xvimage->data_size; |
---|
263 | xvimage->xvimage->data = g_malloc (xvimage->size); |
---|
264 | |
---|
265 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
266 | } |
---|
267 | succeeded = TRUE; |
---|
268 | g_mutex_unlock (xvimagesink->x_lock); |
---|
269 | |
---|
270 | beach: |
---|
271 | if (!succeeded) { |
---|
272 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage); |
---|
273 | xvimage = NULL; |
---|
274 | } |
---|
275 | |
---|
276 | return xvimage; |
---|
277 | } |
---|
278 | |
---|
279 | /* This function destroys a GstXvImage handling XShm availability */ |
---|
280 | static void |
---|
281 | gst_xvimagesink_xvimage_destroy (GstXvImageSink * xvimagesink, |
---|
282 | GstXvImage * xvimage) |
---|
283 | { |
---|
284 | g_return_if_fail (xvimage != NULL); |
---|
285 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
286 | |
---|
287 | /* If the destroyed image is the current one we destroy our reference too */ |
---|
288 | if (xvimagesink->cur_image == xvimage) |
---|
289 | xvimagesink->cur_image = NULL; |
---|
290 | |
---|
291 | g_mutex_lock (xvimagesink->x_lock); |
---|
292 | |
---|
293 | #ifdef HAVE_XSHM |
---|
294 | if (xvimagesink->xcontext->use_xshm) { |
---|
295 | if (xvimage->SHMInfo.shmaddr != ((void *) -1)) { |
---|
296 | XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo); |
---|
297 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
298 | shmdt (xvimage->SHMInfo.shmaddr); |
---|
299 | } |
---|
300 | if (xvimage->SHMInfo.shmid > 0) |
---|
301 | shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0); |
---|
302 | if (xvimage->xvimage) |
---|
303 | XFree (xvimage->xvimage); |
---|
304 | } else |
---|
305 | #endif /* HAVE_XSHM */ |
---|
306 | { |
---|
307 | if (xvimage->xvimage) { |
---|
308 | if (xvimage->xvimage->data) { |
---|
309 | g_free (xvimage->xvimage->data); |
---|
310 | } |
---|
311 | XFree (xvimage->xvimage); |
---|
312 | } |
---|
313 | } |
---|
314 | |
---|
315 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
316 | |
---|
317 | g_mutex_unlock (xvimagesink->x_lock); |
---|
318 | |
---|
319 | g_free (xvimage); |
---|
320 | } |
---|
321 | |
---|
322 | /* This function puts a GstXvImage on a GstXvImageSink's window */ |
---|
323 | static void |
---|
324 | gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstXvImage * xvimage) |
---|
325 | { |
---|
326 | g_return_if_fail (xvimage != NULL); |
---|
327 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
328 | |
---|
329 | /* Store a reference to the last image we put */ |
---|
330 | if (xvimagesink->cur_image != xvimage) |
---|
331 | xvimagesink->cur_image = xvimage; |
---|
332 | |
---|
333 | g_mutex_lock (xvimagesink->x_lock); |
---|
334 | |
---|
335 | /* We scale to the window's geometry */ |
---|
336 | #ifdef HAVE_XSHM |
---|
337 | if (xvimagesink->xcontext->use_xshm) { |
---|
338 | GST_LOG_OBJECT (xvimagesink, |
---|
339 | "XvShmPutImage with image %dx%d and window %dx%d", |
---|
340 | xvimage->width, xvimage->height, |
---|
341 | xvimagesink->xwindow->width, xvimagesink->xwindow->height); |
---|
342 | XvShmPutImage (xvimagesink->xcontext->disp, |
---|
343 | xvimagesink->xcontext->xv_port_id, |
---|
344 | xvimagesink->xwindow->win, |
---|
345 | xvimagesink->xwindow->gc, xvimage->xvimage, |
---|
346 | 0, 0, xvimage->width, xvimage->height, |
---|
347 | 0, 0, xvimagesink->xwindow->width, xvimagesink->xwindow->height, FALSE); |
---|
348 | } else |
---|
349 | #endif /* HAVE_XSHM */ |
---|
350 | { |
---|
351 | XvPutImage (xvimagesink->xcontext->disp, |
---|
352 | xvimagesink->xcontext->xv_port_id, |
---|
353 | xvimagesink->xwindow->win, |
---|
354 | xvimagesink->xwindow->gc, xvimage->xvimage, |
---|
355 | 0, 0, xvimage->width, xvimage->height, |
---|
356 | 0, 0, xvimagesink->xwindow->width, xvimagesink->xwindow->height); |
---|
357 | } |
---|
358 | |
---|
359 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
360 | |
---|
361 | g_mutex_unlock (xvimagesink->x_lock); |
---|
362 | } |
---|
363 | |
---|
364 | static gboolean |
---|
365 | gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink, |
---|
366 | GstXWindow * window) |
---|
367 | { |
---|
368 | Atom hints_atom = None; |
---|
369 | MotifWmHints *hints; |
---|
370 | |
---|
371 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE); |
---|
372 | g_return_val_if_fail (window != NULL, FALSE); |
---|
373 | |
---|
374 | g_mutex_lock (xvimagesink->x_lock); |
---|
375 | |
---|
376 | hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1); |
---|
377 | if (hints_atom == None) { |
---|
378 | g_mutex_unlock (xvimagesink->x_lock); |
---|
379 | return FALSE; |
---|
380 | } |
---|
381 | |
---|
382 | hints = g_malloc0 (sizeof (MotifWmHints)); |
---|
383 | |
---|
384 | hints->flags |= MWM_HINTS_DECORATIONS; |
---|
385 | hints->decorations = 1 << 0; |
---|
386 | |
---|
387 | XChangeProperty (xvimagesink->xcontext->disp, window->win, |
---|
388 | hints_atom, hints_atom, 32, PropModeReplace, |
---|
389 | (guchar *) hints, sizeof (MotifWmHints) / sizeof (long)); |
---|
390 | |
---|
391 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
392 | |
---|
393 | g_mutex_unlock (xvimagesink->x_lock); |
---|
394 | |
---|
395 | g_free (hints); |
---|
396 | |
---|
397 | return TRUE; |
---|
398 | } |
---|
399 | |
---|
400 | /* This function handles a GstXWindow creation |
---|
401 | * The width and height are the actual pixel size on the display */ |
---|
402 | static GstXWindow * |
---|
403 | gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink, |
---|
404 | gint width, gint height) |
---|
405 | { |
---|
406 | GstXWindow *xwindow = NULL; |
---|
407 | XGCValues values; |
---|
408 | |
---|
409 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
---|
410 | |
---|
411 | xwindow = g_new0 (GstXWindow, 1); |
---|
412 | |
---|
413 | xwindow->width = width; |
---|
414 | xwindow->height = height; |
---|
415 | xwindow->internal = TRUE; |
---|
416 | |
---|
417 | g_mutex_lock (xvimagesink->x_lock); |
---|
418 | |
---|
419 | xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp, |
---|
420 | xvimagesink->xcontext->root, |
---|
421 | 0, 0, xwindow->width, xwindow->height, |
---|
422 | 0, 0, xvimagesink->xcontext->black); |
---|
423 | |
---|
424 | XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask | |
---|
425 | StructureNotifyMask | PointerMotionMask | KeyPressMask | |
---|
426 | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask); |
---|
427 | |
---|
428 | xwindow->gc = XCreateGC (xvimagesink->xcontext->disp, |
---|
429 | xwindow->win, 0, &values); |
---|
430 | |
---|
431 | XMapRaised (xvimagesink->xcontext->disp, xwindow->win); |
---|
432 | |
---|
433 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
434 | |
---|
435 | g_mutex_unlock (xvimagesink->x_lock); |
---|
436 | |
---|
437 | gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow); |
---|
438 | |
---|
439 | gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win); |
---|
440 | |
---|
441 | return xwindow; |
---|
442 | } |
---|
443 | |
---|
444 | /* This function destroys a GstXWindow */ |
---|
445 | static void |
---|
446 | gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink, |
---|
447 | GstXWindow * xwindow) |
---|
448 | { |
---|
449 | g_return_if_fail (xwindow != NULL); |
---|
450 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
451 | |
---|
452 | g_mutex_lock (xvimagesink->x_lock); |
---|
453 | |
---|
454 | /* If we did not create that window we just free the GC and let it live */ |
---|
455 | if (xwindow->internal) |
---|
456 | XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win); |
---|
457 | else |
---|
458 | XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0); |
---|
459 | |
---|
460 | XFreeGC (xvimagesink->xcontext->disp, xwindow->gc); |
---|
461 | |
---|
462 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
463 | |
---|
464 | g_mutex_unlock (xvimagesink->x_lock); |
---|
465 | |
---|
466 | g_free (xwindow); |
---|
467 | } |
---|
468 | |
---|
469 | /* This function resizes a GstXWindow. |
---|
470 | * The width and height are the actual pixel size on the display. */ |
---|
471 | static void |
---|
472 | gst_xvimagesink_xwindow_resize (GstXvImageSink * xvimagesink, |
---|
473 | GstXWindow * xwindow, guint width, guint height) |
---|
474 | { |
---|
475 | g_return_if_fail (xwindow != NULL); |
---|
476 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
477 | |
---|
478 | g_mutex_lock (xvimagesink->x_lock); |
---|
479 | |
---|
480 | xwindow->width = width; |
---|
481 | xwindow->height = height; |
---|
482 | |
---|
483 | XResizeWindow (xvimagesink->xcontext->disp, xwindow->win, |
---|
484 | xwindow->width, xwindow->height); |
---|
485 | |
---|
486 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
487 | |
---|
488 | g_mutex_unlock (xvimagesink->x_lock); |
---|
489 | } |
---|
490 | |
---|
491 | static void |
---|
492 | gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink, |
---|
493 | GstXWindow * xwindow) |
---|
494 | { |
---|
495 | g_return_if_fail (xwindow != NULL); |
---|
496 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
497 | |
---|
498 | g_mutex_lock (xvimagesink->x_lock); |
---|
499 | |
---|
500 | XSetForeground (xvimagesink->xcontext->disp, xwindow->gc, |
---|
501 | xvimagesink->xcontext->black); |
---|
502 | |
---|
503 | XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc, |
---|
504 | 0, 0, xwindow->width, xwindow->height); |
---|
505 | |
---|
506 | XSync (xvimagesink->xcontext->disp, FALSE); |
---|
507 | |
---|
508 | g_mutex_unlock (xvimagesink->x_lock); |
---|
509 | } |
---|
510 | |
---|
511 | /* This function commits our internal colorbalance settings to our grabbed Xv |
---|
512 | port. If the xcontext is not initialized yet it simply returns */ |
---|
513 | static void |
---|
514 | gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink) |
---|
515 | { |
---|
516 | GList *channels = NULL; |
---|
517 | |
---|
518 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
519 | |
---|
520 | /* If we haven't initialized the X context we can't update anything */ |
---|
521 | if (xvimagesink->xcontext == NULL) |
---|
522 | return; |
---|
523 | |
---|
524 | /* For each channel of the colorbalance we calculate the correct value |
---|
525 | doing range conversion and then set the Xv port attribute to match our |
---|
526 | values. */ |
---|
527 | channels = xvimagesink->xcontext->channels_list; |
---|
528 | |
---|
529 | while (channels) { |
---|
530 | if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) { |
---|
531 | GstColorBalanceChannel *channel = NULL; |
---|
532 | gint value = 0; |
---|
533 | gdouble convert_coef; |
---|
534 | |
---|
535 | channel = GST_COLOR_BALANCE_CHANNEL (channels->data); |
---|
536 | g_object_ref (channel); |
---|
537 | |
---|
538 | /* Our range conversion coef */ |
---|
539 | convert_coef = (channel->max_value - channel->min_value) / 2000.0; |
---|
540 | |
---|
541 | if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) { |
---|
542 | value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value; |
---|
543 | } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) { |
---|
544 | value = (xvimagesink->saturation + 1000) * convert_coef + |
---|
545 | channel->min_value; |
---|
546 | } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) { |
---|
547 | value = (xvimagesink->contrast + 1000) * convert_coef + |
---|
548 | channel->min_value; |
---|
549 | } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) { |
---|
550 | value = (xvimagesink->brightness + 1000) * convert_coef + |
---|
551 | channel->min_value; |
---|
552 | } else { |
---|
553 | g_warning ("got an unknown channel %s", channel->label); |
---|
554 | g_object_unref (channel); |
---|
555 | return; |
---|
556 | } |
---|
557 | |
---|
558 | /* Committing to Xv port */ |
---|
559 | g_mutex_lock (xvimagesink->x_lock); |
---|
560 | XvSetPortAttribute (xvimagesink->xcontext->disp, |
---|
561 | xvimagesink->xcontext->xv_port_id, |
---|
562 | XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value); |
---|
563 | g_mutex_unlock (xvimagesink->x_lock); |
---|
564 | |
---|
565 | g_object_unref (channel); |
---|
566 | } |
---|
567 | channels = g_list_next (channels); |
---|
568 | } |
---|
569 | } |
---|
570 | |
---|
571 | /* This function handles XEvents that might be in the queue. It generates |
---|
572 | GstEvent that will be sent upstream in the pipeline to handle interactivity |
---|
573 | and navigation. It will also listen for configure events on the window to |
---|
574 | trigger caps renegotiation so on the fly software scaling can work. */ |
---|
575 | static void |
---|
576 | gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink, GstPad * pad) |
---|
577 | { |
---|
578 | XEvent e; |
---|
579 | guint pointer_x = 0, pointer_y = 0; |
---|
580 | gboolean pointer_moved = FALSE; |
---|
581 | |
---|
582 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
583 | |
---|
584 | /* We get all pointer motion events, only the last position is |
---|
585 | interesting. */ |
---|
586 | g_mutex_lock (xvimagesink->x_lock); |
---|
587 | while (XCheckWindowEvent (xvimagesink->xcontext->disp, |
---|
588 | xvimagesink->xwindow->win, PointerMotionMask, &e)) { |
---|
589 | g_mutex_unlock (xvimagesink->x_lock); |
---|
590 | |
---|
591 | switch (e.type) { |
---|
592 | case MotionNotify: |
---|
593 | pointer_x = e.xmotion.x; |
---|
594 | pointer_y = e.xmotion.y; |
---|
595 | pointer_moved = TRUE; |
---|
596 | break; |
---|
597 | default: |
---|
598 | break; |
---|
599 | } |
---|
600 | |
---|
601 | g_mutex_lock (xvimagesink->x_lock); |
---|
602 | } |
---|
603 | g_mutex_unlock (xvimagesink->x_lock); |
---|
604 | |
---|
605 | if (pointer_moved) { |
---|
606 | GST_DEBUG ("xvimagesink pointer moved over window at %d,%d", |
---|
607 | pointer_x, pointer_y); |
---|
608 | gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), |
---|
609 | "mouse-move", 0, pointer_x, pointer_y); |
---|
610 | } |
---|
611 | |
---|
612 | /* We get all events on our window to throw them upstream */ |
---|
613 | g_mutex_lock (xvimagesink->x_lock); |
---|
614 | while (XCheckWindowEvent (xvimagesink->xcontext->disp, |
---|
615 | xvimagesink->xwindow->win, |
---|
616 | StructureNotifyMask | KeyPressMask | |
---|
617 | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, &e)) { |
---|
618 | KeySym keysym; |
---|
619 | |
---|
620 | /* We lock only for the X function call */ |
---|
621 | g_mutex_unlock (xvimagesink->x_lock); |
---|
622 | |
---|
623 | switch (e.type) { |
---|
624 | case ConfigureNotify: |
---|
625 | /* Window got resized or moved. We update our data. */ |
---|
626 | GST_DEBUG ("xvimagesink window is at %d, %d with geometry : %d,%d", |
---|
627 | e.xconfigure.x, e.xconfigure.y, |
---|
628 | e.xconfigure.width, e.xconfigure.height); |
---|
629 | xvimagesink->xwindow->width = e.xconfigure.width; |
---|
630 | xvimagesink->xwindow->height = e.xconfigure.height; |
---|
631 | break; |
---|
632 | case ButtonPress: |
---|
633 | /* Mouse button pressed over our window. We send upstream |
---|
634 | events for interactivity/navigation */ |
---|
635 | GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d", |
---|
636 | e.xbutton.button, e.xbutton.x, e.xbutton.y); |
---|
637 | gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), |
---|
638 | "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y); |
---|
639 | break; |
---|
640 | case ButtonRelease: |
---|
641 | /* Mouse button released over our window. We send upstream |
---|
642 | events for interactivity/navigation */ |
---|
643 | GST_DEBUG ("xvimagesink button %d released over window at %d,%d", |
---|
644 | e.xbutton.button, e.xbutton.x, e.xbutton.y); |
---|
645 | gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), |
---|
646 | "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y); |
---|
647 | break; |
---|
648 | case KeyPress: |
---|
649 | case KeyRelease: |
---|
650 | /* Key pressed/released over our window. We send upstream |
---|
651 | events for interactivity/navigation */ |
---|
652 | GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d", |
---|
653 | e.xkey.keycode, e.xkey.x, e.xkey.y); |
---|
654 | keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp, |
---|
655 | e.xkey.keycode, 0); |
---|
656 | if (keysym != NoSymbol) { |
---|
657 | gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink), |
---|
658 | e.type == KeyPress ? |
---|
659 | "key-press" : "key-release", XKeysymToString (keysym)); |
---|
660 | } else { |
---|
661 | gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink), |
---|
662 | e.type == KeyPress ? "key-press" : "key-release", "unknown"); |
---|
663 | } |
---|
664 | break; |
---|
665 | default: |
---|
666 | GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type); |
---|
667 | } |
---|
668 | |
---|
669 | g_mutex_lock (xvimagesink->x_lock); |
---|
670 | } |
---|
671 | g_mutex_unlock (xvimagesink->x_lock); |
---|
672 | } |
---|
673 | |
---|
674 | /* This function generates a caps with all supported format by the first |
---|
675 | Xv grabable port we find. We store each one of the supported formats in a |
---|
676 | format list and append the format to a newly created caps that we return |
---|
677 | If this function does not return NULL because of an error, it also grabs |
---|
678 | the port via XvGrabPort */ |
---|
679 | static GstCaps * |
---|
680 | gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink, |
---|
681 | GstXContext * xcontext) |
---|
682 | { |
---|
683 | gint i, nb_adaptors; |
---|
684 | XvAdaptorInfo *adaptors; |
---|
685 | gint nb_formats; |
---|
686 | XvImageFormatValues *formats = NULL; |
---|
687 | GstCaps *caps = NULL; |
---|
688 | |
---|
689 | g_return_val_if_fail (xcontext != NULL, NULL); |
---|
690 | |
---|
691 | /* First let's check that XVideo extension is available */ |
---|
692 | if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) { |
---|
693 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL), |
---|
694 | ("XVideo extension is not available")); |
---|
695 | return NULL; |
---|
696 | } |
---|
697 | |
---|
698 | /* Then we get adaptors list */ |
---|
699 | if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root, |
---|
700 | &nb_adaptors, &adaptors)) { |
---|
701 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL), |
---|
702 | ("Failed getting XV adaptors list")); |
---|
703 | return NULL; |
---|
704 | } |
---|
705 | |
---|
706 | xcontext->xv_port_id = 0; |
---|
707 | |
---|
708 | GST_DEBUG ("Found %d XV adaptor(s)", nb_adaptors); |
---|
709 | |
---|
710 | /* Now search for an adaptor that supports XvImageMask */ |
---|
711 | for (i = 0; i < nb_adaptors && !xcontext->xv_port_id; i++) { |
---|
712 | if (adaptors[i].type & XvImageMask) { |
---|
713 | gint j; |
---|
714 | |
---|
715 | /* We found such an adaptor, looking for an available port */ |
---|
716 | for (j = 0; j < adaptors[i].num_ports && !xcontext->xv_port_id; j++) { |
---|
717 | /* We try to grab the port */ |
---|
718 | if (Success == XvGrabPort (xcontext->disp, adaptors[i].base_id + j, 0)) { |
---|
719 | xcontext->xv_port_id = adaptors[i].base_id + j; |
---|
720 | } |
---|
721 | } |
---|
722 | } |
---|
723 | |
---|
724 | GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[i].name, |
---|
725 | adaptors[i].num_ports); |
---|
726 | |
---|
727 | } |
---|
728 | XvFreeAdaptorInfo (adaptors); |
---|
729 | |
---|
730 | if (!xcontext->xv_port_id) { |
---|
731 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY, (NULL), |
---|
732 | ("No port available")); |
---|
733 | return NULL; |
---|
734 | } |
---|
735 | |
---|
736 | /* Set XV_AUTOPAINT_COLORKEY */ |
---|
737 | { |
---|
738 | int count; |
---|
739 | XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp, |
---|
740 | xcontext->xv_port_id, &count); |
---|
741 | static const char autopaint[] = "XV_AUTOPAINT_COLORKEY"; |
---|
742 | |
---|
743 | for (i = 0; i < count; i++) |
---|
744 | if (!strcmp (attr[i].name, autopaint)) { |
---|
745 | const Atom atom = XInternAtom (xcontext->disp, autopaint, False); |
---|
746 | |
---|
747 | XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, 1); |
---|
748 | break; |
---|
749 | } |
---|
750 | |
---|
751 | XFree (attr); |
---|
752 | } |
---|
753 | |
---|
754 | /* We get all image formats supported by our port */ |
---|
755 | formats = XvListImageFormats (xcontext->disp, |
---|
756 | xcontext->xv_port_id, &nb_formats); |
---|
757 | caps = gst_caps_new_empty (); |
---|
758 | for (i = 0; i < nb_formats; i++) { |
---|
759 | GstCaps *format_caps = NULL; |
---|
760 | |
---|
761 | /* We set the image format of the xcontext to an existing one. Sink |
---|
762 | connect method will override that but we need to have at least a |
---|
763 | valid image format so that we can make our xshm calls check before |
---|
764 | caps negotiation really happens. */ |
---|
765 | xcontext->im_format = formats[i].id; |
---|
766 | |
---|
767 | switch (formats[i].type) { |
---|
768 | case XvRGB: |
---|
769 | { |
---|
770 | format_caps = gst_caps_new_simple ("video/x-raw-rgb", |
---|
771 | "endianness", G_TYPE_INT, xcontext->endianness, |
---|
772 | "depth", G_TYPE_INT, xcontext->depth, |
---|
773 | "bpp", G_TYPE_INT, xcontext->bpp, |
---|
774 | "blue_mask", G_TYPE_INT, formats[i].red_mask, |
---|
775 | "green_mask", G_TYPE_INT, formats[i].green_mask, |
---|
776 | "red_mask", G_TYPE_INT, formats[i].blue_mask, |
---|
777 | "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
---|
778 | "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
---|
779 | "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL); |
---|
780 | |
---|
781 | /* For RGB caps we store them and the image |
---|
782 | format so that we can get back the format |
---|
783 | when sinkconnect will give us a caps without |
---|
784 | format property */ |
---|
785 | if (format_caps) { |
---|
786 | GstXvImageFormat *format = NULL; |
---|
787 | |
---|
788 | format = g_new0 (GstXvImageFormat, 1); |
---|
789 | if (format) { |
---|
790 | format->format = formats[i].id; |
---|
791 | format->caps = gst_caps_copy (format_caps); |
---|
792 | xcontext->formats_list = |
---|
793 | g_list_append (xcontext->formats_list, format); |
---|
794 | } |
---|
795 | } |
---|
796 | break; |
---|
797 | } |
---|
798 | case XvYUV: |
---|
799 | format_caps = gst_caps_new_simple ("video/x-raw-yuv", |
---|
800 | "format", GST_TYPE_FOURCC, formats[i].id, |
---|
801 | "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
---|
802 | "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, |
---|
803 | "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL); |
---|
804 | break; |
---|
805 | default: |
---|
806 | g_assert_not_reached (); |
---|
807 | break; |
---|
808 | } |
---|
809 | |
---|
810 | gst_caps_append (caps, format_caps); |
---|
811 | } |
---|
812 | |
---|
813 | if (formats) |
---|
814 | XFree (formats); |
---|
815 | |
---|
816 | GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps); |
---|
817 | |
---|
818 | if (gst_caps_is_empty (caps)) { |
---|
819 | gst_caps_free (caps); |
---|
820 | XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0); |
---|
821 | GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL), |
---|
822 | ("No supported format found")); |
---|
823 | return NULL; |
---|
824 | } |
---|
825 | |
---|
826 | return caps; |
---|
827 | } |
---|
828 | |
---|
829 | /* This function calculates the pixel aspect ratio based on the properties |
---|
830 | * in the xcontext structure and stores it there. */ |
---|
831 | static void |
---|
832 | gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext) |
---|
833 | { |
---|
834 | gint par[][2] = { |
---|
835 | {1, 1}, /* regular screen */ |
---|
836 | {16, 15}, /* PAL TV */ |
---|
837 | {11, 10}, /* 525 line Rec.601 video */ |
---|
838 | {54, 59} /* 625 line Rec.601 video */ |
---|
839 | }; |
---|
840 | gint i; |
---|
841 | gint index; |
---|
842 | gdouble ratio; |
---|
843 | gdouble delta; |
---|
844 | |
---|
845 | #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1]))) |
---|
846 | |
---|
847 | /* first calculate the "real" ratio based on the X values; |
---|
848 | * which is the "physical" w/h divided by the w/h in pixels of the display */ |
---|
849 | ratio = (gdouble) (xcontext->widthmm * xcontext->height) |
---|
850 | / (xcontext->heightmm * xcontext->width); |
---|
851 | |
---|
852 | /* DirectFB's X in 720x576 reports the physical dimensions wrong, so |
---|
853 | * override here */ |
---|
854 | if (xcontext->width == 720 && xcontext->height == 576) { |
---|
855 | ratio = 4.0 * 576 / (3.0 * 720); |
---|
856 | } |
---|
857 | GST_DEBUG ("calculated pixel aspect ratio: %f", ratio); |
---|
858 | /* now find the one from par[][2] with the lowest delta to the real one */ |
---|
859 | delta = DELTA (0); |
---|
860 | index = 0; |
---|
861 | |
---|
862 | for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) { |
---|
863 | gdouble this_delta = DELTA (i); |
---|
864 | |
---|
865 | if (this_delta < delta) { |
---|
866 | index = i; |
---|
867 | delta = this_delta; |
---|
868 | } |
---|
869 | } |
---|
870 | |
---|
871 | GST_DEBUG ("Decided on index %d (%d/%d)", index, |
---|
872 | par[index][0], par[index][1]); |
---|
873 | |
---|
874 | g_free (xcontext->par); |
---|
875 | xcontext->par = g_new0 (GValue, 1); |
---|
876 | g_value_init (xcontext->par, GST_TYPE_FRACTION); |
---|
877 | gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]); |
---|
878 | GST_DEBUG ("set xcontext PAR to %d/%d", |
---|
879 | gst_value_get_fraction_numerator (xcontext->par), |
---|
880 | gst_value_get_fraction_denominator (xcontext->par)); |
---|
881 | } |
---|
882 | |
---|
883 | /* This function gets the X Display and global info about it. Everything is |
---|
884 | stored in our object and will be cleaned when the object is disposed. Note |
---|
885 | here that caps for supported format are generated without any window or |
---|
886 | image creation */ |
---|
887 | static GstXContext * |
---|
888 | gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink) |
---|
889 | { |
---|
890 | GstXContext *xcontext = NULL; |
---|
891 | XPixmapFormatValues *px_formats = NULL; |
---|
892 | gint nb_formats = 0, i, j, N_attr; |
---|
893 | XvAttribute *xv_attr; |
---|
894 | char *channels[4] = { "XV_HUE", "XV_SATURATION", |
---|
895 | "XV_BRIGHTNESS", "XV_CONTRAST" |
---|
896 | }; |
---|
897 | |
---|
898 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
---|
899 | |
---|
900 | xcontext = g_new0 (GstXContext, 1); |
---|
901 | xcontext->im_format = 0; |
---|
902 | |
---|
903 | g_mutex_lock (xvimagesink->x_lock); |
---|
904 | |
---|
905 | xcontext->disp = XOpenDisplay (xvimagesink->display_name); |
---|
906 | |
---|
907 | if (!xcontext->disp) { |
---|
908 | g_mutex_unlock (xvimagesink->x_lock); |
---|
909 | g_free (xcontext); |
---|
910 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL), |
---|
911 | ("Could not open display")); |
---|
912 | return NULL; |
---|
913 | } |
---|
914 | |
---|
915 | xcontext->screen = DefaultScreenOfDisplay (xcontext->disp); |
---|
916 | xcontext->screen_num = DefaultScreen (xcontext->disp); |
---|
917 | xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num); |
---|
918 | xcontext->root = DefaultRootWindow (xcontext->disp); |
---|
919 | xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num); |
---|
920 | xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num); |
---|
921 | xcontext->depth = DefaultDepthOfScreen (xcontext->screen); |
---|
922 | |
---|
923 | xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num); |
---|
924 | xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num); |
---|
925 | xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num); |
---|
926 | xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num); |
---|
927 | |
---|
928 | GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm", |
---|
929 | xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm); |
---|
930 | |
---|
931 | |
---|
932 | gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext); |
---|
933 | /* We get supported pixmap formats at supported depth */ |
---|
934 | px_formats = XListPixmapFormats (xcontext->disp, &nb_formats); |
---|
935 | |
---|
936 | if (!px_formats) { |
---|
937 | XCloseDisplay (xcontext->disp); |
---|
938 | g_mutex_unlock (xvimagesink->x_lock); |
---|
939 | g_free (xcontext); |
---|
940 | GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL), |
---|
941 | ("Could not get pixel formats")); |
---|
942 | return NULL; |
---|
943 | } |
---|
944 | |
---|
945 | /* We get bpp value corresponding to our running depth */ |
---|
946 | for (i = 0; i < nb_formats; i++) { |
---|
947 | if (px_formats[i].depth == xcontext->depth) |
---|
948 | xcontext->bpp = px_formats[i].bits_per_pixel; |
---|
949 | } |
---|
950 | |
---|
951 | XFree (px_formats); |
---|
952 | |
---|
953 | xcontext->endianness = |
---|
954 | (ImageByteOrder (xcontext->disp) == |
---|
955 | LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN; |
---|
956 | |
---|
957 | /* our caps system handles 24/32bpp RGB as big-endian. */ |
---|
958 | if ((xcontext->bpp == 24 || xcontext->bpp == 32) && |
---|
959 | xcontext->endianness == G_LITTLE_ENDIAN) { |
---|
960 | xcontext->endianness = G_BIG_ENDIAN; |
---|
961 | xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask); |
---|
962 | xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask); |
---|
963 | xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask); |
---|
964 | if (xcontext->bpp == 24) { |
---|
965 | xcontext->visual->red_mask >>= 8; |
---|
966 | xcontext->visual->green_mask >>= 8; |
---|
967 | xcontext->visual->blue_mask >>= 8; |
---|
968 | } |
---|
969 | } |
---|
970 | |
---|
971 | xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext); |
---|
972 | |
---|
973 | if (!xcontext->caps) { |
---|
974 | XCloseDisplay (xcontext->disp); |
---|
975 | g_mutex_unlock (xvimagesink->x_lock); |
---|
976 | g_free (xcontext); |
---|
977 | /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */ |
---|
978 | return NULL; |
---|
979 | } |
---|
980 | #ifdef HAVE_XSHM |
---|
981 | /* Search for XShm extension support */ |
---|
982 | if (XShmQueryExtension (xcontext->disp) && |
---|
983 | gst_xvimagesink_check_xshm_calls (xcontext)) { |
---|
984 | xcontext->use_xshm = TRUE; |
---|
985 | GST_DEBUG ("xvimagesink is using XShm extension"); |
---|
986 | } else |
---|
987 | #endif /* HAVE_XSHM */ |
---|
988 | { |
---|
989 | xcontext->use_xshm = FALSE; |
---|
990 | GST_DEBUG ("xvimagesink is not using XShm extension"); |
---|
991 | } |
---|
992 | |
---|
993 | xv_attr = XvQueryPortAttributes (xcontext->disp, |
---|
994 | xcontext->xv_port_id, &N_attr); |
---|
995 | |
---|
996 | |
---|
997 | /* Generate the channels list */ |
---|
998 | for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) { |
---|
999 | XvAttribute *matching_attr = NULL; |
---|
1000 | |
---|
1001 | if (xv_attr != NULL) { |
---|
1002 | for (j = 0; j < N_attr && matching_attr == NULL; ++j) |
---|
1003 | if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name)) |
---|
1004 | matching_attr = xv_attr + j; |
---|
1005 | } |
---|
1006 | |
---|
1007 | if (matching_attr) { |
---|
1008 | GstColorBalanceChannel *channel; |
---|
1009 | |
---|
1010 | channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL); |
---|
1011 | channel->label = g_strdup (channels[i]); |
---|
1012 | channel->min_value = matching_attr ? matching_attr->min_value : -1000; |
---|
1013 | channel->max_value = matching_attr ? matching_attr->max_value : 1000; |
---|
1014 | |
---|
1015 | xcontext->channels_list = g_list_append (xcontext->channels_list, |
---|
1016 | channel); |
---|
1017 | |
---|
1018 | /* If the colorbalance settings have not been touched we get Xv values |
---|
1019 | as defaults and update our internal variables */ |
---|
1020 | if (!xvimagesink->cb_changed) { |
---|
1021 | gint val; |
---|
1022 | |
---|
1023 | XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id, |
---|
1024 | XInternAtom (xcontext->disp, channel->label, 1), &val); |
---|
1025 | /* Normalize val to [-1000, 1000] */ |
---|
1026 | val = -1000 + 2000 * (val - channel->min_value) / |
---|
1027 | (channel->max_value - channel->min_value); |
---|
1028 | |
---|
1029 | if (!g_ascii_strcasecmp (channels[i], "XV_HUE")) |
---|
1030 | xvimagesink->hue = val; |
---|
1031 | else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION")) |
---|
1032 | xvimagesink->saturation = val; |
---|
1033 | else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS")) |
---|
1034 | xvimagesink->brightness = val; |
---|
1035 | else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST")) |
---|
1036 | xvimagesink->contrast = val; |
---|
1037 | } |
---|
1038 | } |
---|
1039 | } |
---|
1040 | |
---|
1041 | if (xv_attr) |
---|
1042 | XFree (xv_attr); |
---|
1043 | |
---|
1044 | g_mutex_unlock (xvimagesink->x_lock); |
---|
1045 | |
---|
1046 | return xcontext; |
---|
1047 | } |
---|
1048 | |
---|
1049 | /* This function cleans the X context. Closing the Display, releasing the XV |
---|
1050 | port and unrefing the caps for supported formats. */ |
---|
1051 | static void |
---|
1052 | gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink) |
---|
1053 | { |
---|
1054 | GList *formats_list, *channels_list; |
---|
1055 | |
---|
1056 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
1057 | g_return_if_fail (xvimagesink->xcontext != NULL); |
---|
1058 | |
---|
1059 | formats_list = xvimagesink->xcontext->formats_list; |
---|
1060 | |
---|
1061 | while (formats_list) { |
---|
1062 | GstXvImageFormat *format = formats_list->data; |
---|
1063 | |
---|
1064 | gst_caps_free (format->caps); |
---|
1065 | g_free (format); |
---|
1066 | formats_list = g_list_next (formats_list); |
---|
1067 | } |
---|
1068 | |
---|
1069 | if (xvimagesink->xcontext->formats_list) |
---|
1070 | g_list_free (xvimagesink->xcontext->formats_list); |
---|
1071 | |
---|
1072 | channels_list = xvimagesink->xcontext->channels_list; |
---|
1073 | |
---|
1074 | while (channels_list) { |
---|
1075 | GstColorBalanceChannel *channel = channels_list->data; |
---|
1076 | |
---|
1077 | g_object_unref (channel); |
---|
1078 | channels_list = g_list_next (channels_list); |
---|
1079 | } |
---|
1080 | |
---|
1081 | if (xvimagesink->xcontext->channels_list) |
---|
1082 | g_list_free (xvimagesink->xcontext->channels_list); |
---|
1083 | |
---|
1084 | gst_caps_free (xvimagesink->xcontext->caps); |
---|
1085 | g_free (xvimagesink->xcontext->par); |
---|
1086 | |
---|
1087 | g_mutex_lock (xvimagesink->x_lock); |
---|
1088 | |
---|
1089 | XvUngrabPort (xvimagesink->xcontext->disp, |
---|
1090 | xvimagesink->xcontext->xv_port_id, 0); |
---|
1091 | |
---|
1092 | XCloseDisplay (xvimagesink->xcontext->disp); |
---|
1093 | |
---|
1094 | g_mutex_unlock (xvimagesink->x_lock); |
---|
1095 | |
---|
1096 | g_free (xvimagesink->xcontext); |
---|
1097 | xvimagesink->xcontext = NULL; |
---|
1098 | } |
---|
1099 | |
---|
1100 | static void |
---|
1101 | gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink) |
---|
1102 | { |
---|
1103 | g_mutex_lock (xvimagesink->pool_lock); |
---|
1104 | |
---|
1105 | while (xvimagesink->image_pool) { |
---|
1106 | GstXvImage *xvimage = xvimagesink->image_pool->data; |
---|
1107 | |
---|
1108 | xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool, |
---|
1109 | xvimagesink->image_pool); |
---|
1110 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage); |
---|
1111 | } |
---|
1112 | |
---|
1113 | g_mutex_unlock (xvimagesink->pool_lock); |
---|
1114 | } |
---|
1115 | |
---|
1116 | /* Element stuff */ |
---|
1117 | |
---|
1118 | static GstCaps * |
---|
1119 | gst_xvimagesink_fixate (GstPad * pad, const GstCaps * caps) |
---|
1120 | { |
---|
1121 | GstStructure *structure; |
---|
1122 | GstCaps *newcaps; |
---|
1123 | |
---|
1124 | if (gst_caps_get_size (caps) > 1) |
---|
1125 | return NULL; |
---|
1126 | |
---|
1127 | newcaps = gst_caps_copy (caps); |
---|
1128 | structure = gst_caps_get_structure (newcaps, 0); |
---|
1129 | |
---|
1130 | if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 320)) { |
---|
1131 | return newcaps; |
---|
1132 | } |
---|
1133 | if (gst_caps_structure_fixate_field_nearest_int (structure, "height", 240)) { |
---|
1134 | return newcaps; |
---|
1135 | } |
---|
1136 | if (gst_caps_structure_fixate_field_nearest_double (structure, "framerate", |
---|
1137 | 30.0)) { |
---|
1138 | return newcaps; |
---|
1139 | } |
---|
1140 | |
---|
1141 | gst_caps_free (newcaps); |
---|
1142 | return NULL; |
---|
1143 | } |
---|
1144 | |
---|
1145 | /* This function tries to get a format matching with a given caps in the |
---|
1146 | supported list of formats we generated in gst_xvimagesink_get_xv_support */ |
---|
1147 | static gint |
---|
1148 | gst_xvimagesink_get_fourcc_from_caps (GstXvImageSink * xvimagesink, |
---|
1149 | GstCaps * caps) |
---|
1150 | { |
---|
1151 | GList *list = NULL; |
---|
1152 | |
---|
1153 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0); |
---|
1154 | |
---|
1155 | list = xvimagesink->xcontext->formats_list; |
---|
1156 | |
---|
1157 | while (list) { |
---|
1158 | GstXvImageFormat *format = list->data; |
---|
1159 | |
---|
1160 | if (format) { |
---|
1161 | GstCaps *icaps = NULL; |
---|
1162 | |
---|
1163 | icaps = gst_caps_intersect (caps, format->caps); |
---|
1164 | if (!gst_caps_is_empty (icaps)) |
---|
1165 | return format->format; |
---|
1166 | } |
---|
1167 | list = g_list_next (list); |
---|
1168 | } |
---|
1169 | |
---|
1170 | return 0; |
---|
1171 | } |
---|
1172 | |
---|
1173 | static GstCaps * |
---|
1174 | gst_xvimagesink_getcaps (GstPad * pad) |
---|
1175 | { |
---|
1176 | GstXvImageSink *xvimagesink; |
---|
1177 | |
---|
1178 | xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); |
---|
1179 | |
---|
1180 | if (xvimagesink->xcontext) |
---|
1181 | return gst_caps_copy (xvimagesink->xcontext->caps); |
---|
1182 | |
---|
1183 | return gst_caps_copy (gst_pad_get_pad_template_caps (pad)); |
---|
1184 | } |
---|
1185 | |
---|
1186 | static GstPadLinkReturn |
---|
1187 | gst_xvimagesink_sink_link (GstPad * pad, const GstCaps * caps) |
---|
1188 | { |
---|
1189 | GstXvImageSink *xvimagesink; |
---|
1190 | GstStructure *structure; |
---|
1191 | gint im_format = 0; |
---|
1192 | gboolean ret; |
---|
1193 | gint video_width, video_height; |
---|
1194 | gint video_par_n, video_par_d; /* video's PAR */ |
---|
1195 | gint display_par_n, display_par_d; /* display's PAR */ |
---|
1196 | GValue display_ratio = { 0, }; /* display w/h ratio */ |
---|
1197 | const GValue *caps_par; |
---|
1198 | gint num, den; |
---|
1199 | |
---|
1200 | xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); |
---|
1201 | |
---|
1202 | GST_DEBUG_OBJECT (xvimagesink, |
---|
1203 | "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %" |
---|
1204 | GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps); |
---|
1205 | |
---|
1206 | structure = gst_caps_get_structure (caps, 0); |
---|
1207 | ret = gst_structure_get_int (structure, "width", &video_width); |
---|
1208 | ret &= gst_structure_get_int (structure, "height", &video_height); |
---|
1209 | ret &= gst_structure_get_double (structure, "framerate", |
---|
1210 | &xvimagesink->framerate); |
---|
1211 | if (!ret) |
---|
1212 | return GST_PAD_LINK_REFUSED; |
---|
1213 | |
---|
1214 | xvimagesink->video_width = video_width; |
---|
1215 | xvimagesink->video_height = video_height; |
---|
1216 | if (!gst_structure_get_fourcc (structure, "format", &im_format)) { |
---|
1217 | im_format = |
---|
1218 | gst_xvimagesink_get_fourcc_from_caps (xvimagesink, |
---|
1219 | gst_caps_copy (caps)); |
---|
1220 | } |
---|
1221 | if (im_format == 0) { |
---|
1222 | return GST_PAD_LINK_REFUSED; |
---|
1223 | } |
---|
1224 | |
---|
1225 | /* get aspect ratio from caps if it's present, and |
---|
1226 | * convert video width and height to a display width and height |
---|
1227 | * using wd / hd = wv / hv * PARv / PARd |
---|
1228 | * the ratio wd / hd will be stored in display_ratio */ |
---|
1229 | g_value_init (&display_ratio, GST_TYPE_FRACTION); |
---|
1230 | |
---|
1231 | /* get video's PAR */ |
---|
1232 | caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio"); |
---|
1233 | if (caps_par) { |
---|
1234 | video_par_n = gst_value_get_fraction_numerator (caps_par); |
---|
1235 | video_par_d = gst_value_get_fraction_denominator (caps_par); |
---|
1236 | } else { |
---|
1237 | video_par_n = 1; |
---|
1238 | video_par_d = 1; |
---|
1239 | } |
---|
1240 | /* get display's PAR */ |
---|
1241 | if (xvimagesink->par) { |
---|
1242 | display_par_n = gst_value_get_fraction_numerator (xvimagesink->par); |
---|
1243 | display_par_d = gst_value_get_fraction_denominator (xvimagesink->par); |
---|
1244 | } else { |
---|
1245 | display_par_n = 1; |
---|
1246 | display_par_d = 1; |
---|
1247 | } |
---|
1248 | |
---|
1249 | gst_value_set_fraction (&display_ratio, |
---|
1250 | video_width * video_par_n * display_par_d, |
---|
1251 | video_height * video_par_d * display_par_n); |
---|
1252 | |
---|
1253 | num = gst_value_get_fraction_numerator (&display_ratio); |
---|
1254 | den = gst_value_get_fraction_denominator (&display_ratio); |
---|
1255 | GST_DEBUG_OBJECT (xvimagesink, |
---|
1256 | "video width/height: %dx%d, calculated display ratio: %d/%d", |
---|
1257 | video_width, video_height, num, den); |
---|
1258 | |
---|
1259 | /* now find a width x height that respects this display ratio. |
---|
1260 | * prefer those that have one of w/h the same as the incoming video |
---|
1261 | * using wd / hd = num / den */ |
---|
1262 | |
---|
1263 | /* start with same height, because of interlaced video */ |
---|
1264 | /* check hd / den is an integer scale factor, and scale wd with the PAR */ |
---|
1265 | if (video_height % den == 0) { |
---|
1266 | GST_DEBUG_OBJECT (xvimagesink, "keeping video height"); |
---|
1267 | GST_VIDEOSINK_WIDTH (xvimagesink) = video_height * num / den; |
---|
1268 | GST_VIDEOSINK_HEIGHT (xvimagesink) = video_height; |
---|
1269 | } else if (video_width % num == 0) { |
---|
1270 | GST_DEBUG_OBJECT (xvimagesink, "keeping video width"); |
---|
1271 | GST_VIDEOSINK_WIDTH (xvimagesink) = video_width; |
---|
1272 | GST_VIDEOSINK_HEIGHT (xvimagesink) = video_width * den / num; |
---|
1273 | } else { |
---|
1274 | GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height"); |
---|
1275 | GST_VIDEOSINK_WIDTH (xvimagesink) = video_height * num / den; |
---|
1276 | GST_VIDEOSINK_HEIGHT (xvimagesink) = video_height; |
---|
1277 | } |
---|
1278 | GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d", |
---|
1279 | GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink)); |
---|
1280 | |
---|
1281 | /* Creating our window and our image with the display size in pixels */ |
---|
1282 | g_assert (GST_VIDEOSINK_WIDTH (xvimagesink) > 0); |
---|
1283 | g_assert (GST_VIDEOSINK_HEIGHT (xvimagesink) > 0); |
---|
1284 | if (!xvimagesink->xwindow) |
---|
1285 | xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink, |
---|
1286 | GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink)); |
---|
1287 | else { |
---|
1288 | if (xvimagesink->xwindow->internal) |
---|
1289 | gst_xvimagesink_xwindow_resize (xvimagesink, xvimagesink->xwindow, |
---|
1290 | GST_VIDEOSINK_WIDTH (xvimagesink), |
---|
1291 | GST_VIDEOSINK_HEIGHT (xvimagesink)); |
---|
1292 | } |
---|
1293 | |
---|
1294 | /* We renew our xvimage only if size or format changed; |
---|
1295 | * the xvimage is the same size as the video pixel size */ |
---|
1296 | if ((xvimagesink->xvimage) && |
---|
1297 | ((im_format != xvimagesink->xvimage->im_format) || |
---|
1298 | (video_width != xvimagesink->xvimage->width) || |
---|
1299 | (video_height != xvimagesink->xvimage->height))) { |
---|
1300 | GST_DEBUG_OBJECT (xvimagesink, |
---|
1301 | "old format " GST_FOURCC_FORMAT ", new format " GST_FOURCC_FORMAT, |
---|
1302 | GST_FOURCC_ARGS (xvimagesink->xcontext->im_format), |
---|
1303 | GST_FOURCC_ARGS (im_format)); |
---|
1304 | GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage"); |
---|
1305 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage); |
---|
1306 | xvimagesink->xvimage = NULL; |
---|
1307 | } |
---|
1308 | |
---|
1309 | xvimagesink->xcontext->im_format = im_format; |
---|
1310 | |
---|
1311 | gst_x_overlay_got_desired_size (GST_X_OVERLAY (xvimagesink), |
---|
1312 | GST_VIDEOSINK_WIDTH (xvimagesink), GST_VIDEOSINK_HEIGHT (xvimagesink)); |
---|
1313 | |
---|
1314 | return GST_PAD_LINK_OK; |
---|
1315 | } |
---|
1316 | |
---|
1317 | static GstElementStateReturn |
---|
1318 | gst_xvimagesink_change_state (GstElement * element) |
---|
1319 | { |
---|
1320 | GstXvImageSink *xvimagesink; |
---|
1321 | |
---|
1322 | xvimagesink = GST_XVIMAGESINK (element); |
---|
1323 | |
---|
1324 | switch (GST_STATE_TRANSITION (element)) { |
---|
1325 | case GST_STATE_NULL_TO_READY: |
---|
1326 | /* Initializing the XContext */ |
---|
1327 | if (!xvimagesink->xcontext && |
---|
1328 | !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) |
---|
1329 | return GST_STATE_FAILURE; |
---|
1330 | /* update object's par with calculated one if not set yet */ |
---|
1331 | if (!xvimagesink->par) { |
---|
1332 | xvimagesink->par = g_new0 (GValue, 1); |
---|
1333 | gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par); |
---|
1334 | GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR"); |
---|
1335 | } |
---|
1336 | /* call XSynchronize with the current value of synchronous */ |
---|
1337 | GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s", |
---|
1338 | xvimagesink->synchronous ? "TRUE" : "FALSE"); |
---|
1339 | XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous); |
---|
1340 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1341 | break; |
---|
1342 | case GST_STATE_READY_TO_PAUSED: |
---|
1343 | if (xvimagesink->xwindow) |
---|
1344 | gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow); |
---|
1345 | xvimagesink->time = 0; |
---|
1346 | break; |
---|
1347 | case GST_STATE_PAUSED_TO_PLAYING: |
---|
1348 | break; |
---|
1349 | case GST_STATE_PLAYING_TO_PAUSED: |
---|
1350 | break; |
---|
1351 | case GST_STATE_PAUSED_TO_READY: |
---|
1352 | xvimagesink->framerate = 0; |
---|
1353 | GST_VIDEOSINK_WIDTH (xvimagesink) = 0; |
---|
1354 | GST_VIDEOSINK_HEIGHT (xvimagesink) = 0; |
---|
1355 | break; |
---|
1356 | case GST_STATE_READY_TO_NULL: |
---|
1357 | if (xvimagesink->xvimage) { |
---|
1358 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage); |
---|
1359 | xvimagesink->xvimage = NULL; |
---|
1360 | } |
---|
1361 | |
---|
1362 | if (xvimagesink->image_pool) |
---|
1363 | gst_xvimagesink_imagepool_clear (xvimagesink); |
---|
1364 | |
---|
1365 | if (xvimagesink->xwindow) { |
---|
1366 | gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow); |
---|
1367 | xvimagesink->xwindow = NULL; |
---|
1368 | } |
---|
1369 | |
---|
1370 | if (xvimagesink->xcontext) { |
---|
1371 | gst_xvimagesink_xcontext_clear (xvimagesink); |
---|
1372 | xvimagesink->xcontext = NULL; |
---|
1373 | } |
---|
1374 | break; |
---|
1375 | } |
---|
1376 | |
---|
1377 | if (GST_ELEMENT_CLASS (parent_class)->change_state) |
---|
1378 | return GST_ELEMENT_CLASS (parent_class)->change_state (element); |
---|
1379 | |
---|
1380 | return GST_STATE_SUCCESS; |
---|
1381 | } |
---|
1382 | |
---|
1383 | static void |
---|
1384 | gst_xvimagesink_chain (GstPad * pad, GstData * data) |
---|
1385 | { |
---|
1386 | GstBuffer *buf = NULL; |
---|
1387 | GstXvImageSink *xvimagesink; |
---|
1388 | |
---|
1389 | g_return_if_fail (GST_IS_PAD (pad)); |
---|
1390 | g_return_if_fail (data != NULL); |
---|
1391 | |
---|
1392 | xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); |
---|
1393 | |
---|
1394 | if (GST_IS_EVENT (data)) { |
---|
1395 | gst_pad_event_default (pad, GST_EVENT (data)); |
---|
1396 | return; |
---|
1397 | } |
---|
1398 | |
---|
1399 | buf = GST_BUFFER (data); |
---|
1400 | /* update time */ |
---|
1401 | if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { |
---|
1402 | xvimagesink->time = GST_BUFFER_TIMESTAMP (buf); |
---|
1403 | } |
---|
1404 | GST_LOG_OBJECT (xvimagesink, "clock wait: %" GST_TIME_FORMAT, |
---|
1405 | GST_TIME_ARGS (xvimagesink->time)); |
---|
1406 | |
---|
1407 | if (GST_VIDEOSINK_CLOCK (xvimagesink)) { |
---|
1408 | gst_element_wait (GST_ELEMENT (xvimagesink), xvimagesink->time); |
---|
1409 | } |
---|
1410 | |
---|
1411 | /* If this buffer has been allocated using our buffer management we simply |
---|
1412 | put the ximage which is in the PRIVATE pointer */ |
---|
1413 | if (GST_BUFFER_FREE_DATA_FUNC (buf) == gst_xvimagesink_buffer_free) { |
---|
1414 | gst_xvimagesink_xvimage_put (xvimagesink, GST_BUFFER_PRIVATE (buf)); |
---|
1415 | } else { |
---|
1416 | /* Else we have to copy the data into our private image, */ |
---|
1417 | /* if we have one... */ |
---|
1418 | if (!xvimagesink->xvimage) { |
---|
1419 | GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage"); |
---|
1420 | xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink, |
---|
1421 | xvimagesink->video_width, xvimagesink->video_height); |
---|
1422 | if (!xvimagesink->xvimage) { |
---|
1423 | /* No image available. That's very bad ! */ |
---|
1424 | gst_buffer_unref (buf); |
---|
1425 | GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL), |
---|
1426 | ("Failed creating an XvImage in xvimagesink chain function.")); |
---|
1427 | return; |
---|
1428 | } |
---|
1429 | } |
---|
1430 | |
---|
1431 | memcpy (xvimagesink->xvimage->xvimage->data, |
---|
1432 | GST_BUFFER_DATA (buf), |
---|
1433 | MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size)); |
---|
1434 | gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage); |
---|
1435 | } |
---|
1436 | |
---|
1437 | /* set correct time for next buffer */ |
---|
1438 | if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && xvimagesink->framerate > 0) { |
---|
1439 | xvimagesink->time += GST_SECOND / xvimagesink->framerate; |
---|
1440 | } |
---|
1441 | |
---|
1442 | gst_buffer_unref (buf); |
---|
1443 | |
---|
1444 | gst_xvimagesink_handle_xevents (xvimagesink, pad); |
---|
1445 | } |
---|
1446 | |
---|
1447 | /* Buffer management */ |
---|
1448 | |
---|
1449 | static void |
---|
1450 | gst_xvimagesink_buffer_free (GstBuffer * buffer) |
---|
1451 | { |
---|
1452 | GstXvImageSink *xvimagesink; |
---|
1453 | GstXvImage *xvimage; |
---|
1454 | |
---|
1455 | xvimage = GST_BUFFER_PRIVATE (buffer); |
---|
1456 | |
---|
1457 | g_assert (GST_IS_XVIMAGESINK (xvimage->xvimagesink)); |
---|
1458 | xvimagesink = xvimage->xvimagesink; |
---|
1459 | |
---|
1460 | /* If our geometry changed we can't reuse that image. */ |
---|
1461 | if ((xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) || |
---|
1462 | (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink))) |
---|
1463 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage); |
---|
1464 | else { |
---|
1465 | /* In that case we can reuse the image and add it to our image pool. */ |
---|
1466 | g_mutex_lock (xvimagesink->pool_lock); |
---|
1467 | xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool, |
---|
1468 | xvimage); |
---|
1469 | g_mutex_unlock (xvimagesink->pool_lock); |
---|
1470 | } |
---|
1471 | } |
---|
1472 | |
---|
1473 | static GstBuffer * |
---|
1474 | gst_xvimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size) |
---|
1475 | { |
---|
1476 | GstXvImageSink *xvimagesink; |
---|
1477 | GstBuffer *buffer; |
---|
1478 | GstXvImage *xvimage = NULL; |
---|
1479 | gboolean not_found = TRUE; |
---|
1480 | |
---|
1481 | xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad)); |
---|
1482 | |
---|
1483 | g_mutex_lock (xvimagesink->pool_lock); |
---|
1484 | |
---|
1485 | /* Walking through the pool cleaning unusable images and searching for a |
---|
1486 | suitable one */ |
---|
1487 | while (not_found && xvimagesink->image_pool) { |
---|
1488 | xvimage = xvimagesink->image_pool->data; |
---|
1489 | |
---|
1490 | if (xvimage) { |
---|
1491 | /* Removing from the pool */ |
---|
1492 | xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool, |
---|
1493 | xvimagesink->image_pool); |
---|
1494 | |
---|
1495 | /* We check for geometry or image format changes */ |
---|
1496 | if ((xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) || |
---|
1497 | (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink)) || |
---|
1498 | (xvimage->im_format != xvimagesink->xcontext->im_format)) { |
---|
1499 | /* This image is unusable. Destroying... */ |
---|
1500 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage); |
---|
1501 | xvimage = NULL; |
---|
1502 | } else { |
---|
1503 | /* We found a suitable image */ |
---|
1504 | break; |
---|
1505 | } |
---|
1506 | } |
---|
1507 | } |
---|
1508 | |
---|
1509 | g_mutex_unlock (xvimagesink->pool_lock); |
---|
1510 | |
---|
1511 | if (!xvimage) { |
---|
1512 | /* We found no suitable image in the pool. Creating... */ |
---|
1513 | GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage"); |
---|
1514 | xvimage = gst_xvimagesink_xvimage_new (xvimagesink, |
---|
1515 | xvimagesink->video_width, xvimagesink->video_height); |
---|
1516 | } |
---|
1517 | |
---|
1518 | if (xvimage) { |
---|
1519 | buffer = gst_buffer_new (); |
---|
1520 | |
---|
1521 | /* Storing some pointers in the buffer */ |
---|
1522 | GST_BUFFER_PRIVATE (buffer) = xvimage; |
---|
1523 | |
---|
1524 | GST_BUFFER_DATA (buffer) = xvimage->xvimage->data; |
---|
1525 | GST_BUFFER_FREE_DATA_FUNC (buffer) = gst_xvimagesink_buffer_free; |
---|
1526 | GST_BUFFER_SIZE (buffer) = xvimage->size; |
---|
1527 | return buffer; |
---|
1528 | } else |
---|
1529 | return NULL; |
---|
1530 | } |
---|
1531 | |
---|
1532 | /* Interfaces stuff */ |
---|
1533 | |
---|
1534 | static gboolean |
---|
1535 | gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type) |
---|
1536 | { |
---|
1537 | g_assert (type == GST_TYPE_NAVIGATION || |
---|
1538 | type == GST_TYPE_X_OVERLAY || type == GST_TYPE_COLOR_BALANCE); |
---|
1539 | return TRUE; |
---|
1540 | } |
---|
1541 | |
---|
1542 | static void |
---|
1543 | gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass) |
---|
1544 | { |
---|
1545 | klass->supported = gst_xvimagesink_interface_supported; |
---|
1546 | } |
---|
1547 | |
---|
1548 | static void |
---|
1549 | gst_xvimagesink_navigation_send_event (GstNavigation * navigation, |
---|
1550 | GstStructure * structure) |
---|
1551 | { |
---|
1552 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation); |
---|
1553 | GstEvent *event; |
---|
1554 | double x, y; |
---|
1555 | |
---|
1556 | event = gst_event_new (GST_EVENT_NAVIGATION); |
---|
1557 | event->event_data.structure.structure = structure; |
---|
1558 | |
---|
1559 | /* Converting pointer coordinates to the non scaled geometry */ |
---|
1560 | if (gst_structure_get_double (structure, "pointer_x", &x)) { |
---|
1561 | x *= GST_VIDEOSINK_WIDTH (xvimagesink); |
---|
1562 | x /= xvimagesink->xwindow->width; |
---|
1563 | gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL); |
---|
1564 | } |
---|
1565 | if (gst_structure_get_double (structure, "pointer_y", &y)) { |
---|
1566 | y *= GST_VIDEOSINK_HEIGHT (xvimagesink); |
---|
1567 | y /= xvimagesink->xwindow->height; |
---|
1568 | gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL); |
---|
1569 | } |
---|
1570 | |
---|
1571 | gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD (xvimagesink)), |
---|
1572 | event); |
---|
1573 | } |
---|
1574 | |
---|
1575 | static void |
---|
1576 | gst_xvimagesink_navigation_init (GstNavigationInterface * iface) |
---|
1577 | { |
---|
1578 | iface->send_event = gst_xvimagesink_navigation_send_event; |
---|
1579 | } |
---|
1580 | |
---|
1581 | static void |
---|
1582 | gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id) |
---|
1583 | { |
---|
1584 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay); |
---|
1585 | GstXWindow *xwindow = NULL; |
---|
1586 | XWindowAttributes attr; |
---|
1587 | |
---|
1588 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
1589 | |
---|
1590 | /* If we already use that window return */ |
---|
1591 | if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) |
---|
1592 | return; |
---|
1593 | |
---|
1594 | /* If the element has not initialized the X11 context try to do so */ |
---|
1595 | if (!xvimagesink->xcontext && |
---|
1596 | !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) |
---|
1597 | /* we have thrown a GST_ELEMENT_ERROR now */ |
---|
1598 | return; |
---|
1599 | |
---|
1600 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1601 | |
---|
1602 | /* Clear image pool as the images are unusable anyway */ |
---|
1603 | gst_xvimagesink_imagepool_clear (xvimagesink); |
---|
1604 | |
---|
1605 | /* Clear the xvimage */ |
---|
1606 | if (xvimagesink->xvimage) { |
---|
1607 | gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage); |
---|
1608 | xvimagesink->xvimage = NULL; |
---|
1609 | } |
---|
1610 | |
---|
1611 | /* If a window is there already we destroy it */ |
---|
1612 | if (xvimagesink->xwindow) { |
---|
1613 | gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow); |
---|
1614 | xvimagesink->xwindow = NULL; |
---|
1615 | } |
---|
1616 | |
---|
1617 | /* If the xid is 0 we go back to an internal window */ |
---|
1618 | if (xwindow_id == 0) { |
---|
1619 | /* If no width/height caps nego did not happen window will be created |
---|
1620 | during caps nego then */ |
---|
1621 | if (GST_VIDEOSINK_WIDTH (xvimagesink) && GST_VIDEOSINK_HEIGHT (xvimagesink)) { |
---|
1622 | xwindow = gst_xvimagesink_xwindow_new (xvimagesink, |
---|
1623 | GST_VIDEOSINK_WIDTH (xvimagesink), |
---|
1624 | GST_VIDEOSINK_HEIGHT (xvimagesink)); |
---|
1625 | } |
---|
1626 | } else { |
---|
1627 | xwindow = g_new0 (GstXWindow, 1); |
---|
1628 | |
---|
1629 | xwindow->win = xwindow_id; |
---|
1630 | |
---|
1631 | /* We get window geometry, set the event we want to receive, |
---|
1632 | and create a GC */ |
---|
1633 | g_mutex_lock (xvimagesink->x_lock); |
---|
1634 | XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr); |
---|
1635 | xwindow->width = attr.width; |
---|
1636 | xwindow->height = attr.height; |
---|
1637 | xwindow->internal = FALSE; |
---|
1638 | XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask | |
---|
1639 | StructureNotifyMask | PointerMotionMask | KeyPressMask | |
---|
1640 | KeyReleaseMask); |
---|
1641 | |
---|
1642 | xwindow->gc = XCreateGC (xvimagesink->xcontext->disp, |
---|
1643 | xwindow->win, 0, NULL); |
---|
1644 | g_mutex_unlock (xvimagesink->x_lock); |
---|
1645 | } |
---|
1646 | |
---|
1647 | if (xwindow) |
---|
1648 | xvimagesink->xwindow = xwindow; |
---|
1649 | } |
---|
1650 | |
---|
1651 | static void |
---|
1652 | gst_xvimagesink_get_desired_size (GstXOverlay * overlay, |
---|
1653 | guint * width, guint * height) |
---|
1654 | { |
---|
1655 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay); |
---|
1656 | |
---|
1657 | *width = GST_VIDEOSINK_WIDTH (xvimagesink); |
---|
1658 | *height = GST_VIDEOSINK_HEIGHT (xvimagesink); |
---|
1659 | } |
---|
1660 | |
---|
1661 | static void |
---|
1662 | gst_xvimagesink_expose (GstXOverlay * overlay) |
---|
1663 | { |
---|
1664 | XWindowAttributes attr; |
---|
1665 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay); |
---|
1666 | |
---|
1667 | if (!xvimagesink->xwindow) |
---|
1668 | return; |
---|
1669 | |
---|
1670 | /* Update the window geometry */ |
---|
1671 | g_mutex_lock (xvimagesink->x_lock); |
---|
1672 | XGetWindowAttributes (xvimagesink->xcontext->disp, |
---|
1673 | xvimagesink->xwindow->win, &attr); |
---|
1674 | g_mutex_unlock (xvimagesink->x_lock); |
---|
1675 | |
---|
1676 | xvimagesink->xwindow->width = attr.width; |
---|
1677 | xvimagesink->xwindow->height = attr.height; |
---|
1678 | |
---|
1679 | if (xvimagesink->cur_image) { |
---|
1680 | gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->cur_image); |
---|
1681 | } |
---|
1682 | } |
---|
1683 | |
---|
1684 | static void |
---|
1685 | gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface) |
---|
1686 | { |
---|
1687 | iface->set_xwindow_id = gst_xvimagesink_set_xwindow_id; |
---|
1688 | iface->get_desired_size = gst_xvimagesink_get_desired_size; |
---|
1689 | iface->expose = gst_xvimagesink_expose; |
---|
1690 | } |
---|
1691 | |
---|
1692 | static const GList * |
---|
1693 | gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance) |
---|
1694 | { |
---|
1695 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance); |
---|
1696 | |
---|
1697 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
---|
1698 | |
---|
1699 | if (xvimagesink->xcontext) |
---|
1700 | return xvimagesink->xcontext->channels_list; |
---|
1701 | else |
---|
1702 | return NULL; |
---|
1703 | } |
---|
1704 | |
---|
1705 | static void |
---|
1706 | gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance, |
---|
1707 | GstColorBalanceChannel * channel, gint value) |
---|
1708 | { |
---|
1709 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance); |
---|
1710 | |
---|
1711 | g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
---|
1712 | g_return_if_fail (channel->label != NULL); |
---|
1713 | |
---|
1714 | xvimagesink->cb_changed = TRUE; |
---|
1715 | |
---|
1716 | /* Normalize val to [-1000, 1000] */ |
---|
1717 | value = -1000 + 2000 * (value - channel->min_value) / |
---|
1718 | (channel->max_value - channel->min_value); |
---|
1719 | |
---|
1720 | if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) { |
---|
1721 | xvimagesink->hue = value; |
---|
1722 | } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) { |
---|
1723 | xvimagesink->saturation = value; |
---|
1724 | } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) { |
---|
1725 | xvimagesink->contrast = value; |
---|
1726 | } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) { |
---|
1727 | xvimagesink->brightness = value; |
---|
1728 | } else { |
---|
1729 | g_warning ("got an unknown channel %s", channel->label); |
---|
1730 | return; |
---|
1731 | } |
---|
1732 | |
---|
1733 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1734 | } |
---|
1735 | |
---|
1736 | static gint |
---|
1737 | gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance, |
---|
1738 | GstColorBalanceChannel * channel) |
---|
1739 | { |
---|
1740 | GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance); |
---|
1741 | gint value = 0; |
---|
1742 | |
---|
1743 | g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0); |
---|
1744 | g_return_val_if_fail (channel->label != NULL, 0); |
---|
1745 | |
---|
1746 | if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) { |
---|
1747 | value = xvimagesink->hue; |
---|
1748 | } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) { |
---|
1749 | value = xvimagesink->saturation; |
---|
1750 | } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) { |
---|
1751 | value = xvimagesink->contrast; |
---|
1752 | } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) { |
---|
1753 | value = xvimagesink->brightness; |
---|
1754 | } else { |
---|
1755 | g_warning ("got an unknown channel %s", channel->label); |
---|
1756 | } |
---|
1757 | |
---|
1758 | /* Normalize val to [channel->min_value, channel->max_value] */ |
---|
1759 | value = channel->min_value + (channel->max_value - channel->min_value) * |
---|
1760 | (value + 1000) / 2000; |
---|
1761 | |
---|
1762 | return value; |
---|
1763 | } |
---|
1764 | |
---|
1765 | static void |
---|
1766 | gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface) |
---|
1767 | { |
---|
1768 | GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE; |
---|
1769 | iface->list_channels = gst_xvimagesink_colorbalance_list_channels; |
---|
1770 | iface->set_value = gst_xvimagesink_colorbalance_set_value; |
---|
1771 | iface->get_value = gst_xvimagesink_colorbalance_get_value; |
---|
1772 | } |
---|
1773 | |
---|
1774 | /* =========================================== */ |
---|
1775 | /* */ |
---|
1776 | /* Init & Class init */ |
---|
1777 | /* */ |
---|
1778 | /* =========================================== */ |
---|
1779 | |
---|
1780 | static void |
---|
1781 | gst_xvimagesink_set_property (GObject * object, guint prop_id, |
---|
1782 | const GValue * value, GParamSpec * pspec) |
---|
1783 | { |
---|
1784 | GstXvImageSink *xvimagesink; |
---|
1785 | |
---|
1786 | g_return_if_fail (GST_IS_XVIMAGESINK (object)); |
---|
1787 | |
---|
1788 | xvimagesink = GST_XVIMAGESINK (object); |
---|
1789 | |
---|
1790 | switch (prop_id) { |
---|
1791 | case ARG_HUE: |
---|
1792 | xvimagesink->hue = g_value_get_int (value); |
---|
1793 | xvimagesink->cb_changed = TRUE; |
---|
1794 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1795 | break; |
---|
1796 | case ARG_CONTRAST: |
---|
1797 | xvimagesink->contrast = g_value_get_int (value); |
---|
1798 | xvimagesink->cb_changed = TRUE; |
---|
1799 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1800 | break; |
---|
1801 | case ARG_BRIGHTNESS: |
---|
1802 | xvimagesink->brightness = g_value_get_int (value); |
---|
1803 | xvimagesink->cb_changed = TRUE; |
---|
1804 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1805 | break; |
---|
1806 | case ARG_SATURATION: |
---|
1807 | xvimagesink->saturation = g_value_get_int (value); |
---|
1808 | xvimagesink->cb_changed = TRUE; |
---|
1809 | gst_xvimagesink_update_colorbalance (xvimagesink); |
---|
1810 | break; |
---|
1811 | case ARG_DISPLAY: |
---|
1812 | xvimagesink->display_name = g_strdup (g_value_get_string (value)); |
---|
1813 | break; |
---|
1814 | case ARG_SYNCHRONOUS: |
---|
1815 | xvimagesink->synchronous = g_value_get_boolean (value); |
---|
1816 | if (xvimagesink->xcontext) { |
---|
1817 | XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous); |
---|
1818 | GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s", |
---|
1819 | xvimagesink->synchronous ? "TRUE" : "FALSE"); |
---|
1820 | } |
---|
1821 | break; |
---|
1822 | case ARG_PIXEL_ASPECT_RATIO: |
---|
1823 | g_free (xvimagesink->par); |
---|
1824 | xvimagesink->par = g_new0 (GValue, 1); |
---|
1825 | g_value_init (xvimagesink->par, GST_TYPE_FRACTION); |
---|
1826 | if (!g_value_transform (value, xvimagesink->par)) { |
---|
1827 | g_warning ("Could not transform string to aspect ratio"); |
---|
1828 | gst_value_set_fraction (xvimagesink->par, 1, 1); |
---|
1829 | } |
---|
1830 | GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d", |
---|
1831 | gst_value_get_fraction_numerator (xvimagesink->par), |
---|
1832 | gst_value_get_fraction_denominator (xvimagesink->par)); |
---|
1833 | break; |
---|
1834 | default: |
---|
1835 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
---|
1836 | break; |
---|
1837 | } |
---|
1838 | } |
---|
1839 | |
---|
1840 | static void |
---|
1841 | gst_xvimagesink_get_property (GObject * object, guint prop_id, |
---|
1842 | GValue * value, GParamSpec * pspec) |
---|
1843 | { |
---|
1844 | GstXvImageSink *xvimagesink; |
---|
1845 | |
---|
1846 | g_return_if_fail (GST_IS_XVIMAGESINK (object)); |
---|
1847 | |
---|
1848 | xvimagesink = GST_XVIMAGESINK (object); |
---|
1849 | |
---|
1850 | switch (prop_id) { |
---|
1851 | case ARG_HUE: |
---|
1852 | g_value_set_int (value, xvimagesink->hue); |
---|
1853 | break; |
---|
1854 | case ARG_CONTRAST: |
---|
1855 | g_value_set_int (value, xvimagesink->contrast); |
---|
1856 | break; |
---|
1857 | case ARG_BRIGHTNESS: |
---|
1858 | g_value_set_int (value, xvimagesink->brightness); |
---|
1859 | break; |
---|
1860 | case ARG_SATURATION: |
---|
1861 | g_value_set_int (value, xvimagesink->saturation); |
---|
1862 | break; |
---|
1863 | case ARG_DISPLAY: |
---|
1864 | g_value_set_string (value, g_strdup (xvimagesink->display_name)); |
---|
1865 | break; |
---|
1866 | case ARG_SYNCHRONOUS: |
---|
1867 | g_value_set_boolean (value, xvimagesink->synchronous); |
---|
1868 | break; |
---|
1869 | case ARG_PIXEL_ASPECT_RATIO: |
---|
1870 | if (xvimagesink->par) |
---|
1871 | g_value_transform (xvimagesink->par, value); |
---|
1872 | break; |
---|
1873 | default: |
---|
1874 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
---|
1875 | break; |
---|
1876 | } |
---|
1877 | } |
---|
1878 | |
---|
1879 | /* Finalize is called only once, dispose can be called multiple times. |
---|
1880 | * We use mutexes and don't reset stuff to NULL here so let's register |
---|
1881 | * as a finalize. */ |
---|
1882 | static void |
---|
1883 | gst_xvimagesink_finalize (GObject * object) |
---|
1884 | { |
---|
1885 | GstXvImageSink *xvimagesink; |
---|
1886 | |
---|
1887 | xvimagesink = GST_XVIMAGESINK (object); |
---|
1888 | |
---|
1889 | if (xvimagesink->display_name) { |
---|
1890 | g_free (xvimagesink->display_name); |
---|
1891 | xvimagesink->display_name = NULL; |
---|
1892 | } |
---|
1893 | |
---|
1894 | if (xvimagesink->par) { |
---|
1895 | g_free (xvimagesink->par); |
---|
1896 | xvimagesink->par = NULL; |
---|
1897 | } |
---|
1898 | if (xvimagesink->x_lock) { |
---|
1899 | g_mutex_free (xvimagesink->x_lock); |
---|
1900 | xvimagesink->x_lock = NULL; |
---|
1901 | } |
---|
1902 | if (xvimagesink->pool_lock) { |
---|
1903 | g_mutex_free (xvimagesink->pool_lock); |
---|
1904 | xvimagesink->pool_lock = NULL; |
---|
1905 | } |
---|
1906 | |
---|
1907 | G_OBJECT_CLASS (parent_class)->finalize (object); |
---|
1908 | } |
---|
1909 | |
---|
1910 | static void |
---|
1911 | gst_xvimagesink_init (GstXvImageSink * xvimagesink) |
---|
1912 | { |
---|
1913 | GST_VIDEOSINK_PAD (xvimagesink) = |
---|
1914 | gst_pad_new_from_template (gst_static_pad_template_get |
---|
1915 | (&gst_xvimagesink_sink_template_factory), "sink"); |
---|
1916 | |
---|
1917 | gst_element_add_pad (GST_ELEMENT (xvimagesink), |
---|
1918 | GST_VIDEOSINK_PAD (xvimagesink)); |
---|
1919 | |
---|
1920 | gst_pad_set_chain_function (GST_VIDEOSINK_PAD (xvimagesink), |
---|
1921 | gst_xvimagesink_chain); |
---|
1922 | gst_pad_set_link_function (GST_VIDEOSINK_PAD (xvimagesink), |
---|
1923 | gst_xvimagesink_sink_link); |
---|
1924 | gst_pad_set_getcaps_function (GST_VIDEOSINK_PAD (xvimagesink), |
---|
1925 | gst_xvimagesink_getcaps); |
---|
1926 | gst_pad_set_fixate_function (GST_VIDEOSINK_PAD (xvimagesink), |
---|
1927 | gst_xvimagesink_fixate); |
---|
1928 | gst_pad_set_bufferalloc_function (GST_VIDEOSINK_PAD (xvimagesink), |
---|
1929 | gst_xvimagesink_buffer_alloc); |
---|
1930 | |
---|
1931 | xvimagesink->display_name = NULL; |
---|
1932 | xvimagesink->xcontext = NULL; |
---|
1933 | xvimagesink->xwindow = NULL; |
---|
1934 | xvimagesink->xvimage = NULL; |
---|
1935 | xvimagesink->cur_image = NULL; |
---|
1936 | |
---|
1937 | xvimagesink->hue = xvimagesink->saturation = 0; |
---|
1938 | xvimagesink->contrast = xvimagesink->brightness = 0; |
---|
1939 | xvimagesink->cb_changed = FALSE; |
---|
1940 | |
---|
1941 | xvimagesink->framerate = 0; |
---|
1942 | xvimagesink->video_width = 0; |
---|
1943 | xvimagesink->video_height = 0; |
---|
1944 | |
---|
1945 | xvimagesink->x_lock = g_mutex_new (); |
---|
1946 | |
---|
1947 | xvimagesink->image_pool = NULL; |
---|
1948 | xvimagesink->pool_lock = g_mutex_new (); |
---|
1949 | |
---|
1950 | xvimagesink->synchronous = FALSE; |
---|
1951 | xvimagesink->par = NULL; |
---|
1952 | |
---|
1953 | GST_FLAG_SET (xvimagesink, GST_ELEMENT_THREAD_SUGGESTED); |
---|
1954 | GST_FLAG_SET (xvimagesink, GST_ELEMENT_EVENT_AWARE); |
---|
1955 | } |
---|
1956 | |
---|
1957 | static void |
---|
1958 | gst_xvimagesink_base_init (gpointer g_class) |
---|
1959 | { |
---|
1960 | GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
---|
1961 | |
---|
1962 | gst_element_class_set_details (element_class, &gst_xvimagesink_details); |
---|
1963 | |
---|
1964 | gst_element_class_add_pad_template (element_class, |
---|
1965 | gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory)); |
---|
1966 | } |
---|
1967 | |
---|
1968 | static void |
---|
1969 | gst_xvimagesink_class_init (GstXvImageSinkClass * klass) |
---|
1970 | { |
---|
1971 | GObjectClass *gobject_class; |
---|
1972 | GstElementClass *gstelement_class; |
---|
1973 | |
---|
1974 | gobject_class = (GObjectClass *) klass; |
---|
1975 | gstelement_class = (GstElementClass *) klass; |
---|
1976 | |
---|
1977 | parent_class = g_type_class_ref (GST_TYPE_VIDEOSINK); |
---|
1978 | |
---|
1979 | g_object_class_install_property (gobject_class, ARG_CONTRAST, |
---|
1980 | g_param_spec_int ("contrast", "Contrast", "The contrast of the video", |
---|
1981 | -1000, 1000, 0, G_PARAM_READWRITE)); |
---|
1982 | g_object_class_install_property (gobject_class, ARG_BRIGHTNESS, |
---|
1983 | g_param_spec_int ("brightness", "Brightness", |
---|
1984 | "The brightness of the video", -1000, 1000, 0, G_PARAM_READWRITE)); |
---|
1985 | g_object_class_install_property (gobject_class, ARG_HUE, |
---|
1986 | g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0, |
---|
1987 | G_PARAM_READWRITE)); |
---|
1988 | g_object_class_install_property (gobject_class, ARG_SATURATION, |
---|
1989 | g_param_spec_int ("saturation", "Saturation", |
---|
1990 | "The saturation of the video", -1000, 1000, 0, G_PARAM_READWRITE)); |
---|
1991 | g_object_class_install_property (gobject_class, ARG_DISPLAY, |
---|
1992 | g_param_spec_string ("display", "Display", "X Display name", NULL, |
---|
1993 | G_PARAM_READWRITE)); |
---|
1994 | g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS, |
---|
1995 | g_param_spec_boolean ("synchronous", "Synchronous", |
---|
1996 | "When enabled, runs " |
---|
1997 | "the X display in synchronous mode. (used only for debugging)", FALSE, |
---|
1998 | G_PARAM_READWRITE)); |
---|
1999 | g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO, |
---|
2000 | g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio", |
---|
2001 | "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE)); |
---|
2002 | |
---|
2003 | gobject_class->finalize = gst_xvimagesink_finalize; |
---|
2004 | gobject_class->set_property = gst_xvimagesink_set_property; |
---|
2005 | gobject_class->get_property = gst_xvimagesink_get_property; |
---|
2006 | |
---|
2007 | gstelement_class->change_state = gst_xvimagesink_change_state; |
---|
2008 | } |
---|
2009 | |
---|
2010 | /* ============================================================= */ |
---|
2011 | /* */ |
---|
2012 | /* Public Methods */ |
---|
2013 | /* */ |
---|
2014 | /* ============================================================= */ |
---|
2015 | |
---|
2016 | /* =========================================== */ |
---|
2017 | /* */ |
---|
2018 | /* Object typing & Creation */ |
---|
2019 | /* */ |
---|
2020 | /* =========================================== */ |
---|
2021 | |
---|
2022 | GType |
---|
2023 | gst_xvimagesink_get_type (void) |
---|
2024 | { |
---|
2025 | static GType xvimagesink_type = 0; |
---|
2026 | |
---|
2027 | if (!xvimagesink_type) { |
---|
2028 | static const GTypeInfo xvimagesink_info = { |
---|
2029 | sizeof (GstXvImageSinkClass), |
---|
2030 | gst_xvimagesink_base_init, |
---|
2031 | NULL, |
---|
2032 | (GClassInitFunc) gst_xvimagesink_class_init, |
---|
2033 | NULL, |
---|
2034 | NULL, |
---|
2035 | sizeof (GstXvImageSink), |
---|
2036 | 0, |
---|
2037 | (GInstanceInitFunc) gst_xvimagesink_init, |
---|
2038 | }; |
---|
2039 | static const GInterfaceInfo iface_info = { |
---|
2040 | (GInterfaceInitFunc) gst_xvimagesink_interface_init, |
---|
2041 | NULL, |
---|
2042 | NULL, |
---|
2043 | }; |
---|
2044 | static const GInterfaceInfo navigation_info = { |
---|
2045 | (GInterfaceInitFunc) gst_xvimagesink_navigation_init, |
---|
2046 | NULL, |
---|
2047 | NULL, |
---|
2048 | }; |
---|
2049 | static const GInterfaceInfo overlay_info = { |
---|
2050 | (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init, |
---|
2051 | NULL, |
---|
2052 | NULL, |
---|
2053 | }; |
---|
2054 | static const GInterfaceInfo colorbalance_info = { |
---|
2055 | (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init, |
---|
2056 | NULL, |
---|
2057 | NULL, |
---|
2058 | }; |
---|
2059 | |
---|
2060 | xvimagesink_type = g_type_register_static (GST_TYPE_VIDEOSINK, |
---|
2061 | "GstXvImageSink", &xvimagesink_info, 0); |
---|
2062 | |
---|
2063 | g_type_add_interface_static (xvimagesink_type, |
---|
2064 | GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info); |
---|
2065 | g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION, |
---|
2066 | &navigation_info); |
---|
2067 | g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY, |
---|
2068 | &overlay_info); |
---|
2069 | g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE, |
---|
2070 | &colorbalance_info); |
---|
2071 | } |
---|
2072 | |
---|
2073 | return xvimagesink_type; |
---|
2074 | } |
---|
2075 | |
---|
2076 | static gboolean |
---|
2077 | plugin_init (GstPlugin * plugin) |
---|
2078 | { |
---|
2079 | /* Loading the library containing GstVideoSink, our parent object */ |
---|
2080 | if (!gst_library_load ("gstvideo")) |
---|
2081 | return FALSE; |
---|
2082 | |
---|
2083 | if (!gst_element_register (plugin, "xvimagesink", |
---|
2084 | GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK)) |
---|
2085 | return FALSE; |
---|
2086 | |
---|
2087 | GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0, |
---|
2088 | "xvimagesink element"); |
---|
2089 | |
---|
2090 | return TRUE; |
---|
2091 | } |
---|
2092 | |
---|
2093 | GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
---|
2094 | GST_VERSION_MINOR, |
---|
2095 | "xvimagesink", |
---|
2096 | "XFree86 video output plugin using Xv extension", |
---|
2097 | plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN) |
---|