ximagesink.c 66.7 KB
Newer Older
Julien Moutte's avatar
Julien Moutte committed
1
/* GStreamer
2
 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
Julien Moutte's avatar
Julien Moutte committed
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

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
/**
 * SECTION:element-ximagesink
 *
 * <refsect2>
 * <para>
 * XImageSink renders video frames to a drawable (XWindow) on a local or 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>
 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
 * reverse caps negotiation to try to get scaled video frames for the drawable.
 * This is accomplished by asking the peer pad if it accepts some different caps
 * which in most cases implies that there is a scaling element in the pipeline,
 * or that an element generating the video frames can generate them with a 
 * different geometry. This mechanism is handled during buffer allocations, for
 * each allocation request the video sink will check the drawable geometry, look
 * at the
 * <link linkend="GstXImageSink--force-aspect-ratio">force-aspect-ratio</link>
 * property, calculate the geometry of desired video frames and then check that
 * the peer pad accept those new caps. If it does it will then allocate a buffer
 * in video memory with this new geometry and return it with the new caps.
 * </para>
 * <title>Events</title>
 * <para>
 * XImageSink 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, XImageSink will open a connection to
 * the display specified in the
 * <link linkend="GstXImageSink--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 caps negotiation will occur, the
 * video sink will set the calculated pixel aspect ratio on the caps to make 
 * sure that incoming video frames will have the correct pixel aspect ratio for
 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
 * then possible to enforce a specific pixel aspect ratio using the
 * <link linkend="GstXImageSink--pixel-aspect-ratio">pixel-aspect-ratio</link>
 * property.
 * </para>
 * <title>Examples</title>
 * <para>
 * Here is a simple pipeline to test reverse negotiation :
 * <programlisting>
 * gst-launch -v videotestsrc ! queue ! ximagesink
 * </programlisting>
 * When the test video signal appears you can resize the window and see that
 * scaled buffers of the desired size are going to arrive with a short delay.
 * This illustrates how buffers of desired size are allocated along the way.
 * If you take away the queue, scaling will happen almost immediately.
 * </para>
 * <para>
 * Here is a simple pipeline to test navigation events :
 * <programlisting>
 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
 * </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.)
 * </para>
 * <para>
 * Here is a simple pipeline to test pixel aspect ratio :
 * <programlisting>
 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
 * </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 videoscale 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>
 * </refsect2>
 */

Julien Moutte's avatar
Julien Moutte committed
110
111
112
113
114
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Our interfaces */
115
116
#include <gst/interfaces/navigation.h>
#include <gst/interfaces/xoverlay.h>
Julien Moutte's avatar
Julien Moutte committed
117
118
119
120

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

121
122
/* Debugging category */
#include <gst/gstinfo.h>
123
124

GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
125
126
#define GST_CAT_DEFAULT gst_debug_ximagesink

127
128
129
130
131
132
133
134
135
136
137
138
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)

139
static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
140
    GstXImageBuffer * ximage);
141
142
143
static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
    GstXWindow * xwindow);
static void gst_ximagesink_expose (GstXOverlay * overlay);
144

Julien Moutte's avatar
Julien Moutte committed
145
/* ElementFactory information */
Stefan Kost's avatar
Stefan Kost committed
146
static const GstElementDetails gst_ximagesink_details =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
148
149
150
GST_ELEMENT_DETAILS ("Video sink",
    "Sink/Video",
    "A standard X based videosink",
    "Julien Moutte <julien@moutte.net>");
Julien Moutte's avatar
Julien Moutte committed
151

David Schleef's avatar
David Schleef committed
152
static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
153
154
155
156
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-rgb, "
157
        "framerate = (fraction) [ 0, MAX ], "
158
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
159
160
161
162
    );

enum
{
163
164
165
  PROP_0,
  PROP_DISPLAY,
  PROP_SYNCHRONOUS,
166
167
  PROP_PIXEL_ASPECT_RATIO,
  PROP_FORCE_ASPECT_RATIO
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168
      /* FILL ME */
169
170
};

171
static GstVideoSinkClass *parent_class = NULL;
Julien Moutte's avatar
Julien Moutte committed
172
173
174
175
176
177
178

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

179
180
181
182
183
184
/* ximage buffers */

#define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())

#define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
#define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
185
186
187
188
189
190
191
192
193
#define GST_XIMAGE_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))

/* So some words about GstMiniObject, this is pretty messy...
   GstMiniObject does not use the standard finalizing of GObjects, you are 
   supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
   which will handle its own refcount system and call gst_mini_object_free.
   gst_mini_object_free will call the class finalize method which is not the 
   one from GObject, after calling this finalize method it will free the object
   instance for you if the refcount is still 0 so you should not chain up */
194
static void
195
gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
196
{
197
198
  GstXImageSink *ximagesink = NULL;
  gboolean recycled = FALSE;
199
  gboolean running;
200

201
  g_return_if_fail (ximage != NULL);
202

203
204
205
206
  ximagesink = ximage->ximagesink;
  if (!ximagesink) {
    GST_WARNING_OBJECT (ximagesink, "no sink found");
    goto beach;
207
208
  }

209
210
211
212
213
214
215
216
217
218
  GST_OBJECT_LOCK (ximagesink);
  running = ximagesink->running;
  GST_OBJECT_UNLOCK (ximagesink);

  if (running == FALSE) {
    /* If the sink is shutting down, need to clear the image */
    GST_DEBUG_OBJECT (ximagesink,
        "destroy image %p because the sink is shutting down", ximage);
    gst_ximagesink_ximage_destroy (ximagesink, ximage);
  } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
219
      (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
220
    /* If our geometry changed we can't reuse that image. */
221
222
223
224
225
226
227
    GST_DEBUG_OBJECT (ximagesink,
        "destroy image %p as its size changed %dx%d vs current %dx%d",
        ximage, ximage->width, ximage->height,
        GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
    gst_ximagesink_ximage_destroy (ximagesink, ximage);
  } else {
    /* In that case we can reuse the image and add it to our image pool. */
228
    GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
229
    /* need to increment the refcount again to recycle */
230
    gst_buffer_ref (GST_BUFFER_CAST (ximage));
231
232
233
234
    g_mutex_lock (ximagesink->pool_lock);
    ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
    g_mutex_unlock (ximagesink->pool_lock);
    recycled = TRUE;
235
236
  }

237
238
beach:
  return;
239
240
}

241
242
243
244
245
246
static void
gst_ximage_buffer_free (GstXImageBuffer * ximage)
{
  /* make sure it is not recycled */
  ximage->width = -1;
  ximage->height = -1;
247
  gst_buffer_unref (GST_BUFFER_CAST (ximage));
248
249
}

250
static void
251
gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
252
{
253
#ifdef HAVE_XSHM
254
255
  ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
  ximage_buffer->SHMInfo.shmid = -1;
256
#endif
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
}

static void
gst_ximage_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_ximage_buffer_finalize;
}

GType
gst_ximage_buffer_get_type (void)
{
  static GType _gst_ximage_buffer_type;

  if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
    static const GTypeInfo ximage_buffer_info = {
      sizeof (GstBufferClass),
      NULL,
      NULL,
      gst_ximage_buffer_class_init,
      NULL,
      NULL,
      sizeof (GstXImageBuffer),
      0,
283
      (GInstanceInitFunc) gst_ximage_buffer_init,
284
285
286
287
288
289
290
291
      NULL
    };
    _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
        "GstXImageBuffer", &ximage_buffer_info, 0);
  }
  return _gst_ximage_buffer_type;
}

Julien Moutte's avatar
Julien Moutte committed
292
293
/* X11 stuff */

294
#ifdef HAVE_XSHM                /* Check that XShm calls actually work */
295
296
static gboolean error_caught = FALSE;

Julien Moutte's avatar
Julien Moutte committed
297
static int
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
298
gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
Julien Moutte's avatar
Julien Moutte committed
299
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
300
301
  char error_msg[1024];

Julien Moutte's avatar
Julien Moutte committed
302
  XGetErrorText (display, xevent->error_code, error_msg, 1024);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
303
  GST_DEBUG ("ximagesink failed to use XShm calls. error: %s", error_msg);
304
  error_caught = TRUE;
Julien Moutte's avatar
Julien Moutte committed
305
306
307
308
  return 0;
}

static gboolean
309
310
gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
    GstXContext * xcontext)
Julien Moutte's avatar
Julien Moutte committed
311
{
312
313
314
  XImage *ximage;
  XShmSegmentInfo SHMInfo;
  size_t size;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315
  int (*handler) (Display *, XErrorEvent *);
316
  gboolean result = FALSE;
317
  gboolean did_attach = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
318

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

321
322
323
324
325
326
327
  /* 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
328
  /* Setting an error handler to catch failure */
329
  error_caught = FALSE;
Julien Moutte's avatar
Julien Moutte committed
330
  handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
331

332
  /* Trying to create a 1x1 ximage */
333
334
  GST_DEBUG ("XShmCreateImage of 1x1");

335
336
  ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
      xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
337
338
339
340

  /* Might cause an error, sync to ensure it is noticed */
  XSync (xcontext->disp, FALSE);
  if (!ximage || error_caught) {
341
342
343
    GST_WARNING ("could not XShmCreateImage a 1x1 image");
    goto beach;
  }
344
  size = ximage->height * ximage->bytes_per_line;
Julien Moutte's avatar
Julien Moutte committed
345

346
347
348
  SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
  if (SHMInfo.shmid == -1) {
    GST_WARNING ("could not get shared memory of %d bytes", size);
349
350
351
    goto beach;
  }

352
353
  SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
  if (SHMInfo.shmaddr == ((void *) -1)) {
354
    GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
355
356
    /* Clean up shm seg */
    shmctl (SHMInfo.shmid, IPC_RMID, 0);
357
358
359
    goto beach;
  }

360
361
362
363
364
  /* 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);

365
366
  ximage->data = SHMInfo.shmaddr;
  SHMInfo.readOnly = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
367

368
  if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
369
370
371
    GST_WARNING ("Failed to XShmAttach");
    goto beach;
  }
Julien Moutte's avatar
Julien Moutte committed
372

373
374
375
376
377
378
379
380
381
382
383
384
385
386
  /* 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;
  }

beach:
  /* Sync to ensure we swallow any errors we caused and reset error_caught */
  XSync (xcontext->disp, FALSE);
  error_caught = FALSE;
  XSetErrorHandler (handler);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
387

388
  if (did_attach) {
389
    XShmDetach (xcontext->disp, &SHMInfo);
390
    XSync (xcontext->disp, FALSE);
391
  }
392
393
  if (SHMInfo.shmaddr != ((void *) -1))
    shmdt (SHMInfo.shmaddr);
394
395
  if (ximage)
    XDestroyImage (ximage);
396
  return result;
Julien Moutte's avatar
Julien Moutte committed
397
}
398
#endif /* HAVE_XSHM */
Julien Moutte's avatar
Julien Moutte committed
399

400
401
/* This function handles GstXImageBuffer creation depending on XShm availability */
static GstXImageBuffer *
402
gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
Julien Moutte's avatar
Julien Moutte committed
403
{
404
  GstXImageBuffer *ximage = NULL;
405
  GstStructure *structure = NULL;
406
  gboolean succeeded = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407

Julien Moutte's avatar
Julien Moutte committed
408
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
409

410
  ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
411

412
413
414
415
416
417
418
419
420
  structure = gst_caps_get_structure (caps, 0);

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

  GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
      ximage->width, ximage->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421

Julien Moutte's avatar
Julien Moutte committed
422
423
  g_mutex_lock (ximagesink->x_lock);

424
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
425
426
  if (ximagesink->xcontext->use_xshm) {
    ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
427
428
429
        ximagesink->xcontext->visual,
        ximagesink->xcontext->depth,
        ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
430
    if (!ximage->ximage) {
431
      g_mutex_unlock (ximagesink->x_lock);
432
433
434
435
436
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
          ("could not XShmCreateImage a %dx%d image",
              ximage->width, ximage->height));
437
438
439
440
      goto beach;
    }

    /* we have to use the returned bytes_per_line for our shm size */
441
    ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
442
    GST_LOG_OBJECT (ximagesink, "XShm image size is %d, width %d, stride %d",
443
        ximage->size, ximage->width, ximage->ximage->bytes_per_line);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444
445

    ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
446
        IPC_CREAT | 0777);
447
    if (ximage->SHMInfo.shmid == -1) {
448
      g_mutex_unlock (ximagesink->x_lock);
449
450
451
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
452
453
454
          ("could not get shared memory of %d bytes", ximage->size));
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
455
456

    ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
457
    if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
458
      g_mutex_unlock (ximagesink->x_lock);
459
460
461
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
462
          ("Failed to shmat: %s", g_strerror (errno)));
463
464
      /* Clean up the shared memory segment */
      shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
465
466
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
467

468
469
470
471
472
    /* Now that we've attached, we can delete the shared memory segment.
     * This way, it will be deleted as soon as we detach later, and not
     * leaked if we crash. */
    shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);

473
    ximage->ximage->data = ximage->SHMInfo.shmaddr;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
474
475
    ximage->SHMInfo.readOnly = FALSE;

476
    if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
477
      g_mutex_unlock (ximagesink->x_lock);
478
479
480
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height), ("Failed to XShmAttach"));
481
482
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483
484
485

    XSync (ximagesink->xcontext->disp, FALSE);
  } else
486
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487
488
  {
    ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
489
490
        ximagesink->xcontext->visual,
        ximagesink->xcontext->depth,
491
        ZPixmap, 0, NULL,
492
        ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
493
    if (!ximage->ximage) {
494
      g_mutex_unlock (ximagesink->x_lock);
495
496
497
498
499
      GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
          ("Failed to create output image buffer of %dx%d pixels",
              ximage->width, ximage->height),
          ("could not XCreateImage a %dx%d image",
              ximage->width, ximage->height));
500
501
      goto beach;
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502

503
    /* we have to use the returned bytes_per_line for our image size */
504
    ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
505
506
    ximage->ximage->data = g_malloc (ximage->size);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
507
508
    XSync (ximagesink->xcontext->disp, FALSE);
  }
509
  succeeded = TRUE;
510
511
512
513

  GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
  GST_BUFFER_SIZE (ximage) = ximage->size;

514
515
516
  /* Keep a ref to our sink */
  ximage->ximagesink = gst_object_ref (ximagesink);

517
  g_mutex_unlock (ximagesink->x_lock);
518
beach:
519
  if (!succeeded) {
520
    gst_ximage_buffer_free (ximage);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
521
522
523
    ximage = NULL;
  }

Julien Moutte's avatar
Julien Moutte committed
524
525
526
  return ximage;
}

527
/* This function destroys a GstXImageBuffer handling XShm availability */
Julien Moutte's avatar
Julien Moutte committed
528
static void
529
530
gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
    GstXImageBuffer * ximage)
Julien Moutte's avatar
Julien Moutte committed
531
532
533
{
  g_return_if_fail (ximage != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
534

535
  /* If the destroyed image is the current one we destroy our reference too */
536
  if (ximagesink->cur_image == ximage) {
537
    ximagesink->cur_image = NULL;
538
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
539

540
541
542
  /* Hold the object lock to ensure the XContext doesn't disappear */
  GST_OBJECT_LOCK (ximagesink);

543
544
  /* We might have some buffers destroyed after changing state to NULL */
  if (!ximagesink->xcontext) {
545
546
547
548
549
550
    GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
#ifdef HAVE_XSHM
    if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
      shmdt (ximage->SHMInfo.shmaddr);
    }
#endif
551
552
553
    goto beach;
  }

Julien Moutte's avatar
Julien Moutte committed
554
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
555

556
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
557
  if (ximagesink->xcontext->use_xshm) {
558
    if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
559
      XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
560
      XSync (ximagesink->xcontext->disp, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
561
      shmdt (ximage->SHMInfo.shmaddr);
562
563
564
565
    }
    if (ximage->ximage)
      XDestroyImage (ximage->ximage);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
566
567
568
  } else
#endif /* HAVE_XSHM */
  {
569
    if (ximage->ximage) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
570
      XDestroyImage (ximage->ximage);
571
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
572
573
  }

574
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
575

Julien Moutte's avatar
Julien Moutte committed
576
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
577

578
beach:
579
580
  GST_OBJECT_UNLOCK (ximagesink);

581
582
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
619
620
621
622
623
  if (ximage->ximagesink) {
    /* Release the ref to our sink */
    ximage->ximagesink = NULL;
    gst_object_unref (ximagesink);
  }

  return;
}

/* We are called with the x_lock taken */
static void
gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
    GstXWindow * xwindow, GstVideoRectangle rect)
{
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
  g_return_if_fail (xwindow != NULL);

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

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

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

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

  /* Bottom border */
  if ((rect.y + rect.h) < xwindow->height) {
    XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
        0, rect.y + rect.h, xwindow->width, xwindow->height);
  }
Julien Moutte's avatar
Julien Moutte committed
624
625
}

626
/* This function puts a GstXImageBuffer on a GstXImageSink's window */
Julien Moutte's avatar
Julien Moutte committed
627
static void
628
gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
Julien Moutte's avatar
Julien Moutte committed
629
{
630
  GstVideoRectangle src, dst, result;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
631

Julien Moutte's avatar
Julien Moutte committed
632
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
633

634
  /* We take the flow_lock. If expose is in there we don't want to run
635
636
     concurrently from the data flow thread */
  g_mutex_lock (ximagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
637

638
  /* Store a reference to the last image we put, lose the previous one */
639
  if (ximage && ximagesink->cur_image != ximage) {
640
    if (ximagesink->cur_image) {
641
      GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
642
643
      gst_buffer_unref (ximagesink->cur_image);
    }
644
    GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
645
    ximagesink->cur_image =
646
        GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
647
648
  }

649
650
651
652
653
654
655
656
657
658
  /* Expose sends a NULL image, we take the latest frame */
  if (!ximage) {
    if (ximagesink->cur_image) {
      ximage = ximagesink->cur_image;
    } else {
      g_mutex_unlock (ximagesink->flow_lock);
      return;
    }
  }

659
660
661
662
663
664
  gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);

  src.w = ximage->width;
  src.h = ximage->height;
  dst.w = ximagesink->xwindow->width;
  dst.h = ximagesink->xwindow->height;
Julien Moutte's avatar
Julien Moutte committed
665

666
  gst_video_sink_center_rect (src, dst, &result, FALSE);
667

668
  g_mutex_lock (ximagesink->x_lock);
669
670
671

  gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow, result);

672
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
673
  if (ximagesink->xcontext->use_xshm) {
674
    GST_LOG_OBJECT (ximagesink,
675
676
677
        "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
        ximage, 0, 0, result.x, result.y, result.w, result.h,
        ximagesink->xwindow->width, ximagesink->xwindow->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
678
    XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
679
680
        ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
        result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
681
  } else
Julien Moutte's avatar
Julien Moutte committed
682
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
683
  {
684
685
686
687
    GST_LOG_OBJECT (ximagesink,
        "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
        ximage, 0, 0, result.x, result.y, result.w, result.h,
        ximagesink->xwindow->width, ximagesink->xwindow->height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
688
    XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
689
690
        ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
        result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
691
692
  }

693
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
694

Julien Moutte's avatar
Julien Moutte committed
695
  g_mutex_unlock (ximagesink->x_lock);
696
697

  g_mutex_unlock (ximagesink->flow_lock);
Julien Moutte's avatar
Julien Moutte committed
698
699
}

700
701
702
703
704
705
706
707
708
709
static gboolean
gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
    GstXWindow * window)
{
  Atom hints_atom = None;
  MotifWmHints *hints;

  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
  g_return_val_if_fail (window != NULL, FALSE);

710
711
  g_mutex_lock (ximagesink->x_lock);

712
  hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
713
  if (hints_atom == None) {
714
    g_mutex_unlock (ximagesink->x_lock);
715
716
717
    return FALSE;
  }

718
719
  hints = g_malloc0 (sizeof (MotifWmHints));

720
721
722
723
724
725
726
727
728
  hints->flags |= MWM_HINTS_DECORATIONS;
  hints->decorations = 1 << 0;

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

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

729
730
  g_mutex_unlock (ximagesink->x_lock);

731
732
733
734
735
  g_free (hints);

  return TRUE;
}

Julien Moutte's avatar
Julien Moutte committed
736
/* This function handles a GstXWindow creation */
Julien Moutte's avatar
Julien Moutte committed
737
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
738
gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
Julien Moutte's avatar
Julien Moutte committed
739
740
{
  GstXWindow *xwindow = NULL;
741
  XGCValues values;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
742

Julien Moutte's avatar
Julien Moutte committed
743
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
744

Julien Moutte's avatar
Julien Moutte committed
745
  xwindow = g_new0 (GstXWindow, 1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
746

Julien Moutte's avatar
Julien Moutte committed
747
748
  xwindow->width = width;
  xwindow->height = height;
749
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
750

Julien Moutte's avatar
Julien Moutte committed
751
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
752

753
  xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
754
755
      ximagesink->xcontext->root,
      0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
756

757
758
759
760
  /* We have to do that to prevent X from redrawing the background on 
     ConfigureNotify. This takes away flickering of video when resizing. */
  XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);

761
  XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
762
763
764
      StructureNotifyMask | PointerMotionMask | KeyPressMask |
      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);

765
766
  xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
      0, &values);
767

768
  XMapRaised (ximagesink->xcontext->disp, xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
769

770
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
771

Julien Moutte's avatar
Julien Moutte committed
772
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
773

774
775
  gst_ximagesink_xwindow_decorate (ximagesink, xwindow);

776
  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
777

Julien Moutte's avatar
Julien Moutte committed
778
779
780
  return xwindow;
}

Julien Moutte's avatar
Julien Moutte committed
781
/* This function destroys a GstXWindow */
Julien Moutte's avatar
Julien Moutte committed
782
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
783
784
gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
    GstXWindow * xwindow)
Julien Moutte's avatar
Julien Moutte committed
785
786
787
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
788

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

Julien Moutte's avatar
Julien Moutte committed
791
  /* If we did not create that window we just free the GC and let it live */
792
  if (xwindow->internal)
Julien Moutte's avatar
Julien Moutte committed
793
    XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
794
  else
795
    XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
796

Julien Moutte's avatar
Julien Moutte committed
797
  XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
798

799
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
800

Julien Moutte's avatar
Julien Moutte committed
801
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
802

Julien Moutte's avatar
Julien Moutte committed
803
804
805
  g_free (xwindow);
}

806
static void
807
808
gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
    GstXWindow * xwindow)
809
{
810
811
  XWindowAttributes attr;

812
813
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
814

815
  /* Update the window geometry */
816
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
817

818
819
  XGetWindowAttributes (ximagesink->xcontext->disp,
      ximagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
820

821
822
  ximagesink->xwindow->width = attr.width;
  ximagesink->xwindow->height = attr.height;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
823

824
825
  g_mutex_unlock (ximagesink->x_lock);
}
826

827
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
828
gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
829
830
831
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
832

833
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
834

835
  XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
836
837
      ximagesink->xcontext->black);

838
  XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
839
840
      0, 0, xwindow->width, xwindow->height);

841
  XSync (ximagesink->xcontext->disp, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
842

843
844
845
  g_mutex_unlock (ximagesink->x_lock);
}

Julien Moutte's avatar
Julien Moutte committed
846
847
/* This function handles XEvents that might be in the queue. It generates
   GstEvent that will be sent upstream in the pipeline to handle interactivity
848
   and navigation.*/
849
static void
850
gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
851
852
{
  XEvent e;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
853

854
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
855

856
857
858
  {
    guint pointer_x = 0, pointer_y = 0;
    gboolean pointer_moved = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
859

860
861
862
863
864
865
    /* Then we get all pointer motion events, only the last position is
       interesting. */
    g_mutex_lock (ximagesink->x_lock);
    while (XCheckWindowEvent (ximagesink->xcontext->disp,
            ximagesink->xwindow->win, PointerMotionMask, &e)) {
      g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
866

867
868
869
870
871
872
873
874
875
      switch (e.type) {
        case MotionNotify:
          pointer_x = e.xmotion.x;
          pointer_y = e.xmotion.y;
          pointer_moved = TRUE;
          break;
        default:
          break;
      }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
876

877
878
879
      g_mutex_lock (ximagesink->x_lock);
    }
    g_mutex_unlock (ximagesink->x_lock);
880

881
882
883
884
885
886
    if (pointer_moved) {
      GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
          pointer_x, pointer_y);
      gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
          "mouse-move", 0, pointer_x, pointer_y);
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
887
888
  }

889
890
891
  /* We get all remaining events on our window to throw them upstream */
  g_mutex_lock (ximagesink->x_lock);
  while (XCheckWindowEvent (ximagesink->xcontext->disp,
892
893
894
          ximagesink->xwindow->win,
          KeyPressMask | KeyReleaseMask |
          ButtonPressMask | ButtonReleaseMask, &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
895
896
897
898
899
900
901
    KeySym keysym;

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

    switch (e.type) {
      case ButtonPress:
902
903
904
905
906
907
908
        /* Mouse button pressed/released over our window. We send upstream
           events for interactivity/navigation */
        GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
            e.xbutton.button, e.xbutton.x, e.xbutton.x);
        gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
            "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
909
      case ButtonRelease:
910
911
912
913
914
        GST_DEBUG ("ximagesink button %d release over window at %d,%d",
            e.xbutton.button, e.xbutton.x, e.xbutton.x);
        gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
            "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
915
916
      case KeyPress:
      case KeyRelease:
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
        /* Key pressed/released over our window. We send upstream
           events for interactivity/navigation */
        GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
            e.xkey.keycode, e.xkey.x, e.xkey.x);
        keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
            e.xkey.keycode, 0);
        if (keysym != NoSymbol) {
          gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
              e.type == KeyPress ?
              "key-press" : "key-release", XKeysymToString (keysym));
        } else {
          gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
              e.type == KeyPress ? "key-press" : "key-release", "unknown");
        }
        break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
932
      default:
933
934
        GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
            e.type);
935
    }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
936
937
    g_mutex_lock (ximagesink->x_lock);
  }
Julien Moutte's avatar
Julien Moutte committed
938
  g_mutex_unlock (ximagesink->x_lock);
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978

  {
    gboolean exposed = FALSE;

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

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

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

    if (exposed) {
      gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
    }
  }
}

static gpointer
gst_ximagesink_event_thread (GstXImageSink * ximagesink)
{
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);

  while (ximagesink->running) {
    if (ximagesink->xwindow) {
      gst_ximagesink_handle_xevents (ximagesink);
    }
    g_usleep (100000);
  }

  return NULL;
979
}
Julien Moutte's avatar
Julien Moutte committed
980

981
982
983
984
985
986
987
988
989
/* This function calculates the pixel aspect ratio based on the properties
 * in the xcontext structure and stores it there. */
static void
gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
{
  gint par[][2] = {
    {1, 1},                     /* regular screen */
    {16, 15},                   /* PAL TV */
    {11, 10},                   /* 525 line Rec.601 video */
990
991
992
993
    {54, 59},                   /* 625 line Rec.601 video */
    {64, 45},                   /* 1280x1024 on 16:9 display */
    {5, 3},                     /* 1280x1024 on 4:3 display */
    {4, 3}                      /*  800x600 on 16:9 display */
994
995
996
997
998
999
1000
1001
1002
1003
  };
  gint i;
  gint index;
  gdouble ratio;
  gdouble delta;

#define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))

  /* first calculate the "real" ratio based on the X values;
   * which is the "physical" w/h divided by the w/h in pixels of the display */
1004
  ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1005
      / (xcontext->heightmm * xcontext->width);
1006
1007
1008
1009
1010
1011

  /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
   * override here */
  if (xcontext->width == 720 && xcontext->height == 576) {
    ratio = 4.0 * 576 / (3.0 * 720);
  }
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);

  /* now find the one from par[][2] with the lowest delta to the real one */
  delta = DELTA (0);
  index = 0;

  for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
    gdouble this_delta = DELTA (i);

    if (this_delta < delta) {
      index = i;
      delta = this_delta;
    }
  }

  GST_DEBUG ("Decided on index %d (%d/%d)", index,
      par[index][0], par[index][1]);

1030
1031
1032
1033
1034
1035
1036
  g_free (xcontext->par);
  xcontext->par = g_new0 (GValue, 1);
  g_value_init (xcontext->par, GST_TYPE_FRACTION);
  gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
  GST_DEBUG ("set xcontext PAR to %d/%d",
      gst_value_get_fraction_numerator (xcontext->par),
      gst_value_get_fraction_denominator (xcontext->par));
1037
1038
}

1039
/* This function gets the X Display and global info about it. Everything is
Julien Moutte's avatar
Julien Moutte committed
1040
   stored in our object and will be cleaned when the object is disposed. Note
1041
   here that caps for supported format are generated without any window or
1042
   image creation */
Julien Moutte's avatar
Julien Moutte committed
1043
static GstXContext *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1044
gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
Julien Moutte's avatar
Julien Moutte committed
1045
1046
1047
1048
{
  GstXContext *xcontext = NULL;
  XPixmapFormatValues *px_formats = NULL;
  gint nb_formats = 0, i;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1049

1050
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1051

Julien Moutte's avatar
Julien Moutte committed
1052
  xcontext = g_new0 (GstXContext, 1);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1053

Julien Moutte's avatar
Julien Moutte committed
1054
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1055

1056
  xcontext->disp = XOpenDisplay (ximagesink->display_name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1057
1058
1059
1060

  if (!xcontext->disp) {
    g_mutex_unlock (ximagesink->x_lock);
    g_free (xcontext);
1061
1062
    GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
        ("Could not initialise X output"), ("Could not open display"));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1063
1064
1065
    return NULL;
  }

Julien Moutte's avatar
Julien Moutte committed
1066
1067
  xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
  xcontext->screen_num = DefaultScreen (xcontext->disp);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1068
  xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
Julien Moutte's avatar
Julien Moutte committed
1069
1070
1071
1072
  xcontext->root = DefaultRootWindow (xcontext->disp);
  xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
  xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
  xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1073

1074
1075
1076
1077
1078
  xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
  xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
  xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
  xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);

1079
1080
1081
  GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
      xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);

1082
1083
  gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);