xvimagesink.c 103 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
/**
 * SECTION:element-xvimagesink
 *
 * <refsect2>
 * <para>
 * XvImageSink renders video frames to a drawable (XWindow) on a local display
26
27
 * using the XVideo extension. Rendering to a remote display is theorically
 * possible but i doubt that the XVideo extension is actually available when
Julien Moutte's avatar
Julien Moutte committed
28
29
 * 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
30
 * in this drawable. If no Window ID was provided by the application, the
Julien Moutte's avatar
Julien Moutte committed
31
32
33
34
 * element will create its own internal window and render into it.
 * </para>
 * <title>Scaling</title>
 * <para>
35
 * The XVideo extension, when it's available, handles hardware accelerated
Julien Moutte's avatar
Julien Moutte committed
36
37
 * 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
38
 * drawable scaling them on the fly. Using the
Julien Moutte's avatar
Julien Moutte committed
39
40
41
42
43
44
45
 * <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
46
 * are several kind of events that can be grouped in 2 big categories: input
Julien Moutte's avatar
Julien Moutte committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 * 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
62
 * then calculate the pixel aspect ratio. When receiving video frames with a
Julien Moutte's avatar
Julien Moutte committed
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
 * 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
90
 * following the mouse pointer. If you press the mouse button somewhere on the
Julien Moutte's avatar
Julien Moutte committed
91
92
93
94
 * 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
95
96
 * original video frame geometry so that the box can be drawn to the correct
 * position. This also handles borders correctly, limiting coordinates to the
Julien Moutte's avatar
Julien Moutte committed
97
98
99
100
101
102
103
104
105
 * 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
106
 * 1/1. This means that XvImageSink will have to do the scaling to convert
Julien Moutte's avatar
Julien Moutte committed
107
 * incoming frames to a size that will match the display pixel aspect ratio
108
 * (from 320x240 to 320x180 in this case). Note that you might have to escape
Julien Moutte's avatar
Julien Moutte committed
109
110
111
112
113
114
115
116
117
118
119
 * some characters for your shell like '\(fraction\)'.
 * </para>
 * <para>
 * Here is a test pipeline to test the colorbalance interface :
 * <programlisting>
 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
 * </programlisting>
 * </para>
 * </refsect2>
 */

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

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

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

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

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

#define MWM_HINTS_DECORATIONS   (1L << 1)

152
153
static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);

154
155
static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);

156
157
158
159
160
161
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);

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

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

enum
{
186
187
188
189
190
  ARG_0,
  ARG_CONTRAST,
  ARG_BRIGHTNESS,
  ARG_HUE,
  ARG_SATURATION,
191
  ARG_DISPLAY,
192
  ARG_SYNCHRONOUS,
193
  ARG_PIXEL_ASPECT_RATIO,
194
  ARG_FORCE_ASPECT_RATIO,
195
  ARG_HANDLE_EVENTS,
196
  ARG_DEVICE,
197
  ARG_DEVICE_NAME,
198
199
  ARG_HANDLE_EXPOSE,
  ARG_DOUBLE_BUFFER
200
201
};

202
static GstVideoSinkClass *parent_class = NULL;
203
204
205
206
207
208
209

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

210
211
212
213
214
215
216
217
218
/* 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
219
gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
220
221
222
{
  GstXvImageSink *xvimagesink;

223
224
  GST_DEBUG_OBJECT (xvimage, "Destroying buffer");

225
  xvimagesink = xvimage->xvimagesink;
226
  if (G_UNLIKELY (xvimagesink == NULL))
Wim Taymans's avatar
Wim Taymans committed
227
    goto no_sink;
228
229
230

  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));

231
232
  GST_OBJECT_LOCK (xvimagesink);

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

237
  /* We might have some buffers destroyed after changing state to NULL */
238
239
240
241
242
243
244
245
246
  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
247
248
249
    goto beach;
  }

250
251
252
253
254
  g_mutex_lock (xvimagesink->x_lock);

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

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
      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
278

279
beach:
280
  GST_OBJECT_UNLOCK (xvimagesink);
281
282
283
  xvimage->xvimagesink = NULL;
  gst_object_unref (xvimagesink);

Wim Taymans's avatar
Wim Taymans committed
284
285
286
287
288
289
290
291
292
293
294
295
296
  return;

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

static void
gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
{
  GstXvImageSink *xvimagesink;
297
  gboolean running;
Wim Taymans's avatar
Wim Taymans committed
298
299

  xvimagesink = xvimage->xvimagesink;
300
  if (G_UNLIKELY (xvimagesink == NULL))
Wim Taymans's avatar
Wim Taymans committed
301
302
    goto no_sink;

303
304
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));

305
306
307
308
  GST_OBJECT_LOCK (xvimagesink);
  running = xvimagesink->running;
  GST_OBJECT_UNLOCK (xvimagesink);

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

Wim Taymans's avatar
Wim Taymans committed
339
340
341
342
343
344
345
346
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));
}
347
348

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

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,
381
      (GInstanceInitFunc) gst_xvimage_buffer_init,
382
383
384
385
386
387
388
389
      NULL
    };
    _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstXvImageBuffer", &xvimage_buffer_info, 0);
  }
  return _gst_xvimage_buffer_type;
}

390
391
/* X11 stuff */

392
393
394
#ifdef HAVE_XSHM
static gboolean error_caught = FALSE;

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

Julien Moutte's avatar
Julien Moutte committed
400
  XGetErrorText (display, xevent->error_code, error_msg, 1024);
401
  GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
402
  error_caught = TRUE;
Julien Moutte's avatar
Julien Moutte committed
403
404
405
406
407
408
  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
409
gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
Julien Moutte's avatar
Julien Moutte committed
410
{
411
412
413
  XvImage *xvimage;
  XShmSegmentInfo SHMInfo;
  gint size;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
414
  int (*handler) (Display *, XErrorEvent *);
Tim Ringenbach's avatar
Tim Ringenbach committed
415
  gboolean result = FALSE;
416
  gboolean did_attach = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
417

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

420
421
422
423
424
425
426
  /* 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
427
  /* Setting an error handler to catch failure */
Tim Ringenbach's avatar
Tim Ringenbach committed
428
  error_caught = FALSE;
Julien Moutte's avatar
Julien Moutte committed
429
  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
430

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

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

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

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

458
459
460
461
462
  /* 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);

463
464
  xvimage->data = SHMInfo.shmaddr;
  SHMInfo.readOnly = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
465

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

471
472
473
  /* Sync to ensure we see any errors we caused */
  XSync (xcontext->disp, FALSE);

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

477
478
479
480
481
  if (!error_caught) {
    did_attach = TRUE;
    /* store whether we succeeded in result */
    result = TRUE;
  }
482
483

beach:
484
485
486
487
  /* 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
488
  XSetErrorHandler (handler);
489

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

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

513
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
514

515
  xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
516
  GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
517

518
519
520
521
522
523
524
525
526
527
528
  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);
529
  if (xvimage->im_format == -1) {
530
531
    GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
        GST_PTR_FORMAT, caps);
532
533
534
    GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
        ("Failed to create output image buffer of %dx%d pixels",
            xvimage->width, xvimage->height), ("Invalid input caps"));
535
    goto beach_unlocked;
536
  }
537
  xvimage->xvimagesink = gst_object_ref (xvimagesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
538

539
540
  g_mutex_lock (xvimagesink->x_lock);

541
542
543
544
  /* Setting an error handler to catch failure */
  error_caught = FALSE;
  handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);

545
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
546
  if (xvimagesink->xcontext->use_xshm) {
547
548
    int expected_size;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
549
    xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
550
        xvimagesink->xcontext->xv_port_id,
551
552
        xvimage->im_format, NULL,
        xvimage->width, xvimage->height, &xvimage->SHMInfo);
553
    if (!xvimage->xvimage || error_caught) {
554
      g_mutex_unlock (xvimagesink->x_lock);
555
556
557
558
      /* Reset error handler */
      error_caught = FALSE;
      XSetErrorHandler (handler);
      /* Push an error */
559
560
561
562
563
      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));
564
      goto beach_unlocked;
565
566
567
    }

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

572
573
    /* calculate the expected size.  This is only for sanity checking the
     * number we get from X. */
574
    switch (xvimage->im_format) {
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
      case GST_MAKE_FOURCC ('I', '4', '2', '0'):
        expected_size =
            GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_4 (xvimage->width);
        expected_size +=
            GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_8 (xvimage->width) /
            2;
        break;
      case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
      case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
      case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
        expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
        break;
      default:
        expected_size = 0;
        break;
    }
    if (expected_size != 0 && xvimage->size != expected_size) {
      GST_WARNING_OBJECT (xvimagesink,
          "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
          xvimage->size, expected_size);
    }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
597
    xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
598
        IPC_CREAT | 0777);
599
    if (xvimage->SHMInfo.shmid == -1) {
600
      g_mutex_unlock (xvimagesink->x_lock);
601
602
603
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
604
605
          ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
              xvimage->size));
606
      goto beach_unlocked;
607
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
608
609

    xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
610
    if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
611
      g_mutex_unlock (xvimagesink->x_lock);
612
613
614
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height),
615
          ("Failed to shmat: %s", g_strerror (errno)));
616
617
      /* Clean up the shared memory segment */
      shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
618
      goto beach_unlocked;
619
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
620

621
622
623
624
625
    /* 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);

626
    xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
627
628
    xvimage->SHMInfo.readOnly = FALSE;

629
    if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
630
      g_mutex_unlock (xvimagesink->x_lock);
631
632
633
      GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              xvimage->width, xvimage->height), ("Failed to XShmAttach"));
634
      goto beach_unlocked;
635
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
636
637

    XSync (xvimagesink->xcontext->disp, FALSE);
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
638
    GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
639
        xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
640
  } else
Julien Moutte's avatar
Julien Moutte committed
641
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
642
643
  {
    xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
644
        xvimagesink->xcontext->xv_port_id,
645
        xvimage->im_format, NULL, xvimage->width, xvimage->height);
646
    if (!xvimage->xvimage || error_caught) {
647
      g_mutex_unlock (xvimagesink->x_lock);
648
649
650
651
      /* Reset error handler */
      error_caught = FALSE;
      XSetErrorHandler (handler);
      /* Push an error */
652
653
654
655
656
      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));
657
      goto beach_unlocked;
658
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
659

660
    /* we have to use the returned data_size for our image size */
661
662
    xvimage->size = xvimage->xvimage->data_size;
    xvimage->xvimage->data = g_malloc (xvimage->size);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
663
664
665

    XSync (xvimagesink->xcontext->disp, FALSE);
  }
666
667
668
669
670

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

671
  succeeded = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
672

673
674
675
  GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
  GST_BUFFER_SIZE (xvimage) = xvimage->size;

676
677
  g_mutex_unlock (xvimagesink->x_lock);

678
beach_unlocked:
679
  if (!succeeded) {
Wim Taymans's avatar
Wim Taymans committed
680
    gst_xvimage_buffer_free (xvimage);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
681
682
    xvimage = NULL;
  }
683

684
685
686
  return xvimage;
}

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
/* 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);
  }
}

723
724
725
/* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
 * if no window was available  */
static gboolean
726
727
gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
    GstXvImageBuffer * xvimage)
728
{
729
  GstVideoRectangle src, dst, result;
730
  gboolean draw_border = FALSE;
731

732
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
733

734
  /* We take the flow_lock. If expose is in there we don't want to run
735
736
737
     concurrently from the data flow thread */
  g_mutex_lock (xvimagesink->flow_lock);

738
739
  if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
    g_mutex_unlock (xvimagesink->flow_lock);
740
    return FALSE;
741
742
  }

743
  /* Draw borders when displaying the first frame. After this
744
745
     draw borders only on expose event or after a size change. */
  if (!xvimagesink->cur_image || xvimagesink->draw_border) {
746
    draw_border = TRUE;
747
    xvimagesink->draw_border = FALSE;
748
749
  }

750
  /* Store a reference to the last image we put, lose the previous one */
751
  if (xvimage && xvimagesink->cur_image != xvimage) {
752
    if (xvimagesink->cur_image) {
753
      GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
754
755
      gst_buffer_unref (xvimagesink->cur_image);
    }
756
    GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
757
758
    xvimagesink->cur_image =
        GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
759
760
  }

761
762
  /* Expose sends a NULL image, we take the latest frame */
  if (!xvimage) {
763
    draw_border = TRUE;
764
765
766
767
    if (xvimagesink->cur_image) {
      xvimage = xvimagesink->cur_image;
    } else {
      g_mutex_unlock (xvimagesink->flow_lock);
768
      return TRUE;
769
770
771
    }
  }

772
773
  gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);

774
  /* We use the calculated geometry from _setcaps as a source to respect
775
776
777
     source and screen pixel aspect ratios. */
  src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
  src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
778
779
780
781
782
783
784
785
786
787
  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
788

789
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
790

791
792
793
794
  if (draw_border) {
    gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
        result);
  }
795

796
  /* We scale to the window's geometry */
797
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
798
  if (xvimagesink->xcontext->use_xshm) {
799
    GST_LOG_OBJECT (xvimagesink,
800
801
        "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
        GST_PTR_FORMAT,
802
        xvimage->width, xvimage->height,
803
804
        xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
805
    XvShmPutImage (xvimagesink->xcontext->disp,
806
807
808
809
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
810
        result.x, result.y, result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
811
  } else
Julien Moutte's avatar
Julien Moutte committed
812
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
813
814
  {
    XvPutImage (xvimagesink->xcontext->disp,
815
816
817
818
        xvimagesink->xcontext->xv_port_id,
        xvimagesink->xwindow->win,
        xvimagesink->xwindow->gc, xvimage->xvimage,
        0, 0, xvimage->width, xvimage->height,
819
        result.x, result.y, result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
820
821
822
823
  }

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

824
  g_mutex_unlock (xvimagesink->x_lock);
825
826

  g_mutex_unlock (xvimagesink->flow_lock);
827
828

  return TRUE;
829
830
}

831
832
833
834
835
836
837
838
839
840
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);

841
842
  g_mutex_lock (xvimagesink->x_lock);

843
  hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
844
  if (hints_atom == None) {
845
    g_mutex_unlock (xvimagesink->x_lock);
846
847
848
    return FALSE;
  }

849
850
  hints = g_malloc0 (sizeof (MotifWmHints));

851
852
853
854
855
856
857
858
859
  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);

860
861
  g_mutex_unlock (xvimagesink->x_lock);

862
863
864
865
866
  g_free (hints);

  return TRUE;
}

867
868
/* This function handles a GstXWindow creation
 * The width and height are the actual pixel size on the display */
869
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
870
871
gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
    gint width, gint height)
872
873
874
{
  GstXWindow *xwindow = NULL;
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
875

876
  g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
877

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

880
881
  xwindow->width = width;
  xwindow->height = height;
Julien Moutte's avatar
Julien Moutte committed
882
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
883

884
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
885

886
  xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
887
888
889
890
      xvimagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height,
      0, 0, xvimagesink->xcontext->black);

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

895
  if (xvimagesink->handle_events) {
896
897
    Atom wm_delete;

898
899
900
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
        StructureNotifyMask | PointerMotionMask | KeyPressMask |
        KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
901
902
903
904
905
906
907

    /* Tell the window manager we'd like delete client messages instead of
     * being killed */
    wm_delete = XInternAtom (xvimagesink->xcontext->disp,
        "WM_DELETE_WINDOW", False);
    (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
        &wm_delete, 1);
908
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
909

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

913
  XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
914
915
916

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

917
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
918

919
920
  gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);

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

923
924
925
926
927
  return xwindow;
}

/* This function destroys a GstXWindow */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
928
929
gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
930
931
932
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
933

934
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
935

Julien Moutte's avatar
Julien Moutte committed
936
937
938
  /* 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);
939
940
  else
    XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
941

942
  XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
943
944
945

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

946
  g_mutex_unlock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
947

948
949
950
  g_free (xwindow);
}

951
static void
952
953
gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
954
{
955
956
  XWindowAttributes attr;

957
958
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
959

960
  /* Update the window geometry */
961
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
962

963
964
  XGetWindowAttributes (xvimagesink->xcontext->disp,
      xvimagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
965

966
967
  xvimagesink->xwindow->width = attr.width;
  xvimagesink->xwindow->height = attr.height;
968
969
970
971

  g_mutex_unlock (xvimagesink->x_lock);
}

972
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
973
974
gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
    GstXWindow * xwindow)
975
976
977
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
978

979
  g_mutex_lock (xvimagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
980

981
982
983
  XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
      xwindow->win);

984
  XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
985
986
      xvimagesink->xcontext->black);

987
  XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
988
989
      0, 0, xwindow->width, xwindow->height);

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

992
993
994
  g_mutex_unlock (xvimagesink->x_lock);
}

995
996
997
/* 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
998
gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
999
1000
{
  GList *channels = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1001

1002
  g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1003

1004
1005
1006
  /* 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
1007

1008
1009
1010
1011
  /* 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
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025

  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) {
1026
        value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1027
      } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1028
1029
        value = (xvimagesink->saturation + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1030
      } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1031
1032
        value = (xvimagesink->contrast + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1033
      } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1034
1035
        value = (xvimagesink->brightness + 1000) * convert_coef +
            channel->min_value;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1036
      } else {
1037
1038
1039
        g_warning ("got an unknown channel %s", channel->label);
        g_object_unref (channel);
        return;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1040
1041
1042
1043
1044
      }

      /* Committing to Xv port */
      g_mutex_lock (xvimagesink->x_lock);
      XvSetPortAttribute (xvimagesink->xcontext->disp,
1045
1046
          xvimagesink->xcontext->xv_port_id,
          XInternAtom (xvimagesink->xcontext->disp, channel->label, 1), value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1047
1048
1049
      g_mutex_unlock (xvimagesink->x_lock);

      g_object_unref (channel);
1050
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1051
1052
    channels = g_list_next (channels);
  }
1053
1054
}

1055
1056
1057
1058
1059
/* 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