xvimagesink.c 90.9 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
129
/* Helper functions */
#include <gst/video/video.h>
130
131
132
133

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

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

139
140
141
142
143
144
145
146
147
148
149
150
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)

151
152
static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);

153
154
155
156
157
158
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);

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

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

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

195
static GstVideoSinkClass *parent_class = NULL;
196
197
198
199
200
201
202

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

203
204
205
206
207
208
209
210
211
/* 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
212
gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
213
214
215
{
  GstXvImageSink *xvimagesink;

216
217
  GST_DEBUG_OBJECT (xvimage, "Destroying buffer");

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

  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));

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

228
  /* We might have some buffers destroyed after changing state to NULL */
229
230
231
232
233
234
235
236
237
238
  GST_OBJECT_LOCK (xvimagesink);
  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
239
240
241
    goto beach;
  }

242
243
244
245
246
  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
247
      GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
248
          xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
249
250
      XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
      XSync (xvimagesink->xcontext->disp, FALSE);
251

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
      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
270

271
beach:
272
  GST_OBJECT_UNLOCK (xvimagesink);
273
274
275
  xvimage->xvimagesink = NULL;
  gst_object_unref (xvimagesink);

Wim Taymans's avatar
Wim Taymans committed
276
277
278
279
280
281
282
283
284
285
286
287
288
  return;

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

static void
gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
{
  GstXvImageSink *xvimagesink;
289
  gboolean running;
Wim Taymans's avatar
Wim Taymans committed
290
291

  xvimagesink = xvimage->xvimagesink;
292
  if (G_UNLIKELY (xvimagesink == NULL))
Wim Taymans's avatar
Wim Taymans committed
293
294
    goto no_sink;

295
296
297
298
  GST_OBJECT_LOCK (xvimagesink);
  running = xvimagesink->running;
  GST_OBJECT_UNLOCK (xvimagesink);

Wim Taymans's avatar
Wim Taymans committed
299
  /* If our geometry changed we can't reuse that image. */
300
301
302
303
  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
304
      (xvimage->height != xvimagesink->video_height)) {
305
306
    GST_LOG_OBJECT (xvimage,
        "destroy image as its size changed %dx%d vs current %dx%d",
Wim Taymans's avatar
Wim Taymans committed
307
308
309
310
311
        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. */
312
    GST_LOG_OBJECT (xvimage, "recycling image in pool");
Wim Taymans's avatar
Wim Taymans committed
313
314
315
316
317
318
319
320
321
322
323
324
325
326
    /* 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;
  }
327
328
}

Wim Taymans's avatar
Wim Taymans committed
329
330
331
332
333
334
335
336
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));
}
337
338

static void
339
gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
340
{
341
#ifdef HAVE_XSHM
342
343
  xvimage->SHMInfo.shmaddr = ((void *) -1);
  xvimage->SHMInfo.shmid = -1;
344
#endif
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
}

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,
371
      (GInstanceInitFunc) gst_xvimage_buffer_init,
372
373
374
375
376
377
378
379
      NULL
    };
    _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstXvImageBuffer", &xvimage_buffer_info, 0);
  }
  return _gst_xvimage_buffer_type;
}

380
381
/* X11 stuff */

382
383
384
#ifdef HAVE_XSHM
static gboolean error_caught = FALSE;

Julien Moutte's avatar
Julien Moutte committed
385
static int
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386
gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
Julien Moutte's avatar
Julien Moutte committed
387
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
388
389
  char error_msg[1024];

Julien Moutte's avatar
Julien Moutte committed
390
  XGetErrorText (display, xevent->error_code, error_msg, 1024);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
391
  GST_DEBUG ("xvimagesink failed to use XShm calls. error: %s", error_msg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
392
  error_caught = TRUE;
Julien Moutte's avatar
Julien Moutte committed
393
394
395
396
397
398
  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
399
gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
Julien Moutte's avatar
Julien Moutte committed
400
{
401
402
403
  XvImage *xvimage;
  XShmSegmentInfo SHMInfo;
  gint size;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
404
  int (*handler) (Display *, XErrorEvent *);
Tim Ringenbach's avatar
Tim Ringenbach committed
405
  gboolean result = FALSE;
406
  gboolean did_attach = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407

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

410
411
412
413
414
415
416
  /* 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
417
  /* Setting an error handler to catch failure */
Tim Ringenbach's avatar
Tim Ringenbach committed
418
  error_caught = FALSE;
Julien Moutte's avatar
Julien Moutte committed
419
  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420

Julien Moutte's avatar
Julien Moutte committed
421
  /* Trying to create a 1x1 picture */
422
  GST_DEBUG ("XvShmCreateImage of 1x1");
423
424
  xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
      xcontext->im_format, NULL, 1, 1, &SHMInfo);
425
426
427
428

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

434
435
436
  SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
  if (SHMInfo.shmid == -1) {
    GST_WARNING ("could not get shared memory of %d bytes", size);
437
438
439
    goto beach;
  }

440
441
  SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
  if (SHMInfo.shmaddr == ((void *) -1)) {
442
    GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
443
444
    /* Clean up the shared memory segment */
    shmctl (SHMInfo.shmid, IPC_RMID, 0);
445
446
447
    goto beach;
  }

448
449
450
451
452
  /* 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);

453
454
  xvimage->data = SHMInfo.shmaddr;
  SHMInfo.readOnly = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
455

456
  if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
457
458
459
    GST_WARNING ("Failed to XShmAttach");
    goto beach;
  }
Julien Moutte's avatar
Julien Moutte committed
460

461
462
463
  /* Sync to ensure we see any errors we caused */
  XSync (xcontext->disp, FALSE);

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

467
468
469
470
471
  if (!error_caught) {
    did_attach = TRUE;
    /* store whether we succeeded in result */
    result = TRUE;
  }
472
473

beach:
474
475
476
477
  /* 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
478
  XSetErrorHandler (handler);
479

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

494
/* This function handles GstXvImage creation depending on XShm availability */
495
static GstXvImageBuffer *
496
gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
497
{
498
  GstXvImageBuffer *xvimage = NULL;
499
  GstStructure *structure = NULL;
500
  gboolean succeeded = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
501

502
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
503

504
  xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
505
  GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506

507
508
509
510
511
512
513
514
515
516
517
  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);
518
  if (xvimage->im_format == -1) {
519
520
    GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
        GST_PTR_FORMAT, caps);
521
522
523
    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
        ("Failed to create output image buffer of %dx%d pixels",
            xvimage->width, xvimage->height), ("Invalid input caps"));
524
    goto beach_unlocked;
525
  }
526
  xvimage->xvimagesink = gst_object_ref (xvimagesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
527

528
529
530
  g_mutex_lock (xvimagesink->x_lock);

#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
531
532
  if (xvimagesink->xcontext->use_xshm) {
    xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
533
        xvimagesink->xcontext->xv_port_id,
534
535
        xvimage->im_format, NULL,
        xvimage->width, xvimage->height, &xvimage->SHMInfo);
536
    if (!xvimage->xvimage) {
537
      g_mutex_unlock (xvimagesink->x_lock);
538
539
540
541
542
      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));
543
      goto beach_unlocked;
544
545
546
    }

    /* we have to use the returned data_size for our shm size */
547
    xvimage->size = xvimage->xvimage->data_size;
548
549
    GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
        xvimage->size);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550
551

    xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
552
        IPC_CREAT | 0777);
553
    if (xvimage->SHMInfo.shmid == -1) {
554
      g_mutex_unlock (xvimagesink->x_lock);
555
556
557
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
558
559
          ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
              xvimage->size));
560
      goto beach_unlocked;
561
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
562
563

    xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
564
    if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
565
      g_mutex_unlock (xvimagesink->x_lock);
566
567
568
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
569
          ("Failed to shmat: %s", g_strerror (errno)));
570
571
      /* Clean up the shared memory segment */
      shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
572
      goto beach_unlocked;
573
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
574

575
576
577
578
579
    /* 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);

580
    xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
581
582
    xvimage->SHMInfo.readOnly = FALSE;

583
    if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
584
      g_mutex_unlock (xvimagesink->x_lock);
585
586
587
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height), ("Failed to XShmAttach"));
588
      goto beach_unlocked;
589
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
590
591

    XSync (xvimagesink->xcontext->disp, FALSE);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
592
    GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
593
        xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
594
  } else
Julien Moutte's avatar
Julien Moutte committed
595
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
596
597
  {
    xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
598
        xvimagesink->xcontext->xv_port_id,
599
        xvimage->im_format, NULL, xvimage->width, xvimage->height);
600
    if (!xvimage->xvimage) {
601
      g_mutex_unlock (xvimagesink->x_lock);
602
603
604
605
606
      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));
607
      goto beach_unlocked;
608
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
609

610
    /* we have to use the returned data_size for our image size */
611
612
    xvimage->size = xvimage->xvimage->data_size;
    xvimage->xvimage->data = g_malloc (xvimage->size);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
613
614
615

    XSync (xvimagesink->xcontext->disp, FALSE);
  }
616
  succeeded = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
617

618
619
620
  GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
  GST_BUFFER_SIZE (xvimage) = xvimage->size;

621
622
  g_mutex_unlock (xvimagesink->x_lock);

623
beach_unlocked:
624
  if (!succeeded) {
Wim Taymans's avatar
Wim Taymans committed
625
    gst_xvimage_buffer_free (xvimage);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
626
627
    xvimage = NULL;
  }
628

629
630
631
  return xvimage;
}

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
/* 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);
  }
}

668
669
/* This function puts a GstXvImage on a GstXvImageSink's window */
static void
670
671
gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
    GstXvImageBuffer * xvimage)
672
{
673
  GstVideoRectangle src, dst, result;
674
  gboolean draw_border = FALSE;
675

676
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
677
  g_return_if_fail (xvimagesink->xwindow != NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
678

679
  /* We take the flow_lock. If expose is in there we don't want to run
680
681
682
     concurrently from the data flow thread */
  g_mutex_lock (xvimagesink->flow_lock);

683
684
685
686
687
688
  /* Draw borders when displaying the first frame. After this
     draw borders only on expose event. */
  if (!xvimagesink->cur_image) {
    draw_border = TRUE;
  }

689
  /* Store a reference to the last image we put, lose the previous one */
690
  if (xvimage && xvimagesink->cur_image != xvimage) {
691
    if (xvimagesink->cur_image) {
692
      GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
693
694
      gst_buffer_unref (xvimagesink->cur_image);
    }
695
    GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
696
697
    xvimagesink->cur_image =
        GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
698
699
  }

700
701
  /* Expose sends a NULL image, we take the latest frame */
  if (!xvimage) {
702
    draw_border = TRUE;
703
704
705
706
707
708
709
710
    if (xvimagesink->cur_image) {
      xvimage = xvimagesink->cur_image;
    } else {
      g_mutex_unlock (xvimagesink->flow_lock);
      return;
    }
  }

711
712
  gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);

713
714
715
716
  /* 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);
717
718
719
720
721
722
723
724
725
726
  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
727

728
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
729

730
731
732
733
  if (draw_border) {
    gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
        result);
  }
734

735
  /* We scale to the window's geometry */
736
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
737
  if (xvimagesink->xcontext->use_xshm) {
738
    GST_LOG_OBJECT (xvimagesink,
739
740
        "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
        GST_PTR_FORMAT,
741
        xvimage->width, xvimage->height,
742
743
        xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
744
    XvShmPutImage (xvimagesink->xcontext->disp,
745
746
747
748
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
749
        result.x, result.y, result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
750
  } else
Julien Moutte's avatar
Julien Moutte committed
751
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
752
753
  {
    XvPutImage (xvimagesink->xcontext->disp,
754
755
756
757
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
758
        result.x, result.y, result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
759
760
761
762
  }

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

763
  g_mutex_unlock (xvimagesink->x_lock);
764
765

  g_mutex_unlock (xvimagesink->flow_lock);
766
767
}

768
769
770
771
772
773
774
775
776
777
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);

778
779
  g_mutex_lock (xvimagesink->x_lock);

780
  hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
781
  if (hints_atom == None) {
782
    g_mutex_unlock (xvimagesink->x_lock);
783
784
785
    return FALSE;
  }

786
787
  hints = g_malloc0 (sizeof (MotifWmHints));

788
789
790
791
792
793
794
795
796
  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);

797
798
  g_mutex_unlock (xvimagesink->x_lock);

799
800
801
802
803
  g_free (hints);

  return TRUE;
}

804
805
/* This function handles a GstXWindow creation
 * The width and height are the actual pixel size on the display */
806
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
807
808
gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
    gint width, gint height)
809
810
811
{
  GstXWindow *xwindow = NULL;
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
812

813
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
814

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

817
818
  xwindow->width = width;
  xwindow->height = height;
Julien Moutte's avatar
Julien Moutte committed
819
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
820

821
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
822

823
  xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
824
825
826
827
      xvimagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height,
      0, 0, xvimagesink->xcontext->black);

828
829
830
831
  /* 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);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
832
833
834
835
  XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
      StructureNotifyMask | PointerMotionMask | KeyPressMask |
      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);

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

839
  XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
840
841
842

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

843
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
844

845
846
  gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);

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

849
850
851
852
853
  return xwindow;
}

/* This function destroys a GstXWindow */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
854
855
gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
856
857
858
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
859

860
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
861

Julien Moutte's avatar
Julien Moutte committed
862
863
864
  /* 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);
865
866
  else
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
867

868
  XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
869
870
871

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

872
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
873

874
875
876
  g_free (xwindow);
}

877
static void
878
879
gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
880
{
881
882
  XWindowAttributes attr;

883
884
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
885

886
  /* Update the window geometry */
887
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
888

889
890
  XGetWindowAttributes (xvimagesink->xcontext->disp,
      xvimagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
891

892
893
  xvimagesink->xwindow->width = attr.width;
  xvimagesink->xwindow->height = attr.height;
894
895
896
897

  g_mutex_unlock (xvimagesink->x_lock);
}

898
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
899
900
gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
901
902
903
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
904

905
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
906

907
908
909
  XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
      xwindow->win);

910
  XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
911
912
      xvimagesink->xcontext->black);

913
  XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
914
915
      0, 0, xwindow->width, xwindow->height);

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

918
919
920
  g_mutex_unlock (xvimagesink->x_lock);
}

921
922
923
/* 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
924
gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
925
926
{
  GList *channels = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
927

928
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
929

930
931
932
  /* 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
933

934
935
936
937
  /* 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
938
939
940
941
942
943
944
945
946
947
948
949
950
951

  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) {
952
        value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
953
      } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
954
955
        value = (xvimagesink->saturation + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
956
      } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
957
958
        value = (xvimagesink->contrast + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
959
      } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
960
961
        value = (xvimagesink->brightness + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
962
      } else {
963
964
965
        g_warning ("got an unknown channel %s", channel->label);
        g_object_unref (channel);
        return;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
966
967
968
969
970
      }

      /* Committing to Xv port */
      g_mutex_lock (xvimagesink->x_lock);
      XvSetPortAttribute (xvimagesink->xcontext->disp,
971
972
          xvimagesink->xcontext->xv_port_id,
          XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
973
974
975
      g_mutex_unlock (xvimagesink->x_lock);

      g_object_unref (channel);
976
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
977
978
    channels = g_list_next (channels);
  }
979
980
}

981
982
983
984
985
/* 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
986
gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
987
988
{
  XEvent e;
989
990
  guint pointer_x = 0, pointer_y = 0;
  gboolean pointer_moved = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
991

992
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
993

994
995
996
997
  /* We get all pointer motion events, only the last position is
     interesting. */
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
998
          xvimagesink->xwindow->win, PointerMotionMask, &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
999
1000
1001
1002
    g_mutex_unlock (xvimagesink->x_lock);

    switch (e.type) {
      case MotionNotify:
1003
1004
1005
1006
        pointer_x = e.xmotion.x;
        pointer_y = e.xmotion.y;
        pointer_moved = TRUE;
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1007
      default:
1008
        break;
1009
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1010
1011
1012

    g_mutex_lock (xvimagesink->x_lock);
  }
1013
1014
  g_mutex_unlock (xvimagesink->x_lock);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1015
1016
  if (pointer_moved) {
    GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1017
        pointer_x, pointer_y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1018
    gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1019
        "mouse-move", 0, e.xbutton.x, e.xbutton.y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1020
1021
  }

1022
1023
1024
  /* We get all events on our window to throw them upstream */
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1025
          xvimagesink->xwindow->win,
1026
1027
          KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
          &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1028
1029
1030
1031
1032
1033
1034
    KeySym keysym;

    /* We lock only for the X function call */
    g_mutex_unlock (xvimagesink->x_lock);

    switch (e.type) {
      case ButtonPress:
1035
1036
1037
1038
1039
1040
1041
        /* 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
1042
      case ButtonRelease:
1043
1044
1045
1046
1047
1048
1049
        /* Mouse button released over our window. We send upstream
           events for interactivity/navigation */
        GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
            e.xbutton.button, e.xbutton.x, e.xbutton.y);
        gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
            "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1050
1051
      case KeyPress:
      case KeyRelease:
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
        /* Key pressed/released over our window. We send upstream
           events for interactivity/navigation */
        GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
            e.xkey.keycode, e.xkey.x, e.xkey.y);
        keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
            e.xkey.keycode, 0);
        if (keysym != NoSymbol) {
          gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
              e.type == KeyPress ?
              "key-press" : "key-release", XKeysymToString (keysym));
        } else {
          gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
              e.type == KeyPress ? "key-press" : "key-release", "unknown");
        }
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1067
      default:
1068
        GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1069
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1070
1071
1072

    g_mutex_lock (xvimagesink->x_lock);
  }
1073
  g_mutex_unlock (xvimagesink->x_lock);
1074
1075
1076
1077
1078