xvimagesink.c 89.3 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
//static void gst_xvimagesink_send_pending_navigation (GstXvImageSink * xvimagesink);
160

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

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

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

197
static GstVideoSinkClass *parent_class = NULL;
198
199
200
201
202
203
204

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

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

218
219
  GST_DEBUG_OBJECT (xvimage, "Destroying buffer");

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

  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;

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

244
245
246
247
248
  g_mutex_lock (xvimagesink->x_lock);

#ifdef HAVE_XSHM
  if (xvimagesink->xcontext->use_xshm) {
    if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
249
250
      GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%x\n",
          xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
251
252
      XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
      XSync (xvimagesink->xcontext->disp, FALSE);
253

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

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

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

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

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

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

297
298
299
300
  GST_OBJECT_LOCK (xvimagesink);
  running = xvimagesink->running;
  GST_OBJECT_UNLOCK (xvimagesink);

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

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

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

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

382
383
/* X11 stuff */

384
385
386
#ifdef HAVE_XSHM
static gboolean error_caught = FALSE;

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

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

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

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

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

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

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

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

450
451
452
453
454
  /* 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);

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

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

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

466
467
468
  GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%x\n", SHMInfo.shmid,
      SHMInfo.shmseg);

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

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

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

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

504
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
505

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

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

530
531
532
  g_mutex_lock (xvimagesink->x_lock);

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

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

    xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
553
        IPC_CREAT | 0777);
554
    if (xvimage->SHMInfo.shmid == -1) {
555
      g_mutex_unlock (xvimagesink->x_lock);
556
557
558
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
559
          ("could not get shared memory of %d 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);
592
593
    GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%x\n",
        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
674
  GstVideoRectangle src, dst, result;

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

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

682
  /* Store a reference to the last image we put, lose the previous one */
683
  if (xvimage && xvimagesink->cur_image != xvimage) {
684
    if (xvimagesink->cur_image) {
685
      GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
686
687
      gst_buffer_unref (xvimagesink->cur_image);
    }
688
    GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
689
690
    xvimagesink->cur_image =
        GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
691
692
  }

693
694
695
696
697
698
699
700
701
702
  /* Expose sends a NULL image, we take the latest frame */
  if (!xvimage) {
    if (xvimagesink->cur_image) {
      xvimage = xvimagesink->cur_image;
    } else {
      g_mutex_unlock (xvimagesink->flow_lock);
      return;
    }
  }

703
704
  gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);

705
706
707
708
  /* 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);
709
710
711
712
713
714
715
716
717
718
  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
719

720
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
721

722
723
724
  gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
      result);

725
  /* We scale to the window's geometry */
726
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
727
  if (xvimagesink->xcontext->use_xshm) {
728
    GST_LOG_OBJECT (xvimagesink,
729
730
        "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
        GST_PTR_FORMAT,
731
        xvimage->width, xvimage->height,
732
733
        xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
734
    XvShmPutImage (xvimagesink->xcontext->disp,
735
736
737
738
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
739
        result.x, result.y, result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
740
  } else
Julien Moutte's avatar
Julien Moutte committed
741
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
742
743
  {
    XvPutImage (xvimagesink->xcontext->disp,
744
745
746
747
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
748
        result.x, result.y, result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
749
750
751
752
  }

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

753
  g_mutex_unlock (xvimagesink->x_lock);
754
755

  g_mutex_unlock (xvimagesink->flow_lock);
756
757
}

758
759
760
761
762
763
764
765
766
767
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);

768
769
  g_mutex_lock (xvimagesink->x_lock);

770
  hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
771
  if (hints_atom == None) {
772
    g_mutex_unlock (xvimagesink->x_lock);
773
774
775
    return FALSE;
  }

776
777
  hints = g_malloc0 (sizeof (MotifWmHints));

778
779
780
781
782
783
784
785
786
  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);

787
788
  g_mutex_unlock (xvimagesink->x_lock);

789
790
791
792
793
  g_free (hints);

  return TRUE;
}

794
795
/* This function handles a GstXWindow creation
 * The width and height are the actual pixel size on the display */
796
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
797
798
gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
    gint width, gint height)
799
800
801
{
  GstXWindow *xwindow = NULL;
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
802

803
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
804

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

807
808
  xwindow->width = width;
  xwindow->height = height;
Julien Moutte's avatar
Julien Moutte committed
809
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
810

811
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
812

813
  xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
814
815
816
817
      xvimagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height,
      0, 0, xvimagesink->xcontext->black);

818
819
820
821
  /* 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
822
823
824
825
  XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
      StructureNotifyMask | PointerMotionMask | KeyPressMask |
      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);

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

829
  XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
830
831
832

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

833
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
834

835
836
  gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);

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

839
840
841
842
843
  return xwindow;
}

/* This function destroys a GstXWindow */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
844
845
gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
846
847
848
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
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

Julien Moutte's avatar
Julien Moutte committed
852
853
854
  /* 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);
855
856
  else
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
857

858
  XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
859
860
861

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

862
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
863

864
865
866
  g_free (xwindow);
}

867
static void
868
869
gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
870
{
871
872
  XWindowAttributes attr;

873
874
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
875

876
  /* Update the window geometry */
877
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
878

879
880
  XGetWindowAttributes (xvimagesink->xcontext->disp,
      xvimagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
881

882
883
  xvimagesink->xwindow->width = attr.width;
  xvimagesink->xwindow->height = attr.height;
884
885
886
887

  g_mutex_unlock (xvimagesink->x_lock);
}

888
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
889
890
gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
891
892
893
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
894

895
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
896

897
898
899
  XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
      xwindow->win);

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

903
  XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
904
905
      0, 0, xwindow->width, xwindow->height);

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

908
909
910
  g_mutex_unlock (xvimagesink->x_lock);
}

911
912
913
/* 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
914
gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
915
916
{
  GList *channels = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
917

918
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
919

920
921
922
  /* 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
923

924
925
926
927
  /* 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
928
929
930
931
932
933
934
935
936
937
938
939
940
941

  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) {
942
        value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
943
      } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
944
945
        value = (xvimagesink->saturation + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
946
      } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
947
948
        value = (xvimagesink->contrast + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
949
      } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
950
951
        value = (xvimagesink->brightness + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
952
      } else {
953
954
955
        g_warning ("got an unknown channel %s", channel->label);
        g_object_unref (channel);
        return;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
956
957
958
959
960
      }

      /* Committing to Xv port */
      g_mutex_lock (xvimagesink->x_lock);
      XvSetPortAttribute (xvimagesink->xcontext->disp,
961
962
          xvimagesink->xcontext->xv_port_id,
          XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
963
964
965
      g_mutex_unlock (xvimagesink->x_lock);

      g_object_unref (channel);
966
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
967
968
    channels = g_list_next (channels);
  }
969
970
}

971
972
973
974
975
/* 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
976
gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
977
978
{
  XEvent e;
979
980
  guint pointer_x = 0, pointer_y = 0;
  gboolean pointer_moved = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
981

982
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
983

984
985
986
987
  /* We get all pointer motion events, only the last position is
     interesting. */
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
988
          xvimagesink->xwindow->win, PointerMotionMask, &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
989
990
991
992
    g_mutex_unlock (xvimagesink->x_lock);

    switch (e.type) {
      case MotionNotify:
993
994
995
996
        pointer_x = e.xmotion.x;
        pointer_y = e.xmotion.y;
        pointer_moved = TRUE;
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
997
      default:
998
        break;
999
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1000
1001
1002

    g_mutex_lock (xvimagesink->x_lock);
  }
1003
1004
  g_mutex_unlock (xvimagesink->x_lock);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1005
1006
  if (pointer_moved) {
    GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1007
        pointer_x, pointer_y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1008
    gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1009
        "mouse-move", 0, e.xbutton.x, e.xbutton.y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1010
1011
  }

1012
1013
1014
  /* We get all events on our window to throw them upstream */
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1015
          xvimagesink->xwindow->win,
1016
1017
          KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
          &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1018
1019
1020
1021
1022
1023
1024
    KeySym keysym;

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

    switch (e.type) {
      case ButtonPress:
1025
1026
1027
1028
1029
1030
1031
        /* 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
1032
      case ButtonRelease:
1033
1034
1035
1036
1037
1038
1039
        /* 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
1040
1041
      case KeyPress:
      case KeyRelease:
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
        /* 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
1057
      default:
1058
        GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1059
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1060
1061
1062

    g_mutex_lock (xvimagesink->x_lock);
  }
1063
  g_mutex_unlock (xvimagesink->x_lock);
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092

  /* Handle Expose */
  {
    gboolean exposed = FALSE, configured = FALSE;

    g_mutex_lock (xvimagesink->x_lock);
    while (XCheckWindowEvent (xvimagesink->xcontext->disp,
            xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask,
            &e)) {
      g_mutex_unlock (xvimagesink->x_lock);

      switch (e.type) {
        case Expose:
          exposed = TRUE;
          break;
        case ConfigureNotify:
          configured = TRUE;
        default:
          break;
      }

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

    if (exposed || configured) {
      gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
    }
  }
1093
1094
}

1095
1096
/* This function generates a caps with all supported format by the first
   Xv grabable port we find. We store each one of the supported formats in a
1097
   format list and append the format to a newly created caps that we return
1098
1099
   If this function does not return NULL because of an error, it also grabs
   the port via XvGrabPort */
1100
static GstCaps *
1101
1102
gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
    GstXContext * xcontext)
1103
{
1104
1105
  gint i;
  guint nb_adaptors;
1106
  XvAdaptorInfo *adaptors;
1107
1108
  gint nb_formats;
  XvImageFormatValues *formats = NULL;
1109
1110
1111
  guint nb_encodings;
  XvEncodingInfo *encodings = NULL;
  gulong max_w = G_MAXINT, max_h = G_MAXINT;
1112
  GstCaps *caps = NULL;
1113
  GstCaps *rgb_caps = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1114

1115
  g_return_val_if_fail (xcontext != NULL, NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1116

1117
  /* First let's check that XVideo extension is available */