1 /* GStreamer |
|
2 * Copyright (C) <2005> 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 /** |
|
21 * SECTION:element-xvimagesink |
|
22 * |
|
23 * <refsect2> |
|
24 * <para> |
|
25 * XvImageSink renders video frames to a drawable (XWindow) on a local display |
|
26 * using the XVideo extension. Rendering to a remote display is theorically |
|
27 * possible but i doubt that the XVideo extension is actually available when |
|
28 * connecting to a remote display. This element can receive a Window ID from the |
|
29 * application through the XOverlay interface and will then render video frames |
|
30 * in this drawable. If no Window ID was provided by the application, the |
|
31 * element will create its own internal window and render into it. |
|
32 * </para> |
|
33 * <title>Scaling</title> |
|
34 * <para> |
|
35 * The XVideo extension, when it's available, handles hardware accelerated |
|
36 * scaling of video frames. This means that the element will just accept |
|
37 * incoming video frames no matter their geometry and will then put them to the |
|
38 * drawable scaling them on the fly. Using the |
|
39 * <link linkend="GstXvImageSink--force-aspect-ratio">force-aspect-ratio</link> |
|
40 * property it is possible to enforce scaling with a constant aspect ratio, |
|
41 * which means drawing black borders around the video frame. |
|
42 * </para> |
|
43 * <title>Events</title> |
|
44 * <para> |
|
45 * XvImageSink creates a thread to handle events coming from the drawable. There |
|
46 * are several kind of events that can be grouped in 2 big categories: input |
|
47 * events and window state related events. Input events will be translated to |
|
48 * navigation events and pushed upstream for other elements to react on them. |
|
49 * This includes events such as pointer moves, key press/release, clicks etc... |
|
50 * Other events are used to handle the drawable appearance even when the data |
|
51 * is not flowing (GST_STATE_PAUSED). That means that even when the element is |
|
52 * paused, it will receive expose events from the drawable and draw the latest |
|
53 * frame with correct borders/aspect-ratio. |
|
54 * </para> |
|
55 * <title>Pixel aspect ratio</title> |
|
56 * <para> |
|
57 * When changing state to GST_STATE_READY, XvImageSink will open a connection to |
|
58 * the display specified in the |
|
59 * <link linkend="GstXvImageSink--display">display</link> property or the |
|
60 * default display if nothing specified. Once this connection is open it will |
|
61 * inspect the display configuration including the physical display geometry and |
|
62 * then calculate the pixel aspect ratio. When receiving video frames with a |
|
63 * different pixel aspect ratio, XvImageSink will use hardware scaling to |
|
64 * display the video frames correctly on display's pixel aspect ratio. |
|
65 * Sometimes the calculated pixel aspect ratio can be wrong, it is |
|
66 * then possible to enforce a specific pixel aspect ratio using the |
|
67 * <link linkend="GstXvImageSink--pixel-aspect-ratio">pixel-aspect-ratio</link> |
|
68 * property. |
|
69 * </para> |
|
70 * <title>Examples</title> |
|
71 * <para> |
|
72 * Here is a simple pipeline to test hardware scaling : |
|
73 * <programlisting> |
|
74 * gst-launch -v videotestsrc ! xvimagesink |
|
75 * </programlisting> |
|
76 * When the test video signal appears you can resize the window and see that |
|
77 * video frames are scaled through hardware (no extra CPU cost). You can try |
|
78 * again setting the force-aspect-ratio property to true and observe the borders |
|
79 * drawn around the scaled image respecting aspect ratio. |
|
80 * <programlisting> |
|
81 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true |
|
82 * </programlisting> |
|
83 * </para> |
|
84 * <para> |
|
85 * Here is a simple pipeline to test navigation events : |
|
86 * <programlisting> |
|
87 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink |
|
88 * </programlisting> |
|
89 * While moving the mouse pointer over the test signal you will see a black box |
|
90 * following the mouse pointer. If you press the mouse button somewhere on the |
|
91 * video and release it somewhere else a green box will appear where you pressed |
|
92 * the button and a red one where you released it. (The navigationtest element |
|
93 * is part of gst-plugins-good.) You can observe here that even if the images |
|
94 * are scaled through hardware the pointer coordinates are converted back to the |
|
95 * original video frame geometry so that the box can be drawn to the correct |
|
96 * position. This also handles borders correctly, limiting coordinates to the |
|
97 * image area |
|
98 * </para> |
|
99 * <para> |
|
100 * Here is a simple pipeline to test pixel aspect ratio : |
|
101 * <programlisting> |
|
102 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink |
|
103 * </programlisting> |
|
104 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by |
|
105 * videotestsrc, in most cases the pixel aspect ratio of the display will be |
|
106 * 1/1. This means that XvImageSink will have to do the scaling to convert |
|
107 * incoming frames to a size that will match the display pixel aspect ratio |
|
108 * (from 320x240 to 320x180 in this case). Note that you might have to escape |
|
109 * some characters for your shell like '\(fraction\)'. |
|
110 * </para> |
|
111 * <para> |
|
112 * Here is a test pipeline to test the colorbalance interface : |
|
113 * <programlisting> |
|
114 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100 |
|
115 * </programlisting> |
|
116 * </para> |
|
117 * </refsect2> |
|
118 */ |
|
119 |
|
120 #ifdef HAVE_CONFIG_H |
|
121 #include "config.h" |
|
122 #endif |
|
123 |
|
124 /* Our interfaces */ |
|
125 #include <gst/interfaces/navigation.h> |
|
126 #include <gst/interfaces/xoverlay.h> |
|
127 #include <gst/interfaces/colorbalance.h> |
|
128 #include <gst/interfaces/propertyprobe.h> |
|
129 /* Helper functions */ |
|
130 #include <gst/video/video.h> |
|
131 |
|
132 /* Object header */ |
|
133 #include "xvimagesink.h" |
|
134 |
|
135 /* Debugging category */ |
|
136 #include <gst/gstinfo.h> |
|
137 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink); |
|
138 #define GST_CAT_DEFAULT gst_debug_xvimagesink |
|
139 |
|
140 typedef struct |
|
141 { |
|
142 unsigned long flags; |
|
143 unsigned long functions; |
|
144 unsigned long decorations; |
|
145 long input_mode; |
|
146 unsigned long status; |
|
147 } |
|
148 MotifWmHints, MwmHints; |
|
149 |
|
150 #define MWM_HINTS_DECORATIONS (1L << 1) |
|
151 |
|
152 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink); |
|
153 |
|
154 static GstBufferClass *xvimage_buffer_parent_class = NULL; |
|
155 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage); |
|
156 |
|
157 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * |
|
158 xvimagesink, GstXWindow * xwindow); |
|
159 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink, |
|
160 GstCaps * caps); |
|
161 static void gst_xvimagesink_expose (GstXOverlay * overlay); |
|
162 |
|
163 /* ElementFactory information */ |
|
164 static const GstElementDetails gst_xvimagesink_details = |
|
165 GST_ELEMENT_DETAILS ("Video sink", |
|
166 "Sink/Video", |
|
167 "A Xv based videosink", |
|
168 "Julien Moutte <julien@moutte.net>"); |
|
169 |
|
170 /* Default template - initiated with class struct to allow gst-register to work |
|
171 without X running */ |
|
172 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory = |
|
173 GST_STATIC_PAD_TEMPLATE ("sink", |
|
174 GST_PAD_SINK, |
|
175 GST_PAD_ALWAYS, |
|
176 GST_STATIC_CAPS ("video/x-raw-rgb, " |
|
177 "framerate = (fraction) [ 0, MAX ], " |
|
178 "width = (int) [ 1, MAX ], " |
|
179 "height = (int) [ 1, MAX ]; " |
|
180 "video/x-raw-yuv, " |
|
181 "framerate = (fraction) [ 0, MAX ], " |
|
182 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") |
|
183 ); |
|
184 |
|
185 enum |
|
186 { |
|
187 ARG_0, |
|
188 ARG_CONTRAST, |
|
189 ARG_BRIGHTNESS, |
|
190 ARG_HUE, |
|
191 ARG_SATURATION, |
|
192 ARG_DISPLAY, |
|
193 ARG_SYNCHRONOUS, |
|
194 ARG_PIXEL_ASPECT_RATIO, |
|
195 ARG_FORCE_ASPECT_RATIO, |
|
196 ARG_HANDLE_EVENTS, |
|
197 ARG_DEVICE, |
|
198 ARG_DEVICE_NAME, |
|
199 ARG_HANDLE_EXPOSE, |
|
200 ARG_DOUBLE_BUFFER |
|
201 }; |
|
202 |
|
203 static GstVideoSinkClass *parent_class = NULL; |
|
204 |
|
205 /* ============================================================= */ |
|
206 /* */ |
|
207 /* Private Methods */ |
|
208 /* */ |
|
209 /* ============================================================= */ |
|
210 |
|
211 /* xvimage buffers */ |
|
212 |
|
213 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type()) |
|
214 |
|
215 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER)) |
|
216 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer)) |
|
217 |
|
218 /* This function destroys a GstXvImage handling XShm availability */ |
|
219 static void |
|
220 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage) |
|
221 { |
|
222 GstXvImageSink *xvimagesink; |
|
223 |
|
224 GST_DEBUG_OBJECT (xvimage, "Destroying buffer"); |
|
225 |
|
226 xvimagesink = xvimage->xvimagesink; |
|
227 if (G_UNLIKELY (xvimagesink == NULL)) |
|
228 goto no_sink; |
|
229 |
|
230 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
231 |
|
232 GST_OBJECT_LOCK (xvimagesink); |
|
233 |
|
234 /* If the destroyed image is the current one we destroy our reference too */ |
|
235 if (xvimagesink->cur_image == xvimage) |
|
236 xvimagesink->cur_image = NULL; |
|
237 |
|
238 /* We might have some buffers destroyed after changing state to NULL */ |
|
239 if (xvimagesink->xcontext == NULL) { |
|
240 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext"); |
|
241 #ifdef HAVE_XSHM |
|
242 /* Need to free the shared memory segment even if the x context |
|
243 * was already cleaned up */ |
|
244 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) { |
|
245 shmdt (xvimage->SHMInfo.shmaddr); |
|
246 } |
|
247 #endif |
|
248 goto beach; |
|
249 } |
|
250 |
|
251 g_mutex_lock (xvimagesink->x_lock); |
|
252 |
|
253 #ifdef HAVE_XSHM |
|
254 if (xvimagesink->xcontext->use_xshm) { |
|
255 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) { |
|
256 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx", |
|
257 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg); |
|
258 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo); |
|
259 XSync (xvimagesink->xcontext->disp, FALSE); |
|
260 |
|
261 shmdt (xvimage->SHMInfo.shmaddr); |
|
262 } |
|
263 if (xvimage->xvimage) |
|
264 XFree (xvimage->xvimage); |
|
265 } else |
|
266 #endif /* HAVE_XSHM */ |
|
267 { |
|
268 if (xvimage->xvimage) { |
|
269 if (xvimage->xvimage->data) { |
|
270 g_free (xvimage->xvimage->data); |
|
271 } |
|
272 XFree (xvimage->xvimage); |
|
273 } |
|
274 } |
|
275 |
|
276 XSync (xvimagesink->xcontext->disp, FALSE); |
|
277 |
|
278 g_mutex_unlock (xvimagesink->x_lock); |
|
279 |
|
280 beach: |
|
281 GST_OBJECT_UNLOCK (xvimagesink); |
|
282 xvimage->xvimagesink = NULL; |
|
283 gst_object_unref (xvimagesink); |
|
284 |
|
285 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)-> |
|
286 finalize (GST_MINI_OBJECT (xvimage)); |
|
287 |
|
288 return; |
|
289 |
|
290 no_sink: |
|
291 { |
|
292 GST_WARNING ("no sink found"); |
|
293 return; |
|
294 } |
|
295 } |
|
296 |
|
297 static void |
|
298 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage) |
|
299 { |
|
300 GstXvImageSink *xvimagesink; |
|
301 gboolean running; |
|
302 |
|
303 xvimagesink = xvimage->xvimagesink; |
|
304 if (G_UNLIKELY (xvimagesink == NULL)) |
|
305 goto no_sink; |
|
306 |
|
307 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
308 |
|
309 GST_OBJECT_LOCK (xvimagesink); |
|
310 running = xvimagesink->running; |
|
311 GST_OBJECT_UNLOCK (xvimagesink); |
|
312 |
|
313 /* If our geometry changed we can't reuse that image. */ |
|
314 if (running == FALSE) { |
|
315 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down"); |
|
316 gst_xvimage_buffer_destroy (xvimage); |
|
317 } else if ((xvimage->width != xvimagesink->video_width) || |
|
318 (xvimage->height != xvimagesink->video_height)) { |
|
319 GST_LOG_OBJECT (xvimage, |
|
320 "destroy image as its size changed %dx%d vs current %dx%d", |
|
321 xvimage->width, xvimage->height, |
|
322 xvimagesink->video_width, xvimagesink->video_height); |
|
323 gst_xvimage_buffer_destroy (xvimage); |
|
324 } else { |
|
325 /* In that case we can reuse the image and add it to our image pool. */ |
|
326 GST_LOG_OBJECT (xvimage, "recycling image in pool"); |
|
327 /* need to increment the refcount again to recycle */ |
|
328 gst_buffer_ref (GST_BUFFER (xvimage)); |
|
329 g_mutex_lock (xvimagesink->pool_lock); |
|
330 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool, |
|
331 xvimage); |
|
332 g_mutex_unlock (xvimagesink->pool_lock); |
|
333 } |
|
334 return; |
|
335 |
|
336 no_sink: |
|
337 { |
|
338 GST_WARNING ("no sink found"); |
|
339 return; |
|
340 } |
|
341 } |
|
342 |
|
343 static void |
|
344 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage) |
|
345 { |
|
346 /* make sure it is not recycled */ |
|
347 xvimage->width = -1; |
|
348 xvimage->height = -1; |
|
349 gst_buffer_unref (GST_BUFFER (xvimage)); |
|
350 } |
|
351 |
|
352 static void |
|
353 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class) |
|
354 { |
|
355 #ifdef HAVE_XSHM |
|
356 xvimage->SHMInfo.shmaddr = ((void *) -1); |
|
357 xvimage->SHMInfo.shmid = -1; |
|
358 #endif |
|
359 } |
|
360 |
|
361 static void |
|
362 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data) |
|
363 { |
|
364 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); |
|
365 |
|
366 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class); |
|
367 |
|
368 mini_object_class->finalize = (GstMiniObjectFinalizeFunction) |
|
369 gst_xvimage_buffer_finalize; |
|
370 } |
|
371 |
|
372 static GType |
|
373 gst_xvimage_buffer_get_type (void) |
|
374 { |
|
375 static GType _gst_xvimage_buffer_type; |
|
376 |
|
377 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) { |
|
378 static const GTypeInfo xvimage_buffer_info = { |
|
379 sizeof (GstBufferClass), |
|
380 NULL, |
|
381 NULL, |
|
382 gst_xvimage_buffer_class_init, |
|
383 NULL, |
|
384 NULL, |
|
385 sizeof (GstXvImageBuffer), |
|
386 0, |
|
387 (GInstanceInitFunc) gst_xvimage_buffer_init, |
|
388 NULL |
|
389 }; |
|
390 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER, |
|
391 "GstXvImageBuffer", &xvimage_buffer_info, 0); |
|
392 } |
|
393 return _gst_xvimage_buffer_type; |
|
394 } |
|
395 |
|
396 /* X11 stuff */ |
|
397 |
|
398 static gboolean error_caught = FALSE; |
|
399 |
|
400 static int |
|
401 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent) |
|
402 { |
|
403 char error_msg[1024]; |
|
404 |
|
405 XGetErrorText (display, xevent->error_code, error_msg, 1024); |
|
406 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg); |
|
407 error_caught = TRUE; |
|
408 return 0; |
|
409 } |
|
410 |
|
411 #ifdef HAVE_XSHM |
|
412 /* This function checks that it is actually really possible to create an image |
|
413 using XShm */ |
|
414 static gboolean |
|
415 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext) |
|
416 { |
|
417 XvImage *xvimage; |
|
418 XShmSegmentInfo SHMInfo; |
|
419 gint size; |
|
420 int (*handler) (Display *, XErrorEvent *); |
|
421 gboolean result = FALSE; |
|
422 gboolean did_attach = FALSE; |
|
423 |
|
424 g_return_val_if_fail (xcontext != NULL, FALSE); |
|
425 |
|
426 /* Sync to ensure any older errors are already processed */ |
|
427 XSync (xcontext->disp, FALSE); |
|
428 |
|
429 /* Set defaults so we don't free these later unnecessarily */ |
|
430 SHMInfo.shmaddr = ((void *) -1); |
|
431 SHMInfo.shmid = -1; |
|
432 |
|
433 /* Setting an error handler to catch failure */ |
|
434 error_caught = FALSE; |
|
435 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror); |
|
436 |
|
437 /* Trying to create a 1x1 picture */ |
|
438 GST_DEBUG ("XvShmCreateImage of 1x1"); |
|
439 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id, |
|
440 xcontext->im_format, NULL, 1, 1, &SHMInfo); |
|
441 |
|
442 /* Might cause an error, sync to ensure it is noticed */ |
|
443 XSync (xcontext->disp, FALSE); |
|
444 if (!xvimage || error_caught) { |
|
445 GST_WARNING ("could not XvShmCreateImage a 1x1 image"); |
|
446 goto beach; |
|
447 } |
|
448 size = xvimage->data_size; |
|
449 |
|
450 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777); |
|
451 if (SHMInfo.shmid == -1) { |
|
452 GST_WARNING ("could not get shared memory of %d bytes", size); |
|
453 goto beach; |
|
454 } |
|
455 |
|
456 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0); |
|
457 if (SHMInfo.shmaddr == ((void *) -1)) { |
|
458 GST_WARNING ("Failed to shmat: %s", g_strerror (errno)); |
|
459 /* Clean up the shared memory segment */ |
|
460 shmctl (SHMInfo.shmid, IPC_RMID, NULL); |
|
461 goto beach; |
|
462 } |
|
463 |
|
464 /* Delete the shared memory segment as soon as we manage to attach. |
|
465 * This way, it will be deleted as soon as we detach later, and not |
|
466 * leaked if we crash. */ |
|
467 shmctl (SHMInfo.shmid, IPC_RMID, NULL); |
|
468 |
|
469 xvimage->data = SHMInfo.shmaddr; |
|
470 SHMInfo.readOnly = FALSE; |
|
471 |
|
472 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) { |
|
473 GST_WARNING ("Failed to XShmAttach"); |
|
474 goto beach; |
|
475 } |
|
476 |
|
477 /* Sync to ensure we see any errors we caused */ |
|
478 XSync (xcontext->disp, FALSE); |
|
479 |
|
480 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid, |
|
481 SHMInfo.shmseg); |
|
482 |
|
483 if (!error_caught) { |
|
484 did_attach = TRUE; |
|
485 /* store whether we succeeded in result */ |
|
486 result = TRUE; |
|
487 } |
|
488 |
|
489 beach: |
|
490 /* Sync to ensure we swallow any errors we caused and reset error_caught */ |
|
491 XSync (xcontext->disp, FALSE); |
|
492 |
|
493 error_caught = FALSE; |
|
494 XSetErrorHandler (handler); |
|
495 |
|
496 if (did_attach) { |
|
497 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx", |
|
498 SHMInfo.shmid, SHMInfo.shmseg); |
|
499 XShmDetach (xcontext->disp, &SHMInfo); |
|
500 XSync (xcontext->disp, FALSE); |
|
501 } |
|
502 if (SHMInfo.shmaddr != ((void *) -1)) |
|
503 shmdt (SHMInfo.shmaddr); |
|
504 if (xvimage) |
|
505 XFree (xvimage); |
|
506 return result; |
|
507 } |
|
508 #endif /* HAVE_XSHM */ |
|
509 |
|
510 /* This function handles GstXvImage creation depending on XShm availability */ |
|
511 static GstXvImageBuffer * |
|
512 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps) |
|
513 { |
|
514 GstXvImageBuffer *xvimage = NULL; |
|
515 GstStructure *structure = NULL; |
|
516 gboolean succeeded = FALSE; |
|
517 int (*handler) (Display *, XErrorEvent *); |
|
518 |
|
519 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
|
520 |
|
521 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER); |
|
522 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer"); |
|
523 |
|
524 structure = gst_caps_get_structure (caps, 0); |
|
525 |
|
526 if (!gst_structure_get_int (structure, "width", &xvimage->width) || |
|
527 !gst_structure_get_int (structure, "height", &xvimage->height)) { |
|
528 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps); |
|
529 } |
|
530 |
|
531 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width, |
|
532 xvimage->height); |
|
533 |
|
534 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps); |
|
535 if (xvimage->im_format == -1) { |
|
536 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %" |
|
537 GST_PTR_FORMAT, caps); |
|
538 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
539 ("Failed to create output image buffer of %dx%d pixels", |
|
540 xvimage->width, xvimage->height), ("Invalid input caps")); |
|
541 goto beach_unlocked; |
|
542 } |
|
543 xvimage->xvimagesink = gst_object_ref (xvimagesink); |
|
544 |
|
545 g_mutex_lock (xvimagesink->x_lock); |
|
546 |
|
547 /* Setting an error handler to catch failure */ |
|
548 error_caught = FALSE; |
|
549 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror); |
|
550 |
|
551 #ifdef HAVE_XSHM |
|
552 if (xvimagesink->xcontext->use_xshm) { |
|
553 int expected_size; |
|
554 |
|
555 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp, |
|
556 xvimagesink->xcontext->xv_port_id, |
|
557 xvimage->im_format, NULL, |
|
558 xvimage->width, xvimage->height, &xvimage->SHMInfo); |
|
559 if (!xvimage->xvimage || error_caught) { |
|
560 g_mutex_unlock (xvimagesink->x_lock); |
|
561 /* Reset error handler */ |
|
562 error_caught = FALSE; |
|
563 XSetErrorHandler (handler); |
|
564 /* Push an error */ |
|
565 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
566 ("Failed to create output image buffer of %dx%d pixels", |
|
567 xvimage->width, xvimage->height), |
|
568 ("could not XvShmCreateImage a %dx%d image", |
|
569 xvimage->width, xvimage->height)); |
|
570 goto beach_unlocked; |
|
571 } |
|
572 |
|
573 /* we have to use the returned data_size for our shm size */ |
|
574 xvimage->size = xvimage->xvimage->data_size; |
|
575 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT, |
|
576 xvimage->size); |
|
577 |
|
578 /* calculate the expected size. This is only for sanity checking the |
|
579 * number we get from X. */ |
|
580 switch (xvimage->im_format) { |
|
581 case GST_MAKE_FOURCC ('I', '4', '2', '0'): |
|
582 expected_size = |
|
583 GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_4 (xvimage->width); |
|
584 expected_size += |
|
585 GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_8 (xvimage->width) / |
|
586 2; |
|
587 break; |
|
588 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): |
|
589 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): |
|
590 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): |
|
591 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2); |
|
592 break; |
|
593 default: |
|
594 expected_size = 0; |
|
595 break; |
|
596 } |
|
597 if (expected_size != 0 && xvimage->size != expected_size) { |
|
598 GST_WARNING_OBJECT (xvimagesink, |
|
599 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)", |
|
600 xvimage->size, expected_size); |
|
601 } |
|
602 |
|
603 /* Be verbose about our XvImage stride */ |
|
604 { |
|
605 guint plane; |
|
606 |
|
607 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) { |
|
608 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, " |
|
609 "offset of %d", plane, xvimage->xvimage->pitches[plane], |
|
610 xvimage->xvimage->offsets[plane]); |
|
611 } |
|
612 } |
|
613 |
|
614 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size, |
|
615 IPC_CREAT | 0777); |
|
616 if (xvimage->SHMInfo.shmid == -1) { |
|
617 g_mutex_unlock (xvimagesink->x_lock); |
|
618 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
619 ("Failed to create output image buffer of %dx%d pixels", |
|
620 xvimage->width, xvimage->height), |
|
621 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes", |
|
622 xvimage->size)); |
|
623 goto beach_unlocked; |
|
624 } |
|
625 |
|
626 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0); |
|
627 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) { |
|
628 g_mutex_unlock (xvimagesink->x_lock); |
|
629 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
630 ("Failed to create output image buffer of %dx%d pixels", |
|
631 xvimage->width, xvimage->height), |
|
632 ("Failed to shmat: %s", g_strerror (errno))); |
|
633 /* Clean up the shared memory segment */ |
|
634 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL); |
|
635 goto beach_unlocked; |
|
636 } |
|
637 |
|
638 /* Delete the shared memory segment as soon as we manage to attach. |
|
639 * This way, it will be deleted as soon as we detach later, and not |
|
640 * leaked if we crash. */ |
|
641 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL); |
|
642 |
|
643 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr; |
|
644 xvimage->SHMInfo.readOnly = FALSE; |
|
645 |
|
646 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) { |
|
647 g_mutex_unlock (xvimagesink->x_lock); |
|
648 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
649 ("Failed to create output image buffer of %dx%d pixels", |
|
650 xvimage->width, xvimage->height), ("Failed to XShmAttach")); |
|
651 goto beach_unlocked; |
|
652 } |
|
653 |
|
654 XSync (xvimagesink->xcontext->disp, FALSE); |
|
655 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx", |
|
656 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg); |
|
657 } else |
|
658 #endif /* HAVE_XSHM */ |
|
659 { |
|
660 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp, |
|
661 xvimagesink->xcontext->xv_port_id, |
|
662 xvimage->im_format, NULL, xvimage->width, xvimage->height); |
|
663 if (!xvimage->xvimage || error_caught) { |
|
664 g_mutex_unlock (xvimagesink->x_lock); |
|
665 /* Reset error handler */ |
|
666 error_caught = FALSE; |
|
667 XSetErrorHandler (handler); |
|
668 /* Push an error */ |
|
669 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
670 ("Failed to create outputimage buffer of %dx%d pixels", |
|
671 xvimage->width, xvimage->height), |
|
672 ("could not XvCreateImage a %dx%d image", |
|
673 xvimage->width, xvimage->height)); |
|
674 goto beach_unlocked; |
|
675 } |
|
676 |
|
677 /* we have to use the returned data_size for our image size */ |
|
678 xvimage->size = xvimage->xvimage->data_size; |
|
679 xvimage->xvimage->data = g_malloc (xvimage->size); |
|
680 |
|
681 XSync (xvimagesink->xcontext->disp, FALSE); |
|
682 } |
|
683 |
|
684 /* Reset error handler */ |
|
685 error_caught = FALSE; |
|
686 XSetErrorHandler (handler); |
|
687 |
|
688 succeeded = TRUE; |
|
689 |
|
690 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data; |
|
691 GST_BUFFER_SIZE (xvimage) = xvimage->size; |
|
692 |
|
693 g_mutex_unlock (xvimagesink->x_lock); |
|
694 |
|
695 beach_unlocked: |
|
696 if (!succeeded) { |
|
697 gst_xvimage_buffer_free (xvimage); |
|
698 xvimage = NULL; |
|
699 } |
|
700 |
|
701 return xvimage; |
|
702 } |
|
703 |
|
704 /* We are called with the x_lock taken */ |
|
705 static void |
|
706 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink, |
|
707 GstXWindow * xwindow, GstVideoRectangle rect) |
|
708 { |
|
709 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
710 g_return_if_fail (xwindow != NULL); |
|
711 |
|
712 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc, |
|
713 xvimagesink->xcontext->black); |
|
714 |
|
715 /* Left border */ |
|
716 if (rect.x > 0) { |
|
717 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc, |
|
718 0, 0, rect.x, xwindow->height); |
|
719 } |
|
720 |
|
721 /* Right border */ |
|
722 if ((rect.x + rect.w) < xwindow->width) { |
|
723 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc, |
|
724 rect.x + rect.w, 0, xwindow->width, xwindow->height); |
|
725 } |
|
726 |
|
727 /* Top border */ |
|
728 if (rect.y > 0) { |
|
729 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc, |
|
730 0, 0, xwindow->width, rect.y); |
|
731 } |
|
732 |
|
733 /* Bottom border */ |
|
734 if ((rect.y + rect.h) < xwindow->height) { |
|
735 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc, |
|
736 0, rect.y + rect.h, xwindow->width, xwindow->height); |
|
737 } |
|
738 } |
|
739 |
|
740 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE |
|
741 * if no window was available */ |
|
742 static gboolean |
|
743 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, |
|
744 GstXvImageBuffer * xvimage) |
|
745 { |
|
746 GstVideoRectangle src, dst, result; |
|
747 gboolean draw_border = FALSE; |
|
748 |
|
749 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE); |
|
750 |
|
751 /* We take the flow_lock. If expose is in there we don't want to run |
|
752 concurrently from the data flow thread */ |
|
753 g_mutex_lock (xvimagesink->flow_lock); |
|
754 |
|
755 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) { |
|
756 g_mutex_unlock (xvimagesink->flow_lock); |
|
757 return FALSE; |
|
758 } |
|
759 |
|
760 /* Draw borders when displaying the first frame. After this |
|
761 draw borders only on expose event or after a size change. */ |
|
762 if (!xvimagesink->cur_image || xvimagesink->draw_border) { |
|
763 draw_border = TRUE; |
|
764 xvimagesink->draw_border = FALSE; |
|
765 } |
|
766 |
|
767 /* Store a reference to the last image we put, lose the previous one */ |
|
768 if (xvimage && xvimagesink->cur_image != xvimage) { |
|
769 if (xvimagesink->cur_image) { |
|
770 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image); |
|
771 gst_buffer_unref (xvimagesink->cur_image); |
|
772 } |
|
773 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage); |
|
774 xvimagesink->cur_image = |
|
775 GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage))); |
|
776 } |
|
777 |
|
778 /* Expose sends a NULL image, we take the latest frame */ |
|
779 if (!xvimage) { |
|
780 draw_border = TRUE; |
|
781 if (xvimagesink->cur_image) { |
|
782 xvimage = xvimagesink->cur_image; |
|
783 } else { |
|
784 g_mutex_unlock (xvimagesink->flow_lock); |
|
785 return TRUE; |
|
786 } |
|
787 } |
|
788 |
|
789 gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow); |
|
790 |
|
791 /* We use the calculated geometry from _setcaps as a source to respect |
|
792 source and screen pixel aspect ratios. */ |
|
793 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink); |
|
794 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink); |
|
795 dst.w = xvimagesink->xwindow->width; |
|
796 dst.h = xvimagesink->xwindow->height; |
|
797 |
|
798 if (xvimagesink->keep_aspect) { |
|
799 gst_video_sink_center_rect (src, dst, &result, TRUE); |
|
800 } else { |
|
801 result.x = result.y = 0; |
|
802 result.w = dst.w; |
|
803 result.h = dst.h; |
|
804 } |
|
805 |
|
806 g_mutex_lock (xvimagesink->x_lock); |
|
807 |
|
808 if (draw_border) { |
|
809 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow, |
|
810 result); |
|
811 } |
|
812 |
|
813 /* We scale to the window's geometry */ |
|
814 #ifdef HAVE_XSHM |
|
815 if (xvimagesink->xcontext->use_xshm) { |
|
816 GST_LOG_OBJECT (xvimagesink, |
|
817 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %" |
|
818 GST_PTR_FORMAT, |
|
819 xvimage->width, xvimage->height, |
|
820 xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage); |
|
821 |
|
822 XvShmPutImage (xvimagesink->xcontext->disp, |
|
823 xvimagesink->xcontext->xv_port_id, |
|
824 xvimagesink->xwindow->win, |
|
825 xvimagesink->xwindow->gc, xvimage->xvimage, |
|
826 0, 0, xvimage->width, xvimage->height, |
|
827 result.x, result.y, result.w, result.h, FALSE); |
|
828 } else |
|
829 #endif /* HAVE_XSHM */ |
|
830 { |
|
831 XvPutImage (xvimagesink->xcontext->disp, |
|
832 xvimagesink->xcontext->xv_port_id, |
|
833 xvimagesink->xwindow->win, |
|
834 xvimagesink->xwindow->gc, xvimage->xvimage, |
|
835 0, 0, xvimage->width, xvimage->height, |
|
836 result.x, result.y, result.w, result.h); |
|
837 } |
|
838 |
|
839 XSync (xvimagesink->xcontext->disp, FALSE); |
|
840 |
|
841 g_mutex_unlock (xvimagesink->x_lock); |
|
842 |
|
843 g_mutex_unlock (xvimagesink->flow_lock); |
|
844 |
|
845 return TRUE; |
|
846 } |
|
847 |
|
848 static gboolean |
|
849 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink, |
|
850 GstXWindow * window) |
|
851 { |
|
852 Atom hints_atom = None; |
|
853 MotifWmHints *hints; |
|
854 |
|
855 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE); |
|
856 g_return_val_if_fail (window != NULL, FALSE); |
|
857 |
|
858 g_mutex_lock (xvimagesink->x_lock); |
|
859 |
|
860 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", |
|
861 True); |
|
862 if (hints_atom == None) { |
|
863 g_mutex_unlock (xvimagesink->x_lock); |
|
864 return FALSE; |
|
865 } |
|
866 |
|
867 hints = g_malloc0 (sizeof (MotifWmHints)); |
|
868 |
|
869 hints->flags |= MWM_HINTS_DECORATIONS; |
|
870 hints->decorations = 1 << 0; |
|
871 |
|
872 XChangeProperty (xvimagesink->xcontext->disp, window->win, |
|
873 hints_atom, hints_atom, 32, PropModeReplace, |
|
874 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long)); |
|
875 |
|
876 XSync (xvimagesink->xcontext->disp, FALSE); |
|
877 |
|
878 g_mutex_unlock (xvimagesink->x_lock); |
|
879 |
|
880 g_free (hints); |
|
881 |
|
882 return TRUE; |
|
883 } |
|
884 |
|
885 /* This function handles a GstXWindow creation |
|
886 * The width and height are the actual pixel size on the display */ |
|
887 static GstXWindow * |
|
888 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink, |
|
889 gint width, gint height) |
|
890 { |
|
891 GstXWindow *xwindow = NULL; |
|
892 XGCValues values; |
|
893 |
|
894 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
|
895 |
|
896 xwindow = g_new0 (GstXWindow, 1); |
|
897 |
|
898 xwindow->width = width; |
|
899 xwindow->height = height; |
|
900 xwindow->internal = TRUE; |
|
901 |
|
902 g_mutex_lock (xvimagesink->x_lock); |
|
903 |
|
904 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp, |
|
905 xvimagesink->xcontext->root, |
|
906 0, 0, xwindow->width, xwindow->height, |
|
907 0, 0, xvimagesink->xcontext->black); |
|
908 |
|
909 /* We have to do that to prevent X from redrawing the background on |
|
910 * ConfigureNotify. This takes away flickering of video when resizing. */ |
|
911 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None); |
|
912 |
|
913 if (xvimagesink->handle_events) { |
|
914 Atom wm_delete; |
|
915 |
|
916 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask | |
|
917 StructureNotifyMask | PointerMotionMask | KeyPressMask | |
|
918 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask); |
|
919 |
|
920 /* Tell the window manager we'd like delete client messages instead of |
|
921 * being killed */ |
|
922 wm_delete = XInternAtom (xvimagesink->xcontext->disp, |
|
923 "WM_DELETE_WINDOW", True); |
|
924 if (wm_delete != None) { |
|
925 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win, |
|
926 &wm_delete, 1); |
|
927 } |
|
928 } |
|
929 |
|
930 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp, |
|
931 xwindow->win, 0, &values); |
|
932 |
|
933 XMapRaised (xvimagesink->xcontext->disp, xwindow->win); |
|
934 |
|
935 XSync (xvimagesink->xcontext->disp, FALSE); |
|
936 |
|
937 g_mutex_unlock (xvimagesink->x_lock); |
|
938 |
|
939 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow); |
|
940 |
|
941 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win); |
|
942 |
|
943 return xwindow; |
|
944 } |
|
945 |
|
946 /* This function destroys a GstXWindow */ |
|
947 static void |
|
948 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink, |
|
949 GstXWindow * xwindow) |
|
950 { |
|
951 g_return_if_fail (xwindow != NULL); |
|
952 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
953 |
|
954 g_mutex_lock (xvimagesink->x_lock); |
|
955 |
|
956 /* If we did not create that window we just free the GC and let it live */ |
|
957 if (xwindow->internal) |
|
958 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win); |
|
959 else |
|
960 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0); |
|
961 |
|
962 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc); |
|
963 |
|
964 XSync (xvimagesink->xcontext->disp, FALSE); |
|
965 |
|
966 g_mutex_unlock (xvimagesink->x_lock); |
|
967 |
|
968 g_free (xwindow); |
|
969 } |
|
970 |
|
971 static void |
|
972 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink, |
|
973 GstXWindow * xwindow) |
|
974 { |
|
975 XWindowAttributes attr; |
|
976 |
|
977 g_return_if_fail (xwindow != NULL); |
|
978 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
979 |
|
980 /* Update the window geometry */ |
|
981 g_mutex_lock (xvimagesink->x_lock); |
|
982 |
|
983 XGetWindowAttributes (xvimagesink->xcontext->disp, |
|
984 xvimagesink->xwindow->win, &attr); |
|
985 |
|
986 xvimagesink->xwindow->width = attr.width; |
|
987 xvimagesink->xwindow->height = attr.height; |
|
988 |
|
989 g_mutex_unlock (xvimagesink->x_lock); |
|
990 } |
|
991 |
|
992 static void |
|
993 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink, |
|
994 GstXWindow * xwindow) |
|
995 { |
|
996 g_return_if_fail (xwindow != NULL); |
|
997 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
998 |
|
999 g_mutex_lock (xvimagesink->x_lock); |
|
1000 |
|
1001 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, |
|
1002 xwindow->win); |
|
1003 |
|
1004 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc, |
|
1005 xvimagesink->xcontext->black); |
|
1006 |
|
1007 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc, |
|
1008 0, 0, xwindow->width, xwindow->height); |
|
1009 |
|
1010 XSync (xvimagesink->xcontext->disp, FALSE); |
|
1011 |
|
1012 g_mutex_unlock (xvimagesink->x_lock); |
|
1013 } |
|
1014 |
|
1015 /* This function commits our internal colorbalance settings to our grabbed Xv |
|
1016 port. If the xcontext is not initialized yet it simply returns */ |
|
1017 static void |
|
1018 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink) |
|
1019 { |
|
1020 GList *channels = NULL; |
|
1021 |
|
1022 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
1023 |
|
1024 /* If we haven't initialized the X context we can't update anything */ |
|
1025 if (xvimagesink->xcontext == NULL) |
|
1026 return; |
|
1027 |
|
1028 /* For each channel of the colorbalance we calculate the correct value |
|
1029 doing range conversion and then set the Xv port attribute to match our |
|
1030 values. */ |
|
1031 channels = xvimagesink->xcontext->channels_list; |
|
1032 |
|
1033 while (channels) { |
|
1034 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) { |
|
1035 GstColorBalanceChannel *channel = NULL; |
|
1036 Atom prop_atom; |
|
1037 gint value = 0; |
|
1038 gdouble convert_coef; |
|
1039 |
|
1040 channel = GST_COLOR_BALANCE_CHANNEL (channels->data); |
|
1041 g_object_ref (channel); |
|
1042 |
|
1043 /* Our range conversion coef */ |
|
1044 convert_coef = (channel->max_value - channel->min_value) / 2000.0; |
|
1045 |
|
1046 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) { |
|
1047 value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value; |
|
1048 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) { |
|
1049 value = (xvimagesink->saturation + 1000) * convert_coef + |
|
1050 channel->min_value; |
|
1051 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) { |
|
1052 value = (xvimagesink->contrast + 1000) * convert_coef + |
|
1053 channel->min_value; |
|
1054 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) { |
|
1055 value = (xvimagesink->brightness + 1000) * convert_coef + |
|
1056 channel->min_value; |
|
1057 } else { |
|
1058 g_warning ("got an unknown channel %s", channel->label); |
|
1059 g_object_unref (channel); |
|
1060 return; |
|
1061 } |
|
1062 |
|
1063 /* Committing to Xv port */ |
|
1064 g_mutex_lock (xvimagesink->x_lock); |
|
1065 prop_atom = |
|
1066 XInternAtom (xvimagesink->xcontext->disp, channel->label, True); |
|
1067 if (prop_atom != None) { |
|
1068 XvSetPortAttribute (xvimagesink->xcontext->disp, |
|
1069 xvimagesink->xcontext->xv_port_id, prop_atom, value); |
|
1070 } |
|
1071 g_mutex_unlock (xvimagesink->x_lock); |
|
1072 |
|
1073 g_object_unref (channel); |
|
1074 } |
|
1075 channels = g_list_next (channels); |
|
1076 } |
|
1077 } |
|
1078 |
|
1079 /* This function handles XEvents that might be in the queue. It generates |
|
1080 GstEvent that will be sent upstream in the pipeline to handle interactivity |
|
1081 and navigation. It will also listen for configure events on the window to |
|
1082 trigger caps renegotiation so on the fly software scaling can work. */ |
|
1083 static void |
|
1084 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink) |
|
1085 { |
|
1086 XEvent e; |
|
1087 guint pointer_x = 0, pointer_y = 0; |
|
1088 gboolean pointer_moved = FALSE; |
|
1089 gboolean exposed = FALSE, configured = FALSE; |
|
1090 |
|
1091 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
1092 |
|
1093 /* We get all pointer motion events, only the last position is |
|
1094 interesting. */ |
|
1095 g_mutex_lock (xvimagesink->flow_lock); |
|
1096 g_mutex_lock (xvimagesink->x_lock); |
|
1097 while (XCheckWindowEvent (xvimagesink->xcontext->disp, |
|
1098 xvimagesink->xwindow->win, PointerMotionMask, &e)) { |
|
1099 g_mutex_unlock (xvimagesink->x_lock); |
|
1100 g_mutex_unlock (xvimagesink->flow_lock); |
|
1101 |
|
1102 switch (e.type) { |
|
1103 case MotionNotify: |
|
1104 pointer_x = e.xmotion.x; |
|
1105 pointer_y = e.xmotion.y; |
|
1106 pointer_moved = TRUE; |
|
1107 break; |
|
1108 default: |
|
1109 break; |
|
1110 } |
|
1111 g_mutex_lock (xvimagesink->flow_lock); |
|
1112 g_mutex_lock (xvimagesink->x_lock); |
|
1113 } |
|
1114 if (pointer_moved) { |
|
1115 g_mutex_unlock (xvimagesink->x_lock); |
|
1116 g_mutex_unlock (xvimagesink->flow_lock); |
|
1117 |
|
1118 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d", |
|
1119 pointer_x, pointer_y); |
|
1120 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), |
|
1121 "mouse-move", 0, e.xbutton.x, e.xbutton.y); |
|
1122 |
|
1123 g_mutex_lock (xvimagesink->flow_lock); |
|
1124 g_mutex_lock (xvimagesink->x_lock); |
|
1125 } |
|
1126 |
|
1127 /* We get all events on our window to throw them upstream */ |
|
1128 while (XCheckWindowEvent (xvimagesink->xcontext->disp, |
|
1129 xvimagesink->xwindow->win, |
|
1130 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, |
|
1131 &e)) { |
|
1132 KeySym keysym; |
|
1133 |
|
1134 /* We lock only for the X function call */ |
|
1135 g_mutex_unlock (xvimagesink->x_lock); |
|
1136 g_mutex_unlock (xvimagesink->flow_lock); |
|
1137 |
|
1138 switch (e.type) { |
|
1139 case ButtonPress: |
|
1140 /* Mouse button pressed over our window. We send upstream |
|
1141 events for interactivity/navigation */ |
|
1142 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d", |
|
1143 e.xbutton.button, e.xbutton.x, e.xbutton.y); |
|
1144 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), |
|
1145 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y); |
|
1146 break; |
|
1147 case ButtonRelease: |
|
1148 /* Mouse button released over our window. We send upstream |
|
1149 events for interactivity/navigation */ |
|
1150 GST_DEBUG ("xvimagesink button %d released over window at %d,%d", |
|
1151 e.xbutton.button, e.xbutton.x, e.xbutton.y); |
|
1152 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink), |
|
1153 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y); |
|
1154 break; |
|
1155 case KeyPress: |
|
1156 case KeyRelease: |
|
1157 /* Key pressed/released over our window. We send upstream |
|
1158 events for interactivity/navigation */ |
|
1159 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d", |
|
1160 e.xkey.keycode, e.xkey.x, e.xkey.y); |
|
1161 g_mutex_lock (xvimagesink->x_lock); |
|
1162 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp, |
|
1163 e.xkey.keycode, 0); |
|
1164 g_mutex_unlock (xvimagesink->x_lock); |
|
1165 if (keysym != NoSymbol) { |
|
1166 char *key_str = NULL; |
|
1167 |
|
1168 g_mutex_lock (xvimagesink->x_lock); |
|
1169 key_str = XKeysymToString (keysym); |
|
1170 g_mutex_unlock (xvimagesink->x_lock); |
|
1171 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink), |
|
1172 e.type == KeyPress ? "key-press" : "key-release", key_str); |
|
1173 } else { |
|
1174 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink), |
|
1175 e.type == KeyPress ? "key-press" : "key-release", "unknown"); |
|
1176 } |
|
1177 break; |
|
1178 default: |
|
1179 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type); |
|
1180 } |
|
1181 g_mutex_lock (xvimagesink->flow_lock); |
|
1182 g_mutex_lock (xvimagesink->x_lock); |
|
1183 } |
|
1184 |
|
1185 /* Handle Expose */ |
|
1186 while (XCheckWindowEvent (xvimagesink->xcontext->disp, |
|
1187 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) { |
|
1188 switch (e.type) { |
|
1189 case Expose: |
|
1190 exposed = TRUE; |
|
1191 break; |
|
1192 case ConfigureNotify: |
|
1193 configured = TRUE; |
|
1194 break; |
|
1195 default: |
|
1196 break; |
|
1197 } |
|
1198 } |
|
1199 |
|
1200 if (xvimagesink->handle_expose && (exposed || configured)) { |
|
1201 g_mutex_unlock (xvimagesink->x_lock); |
|
1202 g_mutex_unlock (xvimagesink->flow_lock); |
|
1203 |
|
1204 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink)); |
|
1205 |
|
1206 g_mutex_lock (xvimagesink->flow_lock); |
|
1207 g_mutex_lock (xvimagesink->x_lock); |
|
1208 } |
|
1209 |
|
1210 /* Handle Display events */ |
|
1211 while (XPending (xvimagesink->xcontext->disp)) { |
|
1212 XNextEvent (xvimagesink->xcontext->disp, &e); |
|
1213 |
|
1214 switch (e.type) { |
|
1215 case ClientMessage:{ |
|
1216 Atom wm_delete; |
|
1217 |
|
1218 wm_delete = XInternAtom (xvimagesink->xcontext->disp, |
|
1219 "WM_DELETE_WINDOW", True); |
|
1220 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) { |
|
1221 /* Handle window deletion by posting an error on the bus */ |
|
1222 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND, |
|
1223 ("Output window was closed"), (NULL)); |
|
1224 |
|
1225 g_mutex_unlock (xvimagesink->x_lock); |
|
1226 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow); |
|
1227 xvimagesink->xwindow = NULL; |
|
1228 g_mutex_lock (xvimagesink->x_lock); |
|
1229 } |
|
1230 break; |
|
1231 } |
|
1232 default: |
|
1233 break; |
|
1234 } |
|
1235 } |
|
1236 |
|
1237 g_mutex_unlock (xvimagesink->x_lock); |
|
1238 g_mutex_unlock (xvimagesink->flow_lock); |
|
1239 } |
|
1240 |
|
1241 static void |
|
1242 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext, |
|
1243 XvAdaptorInfo * adaptors, int adaptor_no) |
|
1244 { |
|
1245 gint j; |
|
1246 |
|
1247 /* Do we support XvImageMask ? */ |
|
1248 if (!(adaptors[adaptor_no].type & XvImageMask)) |
|
1249 return; |
|
1250 |
|
1251 /* We found such an adaptor, looking for an available port */ |
|
1252 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) { |
|
1253 /* We try to grab the port */ |
|
1254 if (Success == XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, |
|
1255 0)) { |
|
1256 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j; |
|
1257 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name, |
|
1258 adaptors[adaptor_no].num_ports); |
|
1259 } |
|
1260 } |
|
1261 } |
|
1262 |
|
1263 /* This function generates a caps with all supported format by the first |
|
1264 Xv grabable port we find. We store each one of the supported formats in a |
|
1265 format list and append the format to a newly created caps that we return |
|
1266 If this function does not return NULL because of an error, it also grabs |
|
1267 the port via XvGrabPort */ |
|
1268 static GstCaps * |
|
1269 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink, |
|
1270 GstXContext * xcontext) |
|
1271 { |
|
1272 gint i; |
|
1273 XvAdaptorInfo *adaptors; |
|
1274 gint nb_formats; |
|
1275 XvImageFormatValues *formats = NULL; |
|
1276 guint nb_encodings; |
|
1277 XvEncodingInfo *encodings = NULL; |
|
1278 gulong max_w = G_MAXINT, max_h = G_MAXINT; |
|
1279 GstCaps *caps = NULL; |
|
1280 GstCaps *rgb_caps = NULL; |
|
1281 |
|
1282 g_return_val_if_fail (xcontext != NULL, NULL); |
|
1283 |
|
1284 /* First let's check that XVideo extension is available */ |
|
1285 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) { |
|
1286 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, |
|
1287 ("Could not initialise Xv output"), |
|
1288 ("XVideo extension is not available")); |
|
1289 return NULL; |
|
1290 } |
|
1291 |
|
1292 /* Then we get adaptors list */ |
|
1293 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root, |
|
1294 &xcontext->nb_adaptors, &adaptors)) { |
|
1295 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, |
|
1296 ("Could not initialise Xv output"), |
|
1297 ("Failed getting XV adaptors list")); |
|
1298 return NULL; |
|
1299 } |
|
1300 |
|
1301 xcontext->xv_port_id = 0; |
|
1302 |
|
1303 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors); |
|
1304 |
|
1305 xcontext->adaptors = |
|
1306 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *)); |
|
1307 |
|
1308 /* Now fill up our adaptor name array */ |
|
1309 for (i = 0; i < xcontext->nb_adaptors; i++) { |
|
1310 xcontext->adaptors[i] = g_strdup (adaptors[i].name); |
|
1311 } |
|
1312 |
|
1313 if (xvimagesink->adaptor_no >= 0 && |
|
1314 xvimagesink->adaptor_no < xcontext->nb_adaptors) { |
|
1315 /* Find xv port from user defined adaptor */ |
|
1316 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, |
|
1317 xvimagesink->adaptor_no); |
|
1318 } |
|
1319 |
|
1320 if (!xcontext->xv_port_id) { |
|
1321 /* Now search for an adaptor that supports XvImageMask */ |
|
1322 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) { |
|
1323 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i); |
|
1324 xvimagesink->adaptor_no = i; |
|
1325 } |
|
1326 } |
|
1327 |
|
1328 XvFreeAdaptorInfo (adaptors); |
|
1329 |
|
1330 if (!xcontext->xv_port_id) { |
|
1331 xvimagesink->adaptor_no = -1; |
|
1332 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY, |
|
1333 ("Could not initialise Xv output"), ("No port available")); |
|
1334 return NULL; |
|
1335 } |
|
1336 |
|
1337 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */ |
|
1338 { |
|
1339 int count; |
|
1340 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp, |
|
1341 xcontext->xv_port_id, &count); |
|
1342 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY"; |
|
1343 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER"; |
|
1344 static const char colorkey[] = "XV_COLORKEY"; |
|
1345 |
|
1346 for (i = 0; i < count; i++) |
|
1347 if (!strcmp (attr[i].name, autopaint)) { |
|
1348 const Atom atom = XInternAtom (xcontext->disp, autopaint, False); |
|
1349 |
|
1350 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, 1); |
|
1351 break; |
|
1352 } |
|
1353 |
|
1354 for (i = 0; i < count; i++) |
|
1355 if (!strcmp (attr[i].name, dbl_buffer)) { |
|
1356 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False); |
|
1357 |
|
1358 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, |
|
1359 (xvimagesink->double_buffer ? 1 : 0)); |
|
1360 break; |
|
1361 } |
|
1362 |
|
1363 /* Set the colorkey to something that is dark but hopefully won't randomly |
|
1364 * appear on the screen elsewhere (ie not black or greys) */ |
|
1365 for (i = 0; i < count; i++) |
|
1366 if (!strcmp (attr[i].name, colorkey)) { |
|
1367 const Atom atom = XInternAtom (xcontext->disp, colorkey, False); |
|
1368 guint32 ckey; |
|
1369 guint32 keymask; |
|
1370 gint bits; |
|
1371 gboolean set_attr = TRUE; |
|
1372 |
|
1373 /* Count the bits in the colorkey mask 'max' value */ |
|
1374 bits = 0; |
|
1375 for (keymask = (guint32) (attr[i].max_value); |
|
1376 keymask != 0; keymask >>= 1) |
|
1377 bits++; |
|
1378 |
|
1379 /* set a colorkey in the right format RGB565/RGB888 |
|
1380 * note that the colorkey is independent from the display |
|
1381 * depth (xcontext->depth). We only handle these 2 cases, because |
|
1382 * they're the only types of devices we've encountered. If we don't |
|
1383 * recognise it, leave it alone */ |
|
1384 if (bits == 16) |
|
1385 ckey = (1 << 10) | (2 << 5) | 3; |
|
1386 else if (bits == 24 || bits == 32) |
|
1387 ckey = (1 << 16) | (2 << 8) | 3; |
|
1388 else |
|
1389 set_attr = FALSE; |
|
1390 |
|
1391 |
|
1392 if (set_attr) { |
|
1393 ckey = CLAMP (ckey, (guint32) attr[i].min_value, |
|
1394 (guint32) attr[i].max_value); |
|
1395 GST_LOG_OBJECT (xvimagesink, |
|
1396 "Setting color key for display depth %d to 0x%x", |
|
1397 xcontext->depth, ckey); |
|
1398 |
|
1399 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, |
|
1400 (gint) ckey); |
|
1401 } else { |
|
1402 GST_LOG_OBJECT (xvimagesink, |
|
1403 "Unknown bit depth for Xv Colorkey - not adjusting "); |
|
1404 } |
|
1405 break; |
|
1406 } |
|
1407 |
|
1408 XFree (attr); |
|
1409 } |
|
1410 |
|
1411 /* Get the list of encodings supported by the adapter and look for the |
|
1412 * XV_IMAGE encoding so we can determine the maximum width and height |
|
1413 * supported */ |
|
1414 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings, |
|
1415 &encodings); |
|
1416 |
|
1417 for (i = 0; i < nb_encodings; i++) { |
|
1418 GST_LOG_OBJECT (xvimagesink, |
|
1419 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d", |
|
1420 i, encodings[i].name, encodings[i].width, encodings[i].height, |
|
1421 encodings[i].rate.numerator, encodings[i].rate.denominator); |
|
1422 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) { |
|
1423 max_w = encodings[i].width; |
|
1424 max_h = encodings[i].height; |
|
1425 } |
|
1426 } |
|
1427 |
|
1428 XvFreeEncodingInfo (encodings); |
|
1429 |
|
1430 /* We get all image formats supported by our port */ |
|
1431 formats = XvListImageFormats (xcontext->disp, |
|
1432 xcontext->xv_port_id, &nb_formats); |
|
1433 caps = gst_caps_new_empty (); |
|
1434 for (i = 0; i < nb_formats; i++) { |
|
1435 GstCaps *format_caps = NULL; |
|
1436 gboolean is_rgb_format = FALSE; |
|
1437 |
|
1438 /* We set the image format of the xcontext to an existing one. This |
|
1439 is just some valid image format for making our xshm calls check before |
|
1440 caps negotiation really happens. */ |
|
1441 xcontext->im_format = formats[i].id; |
|
1442 |
|
1443 switch (formats[i].type) { |
|
1444 case XvRGB: |
|
1445 { |
|
1446 XvImageFormatValues *fmt = &(formats[i]); |
|
1447 gint endianness = G_BIG_ENDIAN; |
|
1448 |
|
1449 if (fmt->byte_order == LSBFirst) { |
|
1450 /* our caps system handles 24/32bpp RGB as big-endian. */ |
|
1451 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) { |
|
1452 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask); |
|
1453 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask); |
|
1454 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask); |
|
1455 |
|
1456 if (fmt->bits_per_pixel == 24) { |
|
1457 fmt->red_mask >>= 8; |
|
1458 fmt->green_mask >>= 8; |
|
1459 fmt->blue_mask >>= 8; |
|
1460 } |
|
1461 } else |
|
1462 endianness = G_LITTLE_ENDIAN; |
|
1463 } |
|
1464 |
|
1465 format_caps = gst_caps_new_simple ("video/x-raw-rgb", |
|
1466 "endianness", G_TYPE_INT, endianness, |
|
1467 "depth", G_TYPE_INT, fmt->depth, |
|
1468 "bpp", G_TYPE_INT, fmt->bits_per_pixel, |
|
1469 "red_mask", G_TYPE_INT, fmt->red_mask, |
|
1470 "green_mask", G_TYPE_INT, fmt->green_mask, |
|
1471 "blue_mask", G_TYPE_INT, fmt->blue_mask, |
|
1472 "width", GST_TYPE_INT_RANGE, 1, max_w, |
|
1473 "height", GST_TYPE_INT_RANGE, 1, max_h, |
|
1474 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); |
|
1475 |
|
1476 is_rgb_format = TRUE; |
|
1477 break; |
|
1478 } |
|
1479 case XvYUV: |
|
1480 format_caps = gst_caps_new_simple ("video/x-raw-yuv", |
|
1481 "format", GST_TYPE_FOURCC, formats[i].id, |
|
1482 "width", GST_TYPE_INT_RANGE, 1, max_w, |
|
1483 "height", GST_TYPE_INT_RANGE, 1, max_h, |
|
1484 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); |
|
1485 break; |
|
1486 default: |
|
1487 g_assert_not_reached (); |
|
1488 break; |
|
1489 } |
|
1490 |
|
1491 if (format_caps) { |
|
1492 GstXvImageFormat *format = NULL; |
|
1493 |
|
1494 format = g_new0 (GstXvImageFormat, 1); |
|
1495 if (format) { |
|
1496 format->format = formats[i].id; |
|
1497 format->caps = gst_caps_copy (format_caps); |
|
1498 xcontext->formats_list = g_list_append (xcontext->formats_list, format); |
|
1499 } |
|
1500 |
|
1501 if (is_rgb_format) { |
|
1502 if (rgb_caps == NULL) |
|
1503 rgb_caps = format_caps; |
|
1504 else |
|
1505 gst_caps_append (rgb_caps, format_caps); |
|
1506 } else |
|
1507 gst_caps_append (caps, format_caps); |
|
1508 } |
|
1509 } |
|
1510 |
|
1511 /* Collected all caps into either the caps or rgb_caps structures. |
|
1512 * Append rgb_caps on the end of YUV, so that YUV is always preferred */ |
|
1513 if (rgb_caps) |
|
1514 gst_caps_append (caps, rgb_caps); |
|
1515 |
|
1516 if (formats) |
|
1517 XFree (formats); |
|
1518 |
|
1519 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps); |
|
1520 |
|
1521 if (gst_caps_is_empty (caps)) { |
|
1522 gst_caps_unref (caps); |
|
1523 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0); |
|
1524 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL), |
|
1525 ("No supported format found")); |
|
1526 return NULL; |
|
1527 } |
|
1528 |
|
1529 return caps; |
|
1530 } |
|
1531 |
|
1532 static gpointer |
|
1533 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink) |
|
1534 { |
|
1535 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
|
1536 |
|
1537 GST_OBJECT_LOCK (xvimagesink); |
|
1538 while (xvimagesink->running) { |
|
1539 GST_OBJECT_UNLOCK (xvimagesink); |
|
1540 |
|
1541 if (xvimagesink->xwindow) { |
|
1542 gst_xvimagesink_handle_xevents (xvimagesink); |
|
1543 } |
|
1544 g_usleep (50000); |
|
1545 |
|
1546 GST_OBJECT_LOCK (xvimagesink); |
|
1547 } |
|
1548 GST_OBJECT_UNLOCK (xvimagesink); |
|
1549 |
|
1550 return NULL; |
|
1551 } |
|
1552 |
|
1553 /* This function calculates the pixel aspect ratio based on the properties |
|
1554 * in the xcontext structure and stores it there. */ |
|
1555 static void |
|
1556 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext) |
|
1557 { |
|
1558 static const gint par[][2] = { |
|
1559 {1, 1}, /* regular screen */ |
|
1560 {16, 15}, /* PAL TV */ |
|
1561 {11, 10}, /* 525 line Rec.601 video */ |
|
1562 {54, 59}, /* 625 line Rec.601 video */ |
|
1563 {64, 45}, /* 1280x1024 on 16:9 display */ |
|
1564 {5, 3}, /* 1280x1024 on 4:3 display */ |
|
1565 {4, 3} /* 800x600 on 16:9 display */ |
|
1566 }; |
|
1567 gint i; |
|
1568 gint index; |
|
1569 gdouble ratio; |
|
1570 gdouble delta; |
|
1571 |
|
1572 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1]))) |
|
1573 |
|
1574 /* first calculate the "real" ratio based on the X values; |
|
1575 * which is the "physical" w/h divided by the w/h in pixels of the display */ |
|
1576 ratio = (gdouble) (xcontext->widthmm * xcontext->height) |
|
1577 / (xcontext->heightmm * xcontext->width); |
|
1578 |
|
1579 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so |
|
1580 * override here */ |
|
1581 if (xcontext->width == 720 && xcontext->height == 576) { |
|
1582 ratio = 4.0 * 576 / (3.0 * 720); |
|
1583 } |
|
1584 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio); |
|
1585 /* now find the one from par[][2] with the lowest delta to the real one */ |
|
1586 delta = DELTA (0); |
|
1587 index = 0; |
|
1588 |
|
1589 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) { |
|
1590 gdouble this_delta = DELTA (i); |
|
1591 |
|
1592 if (this_delta < delta) { |
|
1593 index = i; |
|
1594 delta = this_delta; |
|
1595 } |
|
1596 } |
|
1597 |
|
1598 GST_DEBUG ("Decided on index %d (%d/%d)", index, |
|
1599 par[index][0], par[index][1]); |
|
1600 |
|
1601 g_free (xcontext->par); |
|
1602 xcontext->par = g_new0 (GValue, 1); |
|
1603 g_value_init (xcontext->par, GST_TYPE_FRACTION); |
|
1604 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]); |
|
1605 GST_DEBUG ("set xcontext PAR to %d/%d", |
|
1606 gst_value_get_fraction_numerator (xcontext->par), |
|
1607 gst_value_get_fraction_denominator (xcontext->par)); |
|
1608 } |
|
1609 |
|
1610 /* This function gets the X Display and global info about it. Everything is |
|
1611 stored in our object and will be cleaned when the object is disposed. Note |
|
1612 here that caps for supported format are generated without any window or |
|
1613 image creation */ |
|
1614 static GstXContext * |
|
1615 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink) |
|
1616 { |
|
1617 GstXContext *xcontext = NULL; |
|
1618 XPixmapFormatValues *px_formats = NULL; |
|
1619 gint nb_formats = 0, i, j, N_attr; |
|
1620 XvAttribute *xv_attr; |
|
1621 Atom prop_atom; |
|
1622 char *channels[4] = { "XV_HUE", "XV_SATURATION", |
|
1623 "XV_BRIGHTNESS", "XV_CONTRAST" |
|
1624 }; |
|
1625 |
|
1626 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
|
1627 |
|
1628 xcontext = g_new0 (GstXContext, 1); |
|
1629 xcontext->im_format = 0; |
|
1630 |
|
1631 g_mutex_lock (xvimagesink->x_lock); |
|
1632 |
|
1633 xcontext->disp = XOpenDisplay (xvimagesink->display_name); |
|
1634 |
|
1635 if (!xcontext->disp) { |
|
1636 g_mutex_unlock (xvimagesink->x_lock); |
|
1637 g_free (xcontext); |
|
1638 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
1639 ("Could not initialise Xv output"), ("Could not open display")); |
|
1640 return NULL; |
|
1641 } |
|
1642 |
|
1643 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp); |
|
1644 xcontext->screen_num = DefaultScreen (xcontext->disp); |
|
1645 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num); |
|
1646 xcontext->root = DefaultRootWindow (xcontext->disp); |
|
1647 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num); |
|
1648 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num); |
|
1649 xcontext->depth = DefaultDepthOfScreen (xcontext->screen); |
|
1650 |
|
1651 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num); |
|
1652 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num); |
|
1653 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num); |
|
1654 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num); |
|
1655 |
|
1656 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm", |
|
1657 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm); |
|
1658 |
|
1659 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext); |
|
1660 /* We get supported pixmap formats at supported depth */ |
|
1661 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats); |
|
1662 |
|
1663 if (!px_formats) { |
|
1664 XCloseDisplay (xcontext->disp); |
|
1665 g_mutex_unlock (xvimagesink->x_lock); |
|
1666 g_free (xcontext->par); |
|
1667 g_free (xcontext); |
|
1668 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, |
|
1669 ("Could not initialise Xv output"), ("Could not get pixel formats")); |
|
1670 return NULL; |
|
1671 } |
|
1672 |
|
1673 /* We get bpp value corresponding to our running depth */ |
|
1674 for (i = 0; i < nb_formats; i++) { |
|
1675 if (px_formats[i].depth == xcontext->depth) |
|
1676 xcontext->bpp = px_formats[i].bits_per_pixel; |
|
1677 } |
|
1678 |
|
1679 XFree (px_formats); |
|
1680 |
|
1681 xcontext->endianness = |
|
1682 (ImageByteOrder (xcontext->disp) == |
|
1683 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN; |
|
1684 |
|
1685 /* our caps system handles 24/32bpp RGB as big-endian. */ |
|
1686 if ((xcontext->bpp == 24 || xcontext->bpp == 32) && |
|
1687 xcontext->endianness == G_LITTLE_ENDIAN) { |
|
1688 xcontext->endianness = G_BIG_ENDIAN; |
|
1689 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask); |
|
1690 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask); |
|
1691 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask); |
|
1692 if (xcontext->bpp == 24) { |
|
1693 xcontext->visual->red_mask >>= 8; |
|
1694 xcontext->visual->green_mask >>= 8; |
|
1695 xcontext->visual->blue_mask >>= 8; |
|
1696 } |
|
1697 } |
|
1698 |
|
1699 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext); |
|
1700 |
|
1701 if (!xcontext->caps) { |
|
1702 XCloseDisplay (xcontext->disp); |
|
1703 g_mutex_unlock (xvimagesink->x_lock); |
|
1704 g_free (xcontext->par); |
|
1705 g_free (xcontext); |
|
1706 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */ |
|
1707 return NULL; |
|
1708 } |
|
1709 #ifdef HAVE_XSHM |
|
1710 /* Search for XShm extension support */ |
|
1711 if (XShmQueryExtension (xcontext->disp) && |
|
1712 gst_xvimagesink_check_xshm_calls (xcontext)) { |
|
1713 xcontext->use_xshm = TRUE; |
|
1714 GST_DEBUG ("xvimagesink is using XShm extension"); |
|
1715 } else |
|
1716 #endif /* HAVE_XSHM */ |
|
1717 { |
|
1718 xcontext->use_xshm = FALSE; |
|
1719 GST_DEBUG ("xvimagesink is not using XShm extension"); |
|
1720 } |
|
1721 |
|
1722 xv_attr = XvQueryPortAttributes (xcontext->disp, |
|
1723 xcontext->xv_port_id, &N_attr); |
|
1724 |
|
1725 |
|
1726 /* Generate the channels list */ |
|
1727 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) { |
|
1728 XvAttribute *matching_attr = NULL; |
|
1729 |
|
1730 /* Retrieve the property atom if it exists. If it doesn't exist, |
|
1731 * the attribute itself must not either, so we can skip */ |
|
1732 prop_atom = XInternAtom (xcontext->disp, channels[i], True); |
|
1733 if (prop_atom == None) |
|
1734 continue; |
|
1735 |
|
1736 if (xv_attr != NULL) { |
|
1737 for (j = 0; j < N_attr && matching_attr == NULL; ++j) |
|
1738 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name)) |
|
1739 matching_attr = xv_attr + j; |
|
1740 } |
|
1741 |
|
1742 if (matching_attr) { |
|
1743 GstColorBalanceChannel *channel; |
|
1744 |
|
1745 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL); |
|
1746 channel->label = g_strdup (channels[i]); |
|
1747 channel->min_value = matching_attr ? matching_attr->min_value : -1000; |
|
1748 channel->max_value = matching_attr ? matching_attr->max_value : 1000; |
|
1749 |
|
1750 xcontext->channels_list = g_list_append (xcontext->channels_list, |
|
1751 channel); |
|
1752 |
|
1753 /* If the colorbalance settings have not been touched we get Xv values |
|
1754 as defaults and update our internal variables */ |
|
1755 if (!xvimagesink->cb_changed) { |
|
1756 gint val; |
|
1757 |
|
1758 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id, |
|
1759 prop_atom, &val); |
|
1760 /* Normalize val to [-1000, 1000] */ |
|
1761 val = -1000 + 2000 * (val - channel->min_value) / |
|
1762 (channel->max_value - channel->min_value); |
|
1763 |
|
1764 if (!g_ascii_strcasecmp (channels[i], "XV_HUE")) |
|
1765 xvimagesink->hue = val; |
|
1766 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION")) |
|
1767 xvimagesink->saturation = val; |
|
1768 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS")) |
|
1769 xvimagesink->brightness = val; |
|
1770 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST")) |
|
1771 xvimagesink->contrast = val; |
|
1772 } |
|
1773 } |
|
1774 } |
|
1775 |
|
1776 if (xv_attr) |
|
1777 XFree (xv_attr); |
|
1778 |
|
1779 g_mutex_unlock (xvimagesink->x_lock); |
|
1780 |
|
1781 /* Setup our event listening thread */ |
|
1782 GST_OBJECT_LOCK (xvimagesink); |
|
1783 xvimagesink->running = TRUE; |
|
1784 xvimagesink->event_thread = g_thread_create ( |
|
1785 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL); |
|
1786 GST_OBJECT_UNLOCK (xvimagesink); |
|
1787 |
|
1788 return xcontext; |
|
1789 } |
|
1790 |
|
1791 /* This function cleans the X context. Closing the Display, releasing the XV |
|
1792 port and unrefing the caps for supported formats. */ |
|
1793 static void |
|
1794 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink) |
|
1795 { |
|
1796 GList *formats_list, *channels_list; |
|
1797 GstXContext *xcontext; |
|
1798 gint i = 0; |
|
1799 |
|
1800 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
1801 |
|
1802 GST_OBJECT_LOCK (xvimagesink); |
|
1803 if (xvimagesink->xcontext == NULL) { |
|
1804 GST_OBJECT_UNLOCK (xvimagesink); |
|
1805 return; |
|
1806 } |
|
1807 |
|
1808 /* Take the XContext from the sink and clean it up */ |
|
1809 xcontext = xvimagesink->xcontext; |
|
1810 xvimagesink->xcontext = NULL; |
|
1811 |
|
1812 GST_OBJECT_UNLOCK (xvimagesink); |
|
1813 |
|
1814 |
|
1815 formats_list = xcontext->formats_list; |
|
1816 |
|
1817 while (formats_list) { |
|
1818 GstXvImageFormat *format = formats_list->data; |
|
1819 |
|
1820 gst_caps_unref (format->caps); |
|
1821 g_free (format); |
|
1822 formats_list = g_list_next (formats_list); |
|
1823 } |
|
1824 |
|
1825 if (xcontext->formats_list) |
|
1826 g_list_free (xcontext->formats_list); |
|
1827 |
|
1828 channels_list = xcontext->channels_list; |
|
1829 |
|
1830 while (channels_list) { |
|
1831 GstColorBalanceChannel *channel = channels_list->data; |
|
1832 |
|
1833 g_object_unref (channel); |
|
1834 channels_list = g_list_next (channels_list); |
|
1835 } |
|
1836 |
|
1837 if (xcontext->channels_list) |
|
1838 g_list_free (xcontext->channels_list); |
|
1839 |
|
1840 gst_caps_unref (xcontext->caps); |
|
1841 if (xcontext->last_caps) |
|
1842 gst_caps_replace (&xcontext->last_caps, NULL); |
|
1843 |
|
1844 for (i = 0; i < xcontext->nb_adaptors; i++) { |
|
1845 g_free (xcontext->adaptors[i]); |
|
1846 } |
|
1847 |
|
1848 g_free (xcontext->adaptors); |
|
1849 |
|
1850 g_free (xcontext->par); |
|
1851 |
|
1852 g_mutex_lock (xvimagesink->x_lock); |
|
1853 |
|
1854 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context"); |
|
1855 |
|
1856 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0); |
|
1857 |
|
1858 XCloseDisplay (xcontext->disp); |
|
1859 |
|
1860 g_mutex_unlock (xvimagesink->x_lock); |
|
1861 |
|
1862 g_free (xcontext); |
|
1863 } |
|
1864 |
|
1865 static void |
|
1866 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink) |
|
1867 { |
|
1868 g_mutex_lock (xvimagesink->pool_lock); |
|
1869 |
|
1870 while (xvimagesink->image_pool) { |
|
1871 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data; |
|
1872 |
|
1873 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool, |
|
1874 xvimagesink->image_pool); |
|
1875 gst_xvimage_buffer_free (xvimage); |
|
1876 } |
|
1877 |
|
1878 g_mutex_unlock (xvimagesink->pool_lock); |
|
1879 } |
|
1880 |
|
1881 /* Element stuff */ |
|
1882 |
|
1883 /* This function tries to get a format matching with a given caps in the |
|
1884 supported list of formats we generated in gst_xvimagesink_get_xv_support */ |
|
1885 static gint |
|
1886 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink, |
|
1887 GstCaps * caps) |
|
1888 { |
|
1889 GList *list = NULL; |
|
1890 |
|
1891 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0); |
|
1892 |
|
1893 list = xvimagesink->xcontext->formats_list; |
|
1894 |
|
1895 while (list) { |
|
1896 GstXvImageFormat *format = list->data; |
|
1897 |
|
1898 if (format) { |
|
1899 GstCaps *icaps = NULL; |
|
1900 |
|
1901 icaps = gst_caps_intersect (caps, format->caps); |
|
1902 if (!gst_caps_is_empty (icaps)) { |
|
1903 gst_caps_unref (icaps); |
|
1904 return format->format; |
|
1905 } |
|
1906 gst_caps_unref (icaps); |
|
1907 } |
|
1908 list = g_list_next (list); |
|
1909 } |
|
1910 |
|
1911 return -1; |
|
1912 } |
|
1913 |
|
1914 static GstCaps * |
|
1915 gst_xvimagesink_getcaps (GstBaseSink * bsink) |
|
1916 { |
|
1917 GstXvImageSink *xvimagesink; |
|
1918 |
|
1919 xvimagesink = GST_XVIMAGESINK (bsink); |
|
1920 |
|
1921 if (xvimagesink->xcontext) |
|
1922 return gst_caps_ref (xvimagesink->xcontext->caps); |
|
1923 |
|
1924 return |
|
1925 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD |
|
1926 (xvimagesink))); |
|
1927 } |
|
1928 |
|
1929 static gboolean |
|
1930 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps) |
|
1931 { |
|
1932 GstXvImageSink *xvimagesink; |
|
1933 GstStructure *structure; |
|
1934 GstCaps *intersection; |
|
1935 guint32 im_format = 0; |
|
1936 gboolean ret; |
|
1937 gint video_width, video_height; |
|
1938 gint video_par_n, video_par_d; /* video's PAR */ |
|
1939 gint display_par_n, display_par_d; /* display's PAR */ |
|
1940 const GValue *caps_par; |
|
1941 const GValue *fps; |
|
1942 guint num, den; |
|
1943 |
|
1944 xvimagesink = GST_XVIMAGESINK (bsink); |
|
1945 |
|
1946 GST_DEBUG_OBJECT (xvimagesink, |
|
1947 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %" |
|
1948 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps); |
|
1949 |
|
1950 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps); |
|
1951 GST_DEBUG_OBJECT (xvimagesink, "intersection returned %" GST_PTR_FORMAT, |
|
1952 intersection); |
|
1953 if (gst_caps_is_empty (intersection)) { |
|
1954 gst_caps_unref (intersection); |
|
1955 return FALSE; |
|
1956 } |
|
1957 |
|
1958 gst_caps_unref (intersection); |
|
1959 |
|
1960 structure = gst_caps_get_structure (caps, 0); |
|
1961 ret = gst_structure_get_int (structure, "width", &video_width); |
|
1962 ret &= gst_structure_get_int (structure, "height", &video_height); |
|
1963 fps = gst_structure_get_value (structure, "framerate"); |
|
1964 ret &= (fps != NULL); |
|
1965 |
|
1966 if (!ret) { |
|
1967 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, " |
|
1968 "height or framerate from intersected caps"); |
|
1969 return FALSE; |
|
1970 } |
|
1971 |
|
1972 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps); |
|
1973 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps); |
|
1974 |
|
1975 xvimagesink->video_width = video_width; |
|
1976 xvimagesink->video_height = video_height; |
|
1977 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps); |
|
1978 if (im_format == -1) { |
|
1979 GST_DEBUG_OBJECT (xvimagesink, |
|
1980 "Could not locate image format from caps %" GST_PTR_FORMAT, caps); |
|
1981 return FALSE; |
|
1982 } |
|
1983 |
|
1984 /* get aspect ratio from caps if it's present, and |
|
1985 * convert video width and height to a display width and height |
|
1986 * using wd / hd = wv / hv * PARv / PARd */ |
|
1987 |
|
1988 /* get video's PAR */ |
|
1989 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio"); |
|
1990 if (caps_par) { |
|
1991 video_par_n = gst_value_get_fraction_numerator (caps_par); |
|
1992 video_par_d = gst_value_get_fraction_denominator (caps_par); |
|
1993 } else { |
|
1994 video_par_n = 1; |
|
1995 video_par_d = 1; |
|
1996 } |
|
1997 /* get display's PAR */ |
|
1998 if (xvimagesink->par) { |
|
1999 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par); |
|
2000 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par); |
|
2001 } else { |
|
2002 display_par_n = 1; |
|
2003 display_par_d = 1; |
|
2004 } |
|
2005 |
|
2006 if (!gst_video_calculate_display_ratio (&num, &den, video_width, |
|
2007 video_height, video_par_n, video_par_d, display_par_n, |
|
2008 display_par_d)) { |
|
2009 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL), |
|
2010 ("Error calculating the output display ratio of the video.")); |
|
2011 return FALSE; |
|
2012 } |
|
2013 |
|
2014 GST_DEBUG_OBJECT (xvimagesink, |
|
2015 "video width/height: %dx%d, calculated display ratio: %d/%d", |
|
2016 video_width, video_height, num, den); |
|
2017 |
|
2018 /* now find a width x height that respects this display ratio. |
|
2019 * prefer those that have one of w/h the same as the incoming video |
|
2020 * using wd / hd = num / den */ |
|
2021 |
|
2022 /* start with same height, because of interlaced video */ |
|
2023 /* check hd / den is an integer scale factor, and scale wd with the PAR */ |
|
2024 if (video_height % den == 0) { |
|
2025 GST_DEBUG_OBJECT (xvimagesink, "keeping video height"); |
|
2026 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint) |
|
2027 gst_util_uint64_scale_int (video_height, num, den); |
|
2028 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height; |
|
2029 } else if (video_width % num == 0) { |
|
2030 GST_DEBUG_OBJECT (xvimagesink, "keeping video width"); |
|
2031 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width; |
|
2032 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint) |
|
2033 gst_util_uint64_scale_int (video_width, den, num); |
|
2034 } else { |
|
2035 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height"); |
|
2036 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint) |
|
2037 gst_util_uint64_scale_int (video_height, num, den); |
|
2038 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height; |
|
2039 } |
|
2040 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d", |
|
2041 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink)); |
|
2042 |
|
2043 /* Notify application to set xwindow id now */ |
|
2044 g_mutex_lock (xvimagesink->flow_lock); |
|
2045 if (!xvimagesink->xwindow) { |
|
2046 g_mutex_unlock (xvimagesink->flow_lock); |
|
2047 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink)); |
|
2048 } else { |
|
2049 g_mutex_unlock (xvimagesink->flow_lock); |
|
2050 } |
|
2051 |
|
2052 /* Creating our window and our image with the display size in pixels */ |
|
2053 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 || |
|
2054 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0) { |
|
2055 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL), |
|
2056 ("Error calculating the output display ratio of the video.")); |
|
2057 return FALSE; |
|
2058 } |
|
2059 |
|
2060 g_mutex_lock (xvimagesink->flow_lock); |
|
2061 if (!xvimagesink->xwindow) { |
|
2062 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink, |
|
2063 GST_VIDEO_SINK_WIDTH (xvimagesink), |
|
2064 GST_VIDEO_SINK_HEIGHT (xvimagesink)); |
|
2065 } |
|
2066 |
|
2067 /* After a resize, we want to redraw the borders in case the new frame size |
|
2068 * doesn't cover the same area */ |
|
2069 xvimagesink->draw_border = TRUE; |
|
2070 |
|
2071 /* We renew our xvimage only if size or format changed; |
|
2072 * the xvimage is the same size as the video pixel size */ |
|
2073 if ((xvimagesink->xvimage) && |
|
2074 ((im_format != xvimagesink->xvimage->im_format) || |
|
2075 (video_width != xvimagesink->xvimage->width) || |
|
2076 (video_height != xvimagesink->xvimage->height))) { |
|
2077 GST_DEBUG_OBJECT (xvimagesink, |
|
2078 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT, |
|
2079 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format), |
|
2080 GST_FOURCC_ARGS (im_format)); |
|
2081 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage"); |
|
2082 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage)); |
|
2083 xvimagesink->xvimage = NULL; |
|
2084 } |
|
2085 |
|
2086 g_mutex_unlock (xvimagesink->flow_lock); |
|
2087 |
|
2088 return TRUE; |
|
2089 } |
|
2090 |
|
2091 static GstStateChangeReturn |
|
2092 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition) |
|
2093 { |
|
2094 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
|
2095 GstXvImageSink *xvimagesink; |
|
2096 GstXContext *xcontext = NULL; |
|
2097 |
|
2098 xvimagesink = GST_XVIMAGESINK (element); |
|
2099 |
|
2100 switch (transition) { |
|
2101 case GST_STATE_CHANGE_NULL_TO_READY: |
|
2102 /* Initializing the XContext */ |
|
2103 if (xvimagesink->xcontext == NULL) { |
|
2104 xcontext = gst_xvimagesink_xcontext_get (xvimagesink); |
|
2105 if (xcontext == NULL) |
|
2106 return GST_STATE_CHANGE_FAILURE; |
|
2107 GST_OBJECT_LOCK (xvimagesink); |
|
2108 if (xcontext) |
|
2109 xvimagesink->xcontext = xcontext; |
|
2110 GST_OBJECT_UNLOCK (xvimagesink); |
|
2111 } |
|
2112 |
|
2113 /* update object's par with calculated one if not set yet */ |
|
2114 if (!xvimagesink->par) { |
|
2115 xvimagesink->par = g_new0 (GValue, 1); |
|
2116 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par); |
|
2117 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR"); |
|
2118 } |
|
2119 /* call XSynchronize with the current value of synchronous */ |
|
2120 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s", |
|
2121 xvimagesink->synchronous ? "TRUE" : "FALSE"); |
|
2122 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous); |
|
2123 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2124 break; |
|
2125 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
2126 g_mutex_lock (xvimagesink->flow_lock); |
|
2127 if (xvimagesink->xwindow) |
|
2128 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow); |
|
2129 g_mutex_unlock (xvimagesink->flow_lock); |
|
2130 break; |
|
2131 case GST_STATE_CHANGE_PAUSED_TO_PLAYING: |
|
2132 break; |
|
2133 default: |
|
2134 break; |
|
2135 } |
|
2136 |
|
2137 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
2138 |
|
2139 switch (transition) { |
|
2140 case GST_STATE_CHANGE_PLAYING_TO_PAUSED: |
|
2141 break; |
|
2142 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
2143 xvimagesink->fps_n = 0; |
|
2144 xvimagesink->fps_d = 1; |
|
2145 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0; |
|
2146 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0; |
|
2147 break; |
|
2148 case GST_STATE_CHANGE_READY_TO_NULL: |
|
2149 gst_xvimagesink_reset (xvimagesink); |
|
2150 break; |
|
2151 default: |
|
2152 break; |
|
2153 } |
|
2154 |
|
2155 return ret; |
|
2156 } |
|
2157 |
|
2158 static void |
|
2159 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf, |
|
2160 GstClockTime * start, GstClockTime * end) |
|
2161 { |
|
2162 GstXvImageSink *xvimagesink; |
|
2163 |
|
2164 xvimagesink = GST_XVIMAGESINK (bsink); |
|
2165 |
|
2166 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { |
|
2167 *start = GST_BUFFER_TIMESTAMP (buf); |
|
2168 if (GST_BUFFER_DURATION_IS_VALID (buf)) { |
|
2169 *end = *start + GST_BUFFER_DURATION (buf); |
|
2170 } else { |
|
2171 if (xvimagesink->fps_n > 0) { |
|
2172 *end = *start + |
|
2173 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d, |
|
2174 xvimagesink->fps_n); |
|
2175 } |
|
2176 } |
|
2177 } |
|
2178 } |
|
2179 |
|
2180 static GstFlowReturn |
|
2181 gst_xvimagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf) |
|
2182 { |
|
2183 GstXvImageSink *xvimagesink; |
|
2184 |
|
2185 xvimagesink = GST_XVIMAGESINK (bsink); |
|
2186 |
|
2187 /* If this buffer has been allocated using our buffer management we simply |
|
2188 put the ximage which is in the PRIVATE pointer */ |
|
2189 if (GST_IS_XVIMAGE_BUFFER (buf)) { |
|
2190 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer"); |
|
2191 if (!gst_xvimagesink_xvimage_put (xvimagesink, GST_XVIMAGE_BUFFER (buf))) |
|
2192 goto no_window; |
|
2193 } else { |
|
2194 GST_LOG_OBJECT (xvimagesink, "slow copy into bufferpool buffer"); |
|
2195 /* Else we have to copy the data into our private image, */ |
|
2196 /* if we have one... */ |
|
2197 if (!xvimagesink->xvimage) { |
|
2198 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage"); |
|
2199 |
|
2200 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink, |
|
2201 GST_BUFFER_CAPS (buf)); |
|
2202 |
|
2203 if (!xvimagesink->xvimage) |
|
2204 /* The create method should have posted an informative error */ |
|
2205 goto no_image; |
|
2206 |
|
2207 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) { |
|
2208 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, |
|
2209 ("Failed to create output image buffer of %dx%d pixels", |
|
2210 xvimagesink->xvimage->width, xvimagesink->xvimage->height), |
|
2211 ("XServer allocated buffer size did not match input buffer")); |
|
2212 |
|
2213 gst_xvimage_buffer_destroy (xvimagesink->xvimage); |
|
2214 xvimagesink->xvimage = NULL; |
|
2215 goto no_image; |
|
2216 } |
|
2217 } |
|
2218 |
|
2219 memcpy (xvimagesink->xvimage->xvimage->data, |
|
2220 GST_BUFFER_DATA (buf), |
|
2221 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size)); |
|
2222 |
|
2223 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage)) |
|
2224 goto no_window; |
|
2225 } |
|
2226 |
|
2227 return GST_FLOW_OK; |
|
2228 |
|
2229 /* ERRORS */ |
|
2230 no_image: |
|
2231 { |
|
2232 /* No image available. That's very bad ! */ |
|
2233 GST_WARNING_OBJECT (xvimagesink, "could not create image"); |
|
2234 return GST_FLOW_ERROR; |
|
2235 } |
|
2236 no_window: |
|
2237 { |
|
2238 /* No Window available to put our image into */ |
|
2239 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window"); |
|
2240 return GST_FLOW_ERROR; |
|
2241 } |
|
2242 } |
|
2243 |
|
2244 /* Buffer management */ |
|
2245 |
|
2246 static GstFlowReturn |
|
2247 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, |
|
2248 GstCaps * caps, GstBuffer ** buf) |
|
2249 { |
|
2250 GstFlowReturn ret = GST_FLOW_OK; |
|
2251 GstXvImageSink *xvimagesink; |
|
2252 GstXvImageBuffer *xvimage = NULL; |
|
2253 GstCaps *intersection = NULL; |
|
2254 GstStructure *structure = NULL; |
|
2255 gint width, height, image_format; |
|
2256 |
|
2257 xvimagesink = GST_XVIMAGESINK (bsink); |
|
2258 |
|
2259 if (G_LIKELY (xvimagesink->xcontext->last_caps && |
|
2260 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) { |
|
2261 GST_DEBUG_OBJECT (xvimagesink, |
|
2262 "buffer alloc for same last_caps, reusing caps"); |
|
2263 intersection = gst_caps_ref (caps); |
|
2264 image_format = xvimagesink->xcontext->last_format; |
|
2265 |
|
2266 goto reuse_last_caps; |
|
2267 } |
|
2268 |
|
2269 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested with caps %" |
|
2270 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, caps, |
|
2271 xvimagesink->xcontext->caps); |
|
2272 |
|
2273 /* Check the caps against our xcontext */ |
|
2274 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps); |
|
2275 |
|
2276 /* Ensure the returned caps are fixed */ |
|
2277 gst_caps_truncate (intersection); |
|
2278 |
|
2279 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %" |
|
2280 GST_PTR_FORMAT, intersection); |
|
2281 |
|
2282 if (gst_caps_is_empty (intersection)) { |
|
2283 /* So we don't support this kind of buffer, let's define one we'd like */ |
|
2284 GstCaps *new_caps = gst_caps_copy (caps); |
|
2285 |
|
2286 structure = gst_caps_get_structure (new_caps, 0); |
|
2287 |
|
2288 /* Try with YUV first */ |
|
2289 gst_structure_set_name (structure, "video/x-raw-yuv"); |
|
2290 gst_structure_remove_field (structure, "format"); |
|
2291 gst_structure_remove_field (structure, "endianness"); |
|
2292 gst_structure_remove_field (structure, "depth"); |
|
2293 gst_structure_remove_field (structure, "bpp"); |
|
2294 gst_structure_remove_field (structure, "red_mask"); |
|
2295 gst_structure_remove_field (structure, "green_mask"); |
|
2296 gst_structure_remove_field (structure, "blue_mask"); |
|
2297 gst_structure_remove_field (structure, "alpha_mask"); |
|
2298 |
|
2299 /* Reuse intersection with Xcontext */ |
|
2300 gst_caps_unref (intersection); |
|
2301 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps); |
|
2302 |
|
2303 if (gst_caps_is_empty (intersection)) { |
|
2304 /* Now try with RGB */ |
|
2305 gst_structure_set_name (structure, "video/x-raw-rgb"); |
|
2306 /* And interset again */ |
|
2307 gst_caps_unref (intersection); |
|
2308 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps); |
|
2309 |
|
2310 if (gst_caps_is_empty (intersection)) { |
|
2311 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with " |
|
2312 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT |
|
2313 " are completely incompatible with those caps", new_caps, |
|
2314 xvimagesink->xcontext->caps); |
|
2315 gst_caps_unref (new_caps); |
|
2316 ret = GST_FLOW_UNEXPECTED; |
|
2317 goto beach; |
|
2318 } |
|
2319 } |
|
2320 |
|
2321 /* Clean this copy */ |
|
2322 gst_caps_unref (new_caps); |
|
2323 /* We want fixed caps */ |
|
2324 gst_caps_truncate (intersection); |
|
2325 |
|
2326 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %" |
|
2327 GST_PTR_FORMAT, intersection); |
|
2328 } else if (gst_caps_is_equal (intersection, caps)) { |
|
2329 /* Things work better if we return a buffer with the same caps ptr |
|
2330 * as was asked for when we can */ |
|
2331 gst_caps_replace (&intersection, caps); |
|
2332 } |
|
2333 |
|
2334 /* Get image format from caps */ |
|
2335 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink, |
|
2336 intersection); |
|
2337 |
|
2338 /* Store our caps and format as the last_caps to avoid expensive |
|
2339 * caps intersection next time */ |
|
2340 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection); |
|
2341 xvimagesink->xcontext->last_format = image_format; |
|
2342 |
|
2343 reuse_last_caps: |
|
2344 |
|
2345 /* Get geometry from caps */ |
|
2346 structure = gst_caps_get_structure (intersection, 0); |
|
2347 if (!gst_structure_get_int (structure, "width", &width) || |
|
2348 !gst_structure_get_int (structure, "height", &height) || |
|
2349 image_format == -1) { |
|
2350 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %" |
|
2351 GST_PTR_FORMAT, intersection); |
|
2352 ret = GST_FLOW_UNEXPECTED; |
|
2353 goto beach; |
|
2354 } |
|
2355 |
|
2356 g_mutex_lock (xvimagesink->pool_lock); |
|
2357 |
|
2358 /* Walking through the pool cleaning unusable images and searching for a |
|
2359 suitable one */ |
|
2360 while (xvimagesink->image_pool) { |
|
2361 xvimage = xvimagesink->image_pool->data; |
|
2362 |
|
2363 if (xvimage) { |
|
2364 /* Removing from the pool */ |
|
2365 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool, |
|
2366 xvimagesink->image_pool); |
|
2367 |
|
2368 /* We check for geometry or image format changes */ |
|
2369 if ((xvimage->width != width) || |
|
2370 (xvimage->height != height) || (xvimage->im_format != image_format)) { |
|
2371 /* This image is unusable. Destroying... */ |
|
2372 gst_xvimage_buffer_free (xvimage); |
|
2373 xvimage = NULL; |
|
2374 } else { |
|
2375 /* We found a suitable image */ |
|
2376 GST_LOG_OBJECT (xvimagesink, "found usable image in pool"); |
|
2377 break; |
|
2378 } |
|
2379 } |
|
2380 } |
|
2381 |
|
2382 g_mutex_unlock (xvimagesink->pool_lock); |
|
2383 |
|
2384 if (!xvimage) { |
|
2385 /* We found no suitable image in the pool. Creating... */ |
|
2386 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage"); |
|
2387 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection); |
|
2388 if (xvimage && xvimage->size < size) { |
|
2389 /* This image is unusable. Destroying... */ |
|
2390 gst_xvimage_buffer_free (xvimage); |
|
2391 xvimage = NULL; |
|
2392 } |
|
2393 } |
|
2394 |
|
2395 if (xvimage) { |
|
2396 gst_buffer_set_caps (GST_BUFFER (xvimage), intersection); |
|
2397 } |
|
2398 |
|
2399 *buf = GST_BUFFER (xvimage); |
|
2400 |
|
2401 beach: |
|
2402 if (intersection) { |
|
2403 gst_caps_unref (intersection); |
|
2404 } |
|
2405 |
|
2406 return ret; |
|
2407 } |
|
2408 |
|
2409 /* Interfaces stuff */ |
|
2410 |
|
2411 static gboolean |
|
2412 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type) |
|
2413 { |
|
2414 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY || |
|
2415 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE); |
|
2416 return TRUE; |
|
2417 } |
|
2418 |
|
2419 static void |
|
2420 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass) |
|
2421 { |
|
2422 klass->supported = gst_xvimagesink_interface_supported; |
|
2423 } |
|
2424 |
|
2425 static void |
|
2426 gst_xvimagesink_navigation_send_event (GstNavigation * navigation, |
|
2427 GstStructure * structure) |
|
2428 { |
|
2429 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation); |
|
2430 GstPad *peer; |
|
2431 |
|
2432 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) { |
|
2433 GstEvent *event; |
|
2434 GstVideoRectangle src, dst, result; |
|
2435 gdouble x, y, xscale = 1.0, yscale = 1.0; |
|
2436 |
|
2437 event = gst_event_new_navigation (structure); |
|
2438 |
|
2439 /* We take the flow_lock while we look at the window */ |
|
2440 g_mutex_lock (xvimagesink->flow_lock); |
|
2441 |
|
2442 if (!xvimagesink->xwindow) { |
|
2443 g_mutex_unlock (xvimagesink->flow_lock); |
|
2444 return; |
|
2445 } |
|
2446 |
|
2447 /* We get the frame position using the calculated geometry from _setcaps |
|
2448 that respect pixel aspect ratios */ |
|
2449 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink); |
|
2450 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink); |
|
2451 dst.w = xvimagesink->xwindow->width; |
|
2452 dst.h = xvimagesink->xwindow->height; |
|
2453 |
|
2454 g_mutex_unlock (xvimagesink->flow_lock); |
|
2455 |
|
2456 if (xvimagesink->keep_aspect) { |
|
2457 gst_video_sink_center_rect (src, dst, &result, TRUE); |
|
2458 } else { |
|
2459 result.x = result.y = 0; |
|
2460 result.w = dst.w; |
|
2461 result.h = dst.h; |
|
2462 } |
|
2463 |
|
2464 /* We calculate scaling using the original video frames geometry to include |
|
2465 pixel aspect ratio scaling. */ |
|
2466 xscale = (gdouble) xvimagesink->video_width / result.w; |
|
2467 yscale = (gdouble) xvimagesink->video_height / result.h; |
|
2468 |
|
2469 /* Converting pointer coordinates to the non scaled geometry */ |
|
2470 if (gst_structure_get_double (structure, "pointer_x", &x)) { |
|
2471 x = MIN (x, result.x + result.w); |
|
2472 x = MAX (x - result.x, 0); |
|
2473 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, |
|
2474 (gdouble) x * xscale, NULL); |
|
2475 } |
|
2476 if (gst_structure_get_double (structure, "pointer_y", &y)) { |
|
2477 y = MIN (y, result.y + result.h); |
|
2478 y = MAX (y - result.y, 0); |
|
2479 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, |
|
2480 (gdouble) y * yscale, NULL); |
|
2481 } |
|
2482 |
|
2483 gst_pad_send_event (peer, event); |
|
2484 gst_object_unref (peer); |
|
2485 } |
|
2486 } |
|
2487 |
|
2488 static void |
|
2489 gst_xvimagesink_navigation_init (GstNavigationInterface * iface) |
|
2490 { |
|
2491 iface->send_event = gst_xvimagesink_navigation_send_event; |
|
2492 } |
|
2493 |
|
2494 static void |
|
2495 gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id) |
|
2496 { |
|
2497 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay); |
|
2498 GstXWindow *xwindow = NULL; |
|
2499 XWindowAttributes attr; |
|
2500 |
|
2501 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
2502 |
|
2503 g_mutex_lock (xvimagesink->flow_lock); |
|
2504 |
|
2505 /* If we already use that window return */ |
|
2506 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) { |
|
2507 g_mutex_unlock (xvimagesink->flow_lock); |
|
2508 return; |
|
2509 } |
|
2510 |
|
2511 /* If the element has not initialized the X11 context try to do so */ |
|
2512 if (!xvimagesink->xcontext && |
|
2513 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) { |
|
2514 g_mutex_unlock (xvimagesink->flow_lock); |
|
2515 /* we have thrown a GST_ELEMENT_ERROR now */ |
|
2516 return; |
|
2517 } |
|
2518 |
|
2519 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2520 |
|
2521 /* Clear image pool as the images are unusable anyway */ |
|
2522 gst_xvimagesink_imagepool_clear (xvimagesink); |
|
2523 |
|
2524 /* Clear the xvimage */ |
|
2525 if (xvimagesink->xvimage) { |
|
2526 gst_xvimage_buffer_free (xvimagesink->xvimage); |
|
2527 xvimagesink->xvimage = NULL; |
|
2528 } |
|
2529 |
|
2530 /* If a window is there already we destroy it */ |
|
2531 if (xvimagesink->xwindow) { |
|
2532 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow); |
|
2533 xvimagesink->xwindow = NULL; |
|
2534 } |
|
2535 |
|
2536 /* If the xid is 0 we go back to an internal window */ |
|
2537 if (xwindow_id == 0) { |
|
2538 /* If no width/height caps nego did not happen window will be created |
|
2539 during caps nego then */ |
|
2540 if (GST_VIDEO_SINK_WIDTH (xvimagesink) |
|
2541 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) { |
|
2542 xwindow = |
|
2543 gst_xvimagesink_xwindow_new (xvimagesink, |
|
2544 GST_VIDEO_SINK_WIDTH (xvimagesink), |
|
2545 GST_VIDEO_SINK_HEIGHT (xvimagesink)); |
|
2546 } |
|
2547 } else { |
|
2548 xwindow = g_new0 (GstXWindow, 1); |
|
2549 |
|
2550 xwindow->win = xwindow_id; |
|
2551 |
|
2552 /* We get window geometry, set the event we want to receive, |
|
2553 and create a GC */ |
|
2554 g_mutex_lock (xvimagesink->x_lock); |
|
2555 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr); |
|
2556 xwindow->width = attr.width; |
|
2557 xwindow->height = attr.height; |
|
2558 xwindow->internal = FALSE; |
|
2559 if (xvimagesink->handle_events) { |
|
2560 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask | |
|
2561 StructureNotifyMask | PointerMotionMask | KeyPressMask | |
|
2562 KeyReleaseMask); |
|
2563 } |
|
2564 |
|
2565 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp, |
|
2566 xwindow->win, 0, NULL); |
|
2567 g_mutex_unlock (xvimagesink->x_lock); |
|
2568 } |
|
2569 |
|
2570 if (xwindow) |
|
2571 xvimagesink->xwindow = xwindow; |
|
2572 |
|
2573 g_mutex_unlock (xvimagesink->flow_lock); |
|
2574 } |
|
2575 |
|
2576 static void |
|
2577 gst_xvimagesink_expose (GstXOverlay * overlay) |
|
2578 { |
|
2579 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay); |
|
2580 |
|
2581 gst_xvimagesink_xvimage_put (xvimagesink, NULL); |
|
2582 } |
|
2583 |
|
2584 static void |
|
2585 gst_xvimagesink_set_event_handling (GstXOverlay * overlay, |
|
2586 gboolean handle_events) |
|
2587 { |
|
2588 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay); |
|
2589 |
|
2590 xvimagesink->handle_events = handle_events; |
|
2591 |
|
2592 g_mutex_lock (xvimagesink->flow_lock); |
|
2593 |
|
2594 if (G_UNLIKELY (!xvimagesink->xwindow)) { |
|
2595 g_mutex_unlock (xvimagesink->flow_lock); |
|
2596 return; |
|
2597 } |
|
2598 |
|
2599 g_mutex_lock (xvimagesink->x_lock); |
|
2600 |
|
2601 if (handle_events) { |
|
2602 if (xvimagesink->xwindow->internal) { |
|
2603 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, |
|
2604 ExposureMask | StructureNotifyMask | PointerMotionMask | |
|
2605 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask); |
|
2606 } else { |
|
2607 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, |
|
2608 ExposureMask | StructureNotifyMask | PointerMotionMask | |
|
2609 KeyPressMask | KeyReleaseMask); |
|
2610 } |
|
2611 } else { |
|
2612 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0); |
|
2613 } |
|
2614 |
|
2615 g_mutex_unlock (xvimagesink->x_lock); |
|
2616 |
|
2617 g_mutex_unlock (xvimagesink->flow_lock); |
|
2618 } |
|
2619 |
|
2620 static void |
|
2621 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface) |
|
2622 { |
|
2623 iface->set_xwindow_id = gst_xvimagesink_set_xwindow_id; |
|
2624 iface->expose = gst_xvimagesink_expose; |
|
2625 iface->handle_events = gst_xvimagesink_set_event_handling; |
|
2626 } |
|
2627 |
|
2628 static const GList * |
|
2629 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance) |
|
2630 { |
|
2631 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance); |
|
2632 |
|
2633 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL); |
|
2634 |
|
2635 if (xvimagesink->xcontext) |
|
2636 return xvimagesink->xcontext->channels_list; |
|
2637 else |
|
2638 return NULL; |
|
2639 } |
|
2640 |
|
2641 static void |
|
2642 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance, |
|
2643 GstColorBalanceChannel * channel, gint value) |
|
2644 { |
|
2645 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance); |
|
2646 |
|
2647 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink)); |
|
2648 g_return_if_fail (channel->label != NULL); |
|
2649 |
|
2650 xvimagesink->cb_changed = TRUE; |
|
2651 |
|
2652 /* Normalize val to [-1000, 1000] */ |
|
2653 value = -1000 + 2000 * (value - channel->min_value) / |
|
2654 (channel->max_value - channel->min_value); |
|
2655 |
|
2656 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) { |
|
2657 xvimagesink->hue = value; |
|
2658 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) { |
|
2659 xvimagesink->saturation = value; |
|
2660 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) { |
|
2661 xvimagesink->contrast = value; |
|
2662 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) { |
|
2663 xvimagesink->brightness = value; |
|
2664 } else { |
|
2665 g_warning ("got an unknown channel %s", channel->label); |
|
2666 return; |
|
2667 } |
|
2668 |
|
2669 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2670 } |
|
2671 |
|
2672 static gint |
|
2673 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance, |
|
2674 GstColorBalanceChannel * channel) |
|
2675 { |
|
2676 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance); |
|
2677 gint value = 0; |
|
2678 |
|
2679 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0); |
|
2680 g_return_val_if_fail (channel->label != NULL, 0); |
|
2681 |
|
2682 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) { |
|
2683 value = xvimagesink->hue; |
|
2684 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) { |
|
2685 value = xvimagesink->saturation; |
|
2686 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) { |
|
2687 value = xvimagesink->contrast; |
|
2688 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) { |
|
2689 value = xvimagesink->brightness; |
|
2690 } else { |
|
2691 g_warning ("got an unknown channel %s", channel->label); |
|
2692 } |
|
2693 |
|
2694 /* Normalize val to [channel->min_value, channel->max_value] */ |
|
2695 value = channel->min_value + (channel->max_value - channel->min_value) * |
|
2696 (value + 1000) / 2000; |
|
2697 |
|
2698 return value; |
|
2699 } |
|
2700 |
|
2701 static void |
|
2702 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface) |
|
2703 { |
|
2704 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE; |
|
2705 iface->list_channels = gst_xvimagesink_colorbalance_list_channels; |
|
2706 iface->set_value = gst_xvimagesink_colorbalance_set_value; |
|
2707 iface->get_value = gst_xvimagesink_colorbalance_get_value; |
|
2708 } |
|
2709 |
|
2710 static const GList * |
|
2711 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe) |
|
2712 { |
|
2713 GObjectClass *klass = G_OBJECT_GET_CLASS (probe); |
|
2714 static GList *list = NULL; |
|
2715 |
|
2716 if (!list) { |
|
2717 list = g_list_append (NULL, g_object_class_find_property (klass, "device")); |
|
2718 } |
|
2719 |
|
2720 return list; |
|
2721 } |
|
2722 |
|
2723 static void |
|
2724 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe, |
|
2725 guint prop_id, const GParamSpec * pspec) |
|
2726 { |
|
2727 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe); |
|
2728 |
|
2729 switch (prop_id) { |
|
2730 case ARG_DEVICE: |
|
2731 GST_DEBUG_OBJECT (xvimagesink, "probing device list"); |
|
2732 if (!xvimagesink->xcontext) { |
|
2733 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext"); |
|
2734 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink); |
|
2735 } |
|
2736 break; |
|
2737 default: |
|
2738 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); |
|
2739 break; |
|
2740 } |
|
2741 } |
|
2742 |
|
2743 static gboolean |
|
2744 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe, |
|
2745 guint prop_id, const GParamSpec * pspec) |
|
2746 { |
|
2747 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe); |
|
2748 gboolean ret = FALSE; |
|
2749 |
|
2750 switch (prop_id) { |
|
2751 case ARG_DEVICE: |
|
2752 if (xvimagesink->xcontext != NULL) { |
|
2753 ret = FALSE; |
|
2754 } else { |
|
2755 ret = TRUE; |
|
2756 } |
|
2757 break; |
|
2758 default: |
|
2759 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); |
|
2760 break; |
|
2761 } |
|
2762 |
|
2763 return ret; |
|
2764 } |
|
2765 |
|
2766 static GValueArray * |
|
2767 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe, |
|
2768 guint prop_id, const GParamSpec * pspec) |
|
2769 { |
|
2770 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe); |
|
2771 GValueArray *array = NULL; |
|
2772 |
|
2773 if (G_UNLIKELY (!xvimagesink->xcontext)) { |
|
2774 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't " |
|
2775 "get values"); |
|
2776 goto beach; |
|
2777 } |
|
2778 |
|
2779 switch (prop_id) { |
|
2780 case ARG_DEVICE: |
|
2781 { |
|
2782 guint i; |
|
2783 GValue value = { 0 }; |
|
2784 |
|
2785 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors); |
|
2786 g_value_init (&value, G_TYPE_STRING); |
|
2787 |
|
2788 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) { |
|
2789 gchar *adaptor_id_s = g_strdup_printf ("%u", i); |
|
2790 |
|
2791 g_value_set_string (&value, adaptor_id_s); |
|
2792 g_value_array_append (array, &value); |
|
2793 g_free (adaptor_id_s); |
|
2794 } |
|
2795 g_value_unset (&value); |
|
2796 break; |
|
2797 } |
|
2798 default: |
|
2799 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); |
|
2800 break; |
|
2801 } |
|
2802 |
|
2803 beach: |
|
2804 return array; |
|
2805 } |
|
2806 |
|
2807 static void |
|
2808 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface * |
|
2809 iface) |
|
2810 { |
|
2811 iface->get_properties = gst_xvimagesink_probe_get_properties; |
|
2812 iface->probe_property = gst_xvimagesink_probe_probe_property; |
|
2813 iface->needs_probe = gst_xvimagesink_probe_needs_probe; |
|
2814 iface->get_values = gst_xvimagesink_probe_get_values; |
|
2815 } |
|
2816 |
|
2817 /* =========================================== */ |
|
2818 /* */ |
|
2819 /* Init & Class init */ |
|
2820 /* */ |
|
2821 /* =========================================== */ |
|
2822 |
|
2823 static void |
|
2824 gst_xvimagesink_set_property (GObject * object, guint prop_id, |
|
2825 const GValue * value, GParamSpec * pspec) |
|
2826 { |
|
2827 GstXvImageSink *xvimagesink; |
|
2828 |
|
2829 g_return_if_fail (GST_IS_XVIMAGESINK (object)); |
|
2830 |
|
2831 xvimagesink = GST_XVIMAGESINK (object); |
|
2832 |
|
2833 switch (prop_id) { |
|
2834 case ARG_HUE: |
|
2835 xvimagesink->hue = g_value_get_int (value); |
|
2836 xvimagesink->cb_changed = TRUE; |
|
2837 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2838 break; |
|
2839 case ARG_CONTRAST: |
|
2840 xvimagesink->contrast = g_value_get_int (value); |
|
2841 xvimagesink->cb_changed = TRUE; |
|
2842 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2843 break; |
|
2844 case ARG_BRIGHTNESS: |
|
2845 xvimagesink->brightness = g_value_get_int (value); |
|
2846 xvimagesink->cb_changed = TRUE; |
|
2847 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2848 break; |
|
2849 case ARG_SATURATION: |
|
2850 xvimagesink->saturation = g_value_get_int (value); |
|
2851 xvimagesink->cb_changed = TRUE; |
|
2852 gst_xvimagesink_update_colorbalance (xvimagesink); |
|
2853 break; |
|
2854 case ARG_DISPLAY: |
|
2855 xvimagesink->display_name = g_strdup (g_value_get_string (value)); |
|
2856 break; |
|
2857 case ARG_SYNCHRONOUS: |
|
2858 xvimagesink->synchronous = g_value_get_boolean (value); |
|
2859 if (xvimagesink->xcontext) { |
|
2860 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous); |
|
2861 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s", |
|
2862 xvimagesink->synchronous ? "TRUE" : "FALSE"); |
|
2863 } |
|
2864 break; |
|
2865 case ARG_PIXEL_ASPECT_RATIO: |
|
2866 g_free (xvimagesink->par); |
|
2867 xvimagesink->par = g_new0 (GValue, 1); |
|
2868 g_value_init (xvimagesink->par, GST_TYPE_FRACTION); |
|
2869 if (!g_value_transform (value, xvimagesink->par)) { |
|
2870 g_warning ("Could not transform string to aspect ratio"); |
|
2871 gst_value_set_fraction (xvimagesink->par, 1, 1); |
|
2872 } |
|
2873 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d", |
|
2874 gst_value_get_fraction_numerator (xvimagesink->par), |
|
2875 gst_value_get_fraction_denominator (xvimagesink->par)); |
|
2876 break; |
|
2877 case ARG_FORCE_ASPECT_RATIO: |
|
2878 xvimagesink->keep_aspect = g_value_get_boolean (value); |
|
2879 break; |
|
2880 case ARG_HANDLE_EVENTS: |
|
2881 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink), |
|
2882 g_value_get_boolean (value)); |
|
2883 break; |
|
2884 case ARG_DEVICE: |
|
2885 xvimagesink->adaptor_no = atoi (g_value_get_string (value)); |
|
2886 break; |
|
2887 case ARG_HANDLE_EXPOSE: |
|
2888 xvimagesink->handle_expose = g_value_get_boolean (value); |
|
2889 break; |
|
2890 case ARG_DOUBLE_BUFFER: |
|
2891 xvimagesink->double_buffer = g_value_get_boolean (value); |
|
2892 break; |
|
2893 default: |
|
2894 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
2895 break; |
|
2896 } |
|
2897 } |
|
2898 |
|
2899 static void |
|
2900 gst_xvimagesink_get_property (GObject * object, guint prop_id, |
|
2901 GValue * value, GParamSpec * pspec) |
|
2902 { |
|
2903 GstXvImageSink *xvimagesink; |
|
2904 |
|
2905 g_return_if_fail (GST_IS_XVIMAGESINK (object)); |
|
2906 |
|
2907 xvimagesink = GST_XVIMAGESINK (object); |
|
2908 |
|
2909 switch (prop_id) { |
|
2910 case ARG_HUE: |
|
2911 g_value_set_int (value, xvimagesink->hue); |
|
2912 break; |
|
2913 case ARG_CONTRAST: |
|
2914 g_value_set_int (value, xvimagesink->contrast); |
|
2915 break; |
|
2916 case ARG_BRIGHTNESS: |
|
2917 g_value_set_int (value, xvimagesink->brightness); |
|
2918 break; |
|
2919 case ARG_SATURATION: |
|
2920 g_value_set_int (value, xvimagesink->saturation); |
|
2921 break; |
|
2922 case ARG_DISPLAY: |
|
2923 g_value_set_string (value, xvimagesink->display_name); |
|
2924 break; |
|
2925 case ARG_SYNCHRONOUS: |
|
2926 g_value_set_boolean (value, xvimagesink->synchronous); |
|
2927 break; |
|
2928 case ARG_PIXEL_ASPECT_RATIO: |
|
2929 if (xvimagesink->par) |
|
2930 g_value_transform (xvimagesink->par, value); |
|
2931 break; |
|
2932 case ARG_FORCE_ASPECT_RATIO: |
|
2933 g_value_set_boolean (value, xvimagesink->keep_aspect); |
|
2934 break; |
|
2935 case ARG_HANDLE_EVENTS: |
|
2936 g_value_set_boolean (value, xvimagesink->handle_events); |
|
2937 break; |
|
2938 case ARG_DEVICE: |
|
2939 { |
|
2940 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no); |
|
2941 |
|
2942 g_value_set_string (value, adaptor_no_s); |
|
2943 g_free (adaptor_no_s); |
|
2944 break; |
|
2945 } |
|
2946 case ARG_DEVICE_NAME: |
|
2947 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) { |
|
2948 g_value_set_string (value, |
|
2949 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]); |
|
2950 } else { |
|
2951 g_value_set_string (value, NULL); |
|
2952 } |
|
2953 break; |
|
2954 case ARG_HANDLE_EXPOSE: |
|
2955 g_value_set_boolean (value, xvimagesink->handle_expose); |
|
2956 break; |
|
2957 case ARG_DOUBLE_BUFFER: |
|
2958 g_value_set_boolean (value, xvimagesink->double_buffer); |
|
2959 break; |
|
2960 default: |
|
2961 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
2962 break; |
|
2963 } |
|
2964 } |
|
2965 |
|
2966 static void |
|
2967 gst_xvimagesink_reset (GstXvImageSink * xvimagesink) |
|
2968 { |
|
2969 GThread *thread; |
|
2970 |
|
2971 GST_OBJECT_LOCK (xvimagesink); |
|
2972 xvimagesink->running = FALSE; |
|
2973 /* grab thread and mark it as NULL */ |
|
2974 thread = xvimagesink->event_thread; |
|
2975 xvimagesink->event_thread = NULL; |
|
2976 GST_OBJECT_UNLOCK (xvimagesink); |
|
2977 |
|
2978 /* Wait for our event thread to finish before we clean up our stuff. */ |
|
2979 if (thread) |
|
2980 g_thread_join (thread); |
|
2981 |
|
2982 if (xvimagesink->cur_image) { |
|
2983 gst_buffer_unref (xvimagesink->cur_image); |
|
2984 xvimagesink->cur_image = NULL; |
|
2985 } |
|
2986 if (xvimagesink->xvimage) { |
|
2987 gst_buffer_unref (xvimagesink->xvimage); |
|
2988 xvimagesink->xvimage = NULL; |
|
2989 } |
|
2990 |
|
2991 gst_xvimagesink_imagepool_clear (xvimagesink); |
|
2992 |
|
2993 if (xvimagesink->xwindow) { |
|
2994 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow); |
|
2995 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow); |
|
2996 xvimagesink->xwindow = NULL; |
|
2997 } |
|
2998 |
|
2999 gst_xvimagesink_xcontext_clear (xvimagesink); |
|
3000 } |
|
3001 |
|
3002 /* Finalize is called only once, dispose can be called multiple times. |
|
3003 * We use mutexes and don't reset stuff to NULL here so let's register |
|
3004 * as a finalize. */ |
|
3005 static void |
|
3006 gst_xvimagesink_finalize (GObject * object) |
|
3007 { |
|
3008 GstXvImageSink *xvimagesink; |
|
3009 |
|
3010 xvimagesink = GST_XVIMAGESINK (object); |
|
3011 |
|
3012 gst_xvimagesink_reset (xvimagesink); |
|
3013 |
|
3014 if (xvimagesink->display_name) { |
|
3015 g_free (xvimagesink->display_name); |
|
3016 xvimagesink->display_name = NULL; |
|
3017 } |
|
3018 |
|
3019 if (xvimagesink->par) { |
|
3020 g_free (xvimagesink->par); |
|
3021 xvimagesink->par = NULL; |
|
3022 } |
|
3023 if (xvimagesink->x_lock) { |
|
3024 g_mutex_free (xvimagesink->x_lock); |
|
3025 xvimagesink->x_lock = NULL; |
|
3026 } |
|
3027 if (xvimagesink->flow_lock) { |
|
3028 g_mutex_free (xvimagesink->flow_lock); |
|
3029 xvimagesink->flow_lock = NULL; |
|
3030 } |
|
3031 if (xvimagesink->pool_lock) { |
|
3032 g_mutex_free (xvimagesink->pool_lock); |
|
3033 xvimagesink->pool_lock = NULL; |
|
3034 } |
|
3035 |
|
3036 G_OBJECT_CLASS (parent_class)->finalize (object); |
|
3037 } |
|
3038 |
|
3039 static void |
|
3040 gst_xvimagesink_init (GstXvImageSink * xvimagesink) |
|
3041 { |
|
3042 xvimagesink->display_name = NULL; |
|
3043 xvimagesink->adaptor_no = 0; |
|
3044 xvimagesink->xcontext = NULL; |
|
3045 xvimagesink->xwindow = NULL; |
|
3046 xvimagesink->xvimage = NULL; |
|
3047 xvimagesink->cur_image = NULL; |
|
3048 |
|
3049 xvimagesink->hue = xvimagesink->saturation = 0; |
|
3050 xvimagesink->contrast = xvimagesink->brightness = 0; |
|
3051 xvimagesink->cb_changed = FALSE; |
|
3052 |
|
3053 xvimagesink->fps_n = 0; |
|
3054 xvimagesink->fps_d = 0; |
|
3055 xvimagesink->video_width = 0; |
|
3056 xvimagesink->video_height = 0; |
|
3057 |
|
3058 xvimagesink->x_lock = g_mutex_new (); |
|
3059 xvimagesink->flow_lock = g_mutex_new (); |
|
3060 |
|
3061 xvimagesink->image_pool = NULL; |
|
3062 xvimagesink->pool_lock = g_mutex_new (); |
|
3063 |
|
3064 xvimagesink->synchronous = FALSE; |
|
3065 xvimagesink->double_buffer = TRUE; |
|
3066 xvimagesink->running = FALSE; |
|
3067 xvimagesink->keep_aspect = FALSE; |
|
3068 xvimagesink->handle_events = TRUE; |
|
3069 xvimagesink->par = NULL; |
|
3070 xvimagesink->handle_expose = TRUE; |
|
3071 } |
|
3072 |
|
3073 static void |
|
3074 gst_xvimagesink_base_init (gpointer g_class) |
|
3075 { |
|
3076 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); |
|
3077 |
|
3078 gst_element_class_set_details (element_class, &gst_xvimagesink_details); |
|
3079 |
|
3080 gst_element_class_add_pad_template (element_class, |
|
3081 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory)); |
|
3082 } |
|
3083 |
|
3084 static void |
|
3085 gst_xvimagesink_class_init (GstXvImageSinkClass * klass) |
|
3086 { |
|
3087 GObjectClass *gobject_class; |
|
3088 GstElementClass *gstelement_class; |
|
3089 GstBaseSinkClass *gstbasesink_class; |
|
3090 |
|
3091 gobject_class = (GObjectClass *) klass; |
|
3092 gstelement_class = (GstElementClass *) klass; |
|
3093 gstbasesink_class = (GstBaseSinkClass *) klass; |
|
3094 |
|
3095 parent_class = g_type_class_peek_parent (klass); |
|
3096 |
|
3097 gobject_class->set_property = gst_xvimagesink_set_property; |
|
3098 gobject_class->get_property = gst_xvimagesink_get_property; |
|
3099 |
|
3100 g_object_class_install_property (gobject_class, ARG_CONTRAST, |
|
3101 g_param_spec_int ("contrast", "Contrast", "The contrast of the video", |
|
3102 -1000, 1000, 0, G_PARAM_READWRITE)); |
|
3103 g_object_class_install_property (gobject_class, ARG_BRIGHTNESS, |
|
3104 g_param_spec_int ("brightness", "Brightness", |
|
3105 "The brightness of the video", -1000, 1000, 0, G_PARAM_READWRITE)); |
|
3106 g_object_class_install_property (gobject_class, ARG_HUE, |
|
3107 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0, |
|
3108 G_PARAM_READWRITE)); |
|
3109 g_object_class_install_property (gobject_class, ARG_SATURATION, |
|
3110 g_param_spec_int ("saturation", "Saturation", |
|
3111 "The saturation of the video", -1000, 1000, 0, G_PARAM_READWRITE)); |
|
3112 g_object_class_install_property (gobject_class, ARG_DISPLAY, |
|
3113 g_param_spec_string ("display", "Display", "X Display name", NULL, |
|
3114 G_PARAM_READWRITE)); |
|
3115 g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS, |
|
3116 g_param_spec_boolean ("synchronous", "Synchronous", |
|
3117 "When enabled, runs " |
|
3118 "the X display in synchronous mode. (used only for debugging)", FALSE, |
|
3119 G_PARAM_READWRITE)); |
|
3120 g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO, |
|
3121 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio", |
|
3122 "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE)); |
|
3123 g_object_class_install_property (gobject_class, ARG_FORCE_ASPECT_RATIO, |
|
3124 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio", |
|
3125 "When enabled, scaling will respect original aspect ratio", FALSE, |
|
3126 G_PARAM_READWRITE)); |
|
3127 g_object_class_install_property (gobject_class, ARG_HANDLE_EVENTS, |
|
3128 g_param_spec_boolean ("handle-events", "Handle XEvents", |
|
3129 "When enabled, XEvents will be selected and handled", TRUE, |
|
3130 G_PARAM_READWRITE)); |
|
3131 g_object_class_install_property (gobject_class, ARG_DEVICE, |
|
3132 g_param_spec_string ("device", "Adaptor number", |
|
3133 "The number of the video adaptor", "0", G_PARAM_READWRITE)); |
|
3134 g_object_class_install_property (gobject_class, ARG_DEVICE_NAME, |
|
3135 g_param_spec_string ("device-name", "Adaptor name", |
|
3136 "The name of the video adaptor", NULL, G_PARAM_READABLE)); |
|
3137 g_object_class_install_property (gobject_class, ARG_HANDLE_EXPOSE, |
|
3138 g_param_spec_boolean ("handle-expose", "Handle expose", "When enabled, " |
|
3139 "the current frame will always be drawn in response to X Expose " |
|
3140 "events", TRUE, G_PARAM_READWRITE)); |
|
3141 g_object_class_install_property (gobject_class, ARG_DOUBLE_BUFFER, |
|
3142 g_param_spec_boolean ("double-buffer", "Double-buffer", |
|
3143 "Whether to double-buffer the output", TRUE, G_PARAM_READWRITE)); |
|
3144 |
|
3145 gobject_class->finalize = gst_xvimagesink_finalize; |
|
3146 |
|
3147 gstelement_class->change_state = |
|
3148 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state); |
|
3149 |
|
3150 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps); |
|
3151 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps); |
|
3152 gstbasesink_class->buffer_alloc = |
|
3153 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc); |
|
3154 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times); |
|
3155 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame); |
|
3156 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame); |
|
3157 } |
|
3158 |
|
3159 /* ============================================================= */ |
|
3160 /* */ |
|
3161 /* Public Methods */ |
|
3162 /* */ |
|
3163 /* ============================================================= */ |
|
3164 |
|
3165 /* =========================================== */ |
|
3166 /* */ |
|
3167 /* Object typing & Creation */ |
|
3168 /* */ |
|
3169 /* =========================================== */ |
|
3170 |
|
3171 GType |
|
3172 gst_xvimagesink_get_type (void) |
|
3173 { |
|
3174 static GType xvimagesink_type = 0; |
|
3175 |
|
3176 if (!xvimagesink_type) { |
|
3177 static const GTypeInfo xvimagesink_info = { |
|
3178 sizeof (GstXvImageSinkClass), |
|
3179 gst_xvimagesink_base_init, |
|
3180 NULL, |
|
3181 (GClassInitFunc) gst_xvimagesink_class_init, |
|
3182 NULL, |
|
3183 NULL, |
|
3184 sizeof (GstXvImageSink), |
|
3185 0, |
|
3186 (GInstanceInitFunc) gst_xvimagesink_init, |
|
3187 }; |
|
3188 static const GInterfaceInfo iface_info = { |
|
3189 (GInterfaceInitFunc) gst_xvimagesink_interface_init, |
|
3190 NULL, |
|
3191 NULL, |
|
3192 }; |
|
3193 static const GInterfaceInfo navigation_info = { |
|
3194 (GInterfaceInitFunc) gst_xvimagesink_navigation_init, |
|
3195 NULL, |
|
3196 NULL, |
|
3197 }; |
|
3198 static const GInterfaceInfo overlay_info = { |
|
3199 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init, |
|
3200 NULL, |
|
3201 NULL, |
|
3202 }; |
|
3203 static const GInterfaceInfo colorbalance_info = { |
|
3204 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init, |
|
3205 NULL, |
|
3206 NULL, |
|
3207 }; |
|
3208 static const GInterfaceInfo propertyprobe_info = { |
|
3209 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init, |
|
3210 NULL, |
|
3211 NULL, |
|
3212 }; |
|
3213 xvimagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK, |
|
3214 "GstXvImageSink", &xvimagesink_info, 0); |
|
3215 |
|
3216 g_type_add_interface_static (xvimagesink_type, |
|
3217 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info); |
|
3218 g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION, |
|
3219 &navigation_info); |
|
3220 g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY, |
|
3221 &overlay_info); |
|
3222 g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE, |
|
3223 &colorbalance_info); |
|
3224 g_type_add_interface_static (xvimagesink_type, GST_TYPE_PROPERTY_PROBE, |
|
3225 &propertyprobe_info); |
|
3226 |
|
3227 |
|
3228 /* register type and create class in a more safe place instead of at |
|
3229 * runtime since the type registration and class creation is not |
|
3230 * threadsafe. */ |
|
3231 g_type_class_ref (gst_xvimage_buffer_get_type ()); |
|
3232 } |
|
3233 |
|
3234 return xvimagesink_type; |
|
3235 } |
|
3236 |
|
3237 static gboolean |
|
3238 plugin_init (GstPlugin * plugin) |
|
3239 { |
|
3240 if (!gst_element_register (plugin, "xvimagesink", |
|
3241 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK)) |
|
3242 return FALSE; |
|
3243 |
|
3244 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0, |
|
3245 "xvimagesink element"); |
|
3246 |
|
3247 return TRUE; |
|
3248 } |
|
3249 |
|
3250 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
|
3251 GST_VERSION_MINOR, |
|
3252 "xvimagesink", |
|
3253 "XFree86 video output plugin using Xv extension", |
|
3254 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |
|