ximagesink.c 61.3 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
/**
 * SECTION:element-ximagesink
 *
 * 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.
28
29
 *
 * <refsect2>
30
31
32
33
34
35
36
37
38
 * <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
39
40
41
42
 * at the #GstXImageSink:force-aspect-ratio 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.
43
 * </para>
44
45
 * </refsect2>
 * <refsect2>
46
47
48
49
50
51
52
53
54
55
56
57
 * <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>
58
59
 * </refsect2>
 * <refsect2>
60
61
62
 * <title>Pixel aspect ratio</title>
 * <para>
 * When changing state to GST_STATE_READY, XImageSink will open a connection to
63
 * the display specified in the #GstXImageSink:display property or the default
64
65
66
67
68
69
70
 * 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
71
 * #GstXImageSink:pixel-aspect-ratio property.
72
 * </para>
73
74
 * </refsect2>
 * <refsect2>
75
 * <title>Examples</title>
76
 * |[
77
 * gst-launch -v videotestsrc ! queue ! ximagesink
78
79
80
81
82
83
 * ]| A pipeline to test reverse negotiation. 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.
 * |[
84
 * gst-launch -v videotestsrc ! navigationtest ! videoconvert ! ximagesink
85
 * ]| A pipeline to test navigation events.
86
87
88
89
90
 * 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.)
91
 * |[
92
 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93
 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94
95
96
97
98
99
100
101
 * 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\)'.
 * </refsect2>
 */

Julien Moutte's avatar
Julien Moutte committed
102
103
104
105
106
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Our interfaces */
107
108
#include <gst/interfaces/navigation.h>
#include <gst/interfaces/xoverlay.h>
Julien Moutte's avatar
Julien Moutte committed
109

110
111
#include <gst/video/gstmetavideo.h>

Julien Moutte's avatar
Julien Moutte committed
112
113
114
/* Object header */
#include "ximagesink.h"

115
116
/* Debugging category */
#include <gst/gstinfo.h>
117
118

GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
Wim Taymans's avatar
Wim Taymans committed
119
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
120
121
#define GST_CAT_DEFAULT gst_debug_ximagesink

122
123
124
125
126
127
128
129
130
131
132
133
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)

134
static void gst_ximagesink_reset (GstXImageSink * ximagesink);
135
static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
136
static void gst_ximagesink_expose (GstXOverlay * overlay);
137

David Schleef's avatar
David Schleef committed
138
static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
139
140
141
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
142
    GST_STATIC_CAPS ("video/x-raw, "
143
        "framerate = (fraction) [ 0, MAX ], "
144
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145
146
147
148
    );

enum
{
149
150
151
  PROP_0,
  PROP_DISPLAY,
  PROP_SYNCHRONOUS,
152
  PROP_PIXEL_ASPECT_RATIO,
153
  PROP_FORCE_ASPECT_RATIO,
154
  PROP_HANDLE_EVENTS,
155
156
157
  PROP_HANDLE_EXPOSE,
  PROP_WINDOW_WIDTH,
  PROP_WINDOW_HEIGHT
158
159
};

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/* ============================================================= */
/*                                                               */
/*                       Public Methods                          */
/*                                                               */
/* ============================================================= */

/* =========================================== */
/*                                             */
/*          Object typing & Creation           */
/*                                             */
/* =========================================== */
static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
#define gst_ximagesink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
    G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
    G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_ximagesink_xoverlay_init));
Julien Moutte's avatar
Julien Moutte committed
177
178
179
180
181
182
183
184
185

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

/* X11 stuff */

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* 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
220
221
}

222
/* This function puts a GstXImageBuffer on a GstXImageSink's window */
223
static gboolean
224
gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
Julien Moutte's avatar
Julien Moutte committed
225
{
226
  GstMetaXImage *meta;
Wim Taymans's avatar
Wim Taymans committed
227
  GstMetaVideoCrop *crop;
228
  GstVideoRectangle src, dst, result;
229
  gboolean draw_border = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
230

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

235
236
  if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
    g_mutex_unlock (ximagesink->flow_lock);
237
    return FALSE;
238
239
  }

240
  /* Draw borders when displaying the first frame. After this
241
242
     draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
  if (!ximagesink->cur_image || ximagesink->draw_border) {
243
244
245
    draw_border = TRUE;
  }

246
  /* Store a reference to the last image we put, lose the previous one */
247
  if (ximage && ximagesink->cur_image != ximage) {
248
    if (ximagesink->cur_image) {
249
      GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
250
      gst_buffer_unref (ximagesink->cur_image);
251
    }
252
    GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
253
    ximagesink->cur_image = gst_buffer_ref (ximage);
254
255
  }

256
257
  /* Expose sends a NULL image, we take the latest frame */
  if (!ximage) {
258
    draw_border = TRUE;
259
260
261
262
    if (ximagesink->cur_image) {
      ximage = ximagesink->cur_image;
    } else {
      g_mutex_unlock (ximagesink->flow_lock);
263
      return TRUE;
264
265
266
    }
  }

267
  meta = gst_buffer_get_meta_ximage (ximage);
Wim Taymans's avatar
Wim Taymans committed
268
269
270
271
272
273
274
275
276
277
278
279
280
  crop = gst_buffer_get_meta_video_crop (ximage);

  if (crop) {
    src.x = crop->x;
    src.y = crop->y;
    src.w = crop->width;
    src.h = crop->height;
  } else {
    src.x = 0;
    src.y = 0;
    src.w = meta->width;
    src.h = meta->height;
  }
281
282
  dst.w = ximagesink->xwindow->width;
  dst.h = ximagesink->xwindow->height;
Julien Moutte's avatar
Julien Moutte committed
283

284
  gst_video_sink_center_rect (src, dst, &result, FALSE);
285

286
  g_mutex_lock (ximagesink->x_lock);
287

288
289
290
  if (draw_border) {
    gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
        result);
291
    ximagesink->draw_border = FALSE;
292
  }
293
#ifdef HAVE_XSHM
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
294
  if (ximagesink->xcontext->use_xshm) {
295
    GST_LOG_OBJECT (ximagesink,
296
297
298
        "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
299
    XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
Wim Taymans's avatar
Wim Taymans committed
300
        ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
301
        result.w, result.h, FALSE);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302
  } else
Julien Moutte's avatar
Julien Moutte committed
303
#endif /* HAVE_XSHM */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
304
  {
305
306
307
308
    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
309
    XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
Wim Taymans's avatar
Wim Taymans committed
310
        ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
311
        result.w, result.h);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
312
313
  }

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

Julien Moutte's avatar
Julien Moutte committed
316
  g_mutex_unlock (ximagesink->x_lock);
317
318

  g_mutex_unlock (ximagesink->flow_lock);
319
320

  return TRUE;
Julien Moutte's avatar
Julien Moutte committed
321
322
}

323
324
325
326
327
328
329
330
331
332
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);

333
334
  g_mutex_lock (ximagesink->x_lock);

335
336
  hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
      True);
337
  if (hints_atom == None) {
338
    g_mutex_unlock (ximagesink->x_lock);
339
340
341
    return FALSE;
  }

342
343
  hints = g_malloc0 (sizeof (MotifWmHints));

344
345
346
347
348
349
350
351
352
  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);

353
354
  g_mutex_unlock (ximagesink->x_lock);

355
356
357
358
359
  g_free (hints);

  return TRUE;
}

360
361
static void
gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
362
    GstXWindow * xwindow, const gchar * media_title)
363
364
365
366
367
{
  if (media_title) {
    g_free (ximagesink->media_title);
    ximagesink->media_title = g_strdup (media_title);
  }
368
  if (xwindow) {
369
    /* we have a window */
370
    if (xwindow->internal) {
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
      XTextProperty xproperty;
      const gchar *app_name;
      const gchar *title = NULL;
      gchar *title_mem = NULL;

      /* set application name as a title */
      app_name = g_get_application_name ();

      if (app_name && ximagesink->media_title) {
        title = title_mem = g_strconcat (ximagesink->media_title, " : ",
            app_name, NULL);
      } else if (app_name) {
        title = app_name;
      } else if (ximagesink->media_title) {
        title = ximagesink->media_title;
      }

      if (title) {
        if ((XStringListToTextProperty (((char **) &title), 1,
390
                    &xproperty)) != 0) {
391
          XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
392
393
          XFree (xproperty.value);
        }
394
395
396
397
398
399
400

        g_free (title_mem);
      }
    }
  }
}

Julien Moutte's avatar
Julien Moutte committed
401
/* This function handles a GstXWindow creation */
Julien Moutte's avatar
Julien Moutte committed
402
static GstXWindow *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403
gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
Julien Moutte's avatar
Julien Moutte committed
404
405
{
  GstXWindow *xwindow = NULL;
406
  XGCValues values;
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

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

Julien Moutte's avatar
Julien Moutte committed
412
413
  xwindow->width = width;
  xwindow->height = height;
414
  xwindow->internal = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
415

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

418
  xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
419
      ximagesink->xcontext->root,
420
      0, 0, width, height, 0, 0, ximagesink->xcontext->black);
421

422
423
424
425
  /* 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);

426
  /* set application name as a title */
427
  gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
428

429
  if (ximagesink->handle_events) {
430
431
    Atom wm_delete;

432
433
434
    XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
        StructureNotifyMask | PointerMotionMask | KeyPressMask |
        KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
435
436
437
438
439
440
441

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

444
445
  xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
      0, &values);
446

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

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

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

453
454
  gst_ximagesink_xwindow_decorate (ximagesink, xwindow);

455
  gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
456

Julien Moutte's avatar
Julien Moutte committed
457
458
459
  return xwindow;
}

Julien Moutte's avatar
Julien Moutte committed
460
/* This function destroys a GstXWindow */
Julien Moutte's avatar
Julien Moutte committed
461
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
462
463
gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
    GstXWindow * xwindow)
Julien Moutte's avatar
Julien Moutte committed
464
465
466
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
467

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

Julien Moutte's avatar
Julien Moutte committed
470
  /* If we did not create that window we just free the GC and let it live */
471
  if (xwindow->internal)
Julien Moutte's avatar
Julien Moutte committed
472
    XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
473
  else
474
    XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
475

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

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

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

Julien Moutte's avatar
Julien Moutte committed
482
483
484
  g_free (xwindow);
}

485
static void
486
gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
487
{
488
  XWindowAttributes attr;
489
  gboolean reconfigure;
490

491
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
492

493
  /* Update the window geometry */
494
  g_mutex_lock (ximagesink->x_lock);
495
496
497
498
  if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
    g_mutex_unlock (ximagesink->x_lock);
    return;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
499

500
501
  XGetWindowAttributes (ximagesink->xcontext->disp,
      ximagesink->xwindow->win, &attr);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502

503
504
505
  /* Check if we would suggest a different width/height now */
  reconfigure = (ximagesink->xwindow->width != attr.width)
      || (ximagesink->xwindow->height != attr.height);
506
507
  ximagesink->xwindow->width = attr.width;
  ximagesink->xwindow->height = attr.height;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
508

509
  g_mutex_unlock (ximagesink->x_lock);
510
511
512
513

  if (reconfigure)
    gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
        gst_event_new_reconfigure ());
514
}
515

516
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
517
gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
518
519
520
{
  g_return_if_fail (xwindow != NULL);
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
521

522
  g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
523

524
  XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
525
526
      ximagesink->xcontext->black);

527
  XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
528
529
      0, 0, xwindow->width, xwindow->height);

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

532
533
534
  g_mutex_unlock (ximagesink->x_lock);
}

Julien Moutte's avatar
Julien Moutte committed
535
536
/* This function handles XEvents that might be in the queue. It generates
   GstEvent that will be sent upstream in the pipeline to handle interactivity
537
   and navigation.*/
538
static void
539
gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
540
541
{
  XEvent e;
542
543
544
  guint pointer_x = 0, pointer_y = 0;
  gboolean pointer_moved = FALSE;
  gboolean exposed = FALSE, configured = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
545

546
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
547

548
549
550
551
552
553
554
555
  /* Then we get all pointer motion events, only the last position is
     interesting. */
  g_mutex_lock (ximagesink->flow_lock);
  g_mutex_lock (ximagesink->x_lock);
  while (XCheckWindowEvent (ximagesink->xcontext->disp,
          ximagesink->xwindow->win, PointerMotionMask, &e)) {
    g_mutex_unlock (ximagesink->x_lock);
    g_mutex_unlock (ximagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
556

557
558
559
560
561
562
563
564
565
    switch (e.type) {
      case MotionNotify:
        pointer_x = e.xmotion.x;
        pointer_y = e.xmotion.y;
        pointer_moved = TRUE;
        break;
      default:
        break;
    }
566
    g_mutex_lock (ximagesink->flow_lock);
567
    g_mutex_lock (ximagesink->x_lock);
568
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
569

570
  if (pointer_moved) {
571
    g_mutex_unlock (ximagesink->x_lock);
572
    g_mutex_unlock (ximagesink->flow_lock);
573

574
575
576
577
578
579
580
    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);

    g_mutex_lock (ximagesink->flow_lock);
    g_mutex_lock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
581
582
  }

583
584
  /* We get all remaining events on our window to throw them upstream */
  while (XCheckWindowEvent (ximagesink->xcontext->disp,
585
586
587
          ximagesink->xwindow->win,
          KeyPressMask | KeyReleaseMask |
          ButtonPressMask | ButtonReleaseMask, &e)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
588
589
590
591
    KeySym keysym;

    /* We lock only for the X function call */
    g_mutex_unlock (ximagesink->x_lock);
592
    g_mutex_unlock (ximagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
593
594
595

    switch (e.type) {
      case ButtonPress:
596
597
598
599
600
601
602
        /* 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
603
      case ButtonRelease:
604
605
606
607
608
        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
609
610
      case KeyPress:
      case KeyRelease:
611
612
613
614
        /* 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);
615
        g_mutex_lock (ximagesink->x_lock);
616
617
        keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
            e.xkey.keycode, 0);
618
        g_mutex_unlock (ximagesink->x_lock);
619
        if (keysym != NoSymbol) {
620
621
          char *key_str = NULL;

622
          g_mutex_lock (ximagesink->x_lock);
623
          key_str = XKeysymToString (keysym);
624
          g_mutex_unlock (ximagesink->x_lock);
625
626
          gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
              e.type == KeyPress ? "key-press" : "key-release", key_str);
627
628
629
630
631
        } 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
632
      default:
633
634
        GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
            e.type);
635
    }
636
    g_mutex_lock (ximagesink->flow_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
637
638
    g_mutex_lock (ximagesink->x_lock);
  }
639

640
  /* Handle Expose */
641
642
643
644
645
646
647
  while (XCheckWindowEvent (ximagesink->xcontext->disp,
          ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
    switch (e.type) {
      case Expose:
        exposed = TRUE;
        break;
      case ConfigureNotify:
648
        g_mutex_unlock (ximagesink->x_lock);
649
        gst_ximagesink_xwindow_update_geometry (ximagesink);
650
        g_mutex_lock (ximagesink->x_lock);
651
652
653
654
        configured = TRUE;
        break;
      default:
        break;
655
    }
656
657
  }

658
  if (ximagesink->handle_expose && (exposed || configured)) {
659
    g_mutex_unlock (ximagesink->x_lock);
660
    g_mutex_unlock (ximagesink->flow_lock);
661

662
663
664
    gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));

    g_mutex_lock (ximagesink->flow_lock);
665
    g_mutex_lock (ximagesink->x_lock);
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  }

  /* Handle Display events */
  while (XPending (ximagesink->xcontext->disp)) {
    XNextEvent (ximagesink->xcontext->disp, &e);

    switch (e.type) {
      case ClientMessage:{
        Atom wm_delete;

        wm_delete = XInternAtom (ximagesink->xcontext->disp,
            "WM_DELETE_WINDOW", False);
        if (wm_delete == (Atom) e.xclient.data.l[0]) {
          /* Handle window deletion by posting an error on the bus */
          GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
              ("Output window was closed"), (NULL));

          g_mutex_unlock (ximagesink->x_lock);
          gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
          ximagesink->xwindow = NULL;
          g_mutex_lock (ximagesink->x_lock);
        }
        break;
      }
      default:
        break;
692
693
    }
  }
694
695
696

  g_mutex_unlock (ximagesink->x_lock);
  g_mutex_unlock (ximagesink->flow_lock);
697
698
699
700
701
702
703
}

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

704
  GST_OBJECT_LOCK (ximagesink);
705
  while (ximagesink->running) {
706
707
    GST_OBJECT_UNLOCK (ximagesink);

708
709
710
    if (ximagesink->xwindow) {
      gst_ximagesink_handle_xevents (ximagesink);
    }
711
712
    /* FIXME: do we want to align this with the framerate or anything else? */
    g_usleep (G_USEC_PER_SEC / 20);
713
714

    GST_OBJECT_LOCK (ximagesink);
715
  }
716
  GST_OBJECT_UNLOCK (ximagesink);
717
718

  return NULL;
719
}
Julien Moutte's avatar
Julien Moutte committed
720

721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
static void
gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
{
  GThread *thread = NULL;

  /* don't start the thread too early */
  if (ximagesink->xcontext == NULL) {
    return;
  }

  GST_OBJECT_LOCK (ximagesink);
  if (ximagesink->handle_expose || ximagesink->handle_events) {
    if (!ximagesink->event_thread) {
      /* Setup our event listening thread */
      GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
          ximagesink->handle_expose, ximagesink->handle_events);
      ximagesink->running = TRUE;
      ximagesink->event_thread = g_thread_create (
          (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
    }
  } else {
    if (ximagesink->event_thread) {
      GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
          ximagesink->handle_expose, ximagesink->handle_events);
      ximagesink->running = FALSE;
      /* grab thread and mark it as NULL */
      thread = ximagesink->event_thread;
      ximagesink->event_thread = NULL;
    }
  }
  GST_OBJECT_UNLOCK (ximagesink);

  /* Wait for our event thread to finish */
  if (thread)
    g_thread_join (thread);

}


760
761
762
763
764
/* 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)
{
Wim Taymans's avatar
Wim Taymans committed
765
  static const gint par[][2] = {
766
767
768
    {1, 1},                     /* regular screen */
    {16, 15},                   /* PAL TV */
    {11, 10},                   /* 525 line Rec.601 video */
769
770
771
772
    {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 */
773
774
775
776
777
778
779
780
781
782
  };
  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 */
783
  ratio = (gdouble) (xcontext->widthmm * xcontext->height)
784
      / (xcontext->heightmm * xcontext->width);
785
786
787
788
789
790

  /* 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);
  }
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  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]);

809
810
811
812
813
814
815
  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));
816
817
}

818
/* This function gets the X Display and global info about it. Everything is
Julien Moutte's avatar
Julien Moutte committed
819
   stored in our object and will be cleaned when the object is disposed. Note
820
   here that caps for supported format are generated without any window or
821
   image creation */
Julien Moutte's avatar
Julien Moutte committed
822
static GstXContext *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
823
gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
Julien Moutte's avatar
Julien Moutte committed
824
825
826
827
{
  GstXContext *xcontext = NULL;
  XPixmapFormatValues *px_formats = NULL;
  gint nb_formats = 0, i;
828
829
  gint endianness;
  GstVideoFormat vformat;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
830

831
  g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
832

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

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

837
  xcontext->disp = XOpenDisplay (ximagesink->display_name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
838
839
840
841

  if (!xcontext->disp) {
    g_mutex_unlock (ximagesink->x_lock);
    g_free (xcontext);
842
843
    GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
        ("Could not initialise X output"), ("Could not open display"));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
844
845
846
    return NULL;
  }

Julien Moutte's avatar
Julien Moutte committed
847
848
  xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
  xcontext->screen_num = DefaultScreen (xcontext->disp);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
849
  xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
Julien Moutte's avatar
Julien Moutte committed
850
851
852
853
  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
854

855
856
857
858
859
  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);

860
861
862
  GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
      xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);

863
864
  gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);

Julien Moutte's avatar
Julien Moutte committed
865
866
  /* We get supported pixmap formats at supported depth */
  px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
867
868
869
870

  if (!px_formats) {
    XCloseDisplay (xcontext->disp);
    g_mutex_unlock (ximagesink->x_lock);
871
    g_free (xcontext->par);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
872
    g_free (xcontext);
873
    GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
874
        ("Could not get supported pixmap formats"), (NULL));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
875
876
877
    return NULL;
  }

Julien Moutte's avatar
Julien Moutte committed
878
  /* We get bpp value corresponding to our running depth */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
879
880
881
882
883
  for (i = 0; i < nb_formats; i++) {
    if (px_formats[i].depth == xcontext->depth)
      xcontext->bpp = px_formats[i].bits_per_pixel;
  }

Julien Moutte's avatar
Julien Moutte committed
884
  XFree (px_formats);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
885

886
  endianness = (ImageByteOrder (xcontext->disp) ==
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
887
888
      LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;

Julien Moutte's avatar
Julien Moutte committed
889
  /* Search for XShm extension support */
890
#ifdef HAVE_XSHM
Julien Moutte's avatar
Julien Moutte committed
891
  if (XShmQueryExtension (xcontext->disp) &&
892
      gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
893
894
    xcontext->use_xshm = TRUE;
    GST_DEBUG ("ximagesink is using XShm extension");
895
  } else
896
#endif /* HAVE_XSHM */
897
  {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
898
899
900
    xcontext->use_xshm = FALSE;
    GST_DEBUG ("ximagesink is not using XShm extension");
  }
901

902
903
904
905
906
907
  vformat = gst_video_format_from_masks (xcontext->depth, xcontext->bpp,
      endianness, xcontext->visual->red_mask, xcontext->visual->green_mask,
      xcontext->visual->blue_mask, 0);

  if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
    goto unknown_format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
908

909
910
911
912
913
914
  /* update object's par with calculated one if not set yet */
  if (!ximagesink->par) {
    ximagesink->par = g_new0 (GValue, 1);
    gst_value_init_and_copy (ximagesink->par, xcontext->par);
    GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
  }
915
916
  xcontext->caps = gst_caps_new_simple ("video/x-raw",
      "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
917
918
      "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
      "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
919
      "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
920
921
922
923
924
925
926
927
  if (ximagesink->par) {
    int nom, den;

    nom = gst_value_get_fraction_numerator (ximagesink->par);
    den = gst_value_get_fraction_denominator (ximagesink->par);
    gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
        GST_TYPE_FRACTION, nom, den, NULL);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
928

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

Julien Moutte's avatar
Julien Moutte committed
931
  return xcontext;
932
933
934
935
936
937
938

  /* ERRORS */
unknown_format:
  {
    GST_ERROR_OBJECT (ximagesink, "unknown format");
    return NULL;
  }
Julien Moutte's avatar
Julien Moutte committed
939
940
941
942
943
}

/* This function cleans the X context. Closing the Display and unrefing the
   caps for supported formats. */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
944
gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
Julien Moutte's avatar
Julien Moutte committed
945
{
946
947
  GstXContext *xcontext;

Julien Moutte's avatar
Julien Moutte committed
948
  g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
949
950
951
952
953
954
955
956
957
958
959
960
961
962

  GST_OBJECT_LOCK (ximagesink);
  if (ximagesink->xcontext == NULL) {
    GST_OBJECT_UNLOCK (ximagesink);
    return;
  }

  /* Take the xcontext reference and NULL it while we
   * clean it up, so that any buffer-alloced buffers 
   * arriving after this will be freed correctly */
  xcontext = ximagesink->xcontext;
  ximagesink->xcontext = NULL;

  GST_OBJECT_UNLOCK (ximagesink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
963

964
965
  gst_caps_unref (xcontext->caps);
  g_free (xcontext->par);
966
967
  g_free (ximagesink->par);
  ximagesink->par = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
968

969
970
971
  if (xcontext->last_caps)
    gst_caps_replace (&xcontext->last_caps, NULL);

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

974
  XCloseDisplay (xcontext->disp);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
975

976
  g_mutex_unlock (ximagesink->x_lock);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
977

978
  g_free (xcontext);
Julien Moutte's avatar
Julien Moutte committed
979
980
}

981
982
983
/* Element stuff */

static GstCaps *
984
gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
Julien Moutte's avatar
Julien Moutte committed
985
986
{
  GstXImageSink *ximagesink;
987
988
  GstCaps *caps;
  int i;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
989

990
  ximagesink = GST_XIMAGESINK (bsink);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
991

992
993
  g_mutex_lock (ximagesink->x_lock);
  if (ximagesink->xcontext) {
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
    GstCaps *caps;

    caps = gst_caps_ref (ximagesink->xcontext->caps);

    if (filter) {
      GstCaps *intersection;

      intersection =
          gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
      gst_caps_unref (caps);
      caps = intersection;
    }

1007
1008
1009
    if (ximagesink->xwindow && ximagesink->xwindow->width) {
      GstStructure *s0, *s1;

1010
      caps = gst_caps_make_writable (caps);
1011

1012
1013
      /* There can only be a single structure because the xcontext
       * caps only have a single structure */
1014
      s0 = gst_caps_get_structure (caps, 0);
1015
1016
      s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));

1017
1018
1019
      gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
          "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
      gst_caps_append_structure (caps, s1);
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031

      /* This will not change the order but will remove the
       * fixed width/height caps again if not possible
       * upstream */
      if (filter) {
        GstCaps *intersection;

        intersection =
            gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
        gst_caps_unref (caps);
        caps = intersection;
      }
1032
    }
1033
1034
1035

    g_mutex_unlock (ximagesink->x_lock);
    return caps;
1036
1037
  }
  g_mutex_unlock (ximagesink->x_lock);
David Schleef's avatar
David Schleef committed
1038

1039
  /* get a template copy and add the pixel aspect ratio */
1040
1041
1042
1043
1044
  caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
  if (ximagesink->par) {
    caps = gst_caps_make_writable (caps);
    for (i = 0; i < gst_caps_get_size (caps); ++i) {
      GstStructure *structure = gst_caps_get_structure (caps, i);
1045
1046
1047
1048
1049
1050
1051
      int nom, den;

      nom = gst_value_get_fraction_numerator (ximagesink->par);
      den = gst_value_get_fraction_denominator (ximagesink->par);
      gst_structure_set (structure, "pixel-aspect-ratio",
          GST_TYPE_FRACTION, nom, den, NULL);
    }
1052
  }
1053

1054
1055
1056
1057
1058
1059
1060
1061
1062
  if (filter) {
    GstCaps *intersection;

    intersection =
        gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
    gst_caps_unref (caps);
    caps = intersection;
  }

Thomas Vander Stichele's avatar