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