2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
4 * Copyright (C) 2012, 2013 Samsung Electronics Co., Ltd.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * * Modifications by Samsung Electronics Co., Ltd.
22 * 1. Add display related properties
23 * 2. Support samsung extension format to improve performance
24 * 3. Support video texture overlay of OSP layer
28 * SECTION:element-xvimagesink
30 * XvImageSink renders video frames to a drawable (XWindow) on a local display
31 * using the XVideo extension. Rendering to a remote display is theoretically
32 * possible but i doubt that the XVideo extension is actually available when
33 * connecting to a remote display. This element can receive a Window ID from the
34 * application through the XOverlay interface and will then render video frames
35 * in this drawable. If no Window ID was provided by the application, the
36 * element will create its own internal window and render into it.
39 * <title>Scaling</title>
41 * The XVideo extension, when it's available, handles hardware accelerated
42 * scaling of video frames. This means that the element will just accept
43 * incoming video frames no matter their geometry and will then put them to the
44 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
45 * property it is possible to enforce scaling with a constant aspect ratio,
46 * which means drawing black borders around the video frame.
50 * <title>Events</title>
52 * XvImageSink creates a thread to handle events coming from the drawable. There
53 * are several kind of events that can be grouped in 2 big categories: input
54 * events and window state related events. Input events will be translated to
55 * navigation events and pushed upstream for other elements to react on them.
56 * This includes events such as pointer moves, key press/release, clicks etc...
57 * Other events are used to handle the drawable appearance even when the data
58 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
59 * paused, it will receive expose events from the drawable and draw the latest
60 * frame with correct borders/aspect-ratio.
64 * <title>Pixel aspect ratio</title>
66 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
67 * the display specified in the #GstXvImageSink:display property or the
68 * default display if nothing specified. Once this connection is open it will
69 * inspect the display configuration including the physical display geometry and
70 * then calculate the pixel aspect ratio. When receiving video frames with a
71 * different pixel aspect ratio, XvImageSink will use hardware scaling to
72 * display the video frames correctly on display's pixel aspect ratio.
73 * Sometimes the calculated pixel aspect ratio can be wrong, it is
74 * then possible to enforce a specific pixel aspect ratio using the
75 * #GstXvImageSink:pixel-aspect-ratio property.
79 * <title>Examples</title>
81 * gst-launch -v videotestsrc ! xvimagesink
82 * ]| A pipeline to test hardware scaling.
83 * When the test video signal appears you can resize the window and see that
84 * video frames are scaled through hardware (no extra CPU cost).
86 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
87 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
88 * You can observe the borders drawn around the scaled image respecting aspect
91 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
92 * ]| A pipeline to test navigation events.
93 * While moving the mouse pointer over the test signal you will see a black box
94 * following the mouse pointer. If you press the mouse button somewhere on the
95 * video and release it somewhere else a green box will appear where you pressed
96 * the button and a red one where you released it. (The navigationtest element
97 * is part of gst-plugins-good.) You can observe here that even if the images
98 * are scaled through hardware the pointer coordinates are converted back to the
99 * original video frame geometry so that the box can be drawn to the correct
100 * position. This also handles borders correctly, limiting coordinates to the
103 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
104 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
105 * videotestsrc, in most cases the pixel aspect ratio of the display will be
106 * 1/1. This means that XvImageSink will have to do the scaling to convert
107 * incoming frames to a size that will match the display pixel aspect ratio
108 * (from 320x240 to 320x180 in this case). Note that you might have to escape
109 * some characters for your shell like '\(fraction\)'.
111 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
112 * ]| Demonstrates how to use the colorbalance interface.
116 /* for developers: there are two useful tools : xvinfo and xvattr */
123 #include <gst/interfaces/navigation.h>
124 #include <gst/interfaces/xoverlay.h>
125 #include <gst/interfaces/colorbalance.h>
126 #include <gst/interfaces/propertyprobe.h>
127 /* Helper functions */
128 #include <gst/video/video.h>
131 #include "xvimagesink.h"
133 #ifdef GST_EXT_XV_ENHANCEMENT
134 /* Samsung extension headers */
135 /* For xv extension header for buffer transfer (output) */
136 #include "xv_types.h"
138 /* headers for drm */
139 #include <sys/stat.h>
140 #include <sys/ioctl.h>
144 #include <xf86drmMode.h>
146 #include <dri2/dri2.h>
149 BUF_SHARE_METHOD_PADDR = 0,
151 } buf_share_method_t;
153 /* max channel count *********************************************************/
154 #define SCMN_IMGB_MAX_PLANE (4)
156 /* image buffer definition ***************************************************
158 +------------------------------------------+ ---
161 | +---------------------------+ --- | |
163 | |<---------- w[] ---------->| | | |
171 | +---------------------------+ --- | |
173 +------------------------------------------+ ---
175 |<----------------- s[] ------------------>|
180 /* width of each image plane */
181 int w[SCMN_IMGB_MAX_PLANE];
182 /* height of each image plane */
183 int h[SCMN_IMGB_MAX_PLANE];
184 /* stride of each image plane */
185 int s[SCMN_IMGB_MAX_PLANE];
186 /* elevation of each image plane */
187 int e[SCMN_IMGB_MAX_PLANE];
188 /* user space address of each image plane */
189 void * a[SCMN_IMGB_MAX_PLANE];
190 /* physical address of each image plane, if needs */
191 void * p[SCMN_IMGB_MAX_PLANE];
192 /* color space type of image */
194 /* left postion, if needs */
196 /* top position, if needs */
198 /* to align memory */
203 int dmabuf_fd[SCMN_IMGB_MAX_PLANE];
204 /* buffer share method */
205 int buf_share_method;
207 #endif /* GST_EXT_XV_ENHANCEMENT */
209 /* Debugging category */
210 #include <gst/gstinfo.h>
212 #include "gst/glib-compat-private.h"
214 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
215 #define GST_CAT_DEFAULT gst_debug_xvimagesink
216 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
218 #ifdef GST_EXT_XV_ENHANCEMENT
219 #define GST_TYPE_XVIMAGESINK_DISPLAY_MODE (gst_xvimagesink_display_mode_get_type())
222 gst_xvimagesink_display_mode_get_type(void)
224 static GType xvimagesink_display_mode_type = 0;
225 static const GEnumValue display_mode_type[] = {
226 { 0, "Default mode", "DEFAULT"},
227 { 1, "Primary video ON and Secondary video FULL SCREEN mode", "PRI_VIDEO_ON_AND_SEC_VIDEO_FULL_SCREEN"},
228 { 2, "Primary video OFF and Secondary video FULL SCREEN mode", "PRI_VIDEO_OFF_AND_SEC_VIDEO_FULL_SCREEN"},
232 if (!xvimagesink_display_mode_type) {
233 xvimagesink_display_mode_type = g_enum_register_static("GstXVImageSinkDisplayModeType", display_mode_type);
236 return xvimagesink_display_mode_type;
247 #define GST_TYPE_XVIMAGESINK_ROTATE_ANGLE (gst_xvimagesink_rotate_angle_get_type())
250 gst_xvimagesink_rotate_angle_get_type(void)
252 static GType xvimagesink_rotate_angle_type = 0;
253 static const GEnumValue rotate_angle_type[] = {
254 { 0, "No rotate", "DEGREE_0"},
255 { 1, "Rotate 90 degree", "DEGREE_90"},
256 { 2, "Rotate 180 degree", "DEGREE_180"},
257 { 3, "Rotate 270 degree", "DEGREE_270"},
261 if (!xvimagesink_rotate_angle_type) {
262 xvimagesink_rotate_angle_type = g_enum_register_static("GstXVImageSinkRotateAngleType", rotate_angle_type);
265 return xvimagesink_rotate_angle_type;
269 DISP_GEO_METHOD_LETTER_BOX = 0,
270 DISP_GEO_METHOD_ORIGIN_SIZE,
271 DISP_GEO_METHOD_FULL_SCREEN,
272 DISP_GEO_METHOD_CROPPED_FULL_SCREEN,
273 DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX,
274 DISP_GEO_METHOD_CUSTOM_ROI,
277 #define DEF_DISPLAY_GEOMETRY_METHOD DISP_GEO_METHOD_LETTER_BOX
286 #define DEF_DISPLAY_FLIP FLIP_NONE
288 #define GST_TYPE_XVIMAGESINK_DISPLAY_GEOMETRY_METHOD (gst_xvimagesink_display_geometry_method_get_type())
289 #define GST_TYPE_XVIMAGESINK_FLIP (gst_xvimagesink_flip_get_type())
292 gst_xvimagesink_display_geometry_method_get_type(void)
294 static GType xvimagesink_display_geometry_method_type = 0;
295 static const GEnumValue display_geometry_method_type[] = {
296 { 0, "Letter box", "LETTER_BOX"},
297 { 1, "Origin size", "ORIGIN_SIZE"},
298 { 2, "Full-screen", "FULL_SCREEN"},
299 { 3, "Cropped full-screen", "CROPPED_FULL_SCREEN"},
300 { 4, "Origin size(if screen size is larger than video size(width/height)) or Letter box(if video size(width/height) is larger than screen size)", "ORIGIN_SIZE_OR_LETTER_BOX"},
301 { 5, "Explicitly described destination ROI", "CUSTOM_ROI"},
305 if (!xvimagesink_display_geometry_method_type) {
306 xvimagesink_display_geometry_method_type = g_enum_register_static("GstXVImageSinkDisplayGeometryMethodType", display_geometry_method_type);
309 return xvimagesink_display_geometry_method_type;
313 gst_xvimagesink_flip_get_type(void)
315 static GType xvimagesink_flip_type = 0;
316 static const GEnumValue flip_type[] = {
317 { FLIP_NONE, "Flip NONE", "FLIP_NONE"},
318 { FLIP_HORIZONTAL, "Flip HORIZONTAL", "FLIP_HORIZONTAL"},
319 { FLIP_VERTICAL, "Flip VERTICAL", "FLIP_VERTICAL"},
320 { FLIP_BOTH, "Flip BOTH", "FLIP_BOTH"},
321 { FLIP_NUM, NULL, NULL},
324 if (!xvimagesink_flip_type) {
325 xvimagesink_flip_type = g_enum_register_static("GstXVImageSinkFlipType", flip_type);
328 return xvimagesink_flip_type;
331 #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
333 gst_xvimagesink_BOOLEAN__POINTER (GClosure *closure,
334 GValue *return_value G_GNUC_UNUSED,
335 guint n_param_values,
336 const GValue *param_values,
337 gpointer invocation_hint G_GNUC_UNUSED,
338 gpointer marshal_data)
340 typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1,
343 register GMarshalFunc_BOOLEAN__POINTER callback;
344 register GCClosure *cc = (GCClosure*) closure;
345 register gpointer data1, data2;
349 g_return_if_fail (return_value != NULL);
350 g_return_if_fail (n_param_values == 2);
352 if (G_CCLOSURE_SWAP_DATA (closure)) {
353 data1 = closure->data;
354 data2 = g_value_peek_pointer (param_values + 0);
356 data1 = g_value_peek_pointer (param_values + 0);
357 data2 = closure->data;
359 callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback);
361 v_return = callback (data1,
362 g_marshal_value_peek_pointer (param_values + 1),
365 g_value_set_boolean (return_value, v_return);
370 SIGNAL_FRAME_RENDER_ERROR,
373 static guint gst_xvimagesink_signals[LAST_SIGNAL] = { 0 };
375 #endif /* GST_EXT_XV_ENHANCEMENT */
380 unsigned long functions;
381 unsigned long decorations;
383 unsigned long status;
385 MotifWmHints, MwmHints;
387 #define MWM_HINTS_DECORATIONS (1L << 1)
389 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
391 static GstBufferClass *xvimage_buffer_parent_class = NULL;
392 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
394 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
396 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
398 static void gst_xvimagesink_expose (GstXOverlay * overlay);
400 #ifdef GST_EXT_XV_ENHANCEMENT
401 static XImage *make_transparent_image(Display *d, Window win, int w, int h);
402 static gboolean set_display_mode(GstXContext *xcontext, int set_mode);
403 static void drm_close_gem(GstXvImageSink *xvimagesink, unsigned int *gem_handle);
404 static void gst_xvimagesink_set_pixmap_handle (GstXOverlay * overlay, guintptr id);
405 #endif /* GST_EXT_XV_ENHANCEMENT */
407 /* Default template - initiated with class struct to allow gst-register to work
409 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
410 GST_STATIC_PAD_TEMPLATE ("sink",
413 GST_STATIC_CAPS ("video/x-raw-rgb, "
414 "framerate = (fraction) [ 0, MAX ], "
415 "width = (int) [ 1, MAX ], "
416 "height = (int) [ 1, MAX ]; "
418 "framerate = (fraction) [ 0, MAX ], "
419 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
431 PROP_PIXEL_ASPECT_RATIO,
432 PROP_FORCE_ASPECT_RATIO,
438 PROP_AUTOPAINT_COLORKEY,
443 #ifdef GST_EXT_XV_ENHANCEMENT
447 PROP_DISPLAY_GEOMETRY_METHOD,
456 PROP_PIXMAP_CB_USER_DATA,
457 #endif /* GST_EXT_XV_ENHANCEMENT */
460 static void gst_xvimagesink_init_interfaces (GType type);
462 GST_BOILERPLATE_FULL (GstXvImageSink, gst_xvimagesink, GstVideoSink,
463 GST_TYPE_VIDEO_SINK, gst_xvimagesink_init_interfaces);
466 /* ============================================================= */
468 /* Private Methods */
470 /* ============================================================= */
472 /* xvimage buffers */
474 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
476 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
477 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
478 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
480 /* This function destroys a GstXvImage handling XShm availability */
482 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
484 GstXvImageSink *xvimagesink;
486 GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
488 xvimagesink = xvimage->xvimagesink;
489 if (G_UNLIKELY (xvimagesink == NULL))
492 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
494 GST_OBJECT_LOCK (xvimagesink);
496 /* If the destroyed image is the current one we destroy our reference too */
497 if (xvimagesink->cur_image == xvimage)
498 xvimagesink->cur_image = NULL;
500 /* We might have some buffers destroyed after changing state to NULL */
501 if (xvimagesink->xcontext == NULL) {
502 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
504 /* Need to free the shared memory segment even if the x context
505 * was already cleaned up */
506 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
507 shmdt (xvimage->SHMInfo.shmaddr);
513 g_mutex_lock (xvimagesink->x_lock);
516 if (xvimagesink->xcontext->use_xshm) {
517 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
518 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
519 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
520 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
521 XSync (xvimagesink->xcontext->disp, FALSE);
523 shmdt (xvimage->SHMInfo.shmaddr);
525 if (xvimage->xvimage)
526 XFree (xvimage->xvimage);
528 #endif /* HAVE_XSHM */
530 if (xvimage->xvimage) {
531 if (xvimage->xvimage->data) {
532 g_free (xvimage->xvimage->data);
534 XFree (xvimage->xvimage);
538 XSync (xvimagesink->xcontext->disp, FALSE);
540 g_mutex_unlock (xvimagesink->x_lock);
543 GST_OBJECT_UNLOCK (xvimagesink);
544 xvimage->xvimagesink = NULL;
545 gst_object_unref (xvimagesink);
547 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
554 GST_WARNING ("no sink found");
560 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
562 GstXvImageSink *xvimagesink;
565 xvimagesink = xvimage->xvimagesink;
566 if (G_UNLIKELY (xvimagesink == NULL))
569 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
571 GST_OBJECT_LOCK (xvimagesink);
572 running = xvimagesink->running;
573 GST_OBJECT_UNLOCK (xvimagesink);
575 /* If our geometry changed we can't reuse that image. */
576 if (running == FALSE) {
577 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
578 gst_xvimage_buffer_destroy (xvimage);
579 } else if ((xvimage->width != xvimagesink->video_width) ||
580 (xvimage->height != xvimagesink->video_height)) {
581 GST_LOG_OBJECT (xvimage,
582 "destroy image as its size changed %dx%d vs current %dx%d",
583 xvimage->width, xvimage->height,
584 xvimagesink->video_width, xvimagesink->video_height);
585 gst_xvimage_buffer_destroy (xvimage);
587 /* In that case we can reuse the image and add it to our image pool. */
588 GST_LOG_OBJECT (xvimage, "recycling image in pool");
589 /* need to increment the refcount again to recycle */
590 gst_buffer_ref (GST_BUFFER_CAST (xvimage));
591 g_mutex_lock (xvimagesink->pool_lock);
592 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
594 g_mutex_unlock (xvimagesink->pool_lock);
600 GST_WARNING ("no sink found");
606 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
608 /* make sure it is not recycled */
610 xvimage->height = -1;
611 gst_buffer_unref (GST_BUFFER (xvimage));
615 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
618 xvimage->SHMInfo.shmaddr = ((void *) -1);
619 xvimage->SHMInfo.shmid = -1;
624 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
626 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
628 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
630 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
631 gst_xvimage_buffer_finalize;
635 gst_xvimage_buffer_get_type (void)
637 static GType _gst_xvimage_buffer_type;
639 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
640 static const GTypeInfo xvimage_buffer_info = {
641 sizeof (GstBufferClass),
644 gst_xvimage_buffer_class_init,
647 sizeof (GstXvImageBuffer),
649 (GInstanceInitFunc) gst_xvimage_buffer_init,
652 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
653 "GstXvImageBuffer", &xvimage_buffer_info, 0);
655 return _gst_xvimage_buffer_type;
660 static gboolean error_caught = FALSE;
663 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
665 char error_msg[1024];
667 XGetErrorText (display, xevent->error_code, error_msg, 1024);
668 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
674 /* This function checks that it is actually really possible to create an image
677 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
680 XShmSegmentInfo SHMInfo;
682 int (*handler) (Display *, XErrorEvent *);
683 gboolean result = FALSE;
684 gboolean did_attach = FALSE;
686 g_return_val_if_fail (xcontext != NULL, FALSE);
688 /* Sync to ensure any older errors are already processed */
689 XSync (xcontext->disp, FALSE);
691 /* Set defaults so we don't free these later unnecessarily */
692 SHMInfo.shmaddr = ((void *) -1);
695 /* Setting an error handler to catch failure */
696 error_caught = FALSE;
697 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
699 /* Trying to create a 1x1 picture */
700 GST_DEBUG ("XvShmCreateImage of 1x1");
701 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
702 xcontext->im_format, NULL, 1, 1, &SHMInfo);
704 /* Might cause an error, sync to ensure it is noticed */
705 XSync (xcontext->disp, FALSE);
706 if (!xvimage || error_caught) {
707 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
710 size = xvimage->data_size;
712 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
713 if (SHMInfo.shmid == -1) {
714 GST_WARNING ("could not get shared memory of %d bytes", size);
718 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
719 if (SHMInfo.shmaddr == ((void *) -1)) {
720 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
721 /* Clean up the shared memory segment */
722 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
726 xvimage->data = SHMInfo.shmaddr;
727 SHMInfo.readOnly = FALSE;
729 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
730 GST_WARNING ("Failed to XShmAttach");
731 /* Clean up the shared memory segment */
732 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
736 /* Sync to ensure we see any errors we caused */
737 XSync (xcontext->disp, FALSE);
739 /* Delete the shared memory segment as soon as everyone is attached.
740 * This way, it will be deleted as soon as we detach later, and not
741 * leaked if we crash. */
742 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
745 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
749 /* store whether we succeeded in result */
752 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
753 "Not using shared memory.");
757 /* Sync to ensure we swallow any errors we caused and reset error_caught */
758 XSync (xcontext->disp, FALSE);
760 error_caught = FALSE;
761 XSetErrorHandler (handler);
764 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
765 SHMInfo.shmid, SHMInfo.shmseg);
766 XShmDetach (xcontext->disp, &SHMInfo);
767 XSync (xcontext->disp, FALSE);
769 if (SHMInfo.shmaddr != ((void *) -1))
770 shmdt (SHMInfo.shmaddr);
775 #endif /* HAVE_XSHM */
777 /* This function handles GstXvImage creation depending on XShm availability */
778 static GstXvImageBuffer *
779 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
781 GstXvImageBuffer *xvimage = NULL;
782 GstStructure *structure = NULL;
783 gboolean succeeded = FALSE;
784 int (*handler) (Display *, XErrorEvent *);
786 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
791 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
792 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
794 structure = gst_caps_get_structure (caps, 0);
796 if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
797 !gst_structure_get_int (structure, "height", &xvimage->height)) {
798 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
801 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
803 #ifdef GST_EXT_XV_ENHANCEMENT
804 GST_LOG_OBJECT(xvimagesink, "aligned size %dx%d",
805 xvimagesink->aligned_width, xvimagesink->aligned_height);
806 if (xvimagesink->aligned_width == 0 || xvimagesink->aligned_height == 0) {
807 GST_INFO_OBJECT(xvimagesink, "aligned size is zero. set size of caps.");
808 xvimagesink->aligned_width = xvimage->width;
809 xvimagesink->aligned_height = xvimage->height;
811 #endif /* GST_EXT_XV_ENHANCEMENT */
813 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
814 if (xvimage->im_format == -1) {
815 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
816 GST_PTR_FORMAT, caps);
817 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
818 ("Failed to create output image buffer of %dx%d pixels",
819 xvimage->width, xvimage->height), ("Invalid input caps"));
822 xvimage->xvimagesink = gst_object_ref (xvimagesink);
824 g_mutex_lock (xvimagesink->x_lock);
826 #ifdef GST_EXT_XV_ENHANCEMENT
827 XSync (xvimagesink->xcontext->disp, FALSE);
828 #endif /* GST_EXT_XV_ENHANCEMENT */
829 /* Setting an error handler to catch failure */
830 error_caught = FALSE;
831 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
834 if (xvimagesink->xcontext->use_xshm) {
837 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
838 xvimagesink->xcontext->xv_port_id,
839 xvimage->im_format, NULL,
840 #ifdef GST_EXT_XV_ENHANCEMENT
841 xvimagesink->aligned_width, xvimagesink->aligned_height, &xvimage->SHMInfo);
842 #else /* GST_EXT_XV_ENHANCEMENT */
843 xvimage->width, xvimage->height, &xvimage->SHMInfo);
844 #endif /* GST_EXT_XV_ENHANCEMENT */
845 if (!xvimage->xvimage || error_caught) {
846 g_mutex_unlock (xvimagesink->x_lock);
848 /* Reset error flag */
849 error_caught = FALSE;
852 GST_ELEMENT_WARNING (xvimagesink, RESOURCE, WRITE,
853 ("Failed to create output image buffer of %dx%d pixels",
854 xvimage->width, xvimage->height),
855 ("could not XvShmCreateImage a %dx%d image",
856 xvimage->width, xvimage->height));
858 #ifdef GST_EXT_XV_ENHANCEMENT
860 #else /* GST_EXT_XV_ENHANCEMENT */
861 /* Retry without XShm */
862 xvimagesink->xcontext->use_xshm = FALSE;
864 /* Hold X mutex again to try without XShm */
865 g_mutex_lock (xvimagesink->x_lock);
867 #endif /* GST_EXT_XV_ENHANCEMENT */
870 /* we have to use the returned data_size for our shm size */
871 xvimage->size = xvimage->xvimage->data_size;
872 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
875 /* calculate the expected size. This is only for sanity checking the
876 * number we get from X. */
877 switch (xvimage->im_format) {
878 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
879 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
886 pitches[0] = GST_ROUND_UP_4 (xvimage->width);
887 offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
888 pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
890 offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
891 pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
894 offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
896 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
897 GST_DEBUG_OBJECT (xvimagesink,
898 "Plane %u has a expected pitch of %d bytes, " "offset of %d",
899 plane, pitches[plane], offsets[plane]);
903 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
904 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
905 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
908 #ifdef GST_EXT_XV_ENHANCEMENT
909 case GST_MAKE_FOURCC ('S', 'T', '1', '2'):
910 case GST_MAKE_FOURCC ('S', 'N', '1', '2'):
911 case GST_MAKE_FOURCC ('S', 'N', '2', '1'):
912 case GST_MAKE_FOURCC ('S', 'U', 'Y', 'V'):
913 case GST_MAKE_FOURCC ('S', 'U', 'Y', '2'):
914 case GST_MAKE_FOURCC ('S', '4', '2', '0'):
915 case GST_MAKE_FOURCC ('S', 'Y', 'V', 'Y'):
916 expected_size = sizeof(SCMN_IMGB);
918 #endif /* GST_EXT_XV_ENHANCEMENT */
923 if (expected_size != 0 && xvimage->size != expected_size) {
924 GST_WARNING_OBJECT (xvimagesink,
925 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
926 xvimage->size, expected_size);
929 /* Be verbose about our XvImage stride */
933 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
934 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
935 "offset of %d", plane, xvimage->xvimage->pitches[plane],
936 xvimage->xvimage->offsets[plane]);
940 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
942 if (xvimage->SHMInfo.shmid == -1) {
943 g_mutex_unlock (xvimagesink->x_lock);
944 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
945 ("Failed to create output image buffer of %dx%d pixels",
946 xvimage->width, xvimage->height),
947 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
952 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
953 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
954 g_mutex_unlock (xvimagesink->x_lock);
955 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
956 ("Failed to create output image buffer of %dx%d pixels",
957 xvimage->width, xvimage->height),
958 ("Failed to shmat: %s", g_strerror (errno)));
959 /* Clean up the shared memory segment */
960 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
964 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
965 xvimage->SHMInfo.readOnly = FALSE;
967 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
968 /* Clean up the shared memory segment */
969 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
971 g_mutex_unlock (xvimagesink->x_lock);
972 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
973 ("Failed to create output image buffer of %dx%d pixels",
974 xvimage->width, xvimage->height), ("Failed to XShmAttach"));
978 XSync (xvimagesink->xcontext->disp, FALSE);
980 /* Delete the shared memory segment as soon as we everyone is attached.
981 * This way, it will be deleted as soon as we detach later, and not
982 * leaked if we crash. */
983 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
985 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
986 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
989 #endif /* HAVE_XSHM */
991 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
992 xvimagesink->xcontext->xv_port_id,
993 #ifdef GST_EXT_XV_ENHANCEMENT
994 xvimage->im_format, NULL, xvimagesink->aligned_width, xvimagesink->aligned_height);
995 #else /* GST_EXT_XV_ENHANCEMENT */
996 xvimage->im_format, NULL, xvimage->width, xvimage->height);
997 #endif /* GST_EXT_XV_ENHANCEMENT */
998 if (!xvimage->xvimage || error_caught) {
999 g_mutex_unlock (xvimagesink->x_lock);
1000 /* Reset error handler */
1001 error_caught = FALSE;
1002 XSetErrorHandler (handler);
1004 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1005 ("Failed to create outputimage buffer of %dx%d pixels",
1006 xvimage->width, xvimage->height),
1007 ("could not XvCreateImage a %dx%d image",
1008 xvimage->width, xvimage->height));
1009 goto beach_unlocked;
1012 /* we have to use the returned data_size for our image size */
1013 xvimage->size = xvimage->xvimage->data_size;
1014 xvimage->xvimage->data = g_malloc (xvimage->size);
1016 XSync (xvimagesink->xcontext->disp, FALSE);
1019 /* Reset error handler */
1020 error_caught = FALSE;
1021 XSetErrorHandler (handler);
1025 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
1026 GST_BUFFER_SIZE (xvimage) = xvimage->size;
1028 g_mutex_unlock (xvimagesink->x_lock);
1032 gst_xvimage_buffer_free (xvimage);
1039 /* We are called with the x_lock taken */
1041 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
1042 GstXWindow * xwindow, GstVideoRectangle rect)
1046 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1047 g_return_if_fail (xwindow != NULL);
1049 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
1050 xvimagesink->xcontext->black);
1053 if (rect.x > xvimagesink->render_rect.x) {
1054 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1055 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
1056 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
1060 t1 = rect.x + rect.w;
1061 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
1063 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1064 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
1068 if (rect.y > xvimagesink->render_rect.y) {
1069 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1070 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
1071 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
1075 t1 = rect.y + rect.h;
1076 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
1078 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1079 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
1083 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
1084 * if no window was available */
1086 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
1087 GstXvImageBuffer * xvimage)
1089 GstVideoRectangle result;
1090 gboolean draw_border = FALSE;
1092 #ifdef GST_EXT_XV_ENHANCEMENT
1093 static Atom atom_rotation = None;
1094 static Atom atom_hflip = None;
1095 static Atom atom_vflip = None;
1096 gboolean set_hflip = FALSE;
1097 gboolean set_vflip = FALSE;
1099 GstVideoRectangle src_origin = { 0, 0, 0, 0};
1100 GstVideoRectangle src_input = { 0, 0, 0, 0};
1101 GstVideoRectangle src = { 0, 0, 0, 0};
1102 GstVideoRectangle dst = { 0, 0, 0, 0};
1107 int (*handler) (Display *, XErrorEvent *);
1108 gboolean res = FALSE;
1109 #endif /* GST_EXT_XV_ENHANCEMENT */
1111 /* We take the flow_lock. If expose is in there we don't want to run
1112 concurrently from the data flow thread */
1113 g_mutex_lock (xvimagesink->flow_lock);
1115 #ifdef GST_EXT_XV_ENHANCEMENT
1116 if (xvimagesink->xid_updated) {
1117 if (xvimage && xvimagesink->xvimage == NULL) {
1118 GST_WARNING_OBJECT (xvimagesink, "set xvimage to NULL, new xid was set right after creation of new xvimage");
1121 xvimagesink->xid_updated = FALSE;
1123 #endif /* GST_EXT_XV_ENHANCEMENT */
1125 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1126 #ifdef GST_EXT_XV_ENHANCEMENT
1127 if (xvimagesink->get_pixmap_cb) {
1128 GST_INFO_OBJECT( xvimagesink, "xwindow is NULL, but it has get_pixmap_cb(0x%x), keep going..",xvimagesink->get_pixmap_cb );
1130 GST_INFO_OBJECT( xvimagesink, "xwindow is NULL. Skip xvimage_put." );
1131 #endif /* GST_EXT_XV_ENHANCEMENT */
1132 g_mutex_unlock (xvimagesink->flow_lock);
1134 #ifdef GST_EXT_XV_ENHANCEMENT
1136 #endif /* GST_EXT_XV_ENHANCEMENT */
1139 #ifdef GST_EXT_XV_ENHANCEMENT
1140 if (xvimagesink->visible == FALSE) {
1141 GST_INFO_OBJECT(xvimagesink, "visible is FALSE. Skip xvimage_put.");
1142 g_mutex_unlock(xvimagesink->flow_lock);
1145 #endif /* GST_EXT_XV_ENHANCEMENT */
1147 /* Draw borders when displaying the first frame. After this
1148 draw borders only on expose event or after a size change. */
1149 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
1153 /* Store a reference to the last image we put, lose the previous one */
1154 if (xvimage && xvimagesink->cur_image != xvimage) {
1155 if (xvimagesink->cur_image) {
1156 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
1157 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
1159 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
1160 xvimagesink->cur_image =
1161 GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
1164 /* Expose sends a NULL image, we take the latest frame */
1166 if (xvimagesink->cur_image) {
1168 xvimage = xvimagesink->cur_image;
1170 #ifdef GST_EXT_XV_ENHANCEMENT
1171 GST_INFO_OBJECT(xvimagesink, "cur_image is NULL. Skip xvimage_put.");
1172 #endif /* GST_EXT_XV_ENHANCEMENT */
1173 g_mutex_unlock (xvimagesink->flow_lock);
1178 #ifdef GST_EXT_XV_ENHANCEMENT
1179 if (!xvimagesink->get_pixmap_cb) {
1180 gst_xvimagesink_xwindow_update_geometry( xvimagesink );
1182 /* for multi-pixmap usage for the video texture */
1183 gst_xvimagesink_set_pixmap_handle ((GstXOverlay *)xvimagesink, xvimagesink->get_pixmap_cb(xvimagesink->get_pixmap_cb_user_data));
1184 idx = xvimagesink->current_pixmap_idx;
1186 g_mutex_unlock (xvimagesink->flow_lock);
1188 } else if (idx == -2) {
1189 GST_WARNING_OBJECT(xvimagesink, "Skip putImage().");
1190 g_mutex_unlock (xvimagesink->flow_lock);
1196 src_origin.x = src_origin.y = src_input.x = src_input.y = 0;
1198 src_input.w = src_origin.w = xvimagesink->video_width;
1199 src_input.h = src_origin.h = xvimagesink->video_height;
1201 if (xvimagesink->rotate_angle == DEGREE_0 ||
1202 xvimagesink->rotate_angle == DEGREE_180) {
1203 src.w = src_origin.w;
1204 src.h = src_origin.h;
1206 src.w = src_origin.h;
1207 src.h = src_origin.w;
1210 dst.w = xvimagesink->render_rect.w;
1211 dst.h = xvimagesink->render_rect.h;
1213 switch (xvimagesink->display_geometry_method)
1215 case DISP_GEO_METHOD_LETTER_BOX:
1216 gst_video_sink_center_rect (src, dst, &result, TRUE);
1217 result.x += xvimagesink->render_rect.x;
1218 result.y += xvimagesink->render_rect.y;
1221 case DISP_GEO_METHOD_ORIGIN_SIZE_OR_LETTER_BOX:
1222 GST_WARNING_OBJECT(xvimagesink, "not supported API, set ORIGIN_SIZE mode");
1223 case DISP_GEO_METHOD_ORIGIN_SIZE:
1224 gst_video_sink_center_rect (src, dst, &result, FALSE);
1225 gst_video_sink_center_rect (dst, src, &src_input, FALSE);
1227 if (xvimagesink->rotate_angle == DEGREE_90 ||
1228 xvimagesink->rotate_angle == DEGREE_270) {
1229 src_input.x = src_input.x ^ src_input.y;
1230 src_input.y = src_input.x ^ src_input.y;
1231 src_input.x = src_input.x ^ src_input.y;
1233 src_input.w = src_input.w ^ src_input.h;
1234 src_input.h = src_input.w ^ src_input.h;
1235 src_input.w = src_input.w ^ src_input.h;
1239 case DISP_GEO_METHOD_FULL_SCREEN:
1240 result.x = result.y = 0;
1241 if (!xvimagesink->get_pixmap_cb) {
1242 result.w = xvimagesink->xwindow->width;
1243 result.h = xvimagesink->xwindow->height;
1245 result.w = xvimagesink->xpixmap[idx]->width;
1246 result.h = xvimagesink->xpixmap[idx]->height;
1250 case DISP_GEO_METHOD_CROPPED_FULL_SCREEN:
1251 gst_video_sink_center_rect(dst, src, &src_input, TRUE);
1253 result.x = result.y = 0;
1257 if (xvimagesink->rotate_angle == DEGREE_90 ||
1258 xvimagesink->rotate_angle == DEGREE_270) {
1259 src_input.x = src_input.x ^ src_input.y;
1260 src_input.y = src_input.x ^ src_input.y;
1261 src_input.x = src_input.x ^ src_input.y;
1263 src_input.w = src_input.w ^ src_input.h;
1264 src_input.h = src_input.w ^ src_input.h;
1265 src_input.w = src_input.w ^ src_input.h;
1269 case DISP_GEO_METHOD_CUSTOM_ROI:
1270 #ifdef GST_EXT_XV_ENHANCEMENT_ROI_MODE
1271 switch (xvimagesink->rotate_angle) {
1273 result.w = xvimagesink->dst_roi.h;
1274 result.h = xvimagesink->dst_roi.w;
1276 result.x = xvimagesink->dst_roi.y;
1277 if (!xvimagesink->get_pixmap_cb) {
1278 result.y = xvimagesink->xwindow->height - xvimagesink->dst_roi.x - xvimagesink->dst_roi.w;
1280 result.y = xvimagesink->xpixmap[idx]->height - xvimagesink->dst_roi.x - xvimagesink->dst_roi.w;
1284 result.w = xvimagesink->dst_roi.w;
1285 result.h = xvimagesink->dst_roi.h;
1287 if (!xvimagesink->get_pixmap_cb) {
1288 result.x = xvimagesink->xwindow->width - result.w - xvimagesink->dst_roi.x;
1289 result.y = xvimagesink->xwindow->height - result.h - xvimagesink->dst_roi.y;
1291 result.x = xvimagesink->xpixmap[idx]->width - result.w - xvimagesink->dst_roi.x;
1292 result.y = xvimagesink->xpixmap[idx]->height - result.h - xvimagesink->dst_roi.y;
1296 result.w = xvimagesink->dst_roi.h;
1297 result.h = xvimagesink->dst_roi.w;
1299 if (!xvimagesink->get_pixmap_cb) {
1300 result.x = xvimagesink->xwindow->width - xvimagesink->dst_roi.y - xvimagesink->dst_roi.h;
1302 result.x = xvimagesink->xpixmap[idx]->width - xvimagesink->dst_roi.y - xvimagesink->dst_roi.h;
1304 result.y = xvimagesink->dst_roi.x;
1307 result.x = xvimagesink->dst_roi.x;
1308 result.y = xvimagesink->dst_roi.y;
1309 result.w = xvimagesink->dst_roi.w;
1310 result.h = xvimagesink->dst_roi.h;
1314 GST_LOG_OBJECT(xvimagesink, "rotate[%d], ROI input[%d,%d,%dx%d] > result[%d,%d,%dx%d]",
1315 xvimagesink->rotate_angle,
1316 xvimagesink->dst_roi.x, xvimagesink->dst_roi.y, xvimagesink->dst_roi.w, xvimagesink->dst_roi.h,
1317 result.x, result.y, result.w, result.h);
1318 #else /* GST_EXT_XV_ENHANCEMENT_ROI_MODE */
1319 result.x = xvimagesink->dst_roi.x;
1320 result.y = xvimagesink->dst_roi.y;
1321 result.w = xvimagesink->dst_roi.w;
1322 result.h = xvimagesink->dst_roi.h;
1324 if (xvimagesink->rotate_angle == DEGREE_90 ||
1325 xvimagesink->rotate_angle == DEGREE_270) {
1326 result.w = xvimagesink->dst_roi.h;
1327 result.h = xvimagesink->dst_roi.w;
1329 #endif /* GST_EXT_XV_ENHANCEMENT_ROI_MODE */
1336 if (xvimagesink->zoom > 1 && xvimagesink->zoom < 10) {
1337 src_input.x += (src_input.w-(src_input.w/xvimagesink->zoom))>>1;
1338 src_input.y += (src_input.h-(src_input.h/xvimagesink->zoom))>>1;
1339 src_input.w /= xvimagesink->zoom;
1340 src_input.h /= xvimagesink->zoom;
1343 #else /* GST_EXT_XV_ENHANCEMENT */
1344 if (xvimagesink->keep_aspect) {
1345 GstVideoRectangle src, dst;
1347 /* We use the calculated geometry from _setcaps as a source to respect
1348 source and screen pixel aspect ratios. */
1349 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1350 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1351 dst.w = xvimagesink->render_rect.w;
1352 dst.h = xvimagesink->render_rect.h;
1354 gst_video_sink_center_rect (src, dst, &result, TRUE);
1355 result.x += xvimagesink->render_rect.x;
1356 result.y += xvimagesink->render_rect.y;
1358 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
1360 #endif /* GST_EXT_XV_ENHANCEMENT */
1362 g_mutex_lock (xvimagesink->x_lock);
1364 #ifdef GST_EXT_XV_ENHANCEMENT
1365 if (draw_border && xvimagesink->draw_borders && !xvimagesink->get_pixmap_cb) {
1367 if (draw_border && xvimagesink->draw_borders) {
1368 #endif /* GST_EXT_XV_ENHANCEMENT */
1369 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
1371 xvimagesink->redraw_border = FALSE;
1374 /* We scale to the window's geometry */
1376 if (xvimagesink->xcontext->use_xshm) {
1377 GST_LOG_OBJECT (xvimagesink,
1378 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
1380 xvimage->width, xvimage->height,
1381 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
1383 #ifdef GST_EXT_XV_ENHANCEMENT
1384 switch( xvimagesink->rotate_angle )
1386 /* There's slightly weired code (CCW? CW?) */
1399 GST_WARNING_OBJECT( xvimagesink, "Unsupported rotation [%d]... set DEGREE 0.",
1400 xvimagesink->rotate_angle );
1404 /* Trim as proper size */
1405 if (src_input.w % 2 == 1) {
1408 if (src_input.h % 2 == 1) {
1412 if (!xvimagesink->get_pixmap_cb) {
1413 GST_LOG_OBJECT( xvimagesink, "screen[%dx%d],window[%d,%d,%dx%d],method[%d],rotate[%d],zoom[%d],dp_mode[%d],src[%dx%d],dst[%d,%d,%dx%d],input[%d,%d,%dx%d],result[%d,%d,%dx%d]",
1414 xvimagesink->scr_w, xvimagesink->scr_h,
1415 xvimagesink->xwindow->x, xvimagesink->xwindow->y, xvimagesink->xwindow->width, xvimagesink->xwindow->height,
1416 xvimagesink->display_geometry_method, rotate, xvimagesink->zoom, xvimagesink->display_mode,
1417 src_origin.w, src_origin.h,
1418 dst.x, dst.y, dst.w, dst.h,
1419 src_input.x, src_input.y, src_input.w, src_input.h,
1420 result.x, result.y, result.w, result.h );
1422 GST_LOG_OBJECT( xvimagesink, "pixmap[%d,%d,%dx%d],method[%d],rotate[%d],zoom[%d],dp_mode[%d],src[%dx%d],dst[%d,%d,%dx%d],input[%d,%d,%dx%d],result[%d,%d,%dx%d]",
1423 xvimagesink->xpixmap[idx]->x, xvimagesink->xpixmap[idx]->y, xvimagesink->xpixmap[idx]->width, xvimagesink->xpixmap[idx]->height,
1424 xvimagesink->display_geometry_method, rotate, xvimagesink->zoom, xvimagesink->display_mode,
1425 src_origin.w, src_origin.h,
1426 dst.x, dst.y, dst.w, dst.h,
1427 src_input.x, src_input.y, src_input.w, src_input.h,
1428 result.x, result.y, result.w, result.h );
1431 /* set display rotation */
1432 if (atom_rotation == None) {
1433 atom_rotation = XInternAtom(xvimagesink->xcontext->disp,
1434 "_USER_WM_PORT_ATTRIBUTE_ROTATION", False);
1437 ret = XvSetPortAttribute(xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom_rotation, rotate);
1438 if (ret != Success) {
1439 GST_ERROR_OBJECT( xvimagesink, "XvSetPortAttribute failed[%d]. disp[%x],xv_port_id[%d],atom[%x],rotate[%d]",
1440 ret, xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom_rotation, rotate );
1444 /* set display flip */
1445 if (atom_hflip == None) {
1446 atom_hflip = XInternAtom(xvimagesink->xcontext->disp,
1447 "_USER_WM_PORT_ATTRIBUTE_HFLIP", False);
1449 if (atom_vflip == None) {
1450 atom_vflip = XInternAtom(xvimagesink->xcontext->disp,
1451 "_USER_WM_PORT_ATTRIBUTE_VFLIP", False);
1454 switch (xvimagesink->flip) {
1455 case FLIP_HORIZONTAL:
1474 ret = XvSetPortAttribute(xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom_hflip, set_hflip);
1475 if (ret != Success) {
1476 GST_WARNING("set HFLIP failed[%d]. disp[%x],xv_port_id[%d],atom[%x],hflip[%d]",
1477 ret, xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom_hflip, set_hflip);
1479 ret = XvSetPortAttribute(xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom_vflip, set_vflip);
1480 if (ret != Success) {
1481 GST_WARNING("set VFLIP failed[%d]. disp[%x],xv_port_id[%d],atom[%x],vflip[%d]",
1482 ret, xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, atom_vflip, set_vflip);
1485 /* set error handler */
1486 error_caught = FALSE;
1487 handler = XSetErrorHandler(gst_xvimagesink_handle_xerror);
1489 /* src input indicates the status when degree is 0 */
1490 /* dst input indicates the area that src will be shown regardless of rotate */
1491 if (xvimagesink->visible && !xvimagesink->is_hided) {
1492 if (xvimagesink->xim_transparenter) {
1493 GST_LOG_OBJECT( xvimagesink, "Transparent related issue." );
1494 XPutImage(xvimagesink->xcontext->disp,
1495 xvimagesink->xwindow->win,
1496 xvimagesink->xwindow->gc,
1497 xvimagesink->xim_transparenter,
1499 result.x, result.y, result.w, result.h);
1502 if (xvimagesink->get_pixmap_cb) {
1503 gint idx = xvimagesink->current_pixmap_idx;
1504 ret = XvShmPutImage (xvimagesink->xcontext->disp,
1505 xvimagesink->xcontext->xv_port_id,
1506 xvimagesink->xpixmap[idx]->pixmap,
1507 xvimagesink->xpixmap[idx]->gc, xvimage->xvimage,
1508 src_input.x, src_input.y, src_input.w, src_input.h,
1509 result.x, result.y, result.w, result.h, FALSE);
1510 GST_LOG_OBJECT(xvimagesink, "pixmap[%d]->pixmap = %d", idx, xvimagesink->xpixmap[idx]->pixmap);
1512 ret = XvShmPutImage (xvimagesink->xcontext->disp,
1513 xvimagesink->xcontext->xv_port_id,
1514 xvimagesink->xwindow->win,
1515 xvimagesink->xwindow->gc, xvimage->xvimage,
1516 src_input.x, src_input.y, src_input.w, src_input.h,
1517 result.x, result.y, result.w, result.h, FALSE);
1519 GST_LOG_OBJECT( xvimagesink, "XvShmPutImage return value [%d]", ret );
1521 GST_LOG_OBJECT( xvimagesink, "visible is FALSE. skip this image..." );
1523 #else /* GST_EXT_XV_ENHANCEMENT */
1524 XvShmPutImage (xvimagesink->xcontext->disp,
1525 xvimagesink->xcontext->xv_port_id,
1526 xvimagesink->xwindow->win,
1527 xvimagesink->xwindow->gc, xvimage->xvimage,
1528 xvimagesink->disp_x, xvimagesink->disp_y,
1529 xvimagesink->disp_width, xvimagesink->disp_height,
1530 result.x, result.y, result.w, result.h, FALSE);
1531 #endif /* GST_EXT_XV_ENHANCEMENT */
1533 #endif /* HAVE_XSHM */
1535 XvPutImage (xvimagesink->xcontext->disp,
1536 xvimagesink->xcontext->xv_port_id,
1537 xvimagesink->xwindow->win,
1538 xvimagesink->xwindow->gc, xvimage->xvimage,
1539 xvimagesink->disp_x, xvimagesink->disp_y,
1540 xvimagesink->disp_width, xvimagesink->disp_height,
1541 result.x, result.y, result.w, result.h);
1544 XSync (xvimagesink->xcontext->disp, FALSE);
1547 #ifdef GST_EXT_XV_ENHANCEMENT
1548 if (xvimagesink->get_pixmap_cb) {
1550 g_signal_emit (G_OBJECT (xvimagesink),
1551 gst_xvimagesink_signals[SIGNAL_FRAME_RENDER_ERROR],
1553 &xvimagesink->xpixmap[idx]->pixmap,
1555 GST_WARNING_OBJECT( xvimagesink, "pixmap cb case, putimage error_caught" );
1558 /* Reset error handler */
1559 error_caught = FALSE;
1560 XSetErrorHandler (handler);
1561 #endif /* GST_EXT_XV_ENHANCEMENT */
1562 #endif /* HAVE_XSHM */
1564 g_mutex_unlock (xvimagesink->x_lock);
1566 g_mutex_unlock (xvimagesink->flow_lock);
1572 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
1573 GstXWindow * window)
1575 Atom hints_atom = None;
1576 MotifWmHints *hints;
1578 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
1579 g_return_val_if_fail (window != NULL, FALSE);
1581 g_mutex_lock (xvimagesink->x_lock);
1583 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
1585 if (hints_atom == None) {
1586 g_mutex_unlock (xvimagesink->x_lock);
1590 hints = g_malloc0 (sizeof (MotifWmHints));
1592 hints->flags |= MWM_HINTS_DECORATIONS;
1593 hints->decorations = 1 << 0;
1595 XChangeProperty (xvimagesink->xcontext->disp, window->win,
1596 hints_atom, hints_atom, 32, PropModeReplace,
1597 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
1599 XSync (xvimagesink->xcontext->disp, FALSE);
1601 g_mutex_unlock (xvimagesink->x_lock);
1609 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
1610 GstXWindow * xwindow, const gchar * media_title)
1613 g_free (xvimagesink->media_title);
1614 xvimagesink->media_title = g_strdup (media_title);
1617 /* we have a window */
1618 if (xwindow->internal) {
1619 XTextProperty xproperty;
1620 const gchar *app_name;
1621 const gchar *title = NULL;
1622 gchar *title_mem = NULL;
1624 /* set application name as a title */
1625 app_name = g_get_application_name ();
1627 if (app_name && xvimagesink->media_title) {
1628 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
1630 } else if (app_name) {
1632 } else if (xvimagesink->media_title) {
1633 title = xvimagesink->media_title;
1637 if ((XStringListToTextProperty (((char **) &title), 1,
1638 &xproperty)) != 0) {
1639 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
1640 XFree (xproperty.value);
1649 #ifdef GST_EXT_XV_ENHANCEMENT
1650 static XImage *make_transparent_image(Display *d, Window win, int w, int h)
1654 /* create a normal ximage */
1655 xim = XCreateImage(d, DefaultVisualOfScreen(DefaultScreenOfDisplay(d)), 24, ZPixmap, 0, NULL, w, h, 32, 0);
1657 GST_INFO("ximage %p", xim);
1659 /* allocate data for it */
1661 xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
1663 memset(xim->data, 0x00, xim->bytes_per_line * xim->height);
1666 GST_ERROR("failed to alloc data - size %d", xim->bytes_per_line * xim->height);
1672 GST_ERROR("failed to create Ximage");
1678 static gboolean set_display_mode(GstXContext *xcontext, int set_mode)
1681 static gboolean is_exist = FALSE;
1682 static XvPortID current_port_id = -1;
1683 Atom atom_output = None;
1685 if (xcontext == NULL) {
1686 GST_WARNING("xcontext is NULL");
1690 /* check once per one xv_port_id */
1691 if (current_port_id != xcontext->xv_port_id) {
1692 /* check whether _USER_WM_PORT_ATTRIBUTE_OUTPUT attribute is existed */
1695 XvAttribute *const attr = XvQueryPortAttributes(xcontext->disp,
1696 xcontext->xv_port_id, &count);
1698 current_port_id = xcontext->xv_port_id;
1699 for (i = 0 ; i < count ; i++) {
1700 if (!strcmp(attr[i].name, "_USER_WM_PORT_ATTRIBUTE_OUTPUT")) {
1702 GST_INFO("_USER_WM_PORT_ATTRIBUTE_OUTPUT[index %d] found", i);
1708 GST_WARNING("XvQueryPortAttributes disp:%d, port_id:%d failed",
1709 xcontext->disp, xcontext->xv_port_id);
1714 GST_INFO("set display mode %d", set_mode);
1715 atom_output = XInternAtom(xcontext->disp,
1716 "_USER_WM_PORT_ATTRIBUTE_OUTPUT", False);
1717 ret = XvSetPortAttribute(xcontext->disp, xcontext->xv_port_id,
1718 atom_output, set_mode);
1719 if (ret == Success) {
1722 GST_WARNING("display mode[%d] set failed.", set_mode);
1725 GST_WARNING("_USER_WM_PORT_ATTRIBUTE_OUTPUT is not existed");
1731 static void drm_init(GstXvImageSink *xvimagesink)
1739 char *driverName = NULL;
1740 char *deviceName = NULL;
1741 struct drm_auth auth_arg = {0};
1743 xvimagesink->drm_fd = -1;
1745 dpy = XOpenDisplay(0);
1747 GST_ERROR("XOpenDisplay failed errno:%d", errno);
1754 if (!DRI2QueryExtension(dpy, &eventBase, &errorBase)) {
1755 GST_ERROR("DRI2QueryExtension !!");
1756 goto DRM_INIT_ERROR;
1759 if (!DRI2QueryVersion(dpy, &dri2Major, &dri2Minor)) {
1760 GST_ERROR("DRI2QueryVersion !!");
1761 goto DRM_INIT_ERROR;
1764 if (!DRI2Connect(dpy, RootWindow(dpy, DefaultScreen(dpy)), &driverName, &deviceName)) {
1765 GST_ERROR("DRI2Connect !!");
1766 goto DRM_INIT_ERROR;
1769 if (!driverName || !deviceName) {
1770 GST_ERROR("driverName or deviceName is not valid");
1771 goto DRM_INIT_ERROR;
1774 GST_INFO("Open drm device : %s", deviceName);
1776 /* get the drm_fd though opening the deviceName */
1777 xvimagesink->drm_fd = open(deviceName, O_RDWR);
1778 if (xvimagesink->drm_fd < 0) {
1779 GST_ERROR("cannot open drm device (%s)", deviceName);
1780 goto DRM_INIT_ERROR;
1783 /* get magic from drm to authentication */
1784 if (ioctl(xvimagesink->drm_fd, DRM_IOCTL_GET_MAGIC, &auth_arg)) {
1785 GST_ERROR("cannot get drm auth magic");
1786 goto DRM_INIT_ERROR;
1789 if (!DRI2Authenticate(dpy, RootWindow(dpy, DefaultScreen(dpy)), auth_arg.magic)) {
1790 GST_ERROR("cannot get drm authentication from X");
1791 goto DRM_INIT_ERROR;
1794 /* init gem handle */
1795 for (i = 0 ; i < MAX_PLANE_NUM ; i++) {
1796 xvimagesink->gem_handle[i] = 0;
1808 if (xvimagesink->drm_fd >= 0) {
1809 close(xvimagesink->drm_fd);
1810 xvimagesink->drm_fd = -1;
1825 static void drm_fini(GstXvImageSink *xvimagesink)
1829 if (xvimagesink->drm_fd >= 0) {
1830 GST_INFO("close drm_fd %d", xvimagesink->drm_fd);
1831 close(xvimagesink->drm_fd);
1832 xvimagesink->drm_fd = -1;
1834 GST_INFO("DRM device is NOT opened");
1840 static unsigned int drm_convert_dmabuf_gemname(GstXvImageSink *xvimagesink, int dmabuf_fd, int *gem_handle)
1845 struct drm_prime_handle prime_arg = {0,};
1846 struct drm_gem_flink flink_arg = {0,};
1848 if (!xvimagesink || !gem_handle) {
1849 GST_ERROR("handle[%p,%p] is NULL", xvimagesink, gem_handle);
1853 GST_LOG("START - fd : %d", dmabuf_fd);
1855 if (xvimagesink->drm_fd < 0) {
1856 GST_ERROR("DRM is not opened");
1860 if (dmabuf_fd <= 0) {
1861 GST_LOG("Ignore wrong dmabuf fd(%d)", dmabuf_fd);
1865 prime_arg.fd = dmabuf_fd;
1866 ret = ioctl(xvimagesink->drm_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime_arg);
1868 GST_ERROR("DRM_IOCTL_PRIME_FD_TO_HANDLE failed. ret %d, dmabuf fd : %d", ret, dmabuf_fd);
1872 GST_LOG("gem handle %u", prime_arg.handle);
1874 *gem_handle = prime_arg.handle;
1875 flink_arg.handle = prime_arg.handle;
1876 ret = ioctl(xvimagesink->drm_fd, DRM_IOCTL_GEM_FLINK, &flink_arg);
1878 GST_ERROR("DRM_IOCTL_GEM_FLINK failed. ret %d, gem_handle %u, gem_name %u", ret, *gem_handle, flink_arg.name);
1882 GST_LOG("DONE converted GEM name %u", flink_arg.name);
1884 return flink_arg.name;
1887 static void drm_close_gem(GstXvImageSink *xvimagesink, unsigned int *gem_handle)
1889 struct drm_gem_close close_arg = {0,};
1891 if (xvimagesink->drm_fd < 0 || !gem_handle) {
1892 GST_ERROR("DRM is not opened");
1896 if (*gem_handle <= 0) {
1897 GST_DEBUG("invalid gem handle %d", *gem_handle);
1901 GST_DEBUG("Call DRM_IOCTL_GEM_CLOSE");
1903 close_arg.handle = *gem_handle;
1904 if (ioctl(xvimagesink->drm_fd, DRM_IOCTL_GEM_CLOSE, &close_arg)) {
1905 GST_ERROR("cannot close drm gem handle %d", gem_handle);
1909 GST_DEBUG("close gem handle %d done", *gem_handle);
1915 #endif /* GST_EXT_XV_ENHANCEMENT */
1917 /* This function handles a GstXWindow creation
1918 * The width and height are the actual pixel size on the display */
1920 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
1921 gint width, gint height)
1923 GstXWindow *xwindow = NULL;
1925 #ifdef GST_EXT_XV_ENHANCEMENT
1926 XSetWindowAttributes win_attr;
1927 XWindowAttributes root_attr;
1928 #endif /* GST_EXT_XV_ENHANCEMENT */
1930 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1932 xwindow = g_new0 (GstXWindow, 1);
1934 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1935 #ifdef GST_EXT_XV_ENHANCEMENT
1937 if (xvimagesink->rotate_angle == 0 || xvimagesink->rotate_angle == 2) {
1938 xvimagesink->render_rect.w = xwindow->width = width;
1939 xvimagesink->render_rect.h = xwindow->height = height;
1942 xvimagesink->render_rect.w = xwindow->width = height;
1943 xvimagesink->render_rect.h = xwindow->height = width;
1946 XGetWindowAttributes(xvimagesink->xcontext->disp, xvimagesink->xcontext->root, &root_attr);
1948 if (xwindow->width > root_attr.width) {
1949 GST_INFO_OBJECT(xvimagesink, "Width[%d] is bigger than Max width. Set Max[%d].",
1950 xwindow->width, root_attr.width);
1951 xvimagesink->render_rect.w = xwindow->width = root_attr.width;
1953 if (xwindow->height > root_attr.height) {
1954 GST_INFO_OBJECT(xvimagesink, "Height[%d] is bigger than Max Height. Set Max[%d].",
1955 xwindow->height, root_attr.height);
1956 xvimagesink->render_rect.h = xwindow->height = root_attr.height;
1958 xwindow->internal = TRUE;
1960 g_mutex_lock (xvimagesink->x_lock);
1962 GST_DEBUG_OBJECT( xvimagesink, "window create [%dx%d]", xwindow->width, xwindow->height );
1964 xwindow->win = XCreateSimpleWindow(xvimagesink->xcontext->disp,
1965 xvimagesink->xcontext->root,
1966 0, 0, xwindow->width, xwindow->height,
1969 xvimagesink->xim_transparenter = make_transparent_image(xvimagesink->xcontext->disp,
1970 xvimagesink->xcontext->root,
1971 xwindow->width, xwindow->height);
1973 /* Make window manager not to change window size as Full screen */
1974 win_attr.override_redirect = True;
1975 XChangeWindowAttributes(xvimagesink->xcontext->disp, xwindow->win, CWOverrideRedirect, &win_attr);
1976 #else /* GST_EXT_XV_ENHANCEMENT */
1977 xvimagesink->render_rect.w = width;
1978 xvimagesink->render_rect.h = height;
1980 xwindow->width = width;
1981 xwindow->height = height;
1982 xwindow->internal = TRUE;
1984 g_mutex_lock (xvimagesink->x_lock);
1986 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
1987 xvimagesink->xcontext->root,
1988 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
1989 #endif /* GST_EXT_XV_ENHANCEMENT */
1991 /* We have to do that to prevent X from redrawing the background on
1992 * ConfigureNotify. This takes away flickering of video when resizing. */
1993 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
1995 /* set application name as a title */
1996 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
1998 if (xvimagesink->handle_events) {
2001 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2002 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2003 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2005 /* Tell the window manager we'd like delete client messages instead of
2007 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
2008 "WM_DELETE_WINDOW", True);
2009 if (wm_delete != None) {
2010 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
2015 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2016 xwindow->win, 0, &values);
2018 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
2020 XSync (xvimagesink->xcontext->disp, FALSE);
2022 g_mutex_unlock (xvimagesink->x_lock);
2024 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
2026 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
2031 /* This function destroys a GstXWindow */
2033 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
2034 GstXWindow * xwindow)
2036 g_return_if_fail (xwindow != NULL);
2037 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2039 g_mutex_lock (xvimagesink->x_lock);
2041 /* If we did not create that window we just free the GC and let it live */
2042 if (xwindow->internal) {
2043 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
2044 if (xvimagesink->xim_transparenter) {
2045 XDestroyImage(xvimagesink->xim_transparenter);
2046 xvimagesink->xim_transparenter = NULL;
2049 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
2052 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
2054 XSync (xvimagesink->xcontext->disp, FALSE);
2056 g_mutex_unlock (xvimagesink->x_lock);
2061 #ifdef GST_EXT_XV_ENHANCEMENT
2062 /* This function destroys a GstXWindow */
2064 gst_xvimagesink_xpixmap_destroy (GstXvImageSink * xvimagesink,
2065 GstXPixmap * xpixmap)
2067 g_return_if_fail (xpixmap != NULL);
2068 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2070 g_mutex_lock (xvimagesink->x_lock);
2072 XSelectInput (xvimagesink->xcontext->disp, xpixmap->pixmap, 0);
2074 XFreeGC (xvimagesink->xcontext->disp, xpixmap->gc);
2076 XSync (xvimagesink->xcontext->disp, FALSE);
2078 g_mutex_unlock (xvimagesink->x_lock);
2082 #endif /* GST_EXT_XV_ENHANCEMENT */
2085 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
2087 #ifdef GST_EXT_XV_ENHANCEMENT
2088 Window root_window, child_window;
2089 XWindowAttributes root_attr;
2093 unsigned int cur_win_width = 0;
2094 unsigned int cur_win_height = 0;
2095 unsigned int cur_win_border_width = 0;
2096 unsigned int cur_win_depth = 0;
2097 #else /* GST_EXT_XV_ENHANCEMENT */
2098 XWindowAttributes attr;
2099 #endif /* GST_EXT_XV_ENHANCEMENT */
2101 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2103 /* Update the window geometry */
2104 g_mutex_lock (xvimagesink->x_lock);
2105 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
2106 g_mutex_unlock (xvimagesink->x_lock);
2110 #ifdef GST_EXT_XV_ENHANCEMENT
2111 /* Get root window and size of current window */
2112 XGetGeometry( xvimagesink->xcontext->disp, xvimagesink->xwindow->win, &root_window,
2113 &cur_win_x, &cur_win_y, /* relative x, y */
2114 &cur_win_width, &cur_win_height,
2115 &cur_win_border_width, &cur_win_depth );
2117 xvimagesink->xwindow->width = cur_win_width;
2118 xvimagesink->xwindow->height = cur_win_height;
2120 /* Get absolute coordinates of current window */
2121 XTranslateCoordinates( xvimagesink->xcontext->disp,
2122 xvimagesink->xwindow->win,
2125 &cur_win_x, &cur_win_y, // relative x, y to root window == absolute x, y
2128 xvimagesink->xwindow->x = cur_win_x;
2129 xvimagesink->xwindow->y = cur_win_y;
2131 /* Get size of root window == size of screen */
2132 XGetWindowAttributes(xvimagesink->xcontext->disp, root_window, &root_attr);
2134 xvimagesink->scr_w = root_attr.width;
2135 xvimagesink->scr_h = root_attr.height;
2137 if (!xvimagesink->have_render_rect) {
2138 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2139 xvimagesink->render_rect.w = cur_win_width;
2140 xvimagesink->render_rect.h = cur_win_height;
2143 GST_LOG_OBJECT(xvimagesink, "screen size %dx%d, current window geometry %d,%d,%dx%d, render_rect %d,%d,%dx%d",
2144 xvimagesink->scr_w, xvimagesink->scr_h,
2145 xvimagesink->xwindow->x, xvimagesink->xwindow->y,
2146 xvimagesink->xwindow->width, xvimagesink->xwindow->height,
2147 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
2148 xvimagesink->render_rect.w, xvimagesink->render_rect.h);
2149 #else /* GST_EXT_XV_ENHANCEMENT */
2150 XGetWindowAttributes (xvimagesink->xcontext->disp,
2151 xvimagesink->xwindow->win, &attr);
2153 xvimagesink->xwindow->width = attr.width;
2154 xvimagesink->xwindow->height = attr.height;
2156 if (!xvimagesink->have_render_rect) {
2157 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2158 xvimagesink->render_rect.w = attr.width;
2159 xvimagesink->render_rect.h = attr.height;
2161 #endif /* GST_EXT_XV_ENHANCEMENT */
2163 g_mutex_unlock (xvimagesink->x_lock);
2167 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
2168 GstXWindow * xwindow)
2170 g_return_if_fail (xwindow != NULL);
2171 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2173 g_mutex_lock (xvimagesink->x_lock);
2175 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
2177 #ifndef GST_EXT_XV_ENHANCEMENT
2178 /* Preview area is not updated before other UI is updated in the screen. */
2179 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
2180 xvimagesink->xcontext->black);
2182 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
2183 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
2184 xvimagesink->render_rect.w, xvimagesink->render_rect.h);
2185 #endif /* GST_EXT_XV_ENHANCEMENT */
2187 XSync (xvimagesink->xcontext->disp, FALSE);
2189 g_mutex_unlock (xvimagesink->x_lock);
2192 /* This function commits our internal colorbalance settings to our grabbed Xv
2193 port. If the xcontext is not initialized yet it simply returns */
2195 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
2197 GList *channels = NULL;
2199 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2201 /* If we haven't initialized the X context we can't update anything */
2202 if (xvimagesink->xcontext == NULL)
2205 /* Don't set the attributes if they haven't been changed, to avoid
2206 * rounding errors changing the values */
2207 if (!xvimagesink->cb_changed)
2210 /* For each channel of the colorbalance we calculate the correct value
2211 doing range conversion and then set the Xv port attribute to match our
2213 channels = xvimagesink->xcontext->channels_list;
2216 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
2217 GstColorBalanceChannel *channel = NULL;
2220 gdouble convert_coef;
2222 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
2223 g_object_ref (channel);
2225 /* Our range conversion coef */
2226 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
2228 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2229 value = xvimagesink->hue;
2230 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2231 value = xvimagesink->saturation;
2232 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2233 value = xvimagesink->contrast;
2234 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2235 value = xvimagesink->brightness;
2237 g_warning ("got an unknown channel %s", channel->label);
2238 g_object_unref (channel);
2242 /* Committing to Xv port */
2243 g_mutex_lock (xvimagesink->x_lock);
2245 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
2246 if (prop_atom != None) {
2249 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
2250 XvSetPortAttribute (xvimagesink->xcontext->disp,
2251 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
2253 g_mutex_unlock (xvimagesink->x_lock);
2255 g_object_unref (channel);
2257 channels = g_list_next (channels);
2261 /* This function handles XEvents that might be in the queue. It generates
2262 GstEvent that will be sent upstream in the pipeline to handle interactivity
2263 and navigation. It will also listen for configure events on the window to
2264 trigger caps renegotiation so on the fly software scaling can work. */
2266 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
2269 guint pointer_x = 0, pointer_y = 0;
2270 gboolean pointer_moved = FALSE;
2271 gboolean exposed = FALSE, configured = FALSE;
2273 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2275 /* Handle Interaction, produces navigation events */
2277 /* We get all pointer motion events, only the last position is
2279 g_mutex_lock (xvimagesink->flow_lock);
2280 g_mutex_lock (xvimagesink->x_lock);
2281 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
2282 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
2283 g_mutex_unlock (xvimagesink->x_lock);
2284 g_mutex_unlock (xvimagesink->flow_lock);
2288 pointer_x = e.xmotion.x;
2289 pointer_y = e.xmotion.y;
2290 pointer_moved = TRUE;
2295 g_mutex_lock (xvimagesink->flow_lock);
2296 g_mutex_lock (xvimagesink->x_lock);
2298 if (pointer_moved) {
2299 g_mutex_unlock (xvimagesink->x_lock);
2300 g_mutex_unlock (xvimagesink->flow_lock);
2302 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
2303 pointer_x, pointer_y);
2304 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
2305 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
2307 g_mutex_lock (xvimagesink->flow_lock);
2308 g_mutex_lock (xvimagesink->x_lock);
2311 /* We get all events on our window to throw them upstream */
2312 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
2313 xvimagesink->xwindow->win,
2314 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
2318 /* We lock only for the X function call */
2319 g_mutex_unlock (xvimagesink->x_lock);
2320 g_mutex_unlock (xvimagesink->flow_lock);
2324 /* Mouse button pressed over our window. We send upstream
2325 events for interactivity/navigation */
2326 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
2327 e.xbutton.button, e.xbutton.x, e.xbutton.y);
2328 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
2329 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
2332 /* Mouse button released over our window. We send upstream
2333 events for interactivity/navigation */
2334 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
2335 e.xbutton.button, e.xbutton.x, e.xbutton.y);
2336 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
2337 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
2341 /* Key pressed/released over our window. We send upstream
2342 events for interactivity/navigation */
2343 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
2344 e.xkey.keycode, e.xkey.x, e.xkey.y);
2345 g_mutex_lock (xvimagesink->x_lock);
2346 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
2348 g_mutex_unlock (xvimagesink->x_lock);
2349 if (keysym != NoSymbol) {
2350 char *key_str = NULL;
2352 g_mutex_lock (xvimagesink->x_lock);
2353 key_str = XKeysymToString (keysym);
2354 g_mutex_unlock (xvimagesink->x_lock);
2355 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
2356 e.type == KeyPress ? "key-press" : "key-release", key_str);
2358 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
2359 e.type == KeyPress ? "key-press" : "key-release", "unknown");
2363 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
2365 g_mutex_lock (xvimagesink->flow_lock);
2366 g_mutex_lock (xvimagesink->x_lock);
2370 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
2371 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
2376 case ConfigureNotify:
2377 g_mutex_unlock (xvimagesink->x_lock);
2378 #ifdef GST_EXT_XV_ENHANCEMENT
2379 GST_INFO_OBJECT (xvimagesink, "Call gst_xvimagesink_xwindow_update_geometry!");
2380 #endif /* GST_EXT_XV_ENHANCEMENT */
2381 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2382 g_mutex_lock (xvimagesink->x_lock);
2390 if (xvimagesink->handle_expose && (exposed || configured)) {
2391 g_mutex_unlock (xvimagesink->x_lock);
2392 g_mutex_unlock (xvimagesink->flow_lock);
2394 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
2396 g_mutex_lock (xvimagesink->flow_lock);
2397 g_mutex_lock (xvimagesink->x_lock);
2400 /* Handle Display events */
2401 while (XPending (xvimagesink->xcontext->disp)) {
2402 XNextEvent (xvimagesink->xcontext->disp, &e);
2405 case ClientMessage:{
2408 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
2409 "WM_DELETE_WINDOW", True);
2410 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
2411 /* Handle window deletion by posting an error on the bus */
2412 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
2413 ("Output window was closed"), (NULL));
2415 g_mutex_unlock (xvimagesink->x_lock);
2416 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2417 xvimagesink->xwindow = NULL;
2418 g_mutex_lock (xvimagesink->x_lock);
2422 #ifdef GST_EXT_XV_ENHANCEMENT
2423 case VisibilityNotify:
2424 if (e.xvisibility.window == xvimagesink->xwindow->win) {
2425 if (e.xvisibility.state == VisibilityFullyObscured) {
2428 GST_INFO_OBJECT(xvimagesink, "current window is FULLY HIDED");
2430 xvimagesink->is_hided = TRUE;
2431 atom_stream = XInternAtom(xvimagesink->xcontext->disp,
2432 "_USER_WM_PORT_ATTRIBUTE_STREAM_OFF", False);
2433 if (atom_stream != None) {
2434 if (XvSetPortAttribute(xvimagesink->xcontext->disp,
2435 xvimagesink->xcontext->xv_port_id,
2436 atom_stream, 0) != Success) {
2437 GST_WARNING_OBJECT(xvimagesink, "STREAM OFF failed");
2439 XSync(xvimagesink->xcontext->disp, FALSE);
2441 GST_WARNING_OBJECT(xvimagesink, "atom_stream is NONE");
2444 GST_INFO_OBJECT(xvimagesink, "current window is SHOWN");
2446 if (xvimagesink->is_hided) {
2447 g_mutex_unlock(xvimagesink->x_lock);
2448 g_mutex_unlock(xvimagesink->flow_lock);
2450 xvimagesink->is_hided = FALSE;
2451 gst_xvimagesink_expose(GST_X_OVERLAY(xvimagesink));
2453 g_mutex_lock(xvimagesink->flow_lock);
2454 g_mutex_lock(xvimagesink->x_lock);
2456 GST_INFO_OBJECT(xvimagesink, "current window is not HIDED, skip this event");
2461 #endif /* GST_EXT_XV_ENHANCEMENT */
2467 g_mutex_unlock (xvimagesink->x_lock);
2468 g_mutex_unlock (xvimagesink->flow_lock);
2472 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
2473 XvAdaptorInfo * adaptors, int adaptor_no)
2478 /* Do we support XvImageMask ? */
2479 if (!(adaptors[adaptor_no].type & XvImageMask)) {
2480 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
2481 adaptors[adaptor_no].name);
2485 /* We found such an adaptor, looking for an available port */
2486 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
2487 /* We try to grab the port */
2488 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
2489 if (Success == res) {
2490 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
2491 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
2492 adaptors[adaptor_no].num_ports);
2494 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
2495 adaptors[adaptor_no].name, res);
2500 /* This function generates a caps with all supported format by the first
2501 Xv grabable port we find. We store each one of the supported formats in a
2502 format list and append the format to a newly created caps that we return
2503 If this function does not return NULL because of an error, it also grabs
2504 the port via XvGrabPort */
2506 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
2507 GstXContext * xcontext)
2510 XvAdaptorInfo *adaptors;
2512 XvImageFormatValues *formats = NULL;
2514 XvEncodingInfo *encodings = NULL;
2515 gulong max_w = G_MAXINT, max_h = G_MAXINT;
2516 GstCaps *caps = NULL;
2517 GstCaps *rgb_caps = NULL;
2519 g_return_val_if_fail (xcontext != NULL, NULL);
2521 /* First let's check that XVideo extension is available */
2522 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
2523 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
2524 ("Could not initialise Xv output"),
2525 ("XVideo extension is not available"));
2529 /* Then we get adaptors list */
2530 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
2531 &xcontext->nb_adaptors, &adaptors)) {
2532 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
2533 ("Could not initialise Xv output"),
2534 ("Failed getting XV adaptors list"));
2538 xcontext->xv_port_id = 0;
2540 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
2542 xcontext->adaptors =
2543 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
2545 /* Now fill up our adaptor name array */
2546 for (i = 0; i < xcontext->nb_adaptors; i++) {
2547 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
2550 if (xvimagesink->adaptor_no >= 0 &&
2551 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
2552 /* Find xv port from user defined adaptor */
2553 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
2554 xvimagesink->adaptor_no);
2557 if (!xcontext->xv_port_id) {
2558 /* Now search for an adaptor that supports XvImageMask */
2559 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
2560 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
2561 xvimagesink->adaptor_no = i;
2565 XvFreeAdaptorInfo (adaptors);
2567 if (!xcontext->xv_port_id) {
2568 xvimagesink->adaptor_no = -1;
2569 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
2570 ("Could not initialise Xv output"), ("No port available"));
2574 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
2576 int count, todo = 3;
2577 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
2578 xcontext->xv_port_id, &count);
2579 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
2580 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
2581 static const char colorkey[] = "XV_COLORKEY";
2583 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
2585 xvimagesink->have_autopaint_colorkey = FALSE;
2586 xvimagesink->have_double_buffer = FALSE;
2587 xvimagesink->have_colorkey = FALSE;
2589 for (i = 0; ((i < count) && todo); i++)
2590 if (!strcmp (attr[i].name, autopaint)) {
2591 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
2593 /* turn on autopaint colorkey */
2594 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
2595 (xvimagesink->autopaint_colorkey ? 1 : 0));
2597 xvimagesink->have_autopaint_colorkey = TRUE;
2598 } else if (!strcmp (attr[i].name, dbl_buffer)) {
2599 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
2601 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
2602 (xvimagesink->double_buffer ? 1 : 0));
2604 xvimagesink->have_double_buffer = TRUE;
2605 } else if (!strcmp (attr[i].name, colorkey)) {
2606 /* Set the colorkey, default is something that is dark but hopefully
2607 * won't randomly appear on the screen elsewhere (ie not black or greys)
2608 * can be overridden by setting "colorkey" property
2610 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
2612 gboolean set_attr = TRUE;
2615 /* set a colorkey in the right format RGB565/RGB888
2616 * We only handle these 2 cases, because they're the only types of
2617 * devices we've encountered. If we don't recognise it, leave it alone
2619 cr = (xvimagesink->colorkey >> 16);
2620 cg = (xvimagesink->colorkey >> 8) & 0xFF;
2621 cb = (xvimagesink->colorkey) & 0xFF;
2622 switch (xcontext->depth) {
2623 case 16: /* RGB 565 */
2627 ckey = (cr << 11) | (cg << 5) | cb;
2630 case 32: /* RGB 888 / ARGB 8888 */
2631 ckey = (cr << 16) | (cg << 8) | cb;
2634 GST_DEBUG_OBJECT (xvimagesink,
2635 "Unknown bit depth %d for Xv Colorkey - not adjusting",
2642 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
2643 (guint32) attr[i].max_value);
2644 GST_LOG_OBJECT (xvimagesink,
2645 "Setting color key for display depth %d to 0x%x",
2646 xcontext->depth, ckey);
2648 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
2652 xvimagesink->have_colorkey = TRUE;
2658 /* Get the list of encodings supported by the adapter and look for the
2659 * XV_IMAGE encoding so we can determine the maximum width and height
2661 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
2664 for (i = 0; i < nb_encodings; i++) {
2665 GST_LOG_OBJECT (xvimagesink,
2666 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
2667 i, encodings[i].name, encodings[i].width, encodings[i].height,
2668 encodings[i].rate.numerator, encodings[i].rate.denominator);
2669 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
2670 max_w = encodings[i].width;
2671 max_h = encodings[i].height;
2672 #ifdef GST_EXT_XV_ENHANCEMENT
2673 xvimagesink->scr_w = max_w;
2674 xvimagesink->scr_h = max_h;
2675 #endif /* GST_EXT_XV_ENHANCEMENT */
2679 XvFreeEncodingInfo (encodings);
2681 /* We get all image formats supported by our port */
2682 formats = XvListImageFormats (xcontext->disp,
2683 xcontext->xv_port_id, &nb_formats);
2684 caps = gst_caps_new_empty ();
2685 for (i = 0; i < nb_formats; i++) {
2686 GstCaps *format_caps = NULL;
2687 gboolean is_rgb_format = FALSE;
2689 /* We set the image format of the xcontext to an existing one. This
2690 is just some valid image format for making our xshm calls check before
2691 caps negotiation really happens. */
2692 xcontext->im_format = formats[i].id;
2694 switch (formats[i].type) {
2697 XvImageFormatValues *fmt = &(formats[i]);
2698 gint endianness = G_BIG_ENDIAN;
2700 if (fmt->byte_order == LSBFirst) {
2701 /* our caps system handles 24/32bpp RGB as big-endian. */
2702 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
2703 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
2704 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
2705 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
2707 if (fmt->bits_per_pixel == 24) {
2708 fmt->red_mask >>= 8;
2709 fmt->green_mask >>= 8;
2710 fmt->blue_mask >>= 8;
2713 endianness = G_LITTLE_ENDIAN;
2716 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
2717 "endianness", G_TYPE_INT, endianness,
2718 "depth", G_TYPE_INT, fmt->depth,
2719 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
2720 "red_mask", G_TYPE_INT, fmt->red_mask,
2721 "green_mask", G_TYPE_INT, fmt->green_mask,
2722 "blue_mask", G_TYPE_INT, fmt->blue_mask,
2723 "width", GST_TYPE_INT_RANGE, 1, max_w,
2724 "height", GST_TYPE_INT_RANGE, 1, max_h,
2725 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
2727 is_rgb_format = TRUE;
2731 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
2732 "format", GST_TYPE_FOURCC, formats[i].id,
2733 "width", GST_TYPE_INT_RANGE, 1, max_w,
2734 "height", GST_TYPE_INT_RANGE, 1, max_h,
2735 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
2738 g_assert_not_reached ();
2743 GstXvImageFormat *format = NULL;
2745 format = g_new0 (GstXvImageFormat, 1);
2747 format->format = formats[i].id;
2748 format->caps = gst_caps_copy (format_caps);
2749 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
2752 if (is_rgb_format) {
2753 if (rgb_caps == NULL)
2754 rgb_caps = format_caps;
2756 gst_caps_append (rgb_caps, format_caps);
2758 gst_caps_append (caps, format_caps);
2762 /* Collected all caps into either the caps or rgb_caps structures.
2763 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
2765 gst_caps_append (caps, rgb_caps);
2770 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
2772 if (gst_caps_is_empty (caps)) {
2773 gst_caps_unref (caps);
2774 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2775 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
2776 ("No supported format found"));
2784 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
2786 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2788 GST_OBJECT_LOCK (xvimagesink);
2789 while (xvimagesink->running) {
2790 GST_OBJECT_UNLOCK (xvimagesink);
2792 if (xvimagesink->xwindow) {
2793 gst_xvimagesink_handle_xevents (xvimagesink);
2795 /* FIXME: do we want to align this with the framerate or anything else? */
2796 g_usleep (G_USEC_PER_SEC / 20);
2798 GST_OBJECT_LOCK (xvimagesink);
2800 GST_OBJECT_UNLOCK (xvimagesink);
2806 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
2808 GThread *thread = NULL;
2810 /* don't start the thread too early */
2811 if (xvimagesink->xcontext == NULL) {
2815 GST_OBJECT_LOCK (xvimagesink);
2816 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
2817 if (!xvimagesink->event_thread) {
2818 /* Setup our event listening thread */
2819 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
2820 xvimagesink->handle_expose, xvimagesink->handle_events);
2821 xvimagesink->running = TRUE;
2822 #if !GLIB_CHECK_VERSION (2, 31, 0)
2823 xvimagesink->event_thread = g_thread_create (
2824 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
2826 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
2827 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
2831 if (xvimagesink->event_thread) {
2832 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
2833 xvimagesink->handle_expose, xvimagesink->handle_events);
2834 xvimagesink->running = FALSE;
2835 /* grab thread and mark it as NULL */
2836 thread = xvimagesink->event_thread;
2837 xvimagesink->event_thread = NULL;
2840 GST_OBJECT_UNLOCK (xvimagesink);
2842 /* Wait for our event thread to finish */
2844 g_thread_join (thread);
2849 #ifdef GST_EXT_XV_ENHANCEMENT
2851 * gst_xvimagesink_prepare_xid:
2852 * @overlay: a #GstXOverlay which does not yet have an XWindow or XPixmap.
2854 * This will post a "prepare-xid" element message with video size and display size on the bus
2855 * to give applications an opportunity to call
2856 * gst_x_overlay_set_xwindow_id() before a plugin creates its own
2859 * This function should only be used by video overlay plugin developers.
2862 gst_xvimagesink_prepare_xid (GstXOverlay * overlay)
2867 g_return_if_fail (overlay != NULL);
2868 g_return_if_fail (GST_IS_X_OVERLAY (overlay));
2870 GstXvImageSink *xvimagesink;
2871 xvimagesink = GST_XVIMAGESINK (GST_OBJECT (overlay));
2873 GST_DEBUG ("post \"prepare-xid\" element message with video-width(%d), video-height(%d), display-width(%d), display-height(%d)",
2874 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink), xvimagesink->xcontext->width, xvimagesink->xcontext->height);
2876 GST_LOG_OBJECT (GST_OBJECT (overlay), "prepare xid");
2877 s = gst_structure_new ("prepare-xid",
2878 "video-width", G_TYPE_INT, GST_VIDEO_SINK_WIDTH (xvimagesink),
2879 "video-height", G_TYPE_INT, GST_VIDEO_SINK_HEIGHT (xvimagesink),
2880 "display-width", G_TYPE_INT, xvimagesink->xcontext->width,
2881 "display-height", G_TYPE_INT, xvimagesink->xcontext->height,
2883 msg = gst_message_new_element (GST_OBJECT (overlay), s);
2884 gst_element_post_message (GST_ELEMENT (overlay), msg);
2886 #endif /* GST_EXT_XV_ENHANCEMENT */
2889 /* This function calculates the pixel aspect ratio based on the properties
2890 * in the xcontext structure and stores it there. */
2892 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
2894 static const gint par[][2] = {
2895 {1, 1}, /* regular screen */
2896 {16, 15}, /* PAL TV */
2897 {11, 10}, /* 525 line Rec.601 video */
2898 {54, 59}, /* 625 line Rec.601 video */
2899 {64, 45}, /* 1280x1024 on 16:9 display */
2900 {5, 3}, /* 1280x1024 on 4:3 display */
2901 {4, 3} /* 800x600 on 16:9 display */
2908 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
2910 /* first calculate the "real" ratio based on the X values;
2911 * which is the "physical" w/h divided by the w/h in pixels of the display */
2912 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
2913 / (xcontext->heightmm * xcontext->width);
2915 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
2917 if (xcontext->width == 720 && xcontext->height == 576) {
2918 ratio = 4.0 * 576 / (3.0 * 720);
2920 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
2921 /* now find the one from par[][2] with the lowest delta to the real one */
2925 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
2926 gdouble this_delta = DELTA (i);
2928 if (this_delta < delta) {
2934 GST_DEBUG ("Decided on index %d (%d/%d)", index,
2935 par[index][0], par[index][1]);
2937 g_free (xcontext->par);
2938 xcontext->par = g_new0 (GValue, 1);
2939 g_value_init (xcontext->par, GST_TYPE_FRACTION);
2940 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
2941 GST_DEBUG ("set xcontext PAR to %d/%d",
2942 gst_value_get_fraction_numerator (xcontext->par),
2943 gst_value_get_fraction_denominator (xcontext->par));
2946 /* This function gets the X Display and global info about it. Everything is
2947 stored in our object and will be cleaned when the object is disposed. Note
2948 here that caps for supported format are generated without any window or
2950 static GstXContext *
2951 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
2953 GstXContext *xcontext = NULL;
2954 XPixmapFormatValues *px_formats = NULL;
2955 gint nb_formats = 0, i, j, N_attr;
2956 XvAttribute *xv_attr;
2958 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
2959 "XV_BRIGHTNESS", "XV_CONTRAST"
2962 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2964 xcontext = g_new0 (GstXContext, 1);
2965 xcontext->im_format = 0;
2967 g_mutex_lock (xvimagesink->x_lock);
2969 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
2971 if (!xcontext->disp) {
2972 g_mutex_unlock (xvimagesink->x_lock);
2974 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2975 ("Could not initialise Xv output"), ("Could not open display"));
2979 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
2980 xcontext->screen_num = DefaultScreen (xcontext->disp);
2981 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
2982 xcontext->root = DefaultRootWindow (xcontext->disp);
2983 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
2984 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
2985 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
2987 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
2988 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
2989 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
2990 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
2992 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
2993 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
2995 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
2996 /* We get supported pixmap formats at supported depth */
2997 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
3000 XCloseDisplay (xcontext->disp);
3001 g_mutex_unlock (xvimagesink->x_lock);
3002 g_free (xcontext->par);
3004 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
3005 ("Could not initialise Xv output"), ("Could not get pixel formats"));
3009 /* We get bpp value corresponding to our running depth */
3010 for (i = 0; i < nb_formats; i++) {
3011 if (px_formats[i].depth == xcontext->depth)
3012 xcontext->bpp = px_formats[i].bits_per_pixel;
3017 xcontext->endianness =
3018 (ImageByteOrder (xcontext->disp) ==
3019 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
3021 /* our caps system handles 24/32bpp RGB as big-endian. */
3022 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
3023 xcontext->endianness == G_LITTLE_ENDIAN) {
3024 xcontext->endianness = G_BIG_ENDIAN;
3025 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
3026 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
3027 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
3028 if (xcontext->bpp == 24) {
3029 xcontext->visual->red_mask >>= 8;
3030 xcontext->visual->green_mask >>= 8;
3031 xcontext->visual->blue_mask >>= 8;
3035 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
3037 if (!xcontext->caps) {
3038 XCloseDisplay (xcontext->disp);
3039 g_mutex_unlock (xvimagesink->x_lock);
3040 g_free (xcontext->par);
3042 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
3046 /* Search for XShm extension support */
3047 if (XShmQueryExtension (xcontext->disp) &&
3048 gst_xvimagesink_check_xshm_calls (xcontext)) {
3049 xcontext->use_xshm = TRUE;
3050 GST_DEBUG ("xvimagesink is using XShm extension");
3052 #endif /* HAVE_XSHM */
3054 xcontext->use_xshm = FALSE;
3055 GST_DEBUG ("xvimagesink is not using XShm extension");
3058 xv_attr = XvQueryPortAttributes (xcontext->disp,
3059 xcontext->xv_port_id, &N_attr);
3062 /* Generate the channels list */
3063 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
3064 XvAttribute *matching_attr = NULL;
3066 /* Retrieve the property atom if it exists. If it doesn't exist,
3067 * the attribute itself must not either, so we can skip */
3068 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
3069 if (prop_atom == None)
3072 if (xv_attr != NULL) {
3073 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
3074 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
3075 matching_attr = xv_attr + j;
3078 if (matching_attr) {
3079 GstColorBalanceChannel *channel;
3081 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
3082 channel->label = g_strdup (channels[i]);
3083 channel->min_value = matching_attr->min_value;
3084 channel->max_value = matching_attr->max_value;
3086 xcontext->channels_list = g_list_append (xcontext->channels_list,
3089 /* If the colorbalance settings have not been touched we get Xv values
3090 as defaults and update our internal variables */
3091 if (!xvimagesink->cb_changed) {
3094 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
3096 /* Normalize val to [-1000, 1000] */
3097 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
3098 (double) (channel->max_value - channel->min_value));
3100 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
3101 xvimagesink->hue = val;
3102 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
3103 xvimagesink->saturation = val;
3104 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
3105 xvimagesink->brightness = val;
3106 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
3107 xvimagesink->contrast = val;
3115 #ifdef GST_EXT_XV_ENHANCEMENT
3116 set_display_mode(xcontext, xvimagesink->display_mode);
3117 #endif /* GST_EXT_XV_ENHANCEMENT */
3119 g_mutex_unlock (xvimagesink->x_lock);
3124 /* This function cleans the X context. Closing the Display, releasing the XV
3125 port and unrefing the caps for supported formats. */
3127 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
3129 GList *formats_list, *channels_list;
3130 GstXContext *xcontext;
3133 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3135 GST_OBJECT_LOCK (xvimagesink);
3136 if (xvimagesink->xcontext == NULL) {
3137 GST_OBJECT_UNLOCK (xvimagesink);
3141 /* Take the XContext from the sink and clean it up */
3142 xcontext = xvimagesink->xcontext;
3143 xvimagesink->xcontext = NULL;
3145 GST_OBJECT_UNLOCK (xvimagesink);
3148 formats_list = xcontext->formats_list;
3150 while (formats_list) {
3151 GstXvImageFormat *format = formats_list->data;
3153 gst_caps_unref (format->caps);
3155 formats_list = g_list_next (formats_list);
3158 if (xcontext->formats_list)
3159 g_list_free (xcontext->formats_list);
3161 channels_list = xcontext->channels_list;
3163 while (channels_list) {
3164 GstColorBalanceChannel *channel = channels_list->data;
3166 g_object_unref (channel);
3167 channels_list = g_list_next (channels_list);
3170 if (xcontext->channels_list)
3171 g_list_free (xcontext->channels_list);
3173 gst_caps_unref (xcontext->caps);
3174 if (xcontext->last_caps)
3175 gst_caps_replace (&xcontext->last_caps, NULL);
3177 for (i = 0; i < xcontext->nb_adaptors; i++) {
3178 g_free (xcontext->adaptors[i]);
3181 g_free (xcontext->adaptors);
3183 g_free (xcontext->par);
3185 g_mutex_lock (xvimagesink->x_lock);
3187 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
3189 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
3191 XCloseDisplay (xcontext->disp);
3193 g_mutex_unlock (xvimagesink->x_lock);
3199 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
3201 g_mutex_lock (xvimagesink->pool_lock);
3203 while (xvimagesink->image_pool) {
3204 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
3206 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
3207 xvimagesink->image_pool);
3208 gst_xvimage_buffer_free (xvimage);
3211 g_mutex_unlock (xvimagesink->pool_lock);
3216 /* This function tries to get a format matching with a given caps in the
3217 supported list of formats we generated in gst_xvimagesink_get_xv_support */
3219 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
3224 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3226 list = xvimagesink->xcontext->formats_list;
3229 GstXvImageFormat *format = list->data;
3232 if (gst_caps_can_intersect (caps, format->caps)) {
3233 return format->format;
3236 list = g_list_next (list);
3243 gst_xvimagesink_getcaps (GstBaseSink * bsink)
3245 GstXvImageSink *xvimagesink;
3247 xvimagesink = GST_XVIMAGESINK (bsink);
3249 if (xvimagesink->xcontext)
3250 return gst_caps_ref (xvimagesink->xcontext->caps);
3253 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
3258 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
3260 GstXvImageSink *xvimagesink;
3261 GstStructure *structure;
3262 guint32 im_format = 0;
3264 gint video_width, video_height;
3265 gint disp_x, disp_y;
3266 gint disp_width, disp_height;
3267 gint video_par_n, video_par_d; /* video's PAR */
3268 gint display_par_n, display_par_d; /* display's PAR */
3269 const GValue *caps_par;
3270 const GValue *caps_disp_reg;
3273 #ifdef GST_EXT_XV_ENHANCEMENT
3274 gboolean enable_last_buffer;
3275 #endif /* #ifdef GST_EXT_XV_ENHANCEMENT */
3277 xvimagesink = GST_XVIMAGESINK (bsink);
3279 GST_DEBUG_OBJECT (xvimagesink,
3280 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
3281 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
3283 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
3284 goto incompatible_caps;
3286 structure = gst_caps_get_structure (caps, 0);
3287 ret = gst_structure_get_int (structure, "width", &video_width);
3288 ret &= gst_structure_get_int (structure, "height", &video_height);
3289 fps = gst_structure_get_value (structure, "framerate");
3290 ret &= (fps != NULL);
3293 goto incomplete_caps;
3295 #ifdef GST_EXT_XV_ENHANCEMENT
3296 xvimagesink->aligned_width = video_width;
3297 xvimagesink->aligned_height = video_height;
3299 /* get enable-last-buffer */
3300 g_object_get(G_OBJECT(xvimagesink), "enable-last-buffer", &enable_last_buffer, NULL);
3301 GST_INFO_OBJECT(xvimagesink, "current enable-last-buffer : %d", enable_last_buffer);
3303 /* flush if enable-last-buffer is TRUE */
3304 if (enable_last_buffer) {
3305 GST_INFO_OBJECT(xvimagesink, "flush last-buffer");
3306 g_object_set(G_OBJECT(xvimagesink), "enable-last-buffer", FALSE, NULL);
3307 g_object_set(G_OBJECT(xvimagesink), "enable-last-buffer", TRUE, NULL);
3309 #endif /* GST_EXT_XV_ENHANCEMENT */
3311 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
3312 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
3314 xvimagesink->video_width = video_width;
3315 xvimagesink->video_height = video_height;
3317 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
3318 if (im_format == -1)
3319 goto invalid_format;
3321 /* get aspect ratio from caps if it's present, and
3322 * convert video width and height to a display width and height
3323 * using wd / hd = wv / hv * PARv / PARd */
3325 /* get video's PAR */
3326 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
3328 video_par_n = gst_value_get_fraction_numerator (caps_par);
3329 video_par_d = gst_value_get_fraction_denominator (caps_par);
3334 /* get display's PAR */
3335 if (xvimagesink->par) {
3336 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
3337 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
3343 /* get the display region */
3344 caps_disp_reg = gst_structure_get_value (structure, "display-region");
3345 if (caps_disp_reg) {
3346 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
3347 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
3348 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
3350 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
3352 disp_x = disp_y = 0;
3353 disp_width = video_width;
3354 disp_height = video_height;
3357 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
3358 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
3361 xvimagesink->disp_x = disp_x;
3362 xvimagesink->disp_y = disp_y;
3363 xvimagesink->disp_width = disp_width;
3364 xvimagesink->disp_height = disp_height;
3366 GST_DEBUG_OBJECT (xvimagesink,
3367 "video width/height: %dx%d, calculated display ratio: %d/%d",
3368 video_width, video_height, num, den);
3370 /* now find a width x height that respects this display ratio.
3371 * prefer those that have one of w/h the same as the incoming video
3372 * using wd / hd = num / den */
3374 /* start with same height, because of interlaced video */
3375 /* check hd / den is an integer scale factor, and scale wd with the PAR */
3376 if (video_height % den == 0) {
3377 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
3378 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
3379 gst_util_uint64_scale_int (video_height, num, den);
3380 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
3381 } else if (video_width % num == 0) {
3382 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
3383 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
3384 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
3385 gst_util_uint64_scale_int (video_width, den, num);
3387 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
3388 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
3389 gst_util_uint64_scale_int (video_height, num, den);
3390 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
3392 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
3393 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
3395 /* Notify application to set xwindow id now */
3396 g_mutex_lock (xvimagesink->flow_lock);
3397 #ifdef GST_EXT_XV_ENHANCEMENT
3398 if (!xvimagesink->xwindow && !xvimagesink->get_pixmap_cb) {
3399 g_mutex_unlock (xvimagesink->flow_lock);
3400 gst_xvimagesink_prepare_xid (GST_X_OVERLAY (xvimagesink));
3402 if (!xvimagesink->xwindow) {
3403 g_mutex_unlock (xvimagesink->flow_lock);
3404 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
3407 g_mutex_unlock (xvimagesink->flow_lock);
3410 /* Creating our window and our image with the display size in pixels */
3411 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
3412 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
3413 goto no_display_size;
3415 g_mutex_lock (xvimagesink->flow_lock);
3416 #ifdef GST_EXT_XV_ENHANCEMENT
3417 if (!xvimagesink->xwindow && !xvimagesink->get_pixmap_cb) {
3418 GST_DEBUG_OBJECT (xvimagesink, "xwindow is null and not multi-pixmaps usage case");
3420 if (!xvimagesink->xwindow) {
3422 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
3423 GST_VIDEO_SINK_WIDTH (xvimagesink),
3424 GST_VIDEO_SINK_HEIGHT (xvimagesink));
3427 /* After a resize, we want to redraw the borders in case the new frame size
3428 * doesn't cover the same area */
3429 xvimagesink->redraw_border = TRUE;
3431 /* We renew our xvimage only if size or format changed;
3432 * the xvimage is the same size as the video pixel size */
3433 if ((xvimagesink->xvimage) &&
3434 ((im_format != xvimagesink->xvimage->im_format) ||
3435 (video_width != xvimagesink->xvimage->width) ||
3436 (video_height != xvimagesink->xvimage->height))) {
3437 GST_DEBUG_OBJECT (xvimagesink,
3438 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
3439 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
3440 GST_FOURCC_ARGS (im_format));
3441 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
3442 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
3443 xvimagesink->xvimage = NULL;
3446 g_mutex_unlock (xvimagesink->flow_lock);
3453 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
3458 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
3459 "height or framerate from intersected caps");
3464 GST_DEBUG_OBJECT (xvimagesink,
3465 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
3470 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
3471 ("Error calculating the output display ratio of the video."));
3476 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
3477 ("Error calculating the output display ratio of the video."));
3482 static GstStateChangeReturn
3483 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
3485 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
3486 GstXvImageSink *xvimagesink;
3487 GstXContext *xcontext = NULL;
3488 #ifdef GST_EXT_XV_ENHANCEMENT
3489 Atom atom_preemption = None;
3490 #endif /* GST_EXT_XV_ENHANCEMENT */
3492 xvimagesink = GST_XVIMAGESINK (element);
3494 switch (transition) {
3495 case GST_STATE_CHANGE_NULL_TO_READY:
3496 /* Initializing the XContext */
3497 if (xvimagesink->xcontext == NULL) {
3498 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3499 if (xcontext == NULL)
3500 return GST_STATE_CHANGE_FAILURE;
3501 GST_OBJECT_LOCK (xvimagesink);
3503 xvimagesink->xcontext = xcontext;
3504 GST_OBJECT_UNLOCK (xvimagesink);
3507 /* update object's par with calculated one if not set yet */
3508 if (!xvimagesink->par) {
3509 xvimagesink->par = g_new0 (GValue, 1);
3510 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
3511 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
3513 /* call XSynchronize with the current value of synchronous */
3514 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3515 xvimagesink->synchronous ? "TRUE" : "FALSE");
3516 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3517 gst_xvimagesink_update_colorbalance (xvimagesink);
3518 gst_xvimagesink_manage_event_thread (xvimagesink);
3520 case GST_STATE_CHANGE_READY_TO_PAUSED:
3521 g_mutex_lock (xvimagesink->pool_lock);
3522 xvimagesink->pool_invalid = FALSE;
3523 g_mutex_unlock (xvimagesink->pool_lock);
3525 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3526 #ifdef GST_EXT_XV_ENHANCEMENT
3527 g_mutex_lock (xvimagesink->x_lock);
3528 atom_preemption = XInternAtom( xvimagesink->xcontext->disp,
3529 "_USER_WM_PORT_ATTRIBUTE_PREEMPTION", False );
3530 if (atom_preemption != None) {
3531 if (XvSetPortAttribute(xvimagesink->xcontext->disp,
3532 xvimagesink->xcontext->xv_port_id,
3533 atom_preemption, 1 ) != Success) {
3534 GST_ERROR_OBJECT(xvimagesink, "%d: XvSetPortAttribute: preemption failed.\n", atom_preemption);
3536 XSync (xvimagesink->xcontext->disp, FALSE);
3538 g_mutex_unlock (xvimagesink->x_lock);
3539 #endif /* GST_EXT_XV_ENHANCEMENT */
3541 case GST_STATE_CHANGE_PAUSED_TO_READY:
3542 g_mutex_lock (xvimagesink->pool_lock);
3543 xvimagesink->pool_invalid = TRUE;
3544 g_mutex_unlock (xvimagesink->pool_lock);
3550 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
3552 switch (transition) {
3553 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3554 #ifdef GST_EXT_XV_ENHANCEMENT
3555 xvimagesink->rotate_changed = TRUE;
3556 g_mutex_lock (xvimagesink->x_lock);
3557 atom_preemption = XInternAtom( xvimagesink->xcontext->disp,
3558 "_USER_WM_PORT_ATTRIBUTE_PREEMPTION", False );
3559 if (atom_preemption != None) {
3560 if (XvSetPortAttribute(xvimagesink->xcontext->disp,
3561 xvimagesink->xcontext->xv_port_id,
3562 atom_preemption, 0 ) != Success) {
3563 GST_ERROR_OBJECT(xvimagesink, "%d: XvSetPortAttribute: preemption failed.\n", atom_preemption);
3565 XSync (xvimagesink->xcontext->disp, FALSE);
3567 g_mutex_unlock (xvimagesink->x_lock);
3568 #endif /* GST_EXT_XV_ENHANCEMENT */
3570 case GST_STATE_CHANGE_PAUSED_TO_READY:
3571 xvimagesink->fps_n = 0;
3572 xvimagesink->fps_d = 1;
3573 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
3574 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
3575 #ifdef GST_EXT_XV_ENHANCEMENT
3577 drm_fini(xvimagesink);
3578 #endif /* GST_EXT_XV_ENHANCEMENT */
3580 case GST_STATE_CHANGE_READY_TO_NULL:
3581 gst_xvimagesink_reset (xvimagesink);
3591 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
3592 GstClockTime * start, GstClockTime * end)
3594 GstXvImageSink *xvimagesink;
3596 xvimagesink = GST_XVIMAGESINK (bsink);
3598 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
3599 *start = GST_BUFFER_TIMESTAMP (buf);
3600 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
3601 *end = *start + GST_BUFFER_DURATION (buf);
3603 if (xvimagesink->fps_n > 0) {
3605 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
3606 xvimagesink->fps_n);
3612 static GstFlowReturn
3613 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
3615 GstXvImageSink *xvimagesink;
3617 #ifdef GST_EXT_XV_ENHANCEMENT
3618 XV_PUTIMAGE_DATA_PTR img_data = NULL;
3619 SCMN_IMGB *scmn_imgb = NULL;
3621 gboolean ret = FALSE;
3622 #endif /* GST_EXT_XV_ENHANCEMENT */
3624 xvimagesink = GST_XVIMAGESINK (vsink);
3626 #ifdef GST_EXT_XV_ENHANCEMENT
3627 if (xvimagesink->stop_video) {
3628 GST_INFO( "Stop video is TRUE. so skip show frame..." );
3631 #endif /* GST_EXT_XV_ENHANCEMENT */
3633 /* If this buffer has been allocated using our buffer management we simply
3634 put the ximage which is in the PRIVATE pointer */
3635 if (GST_IS_XVIMAGE_BUFFER (buf)) {
3636 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
3637 #ifdef GST_EXT_XV_ENHANCEMENT
3638 xvimagesink->xid_updated = FALSE;
3639 #endif /* GST_EXT_XV_ENHANCEMENT */
3640 if (!gst_xvimagesink_xvimage_put (xvimagesink,
3641 GST_XVIMAGE_BUFFER_CAST (buf)))
3644 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
3645 "slow copy into bufferpool buffer %p", buf);
3646 /* Else we have to copy the data into our private image, */
3647 /* if we have one... */
3648 #ifdef GST_EXT_XV_ENHANCEMENT
3649 g_mutex_lock (xvimagesink->flow_lock);
3650 #endif /* GST_EXT_XV_ENHANCEMENT */
3651 if (!xvimagesink->xvimage) {
3652 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
3654 #ifdef GST_EXT_XV_ENHANCEMENT
3655 format = gst_xvimagesink_get_format_from_caps(xvimagesink, GST_BUFFER_CAPS(buf));
3657 case GST_MAKE_FOURCC('S', 'T', '1', '2'):
3658 case GST_MAKE_FOURCC('S', 'N', '1', '2'):
3659 case GST_MAKE_FOURCC('S', 'N', '2', '1'):
3660 case GST_MAKE_FOURCC('S', '4', '2', '0'):
3661 case GST_MAKE_FOURCC('S', 'U', 'Y', '2'):
3662 case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
3663 case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
3664 scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
3665 if(scmn_imgb == NULL) {
3666 GST_DEBUG_OBJECT( xvimagesink, "scmn_imgb is NULL. Skip xvimage put..." );
3667 g_mutex_unlock (xvimagesink->flow_lock);
3671 /* skip buffer if aligned size is smaller than size of caps */
3672 if (scmn_imgb->s[0] < xvimagesink->video_width ||
3673 scmn_imgb->e[0] < xvimagesink->video_height) {
3674 GST_WARNING_OBJECT(xvimagesink, "invalid size[caps:%dx%d,aligned:%dx%d]. Skip this buffer...",
3675 xvimagesink->video_width, xvimagesink->video_height,
3676 scmn_imgb->s[0], scmn_imgb->e[0]);
3677 g_mutex_unlock (xvimagesink->flow_lock);
3681 xvimagesink->aligned_width = scmn_imgb->s[0];
3682 xvimagesink->aligned_height = scmn_imgb->e[0];
3683 GST_INFO_OBJECT(xvimagesink, "Use aligned width,height[%dx%d]",
3684 xvimagesink->aligned_width, xvimagesink->aligned_height);
3687 GST_INFO_OBJECT(xvimagesink, "Use original width,height of caps");
3690 #endif /* GST_EXT_XV_ENHANCEMENT */
3692 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
3693 GST_BUFFER_CAPS (buf));
3695 if (!xvimagesink->xvimage)
3696 /* The create method should have posted an informative error */
3699 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
3700 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
3701 ("Failed to create output image buffer of %dx%d pixels",
3702 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
3703 ("XServer allocated buffer size did not match input buffer"));
3705 gst_xvimage_buffer_destroy (xvimagesink->xvimage);
3706 xvimagesink->xvimage = NULL;
3711 #ifdef GST_EXT_XV_ENHANCEMENT
3712 switch (xvimagesink->xvimage->im_format) {
3713 /* Cases for specified formats of Samsung extension */
3714 case GST_MAKE_FOURCC('S', 'T', '1', '2'):
3715 case GST_MAKE_FOURCC('S', 'N', '1', '2'):
3716 case GST_MAKE_FOURCC('S', 'N', '2', '1'):
3717 case GST_MAKE_FOURCC('S', '4', '2', '0'):
3718 case GST_MAKE_FOURCC('S', 'U', 'Y', '2'):
3719 case GST_MAKE_FOURCC('S', 'U', 'Y', 'V'):
3720 case GST_MAKE_FOURCC('S', 'Y', 'V', 'Y'):
3721 case GST_MAKE_FOURCC('I', 'T', 'L', 'V'):
3723 GST_LOG("Samsung EXT format - fourcc:%c%c%c%c, display mode:%d, Rotate angle:%d",
3724 xvimagesink->xvimage->im_format, xvimagesink->xvimage->im_format>>8,
3725 xvimagesink->xvimage->im_format>>16, xvimagesink->xvimage->im_format>>24,
3726 xvimagesink->display_mode, xvimagesink->rotate_angle);
3728 if (xvimagesink->xvimage->xvimage->data) {
3729 img_data = (XV_PUTIMAGE_DATA_PTR) xvimagesink->xvimage->xvimage->data;
3730 memset(img_data, 0x0, sizeof(XV_PUTIMAGE_DATA));
3731 XV_PUTIMAGE_INIT_DATA(img_data);
3733 scmn_imgb = (SCMN_IMGB *)GST_BUFFER_MALLOCDATA(buf);
3734 if (scmn_imgb == NULL) {
3735 GST_DEBUG_OBJECT( xvimagesink, "scmn_imgb is NULL. Skip xvimage put..." );
3736 g_mutex_unlock (xvimagesink->flow_lock);
3740 if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_PADDR) {
3741 img_data->YBuf = (unsigned int)scmn_imgb->p[0];
3742 img_data->CbBuf = (unsigned int)scmn_imgb->p[1];
3743 img_data->CrBuf = (unsigned int)scmn_imgb->p[2];
3744 img_data->BufType = XV_BUF_TYPE_LEGACY;
3745 } else if (scmn_imgb->buf_share_method == BUF_SHARE_METHOD_FD) {
3746 /* open drm to use gem */
3747 if (xvimagesink->drm_fd < 0) {
3748 drm_init(xvimagesink);
3751 /* convert dma-buf fd into drm gem name */
3752 img_data->YBuf = drm_convert_dmabuf_gemname(xvimagesink, (int)scmn_imgb->dmabuf_fd[0], &xvimagesink->gem_handle[0]);
3753 img_data->CbBuf = drm_convert_dmabuf_gemname(xvimagesink, (int)scmn_imgb->dmabuf_fd[1], &xvimagesink->gem_handle[1]);
3754 img_data->CrBuf = drm_convert_dmabuf_gemname(xvimagesink, (int)scmn_imgb->dmabuf_fd[2], &xvimagesink->gem_handle[2]);
3755 img_data->BufType = XV_BUF_TYPE_DMABUF;
3757 GST_WARNING("unknown buf_share_method type [%d]. skip xvimage put...",
3758 scmn_imgb->buf_share_method);
3759 g_mutex_unlock (xvimagesink->flow_lock);
3763 GST_LOG_OBJECT(xvimagesink, "YBuf[%d], CbBuf[%d], CrBuf[%d]",
3764 img_data->YBuf, img_data->CbBuf, img_data->CrBuf );
3766 GST_WARNING_OBJECT( xvimagesink, "xvimage->data is NULL. skip xvimage put..." );
3767 g_mutex_unlock (xvimagesink->flow_lock);
3774 GST_DEBUG("Normal format activated. fourcc = %d", xvimagesink->xvimage->im_format);
3775 memcpy (xvimagesink->xvimage->xvimage->data,
3776 GST_BUFFER_DATA (buf),
3777 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
3782 g_mutex_unlock (xvimagesink->flow_lock);
3783 ret = gst_xvimagesink_xvimage_put(xvimagesink, xvimagesink->xvimage);
3785 /* close gem handle */
3786 if (xvimagesink->drm_fd >= 0 ) {
3787 drm_close_gem(xvimagesink, &xvimagesink->gem_handle[0]);
3788 drm_close_gem(xvimagesink, &xvimagesink->gem_handle[1]);
3789 drm_close_gem(xvimagesink, &xvimagesink->gem_handle[2]);
3795 #else /* GST_EXT_XV_ENHANCEMENT */
3796 memcpy (xvimagesink->xvimage->xvimage->data,
3797 GST_BUFFER_DATA (buf),
3798 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
3800 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
3802 #endif /* GST_EXT_XV_ENHANCEMENT */
3810 /* No image available. That's very bad ! */
3811 GST_WARNING_OBJECT (xvimagesink, "could not create image");
3812 #ifdef GST_EXT_XV_ENHANCEMENT
3813 g_mutex_unlock (xvimagesink->flow_lock);
3815 return GST_FLOW_ERROR;
3819 /* No Window available to put our image into */
3820 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
3821 return GST_FLOW_ERROR;
3826 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
3828 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
3830 switch (GST_EVENT_TYPE (event)) {
3831 case GST_EVENT_TAG:{
3833 gchar *title = NULL;
3835 gst_event_parse_tag (event, &l);
3836 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
3839 #ifdef GST_EXT_XV_ENHANCEMENT
3840 if (!xvimagesink->get_pixmap_cb) {
3842 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
3843 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
3847 #ifdef GST_EXT_XV_ENHANCEMENT
3856 if (GST_BASE_SINK_CLASS (parent_class)->event)
3857 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
3862 /* Buffer management */
3865 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
3868 GstCaps *intersection;
3872 gint par_n = 1, par_d = 1;
3876 new_caps = gst_caps_copy (caps);
3878 s = gst_caps_get_structure (new_caps, 0);
3880 gst_structure_get_int (s, "width", &width);
3881 gst_structure_get_int (s, "height", &height);
3882 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
3884 gst_structure_remove_field (s, "width");
3885 gst_structure_remove_field (s, "height");
3886 gst_structure_remove_field (s, "pixel-aspect-ratio");
3888 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
3889 gst_caps_unref (new_caps);
3891 if (gst_caps_is_empty (intersection))
3892 return intersection;
3894 s = gst_caps_get_structure (intersection, 0);
3896 gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
3898 /* xvimagesink supports all PARs */
3900 gst_structure_fixate_field_nearest_int (s, "width", width);
3901 gst_structure_fixate_field_nearest_int (s, "height", height);
3902 gst_structure_get_int (s, "width", &w);
3903 gst_structure_get_int (s, "height", &h);
3905 gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
3906 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
3909 return intersection;
3912 static GstFlowReturn
3913 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
3914 GstCaps * caps, GstBuffer ** buf)
3916 GstFlowReturn ret = GST_FLOW_OK;
3917 GstXvImageSink *xvimagesink;
3918 GstXvImageBuffer *xvimage = NULL;
3919 GstCaps *intersection = NULL;
3920 GstStructure *structure = NULL;
3921 gint width, height, image_format;
3923 xvimagesink = GST_XVIMAGESINK (bsink);
3925 if (G_UNLIKELY (!caps))
3928 g_mutex_lock (xvimagesink->pool_lock);
3929 if (G_UNLIKELY (xvimagesink->pool_invalid))
3932 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
3933 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
3934 GST_LOG_OBJECT (xvimagesink,
3935 "buffer alloc for same last_caps, reusing caps");
3936 intersection = gst_caps_ref (caps);
3937 image_format = xvimagesink->xcontext->last_format;
3938 width = xvimagesink->xcontext->last_width;
3939 height = xvimagesink->xcontext->last_height;
3941 goto reuse_last_caps;
3944 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
3945 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
3946 caps, xvimagesink->xcontext->caps);
3948 /* Check the caps against our xcontext */
3949 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
3951 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
3952 GST_PTR_FORMAT, intersection);
3954 if (gst_caps_is_empty (intersection)) {
3957 gst_caps_unref (intersection);
3959 /* So we don't support this kind of buffer, let's define one we'd like */
3960 new_caps = gst_caps_copy (caps);
3962 structure = gst_caps_get_structure (new_caps, 0);
3963 if (!gst_structure_has_field (structure, "width") ||
3964 !gst_structure_has_field (structure, "height")) {
3965 gst_caps_unref (new_caps);
3969 /* Try different dimensions */
3971 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
3973 if (gst_caps_is_empty (intersection)) {
3974 /* Try with different YUV formats first */
3975 gst_structure_set_name (structure, "video/x-raw-yuv");
3977 /* Remove format specific fields */
3978 gst_structure_remove_field (structure, "format");
3979 gst_structure_remove_field (structure, "endianness");
3980 gst_structure_remove_field (structure, "depth");
3981 gst_structure_remove_field (structure, "bpp");
3982 gst_structure_remove_field (structure, "red_mask");
3983 gst_structure_remove_field (structure, "green_mask");
3984 gst_structure_remove_field (structure, "blue_mask");
3985 gst_structure_remove_field (structure, "alpha_mask");
3987 /* Reuse intersection with Xcontext */
3988 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
3991 if (gst_caps_is_empty (intersection)) {
3992 /* Try with different dimensions and YUV formats */
3994 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
3997 if (gst_caps_is_empty (intersection)) {
3998 /* Now try with RGB */
3999 gst_structure_set_name (structure, "video/x-raw-rgb");
4000 /* And interset again */
4001 gst_caps_unref (intersection);
4002 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
4005 if (gst_caps_is_empty (intersection)) {
4006 /* Try with different dimensions and RGB formats */
4008 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
4011 /* Clean this copy */
4012 gst_caps_unref (new_caps);
4014 if (gst_caps_is_empty (intersection))
4018 /* Ensure the returned caps are fixed */
4019 gst_caps_truncate (intersection);
4021 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
4022 GST_PTR_FORMAT, intersection);
4023 if (gst_caps_is_equal (intersection, caps)) {
4024 /* Things work better if we return a buffer with the same caps ptr
4025 * as was asked for when we can */
4026 gst_caps_replace (&intersection, caps);
4029 /* Get image format from caps */
4030 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
4033 /* Get geometry from caps */
4034 structure = gst_caps_get_structure (intersection, 0);
4035 if (!gst_structure_get_int (structure, "width", &width) ||
4036 !gst_structure_get_int (structure, "height", &height) ||
4040 /* Store our caps and format as the last_caps to avoid expensive
4041 * caps intersection next time */
4042 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
4043 xvimagesink->xcontext->last_format = image_format;
4044 xvimagesink->xcontext->last_width = width;
4045 xvimagesink->xcontext->last_height = height;
4049 /* Walking through the pool cleaning unusable images and searching for a
4051 while (xvimagesink->image_pool) {
4052 xvimage = xvimagesink->image_pool->data;
4055 /* Removing from the pool */
4056 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
4057 xvimagesink->image_pool);
4059 /* We check for geometry or image format changes */
4060 if ((xvimage->width != width) ||
4061 (xvimage->height != height) || (xvimage->im_format != image_format)) {
4062 /* This image is unusable. Destroying... */
4063 gst_xvimage_buffer_free (xvimage);
4066 /* We found a suitable image */
4067 GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
4074 #ifdef GST_EXT_XV_ENHANCEMENT
4075 /* init aligned size */
4076 xvimagesink->aligned_width = 0;
4077 xvimagesink->aligned_height = 0;
4078 #endif /* GST_EXT_XV_ENHANCEMENT */
4080 /* We found no suitable image in the pool. Creating... */
4081 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
4082 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
4084 g_mutex_unlock (xvimagesink->pool_lock);
4087 /* Make sure the buffer is cleared of any previously used flags */
4088 GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
4089 gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
4092 *buf = GST_BUFFER_CAST (xvimage);
4096 gst_caps_unref (intersection);
4104 GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
4105 ret = GST_FLOW_WRONG_STATE;
4106 g_mutex_unlock (xvimagesink->pool_lock);
4111 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
4112 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
4113 " are completely incompatible with those caps", caps,
4114 xvimagesink->xcontext->caps);
4115 ret = GST_FLOW_NOT_NEGOTIATED;
4116 g_mutex_unlock (xvimagesink->pool_lock);
4121 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
4122 GST_PTR_FORMAT, intersection);
4123 ret = GST_FLOW_NOT_NEGOTIATED;
4124 g_mutex_unlock (xvimagesink->pool_lock);
4129 GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
4136 /* Interfaces stuff */
4139 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
4141 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
4142 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
4149 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
4151 klass->supported = gst_xvimagesink_interface_supported;
4155 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
4156 GstStructure * structure)
4158 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
4161 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
4163 GstVideoRectangle src, dst, result;
4164 gdouble x, y, xscale = 1.0, yscale = 1.0;
4166 event = gst_event_new_navigation (structure);
4168 /* We take the flow_lock while we look at the window */
4169 g_mutex_lock (xvimagesink->flow_lock);
4171 if (!xvimagesink->xwindow) {
4172 g_mutex_unlock (xvimagesink->flow_lock);
4176 if (xvimagesink->keep_aspect) {
4177 /* We get the frame position using the calculated geometry from _setcaps
4178 that respect pixel aspect ratios */
4179 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
4180 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
4181 dst.w = xvimagesink->render_rect.w;
4182 dst.h = xvimagesink->render_rect.h;
4184 gst_video_sink_center_rect (src, dst, &result, TRUE);
4185 result.x += xvimagesink->render_rect.x;
4186 result.y += xvimagesink->render_rect.y;
4188 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
4191 g_mutex_unlock (xvimagesink->flow_lock);
4193 /* We calculate scaling using the original video frames geometry to include
4194 pixel aspect ratio scaling. */
4195 xscale = (gdouble) xvimagesink->video_width / result.w;
4196 yscale = (gdouble) xvimagesink->video_height / result.h;
4198 /* Converting pointer coordinates to the non scaled geometry */
4199 if (gst_structure_get_double (structure, "pointer_x", &x)) {
4200 x = MIN (x, result.x + result.w);
4201 x = MAX (x - result.x, 0);
4202 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
4203 (gdouble) x * xscale, NULL);
4205 if (gst_structure_get_double (structure, "pointer_y", &y)) {
4206 y = MIN (y, result.y + result.h);
4207 y = MAX (y - result.y, 0);
4208 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
4209 (gdouble) y * yscale, NULL);
4212 gst_pad_send_event (peer, event);
4213 gst_object_unref (peer);
4218 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
4220 iface->send_event = gst_xvimagesink_navigation_send_event;
4223 #ifdef GST_EXT_XV_ENHANCEMENT
4225 gst_xvimagesink_set_pixmap_handle (GstXOverlay * overlay, guintptr id)
4229 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
4230 GstXPixmap *xpixmap = NULL;
4232 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
4234 /* If the element has not initialized the X11 context try to do so */
4235 if (!xvimagesink->xcontext && !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
4236 /* we have thrown a GST_ELEMENT_ERROR now */
4240 gst_xvimagesink_update_colorbalance (xvimagesink);
4242 GST_DEBUG_OBJECT( xvimagesink, "pixmap id : %d", pixmap_id );
4244 /* if the returned pixmap_id is 0, set current pixmap index to -2 to skip putImage() */
4245 if (pixmap_id == 0) {
4246 xvimagesink->current_pixmap_idx = -2;
4250 g_mutex_lock (xvimagesink->x_lock);
4252 for (i = 0; i < MAX_PIXMAP_NUM; i++) {
4253 if (!xvimagesink->xpixmap[i]) {
4257 unsigned int cur_win_width = 0;
4258 unsigned int cur_win_height = 0;
4259 unsigned int cur_win_border_width = 0;
4260 unsigned int cur_win_depth = 0;
4262 GST_INFO_OBJECT( xvimagesink, "xpixmap[%d] is empty, create it with pixmap_id(%d)", i, pixmap_id );
4264 xpixmap = g_new0 (GstXPixmap, 1);
4266 xpixmap->pixmap = pixmap_id;
4268 /* Get root window and size of current window */
4269 XGetGeometry(xvimagesink->xcontext->disp, xpixmap->pixmap, &root_window,
4270 &cur_win_x, &cur_win_y, /* relative x, y */
4271 &cur_win_width, &cur_win_height,
4272 &cur_win_border_width, &cur_win_depth);
4273 if (!cur_win_width || !cur_win_height) {
4274 GST_INFO_OBJECT( xvimagesink, "cur_win_width(%d) or cur_win_height(%d) is null..", cur_win_width, cur_win_height );
4275 g_mutex_unlock (xvimagesink->x_lock);
4278 xpixmap->width = cur_win_width;
4279 xpixmap->height = cur_win_height;
4281 if (!xvimagesink->render_rect.w)
4282 xvimagesink->render_rect.w = cur_win_width;
4283 if (!xvimagesink->render_rect.h)
4284 xvimagesink->render_rect.h = cur_win_height;
4287 xpixmap->gc = XCreateGC (xvimagesink->xcontext->disp, xpixmap->pixmap, 0, NULL);
4289 xvimagesink->xpixmap[i] = xpixmap;
4290 xvimagesink->current_pixmap_idx = i;
4292 GST_ERROR("failed to create xpixmap errno: %d", errno);
4295 g_mutex_unlock (xvimagesink->x_lock);
4298 } else if (xvimagesink->xpixmap[i]->pixmap == pixmap_id) {
4299 GST_DEBUG_OBJECT( xvimagesink, "found xpixmap[%d]->pixmap : %d", i, pixmap_id );
4300 xvimagesink->current_pixmap_idx = i;
4302 g_mutex_unlock (xvimagesink->x_lock);
4310 GST_ERROR_OBJECT( xvimagesink, "could not find the pixmap id(%d) in xpixmap array", pixmap_id );
4311 xvimagesink->current_pixmap_idx = -1;
4313 g_mutex_unlock (xvimagesink->x_lock);
4316 #endif /* GST_EXT_XV_ENHANCEMENT */
4319 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
4321 XID xwindow_id = id;
4322 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
4323 GstXWindow *xwindow = NULL;
4325 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
4327 g_mutex_lock (xvimagesink->flow_lock);
4329 #ifdef GST_EXT_XV_ENHANCEMENT
4330 GST_INFO_OBJECT( xvimagesink, "ENTER, id : %d", xwindow_id );
4331 #endif /* GST_EXT_XV_ENHANCEMENT */
4333 /* If we already use that window return */
4334 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
4335 g_mutex_unlock (xvimagesink->flow_lock);
4339 /* If the element has not initialized the X11 context try to do so */
4340 if (!xvimagesink->xcontext &&
4341 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
4342 g_mutex_unlock (xvimagesink->flow_lock);
4343 /* we have thrown a GST_ELEMENT_ERROR now */
4347 gst_xvimagesink_update_colorbalance (xvimagesink);
4349 /* Clear image pool as the images are unusable anyway */
4350 gst_xvimagesink_imagepool_clear (xvimagesink);
4352 /* Clear the xvimage */
4353 if (xvimagesink->xvimage) {
4354 gst_xvimage_buffer_free (xvimagesink->xvimage);
4355 xvimagesink->xvimage = NULL;
4358 /* If a window is there already we destroy it */
4359 if (xvimagesink->xwindow) {
4360 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
4361 xvimagesink->xwindow = NULL;
4364 /* If the xid is 0 we go back to an internal window */
4365 if (xwindow_id == 0) {
4366 /* If no width/height caps nego did not happen window will be created
4367 during caps nego then */
4368 #ifdef GST_EXT_XV_ENHANCEMENT
4369 GST_INFO_OBJECT( xvimagesink, "xid is 0. create window[%dx%d]",
4370 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink) );
4371 #endif /* GST_EXT_XV_ENHANCEMENT */
4372 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
4373 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
4375 gst_xvimagesink_xwindow_new (xvimagesink,
4376 GST_VIDEO_SINK_WIDTH (xvimagesink),
4377 GST_VIDEO_SINK_HEIGHT (xvimagesink));
4380 XWindowAttributes attr;
4382 xwindow = g_new0 (GstXWindow, 1);
4383 xwindow->win = xwindow_id;
4385 /* Set the event we want to receive and create a GC */
4386 g_mutex_lock (xvimagesink->x_lock);
4388 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
4390 xwindow->width = attr.width;
4391 xwindow->height = attr.height;
4392 xwindow->internal = FALSE;
4393 if (!xvimagesink->have_render_rect) {
4394 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
4395 xvimagesink->render_rect.w = attr.width;
4396 xvimagesink->render_rect.h = attr.height;
4398 if (xvimagesink->handle_events) {
4399 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
4400 StructureNotifyMask | PointerMotionMask | KeyPressMask |
4404 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
4405 xwindow->win, 0, NULL);
4406 g_mutex_unlock (xvimagesink->x_lock);
4410 xvimagesink->xwindow = xwindow;
4412 #ifdef GST_EXT_XV_ENHANCEMENT
4413 xvimagesink->xid_updated = TRUE;
4414 #endif /* GST_EXT_XV_ENHANCEMENT */
4416 g_mutex_unlock (xvimagesink->flow_lock);
4420 gst_xvimagesink_expose (GstXOverlay * overlay)
4422 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
4424 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
4425 #ifdef GST_EXT_XV_ENHANCEMENT
4426 GST_INFO_OBJECT( xvimagesink, "Overlay window exposed. update it");
4427 gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
4428 #else /* GST_EXT_XV_ENHANCEMENT */
4429 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
4430 #endif /* GST_EXT_XV_ENHANCEMENT */
4434 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
4435 gboolean handle_events)
4437 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
4439 xvimagesink->handle_events = handle_events;
4441 g_mutex_lock (xvimagesink->flow_lock);
4443 if (G_UNLIKELY (!xvimagesink->xwindow)) {
4444 g_mutex_unlock (xvimagesink->flow_lock);
4448 g_mutex_lock (xvimagesink->x_lock);
4450 if (handle_events) {
4451 if (xvimagesink->xwindow->internal) {
4452 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
4453 #ifdef GST_EXT_XV_ENHANCEMENT
4454 ExposureMask | StructureNotifyMask | PointerMotionMask | VisibilityChangeMask |
4455 #else /* GST_EXT_XV_ENHANCEMENT */
4456 ExposureMask | StructureNotifyMask | PointerMotionMask |
4457 #endif /* GST_EXT_XV_ENHANCEMENT */
4458 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
4460 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
4461 #ifdef GST_EXT_XV_ENHANCEMENT
4462 ExposureMask | StructureNotifyMask | PointerMotionMask | VisibilityChangeMask |
4463 #else /* GST_EXT_XV_ENHANCEMENT */
4464 ExposureMask | StructureNotifyMask | PointerMotionMask |
4465 #endif /* GST_EXT_XV_ENHANCEMENT */
4466 KeyPressMask | KeyReleaseMask);
4469 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
4472 g_mutex_unlock (xvimagesink->x_lock);
4474 g_mutex_unlock (xvimagesink->flow_lock);
4478 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
4479 gint width, gint height)
4481 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
4483 /* FIXME: how about some locking? */
4484 if (width >= 0 && height >= 0) {
4485 xvimagesink->render_rect.x = x;
4486 xvimagesink->render_rect.y = y;
4487 xvimagesink->render_rect.w = width;
4488 xvimagesink->render_rect.h = height;
4489 xvimagesink->have_render_rect = TRUE;
4491 xvimagesink->render_rect.x = 0;
4492 xvimagesink->render_rect.y = 0;
4493 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
4494 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
4495 xvimagesink->have_render_rect = FALSE;
4500 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
4502 iface->set_window_handle = gst_xvimagesink_set_window_handle;
4503 iface->expose = gst_xvimagesink_expose;
4504 iface->handle_events = gst_xvimagesink_set_event_handling;
4505 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
4508 static const GList *
4509 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
4511 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
4513 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
4515 if (xvimagesink->xcontext)
4516 return xvimagesink->xcontext->channels_list;
4522 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
4523 GstColorBalanceChannel * channel, gint value)
4525 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
4527 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
4528 g_return_if_fail (channel->label != NULL);
4530 xvimagesink->cb_changed = TRUE;
4532 /* Normalize val to [-1000, 1000] */
4533 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
4534 (double) (channel->max_value - channel->min_value));
4536 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
4537 xvimagesink->hue = value;
4538 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
4539 xvimagesink->saturation = value;
4540 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
4541 xvimagesink->contrast = value;
4542 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
4543 xvimagesink->brightness = value;
4545 g_warning ("got an unknown channel %s", channel->label);
4549 gst_xvimagesink_update_colorbalance (xvimagesink);
4553 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
4554 GstColorBalanceChannel * channel)
4556 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
4559 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
4560 g_return_val_if_fail (channel->label != NULL, 0);
4562 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
4563 value = xvimagesink->hue;
4564 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
4565 value = xvimagesink->saturation;
4566 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
4567 value = xvimagesink->contrast;
4568 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
4569 value = xvimagesink->brightness;
4571 g_warning ("got an unknown channel %s", channel->label);
4574 /* Normalize val to [channel->min_value, channel->max_value] */
4575 value = channel->min_value + (channel->max_value - channel->min_value) *
4576 (value + 1000) / 2000;
4582 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
4584 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
4585 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
4586 iface->set_value = gst_xvimagesink_colorbalance_set_value;
4587 iface->get_value = gst_xvimagesink_colorbalance_get_value;
4590 static const GList *
4591 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
4593 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
4594 static GList *list = NULL;
4597 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
4599 g_list_append (list, g_object_class_find_property (klass,
4600 "autopaint-colorkey"));
4602 g_list_append (list, g_object_class_find_property (klass,
4605 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
4612 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
4613 guint prop_id, const GParamSpec * pspec)
4615 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
4619 case PROP_AUTOPAINT_COLORKEY:
4620 case PROP_DOUBLE_BUFFER:
4622 GST_DEBUG_OBJECT (xvimagesink,
4623 "probing device list and get capabilities");
4624 if (!xvimagesink->xcontext) {
4625 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
4626 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
4630 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
4636 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
4637 guint prop_id, const GParamSpec * pspec)
4639 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
4640 gboolean ret = FALSE;
4644 case PROP_AUTOPAINT_COLORKEY:
4645 case PROP_DOUBLE_BUFFER:
4647 if (xvimagesink->xcontext != NULL) {
4654 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
4661 static GValueArray *
4662 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
4663 guint prop_id, const GParamSpec * pspec)
4665 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
4666 GValueArray *array = NULL;
4668 if (G_UNLIKELY (!xvimagesink->xcontext)) {
4669 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
4678 GValue value = { 0 };
4680 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
4681 g_value_init (&value, G_TYPE_STRING);
4683 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
4684 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
4686 g_value_set_string (&value, adaptor_id_s);
4687 g_value_array_append (array, &value);
4688 g_free (adaptor_id_s);
4690 g_value_unset (&value);
4693 case PROP_AUTOPAINT_COLORKEY:
4694 if (xvimagesink->have_autopaint_colorkey) {
4695 GValue value = { 0 };
4697 array = g_value_array_new (2);
4698 g_value_init (&value, G_TYPE_BOOLEAN);
4699 g_value_set_boolean (&value, FALSE);
4700 g_value_array_append (array, &value);
4701 g_value_set_boolean (&value, TRUE);
4702 g_value_array_append (array, &value);
4703 g_value_unset (&value);
4706 case PROP_DOUBLE_BUFFER:
4707 if (xvimagesink->have_double_buffer) {
4708 GValue value = { 0 };
4710 array = g_value_array_new (2);
4711 g_value_init (&value, G_TYPE_BOOLEAN);
4712 g_value_set_boolean (&value, FALSE);
4713 g_value_array_append (array, &value);
4714 g_value_set_boolean (&value, TRUE);
4715 g_value_array_append (array, &value);
4716 g_value_unset (&value);
4720 if (xvimagesink->have_colorkey) {
4721 GValue value = { 0 };
4723 array = g_value_array_new (1);
4724 g_value_init (&value, GST_TYPE_INT_RANGE);
4725 gst_value_set_int_range (&value, 0, 0xffffff);
4726 g_value_array_append (array, &value);
4727 g_value_unset (&value);
4731 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
4740 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
4743 iface->get_properties = gst_xvimagesink_probe_get_properties;
4744 iface->probe_property = gst_xvimagesink_probe_probe_property;
4745 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
4746 iface->get_values = gst_xvimagesink_probe_get_values;
4749 /* =========================================== */
4751 /* Init & Class init */
4753 /* =========================================== */
4756 gst_xvimagesink_set_property (GObject * object, guint prop_id,
4757 const GValue * value, GParamSpec * pspec)
4759 GstXvImageSink *xvimagesink;
4761 g_return_if_fail (GST_IS_XVIMAGESINK (object));
4763 xvimagesink = GST_XVIMAGESINK (object);
4767 xvimagesink->hue = g_value_get_int (value);
4768 xvimagesink->cb_changed = TRUE;
4769 gst_xvimagesink_update_colorbalance (xvimagesink);
4772 xvimagesink->contrast = g_value_get_int (value);
4773 xvimagesink->cb_changed = TRUE;
4774 gst_xvimagesink_update_colorbalance (xvimagesink);
4776 case PROP_BRIGHTNESS:
4777 xvimagesink->brightness = g_value_get_int (value);
4778 xvimagesink->cb_changed = TRUE;
4779 gst_xvimagesink_update_colorbalance (xvimagesink);
4781 case PROP_SATURATION:
4782 xvimagesink->saturation = g_value_get_int (value);
4783 xvimagesink->cb_changed = TRUE;
4784 gst_xvimagesink_update_colorbalance (xvimagesink);
4787 xvimagesink->display_name = g_strdup (g_value_get_string (value));
4789 case PROP_SYNCHRONOUS:
4790 xvimagesink->synchronous = g_value_get_boolean (value);
4791 if (xvimagesink->xcontext) {
4792 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
4793 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
4794 xvimagesink->synchronous ? "TRUE" : "FALSE");
4797 case PROP_PIXEL_ASPECT_RATIO:
4798 g_free (xvimagesink->par);
4799 xvimagesink->par = g_new0 (GValue, 1);
4800 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
4801 if (!g_value_transform (value, xvimagesink->par)) {
4802 g_warning ("Could not transform string to aspect ratio");
4803 gst_value_set_fraction (xvimagesink->par, 1, 1);
4805 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
4806 gst_value_get_fraction_numerator (xvimagesink->par),
4807 gst_value_get_fraction_denominator (xvimagesink->par));
4809 case PROP_FORCE_ASPECT_RATIO:
4810 xvimagesink->keep_aspect = g_value_get_boolean (value);
4812 case PROP_HANDLE_EVENTS:
4813 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
4814 g_value_get_boolean (value));
4815 gst_xvimagesink_manage_event_thread (xvimagesink);
4818 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
4820 case PROP_HANDLE_EXPOSE:
4821 xvimagesink->handle_expose = g_value_get_boolean (value);
4822 gst_xvimagesink_manage_event_thread (xvimagesink);
4824 case PROP_DOUBLE_BUFFER:
4825 xvimagesink->double_buffer = g_value_get_boolean (value);
4827 case PROP_AUTOPAINT_COLORKEY:
4828 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
4831 xvimagesink->colorkey = g_value_get_int (value);
4833 case PROP_DRAW_BORDERS:
4834 xvimagesink->draw_borders = g_value_get_boolean (value);
4836 #ifdef GST_EXT_XV_ENHANCEMENT
4837 case PROP_DISPLAY_MODE:
4839 int set_mode = g_value_get_enum (value);
4841 g_mutex_lock(xvimagesink->flow_lock);
4842 g_mutex_lock(xvimagesink->x_lock);
4844 if (xvimagesink->display_mode != set_mode) {
4845 if (xvimagesink->xcontext) {
4846 /* set display mode */
4847 if (set_display_mode(xvimagesink->xcontext, set_mode)) {
4848 xvimagesink->display_mode = set_mode;
4850 GST_WARNING_OBJECT(xvimagesink, "display mode[%d] set failed.", set_mode);
4853 /* "xcontext" is not created yet. It will be applied when xcontext is created. */
4854 GST_INFO_OBJECT(xvimagesink, "xcontext is NULL. display-mode will be set later.");
4855 xvimagesink->display_mode = set_mode;
4858 GST_INFO_OBJECT(xvimagesink, "skip display mode %d, because current mode is same", set_mode);
4861 g_mutex_unlock(xvimagesink->x_lock);
4862 g_mutex_unlock(xvimagesink->flow_lock);
4865 case PROP_DISPLAY_GEOMETRY_METHOD:
4866 xvimagesink->display_geometry_method = g_value_get_enum (value);
4867 GST_LOG("Overlay geometry changed. update it");
4868 gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
4871 xvimagesink->flip = g_value_get_enum(value);
4873 case PROP_ROTATE_ANGLE:
4874 xvimagesink->rotate_angle = g_value_get_enum (value);
4875 xvimagesink->rotate_changed = TRUE;
4878 g_mutex_lock( xvimagesink->flow_lock );
4879 g_mutex_lock( xvimagesink->x_lock );
4881 if (xvimagesink->visible && (g_value_get_boolean(value) == FALSE)) {
4882 Atom atom_stream = XInternAtom( xvimagesink->xcontext->disp,
4883 "_USER_WM_PORT_ATTRIBUTE_STREAM_OFF", False );
4884 if (atom_stream != None) {
4885 if (XvSetPortAttribute(xvimagesink->xcontext->disp,
4886 xvimagesink->xcontext->xv_port_id,
4887 atom_stream, 0 ) != Success) {
4888 GST_WARNING_OBJECT( xvimagesink, "Set visible FALSE failed" );
4891 XSync( xvimagesink->xcontext->disp, FALSE );
4893 } else if (!xvimagesink->visible && (g_value_get_boolean(value) == TRUE)) {
4894 g_mutex_unlock( xvimagesink->x_lock );
4895 g_mutex_unlock( xvimagesink->flow_lock );
4896 GST_INFO_OBJECT( xvimagesink, "Set visible as TRUE. Update it." );
4897 gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
4898 g_mutex_lock( xvimagesink->flow_lock );
4899 g_mutex_lock( xvimagesink->x_lock );
4902 xvimagesink->visible = g_value_get_boolean (value);
4904 g_mutex_unlock( xvimagesink->x_lock );
4905 g_mutex_unlock( xvimagesink->flow_lock );
4908 xvimagesink->zoom = g_value_get_int (value);
4910 case PROP_DST_ROI_X:
4911 xvimagesink->dst_roi.x = g_value_get_int (value);
4913 case PROP_DST_ROI_Y:
4914 xvimagesink->dst_roi.y = g_value_get_int (value);
4916 case PROP_DST_ROI_W:
4917 xvimagesink->dst_roi.w = g_value_get_int (value);
4919 case PROP_DST_ROI_H:
4920 xvimagesink->dst_roi.h = g_value_get_int (value);
4922 case PROP_STOP_VIDEO:
4923 xvimagesink->stop_video = g_value_get_int (value);
4924 g_mutex_lock( xvimagesink->flow_lock );
4926 if( xvimagesink->stop_video )
4928 if ( xvimagesink->get_pixmap_cb ) {
4929 if (xvimagesink->xpixmap[0] && xvimagesink->xpixmap[0]->pixmap) {
4930 g_mutex_lock (xvimagesink->x_lock);
4931 GST_INFO_OBJECT( xvimagesink, "calling XvStopVideo()" );
4932 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, xvimagesink->xpixmap[0]->pixmap);
4933 g_mutex_unlock (xvimagesink->x_lock);
4936 GST_INFO_OBJECT( xvimagesink, "Xwindow CLEAR when set video-stop property" );
4937 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
4941 g_mutex_unlock( xvimagesink->flow_lock );
4943 case PROP_PIXMAP_CB:
4946 cb_func = g_value_get_pointer(value);
4948 xvimagesink->get_pixmap_cb = cb_func;
4949 GST_INFO_OBJECT (xvimagesink, "Set callback(0x%x) for getting pixmap id", xvimagesink->get_pixmap_cb);
4953 case PROP_PIXMAP_CB_USER_DATA:
4956 user_data = g_value_get_pointer(value);
4958 xvimagesink->get_pixmap_cb_user_data = user_data;
4959 GST_INFO_OBJECT (xvimagesink, "Set user data(0x%x) for getting pixmap id", xvimagesink->get_pixmap_cb_user_data);
4963 #endif /* GST_EXT_XV_ENHANCEMENT */
4965 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4971 gst_xvimagesink_get_property (GObject * object, guint prop_id,
4972 GValue * value, GParamSpec * pspec)
4974 GstXvImageSink *xvimagesink;
4976 g_return_if_fail (GST_IS_XVIMAGESINK (object));
4978 xvimagesink = GST_XVIMAGESINK (object);
4982 g_value_set_int (value, xvimagesink->hue);
4985 g_value_set_int (value, xvimagesink->contrast);
4987 case PROP_BRIGHTNESS:
4988 g_value_set_int (value, xvimagesink->brightness);
4990 case PROP_SATURATION:
4991 g_value_set_int (value, xvimagesink->saturation);
4994 g_value_set_string (value, xvimagesink->display_name);
4996 case PROP_SYNCHRONOUS:
4997 g_value_set_boolean (value, xvimagesink->synchronous);
4999 case PROP_PIXEL_ASPECT_RATIO:
5000 if (xvimagesink->par)
5001 g_value_transform (xvimagesink->par, value);
5003 case PROP_FORCE_ASPECT_RATIO:
5004 g_value_set_boolean (value, xvimagesink->keep_aspect);
5006 case PROP_HANDLE_EVENTS:
5007 g_value_set_boolean (value, xvimagesink->handle_events);
5011 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
5013 g_value_set_string (value, adaptor_no_s);
5014 g_free (adaptor_no_s);
5017 case PROP_DEVICE_NAME:
5018 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
5019 g_value_set_string (value,
5020 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
5022 g_value_set_string (value, NULL);
5025 case PROP_HANDLE_EXPOSE:
5026 g_value_set_boolean (value, xvimagesink->handle_expose);
5028 case PROP_DOUBLE_BUFFER:
5029 g_value_set_boolean (value, xvimagesink->double_buffer);
5031 case PROP_AUTOPAINT_COLORKEY:
5032 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
5035 g_value_set_int (value, xvimagesink->colorkey);
5037 case PROP_DRAW_BORDERS:
5038 g_value_set_boolean (value, xvimagesink->draw_borders);
5040 case PROP_WINDOW_WIDTH:
5041 if (xvimagesink->xwindow)
5042 g_value_set_uint64 (value, xvimagesink->xwindow->width);
5044 g_value_set_uint64 (value, 0);
5046 case PROP_WINDOW_HEIGHT:
5047 if (xvimagesink->xwindow)
5048 g_value_set_uint64 (value, xvimagesink->xwindow->height);
5050 g_value_set_uint64 (value, 0);
5052 #ifdef GST_EXT_XV_ENHANCEMENT
5053 case PROP_DISPLAY_MODE:
5054 g_value_set_enum (value, xvimagesink->display_mode);
5056 case PROP_DISPLAY_GEOMETRY_METHOD:
5057 g_value_set_enum (value, xvimagesink->display_geometry_method);
5060 g_value_set_enum(value, xvimagesink->flip);
5062 case PROP_ROTATE_ANGLE:
5063 g_value_set_enum (value, xvimagesink->rotate_angle);
5066 g_value_set_boolean (value, xvimagesink->visible);
5069 g_value_set_int (value, xvimagesink->zoom);
5071 case PROP_DST_ROI_X:
5072 g_value_set_int (value, xvimagesink->dst_roi.x);
5074 case PROP_DST_ROI_Y:
5075 g_value_set_int (value, xvimagesink->dst_roi.y);
5077 case PROP_DST_ROI_W:
5078 g_value_set_int (value, xvimagesink->dst_roi.w);
5080 case PROP_DST_ROI_H:
5081 g_value_set_int (value, xvimagesink->dst_roi.h);
5083 case PROP_STOP_VIDEO:
5084 g_value_set_int (value, xvimagesink->stop_video);
5086 case PROP_PIXMAP_CB:
5087 g_value_set_pointer (value, xvimagesink->get_pixmap_cb);
5089 case PROP_PIXMAP_CB_USER_DATA:
5090 g_value_set_pointer (value, xvimagesink->get_pixmap_cb_user_data);
5092 #endif /* GST_EXT_XV_ENHANCEMENT */
5094 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5100 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
5104 GST_OBJECT_LOCK (xvimagesink);
5105 xvimagesink->running = FALSE;
5106 /* grab thread and mark it as NULL */
5107 thread = xvimagesink->event_thread;
5108 xvimagesink->event_thread = NULL;
5109 GST_OBJECT_UNLOCK (xvimagesink);
5111 /* invalidate the pool, current allocations continue, new buffer_alloc fails
5112 * with wrong_state */
5113 g_mutex_lock (xvimagesink->pool_lock);
5114 xvimagesink->pool_invalid = TRUE;
5115 g_mutex_unlock (xvimagesink->pool_lock);
5117 /* Wait for our event thread to finish before we clean up our stuff. */
5119 g_thread_join (thread);
5121 if (xvimagesink->cur_image) {
5122 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
5123 xvimagesink->cur_image = NULL;
5125 if (xvimagesink->xvimage) {
5126 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
5127 xvimagesink->xvimage = NULL;
5130 gst_xvimagesink_imagepool_clear (xvimagesink);
5132 if (xvimagesink->xwindow) {
5133 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
5134 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
5135 xvimagesink->xwindow = NULL;
5137 #ifdef GST_EXT_XV_ENHANCEMENT
5138 if (xvimagesink->get_pixmap_cb) {
5140 if (xvimagesink->xpixmap[0] && xvimagesink->xpixmap[0]->pixmap) {
5141 g_mutex_lock (xvimagesink->x_lock);
5142 GST_INFO_OBJECT( xvimagesink, "calling XvStopVideo()" );
5143 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id, xvimagesink->xpixmap[0]->pixmap);
5144 g_mutex_unlock (xvimagesink->x_lock);
5146 for (i = 0; i < MAX_PIXMAP_NUM; i++) {
5147 if (xvimagesink->xpixmap[i]) {
5148 gst_xvimagesink_xpixmap_destroy (xvimagesink, xvimagesink->xpixmap[i]);
5149 xvimagesink->xpixmap[i] = NULL;
5152 xvimagesink->get_pixmap_cb = NULL;
5153 xvimagesink->get_pixmap_cb_user_data = NULL;
5155 #endif /* GST_EXT_XV_ENHANCEMENT */
5156 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
5157 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
5158 xvimagesink->have_render_rect = FALSE;
5160 gst_xvimagesink_xcontext_clear (xvimagesink);
5163 /* Finalize is called only once, dispose can be called multiple times.
5164 * We use mutexes and don't reset stuff to NULL here so let's register
5167 gst_xvimagesink_finalize (GObject * object)
5169 GstXvImageSink *xvimagesink;
5171 xvimagesink = GST_XVIMAGESINK (object);
5173 gst_xvimagesink_reset (xvimagesink);
5175 if (xvimagesink->display_name) {
5176 g_free (xvimagesink->display_name);
5177 xvimagesink->display_name = NULL;
5180 if (xvimagesink->par) {
5181 g_free (xvimagesink->par);
5182 xvimagesink->par = NULL;
5184 if (xvimagesink->x_lock) {
5185 g_mutex_free (xvimagesink->x_lock);
5186 xvimagesink->x_lock = NULL;
5188 if (xvimagesink->flow_lock) {
5189 g_mutex_free (xvimagesink->flow_lock);
5190 xvimagesink->flow_lock = NULL;
5192 if (xvimagesink->pool_lock) {
5193 g_mutex_free (xvimagesink->pool_lock);
5194 xvimagesink->pool_lock = NULL;
5197 g_free (xvimagesink->media_title);
5199 G_OBJECT_CLASS (parent_class)->finalize (object);
5203 gst_xvimagesink_init (GstXvImageSink * xvimagesink,
5204 GstXvImageSinkClass * xvimagesinkclass)
5206 xvimagesink->display_name = NULL;
5207 xvimagesink->adaptor_no = 0;
5208 xvimagesink->xcontext = NULL;
5209 xvimagesink->xwindow = NULL;
5210 xvimagesink->xvimage = NULL;
5211 xvimagesink->cur_image = NULL;
5213 xvimagesink->hue = xvimagesink->saturation = 0;
5214 xvimagesink->contrast = xvimagesink->brightness = 0;
5215 xvimagesink->cb_changed = FALSE;
5217 xvimagesink->fps_n = 0;
5218 xvimagesink->fps_d = 0;
5219 xvimagesink->video_width = 0;
5220 xvimagesink->video_height = 0;
5222 xvimagesink->x_lock = g_mutex_new ();
5223 xvimagesink->flow_lock = g_mutex_new ();
5225 xvimagesink->image_pool = NULL;
5226 xvimagesink->pool_lock = g_mutex_new ();
5228 xvimagesink->synchronous = FALSE;
5229 xvimagesink->double_buffer = TRUE;
5230 xvimagesink->running = FALSE;
5231 xvimagesink->keep_aspect = FALSE;
5232 xvimagesink->handle_events = TRUE;
5233 xvimagesink->par = NULL;
5234 xvimagesink->handle_expose = TRUE;
5235 xvimagesink->autopaint_colorkey = TRUE;
5237 /* on 16bit displays this becomes r,g,b = 1,2,3
5238 * on 24bit displays this becomes r,g,b = 8,8,16
5239 * as a port atom value
5241 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
5242 xvimagesink->draw_borders = TRUE;
5244 #ifdef GST_EXT_XV_ENHANCEMENT
5245 xvimagesink->xid_updated = FALSE;
5246 xvimagesink->display_mode = DISPLAY_MODE_DEFAULT;
5247 xvimagesink->rotate_changed = TRUE;
5248 xvimagesink->display_geometry_method = DEF_DISPLAY_GEOMETRY_METHOD;
5249 xvimagesink->flip = DEF_DISPLAY_FLIP;
5250 xvimagesink->rotate_angle = DEGREE_270;
5251 xvimagesink->visible = TRUE;
5252 xvimagesink->zoom = 1;
5253 xvimagesink->rotation = -1;
5254 xvimagesink->dst_roi.x = 0;
5255 xvimagesink->dst_roi.y = 0;
5256 xvimagesink->dst_roi.w = 0;
5257 xvimagesink->dst_roi.h = 0;
5258 xvimagesink->xim_transparenter = NULL;
5259 xvimagesink->scr_w = 0;
5260 xvimagesink->scr_h = 0;
5261 xvimagesink->aligned_width = 0;
5262 xvimagesink->aligned_height = 0;
5263 xvimagesink->stop_video = FALSE;
5264 xvimagesink->is_hided = FALSE;
5265 xvimagesink->drm_fd = -1;
5266 xvimagesink->current_pixmap_idx = -1;
5267 xvimagesink->get_pixmap_cb = NULL;
5268 xvimagesink->get_pixmap_cb_user_data = NULL;
5269 #endif /* GST_EXT_XV_ENHANCEMENT */
5273 gst_xvimagesink_base_init (gpointer g_class)
5275 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
5277 gst_element_class_set_details_simple (element_class,
5278 "Video sink", "Sink/Video",
5279 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
5281 gst_element_class_add_static_pad_template (element_class,
5282 &gst_xvimagesink_sink_template_factory);
5286 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
5288 GObjectClass *gobject_class;
5289 GstElementClass *gstelement_class;
5290 GstBaseSinkClass *gstbasesink_class;
5291 GstVideoSinkClass *videosink_class;
5293 gobject_class = (GObjectClass *) klass;
5294 gstelement_class = (GstElementClass *) klass;
5295 gstbasesink_class = (GstBaseSinkClass *) klass;
5296 videosink_class = (GstVideoSinkClass *) klass;
5298 gobject_class->set_property = gst_xvimagesink_set_property;
5299 gobject_class->get_property = gst_xvimagesink_get_property;
5301 g_object_class_install_property (gobject_class, PROP_CONTRAST,
5302 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
5303 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5304 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
5305 g_param_spec_int ("brightness", "Brightness",
5306 "The brightness of the video", -1000, 1000, 0,
5307 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5308 g_object_class_install_property (gobject_class, PROP_HUE,
5309 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
5310 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5311 g_object_class_install_property (gobject_class, PROP_SATURATION,
5312 g_param_spec_int ("saturation", "Saturation",
5313 "The saturation of the video", -1000, 1000, 0,
5314 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5315 g_object_class_install_property (gobject_class, PROP_DISPLAY,
5316 g_param_spec_string ("display", "Display", "X Display name", NULL,
5317 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5318 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
5319 g_param_spec_boolean ("synchronous", "Synchronous",
5320 "When enabled, runs the X display in synchronous mode. "
5321 "(unrelated to A/V sync, used only for debugging)", FALSE,
5322 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5323 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
5324 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
5325 "The pixel aspect ratio of the device", "1/1",
5326 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5327 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
5328 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
5329 "When enabled, scaling will respect original aspect ratio", FALSE,
5330 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5331 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
5332 g_param_spec_boolean ("handle-events", "Handle XEvents",
5333 "When enabled, XEvents will be selected and handled", TRUE,
5334 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5335 g_object_class_install_property (gobject_class, PROP_DEVICE,
5336 g_param_spec_string ("device", "Adaptor number",
5337 "The number of the video adaptor", "0",
5338 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5339 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
5340 g_param_spec_string ("device-name", "Adaptor name",
5341 "The name of the video adaptor", NULL,
5342 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5344 * GstXvImageSink:handle-expose
5346 * When enabled, the current frame will always be drawn in response to X
5351 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
5352 g_param_spec_boolean ("handle-expose", "Handle expose",
5354 "the current frame will always be drawn in response to X Expose "
5355 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5358 * GstXvImageSink:double-buffer
5360 * Whether to double-buffer the output.
5364 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
5365 g_param_spec_boolean ("double-buffer", "Double-buffer",
5366 "Whether to double-buffer the output", TRUE,
5367 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5369 * GstXvImageSink:autopaint-colorkey
5371 * Whether to autofill overlay with colorkey
5375 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
5376 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
5377 "Whether to autofill overlay with colorkey", TRUE,
5378 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5380 * GstXvImageSink:colorkey
5382 * Color to use for the overlay mask.
5386 g_object_class_install_property (gobject_class, PROP_COLORKEY,
5387 g_param_spec_int ("colorkey", "Colorkey",
5388 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
5389 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5392 * GstXvImageSink:draw-borders
5394 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
5395 * unused parts of the video area.
5399 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
5400 g_param_spec_boolean ("draw-borders", "Colorkey",
5401 "Draw black borders to fill unused area in force-aspect-ratio mode",
5402 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5405 * GstXvImageSink:window-width
5407 * Actual width of the video window.
5411 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
5412 g_param_spec_uint64 ("window-width", "window-width",
5413 "Width of the window", 0, G_MAXUINT64, 0,
5414 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5417 * GstXvImageSink:window-height
5419 * Actual height of the video window.
5423 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
5424 g_param_spec_uint64 ("window-height", "window-height",
5425 "Height of the window", 0, G_MAXUINT64, 0,
5426 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5428 #ifdef GST_EXT_XV_ENHANCEMENT
5430 * GstXvImageSink:display-mode
5432 * select display mode
5434 g_object_class_install_property(gobject_class, PROP_DISPLAY_MODE,
5435 g_param_spec_enum("display-mode", "Display Mode",
5436 "Display device setting",
5437 GST_TYPE_XVIMAGESINK_DISPLAY_MODE, DISPLAY_MODE_DEFAULT,
5438 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5441 * GstXvImageSink:display-geometry-method
5443 * Display geometrical method setting
5445 g_object_class_install_property(gobject_class, PROP_DISPLAY_GEOMETRY_METHOD,
5446 g_param_spec_enum("display-geometry-method", "Display geometry method",
5447 "Geometrical method for display",
5448 GST_TYPE_XVIMAGESINK_DISPLAY_GEOMETRY_METHOD, DEF_DISPLAY_GEOMETRY_METHOD,
5449 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5452 * GstXvImageSink:display-flip
5454 * Display flip setting
5456 g_object_class_install_property(gobject_class, PROP_FLIP,
5457 g_param_spec_enum("flip", "Display flip",
5459 GST_TYPE_XVIMAGESINK_FLIP, DEF_DISPLAY_FLIP,
5460 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5463 * GstXvImageSink:rotate
5465 * Draw rotation angle setting
5467 g_object_class_install_property(gobject_class, PROP_ROTATE_ANGLE,
5468 g_param_spec_enum("rotate", "Rotate angle",
5469 "Rotate angle of display output",
5470 GST_TYPE_XVIMAGESINK_ROTATE_ANGLE, DEGREE_270,
5471 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5474 * GstXvImageSink:visible
5476 * Whether reserve original src size or not
5478 g_object_class_install_property (gobject_class, PROP_VISIBLE,
5479 g_param_spec_boolean ("visible", "Visible",
5480 "Draws screen or blacks out, true means visible, false blacks out",
5481 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5484 * GstXvImageSink:zoom
5486 * Scale small area of screen to 2X, 3X, ... , 9X
5488 g_object_class_install_property (gobject_class, PROP_ZOOM,
5489 g_param_spec_int ("zoom", "Zoom",
5490 "Zooms screen as nX", 1, 9, 1,
5491 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5494 * GstXvImageSink:dst-roi-x
5496 * X value of Destination ROI
5498 g_object_class_install_property (gobject_class, PROP_DST_ROI_X,
5499 g_param_spec_int ("dst-roi-x", "Dst-ROI-X",
5500 "X value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_WIDTH, 0,
5501 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5504 * GstXvImageSink:dst-roi-y
5506 * Y value of Destination ROI
5508 g_object_class_install_property (gobject_class, PROP_DST_ROI_Y,
5509 g_param_spec_int ("dst-roi-y", "Dst-ROI-Y",
5510 "Y value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_HEIGHT, 0,
5511 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5514 * GstXvImageSink:dst-roi-w
5516 * W value of Destination ROI
5518 g_object_class_install_property (gobject_class, PROP_DST_ROI_W,
5519 g_param_spec_int ("dst-roi-w", "Dst-ROI-W",
5520 "W value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_WIDTH, 0,
5521 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5524 * GstXvImageSink:dst-roi-h
5526 * H value of Destination ROI
5528 g_object_class_install_property (gobject_class, PROP_DST_ROI_H,
5529 g_param_spec_int ("dst-roi-h", "Dst-ROI-H",
5530 "H value of Destination ROI(only effective \"CUSTOM_ROI\")", 0, XV_SCREEN_SIZE_HEIGHT, 0,
5531 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5534 * GstXvImageSink:stop-video
5536 * Stop video for releasing video source buffer
5538 g_object_class_install_property (gobject_class, PROP_STOP_VIDEO,
5539 g_param_spec_int ("stop-video", "Stop-Video",
5540 "Stop video for releasing video source buffer", 0, 1, 0,
5541 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5543 g_object_class_install_property (gobject_class, PROP_PIXMAP_CB,
5544 g_param_spec_pointer("pixmap-id-callback", "Pixmap-Id-Callback",
5545 "pointer of callback function for getting pixmap id", G_PARAM_READWRITE));
5547 g_object_class_install_property (gobject_class, PROP_PIXMAP_CB_USER_DATA,
5548 g_param_spec_pointer("pixmap-id-callback-userdata", "Pixmap-Id-Callback-Userdata",
5549 "pointer of user data of callback function for getting pixmap id", G_PARAM_READWRITE));
5552 * GstXvImageSink::frame-render-error
5554 gst_xvimagesink_signals[SIGNAL_FRAME_RENDER_ERROR] = g_signal_new (
5555 "frame-render-error",
5556 G_TYPE_FROM_CLASS (klass),
5561 gst_xvimagesink_BOOLEAN__POINTER,
5566 #endif /* GST_EXT_XV_ENHANCEMENT */
5568 gobject_class->finalize = gst_xvimagesink_finalize;
5570 gstelement_class->change_state =
5571 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
5573 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
5574 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
5575 gstbasesink_class->buffer_alloc =
5576 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
5577 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
5578 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
5580 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
5583 /* ============================================================= */
5585 /* Public Methods */
5587 /* ============================================================= */
5589 /* =========================================== */
5591 /* Object typing & Creation */
5593 /* =========================================== */
5595 gst_xvimagesink_init_interfaces (GType type)
5597 static const GInterfaceInfo iface_info = {
5598 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
5602 static const GInterfaceInfo navigation_info = {
5603 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
5607 static const GInterfaceInfo overlay_info = {
5608 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
5612 static const GInterfaceInfo colorbalance_info = {
5613 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
5617 static const GInterfaceInfo propertyprobe_info = {
5618 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
5623 g_type_add_interface_static (type,
5624 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
5625 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
5626 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &overlay_info);
5627 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
5628 &colorbalance_info);
5629 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
5630 &propertyprobe_info);
5632 /* register type and create class in a more safe place instead of at
5633 * runtime since the type registration and class creation is not
5635 g_type_class_ref (gst_xvimage_buffer_get_type ());
5639 plugin_init (GstPlugin * plugin)
5641 if (!gst_element_register (plugin, "xvimagesink",
5642 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
5645 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
5646 "xvimagesink element");
5647 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
5652 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
5655 "XFree86 video output plugin using Xv extension",
5656 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)