xvimagesink.c 99.4 KB
Newer Older
1
/* GStreamer
2
 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
19

Julien Moutte's avatar
Julien Moutte committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
 * SECTION:element-xvimagesink
 *
 * <refsect2>
 * <para>
 * XvImageSink renders video frames to a drawable (XWindow) on a local display
 * using the XVideo extension. Rendering to a remote display is theorically 
 * possible but i doubt that the XVideo extension is actually available when 
 * connecting to a remote display. This element can receive a Window ID from the
 * application through the XOverlay interface and will then render video frames
 * in this drawable. If no Window ID was provided by the application, the 
 * element will create its own internal window and render into it.
 * </para>
 * <title>Scaling</title>
 * <para>
 * The XVideo extension, when it's available, handles hardware accelerated 
 * scaling of video frames. This means that the element will just accept
 * incoming video frames no matter their geometry and will then put them to the
 * drawable scaling them on the fly. Using the 
 * <link linkend="GstXvImageSink--force-aspect-ratio">force-aspect-ratio</link>
 * property it is possible to enforce scaling with a constant aspect ratio,
 * which means drawing black borders around the video frame.
 * </para>
 * <title>Events</title>
 * <para>
 * XvImageSink creates a thread to handle events coming from the drawable. There
 * are several kind of events that can be grouped in 2 big categories: input 
 * events and window state related events. Input events will be translated to
 * navigation events and pushed upstream for other elements to react on them.
 * This includes events such as pointer moves, key press/release, clicks etc...
 * Other events are used to handle the drawable appearance even when the data
 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
 * paused, it will receive expose events from the drawable and draw the latest
 * frame with correct borders/aspect-ratio.
 * </para>
 * <title>Pixel aspect ratio</title>
 * <para>
 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
 * the display specified in the
 * <link linkend="GstXvImageSink--display">display</link> property or the
 * default display if nothing specified. Once this connection is open it will
 * inspect the display configuration including the physical display geometry and
 * then calculate the pixel aspect ratio. When receiving video frames with a 
 * different pixel aspect ratio, XvImageSink will use hardware scaling to
 * display the video frames correctly on display's pixel aspect ratio.
 * Sometimes the calculated pixel aspect ratio can be wrong, it is
 * then possible to enforce a specific pixel aspect ratio using the
 * <link linkend="GstXvImageSink--pixel-aspect-ratio">pixel-aspect-ratio</link>
 * property.
 * </para>
 * <title>Examples</title>
 * <para>
 * Here is a simple pipeline to test hardware scaling :
 * <programlisting>
 * gst-launch -v videotestsrc ! xvimagesink
 * </programlisting>
 * When the test video signal appears you can resize the window and see that
 * video frames are scaled through hardware (no extra CPU cost). You can try
 * again setting the force-aspect-ratio property to true and observe the borders
 * drawn around the scaled image respecting aspect ratio.
 * <programlisting>
 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
 * </programlisting>
 * </para>
 * <para>
 * Here is a simple pipeline to test navigation events :
 * <programlisting>
 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
 * </programlisting>
 * While moving the mouse pointer over the test signal you will see a black box
 * following the mouse pointer. If you press the mouse button somewhere on the 
 * video and release it somewhere else a green box will appear where you pressed
 * the button and a red one where you released it. (The navigationtest element
 * is part of gst-plugins-good.) You can observe here that even if the images
 * are scaled through hardware the pointer coordinates are converted back to the
 * original video frame geometry so that the box can be drawn to the correct 
 * position. This also handles borders correctly, limiting coordinates to the 
 * image area
 * </para>
 * <para>
 * Here is a simple pipeline to test pixel aspect ratio :
 * <programlisting>
 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
 * </programlisting>
 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
 * videotestsrc, in most cases the pixel aspect ratio of the display will be
 * 1/1. This means that XvImageSink will have to do the scaling to convert 
 * incoming frames to a size that will match the display pixel aspect ratio
 * (from 320x240 to 320x180 in this case). Note that you might have to escape 
 * some characters for your shell like '\(fraction\)'.
 * </para>
 * <para>
 * Here is a test pipeline to test the colorbalance interface :
 * <programlisting>
 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
 * </programlisting>
 * </para>
 * </refsect2>
 */

120
121
122
123
124
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Our interfaces */
125
126
127
#include <gst/interfaces/navigation.h>
#include <gst/interfaces/xoverlay.h>
#include <gst/interfaces/colorbalance.h>
128
#include <gst/interfaces/propertyprobe.h>
129
130
/* Helper functions */
#include <gst/video/video.h>
131
132
133
134

/* Object header */
#include "xvimagesink.h"

135
136
137
138
139
/* Debugging category */
#include <gst/gstinfo.h>
GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
#define GST_CAT_DEFAULT gst_debug_xvimagesink

140
141
142
143
144
145
146
147
148
149
150
151
typedef struct
{
  unsigned long flags;
  unsigned long functions;
  unsigned long decorations;
  long input_mode;
  unsigned long status;
}
MotifWmHints, MwmHints;

#define MWM_HINTS_DECORATIONS   (1L << 1)

152
153
static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);

154
155
156
157
158
159
static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
    xvimagesink, GstXWindow * xwindow);
static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
    GstCaps * caps);
static void gst_xvimagesink_expose (GstXOverlay * overlay);

160
/* ElementFactory information */
Stefan Kost's avatar
Stefan Kost committed
161
static const GstElementDetails gst_xvimagesink_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
163
164
165
GST_ELEMENT_DETAILS ("Video sink",
    "Sink/Video",
    "A Xv based videosink",
    "Julien Moutte <julien@moutte.net>");
166
167
168

/* Default template - initiated with class struct to allow gst-register to work
   without X running */
David Schleef's avatar
David Schleef committed
169
static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
170
171
172
173
    GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-rgb, "
174
        "framerate = (fraction) [ 0, MAX ], "
175
176
        "width = (int) [ 1, MAX ], "
        "height = (int) [ 1, MAX ]; "
177
        "video/x-raw-yuv, "
178
        "framerate = (fraction) [ 0, MAX ], "
179
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
180
181
182
183
    );

enum
{
184
185
186
187
188
  ARG_0,
  ARG_CONTRAST,
  ARG_BRIGHTNESS,
  ARG_HUE,
  ARG_SATURATION,
189
  ARG_DISPLAY,
190
  ARG_SYNCHRONOUS,
191
  ARG_PIXEL_ASPECT_RATIO,
192
  ARG_FORCE_ASPECT_RATIO,
193
  ARG_HANDLE_EVENTS,
194
195
  ARG_DEVICE,
  ARG_DEVICE_NAME
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
196
      /* FILL ME */
197
198
};

199
static GstVideoSinkClass *parent_class = NULL;
200
201
202
203
204
205
206

/* ============================================================= */
/*                                                               */
/*                       Private Methods                         */
/*                                                               */
/* ============================================================= */

207
208
209
210
211
212
213
214
215
/* xvimage buffers */

#define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())

#define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
#define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))

/* This function destroys a GstXvImage handling XShm availability */
static void
Wim Taymans's avatar
Wim Taymans committed
216
gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
217
218
219
{
  GstXvImageSink *xvimagesink;

220
221
  GST_DEBUG_OBJECT (xvimage, "Destroying buffer");

222
  xvimagesink = xvimage->xvimagesink;
223
  if (G_UNLIKELY (xvimagesink == NULL))
Wim Taymans's avatar
Wim Taymans committed
224
    goto no_sink;
225
226
227

  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));

228
229
  GST_OBJECT_LOCK (xvimagesink);

230
231
232
233
  /* If the destroyed image is the current one we destroy our reference too */
  if (xvimagesink->cur_image == xvimage)
    xvimagesink->cur_image = NULL;

234
  /* We might have some buffers destroyed after changing state to NULL */
235
236
237
238
239
240
241
242
243
  if (xvimagesink->xcontext == NULL) {
    GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
#ifdef HAVE_XSHM
    /* Need to free the shared memory segment even if the x context
     * was already cleaned up */
    if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
      shmdt (xvimage->SHMInfo.shmaddr);
    }
#endif
244
245
246
    goto beach;
  }

247
248
249
250
251
  g_mutex_lock (xvimagesink->x_lock);

#ifdef HAVE_XSHM
  if (xvimagesink->xcontext->use_xshm) {
    if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
252
      GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
253
          xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
254
255
      XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
      XSync (xvimagesink->xcontext->disp, FALSE);
256

257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
      shmdt (xvimage->SHMInfo.shmaddr);
    }
    if (xvimage->xvimage)
      XFree (xvimage->xvimage);
  } else
#endif /* HAVE_XSHM */
  {
    if (xvimage->xvimage) {
      if (xvimage->xvimage->data) {
        g_free (xvimage->xvimage->data);
      }
      XFree (xvimage->xvimage);
    }
  }

  XSync (xvimagesink->xcontext->disp, FALSE);

  g_mutex_unlock (xvimagesink->x_lock);
Wim Taymans's avatar
Wim Taymans committed
275

276
beach:
277
  GST_OBJECT_UNLOCK (xvimagesink);
278
279
280
  xvimage->xvimagesink = NULL;
  gst_object_unref (xvimagesink);

Wim Taymans's avatar
Wim Taymans committed
281
282
283
284
285
286
287
288
289
290
291
292
293
  return;

no_sink:
  {
    GST_WARNING ("no sink found");
    return;
  }
}

static void
gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
{
  GstXvImageSink *xvimagesink;
294
  gboolean running;
Wim Taymans's avatar
Wim Taymans committed
295
296

  xvimagesink = xvimage->xvimagesink;
297
  if (G_UNLIKELY (xvimagesink == NULL))
Wim Taymans's avatar
Wim Taymans committed
298
299
    goto no_sink;

300
301
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));

302
303
304
305
  GST_OBJECT_LOCK (xvimagesink);
  running = xvimagesink->running;
  GST_OBJECT_UNLOCK (xvimagesink);

Wim Taymans's avatar
Wim Taymans committed
306
  /* If our geometry changed we can't reuse that image. */
307
308
309
310
  if (running == FALSE) {
    GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
    gst_xvimage_buffer_destroy (xvimage);
  } else if ((xvimage->width != xvimagesink->video_width) ||
Wim Taymans's avatar
Wim Taymans committed
311
      (xvimage->height != xvimagesink->video_height)) {
312
313
    GST_LOG_OBJECT (xvimage,
        "destroy image as its size changed %dx%d vs current %dx%d",
Wim Taymans's avatar
Wim Taymans committed
314
315
316
317
318
        xvimage->width, xvimage->height,
        xvimagesink->video_width, xvimagesink->video_height);
    gst_xvimage_buffer_destroy (xvimage);
  } else {
    /* In that case we can reuse the image and add it to our image pool. */
319
    GST_LOG_OBJECT (xvimage, "recycling image in pool");
Wim Taymans's avatar
Wim Taymans committed
320
321
322
323
324
325
326
327
328
329
330
331
332
333
    /* need to increment the refcount again to recycle */
    gst_buffer_ref (GST_BUFFER (xvimage));
    g_mutex_lock (xvimagesink->pool_lock);
    xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
        xvimage);
    g_mutex_unlock (xvimagesink->pool_lock);
  }
  return;

no_sink:
  {
    GST_WARNING ("no sink found");
    return;
  }
334
335
}

Wim Taymans's avatar
Wim Taymans committed
336
337
338
339
340
341
342
343
static void
gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
{
  /* make sure it is not recycled */
  xvimage->width = -1;
  xvimage->height = -1;
  gst_buffer_unref (GST_BUFFER (xvimage));
}
344
345

static void
346
gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
347
{
348
#ifdef HAVE_XSHM
349
350
  xvimage->SHMInfo.shmaddr = ((void *) -1);
  xvimage->SHMInfo.shmid = -1;
351
#endif
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
}

static void
gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
{
  GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);

  mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
      gst_xvimage_buffer_finalize;
}

GType
gst_xvimage_buffer_get_type (void)
{
  static GType _gst_xvimage_buffer_type;

  if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
    static const GTypeInfo xvimage_buffer_info = {
      sizeof (GstBufferClass),
      NULL,
      NULL,
      gst_xvimage_buffer_class_init,
      NULL,
      NULL,
      sizeof (GstXvImageBuffer),
      0,
378
      (GInstanceInitFunc) gst_xvimage_buffer_init,
379
380
381
382
383
384
385
386
      NULL
    };
    _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstXvImageBuffer", &xvimage_buffer_info, 0);
  }
  return _gst_xvimage_buffer_type;
}

387
388
/* X11 stuff */

389
390
391
#ifdef HAVE_XSHM
static gboolean error_caught = FALSE;

Julien Moutte's avatar
Julien Moutte committed
392
static int
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393
gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
Julien Moutte's avatar
Julien Moutte committed
394
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
395
396
  char error_msg[1024];

Julien Moutte's avatar
Julien Moutte committed
397
  XGetErrorText (display, xevent->error_code, error_msg, 1024);
398
  GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
399
  error_caught = TRUE;
Julien Moutte's avatar
Julien Moutte committed
400
401
402
403
404
405
  return 0;
}

/* This function checks that it is actually really possible to create an image
   using XShm */
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
406
gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
Julien Moutte's avatar
Julien Moutte committed
407
{
408
409
410
  XvImage *xvimage;
  XShmSegmentInfo SHMInfo;
  gint size;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
411
  int (*handler) (Display *, XErrorEvent *);
Tim Ringenbach's avatar
Tim Ringenbach committed
412
  gboolean result = FALSE;
413
  gboolean did_attach = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
414

Julien Moutte's avatar
Julien Moutte committed
415
  g_return_val_if_fail (xcontext != NULL, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
416

417
418
419
420
421
422
423
  /* Sync to ensure any older errors are already processed */
  XSync (xcontext->disp, FALSE);

  /* Set defaults so we don't free these later unnecessarily */
  SHMInfo.shmaddr = ((void *) -1);
  SHMInfo.shmid = -1;

Julien Moutte's avatar
Julien Moutte committed
424
  /* Setting an error handler to catch failure */
Tim Ringenbach's avatar
Tim Ringenbach committed
425
  error_caught = FALSE;
Julien Moutte's avatar
Julien Moutte committed
426
  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
427

Julien Moutte's avatar
Julien Moutte committed
428
  /* Trying to create a 1x1 picture */
429
  GST_DEBUG ("XvShmCreateImage of 1x1");
430
431
  xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
      xcontext->im_format, NULL, 1, 1, &SHMInfo);
432
433
434
435

  /* Might cause an error, sync to ensure it is noticed */
  XSync (xcontext->disp, FALSE);
  if (!xvimage || error_caught) {
436
437
438
    GST_WARNING ("could not XvShmCreateImage a 1x1 image");
    goto beach;
  }
439
  size = xvimage->data_size;
Julien Moutte's avatar
Julien Moutte committed
440

441
442
443
  SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
  if (SHMInfo.shmid == -1) {
    GST_WARNING ("could not get shared memory of %d bytes", size);
444
445
446
    goto beach;
  }

447
448
  SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
  if (SHMInfo.shmaddr == ((void *) -1)) {
449
    GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
450
451
    /* Clean up the shared memory segment */
    shmctl (SHMInfo.shmid, IPC_RMID, 0);
452
453
454
    goto beach;
  }

455
456
457
458
459
  /* Delete the shared memory segment as soon as we manage to attach.
   * This way, it will be deleted as soon as we detach later, and not
   * leaked if we crash. */
  shmctl (SHMInfo.shmid, IPC_RMID, 0);

460
461
  xvimage->data = SHMInfo.shmaddr;
  SHMInfo.readOnly = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
462

463
  if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
464
465
466
    GST_WARNING ("Failed to XShmAttach");
    goto beach;
  }
Julien Moutte's avatar
Julien Moutte committed
467

468
469
470
  /* Sync to ensure we see any errors we caused */
  XSync (xcontext->disp, FALSE);

Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
471
  GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
472
473
      SHMInfo.shmseg);

474
475
476
477
478
  if (!error_caught) {
    did_attach = TRUE;
    /* store whether we succeeded in result */
    result = TRUE;
  }
479
480

beach:
481
482
483
484
  /* Sync to ensure we swallow any errors we caused and reset error_caught */
  XSync (xcontext->disp, FALSE);

  error_caught = FALSE;
Tim Ringenbach's avatar
Tim Ringenbach committed
485
  XSetErrorHandler (handler);
486

487
  if (did_attach) {
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
488
    GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
489
        SHMInfo.shmid, SHMInfo.shmseg);
490
491
492
    XShmDetach (xcontext->disp, &SHMInfo);
    XSync (xcontext->disp, FALSE);
  }
493
494
  if (SHMInfo.shmaddr != ((void *) -1))
    shmdt (SHMInfo.shmaddr);
495
496
  if (xvimage)
    XFree (xvimage);
Tim Ringenbach's avatar
Tim Ringenbach committed
497
  return result;
Julien Moutte's avatar
Julien Moutte committed
498
}
499
#endif /* HAVE_XSHM */
Julien Moutte's avatar
Julien Moutte committed
500

501
/* This function handles GstXvImage creation depending on XShm availability */
502
static GstXvImageBuffer *
503
gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
504
{
505
  GstXvImageBuffer *xvimage = NULL;
506
  GstStructure *structure = NULL;
507
  gboolean succeeded = FALSE;
508
  int (*handler) (Display *, XErrorEvent *);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
509

510
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
511

512
  xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
513
  GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
514

515
516
517
518
519
520
521
522
523
524
525
  structure = gst_caps_get_structure (caps, 0);

  if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
      !gst_structure_get_int (structure, "height", &xvimage->height)) {
    GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
  }

  GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
      xvimage->height);

  xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
526
  if (xvimage->im_format == -1) {
527
528
    GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
        GST_PTR_FORMAT, caps);
529
530
531
    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
        ("Failed to create output image buffer of %dx%d pixels",
            xvimage->width, xvimage->height), ("Invalid input caps"));
532
    goto beach_unlocked;
533
  }
534
  xvimage->xvimagesink = gst_object_ref (xvimagesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
535

536
537
  g_mutex_lock (xvimagesink->x_lock);

538
539
540
541
  /* Setting an error handler to catch failure */
  error_caught = FALSE;
  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);

542
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
543
544
  if (xvimagesink->xcontext->use_xshm) {
    xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
545
        xvimagesink->xcontext->xv_port_id,
546
547
        xvimage->im_format, NULL,
        xvimage->width, xvimage->height, &xvimage->SHMInfo);
548
    if (!xvimage->xvimage || error_caught) {
549
      g_mutex_unlock (xvimagesink->x_lock);
550
551
552
553
      /* Reset error handler */
      error_caught = FALSE;
      XSetErrorHandler (handler);
      /* Push an error */
554
555
556
557
558
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
          ("could not XvShmCreateImage a %dx%d image",
              xvimage->width, xvimage->height));
559
      goto beach_unlocked;
560
561
562
    }

    /* we have to use the returned data_size for our shm size */
563
    xvimage->size = xvimage->xvimage->data_size;
564
565
    GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
        xvimage->size);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
566
567

    xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
568
        IPC_CREAT | 0777);
569
    if (xvimage->SHMInfo.shmid == -1) {
570
      g_mutex_unlock (xvimagesink->x_lock);
571
572
573
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
574
575
          ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
              xvimage->size));
576
      goto beach_unlocked;
577
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
578
579

    xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
580
    if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
581
      g_mutex_unlock (xvimagesink->x_lock);
582
583
584
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
585
          ("Failed to shmat: %s", g_strerror (errno)));
586
587
      /* Clean up the shared memory segment */
      shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
588
      goto beach_unlocked;
589
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
590

591
592
593
594
595
    /* Delete the shared memory segment as soon as we manage to attach.
     * This way, it will be deleted as soon as we detach later, and not
     * leaked if we crash. */
    shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);

596
    xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
597
598
    xvimage->SHMInfo.readOnly = FALSE;

599
    if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
600
      g_mutex_unlock (xvimagesink->x_lock);
601
602
603
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height), ("Failed to XShmAttach"));
604
      goto beach_unlocked;
605
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
606
607

    XSync (xvimagesink->xcontext->disp, FALSE);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
608
    GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
609
        xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
610
  } else
Julien Moutte's avatar
Julien Moutte committed
611
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
612
613
  {
    xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
614
        xvimagesink->xcontext->xv_port_id,
615
        xvimage->im_format, NULL, xvimage->width, xvimage->height);
616
    if (!xvimage->xvimage || error_caught) {
617
      g_mutex_unlock (xvimagesink->x_lock);
618
619
620
621
      /* Reset error handler */
      error_caught = FALSE;
      XSetErrorHandler (handler);
      /* Push an error */
622
623
624
625
626
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create outputimage buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
          ("could not XvCreateImage a %dx%d image",
              xvimage->width, xvimage->height));
627
      goto beach_unlocked;
628
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
629

630
    /* we have to use the returned data_size for our image size */
631
632
    xvimage->size = xvimage->xvimage->data_size;
    xvimage->xvimage->data = g_malloc (xvimage->size);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
633
634
635

    XSync (xvimagesink->xcontext->disp, FALSE);
  }
636
637
638
639
640

  /* Reset error handler */
  error_caught = FALSE;
  XSetErrorHandler (handler);

641
  succeeded = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
642

643
644
645
  GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
  GST_BUFFER_SIZE (xvimage) = xvimage->size;

646
647
  g_mutex_unlock (xvimagesink->x_lock);

648
beach_unlocked:
649
  if (!succeeded) {
Wim Taymans's avatar
Wim Taymans committed
650
    gst_xvimage_buffer_free (xvimage);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
651
652
    xvimage = NULL;
  }
653

654
655
656
  return xvimage;
}

657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
/* We are called with the x_lock taken */
static void
gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow, GstVideoRectangle rect)
{
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
  g_return_if_fail (xwindow != NULL);

  XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
      xvimagesink->xcontext->black);

  /* Left border */
  if (rect.x > 0) {
    XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, 0, rect.x, xwindow->height);
  }

  /* Right border */
  if ((rect.x + rect.w) < xwindow->width) {
    XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
        rect.x + rect.w, 0, xwindow->width, xwindow->height);
  }

  /* Top border */
  if (rect.y > 0) {
    XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, 0, xwindow->width, rect.y);
  }

  /* Bottom border */
  if ((rect.y + rect.h) < xwindow->height) {
    XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, rect.y + rect.h, xwindow->width, xwindow->height);
  }
}

693
694
/* This function puts a GstXvImage on a GstXvImageSink's window */
static void
695
696
gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
    GstXvImageBuffer * xvimage)
697
{
698
  GstVideoRectangle src, dst, result;
699
  gboolean draw_border = FALSE;
700

701
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
702

703
  /* We take the flow_lock. If expose is in there we don't want to run
704
705
706
     concurrently from the data flow thread */
  g_mutex_lock (xvimagesink->flow_lock);

707
708
709
710
711
  if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
    g_mutex_unlock (xvimagesink->flow_lock);
    return;
  }

712
713
714
715
716
717
  /* Draw borders when displaying the first frame. After this
     draw borders only on expose event. */
  if (!xvimagesink->cur_image) {
    draw_border = TRUE;
  }

718
  /* Store a reference to the last image we put, lose the previous one */
719
  if (xvimage && xvimagesink->cur_image != xvimage) {
720
    if (xvimagesink->cur_image) {
721
      GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
722
723
      gst_buffer_unref (xvimagesink->cur_image);
    }
724
    GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
725
726
    xvimagesink->cur_image =
        GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
727
728
  }

729
730
  /* Expose sends a NULL image, we take the latest frame */
  if (!xvimage) {
731
    draw_border = TRUE;
732
733
734
735
736
737
738
739
    if (xvimagesink->cur_image) {
      xvimage = xvimagesink->cur_image;
    } else {
      g_mutex_unlock (xvimagesink->flow_lock);
      return;
    }
  }

740
741
  gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);

742
743
744
745
  /* We use the calculated geometry from _setcaps as a source to respect 
     source and screen pixel aspect ratios. */
  src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
  src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
746
747
748
749
750
751
752
753
754
755
  dst.w = xvimagesink->xwindow->width;
  dst.h = xvimagesink->xwindow->height;

  if (xvimagesink->keep_aspect) {
    gst_video_sink_center_rect (src, dst, &result, TRUE);
  } else {
    result.x = result.y = 0;
    result.w = dst.w;
    result.h = dst.h;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
756

757
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
758

759
760
761
762
  if (draw_border) {
    gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
        result);
  }
763

764
  /* We scale to the window's geometry */
765
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
766
  if (xvimagesink->xcontext->use_xshm) {
767
    GST_LOG_OBJECT (xvimagesink,
768
769
        "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
        GST_PTR_FORMAT,
770
        xvimage->width, xvimage->height,
771
772
        xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
773
    XvShmPutImage (xvimagesink->xcontext->disp,
774
775
776
777
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
778
        result.x, result.y, result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
779
  } else
Julien Moutte's avatar
Julien Moutte committed
780
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
781
782
  {
    XvPutImage (xvimagesink->xcontext->disp,
783
784
785
786
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
787
        result.x, result.y, result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
788
789
790
791
  }

  XSync (xvimagesink->xcontext->disp, FALSE);

792
  g_mutex_unlock (xvimagesink->x_lock);
793
794

  g_mutex_unlock (xvimagesink->flow_lock);
795
796
}

797
798
799
800
801
802
803
804
805
806
static gboolean
gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
    GstXWindow * window)
{
  Atom hints_atom = None;
  MotifWmHints *hints;

  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
  g_return_val_if_fail (window != NULL, FALSE);

807
808
  g_mutex_lock (xvimagesink->x_lock);

809
  hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
810
  if (hints_atom == None) {
811
    g_mutex_unlock (xvimagesink->x_lock);
812
813
814
    return FALSE;
  }

815
816
  hints = g_malloc0 (sizeof (MotifWmHints));

817
818
819
820
821
822
823
824
825
  hints->flags |= MWM_HINTS_DECORATIONS;
  hints->decorations = 1 << 0;

  XChangeProperty (xvimagesink->xcontext->disp, window->win,
      hints_atom, hints_atom, 32, PropModeReplace,
      (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));

  XSync (xvimagesink->xcontext->disp, FALSE);

826
827
  g_mutex_unlock (xvimagesink->x_lock);

828
829
830
831
832
  g_free (hints);

  return TRUE;
}

833
834
/* This function handles a GstXWindow creation
 * The width and height are the actual pixel size on the display */
835
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
836
837
gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
    gint width, gint height)
838
839
840
{
  GstXWindow *xwindow = NULL;
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
841

842
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
843

844
  xwindow = g_new0 (GstXWindow, 1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
845

846
847
  xwindow->width = width;
  xwindow->height = height;
Julien Moutte's avatar
Julien Moutte committed
848
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
849

850
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
851

852
  xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
853
854
855
856
      xvimagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height,
      0, 0, xvimagesink->xcontext->black);

857
858
859
860
  /* We have to do that to prevent X from redrawing the background on 
   * ConfigureNotify. This takes away flickering of video when resizing. */
  XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);

861
862
863
864
865
  if (xvimagesink->handle_events) {
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
        StructureNotifyMask | PointerMotionMask | KeyPressMask |
        KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
866

867
  xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
868
869
      xwindow->win, 0, &values);

870
  XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
871
872
873

  XSync (xvimagesink->xcontext->disp, FALSE);

874
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
875

876
877
  gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);

878
  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
879

880
881
882
883
884
  return xwindow;
}

/* This function destroys a GstXWindow */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
885
886
gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
887
888
889
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
890

891
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
892

Julien Moutte's avatar
Julien Moutte committed
893
894
895
  /* If we did not create that window we just free the GC and let it live */
  if (xwindow->internal)
    XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
896
897
  else
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
898

899
  XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
900
901
902

  XSync (xvimagesink->xcontext->disp, FALSE);

903
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
904

905
906
907
  g_free (xwindow);
}

908
static void
909
910
gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
911
{
912
913
  XWindowAttributes attr;

914
915
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
916

917
  /* Update the window geometry */
918
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
919

920
921
  XGetWindowAttributes (xvimagesink->xcontext->disp,
      xvimagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
922

923
924
  xvimagesink->xwindow->width = attr.width;
  xvimagesink->xwindow->height = attr.height;
925
926
927
928

  g_mutex_unlock (xvimagesink->x_lock);
}

929
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
930
931
gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
932
933
934
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
935

936
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
937

938
939
940
  XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
      xwindow->win);

941
  XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
942
943
      xvimagesink->xcontext->black);

944
  XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
945
946
      0, 0, xwindow->width, xwindow->height);

947
  XSync (xvimagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
948

949
950
951
  g_mutex_unlock (xvimagesink->x_lock);
}

952
953
954
/* This function commits our internal colorbalance settings to our grabbed Xv
   port. If the xcontext is not initialized yet it simply returns */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
955
gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
956
957
{
  GList *channels = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
958

959
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
960

961
962
963
  /* If we haven't initialized the X context we can't update anything */
  if (xvimagesink->xcontext == NULL)
    return;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
964

965
966
967
968
  /* For each channel of the colorbalance we calculate the correct value
     doing range conversion and then set the Xv port attribute to match our
     values. */
  channels = xvimagesink->xcontext->channels_list;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
969
970
971
972
973
974
975
976
977
978
979
980
981
982

  while (channels) {
    if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
      GstColorBalanceChannel *channel = NULL;
      gint value = 0;
      gdouble convert_coef;

      channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
      g_object_ref (channel);

      /* Our range conversion coef */
      convert_coef = (channel->max_value - channel->min_value) / 2000.0;

      if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
983
        value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
984
      } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
985
986
        value = (xvimagesink->saturation + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
987
      } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
988
989
        value = (xvimagesink->contrast + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
990
      } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
991
992
        value = (xvimagesink->brightness + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
993
      } else {
994
995
996
        g_warning ("got an unknown channel %s", channel->label);
        g_object_unref (channel);
        return;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
997
998
999
1000
1001
      }

      /* Committing to Xv port */
      g_mutex_lock (xvimagesink->x_lock);
      XvSetPortAttribute (xvimagesink->xcontext->disp,
1002
1003
          xvimagesink->xcontext->xv_port_id,
          XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1004
1005
1006
      g_mutex_unlock (xvimagesink->x_lock);

      g_object_unref (channel);
1007
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1008
1009
    channels = g_list_next (channels);
  }
1010
1011
}

1012
1013
1014
1015
1016
/* This function handles XEvents that might be in the queue. It generates
   GstEvent that will be sent upstream in the pipeline to handle interactivity
   and navigation. It will also listen for configure events on the window to
   trigger caps renegotiation so on the fly software scaling can work. */
static void
1017
gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1018
1019
{
  XEvent e;
1020
1021
  guint pointer_x = 0, pointer_y = 0;
  gboolean pointer_moved = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1022

1023
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1024

1025
1026
  /* We get all pointer motion events, only the last position is
     interesting. */
1027
  g_mutex_lock (xvimagesink->flow_lock);
1028
1029
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1030
          xvimagesink->xwindow->win, PointerMotionMask, &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1031
    g_mutex_unlock (xvimagesink->x_lock);
1032
    g_mutex_unlock (xvimagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1033
1034
1035

    switch (e.type) {
      case MotionNotify:
1036
1037
1038
1039
        pointer_x = e.xmotion.x;
        pointer_y = e.xmotion.y;
        pointer_moved = TRUE;
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1040
      default:
1041
        break;
1042
    }
1043
    g_mutex_lock (xvimagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1044
1045
    g_mutex_lock (xvimagesink->x_lock);
  }
1046
  g_mutex_unlock (xvimagesink->x_lock);
1047
  g_mutex_unlock (xvimagesink->flow_lock);
1048

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1049
1050
  if (pointer_moved) {
    GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1051
        pointer_x, pointer_y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1052
    gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1053
        "mouse-move", 0, e.xbutton.x, e.xbutton.y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1054
1055
  }

1056
  /* We get all events on our window to throw them upstream */
1057
  g_mutex_lock (xvimagesink->flow_lock);
1058
1059
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1060
          xvimagesink->xwindow->win,
1061
1062
          KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
          &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1063
1064
1065
1066
    KeySym keysym;

    /* We lock only for the X function call */
    g_mutex_unlock (xvimagesink->x_lock);
1067
    g_mutex_unlock (xvimagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1068
1069
1070

    switch (e.type) {
      case ButtonPress:
1071
1072
1073
1074
1075
1076
1077
        /* Mouse button pressed over our window. We send upstream
           events for interactivity/navigation */
        GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
            e.xbutton.button, e.xbutton.x, e.xbutton.y);
        gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
            "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1078
      case ButtonRelease:
1079
1080
1081
1082
1083
1084
1085
        /* Mouse button released over our window. We send upstream
           events for interactivity/navigation */
        GST_DEBUG ("xvimagesink button %d released over window at %d,%d",