xvimagesink.c 83 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
218
{
  GstXvImageSink *xvimagesink;

  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
229
230
231
232
  /* We might have some buffers destroyed after changing state to NULL */
  if (xvimagesink->xcontext) {
    goto beach;
  }

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  g_mutex_lock (xvimagesink->x_lock);

#ifdef HAVE_XSHM
  if (xvimagesink->xcontext->use_xshm) {
    if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
      XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
      XSync (xvimagesink->xcontext->disp, FALSE);
      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
258

259
beach:
260
261
262
  xvimage->xvimagesink = NULL;
  gst_object_unref (xvimagesink);

Wim Taymans's avatar
Wim Taymans committed
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  return;

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

static void
gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
{
  GstXvImageSink *xvimagesink;

  xvimagesink = xvimage->xvimagesink;
  if (xvimagesink == NULL)
    goto no_sink;

  /* If our geometry changed we can't reuse that image. */
  if ((xvimage->width != xvimagesink->video_width) ||
      (xvimage->height != xvimagesink->video_height)) {
284
285
    GST_LOG_OBJECT (xvimage,
        "destroy image as its size changed %dx%d vs current %dx%d",
Wim Taymans's avatar
Wim Taymans committed
286
287
288
289
290
        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. */
291
    GST_LOG_OBJECT (xvimage, "recycling image in pool");
Wim Taymans's avatar
Wim Taymans committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
    /* 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;
  }
306
307
}

Wim Taymans's avatar
Wim Taymans committed
308
309
310
311
312
313
314
315
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));
}
316
317

static void
318
gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
319
{
320
#ifdef HAVE_XSHM
321
322
  xvimage->SHMInfo.shmaddr = ((void *) -1);
  xvimage->SHMInfo.shmid = -1;
323
#endif
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
}

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,
350
      (GInstanceInitFunc) gst_xvimage_buffer_init,
351
352
353
354
355
356
357
358
      NULL
    };
    _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstXvImageBuffer", &xvimage_buffer_info, 0);
  }
  return _gst_xvimage_buffer_type;
}

359
360
/* X11 stuff */

361
362
363
#ifdef HAVE_XSHM
static gboolean error_caught = FALSE;

Julien Moutte's avatar
Julien Moutte committed
364
static int
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
365
gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
Julien Moutte's avatar
Julien Moutte committed
366
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367
368
  char error_msg[1024];

Julien Moutte's avatar
Julien Moutte committed
369
  XGetErrorText (display, xevent->error_code, error_msg, 1024);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
370
  GST_DEBUG ("xvimagesink failed to use XShm calls. error: %s", error_msg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
371
  error_caught = TRUE;
Julien Moutte's avatar
Julien Moutte committed
372
373
374
375
376
377
  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
378
gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
Julien Moutte's avatar
Julien Moutte committed
379
{
380
381
382
  XvImage *xvimage;
  XShmSegmentInfo SHMInfo;
  gint size;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
383
  int (*handler) (Display *, XErrorEvent *);
Tim Ringenbach's avatar
Tim Ringenbach committed
384
  gboolean result = FALSE;
385
  gboolean did_attach = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386

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

389
390
391
392
393
394
395
  /* 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
396
  /* Setting an error handler to catch failure */
Tim Ringenbach's avatar
Tim Ringenbach committed
397
  error_caught = FALSE;
Julien Moutte's avatar
Julien Moutte committed
398
  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
399

Julien Moutte's avatar
Julien Moutte committed
400
  /* Trying to create a 1x1 picture */
401
  GST_DEBUG ("XvShmCreateImage of 1x1");
402
403
  xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
      xcontext->im_format, NULL, 1, 1, &SHMInfo);
404
405
406
407

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

413
414
415
  SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
  if (SHMInfo.shmid == -1) {
    GST_WARNING ("could not get shared memory of %d bytes", size);
416
417
418
    goto beach;
  }

419
420
  SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
  if (SHMInfo.shmaddr == ((void *) -1)) {
421
    GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
422
423
    /* Clean up the shared memory segment */
    shmctl (SHMInfo.shmid, IPC_RMID, 0);
424
425
426
    goto beach;
  }

427
428
429
430
431
  /* 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);

432
433
  xvimage->data = SHMInfo.shmaddr;
  SHMInfo.readOnly = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
434

435
  if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
436
437
438
    GST_WARNING ("Failed to XShmAttach");
    goto beach;
  }
Julien Moutte's avatar
Julien Moutte committed
439

440
441
442
443
444
445
446
447
  /* Sync to ensure we see any errors we caused */
  XSync (xcontext->disp, FALSE);

  if (!error_caught) {
    did_attach = TRUE;
    /* store whether we succeeded in result */
    result = TRUE;
  }
448
449

beach:
450
451
452
453
  /* 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
454
  XSetErrorHandler (handler);
455

456
  if (did_attach) {
457
458
459
    XShmDetach (xcontext->disp, &SHMInfo);
    XSync (xcontext->disp, FALSE);
  }
460
461
  if (SHMInfo.shmaddr != ((void *) -1))
    shmdt (SHMInfo.shmaddr);
462
463
  if (xvimage)
    XFree (xvimage);
Tim Ringenbach's avatar
Tim Ringenbach committed
464
  return result;
Julien Moutte's avatar
Julien Moutte committed
465
}
466
#endif /* HAVE_XSHM */
Julien Moutte's avatar
Julien Moutte committed
467

468
/* This function handles GstXvImage creation depending on XShm availability */
469
static GstXvImageBuffer *
470
gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
471
{
472
  GstXvImageBuffer *xvimage = NULL;
473
  GstStructure *structure = NULL;
474
  gboolean succeeded = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
475

476
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
477

478
  xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
479

480
481
482
483
484
485
486
487
488
489
490
491
492
493
  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);
  if (!xvimage->im_format) {
    GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
        GST_PTR_FORMAT, caps);
494
    goto beach_unlocked;
495
  }
496
  xvimage->xvimagesink = gst_object_ref (xvimagesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
497

498
499
500
  g_mutex_lock (xvimagesink->x_lock);

#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
501
502
  if (xvimagesink->xcontext->use_xshm) {
    xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
503
        xvimagesink->xcontext->xv_port_id,
504
505
        xvimage->im_format, NULL,
        xvimage->width, xvimage->height, &xvimage->SHMInfo);
506
507
508
509
510
511
512
    if (!xvimage->xvimage) {
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
          ("could not XvShmCreateImage a %dx%d image"));
      goto beach;
    }

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

    xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
517
        IPC_CREAT | 0777);
518
519
520
521
522
    if (xvimage->SHMInfo.shmid == -1) {
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
          ("could not get shared memory of %d bytes", xvimage->size));
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
523
524

    xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
525
    if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
526
527
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
          ("Failed to shmat: %s", g_strerror (errno)));
528
529
      /* Clean up the shared memory segment */
      shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
530
531
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
532

533
534
535
536
537
    /* 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);

538
    xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
539
540
    xvimage->SHMInfo.readOnly = FALSE;

541
542
543
544
545
    if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
          ("Failed to XShmAttach"));
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
546
547
548

    XSync (xvimagesink->xcontext->disp, FALSE);
  } else
Julien Moutte's avatar
Julien Moutte committed
549
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
550
551
  {
    xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
552
        xvimagesink->xcontext->xv_port_id,
553
        xvimage->im_format, NULL, xvimage->width, xvimage->height);
554
555
556
557
558
    if (!xvimage->xvimage) {
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE, (NULL),
          ("could not XvCreateImage a %dx%d image"));
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
559

560
    /* we have to use the returned data_size for our image size */
561
562
    xvimage->size = xvimage->xvimage->data_size;
    xvimage->xvimage->data = g_malloc (xvimage->size);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
563
564
565

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

568
569
570
  GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
  GST_BUFFER_SIZE (xvimage) = xvimage->size;

571
beach:
572
573
  g_mutex_unlock (xvimagesink->x_lock);

574
beach_unlocked:
575
  if (!succeeded) {
Wim Taymans's avatar
Wim Taymans committed
576
    gst_xvimage_buffer_free (xvimage);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
577
578
    xvimage = NULL;
  }
579

580
581
582
  return xvimage;
}

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
/* 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);
  }
}

619
620
/* This function puts a GstXvImage on a GstXvImageSink's window */
static void
621
622
gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
    GstXvImageBuffer * xvimage)
623
{
624
625
  GstVideoRectangle src, dst, result;

626
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
627
  g_return_if_fail (xvimagesink->xwindow != NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
628

629
  /* We take the flow_lock. If expose is in there we don't want to run
630
631
632
     concurrently from the data flow thread */
  g_mutex_lock (xvimagesink->flow_lock);

633
  /* Store a reference to the last image we put, lose the previous one */
634
  if (xvimage && xvimagesink->cur_image != xvimage) {
635
    if (xvimagesink->cur_image) {
636
      GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
637
638
      gst_buffer_unref (xvimagesink->cur_image);
    }
639
    GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
640
641
    xvimagesink->cur_image =
        GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
642
643
  }

644
645
646
647
648
649
650
651
652
653
  /* 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;
    }
  }

654
655
  gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);

656
657
658
659
  /* 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);
660
661
662
663
664
665
666
667
668
669
  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
670

671
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
672

673
674
675
  gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
      result);

676
  /* We scale to the window's geometry */
677
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
678
  if (xvimagesink->xcontext->use_xshm) {
679
680
681
682
    GST_LOG_OBJECT (xvimagesink,
        "XvShmPutImage with image %dx%d and window %dx%d",
        xvimage->width, xvimage->height,
        xvimagesink->xwindow->width, xvimagesink->xwindow->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
683
    XvShmPutImage (xvimagesink->xcontext->disp,
684
685
686
687
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
688
        result.x, result.y, result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
689
  } else
Julien Moutte's avatar
Julien Moutte committed
690
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
691
692
  {
    XvPutImage (xvimagesink->xcontext->disp,
693
694
695
696
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
697
        result.x, result.y, result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
698
699
700
701
  }

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

702
  g_mutex_unlock (xvimagesink->x_lock);
703
704

  g_mutex_unlock (xvimagesink->flow_lock);
705
706
}

707
708
709
710
711
712
713
714
715
716
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);

717
718
  g_mutex_lock (xvimagesink->x_lock);

719
  hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
720
  if (hints_atom == None) {
721
    g_mutex_unlock (xvimagesink->x_lock);
722
723
724
    return FALSE;
  }

725
726
  hints = g_malloc0 (sizeof (MotifWmHints));

727
728
729
730
731
732
733
734
735
  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);

736
737
  g_mutex_unlock (xvimagesink->x_lock);

738
739
740
741
742
  g_free (hints);

  return TRUE;
}

743
744
/* This function handles a GstXWindow creation
 * The width and height are the actual pixel size on the display */
745
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
746
747
gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
    gint width, gint height)
748
749
750
{
  GstXWindow *xwindow = NULL;
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
751

752
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
753

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

756
757
  xwindow->width = width;
  xwindow->height = height;
Julien Moutte's avatar
Julien Moutte committed
758
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
759

760
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
761

762
  xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
763
764
765
766
      xvimagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height,
      0, 0, xvimagesink->xcontext->black);

767
768
769
770
  /* 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
771
772
773
774
  XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
      StructureNotifyMask | PointerMotionMask | KeyPressMask |
      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);

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

778
  XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
779
780
781

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

782
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
783

784
785
  gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);

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

788
789
790
791
792
  return xwindow;
}

/* This function destroys a GstXWindow */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
793
794
gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
795
796
797
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
798

799
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
800

Julien Moutte's avatar
Julien Moutte committed
801
802
803
  /* 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);
804
805
  else
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
806

807
  XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
808
809
810

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

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

813
814
815
  g_free (xwindow);
}

816
static void
817
818
gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
819
{
820
821
  XWindowAttributes attr;

822
823
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
824

825
  /* Update the window geometry */
826
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
827

828
829
  XGetWindowAttributes (xvimagesink->xcontext->disp,
      xvimagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
830

831
832
  xvimagesink->xwindow->width = attr.width;
  xvimagesink->xwindow->height = attr.height;
833
834
835
836

  g_mutex_unlock (xvimagesink->x_lock);
}

837
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
838
839
gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
840
841
842
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
843

844
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
845

846
847
848
  XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
      xwindow->win);

849
  XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
850
851
      xvimagesink->xcontext->black);

852
  XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
853
854
      0, 0, xwindow->width, xwindow->height);

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

857
858
859
  g_mutex_unlock (xvimagesink->x_lock);
}

860
861
862
/* 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
863
gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
864
865
{
  GList *channels = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
866

867
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
868

869
870
871
  /* 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
872

873
874
875
876
  /* 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
877
878
879
880
881
882
883
884
885
886
887
888
889
890

  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) {
891
        value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
892
      } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
893
894
        value = (xvimagesink->saturation + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
895
      } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
896
897
        value = (xvimagesink->contrast + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
898
      } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
899
900
        value = (xvimagesink->brightness + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
901
      } else {
902
903
904
        g_warning ("got an unknown channel %s", channel->label);
        g_object_unref (channel);
        return;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
905
906
907
908
909
      }

      /* Committing to Xv port */
      g_mutex_lock (xvimagesink->x_lock);
      XvSetPortAttribute (xvimagesink->xcontext->disp,
910
911
          xvimagesink->xcontext->xv_port_id,
          XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
912
913
914
      g_mutex_unlock (xvimagesink->x_lock);

      g_object_unref (channel);
915
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
916
917
    channels = g_list_next (channels);
  }
918
919
}

920
921
922
923
924
/* 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
925
gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
926
927
{
  XEvent e;
928
929
  guint pointer_x = 0, pointer_y = 0;
  gboolean pointer_moved = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
930

931
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
932

933
934
935
936
  /* We get all pointer motion events, only the last position is
     interesting. */
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
937
          xvimagesink->xwindow->win, PointerMotionMask, &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
938
939
940
941
    g_mutex_unlock (xvimagesink->x_lock);

    switch (e.type) {
      case MotionNotify:
942
943
944
945
        pointer_x = e.xmotion.x;
        pointer_y = e.xmotion.y;
        pointer_moved = TRUE;
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
946
      default:
947
        break;
948
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
949
950
951

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
954
955
  if (pointer_moved) {
    GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
956
        pointer_x, pointer_y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
957
    gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
958
        "mouse-move", 0, e.xbutton.x, e.xbutton.y);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
959
960
  }

961
962
963
  /* We get all events on our window to throw them upstream */
  g_mutex_lock (xvimagesink->x_lock);
  while (XCheckWindowEvent (xvimagesink->xcontext->disp,
964
          xvimagesink->xwindow->win,
965
966
          KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
          &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
967
968
969
970
971
972
973
    KeySym keysym;

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

    switch (e.type) {
      case ButtonPress:
974
975
976
977
978
979
980
        /* 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
981
      case ButtonRelease:
982
983
984
985
986
987
988
        /* 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
989
990
      case KeyPress:
      case KeyRelease:
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
        /* 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
1006
      default:
1007
        GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1008
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1009
1010
1011

    g_mutex_lock (xvimagesink->x_lock);
  }
1012
  g_mutex_unlock (xvimagesink->x_lock);
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041

  /* 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));
    }
  }
1042
1043
}

1044
1045
/* 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
1046
   format list and append the format to a newly created caps that we return
1047
1048
   If this function does not return NULL because of an error, it also grabs
   the port via XvGrabPort */
1049
static GstCaps *
1050
1051
gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
    GstXContext * xcontext)
1052
{
1053
1054
  gint i;
  guint nb_adaptors;
1055
  XvAdaptorInfo *adaptors;
1056
1057
1058
  gint nb_formats;
  XvImageFormatValues *formats = NULL;
  GstCaps *caps = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1059

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

1062
  /* First let's check that XVideo extension is available */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1063
  if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1064
    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL),
1065
        ("XVideo extension is not available"));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1066
1067
1068
    return NULL;
  }

1069
1070
  /* Then we get adaptors list */
  if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1071
          &nb_adaptors, &adaptors)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1072
    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS, (NULL),
1073
        ("Failed getting XV adaptors list"));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1074
1075
1076
    return NULL;
  }

1077
  xcontext->xv_port_id = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1078

1079
  GST_DEBUG ("Found %u XV adaptor(s)", nb_adaptors);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1080

1081
  /* Now search for an adaptor that supports XvImageMask */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1082
1083
1084
1085
1086
1087
  for (i = 0; i < nb_adaptors && !xcontext->xv_port_id; i++) {
    if (adaptors[i].type & XvImageMask) {
      gint j;

      /* We found such an adaptor, looking for an available port */
      for (j = 0; j < adaptors[i].num_ports && !xcontext->xv_port_id; j++) {
1088
1089
1090
1091
        /* We try to grab the port */
        if (Success == XvGrabPort (xcontext->disp, adaptors[i].base_id + j, 0)) {
          xcontext->xv_port_id = adaptors[i].base_id + j;
        }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1092
      }
1093
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1094
1095

    GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[i].name,
1096
        adaptors[i].num_ports);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1097
1098

  }
1099
  XvFreeAdaptorInfo (adaptors);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1100

1101
  if (!xcontext->xv_port_id) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1102
    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY, (NULL),
1103
1104
1105
1106
        ("No port available"));
    return NULL;
  }

1107
  /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1108
1109
  {
    int count;
Iain Holmes's avatar
Iain Holmes committed
1110
    XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1111
1112
        xcontext->xv_port_id, &count);
    static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1113
1114
    static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
    static const char colorkey[] = "XV_COLORKEY";
1115
1116
1117
1118
1119
1120
1121
1122

    for (i = 0; i < count; i++)
      if (!strcmp (attr[i].name, autopaint)) {
        const Atom atom = XInternAtom (xcontext->disp, autopaint, False);

        XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, 1);
        break;
      }
Iain Holmes's avatar
Iain Holmes committed