1 /* GStreamer
2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:element-xvimagesink
23 * @title: xvimagesink
24 *
25 * XvImageSink renders video frames to a drawable (XWindow) on a local display
26 * using the XVideo extension. Rendering to a remote display is theoretically
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 #GstVideoOverlay interface and will then render
30 * video frames in this drawable. If no Window ID was provided by the
31 * application, the element will create its own internal window and render
32 * into it.
33 *
34 * ## Scaling
35 *
36 * The XVideo extension, when it's available, handles hardware accelerated
37 * scaling of video frames. This means that the element will just accept
38 * incoming video frames no matter their geometry and will then put them to the
39 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40 * property it is possible to enforce scaling with a constant aspect ratio,
41 * which means drawing black borders around the video frame.
42 *
43 * ## Events
44 *
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 *
55 * ## Pixel aspect ratio
56 *
57 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
58 * the display specified in the #GstXvImageSink:display property or the
59 * default display if nothing specified. Once this connection is open it will
60 * inspect the display configuration including the physical display geometry and
61 * then calculate the pixel aspect ratio. When receiving video frames with a
62 * different pixel aspect ratio, XvImageSink will use hardware scaling to
63 * display the video frames correctly on display's pixel aspect ratio.
64 * Sometimes the calculated pixel aspect ratio can be wrong, it is
65 * then possible to enforce a specific pixel aspect ratio using the
66 * #GstXvImageSink:pixel-aspect-ratio property.
67 *
68 * ## Examples
69 * |[
70 * gst-launch-1.0 -v videotestsrc ! xvimagesink
71 * ]|
72 * A pipeline to test hardware scaling.
73 * When the test video signal appears you can resize the window and see that
74 * video frames are scaled through hardware (no extra CPU cost). By default
75 * the image will never be distorted when scaled, instead black borders will
76 * be added if needed.
77 * |[
78 * gst-launch-1.0 -v videotestsrc ! xvimagesink force-aspect-ratio=false
79 * ]|
80 * Same pipeline with #GstXvImageSink:force-aspect-ratio property set to
81 * false. You can observe that no borders are drawn around the scaled image
82 * now and it will be distorted to fill the entire frame instead of respecting
83 * the aspect ratio.
84 * |[
85 * gst-launch-1.0 -v videotestsrc ! navigationtest ! xvimagesink
86 * ]|
87 * A pipeline to test navigation events.
88 * While moving the mouse pointer over the test signal you will see a black box
89 * following the mouse pointer. If you press the mouse button somewhere on the
90 * video and release it somewhere else a green box will appear where you pressed
91 * the button and a red one where you released it. (The navigationtest element
92 * is part of gst-plugins-good.) You can observe here that even if the images
93 * are scaled through hardware the pointer coordinates are converted back to the
94 * original video frame geometry so that the box can be drawn to the correct
95 * position. This also handles borders correctly, limiting coordinates to the
96 * image area
97 * |[
98 * gst-launch-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=4/3 ! xvimagesink
99 * ]|
100 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
101 * videotestsrc, in most cases the pixel aspect ratio of the display will be
102 * 1/1. This means that XvImageSink will have to do the scaling to convert
103 * incoming frames to a size that will match the display pixel aspect ratio
104 * (from 320x240 to 320x180 in this case).
105 * |[
106 * gst-launch-1.0 -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
107 * ]|
108 * Demonstrates how to use the colorbalance interface.
109 *
110 */
111
112 /* for developers: there are two useful tools : xvinfo and xvattr */
113
114 #ifdef HAVE_CONFIG_H
115 #include "config.h"
116 #endif
117
118 /* Our interfaces */
119 #include <gst/video/navigation.h>
120 #include <gst/video/videooverlay.h>
121 #include <gst/video/colorbalance.h>
122 /* Helper functions */
123 #include <gst/video/gstvideometa.h>
124
125 /* Object header */
126 #include "xvimagesink.h"
127 #include "xvimageallocator.h"
128
129 /* Debugging category */
130 #include <gst/gstinfo.h>
131
132 /* for XkbKeycodeToKeysym */
133 #include <X11/XKBlib.h>
134
135 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_context);
136 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_image_pool);
137 GST_DEBUG_CATEGORY (gst_debug_xv_image_sink);
138
139 #define GST_CAT_DEFAULT gst_debug_xv_image_sink
140 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
141
142 typedef struct
143 {
144 unsigned long flags;
145 unsigned long functions;
146 unsigned long decorations;
147 long input_mode;
148 unsigned long status;
149 }
150 MotifWmHints, MwmHints;
151
152 #define MWM_HINTS_DECORATIONS (1L << 1)
153
154 static gboolean gst_xv_image_sink_open (GstXvImageSink * xvimagesink);
155 static void gst_xv_image_sink_close (GstXvImageSink * xvimagesink);
156 static void gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink *
157 xvimagesink);
158 static void gst_xv_image_sink_expose (GstVideoOverlay * overlay);
159
160 /* Default template - initiated with class struct to allow gst-register to work
161 without X running */
162 static GstStaticPadTemplate gst_xv_image_sink_sink_template_factory =
163 GST_STATIC_PAD_TEMPLATE ("sink",
164 GST_PAD_SINK,
165 GST_PAD_ALWAYS,
166 GST_STATIC_CAPS ("video/x-raw, "
167 "framerate = (fraction) [ 0, MAX ], "
168 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
169 );
170
171 enum
172 {
173 PROP_0,
174 PROP_CONTRAST,
175 PROP_BRIGHTNESS,
176 PROP_HUE,
177 PROP_SATURATION,
178 PROP_DISPLAY,
179 PROP_SYNCHRONOUS,
180 PROP_PIXEL_ASPECT_RATIO,
181 PROP_FORCE_ASPECT_RATIO,
182 PROP_HANDLE_EVENTS,
183 PROP_DEVICE,
184 PROP_DEVICE_NAME,
185 PROP_HANDLE_EXPOSE,
186 PROP_DOUBLE_BUFFER,
187 PROP_AUTOPAINT_COLORKEY,
188 PROP_COLORKEY,
189 PROP_DRAW_BORDERS,
190 PROP_WINDOW_WIDTH,
191 PROP_WINDOW_HEIGHT,
192 PROP_LAST
193 };
194
195 /* ============================================================= */
196 /* */
197 /* Public Methods */
198 /* */
199 /* ============================================================= */
200
201 /* =========================================== */
202 /* */
203 /* Object typing & Creation */
204 /* */
205 /* =========================================== */
206 static void gst_xv_image_sink_navigation_init (GstNavigationInterface * iface);
207 static void gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface *
208 iface);
209 static void gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface *
210 iface);
211 #define gst_xv_image_sink_parent_class parent_class
212 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xv_image_sink, GST_TYPE_VIDEO_SINK,
213 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
214 gst_xv_image_sink_navigation_init);
215 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
216 gst_xv_image_sink_video_overlay_init);
217 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
218 gst_xv_image_sink_colorbalance_init));
219 #define _do_init \
220 GST_DEBUG_CATEGORY_INIT (gst_debug_xv_context, "xcontext", 0, "xcontext miniobject");\
221 GST_DEBUG_CATEGORY_INIT (gst_debug_xv_image_sink, "xvimagesink", 0, "ximagesink element");\
222 GST_DEBUG_CATEGORY_INIT (gst_debug_xv_image_pool, "xvimagepool", 0, "ximagepool object");\
223
224 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (xvimagesink, "xvimagesink",
225 GST_RANK_PRIMARY, GST_TYPE_XV_IMAGE_SINK, _do_init);
226
227 /* ============================================================= */
228 /* */
229 /* Private Methods */
230 /* */
231 /* ============================================================= */
232
233
234 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
235 * if no window was available */
236 static gboolean
gst_xv_image_sink_xvimage_put(GstXvImageSink * xvimagesink,GstBuffer * xvimage)237 gst_xv_image_sink_xvimage_put (GstXvImageSink * xvimagesink,
238 GstBuffer * xvimage)
239 {
240 GstXvImageMemory *mem;
241 GstVideoCropMeta *crop;
242 GstVideoRectangle result;
243 gboolean draw_border = FALSE;
244 GstVideoRectangle src = { 0, };
245 GstVideoRectangle dst = { 0, };
246 GstVideoRectangle mem_crop;
247 GstXWindow *xwindow;
248
249 /* We take the flow_lock. If expose is in there we don't want to run
250 concurrently from the data flow thread */
251 g_mutex_lock (&xvimagesink->flow_lock);
252
253 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
254 g_mutex_unlock (&xvimagesink->flow_lock);
255 return FALSE;
256 }
257
258 /* Draw borders when displaying the first frame. After this
259 draw borders only on expose event or after a size change. */
260 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
261 draw_border = xvimagesink->draw_borders;
262 xvimagesink->redraw_border = FALSE;
263 }
264
265 /* Store a reference to the last image we put, lose the previous one */
266 if (xvimage && xvimagesink->cur_image != xvimage) {
267 if (xvimagesink->cur_image) {
268 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
269 gst_buffer_unref (xvimagesink->cur_image);
270 }
271 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
272 xvimagesink->cur_image = gst_buffer_ref (xvimage);
273 }
274
275 /* Expose sends a NULL image, we take the latest frame */
276 if (!xvimage) {
277 if (xvimagesink->cur_image) {
278 draw_border = TRUE;
279 xvimage = xvimagesink->cur_image;
280 } else {
281 g_mutex_unlock (&xvimagesink->flow_lock);
282 return TRUE;
283 }
284 }
285
286 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
287 gst_xvimage_memory_get_crop (mem, &mem_crop);
288
289 crop = gst_buffer_get_video_crop_meta (xvimage);
290
291 if (crop) {
292 src.x = crop->x + mem_crop.x;
293 src.y = crop->y + mem_crop.y;
294 src.w = crop->width;
295 src.h = crop->height;
296 GST_LOG_OBJECT (xvimagesink,
297 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
298 } else {
299 src = mem_crop;
300 }
301
302 if (xvimagesink->keep_aspect) {
303 GstVideoRectangle s;
304
305 /* We take the size of the source material as it was negotiated and
306 * corrected for DAR. This size can be different from the cropped size in
307 * which case the image will be scaled to fit the negotiated size. */
308 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
309 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
310 dst.w = xwindow->render_rect.w;
311 dst.h = xwindow->render_rect.h;
312
313 gst_video_sink_center_rect (s, dst, &result, TRUE);
314 result.x += xwindow->render_rect.x;
315 result.y += xwindow->render_rect.y;
316 } else {
317 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
318 }
319
320 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
321
322 g_mutex_unlock (&xvimagesink->flow_lock);
323
324 return TRUE;
325 }
326
327 static void
gst_xv_image_sink_xwindow_set_title(GstXvImageSink * xvimagesink,GstXWindow * xwindow,const gchar * media_title)328 gst_xv_image_sink_xwindow_set_title (GstXvImageSink * xvimagesink,
329 GstXWindow * xwindow, const gchar * media_title)
330 {
331 if (media_title) {
332 g_free (xvimagesink->media_title);
333 xvimagesink->media_title = g_strdup (media_title);
334 }
335 if (xwindow) {
336 /* we have a window */
337 const gchar *app_name;
338 const gchar *title = NULL;
339 gchar *title_mem = NULL;
340
341 /* set application name as a title */
342 app_name = g_get_application_name ();
343
344 if (app_name && xvimagesink->media_title) {
345 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
346 app_name, NULL);
347 } else if (app_name) {
348 title = app_name;
349 } else if (xvimagesink->media_title) {
350 title = xvimagesink->media_title;
351 }
352
353 gst_xwindow_set_title (xwindow, title);
354 g_free (title_mem);
355 }
356 }
357
358 /* This function handles a GstXWindow creation
359 * The width and height are the actual pixel size on the display */
360 static GstXWindow *
gst_xv_image_sink_xwindow_new(GstXvImageSink * xvimagesink,gint width,gint height)361 gst_xv_image_sink_xwindow_new (GstXvImageSink * xvimagesink,
362 gint width, gint height)
363 {
364 GstXWindow *xwindow = NULL;
365 GstXvContext *context;
366
367 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
368
369 context = xvimagesink->context;
370
371 xwindow = gst_xvcontext_create_xwindow (context, width, height);
372
373 /* set application name as a title */
374 gst_xv_image_sink_xwindow_set_title (xvimagesink, xwindow, NULL);
375
376 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
377
378 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
379 xwindow->win);
380
381 return xwindow;
382 }
383
384 static void
gst_xv_image_sink_xwindow_update_geometry(GstXvImageSink * xvimagesink)385 gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
386 {
387 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
388
389 /* Update the window geometry */
390 g_mutex_lock (&xvimagesink->flow_lock);
391 if (G_LIKELY (xvimagesink->xwindow))
392 gst_xwindow_update_geometry (xvimagesink->xwindow);
393 g_mutex_unlock (&xvimagesink->flow_lock);
394 }
395
396 /* This function commits our internal colorbalance settings to our grabbed Xv
397 port. If the context is not initialized yet it simply returns */
398 static void
gst_xv_image_sink_update_colorbalance(GstXvImageSink * xvimagesink)399 gst_xv_image_sink_update_colorbalance (GstXvImageSink * xvimagesink)
400 {
401 GstXvContext *context;
402
403 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
404
405 /* If we haven't initialized the X context we can't update anything */
406 if ((context = xvimagesink->context) == NULL)
407 return;
408
409 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
410 }
411
412 /* This function handles XEvents that might be in the queue. It generates
413 GstEvent that will be sent upstream in the pipeline to handle interactivity
414 and navigation. It will also listen for configure events on the window to
415 trigger caps renegotiation so on the fly software scaling can work. */
416 static void
gst_xv_image_sink_handle_xevents(GstXvImageSink * xvimagesink)417 gst_xv_image_sink_handle_xevents (GstXvImageSink * xvimagesink)
418 {
419 XEvent e;
420 gint pointer_x = 0, pointer_y = 0;
421 gboolean pointer_moved = FALSE;
422 gboolean exposed = FALSE, configured = FALSE;
423
424 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
425
426 /* Handle Interaction, produces navigation events */
427
428 /* We get all pointer motion events, only the last position is
429 interesting. */
430 g_mutex_lock (&xvimagesink->flow_lock);
431 g_mutex_lock (&xvimagesink->context->lock);
432 while (XCheckWindowEvent (xvimagesink->context->disp,
433 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
434 g_mutex_unlock (&xvimagesink->context->lock);
435 g_mutex_unlock (&xvimagesink->flow_lock);
436
437 switch (e.type) {
438 case MotionNotify:
439 pointer_x = e.xmotion.x;
440 pointer_y = e.xmotion.y;
441 pointer_moved = TRUE;
442 break;
443 default:
444 break;
445 }
446 g_mutex_lock (&xvimagesink->flow_lock);
447 g_mutex_lock (&xvimagesink->context->lock);
448 }
449
450 if (pointer_moved) {
451 g_mutex_unlock (&xvimagesink->context->lock);
452 g_mutex_unlock (&xvimagesink->flow_lock);
453
454 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
455 pointer_x, pointer_y);
456 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
457 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
458
459 g_mutex_lock (&xvimagesink->flow_lock);
460 g_mutex_lock (&xvimagesink->context->lock);
461 }
462
463 /* We get all events on our window to throw them upstream */
464 while (XCheckWindowEvent (xvimagesink->context->disp,
465 xvimagesink->xwindow->win,
466 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
467 &e)) {
468 KeySym keysym;
469 const char *key_str = NULL;
470
471 /* We lock only for the X function call */
472 g_mutex_unlock (&xvimagesink->context->lock);
473 g_mutex_unlock (&xvimagesink->flow_lock);
474
475 switch (e.type) {
476 case ButtonPress:
477 /* Mouse button pressed over our window. We send upstream
478 events for interactivity/navigation */
479 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
480 e.xbutton.button, e.xbutton.x, e.xbutton.y);
481 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
482 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
483 break;
484 case ButtonRelease:
485 /* Mouse button released over our window. We send upstream
486 events for interactivity/navigation */
487 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
488 e.xbutton.button, e.xbutton.x, e.xbutton.y);
489 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
490 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
491 break;
492 case KeyPress:
493 case KeyRelease:
494 /* Key pressed/released over our window. We send upstream
495 events for interactivity/navigation */
496 g_mutex_lock (&xvimagesink->context->lock);
497 if (xvimagesink->context->use_xkb) {
498 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
499 e.xkey.keycode, 0, 0);
500 } else {
501 keysym = XKeycodeToKeysym (xvimagesink->context->disp,
502 e.xkey.keycode, 0);
503 }
504 if (keysym != NoSymbol) {
505 key_str = XKeysymToString (keysym);
506 } else {
507 key_str = "unknown";
508 }
509 g_mutex_unlock (&xvimagesink->context->lock);
510 GST_DEBUG_OBJECT (xvimagesink,
511 "key %d pressed over window at %d,%d (%s)",
512 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
513 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
514 e.type == KeyPress ? "key-press" : "key-release", key_str);
515 break;
516 default:
517 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
518 e.type);
519 }
520 g_mutex_lock (&xvimagesink->flow_lock);
521 g_mutex_lock (&xvimagesink->context->lock);
522 }
523
524 /* Handle Expose */
525 while (XCheckWindowEvent (xvimagesink->context->disp,
526 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
527 switch (e.type) {
528 case Expose:
529 exposed = TRUE;
530 break;
531 case ConfigureNotify:
532 g_mutex_unlock (&xvimagesink->context->lock);
533 g_mutex_unlock (&xvimagesink->flow_lock);
534
535 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
536
537 g_mutex_lock (&xvimagesink->flow_lock);
538 g_mutex_lock (&xvimagesink->context->lock);
539 configured = TRUE;
540 break;
541 default:
542 break;
543 }
544 }
545
546 if (xvimagesink->handle_expose && (exposed || configured)) {
547 g_mutex_unlock (&xvimagesink->context->lock);
548 g_mutex_unlock (&xvimagesink->flow_lock);
549
550 gst_xv_image_sink_expose (GST_VIDEO_OVERLAY (xvimagesink));
551
552 g_mutex_lock (&xvimagesink->flow_lock);
553 g_mutex_lock (&xvimagesink->context->lock);
554 }
555
556 /* Handle Display events */
557 while (XPending (xvimagesink->context->disp)) {
558 XNextEvent (xvimagesink->context->disp, &e);
559
560 switch (e.type) {
561 case ClientMessage:{
562 Atom wm_delete;
563
564 wm_delete = XInternAtom (xvimagesink->context->disp,
565 "WM_DELETE_WINDOW", True);
566 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
567 /* Handle window deletion by posting an error on the bus */
568 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
569 ("Output window was closed"), (NULL));
570
571 g_mutex_unlock (&xvimagesink->context->lock);
572 gst_xwindow_destroy (xvimagesink->xwindow);
573 xvimagesink->xwindow = NULL;
574 g_mutex_lock (&xvimagesink->context->lock);
575 }
576 break;
577 }
578 default:
579 break;
580 }
581 }
582
583 g_mutex_unlock (&xvimagesink->context->lock);
584 g_mutex_unlock (&xvimagesink->flow_lock);
585 }
586
587 static gpointer
gst_xv_image_sink_event_thread(GstXvImageSink * xvimagesink)588 gst_xv_image_sink_event_thread (GstXvImageSink * xvimagesink)
589 {
590 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
591
592 GST_OBJECT_LOCK (xvimagesink);
593 while (xvimagesink->running) {
594 GST_OBJECT_UNLOCK (xvimagesink);
595
596 if (xvimagesink->xwindow) {
597 gst_xv_image_sink_handle_xevents (xvimagesink);
598 }
599 /* FIXME: do we want to align this with the framerate or anything else? */
600 g_usleep (G_USEC_PER_SEC / 20);
601
602 GST_OBJECT_LOCK (xvimagesink);
603 }
604 GST_OBJECT_UNLOCK (xvimagesink);
605
606 return NULL;
607 }
608
609 static void
gst_xv_image_sink_manage_event_thread(GstXvImageSink * xvimagesink)610 gst_xv_image_sink_manage_event_thread (GstXvImageSink * xvimagesink)
611 {
612 GThread *thread = NULL;
613
614 /* don't start the thread too early */
615 if (xvimagesink->context == NULL) {
616 return;
617 }
618
619 GST_OBJECT_LOCK (xvimagesink);
620 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
621 if (!xvimagesink->event_thread) {
622 /* Setup our event listening thread */
623 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
624 xvimagesink->handle_expose, xvimagesink->handle_events);
625 xvimagesink->running = TRUE;
626 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
627 (GThreadFunc) gst_xv_image_sink_event_thread, xvimagesink, NULL);
628 }
629 } else {
630 if (xvimagesink->event_thread) {
631 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
632 xvimagesink->handle_expose, xvimagesink->handle_events);
633 xvimagesink->running = FALSE;
634 /* grab thread and mark it as NULL */
635 thread = xvimagesink->event_thread;
636 xvimagesink->event_thread = NULL;
637 }
638 }
639 GST_OBJECT_UNLOCK (xvimagesink);
640
641 /* Wait for our event thread to finish */
642 if (thread)
643 g_thread_join (thread);
644
645 }
646
647 /* Element stuff */
648
649 static GstCaps *
gst_xv_image_sink_getcaps(GstBaseSink * bsink,GstCaps * filter)650 gst_xv_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
651 {
652 GstXvImageSink *xvimagesink;
653 GstCaps *caps;
654
655 xvimagesink = GST_XV_IMAGE_SINK (bsink);
656
657 if (xvimagesink->context) {
658 if (filter)
659 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
660 GST_CAPS_INTERSECT_FIRST);
661 else
662 return gst_caps_ref (xvimagesink->context->caps);
663 }
664
665 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
666 if (filter) {
667 GstCaps *intersection;
668
669 intersection =
670 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
671 gst_caps_unref (caps);
672 caps = intersection;
673 }
674 return caps;
675 }
676
677 static GstBufferPool *
gst_xv_image_sink_create_pool(GstXvImageSink * xvimagesink,GstCaps * caps,gsize size,gint min)678 gst_xv_image_sink_create_pool (GstXvImageSink * xvimagesink, GstCaps * caps,
679 gsize size, gint min)
680 {
681 GstBufferPool *pool;
682 GstStructure *config;
683
684 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
685
686 config = gst_buffer_pool_get_config (pool);
687 gst_buffer_pool_config_set_params (config, caps, size, min, 0);
688
689 if (!gst_buffer_pool_set_config (pool, config))
690 goto config_failed;
691
692 return pool;
693
694 config_failed:
695 {
696 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
697 gst_object_unref (pool);
698 return NULL;
699 }
700 }
701
702 static gboolean
gst_xv_image_sink_setcaps(GstBaseSink * bsink,GstCaps * caps)703 gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
704 {
705 GstXvImageSink *xvimagesink;
706 GstXvContext *context;
707 GstBufferPool *oldpool;
708 GstVideoInfo info;
709 guint32 im_format = 0;
710 gint video_par_n, video_par_d; /* video's PAR */
711 gint display_par_n, display_par_d; /* display's PAR */
712 guint num, den;
713
714 xvimagesink = GST_XV_IMAGE_SINK (bsink);
715 context = xvimagesink->context;
716
717 GST_DEBUG_OBJECT (xvimagesink,
718 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
719 GST_PTR_FORMAT, context->caps, caps);
720
721 if (!gst_caps_can_intersect (context->caps, caps))
722 goto incompatible_caps;
723
724 if (!gst_video_info_from_caps (&info, caps))
725 goto invalid_format;
726
727 xvimagesink->fps_n = info.fps_n;
728 xvimagesink->fps_d = info.fps_d;
729
730 xvimagesink->video_width = info.width;
731 xvimagesink->video_height = info.height;
732
733 im_format = gst_xvcontext_get_format_from_info (context, &info);
734 if (im_format == -1)
735 goto invalid_format;
736
737 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
738
739 /* get aspect ratio from caps if it's present, and
740 * convert video width and height to a display width and height
741 * using wd / hd = wv / hv * PARv / PARd */
742
743 /* get video's PAR */
744 video_par_n = info.par_n;
745 video_par_d = info.par_d;
746
747 /* get display's PAR */
748 if (xvimagesink->par) {
749 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
750 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
751 } else {
752 display_par_n = 1;
753 display_par_d = 1;
754 }
755
756 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
757 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
758 goto no_disp_ratio;
759
760 GST_DEBUG_OBJECT (xvimagesink,
761 "video width/height: %dx%d, calculated display ratio: %d/%d",
762 info.width, info.height, num, den);
763
764 /* now find a width x height that respects this display ratio.
765 * prefer those that have one of w/h the same as the incoming video
766 * using wd / hd = num / den */
767
768 /* start with same height, because of interlaced video */
769 /* check hd / den is an integer scale factor, and scale wd with the PAR */
770 if (info.height % den == 0) {
771 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
772 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
773 gst_util_uint64_scale_int (info.height, num, den);
774 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
775 } else if (info.width % num == 0) {
776 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
777 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
778 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
779 gst_util_uint64_scale_int (info.width, den, num);
780 } else {
781 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
782 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
783 gst_util_uint64_scale_int (info.height, num, den);
784 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
785 }
786 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
787 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
788
789 /* Notify application to set xwindow id now */
790 g_mutex_lock (&xvimagesink->flow_lock);
791 if (!xvimagesink->xwindow) {
792 g_mutex_unlock (&xvimagesink->flow_lock);
793 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
794 } else {
795 g_mutex_unlock (&xvimagesink->flow_lock);
796 }
797
798 /* Creating our window and our image with the display size in pixels */
799 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
800 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
801 goto no_display_size;
802
803 g_mutex_lock (&xvimagesink->flow_lock);
804 if (!xvimagesink->xwindow) {
805 xvimagesink->xwindow = gst_xv_image_sink_xwindow_new (xvimagesink,
806 GST_VIDEO_SINK_WIDTH (xvimagesink),
807 GST_VIDEO_SINK_HEIGHT (xvimagesink));
808 }
809
810 if (xvimagesink->pending_render_rect) {
811 xvimagesink->pending_render_rect = FALSE;
812 gst_xwindow_set_render_rectangle (xvimagesink->xwindow,
813 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
814 xvimagesink->render_rect.w, xvimagesink->render_rect.h);
815 }
816
817 xvimagesink->info = info;
818
819 /* After a resize, we want to redraw the borders in case the new frame size
820 * doesn't cover the same area */
821 xvimagesink->redraw_border = TRUE;
822
823 /* destroy current pool */
824 oldpool = xvimagesink->pool;
825 xvimagesink->pool = NULL;
826 g_mutex_unlock (&xvimagesink->flow_lock);
827
828 /* deactivate and unref the old internal pool */
829 if (oldpool) {
830 gst_buffer_pool_set_active (oldpool, FALSE);
831 gst_object_unref (oldpool);
832 }
833
834 return TRUE;
835
836 /* ERRORS */
837 incompatible_caps:
838 {
839 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
840 return FALSE;
841 }
842 invalid_format:
843 {
844 GST_DEBUG_OBJECT (xvimagesink,
845 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
846 return FALSE;
847 }
848 no_disp_ratio:
849 {
850 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
851 ("Error calculating the output display ratio of the video."));
852 return FALSE;
853 }
854 no_display_size:
855 {
856 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
857 ("Error calculating the output display size of the video."));
858 return FALSE;
859 }
860 }
861
862 static GstStateChangeReturn
gst_xv_image_sink_change_state(GstElement * element,GstStateChange transition)863 gst_xv_image_sink_change_state (GstElement * element, GstStateChange transition)
864 {
865 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
866 GstXvImageSink *xvimagesink;
867
868 xvimagesink = GST_XV_IMAGE_SINK (element);
869
870 switch (transition) {
871 case GST_STATE_CHANGE_NULL_TO_READY:
872 if (!gst_xv_image_sink_open (xvimagesink))
873 goto error;
874 break;
875 case GST_STATE_CHANGE_READY_TO_PAUSED:
876 break;
877 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
878 break;
879 case GST_STATE_CHANGE_PAUSED_TO_READY:
880 break;
881 default:
882 break;
883 }
884
885 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
886
887 switch (transition) {
888 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
889 break;
890 case GST_STATE_CHANGE_PAUSED_TO_READY:
891 xvimagesink->fps_n = 0;
892 xvimagesink->fps_d = 1;
893 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
894 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
895 g_mutex_lock (&xvimagesink->flow_lock);
896 if (xvimagesink->pool)
897 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
898 g_mutex_unlock (&xvimagesink->flow_lock);
899 break;
900 case GST_STATE_CHANGE_READY_TO_NULL:
901 gst_xv_image_sink_close (xvimagesink);
902 break;
903 default:
904 break;
905 }
906 return ret;
907
908 error:
909 {
910 return GST_STATE_CHANGE_FAILURE;
911 }
912 }
913
914 static void
gst_xv_image_sink_get_times(GstBaseSink * bsink,GstBuffer * buf,GstClockTime * start,GstClockTime * end)915 gst_xv_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
916 GstClockTime * start, GstClockTime * end)
917 {
918 GstXvImageSink *xvimagesink;
919
920 xvimagesink = GST_XV_IMAGE_SINK (bsink);
921
922 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
923 *start = GST_BUFFER_TIMESTAMP (buf);
924 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
925 *end = *start + GST_BUFFER_DURATION (buf);
926 } else {
927 if (xvimagesink->fps_n > 0) {
928 *end = *start +
929 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
930 xvimagesink->fps_n);
931 }
932 }
933 }
934 }
935
936 static GstFlowReturn
gst_xv_image_sink_show_frame(GstVideoSink * vsink,GstBuffer * buf)937 gst_xv_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
938 {
939 GstFlowReturn res;
940 GstXvImageSink *xvimagesink;
941 GstBuffer *to_put = NULL;
942 GstMemory *mem;
943
944 xvimagesink = GST_XV_IMAGE_SINK (vsink);
945
946 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
947 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
948 /* If this buffer has been allocated using our buffer management we simply
949 put the ximage which is in the PRIVATE pointer */
950 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
951 buf);
952 to_put = buf;
953 res = GST_FLOW_OK;
954 } else {
955 GstVideoFrame src, dest;
956 GstBufferPoolAcquireParams params = { 0, };
957 GstVideoCropMeta *crop_meta;
958
959 /* Else we have to copy the data into our private image, */
960 /* if we have one... */
961 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
962
963 if (xvimagesink->pool == NULL) {
964 GstCaps *caps = gst_video_info_to_caps (&xvimagesink->info);
965
966 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
967 xvimagesink->pool = gst_xv_image_sink_create_pool (xvimagesink, caps,
968 xvimagesink->info.size, 2);
969 if (xvimagesink->pool == NULL)
970 goto no_pool;
971
972 gst_caps_unref (caps);
973 }
974
975 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
976 goto activate_failed;
977
978 /* take a buffer from our pool, if there is no buffer in the pool something
979 * is seriously wrong, waiting for the pool here might deadlock when we try
980 * to go to PAUSED because we never flush the pool then. */
981 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
982 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
983 if (res != GST_FLOW_OK)
984 goto no_buffer;
985
986 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
987 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
988
989 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
990 goto invalid_buffer;
991
992 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
993 gst_video_frame_unmap (&src);
994 goto invalid_buffer;
995 }
996
997 gst_video_frame_copy (&dest, &src);
998
999 gst_video_frame_unmap (&dest);
1000 gst_video_frame_unmap (&src);
1001
1002 if ((crop_meta = gst_buffer_get_video_crop_meta (buf))) {
1003 GstVideoCropMeta *dmeta = gst_buffer_add_video_crop_meta (to_put);
1004
1005 dmeta->x = crop_meta->x;
1006 dmeta->y = crop_meta->y;
1007 dmeta->width = crop_meta->width;
1008 dmeta->height = crop_meta->height;
1009 }
1010 }
1011
1012 if (!gst_xv_image_sink_xvimage_put (xvimagesink, to_put))
1013 goto no_window;
1014
1015 done:
1016 if (to_put != buf)
1017 gst_buffer_unref (to_put);
1018
1019 return res;
1020
1021 /* ERRORS */
1022 no_pool:
1023 {
1024 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1025 ("Internal error: can't allocate images"),
1026 ("We don't have a bufferpool negotiated"));
1027 return GST_FLOW_ERROR;
1028 }
1029 no_buffer:
1030 {
1031 /* No image available. That's very bad ! */
1032 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1033 return GST_FLOW_OK;
1034 }
1035 invalid_buffer:
1036 {
1037 /* No Window available to put our image into */
1038 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1039 res = GST_FLOW_OK;
1040 goto done;
1041 }
1042 no_window:
1043 {
1044 /* No Window available to put our image into */
1045 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1046 res = GST_FLOW_ERROR;
1047 goto done;
1048 }
1049 activate_failed:
1050 {
1051 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1052 res = GST_FLOW_ERROR;
1053 goto done;
1054 }
1055 }
1056
1057 static gboolean
gst_xv_image_sink_event(GstBaseSink * sink,GstEvent * event)1058 gst_xv_image_sink_event (GstBaseSink * sink, GstEvent * event)
1059 {
1060 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (sink);
1061
1062 switch (GST_EVENT_TYPE (event)) {
1063 case GST_EVENT_TAG:{
1064 GstTagList *l;
1065 gchar *title = NULL;
1066
1067 gst_event_parse_tag (event, &l);
1068 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1069
1070 if (title) {
1071 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1072 gst_xv_image_sink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1073 title);
1074
1075 g_free (title);
1076 }
1077 break;
1078 }
1079 default:
1080 break;
1081 }
1082 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1083 }
1084
1085 static gboolean
gst_xv_image_sink_propose_allocation(GstBaseSink * bsink,GstQuery * query)1086 gst_xv_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1087 {
1088 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (bsink);
1089 GstBufferPool *pool = NULL;
1090 GstCaps *caps;
1091 GstVideoInfo info;
1092 guint size;
1093 gboolean need_pool;
1094
1095 gst_query_parse_allocation (query, &caps, &need_pool);
1096
1097 if (caps == NULL)
1098 goto no_caps;
1099
1100 if (!gst_video_info_from_caps (&info, caps))
1101 goto invalid_caps;
1102
1103 /* the normal size of a frame */
1104 size = info.size;
1105
1106 if (need_pool) {
1107 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1108 pool = gst_xv_image_sink_create_pool (xvimagesink, caps, info.size, 0);
1109
1110 if (pool == NULL)
1111 goto no_pool;
1112 }
1113
1114 /* update info since allocation frame's wxh might differ from the
1115 * negotiation ones */
1116 xvimagesink->info = info;
1117
1118 /* we need at least 2 buffer because we hold on to the last one */
1119 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1120 if (pool)
1121 gst_object_unref (pool);
1122
1123 /* we also support various metadata */
1124 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1125 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1126
1127 return TRUE;
1128
1129 /* ERRORS */
1130 no_caps:
1131 {
1132 GST_DEBUG_OBJECT (bsink, "no caps specified");
1133 return FALSE;
1134 }
1135 invalid_caps:
1136 {
1137 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1138 return FALSE;
1139 }
1140 no_pool:
1141 {
1142 /* Already warned in create_pool */
1143 return FALSE;
1144 }
1145 }
1146
1147 /* Interfaces stuff */
1148 static void
gst_xv_image_sink_navigation_send_event(GstNavigation * navigation,GstStructure * structure)1149 gst_xv_image_sink_navigation_send_event (GstNavigation * navigation,
1150 GstStructure * structure)
1151 {
1152 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (navigation);
1153 gboolean handled = FALSE;
1154 GstEvent *event = NULL;
1155
1156 GstVideoRectangle src = { 0, };
1157 GstVideoRectangle dst = { 0, };
1158 GstVideoRectangle result;
1159 gdouble x, y, xscale = 1.0, yscale = 1.0;
1160 GstXWindow *xwindow;
1161
1162 /* We take the flow_lock while we look at the window */
1163 g_mutex_lock (&xvimagesink->flow_lock);
1164
1165 if (!(xwindow = xvimagesink->xwindow)) {
1166 g_mutex_unlock (&xvimagesink->flow_lock);
1167 gst_structure_free (structure);
1168 return;
1169 }
1170
1171 if (xvimagesink->keep_aspect) {
1172 /* We get the frame position using the calculated geometry from _setcaps
1173 that respect pixel aspect ratios */
1174 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1175 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1176 dst.w = xwindow->render_rect.w;
1177 dst.h = xwindow->render_rect.h;
1178
1179 gst_video_sink_center_rect (src, dst, &result, TRUE);
1180 result.x += xwindow->render_rect.x;
1181 result.y += xwindow->render_rect.y;
1182 } else {
1183 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1184 }
1185
1186 g_mutex_unlock (&xvimagesink->flow_lock);
1187
1188 /* We calculate scaling using the original video frames geometry to include
1189 pixel aspect ratio scaling. */
1190 xscale = (gdouble) xvimagesink->video_width / result.w;
1191 yscale = (gdouble) xvimagesink->video_height / result.h;
1192
1193 /* Converting pointer coordinates to the non scaled geometry */
1194 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1195 x = MIN (x, result.x + result.w);
1196 x = MAX (x - result.x, 0);
1197 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1198 (gdouble) x * xscale, NULL);
1199 }
1200 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1201 y = MIN (y, result.y + result.h);
1202 y = MAX (y - result.y, 0);
1203 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1204 (gdouble) y * yscale, NULL);
1205 }
1206
1207 event = gst_event_new_navigation (structure);
1208 if (event) {
1209 gst_event_ref (event);
1210 handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (xvimagesink), event);
1211
1212 if (!handled)
1213 gst_element_post_message ((GstElement *) xvimagesink,
1214 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1215
1216 gst_event_unref (event);
1217 }
1218 }
1219
1220 static void
gst_xv_image_sink_navigation_init(GstNavigationInterface * iface)1221 gst_xv_image_sink_navigation_init (GstNavigationInterface * iface)
1222 {
1223 iface->send_event = gst_xv_image_sink_navigation_send_event;
1224 }
1225
1226 static void
gst_xv_image_sink_set_window_handle(GstVideoOverlay * overlay,guintptr id)1227 gst_xv_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1228 {
1229 XID xwindow_id = id;
1230 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1231 GstXWindow *xwindow = NULL;
1232 GstXvContext *context;
1233
1234 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1235
1236 g_mutex_lock (&xvimagesink->flow_lock);
1237
1238 /* If we already use that window return */
1239 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1240 g_mutex_unlock (&xvimagesink->flow_lock);
1241 return;
1242 }
1243
1244 /* If the element has not initialized the X11 context try to do so */
1245 if (!xvimagesink->context &&
1246 !(xvimagesink->context =
1247 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1248 g_mutex_unlock (&xvimagesink->flow_lock);
1249 /* we have thrown a GST_ELEMENT_ERROR now */
1250 return;
1251 }
1252
1253 context = xvimagesink->context;
1254
1255 gst_xv_image_sink_update_colorbalance (xvimagesink);
1256
1257 /* If a window is there already we destroy it */
1258 if (xvimagesink->xwindow) {
1259 gst_xwindow_destroy (xvimagesink->xwindow);
1260 xvimagesink->xwindow = NULL;
1261 }
1262
1263 /* If the xid is 0 we go back to an internal window */
1264 if (xwindow_id == 0) {
1265 /* If no width/height caps nego did not happen window will be created
1266 during caps nego then */
1267 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1268 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1269 xwindow =
1270 gst_xv_image_sink_xwindow_new (xvimagesink,
1271 GST_VIDEO_SINK_WIDTH (xvimagesink),
1272 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1273 }
1274 } else {
1275 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1276 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1277 }
1278
1279 if (xwindow)
1280 xvimagesink->xwindow = xwindow;
1281
1282 g_mutex_unlock (&xvimagesink->flow_lock);
1283 }
1284
1285 static void
gst_xv_image_sink_expose(GstVideoOverlay * overlay)1286 gst_xv_image_sink_expose (GstVideoOverlay * overlay)
1287 {
1288 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1289
1290 GST_DEBUG ("doing expose");
1291 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
1292 gst_xv_image_sink_xvimage_put (xvimagesink, NULL);
1293 }
1294
1295 static void
gst_xv_image_sink_set_event_handling(GstVideoOverlay * overlay,gboolean handle_events)1296 gst_xv_image_sink_set_event_handling (GstVideoOverlay * overlay,
1297 gboolean handle_events)
1298 {
1299 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1300
1301 g_mutex_lock (&xvimagesink->flow_lock);
1302 xvimagesink->handle_events = handle_events;
1303 if (G_LIKELY (xvimagesink->xwindow))
1304 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1305 g_mutex_unlock (&xvimagesink->flow_lock);
1306 }
1307
1308 static void
gst_xv_image_sink_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)1309 gst_xv_image_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
1310 gint y, gint width, gint height)
1311 {
1312 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1313
1314 g_mutex_lock (&xvimagesink->flow_lock);
1315 if (G_LIKELY (xvimagesink->xwindow)) {
1316 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1317 height);
1318 } else {
1319 xvimagesink->render_rect.x = x;
1320 xvimagesink->render_rect.y = y;
1321 xvimagesink->render_rect.w = width;
1322 xvimagesink->render_rect.h = height;
1323 xvimagesink->pending_render_rect = TRUE;
1324 }
1325 g_mutex_unlock (&xvimagesink->flow_lock);
1326 }
1327
1328 static void
gst_xv_image_sink_video_overlay_init(GstVideoOverlayInterface * iface)1329 gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1330 {
1331 iface->set_window_handle = gst_xv_image_sink_set_window_handle;
1332 iface->expose = gst_xv_image_sink_expose;
1333 iface->handle_events = gst_xv_image_sink_set_event_handling;
1334 iface->set_render_rectangle = gst_xv_image_sink_set_render_rectangle;
1335 }
1336
1337 static const GList *
gst_xv_image_sink_colorbalance_list_channels(GstColorBalance * balance)1338 gst_xv_image_sink_colorbalance_list_channels (GstColorBalance * balance)
1339 {
1340 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1341
1342 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
1343
1344 if (xvimagesink->context)
1345 return xvimagesink->context->channels_list;
1346 else
1347 return NULL;
1348 }
1349
1350 static void
gst_xv_image_sink_colorbalance_set_value(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value)1351 gst_xv_image_sink_colorbalance_set_value (GstColorBalance * balance,
1352 GstColorBalanceChannel * channel, gint value)
1353 {
1354 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1355
1356 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1357 g_return_if_fail (channel->label != NULL);
1358
1359 xvimagesink->config.cb_changed = TRUE;
1360
1361 /* Normalize val to [-1000, 1000] */
1362 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1363 (double) (channel->max_value - channel->min_value));
1364
1365 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1366 xvimagesink->config.hue = value;
1367 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1368 xvimagesink->config.saturation = value;
1369 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1370 xvimagesink->config.contrast = value;
1371 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1372 xvimagesink->config.brightness = value;
1373 } else {
1374 g_warning ("got an unknown channel %s", channel->label);
1375 return;
1376 }
1377
1378 gst_xv_image_sink_update_colorbalance (xvimagesink);
1379 }
1380
1381 static gint
gst_xv_image_sink_colorbalance_get_value(GstColorBalance * balance,GstColorBalanceChannel * channel)1382 gst_xv_image_sink_colorbalance_get_value (GstColorBalance * balance,
1383 GstColorBalanceChannel * channel)
1384 {
1385 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1386 gint value = 0;
1387
1388 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), 0);
1389 g_return_val_if_fail (channel->label != NULL, 0);
1390
1391 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1392 value = xvimagesink->config.hue;
1393 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1394 value = xvimagesink->config.saturation;
1395 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1396 value = xvimagesink->config.contrast;
1397 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1398 value = xvimagesink->config.brightness;
1399 } else {
1400 g_warning ("got an unknown channel %s", channel->label);
1401 }
1402
1403 /* Normalize val to [channel->min_value, channel->max_value] */
1404 value = channel->min_value + (channel->max_value - channel->min_value) *
1405 (value + 1000) / 2000;
1406
1407 return value;
1408 }
1409
1410 static GstColorBalanceType
gst_xv_image_sink_colorbalance_get_balance_type(GstColorBalance * balance)1411 gst_xv_image_sink_colorbalance_get_balance_type (GstColorBalance * balance)
1412 {
1413 return GST_COLOR_BALANCE_HARDWARE;
1414 }
1415
1416 static void
gst_xv_image_sink_colorbalance_init(GstColorBalanceInterface * iface)1417 gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface * iface)
1418 {
1419 iface->list_channels = gst_xv_image_sink_colorbalance_list_channels;
1420 iface->set_value = gst_xv_image_sink_colorbalance_set_value;
1421 iface->get_value = gst_xv_image_sink_colorbalance_get_value;
1422 iface->get_balance_type = gst_xv_image_sink_colorbalance_get_balance_type;
1423 }
1424
1425 #if 0
1426 static const GList *
1427 gst_xv_image_sink_probe_get_properties (GstPropertyProbe * probe)
1428 {
1429 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1430 static GList *list = NULL;
1431
1432 if (!list) {
1433 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1434 list =
1435 g_list_append (list, g_object_class_find_property (klass,
1436 "autopaint-colorkey"));
1437 list =
1438 g_list_append (list, g_object_class_find_property (klass,
1439 "double-buffer"));
1440 list =
1441 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1442 }
1443
1444 return list;
1445 }
1446
1447 static void
1448 gst_xv_image_sink_probe_probe_property (GstPropertyProbe * probe,
1449 guint prop_id, const GParamSpec * pspec)
1450 {
1451 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1452
1453 switch (prop_id) {
1454 case PROP_DEVICE:
1455 case PROP_AUTOPAINT_COLORKEY:
1456 case PROP_DOUBLE_BUFFER:
1457 case PROP_COLORKEY:
1458 GST_DEBUG_OBJECT (xvimagesink,
1459 "probing device list and get capabilities");
1460 if (!xvimagesink->context) {
1461 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1462 xvimagesink->context = gst_xv_image_sink_context_get (xvimagesink);
1463 }
1464 break;
1465 default:
1466 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1467 break;
1468 }
1469 }
1470
1471 static gboolean
1472 gst_xv_image_sink_probe_needs_probe (GstPropertyProbe * probe,
1473 guint prop_id, const GParamSpec * pspec)
1474 {
1475 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1476 gboolean ret = FALSE;
1477
1478 switch (prop_id) {
1479 case PROP_DEVICE:
1480 case PROP_AUTOPAINT_COLORKEY:
1481 case PROP_DOUBLE_BUFFER:
1482 case PROP_COLORKEY:
1483 if (xvimagesink->context != NULL) {
1484 ret = FALSE;
1485 } else {
1486 ret = TRUE;
1487 }
1488 break;
1489 default:
1490 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1491 break;
1492 }
1493
1494 return ret;
1495 }
1496
1497 static GValueArray *
1498 gst_xv_image_sink_probe_get_values (GstPropertyProbe * probe,
1499 guint prop_id, const GParamSpec * pspec)
1500 {
1501 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1502 GValueArray *array = NULL;
1503
1504 if (G_UNLIKELY (!xvimagesink->context)) {
1505 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1506 "get values");
1507 goto beach;
1508 }
1509
1510 switch (prop_id) {
1511 case PROP_DEVICE:
1512 {
1513 guint i;
1514 GValue value = { 0 };
1515
1516 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1517 g_value_init (&value, G_TYPE_STRING);
1518
1519 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1520 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1521
1522 g_value_set_string (&value, adaptor_id_s);
1523 g_value_array_append (array, &value);
1524 g_free (adaptor_id_s);
1525 }
1526 g_value_unset (&value);
1527 break;
1528 }
1529 case PROP_AUTOPAINT_COLORKEY:
1530 if (xvimagesink->have_autopaint_colorkey) {
1531 GValue value = { 0 };
1532
1533 array = g_value_array_new (2);
1534 g_value_init (&value, G_TYPE_BOOLEAN);
1535 g_value_set_boolean (&value, FALSE);
1536 g_value_array_append (array, &value);
1537 g_value_set_boolean (&value, TRUE);
1538 g_value_array_append (array, &value);
1539 g_value_unset (&value);
1540 }
1541 break;
1542 case PROP_DOUBLE_BUFFER:
1543 if (xvimagesink->have_double_buffer) {
1544 GValue value = { 0 };
1545
1546 array = g_value_array_new (2);
1547 g_value_init (&value, G_TYPE_BOOLEAN);
1548 g_value_set_boolean (&value, FALSE);
1549 g_value_array_append (array, &value);
1550 g_value_set_boolean (&value, TRUE);
1551 g_value_array_append (array, &value);
1552 g_value_unset (&value);
1553 }
1554 break;
1555 case PROP_COLORKEY:
1556 if (xvimagesink->have_colorkey) {
1557 GValue value = { 0 };
1558
1559 array = g_value_array_new (1);
1560 g_value_init (&value, GST_TYPE_INT_RANGE);
1561 gst_value_set_int_range (&value, 0, 0xffffff);
1562 g_value_array_append (array, &value);
1563 g_value_unset (&value);
1564 }
1565 break;
1566 default:
1567 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1568 break;
1569 }
1570
1571 beach:
1572 return array;
1573 }
1574
1575 static void
1576 gst_xv_image_sink_property_probe_interface_init (GstPropertyProbeInterface *
1577 iface)
1578 {
1579 iface->get_properties = gst_xv_image_sink_probe_get_properties;
1580 iface->probe_property = gst_xv_image_sink_probe_probe_property;
1581 iface->needs_probe = gst_xv_image_sink_probe_needs_probe;
1582 iface->get_values = gst_xv_image_sink_probe_get_values;
1583 }
1584 #endif
1585
1586 /* =========================================== */
1587 /* */
1588 /* Init & Class init */
1589 /* */
1590 /* =========================================== */
1591
1592 static void
gst_xv_image_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1593 gst_xv_image_sink_set_property (GObject * object, guint prop_id,
1594 const GValue * value, GParamSpec * pspec)
1595 {
1596 GstXvImageSink *xvimagesink;
1597
1598 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1599
1600 xvimagesink = GST_XV_IMAGE_SINK (object);
1601
1602 switch (prop_id) {
1603 case PROP_HUE:
1604 xvimagesink->config.hue = g_value_get_int (value);
1605 xvimagesink->config.cb_changed = TRUE;
1606 gst_xv_image_sink_update_colorbalance (xvimagesink);
1607 break;
1608 case PROP_CONTRAST:
1609 xvimagesink->config.contrast = g_value_get_int (value);
1610 xvimagesink->config.cb_changed = TRUE;
1611 gst_xv_image_sink_update_colorbalance (xvimagesink);
1612 break;
1613 case PROP_BRIGHTNESS:
1614 xvimagesink->config.brightness = g_value_get_int (value);
1615 xvimagesink->config.cb_changed = TRUE;
1616 gst_xv_image_sink_update_colorbalance (xvimagesink);
1617 break;
1618 case PROP_SATURATION:
1619 xvimagesink->config.saturation = g_value_get_int (value);
1620 xvimagesink->config.cb_changed = TRUE;
1621 gst_xv_image_sink_update_colorbalance (xvimagesink);
1622 break;
1623 case PROP_DISPLAY:
1624 g_free (xvimagesink->config.display_name);
1625 xvimagesink->config.display_name = g_value_dup_string (value);
1626 break;
1627 case PROP_SYNCHRONOUS:
1628 xvimagesink->synchronous = g_value_get_boolean (value);
1629 if (xvimagesink->context) {
1630 gst_xvcontext_set_synchronous (xvimagesink->context,
1631 xvimagesink->synchronous);
1632 }
1633 break;
1634 case PROP_PIXEL_ASPECT_RATIO:
1635 g_free (xvimagesink->par);
1636 xvimagesink->par = g_new0 (GValue, 1);
1637 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1638 if (!g_value_transform (value, xvimagesink->par)) {
1639 g_warning ("Could not transform string to aspect ratio");
1640 gst_value_set_fraction (xvimagesink->par, 1, 1);
1641 }
1642 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1643 gst_value_get_fraction_numerator (xvimagesink->par),
1644 gst_value_get_fraction_denominator (xvimagesink->par));
1645 break;
1646 case PROP_FORCE_ASPECT_RATIO:
1647 xvimagesink->keep_aspect = g_value_get_boolean (value);
1648 break;
1649 case PROP_HANDLE_EVENTS:
1650 gst_xv_image_sink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1651 g_value_get_boolean (value));
1652 gst_xv_image_sink_manage_event_thread (xvimagesink);
1653 break;
1654 case PROP_DEVICE:
1655 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1656 break;
1657 case PROP_HANDLE_EXPOSE:
1658 xvimagesink->handle_expose = g_value_get_boolean (value);
1659 gst_xv_image_sink_manage_event_thread (xvimagesink);
1660 break;
1661 case PROP_DOUBLE_BUFFER:
1662 xvimagesink->double_buffer = g_value_get_boolean (value);
1663 break;
1664 case PROP_AUTOPAINT_COLORKEY:
1665 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1666 break;
1667 case PROP_COLORKEY:
1668 xvimagesink->config.colorkey = g_value_get_int (value);
1669 break;
1670 case PROP_DRAW_BORDERS:
1671 xvimagesink->draw_borders = g_value_get_boolean (value);
1672 break;
1673 default:
1674 if (!gst_video_overlay_set_property (object, PROP_LAST, prop_id, value))
1675 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1676 break;
1677 }
1678 }
1679
1680 static void
gst_xv_image_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1681 gst_xv_image_sink_get_property (GObject * object, guint prop_id,
1682 GValue * value, GParamSpec * pspec)
1683 {
1684 GstXvImageSink *xvimagesink;
1685
1686 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1687
1688 xvimagesink = GST_XV_IMAGE_SINK (object);
1689
1690 switch (prop_id) {
1691 case PROP_HUE:
1692 g_value_set_int (value, xvimagesink->config.hue);
1693 break;
1694 case PROP_CONTRAST:
1695 g_value_set_int (value, xvimagesink->config.contrast);
1696 break;
1697 case PROP_BRIGHTNESS:
1698 g_value_set_int (value, xvimagesink->config.brightness);
1699 break;
1700 case PROP_SATURATION:
1701 g_value_set_int (value, xvimagesink->config.saturation);
1702 break;
1703 case PROP_DISPLAY:
1704 g_value_set_string (value, xvimagesink->config.display_name);
1705 break;
1706 case PROP_SYNCHRONOUS:
1707 g_value_set_boolean (value, xvimagesink->synchronous);
1708 break;
1709 case PROP_PIXEL_ASPECT_RATIO:
1710 if (xvimagesink->par)
1711 g_value_transform (xvimagesink->par, value);
1712 break;
1713 case PROP_FORCE_ASPECT_RATIO:
1714 g_value_set_boolean (value, xvimagesink->keep_aspect);
1715 break;
1716 case PROP_HANDLE_EVENTS:
1717 g_value_set_boolean (value, xvimagesink->handle_events);
1718 break;
1719 case PROP_DEVICE:
1720 {
1721 char *adaptor_nr_s =
1722 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1723
1724 g_value_set_string (value, adaptor_nr_s);
1725 g_free (adaptor_nr_s);
1726 break;
1727 }
1728 case PROP_DEVICE_NAME:
1729 if (xvimagesink->context && xvimagesink->context->adaptors) {
1730 g_value_set_string (value,
1731 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1732 } else {
1733 g_value_set_string (value, NULL);
1734 }
1735 break;
1736 case PROP_HANDLE_EXPOSE:
1737 g_value_set_boolean (value, xvimagesink->handle_expose);
1738 break;
1739 case PROP_DOUBLE_BUFFER:
1740 g_value_set_boolean (value, xvimagesink->double_buffer);
1741 break;
1742 case PROP_AUTOPAINT_COLORKEY:
1743 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1744 break;
1745 case PROP_COLORKEY:
1746 g_value_set_int (value, xvimagesink->config.colorkey);
1747 break;
1748 case PROP_DRAW_BORDERS:
1749 g_value_set_boolean (value, xvimagesink->draw_borders);
1750 break;
1751 case PROP_WINDOW_WIDTH:
1752 if (xvimagesink->xwindow)
1753 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1754 else
1755 g_value_set_uint64 (value, 0);
1756 break;
1757 case PROP_WINDOW_HEIGHT:
1758 if (xvimagesink->xwindow)
1759 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1760 else
1761 g_value_set_uint64 (value, 0);
1762 break;
1763 default:
1764 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1765 break;
1766 }
1767 }
1768
1769 static gboolean
gst_xv_image_sink_open(GstXvImageSink * xvimagesink)1770 gst_xv_image_sink_open (GstXvImageSink * xvimagesink)
1771 {
1772 GError *error = NULL;
1773
1774 /* Initializing the XvContext unless already done through GstVideoOverlay */
1775 if (!xvimagesink->context) {
1776 GstXvContext *context;
1777 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1778 goto no_context;
1779
1780 GST_OBJECT_LOCK (xvimagesink);
1781 xvimagesink->context = context;
1782 } else
1783 GST_OBJECT_LOCK (xvimagesink);
1784 /* make an allocator for this context */
1785 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1786 GST_OBJECT_UNLOCK (xvimagesink);
1787
1788 /* update object's par with calculated one if not set yet */
1789 if (!xvimagesink->par) {
1790 xvimagesink->par = g_new0 (GValue, 1);
1791 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1792 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1793 }
1794 /* call XSynchronize with the current value of synchronous */
1795 gst_xvcontext_set_synchronous (xvimagesink->context,
1796 xvimagesink->synchronous);
1797 gst_xv_image_sink_update_colorbalance (xvimagesink);
1798 gst_xv_image_sink_manage_event_thread (xvimagesink);
1799
1800 return TRUE;
1801
1802 no_context:
1803 {
1804 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1805 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1806 g_strdup (error->message), __FILE__, GST_FUNCTION, __LINE__);
1807 g_clear_error (&error);
1808 return FALSE;
1809 }
1810 }
1811
1812 static void
gst_xv_image_sink_close(GstXvImageSink * xvimagesink)1813 gst_xv_image_sink_close (GstXvImageSink * xvimagesink)
1814 {
1815 GThread *thread;
1816 GstXvContext *context;
1817
1818 GST_OBJECT_LOCK (xvimagesink);
1819 xvimagesink->running = FALSE;
1820 /* grab thread and mark it as NULL */
1821 thread = xvimagesink->event_thread;
1822 xvimagesink->event_thread = NULL;
1823 GST_OBJECT_UNLOCK (xvimagesink);
1824
1825 /* Wait for our event thread to finish before we clean up our stuff. */
1826 if (thread)
1827 g_thread_join (thread);
1828
1829 if (xvimagesink->cur_image) {
1830 gst_buffer_unref (xvimagesink->cur_image);
1831 xvimagesink->cur_image = NULL;
1832 }
1833
1834 g_mutex_lock (&xvimagesink->flow_lock);
1835
1836 if (xvimagesink->pool) {
1837 gst_object_unref (xvimagesink->pool);
1838 xvimagesink->pool = NULL;
1839 }
1840
1841 if (xvimagesink->xwindow) {
1842 gst_xwindow_clear (xvimagesink->xwindow);
1843 gst_xwindow_destroy (xvimagesink->xwindow);
1844 xvimagesink->xwindow = NULL;
1845 }
1846 g_mutex_unlock (&xvimagesink->flow_lock);
1847
1848 if (xvimagesink->allocator) {
1849 gst_object_unref (xvimagesink->allocator);
1850 xvimagesink->allocator = NULL;
1851 }
1852
1853 GST_OBJECT_LOCK (xvimagesink);
1854 /* grab context and mark it as NULL */
1855 context = xvimagesink->context;
1856 xvimagesink->context = NULL;
1857 GST_OBJECT_UNLOCK (xvimagesink);
1858
1859 if (context)
1860 gst_xvcontext_unref (context);
1861 }
1862
1863 /* Finalize is called only once, dispose can be called multiple times.
1864 * We use mutexes and don't reset stuff to NULL here so let's register
1865 * as a finalize. */
1866 static void
gst_xv_image_sink_finalize(GObject * object)1867 gst_xv_image_sink_finalize (GObject * object)
1868 {
1869 GstXvImageSink *xvimagesink;
1870
1871 xvimagesink = GST_XV_IMAGE_SINK (object);
1872
1873 gst_xv_image_sink_close (xvimagesink);
1874
1875 gst_xvcontext_config_clear (&xvimagesink->config);
1876
1877 if (xvimagesink->par) {
1878 g_free (xvimagesink->par);
1879 xvimagesink->par = NULL;
1880 }
1881 g_mutex_clear (&xvimagesink->flow_lock);
1882 g_free (xvimagesink->media_title);
1883
1884 G_OBJECT_CLASS (parent_class)->finalize (object);
1885 }
1886
1887 static void
gst_xv_image_sink_init(GstXvImageSink * xvimagesink)1888 gst_xv_image_sink_init (GstXvImageSink * xvimagesink)
1889 {
1890 xvimagesink->config.display_name = NULL;
1891 xvimagesink->config.adaptor_nr = 0;
1892 xvimagesink->config.autopaint_colorkey = TRUE;
1893 xvimagesink->config.double_buffer = TRUE;
1894 /* on 16bit displays this becomes r,g,b = 1,2,3
1895 * on 24bit displays this becomes r,g,b = 8,8,16
1896 * as a port atom value */
1897 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1898 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1899 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1900 xvimagesink->config.cb_changed = FALSE;
1901
1902 xvimagesink->context = NULL;
1903 xvimagesink->xwindow = NULL;
1904 xvimagesink->cur_image = NULL;
1905
1906 xvimagesink->fps_n = 0;
1907 xvimagesink->fps_d = 0;
1908 xvimagesink->video_width = 0;
1909 xvimagesink->video_height = 0;
1910
1911 g_mutex_init (&xvimagesink->flow_lock);
1912
1913 xvimagesink->pool = NULL;
1914
1915 xvimagesink->synchronous = FALSE;
1916 xvimagesink->running = FALSE;
1917 xvimagesink->keep_aspect = TRUE;
1918 xvimagesink->handle_events = TRUE;
1919 xvimagesink->par = NULL;
1920 xvimagesink->handle_expose = TRUE;
1921
1922 xvimagesink->draw_borders = TRUE;
1923 }
1924
1925 static void
gst_xv_image_sink_class_init(GstXvImageSinkClass * klass)1926 gst_xv_image_sink_class_init (GstXvImageSinkClass * klass)
1927 {
1928 GObjectClass *gobject_class;
1929 GstElementClass *gstelement_class;
1930 GstBaseSinkClass *gstbasesink_class;
1931 GstVideoSinkClass *videosink_class;
1932
1933 gobject_class = (GObjectClass *) klass;
1934 gstelement_class = (GstElementClass *) klass;
1935 gstbasesink_class = (GstBaseSinkClass *) klass;
1936 videosink_class = (GstVideoSinkClass *) klass;
1937
1938 parent_class = g_type_class_peek_parent (klass);
1939
1940 gobject_class->set_property = gst_xv_image_sink_set_property;
1941 gobject_class->get_property = gst_xv_image_sink_get_property;
1942
1943 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1944 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1945 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1946 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1947 g_param_spec_int ("brightness", "Brightness",
1948 "The brightness of the video", -1000, 1000, 0,
1949 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1950 g_object_class_install_property (gobject_class, PROP_HUE,
1951 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1952 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1953 g_object_class_install_property (gobject_class, PROP_SATURATION,
1954 g_param_spec_int ("saturation", "Saturation",
1955 "The saturation of the video", -1000, 1000, 0,
1956 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1957 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1958 g_param_spec_string ("display", "Display", "X Display name", NULL,
1959 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1960 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1961 g_param_spec_boolean ("synchronous", "Synchronous",
1962 "When enabled, runs the X display in synchronous mode. "
1963 "(unrelated to A/V sync, used only for debugging)", FALSE,
1964 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1965 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1966 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1967 "The pixel aspect ratio of the device", "1/1",
1968 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1969 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1970 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1971 "When enabled, scaling will respect original aspect ratio", TRUE,
1972 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1973 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1974 g_param_spec_boolean ("handle-events", "Handle XEvents",
1975 "When enabled, XEvents will be selected and handled", TRUE,
1976 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1977 g_object_class_install_property (gobject_class, PROP_DEVICE,
1978 g_param_spec_string ("device", "Adaptor number",
1979 "The number of the video adaptor", "0",
1980 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1981 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1982 g_param_spec_string ("device-name", "Adaptor name",
1983 "The name of the video adaptor", NULL,
1984 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1985
1986 gst_video_overlay_install_properties (gobject_class, PROP_LAST);
1987
1988 /**
1989 * GstXvImageSink:handle-expose
1990 *
1991 * When enabled, the current frame will always be drawn in response to X
1992 * Expose.
1993 */
1994 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1995 g_param_spec_boolean ("handle-expose", "Handle expose",
1996 "When enabled, "
1997 "the current frame will always be drawn in response to X Expose "
1998 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1999
2000 /**
2001 * GstXvImageSink:double-buffer
2002 *
2003 * Whether to double-buffer the output.
2004 */
2005 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2006 g_param_spec_boolean ("double-buffer", "Double-buffer",
2007 "Whether to double-buffer the output", TRUE,
2008 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2009 /**
2010 * GstXvImageSink:autopaint-colorkey
2011 *
2012 * Whether to autofill overlay with colorkey
2013 */
2014 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2015 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2016 "Whether to autofill overlay with colorkey", TRUE,
2017 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2018 /**
2019 * GstXvImageSink:colorkey
2020 *
2021 * Color to use for the overlay mask.
2022 */
2023 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2024 g_param_spec_int ("colorkey", "Colorkey",
2025 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2026 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2027
2028 /**
2029 * GstXvImageSink:draw-borders
2030 *
2031 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2032 * unused parts of the video area.
2033 */
2034 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2035 g_param_spec_boolean ("draw-borders", "Draw Borders",
2036 "Draw black borders to fill unused area in force-aspect-ratio mode",
2037 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2038
2039 /**
2040 * GstXvImageSink:window-width
2041 *
2042 * Actual width of the video window.
2043 */
2044 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2045 g_param_spec_uint64 ("window-width", "window-width",
2046 "Width of the window", 0, G_MAXUINT64, 0,
2047 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2048
2049 /**
2050 * GstXvImageSink:window-height
2051 *
2052 * Actual height of the video window.
2053 */
2054 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2055 g_param_spec_uint64 ("window-height", "window-height",
2056 "Height of the window", 0, G_MAXUINT64, 0,
2057 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2058
2059 gobject_class->finalize = gst_xv_image_sink_finalize;
2060
2061 gst_element_class_set_static_metadata (gstelement_class,
2062 "Video sink", "Sink/Video",
2063 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2064
2065 gst_element_class_add_static_pad_template (gstelement_class,
2066 &gst_xv_image_sink_sink_template_factory);
2067
2068 gstelement_class->change_state =
2069 GST_DEBUG_FUNCPTR (gst_xv_image_sink_change_state);
2070
2071 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_getcaps);
2072 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_setcaps);
2073 gstbasesink_class->get_times =
2074 GST_DEBUG_FUNCPTR (gst_xv_image_sink_get_times);
2075 gstbasesink_class->propose_allocation =
2076 GST_DEBUG_FUNCPTR (gst_xv_image_sink_propose_allocation);
2077 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xv_image_sink_event);
2078
2079 videosink_class->show_frame =
2080 GST_DEBUG_FUNCPTR (gst_xv_image_sink_show_frame);
2081
2082 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
2083 }
2084