~ubuntu-branches/ubuntu/quantal/gst-plugins-bad-multiverse0.10/quantal

« back to all changes in this revision

Viewing changes to sys/vdpau/gstvdpsink.c

  • Committer: Bazaar Package Importer
  • Author(s): Onkar Shinde
  • Date: 2009-12-07 08:54:28 UTC
  • mfrom: (1.1.15 upstream)
  • Revision ID: james.westby@ubuntu.com-20091207085428-ml6aaukf0p2ph34d
Tags: 0.10.17-0ubuntu1
* New upstream release.
* Add myself to maintainer.
* Fix misc lintian warnings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GStreamer
 
2
 * Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>
 
3
 * Copyright (C) 2005 Julien Moutte <julien@moutte.net>
 
4
 *
 
5
 * This library is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU Library General Public
 
7
 * License as published by the Free Software Foundation; either
 
8
 * version 2 of the License, or (at your option) any later version.
 
9
 *
 
10
 * This library is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * Library General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU Library General Public
 
16
 * License along with this library; if not, write to the
 
17
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
18
 * Boston, MA 02111-1307, USA.
 
19
 */
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include "config.h"
 
23
#endif
 
24
 
 
25
/* Our interfaces */
 
26
#include <gst/interfaces/navigation.h>
 
27
#include <gst/interfaces/xoverlay.h>
 
28
 
 
29
/* Debugging category */
 
30
#include <gst/gstinfo.h>
 
31
 
 
32
#include "gstvdpoutputbuffer.h"
 
33
 
 
34
/* Object header */
 
35
#include "gstvdpsink.h"
 
36
 
 
37
GST_DEBUG_CATEGORY_STATIC (gst_vdp_sink_debug);
 
38
#define GST_CAT_DEFAULT gst_vdp_sink_debug
 
39
 
 
40
typedef struct
 
41
{
 
42
  unsigned long flags;
 
43
  unsigned long functions;
 
44
  unsigned long decorations;
 
45
  long input_mode;
 
46
  unsigned long status;
 
47
}
 
48
MotifWmHints, MwmHints;
 
49
 
 
50
#define MWM_HINTS_DECORATIONS   (1L << 1)
 
51
 
 
52
static void gst_vdp_sink_expose (GstXOverlay * overlay);
 
53
 
 
54
enum
 
55
{
 
56
  PROP_0,
 
57
  PROP_DISPLAY,
 
58
  PROP_SYNCHRONOUS,
 
59
  PROP_PIXEL_ASPECT_RATIO,
 
60
  PROP_HANDLE_EVENTS,
 
61
  PROP_HANDLE_EXPOSE
 
62
};
 
63
 
 
64
static GstVideoSinkClass *parent_class = NULL;
 
65
 
 
66
/* the capabilities of the inputs and outputs.
 
67
 *
 
68
 * describe the real formats here.
 
69
 */
 
70
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
 
71
    GST_PAD_SINK,
 
72
    GST_PAD_ALWAYS,
 
73
    GST_STATIC_CAPS (GST_VDP_OUTPUT_CAPS));
 
74
 
 
75
#define DEBUG_INIT(bla) \
 
76
GST_DEBUG_CATEGORY_INIT (gst_vdp_sink_debug, "vdpausink", 0, "VDPAU video sink");
 
77
 
 
78
/* ============================================================= */
 
79
/*                                                               */
 
80
/*                       Private Methods                         */
 
81
/*                                                               */
 
82
/* ============================================================= */
 
83
 
 
84
/* X11 stuff */
 
85
 
 
86
static gboolean
 
87
gst_vdp_sink_window_decorate (VdpSink * vdp_sink, GstVdpWindow * window)
 
88
{
 
89
  Atom hints_atom = None;
 
90
  MotifWmHints *hints;
 
91
 
 
92
  g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), FALSE);
 
93
  g_return_val_if_fail (window != NULL, FALSE);
 
94
 
 
95
  g_mutex_lock (vdp_sink->x_lock);
 
96
 
 
97
  hints_atom = XInternAtom (vdp_sink->device->display, "_MOTIF_WM_HINTS", 1);
 
98
  if (hints_atom == None) {
 
99
    g_mutex_unlock (vdp_sink->x_lock);
 
100
    return FALSE;
 
101
  }
 
102
 
 
103
  hints = g_malloc0 (sizeof (MotifWmHints));
 
104
 
 
105
  hints->flags |= MWM_HINTS_DECORATIONS;
 
106
  hints->decorations = 1 << 0;
 
107
 
 
108
  XChangeProperty (vdp_sink->device->display, window->win,
 
109
      hints_atom, hints_atom, 32, PropModeReplace,
 
110
      (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
 
111
 
 
112
  XSync (vdp_sink->device->display, FALSE);
 
113
 
 
114
  g_mutex_unlock (vdp_sink->x_lock);
 
115
 
 
116
  g_free (hints);
 
117
 
 
118
  return TRUE;
 
119
}
 
120
 
 
121
static void
 
122
gst_vdp_sink_window_set_title (VdpSink * vdp_sink,
 
123
    GstVdpWindow * window, const gchar * media_title)
 
124
{
 
125
  if (media_title) {
 
126
    g_free (vdp_sink->media_title);
 
127
    vdp_sink->media_title = g_strdup (media_title);
 
128
  }
 
129
  if (window) {
 
130
    /* we have a window */
 
131
    if (window->internal) {
 
132
      XTextProperty xproperty;
 
133
      const gchar *app_name;
 
134
      const gchar *title = NULL;
 
135
      gchar *title_mem = NULL;
 
136
 
 
137
      /* set application name as a title */
 
138
      app_name = g_get_application_name ();
 
139
 
 
140
      if (app_name && vdp_sink->media_title) {
 
141
        title = title_mem = g_strconcat (vdp_sink->media_title, " : ",
 
142
            app_name, NULL);
 
143
      } else if (app_name) {
 
144
        title = app_name;
 
145
      } else if (vdp_sink->media_title) {
 
146
        title = vdp_sink->media_title;
 
147
      }
 
148
 
 
149
      if (title) {
 
150
        if ((XStringListToTextProperty (((char **) &title), 1,
 
151
                    &xproperty)) != 0)
 
152
          XSetWMName (vdp_sink->device->display, window->win, &xproperty);
 
153
 
 
154
        g_free (title_mem);
 
155
      }
 
156
    }
 
157
  }
 
158
}
 
159
 
 
160
/* This function handles a GstVdpWindow creation */
 
161
static GstVdpWindow *
 
162
gst_vdp_sink_window_new (VdpSink * vdp_sink, gint width, gint height)
 
163
{
 
164
  GstVdpWindow *window = NULL;
 
165
  GstVdpDevice *device = vdp_sink->device;
 
166
 
 
167
  Window root;
 
168
  gint screen_num;
 
169
  gulong black;
 
170
 
 
171
  VdpStatus status;
 
172
  VdpColor color = { 0, };
 
173
 
 
174
  g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), NULL);
 
175
 
 
176
  window = g_new0 (GstVdpWindow, 1);
 
177
 
 
178
  window->width = width;
 
179
  window->height = height;
 
180
  window->internal = TRUE;
 
181
 
 
182
  g_mutex_lock (vdp_sink->x_lock);
 
183
 
 
184
  screen_num = DefaultScreen (device->display);
 
185
  root = DefaultRootWindow (device->display);
 
186
  black = XBlackPixel (device->display, screen_num);
 
187
 
 
188
  window->win = XCreateSimpleWindow (vdp_sink->device->display,
 
189
      root, 0, 0, window->width, window->height, 0, 0, black);
 
190
 
 
191
  /* We have to do that to prevent X from redrawing the background on 
 
192
     ConfigureNotify. This takes away flickering of video when resizing. */
 
193
  XSetWindowBackgroundPixmap (vdp_sink->device->display, window->win, None);
 
194
 
 
195
  /* set application name as a title */
 
196
  gst_vdp_sink_window_set_title (vdp_sink, window, NULL);
 
197
 
 
198
  if (vdp_sink->handle_events) {
 
199
    Atom wm_delete;
 
200
 
 
201
    XSelectInput (vdp_sink->device->display, window->win, ExposureMask |
 
202
        StructureNotifyMask | PointerMotionMask | KeyPressMask |
 
203
        KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
 
204
 
 
205
    /* Tell the window manager we'd like delete client messages instead of
 
206
     * being killed */
 
207
    wm_delete =
 
208
        XInternAtom (vdp_sink->device->display, "WM_DELETE_WINDOW", False);
 
209
    (void) XSetWMProtocols (vdp_sink->device->display, window->win, &wm_delete,
 
210
        1);
 
211
  }
 
212
 
 
213
  XMapRaised (vdp_sink->device->display, window->win);
 
214
 
 
215
  XSync (vdp_sink->device->display, FALSE);
 
216
 
 
217
  g_mutex_unlock (vdp_sink->x_lock);
 
218
 
 
219
  gst_vdp_sink_window_decorate (vdp_sink, window);
 
220
 
 
221
  status = device->vdp_presentation_queue_target_create_x11 (device->device,
 
222
      window->win, &window->target);
 
223
  if (status != VDP_STATUS_OK) {
 
224
    GST_ELEMENT_ERROR (vdp_sink, RESOURCE, READ,
 
225
        ("Could not create presentation target"),
 
226
        ("Error returned from vdpau was: %s",
 
227
            device->vdp_get_error_string (status)));
 
228
  }
 
229
 
 
230
  status =
 
231
      device->vdp_presentation_queue_create (device->device, window->target,
 
232
      &window->queue);
 
233
  if (status != VDP_STATUS_OK) {
 
234
    GST_ELEMENT_ERROR (vdp_sink, RESOURCE, READ,
 
235
        ("Could not create presentation queue"),
 
236
        ("Error returned from vdpau was: %s",
 
237
            device->vdp_get_error_string (status)));
 
238
  }
 
239
 
 
240
  status =
 
241
      device->vdp_presentation_queue_set_background_color (window->queue,
 
242
      &color);
 
243
  if (status != VDP_STATUS_OK) {
 
244
    GST_ELEMENT_ERROR (vdp_sink, RESOURCE, READ,
 
245
        ("Could not set background color"),
 
246
        ("Error returned from vdpau was: %s",
 
247
            device->vdp_get_error_string (status)));
 
248
  }
 
249
 
 
250
  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (vdp_sink), window->win);
 
251
 
 
252
  return window;
 
253
}
 
254
 
 
255
/* This function destroys a GstVdpWindow */
 
256
static void
 
257
gst_vdp_sink_window_destroy (VdpSink * vdp_sink, GstVdpWindow * window)
 
258
{
 
259
  g_return_if_fail (window != NULL);
 
260
  g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
 
261
 
 
262
  g_mutex_lock (vdp_sink->x_lock);
 
263
 
 
264
  /* If we did not create that window we just free the GC and let it live */
 
265
  if (window->internal)
 
266
    XDestroyWindow (vdp_sink->device->display, window->win);
 
267
  else
 
268
    XSelectInput (vdp_sink->device->display, window->win, 0);
 
269
 
 
270
  XSync (vdp_sink->device->display, FALSE);
 
271
 
 
272
  g_mutex_unlock (vdp_sink->x_lock);
 
273
 
 
274
  g_free (window);
 
275
}
 
276
 
 
277
static void
 
278
gst_vdp_sink_window_update_geometry (VdpSink * vdp_sink, GstVdpWindow * window)
 
279
{
 
280
  XWindowAttributes attr;
 
281
 
 
282
  g_return_if_fail (window != NULL);
 
283
  g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
 
284
 
 
285
  /* Update the window geometry */
 
286
  g_mutex_lock (vdp_sink->x_lock);
 
287
 
 
288
  XGetWindowAttributes (vdp_sink->device->display, window->win, &attr);
 
289
 
 
290
  window->width = attr.width;
 
291
  window->height = attr.height;
 
292
 
 
293
  g_mutex_unlock (vdp_sink->x_lock);
 
294
}
 
295
 
 
296
/* This function handles XEvents that might be in the queue. It generates
 
297
   GstEvent that will be sent upstream in the pipeline to handle interactivity
 
298
   and navigation.*/
 
299
static void
 
300
gst_vdp_sink_handle_xevents (VdpSink * vdp_sink)
 
301
{
 
302
  XEvent e;
 
303
  guint pointer_x = 0, pointer_y = 0;
 
304
  gboolean pointer_moved = FALSE;
 
305
  gboolean exposed = FALSE, configured = FALSE;
 
306
 
 
307
  g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
 
308
 
 
309
  /* Then we get all pointer motion events, only the last position is
 
310
     interesting. */
 
311
  g_mutex_lock (vdp_sink->flow_lock);
 
312
  g_mutex_lock (vdp_sink->x_lock);
 
313
  while (XCheckWindowEvent (vdp_sink->device->display,
 
314
          vdp_sink->window->win, PointerMotionMask, &e)) {
 
315
    g_mutex_unlock (vdp_sink->x_lock);
 
316
    g_mutex_unlock (vdp_sink->flow_lock);
 
317
 
 
318
    switch (e.type) {
 
319
      case MotionNotify:
 
320
        pointer_x = e.xmotion.x;
 
321
        pointer_y = e.xmotion.y;
 
322
        pointer_moved = TRUE;
 
323
        break;
 
324
      default:
 
325
        break;
 
326
    }
 
327
    g_mutex_lock (vdp_sink->flow_lock);
 
328
    g_mutex_lock (vdp_sink->x_lock);
 
329
  }
 
330
 
 
331
  if (pointer_moved) {
 
332
    g_mutex_unlock (vdp_sink->x_lock);
 
333
    g_mutex_unlock (vdp_sink->flow_lock);
 
334
 
 
335
    GST_DEBUG ("vdp_sink pointer moved over window at %d,%d",
 
336
        pointer_x, pointer_y);
 
337
    gst_navigation_send_mouse_event (GST_NAVIGATION (vdp_sink),
 
338
        "mouse-move", 0, pointer_x, pointer_y);
 
339
 
 
340
    g_mutex_lock (vdp_sink->flow_lock);
 
341
    g_mutex_lock (vdp_sink->x_lock);
 
342
  }
 
343
 
 
344
  /* We get all remaining events on our window to throw them upstream */
 
345
  while (XCheckWindowEvent (vdp_sink->device->display,
 
346
          vdp_sink->window->win,
 
347
          KeyPressMask | KeyReleaseMask |
 
348
          ButtonPressMask | ButtonReleaseMask, &e)) {
 
349
    KeySym keysym;
 
350
 
 
351
    /* We lock only for the X function call */
 
352
    g_mutex_unlock (vdp_sink->x_lock);
 
353
    g_mutex_unlock (vdp_sink->flow_lock);
 
354
 
 
355
    switch (e.type) {
 
356
      case ButtonPress:
 
357
        /* Mouse button pressed/released over our window. We send upstream
 
358
           events for interactivity/navigation */
 
359
        GST_DEBUG ("vdp_sink button %d pressed over window at %d,%d",
 
360
            e.xbutton.button, e.xbutton.x, e.xbutton.x);
 
361
        gst_navigation_send_mouse_event (GST_NAVIGATION (vdp_sink),
 
362
            "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
 
363
        break;
 
364
      case ButtonRelease:
 
365
        GST_DEBUG ("vdp_sink button %d release over window at %d,%d",
 
366
            e.xbutton.button, e.xbutton.x, e.xbutton.x);
 
367
        gst_navigation_send_mouse_event (GST_NAVIGATION (vdp_sink),
 
368
            "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
 
369
        break;
 
370
      case KeyPress:
 
371
      case KeyRelease:
 
372
        /* Key pressed/released over our window. We send upstream
 
373
           events for interactivity/navigation */
 
374
        GST_DEBUG ("vdp_sink key %d pressed over window at %d,%d",
 
375
            e.xkey.keycode, e.xkey.x, e.xkey.x);
 
376
        g_mutex_lock (vdp_sink->x_lock);
 
377
        keysym =
 
378
            XKeycodeToKeysym (vdp_sink->device->display, e.xkey.keycode, 0);
 
379
        g_mutex_unlock (vdp_sink->x_lock);
 
380
        if (keysym != NoSymbol) {
 
381
          char *key_str = NULL;
 
382
 
 
383
          g_mutex_lock (vdp_sink->x_lock);
 
384
          key_str = XKeysymToString (keysym);
 
385
          g_mutex_unlock (vdp_sink->x_lock);
 
386
          gst_navigation_send_key_event (GST_NAVIGATION (vdp_sink),
 
387
              e.type == KeyPress ? "key-press" : "key-release", key_str);
 
388
 
 
389
        } else {
 
390
          gst_navigation_send_key_event (GST_NAVIGATION (vdp_sink),
 
391
              e.type == KeyPress ? "key-press" : "key-release", "unknown");
 
392
        }
 
393
        break;
 
394
      default:
 
395
        GST_DEBUG_OBJECT (vdp_sink, "vdp_sink unhandled X event (%d)", e.type);
 
396
    }
 
397
    g_mutex_lock (vdp_sink->flow_lock);
 
398
    g_mutex_lock (vdp_sink->x_lock);
 
399
  }
 
400
 
 
401
  while (XCheckWindowEvent (vdp_sink->device->display,
 
402
          vdp_sink->window->win, ExposureMask | StructureNotifyMask, &e)) {
 
403
    switch (e.type) {
 
404
      case Expose:
 
405
        exposed = TRUE;
 
406
        break;
 
407
      case ConfigureNotify:
 
408
        configured = TRUE;
 
409
        break;
 
410
      default:
 
411
        break;
 
412
    }
 
413
  }
 
414
 
 
415
  if (vdp_sink->handle_expose && (exposed || configured)) {
 
416
    g_mutex_unlock (vdp_sink->x_lock);
 
417
    g_mutex_unlock (vdp_sink->flow_lock);
 
418
 
 
419
    gst_vdp_sink_expose (GST_X_OVERLAY (vdp_sink));
 
420
 
 
421
    g_mutex_lock (vdp_sink->flow_lock);
 
422
    g_mutex_lock (vdp_sink->x_lock);
 
423
  }
 
424
 
 
425
  /* Handle Display events */
 
426
  while (XPending (vdp_sink->device->display)) {
 
427
    XNextEvent (vdp_sink->device->display, &e);
 
428
 
 
429
    switch (e.type) {
 
430
      case ClientMessage:{
 
431
        Atom wm_delete;
 
432
 
 
433
        wm_delete = XInternAtom (vdp_sink->device->display,
 
434
            "WM_DELETE_WINDOW", False);
 
435
        if (wm_delete == (Atom) e.xclient.data.l[0]) {
 
436
          /* Handle window deletion by posting an error on the bus */
 
437
          GST_ELEMENT_ERROR (vdp_sink, RESOURCE, NOT_FOUND,
 
438
              ("Output window was closed"), (NULL));
 
439
 
 
440
          g_mutex_unlock (vdp_sink->x_lock);
 
441
          gst_vdp_sink_window_destroy (vdp_sink, vdp_sink->window);
 
442
          vdp_sink->window = NULL;
 
443
          g_mutex_lock (vdp_sink->x_lock);
 
444
        }
 
445
        break;
 
446
      }
 
447
      default:
 
448
        break;
 
449
    }
 
450
  }
 
451
 
 
452
  g_mutex_unlock (vdp_sink->x_lock);
 
453
  g_mutex_unlock (vdp_sink->flow_lock);
 
454
}
 
455
 
 
456
static gpointer
 
457
gst_vdp_sink_event_thread (VdpSink * vdp_sink)
 
458
{
 
459
  g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), NULL);
 
460
 
 
461
  GST_OBJECT_LOCK (vdp_sink);
 
462
  while (vdp_sink->running) {
 
463
    GST_OBJECT_UNLOCK (vdp_sink);
 
464
 
 
465
    if (vdp_sink->window) {
 
466
      gst_vdp_sink_handle_xevents (vdp_sink);
 
467
    }
 
468
    g_usleep (100000);
 
469
 
 
470
    GST_OBJECT_LOCK (vdp_sink);
 
471
  }
 
472
  GST_OBJECT_UNLOCK (vdp_sink);
 
473
 
 
474
  return NULL;
 
475
}
 
476
 
 
477
/* This function calculates the pixel aspect ratio */
 
478
static GValue *
 
479
gst_vdp_sink_calculate_par (Display * display)
 
480
{
 
481
  static const gint par[][2] = {
 
482
    {1, 1},                     /* regular screen */
 
483
    {16, 15},                   /* PAL TV */
 
484
    {11, 10},                   /* 525 line Rec.601 video */
 
485
    {54, 59},                   /* 625 line Rec.601 video */
 
486
    {64, 45},                   /* 1280x1024 on 16:9 display */
 
487
    {5, 3},                     /* 1280x1024 on 4:3 display */
 
488
    {4, 3}                      /*  800x600 on 16:9 display */
 
489
  };
 
490
  gint screen_num;
 
491
  gint width, height;
 
492
  gint widthmm, heightmm;
 
493
  gint i;
 
494
  gint index;
 
495
  gdouble ratio;
 
496
  gdouble delta;
 
497
  GValue *par_value;
 
498
 
 
499
#define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
 
500
 
 
501
  screen_num = DefaultScreen (display);
 
502
  width = DisplayWidth (display, screen_num);
 
503
  height = DisplayHeight (display, screen_num);
 
504
  widthmm = DisplayWidthMM (display, screen_num);
 
505
  heightmm = DisplayHeightMM (display, screen_num);
 
506
 
 
507
  /* first calculate the "real" ratio based on the X values;
 
508
   * which is the "physical" w/h divided by the w/h in pixels of the display */
 
509
  ratio = (gdouble) (widthmm * height)
 
510
      / (heightmm * width);
 
511
 
 
512
  /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
 
513
   * override here */
 
514
  if (width == 720 && height == 576) {
 
515
    ratio = 4.0 * 576 / (3.0 * 720);
 
516
  }
 
517
  GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
 
518
 
 
519
  /* now find the one from par[][2] with the lowest delta to the real one */
 
520
  delta = DELTA (0);
 
521
  index = 0;
 
522
 
 
523
  for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
 
524
    gdouble this_delta = DELTA (i);
 
525
 
 
526
    if (this_delta < delta) {
 
527
      index = i;
 
528
      delta = this_delta;
 
529
    }
 
530
  }
 
531
 
 
532
  GST_DEBUG ("Decided on index %d (%d/%d)", index,
 
533
      par[index][0], par[index][1]);
 
534
 
 
535
  par_value = g_new0 (GValue, 1);
 
536
  g_value_init (par_value, GST_TYPE_FRACTION);
 
537
  gst_value_set_fraction (par_value, par[index][0], par[index][1]);
 
538
  GST_DEBUG ("set X11 PAR to %d/%d",
 
539
      gst_value_get_fraction_numerator (par_value),
 
540
      gst_value_get_fraction_denominator (par_value));
 
541
 
 
542
  return par_value;
 
543
}
 
544
 
 
545
static GstCaps *
 
546
gst_vdp_sink_get_allowed_caps (GstVdpDevice * device, GValue * par)
 
547
{
 
548
  GstCaps *caps;
 
549
  gint i;
 
550
 
 
551
  caps = gst_vdp_output_buffer_get_allowed_caps (device);
 
552
 
 
553
  if (!par)
 
554
    par = gst_vdp_sink_calculate_par (device->display);
 
555
 
 
556
  for (i = 0; i < gst_caps_get_size (caps); i++) {
 
557
    GstStructure *structure;
 
558
 
 
559
    structure = gst_caps_get_structure (caps, i);
 
560
    gst_structure_set_value (structure, "pixel-aspect-ratio", par);
 
561
  }
 
562
 
 
563
  return caps;
 
564
}
 
565
 
 
566
static GstVdpDevice *
 
567
gst_vdp_sink_setup_device (VdpSink * vdp_sink)
 
568
{
 
569
  GstVdpDevice *device;
 
570
 
 
571
  device = gst_vdp_get_device (vdp_sink->display_name);
 
572
  if (!device)
 
573
    return NULL;
 
574
 
 
575
  vdp_sink->caps = gst_vdp_sink_get_allowed_caps (device, vdp_sink->par);
 
576
 
 
577
  /* call XSynchronize with the current value of synchronous */
 
578
  GST_DEBUG_OBJECT (vdp_sink, "XSynchronize called with %s",
 
579
      vdp_sink->synchronous ? "TRUE" : "FALSE");
 
580
  XSynchronize (device->display, vdp_sink->synchronous);
 
581
 
 
582
  /* Setup our event listening thread */
 
583
  vdp_sink->running = TRUE;
 
584
  vdp_sink->event_thread = g_thread_create (
 
585
      (GThreadFunc) gst_vdp_sink_event_thread, vdp_sink, TRUE, NULL);
 
586
 
 
587
  return device;
 
588
}
 
589
 
 
590
static gboolean
 
591
gst_vdp_sink_start (GstBaseSink * bsink)
 
592
{
 
593
  VdpSink *vdp_sink = GST_VDP_SINK (bsink);
 
594
  gboolean res = TRUE;
 
595
 
 
596
  vdp_sink->window = NULL;
 
597
  vdp_sink->cur_image = NULL;
 
598
 
 
599
  vdp_sink->event_thread = NULL;
 
600
 
 
601
  vdp_sink->fps_n = 0;
 
602
  vdp_sink->fps_d = 1;
 
603
 
 
604
  GST_OBJECT_LOCK (vdp_sink);
 
605
  if (!vdp_sink->device) {
 
606
    if (!(vdp_sink->device = gst_vdp_sink_setup_device (vdp_sink)))
 
607
      res = FALSE;
 
608
  }
 
609
  GST_OBJECT_UNLOCK (vdp_sink);
 
610
 
 
611
  return res;
 
612
}
 
613
 
 
614
static void
 
615
gst_vdp_device_clear (VdpSink * vdp_sink)
 
616
{
 
617
  g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
 
618
 
 
619
  GST_OBJECT_LOCK (vdp_sink);
 
620
  if (vdp_sink->device == NULL) {
 
621
    GST_OBJECT_UNLOCK (vdp_sink);
 
622
    return;
 
623
  }
 
624
  GST_OBJECT_UNLOCK (vdp_sink);
 
625
 
 
626
  g_mutex_lock (vdp_sink->x_lock);
 
627
 
 
628
  g_object_unref (vdp_sink->device);
 
629
  vdp_sink->device = NULL;
 
630
 
 
631
  g_mutex_unlock (vdp_sink->x_lock);
 
632
}
 
633
 
 
634
static gboolean
 
635
gst_vdp_sink_stop (GstBaseSink * bsink)
 
636
{
 
637
  VdpSink *vdp_sink = GST_VDP_SINK (bsink);
 
638
 
 
639
  vdp_sink->running = FALSE;
 
640
  /* Wait for our event thread to finish before we clean up our stuff. */
 
641
  if (vdp_sink->event_thread)
 
642
    g_thread_join (vdp_sink->event_thread);
 
643
 
 
644
  if (vdp_sink->cur_image) {
 
645
    gst_buffer_unref (GST_BUFFER_CAST (vdp_sink->cur_image));
 
646
    vdp_sink->cur_image = NULL;
 
647
  }
 
648
 
 
649
  g_mutex_lock (vdp_sink->flow_lock);
 
650
  if (vdp_sink->window) {
 
651
    gst_vdp_sink_window_destroy (vdp_sink, vdp_sink->window);
 
652
    vdp_sink->window = NULL;
 
653
  }
 
654
  g_mutex_unlock (vdp_sink->flow_lock);
 
655
 
 
656
  gst_vdp_device_clear (vdp_sink);
 
657
 
 
658
  return TRUE;
 
659
}
 
660
 
 
661
/* Element stuff */
 
662
 
 
663
static GstCaps *
 
664
gst_vdp_sink_getcaps (GstBaseSink * bsink)
 
665
{
 
666
  VdpSink *vdp_sink;
 
667
  GstCaps *caps;
 
668
 
 
669
  vdp_sink = GST_VDP_SINK (bsink);
 
670
 
 
671
  if (vdp_sink->caps)
 
672
    caps = gst_caps_copy (vdp_sink->caps);
 
673
  else
 
674
    caps = gst_static_pad_template_get_caps (&sink_template);
 
675
 
 
676
  return caps;
 
677
}
 
678
 
 
679
static gboolean
 
680
gst_vdp_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
 
681
{
 
682
  VdpSink *vdp_sink;
 
683
  GstCaps *allowed_caps;
 
684
  gboolean ret = TRUE;
 
685
  GstStructure *structure;
 
686
  GstCaps *intersection;
 
687
  gint new_width, new_height;
 
688
  const GValue *fps;
 
689
 
 
690
  vdp_sink = GST_VDP_SINK (bsink);
 
691
 
 
692
  GST_OBJECT_LOCK (vdp_sink);
 
693
  if (!vdp_sink->device)
 
694
    return FALSE;
 
695
  GST_OBJECT_UNLOCK (vdp_sink);
 
696
 
 
697
  allowed_caps = gst_pad_get_caps (GST_BASE_SINK_PAD (bsink));
 
698
  GST_DEBUG_OBJECT (vdp_sink,
 
699
      "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
 
700
      GST_PTR_FORMAT, allowed_caps, caps);
 
701
 
 
702
  /* We intersect those caps with our template to make sure they are correct */
 
703
  intersection = gst_caps_intersect (allowed_caps, caps);
 
704
  gst_caps_unref (allowed_caps);
 
705
 
 
706
  GST_DEBUG_OBJECT (vdp_sink, "intersection returned %" GST_PTR_FORMAT,
 
707
      intersection);
 
708
  if (gst_caps_is_empty (intersection)) {
 
709
    gst_caps_unref (intersection);
 
710
    return FALSE;
 
711
  }
 
712
 
 
713
  gst_caps_unref (intersection);
 
714
 
 
715
  structure = gst_caps_get_structure (caps, 0);
 
716
 
 
717
  ret &= gst_structure_get_int (structure, "width", &new_width);
 
718
  ret &= gst_structure_get_int (structure, "height", &new_height);
 
719
  fps = gst_structure_get_value (structure, "framerate");
 
720
  ret &= (fps != NULL);
 
721
  if (!ret)
 
722
    return FALSE;
 
723
 
 
724
  GST_VIDEO_SINK_WIDTH (vdp_sink) = new_width;
 
725
  GST_VIDEO_SINK_HEIGHT (vdp_sink) = new_height;
 
726
  vdp_sink->fps_n = gst_value_get_fraction_numerator (fps);
 
727
  vdp_sink->fps_d = gst_value_get_fraction_denominator (fps);
 
728
 
 
729
  /* Notify application to set xwindow id now */
 
730
  g_mutex_lock (vdp_sink->flow_lock);
 
731
  if (!vdp_sink->window) {
 
732
    g_mutex_unlock (vdp_sink->flow_lock);
 
733
    gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (vdp_sink));
 
734
  } else {
 
735
    g_mutex_unlock (vdp_sink->flow_lock);
 
736
  }
 
737
 
 
738
  /* Creating our window and our image */
 
739
  if (GST_VIDEO_SINK_WIDTH (vdp_sink) <= 0
 
740
      || GST_VIDEO_SINK_HEIGHT (vdp_sink) <= 0) {
 
741
    GST_ELEMENT_ERROR (vdp_sink, CORE, NEGOTIATION, (NULL),
 
742
        ("Invalid image size."));
 
743
    return FALSE;
 
744
  }
 
745
 
 
746
  g_mutex_lock (vdp_sink->flow_lock);
 
747
  if (!vdp_sink->window) {
 
748
    vdp_sink->window = gst_vdp_sink_window_new (vdp_sink,
 
749
        GST_VIDEO_SINK_WIDTH (vdp_sink), GST_VIDEO_SINK_HEIGHT (vdp_sink));
 
750
  }
 
751
  g_mutex_unlock (vdp_sink->flow_lock);
 
752
 
 
753
  return TRUE;
 
754
}
 
755
 
 
756
static void
 
757
gst_vdp_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
 
758
    GstClockTime * start, GstClockTime * end)
 
759
{
 
760
  VdpSink *vdp_sink;
 
761
 
 
762
  vdp_sink = GST_VDP_SINK (bsink);
 
763
 
 
764
  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
 
765
    *start = GST_BUFFER_TIMESTAMP (buf);
 
766
    if (GST_BUFFER_DURATION_IS_VALID (buf)) {
 
767
      *end = *start + GST_BUFFER_DURATION (buf);
 
768
    } else {
 
769
      if (vdp_sink->fps_n > 0) {
 
770
        *end = *start +
 
771
            gst_util_uint64_scale_int (GST_SECOND, vdp_sink->fps_d,
 
772
            vdp_sink->fps_n);
 
773
      }
 
774
    }
 
775
  }
 
776
}
 
777
 
 
778
static GstFlowReturn
 
779
gst_vdp_sink_show_frame (GstBaseSink * bsink, GstBuffer * outbuf)
 
780
{
 
781
  VdpSink *vdp_sink = GST_VDP_SINK (bsink);
 
782
  GstVdpOutputBuffer *prev_image = NULL;
 
783
  VdpStatus status;
 
784
  GstVdpDevice *device;
 
785
 
 
786
  g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), FALSE);
 
787
 
 
788
  /* We take the flow_lock. If expose is in there we don't want to run
 
789
     concurrently from the data flow thread */
 
790
  g_mutex_lock (vdp_sink->flow_lock);
 
791
 
 
792
  if (G_UNLIKELY (vdp_sink->window == NULL)) {
 
793
    g_mutex_unlock (vdp_sink->flow_lock);
 
794
    return GST_FLOW_ERROR;
 
795
  }
 
796
 
 
797
  /* Store a reference to the last image we put, lose the previous one */
 
798
  if (outbuf && vdp_sink->cur_image != outbuf) {
 
799
    if (vdp_sink->cur_image) {
 
800
      prev_image = GST_VDP_OUTPUT_BUFFER (vdp_sink->cur_image);
 
801
    }
 
802
    GST_LOG_OBJECT (vdp_sink, "reffing %p as our current image", outbuf);
 
803
    vdp_sink->cur_image = gst_buffer_ref (outbuf);
 
804
  }
 
805
 
 
806
  /* Expose sends a NULL image, we take the latest frame */
 
807
  if (!outbuf) {
 
808
    if (vdp_sink->cur_image) {
 
809
      outbuf = vdp_sink->cur_image;
 
810
    } else {
 
811
      g_mutex_unlock (vdp_sink->flow_lock);
 
812
      return GST_FLOW_OK;
 
813
    }
 
814
  }
 
815
 
 
816
  gst_vdp_sink_window_update_geometry (vdp_sink, vdp_sink->window);
 
817
 
 
818
  g_mutex_lock (vdp_sink->x_lock);
 
819
 
 
820
  device = vdp_sink->device;
 
821
  status = device->vdp_presentation_queue_display (vdp_sink->window->queue,
 
822
      GST_VDP_OUTPUT_BUFFER (outbuf)->surface, 0, 0, 0);
 
823
  if (status != VDP_STATUS_OK) {
 
824
    GST_ELEMENT_ERROR (vdp_sink, RESOURCE, READ,
 
825
        ("Could not display frame"),
 
826
        ("Error returned from vdpau was: %s",
 
827
            device->vdp_get_error_string (status)));
 
828
 
 
829
    g_mutex_unlock (vdp_sink->x_lock);
 
830
    g_mutex_unlock (vdp_sink->flow_lock);
 
831
    return GST_FLOW_ERROR;
 
832
  }
 
833
 
 
834
  if (prev_image) {
 
835
    VdpTime time;
 
836
 
 
837
    /* block till the previous surface has been displayed */
 
838
    status =
 
839
        device->vdp_presentation_queue_block_until_surface_idle (vdp_sink->
 
840
        window->queue, prev_image->surface, &time);
 
841
    if (status != VDP_STATUS_OK) {
 
842
      GST_ELEMENT_ERROR (vdp_sink, RESOURCE, READ,
 
843
          ("Could not display frame"),
 
844
          ("Error returned from vdpau was: %s",
 
845
              device->vdp_get_error_string (status)));
 
846
 
 
847
      g_mutex_unlock (vdp_sink->x_lock);
 
848
      g_mutex_unlock (vdp_sink->flow_lock);
 
849
      return GST_FLOW_ERROR;
 
850
    }
 
851
    gst_buffer_unref (GST_BUFFER (prev_image));
 
852
  }
 
853
 
 
854
  XSync (vdp_sink->device->display, FALSE);
 
855
 
 
856
  g_mutex_unlock (vdp_sink->x_lock);
 
857
  g_mutex_unlock (vdp_sink->flow_lock);
 
858
 
 
859
  return GST_FLOW_OK;
 
860
}
 
861
 
 
862
 
 
863
static gboolean
 
864
gst_vdp_sink_event (GstBaseSink * sink, GstEvent * event)
 
865
{
 
866
  VdpSink *vdp_sink = GST_VDP_SINK (sink);
 
867
 
 
868
  switch (GST_EVENT_TYPE (event)) {
 
869
    case GST_EVENT_TAG:{
 
870
      GstTagList *l;
 
871
      gchar *title = NULL;
 
872
 
 
873
      gst_event_parse_tag (event, &l);
 
874
      gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
 
875
 
 
876
      if (title) {
 
877
        GST_DEBUG_OBJECT (vdp_sink, "got tags, title='%s'", title);
 
878
        gst_vdp_sink_window_set_title (vdp_sink, vdp_sink->window, title);
 
879
 
 
880
        g_free (title);
 
881
      }
 
882
      break;
 
883
    }
 
884
    default:
 
885
      break;
 
886
  }
 
887
  if (GST_BASE_SINK_CLASS (parent_class)->event)
 
888
    return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
 
889
  else
 
890
    return TRUE;
 
891
}
 
892
 
 
893
static GstFlowReturn
 
894
gst_vdp_sink_get_output_buffer (VdpSink * vdp_sink, GstCaps * caps,
 
895
    GstBuffer ** buf)
 
896
{
 
897
  GstStructure *structure;
 
898
  gint width, height;
 
899
  gint rgba_format;
 
900
 
 
901
  structure = gst_caps_get_structure (caps, 0);
 
902
  if (!gst_structure_get_int (structure, "width", &width) ||
 
903
      !gst_structure_get_int (structure, "height", &height) ||
 
904
      !gst_structure_get_int (structure, "rgba-format", &rgba_format)) {
 
905
    GST_WARNING_OBJECT (vdp_sink, "invalid caps for buffer allocation %"
 
906
        GST_PTR_FORMAT, caps);
 
907
    return GST_FLOW_ERROR;
 
908
  }
 
909
 
 
910
  *buf = GST_BUFFER (gst_vdp_output_buffer_new (vdp_sink->device,
 
911
          rgba_format, width, height));
 
912
  if (*buf == NULL) {
 
913
    return GST_FLOW_ERROR;
 
914
  }
 
915
 
 
916
  gst_buffer_set_caps (*buf, caps);
 
917
 
 
918
  return GST_FLOW_OK;
 
919
}
 
920
 
 
921
/* Buffer management
 
922
 *
 
923
 * The buffer_alloc function must either return a buffer with given size and
 
924
 * caps or create a buffer with different caps attached to the buffer. This
 
925
 * last option is called reverse negotiation, ie, where the sink suggests a
 
926
 * different format from the upstream peer. 
 
927
 *
 
928
 * We try to do reverse negotiation when our geometry changes and we like a
 
929
 * resized buffer.
 
930
 */
 
931
static GstFlowReturn
 
932
gst_vdp_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
 
933
    GstCaps * caps, GstBuffer ** buf)
 
934
{
 
935
  VdpSink *vdp_sink;
 
936
  GstStructure *structure = NULL;
 
937
  GstFlowReturn ret = GST_FLOW_OK;
 
938
  GstCaps *alloc_caps;
 
939
  gboolean alloc_unref = FALSE;
 
940
  gint width, height;
 
941
  gint w_width, w_height;
 
942
 
 
943
  vdp_sink = GST_VDP_SINK (bsink);
 
944
 
 
945
  GST_LOG_OBJECT (vdp_sink,
 
946
      "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
 
947
      " and offset %" G_GUINT64_FORMAT, size, caps, offset);
 
948
 
 
949
  /* assume we're going to alloc what was requested, keep track of
 
950
   * wheter we need to unref or not. When we suggest a new format 
 
951
   * upstream we will create a new caps that we need to unref. */
 
952
  alloc_caps = caps;
 
953
  alloc_unref = FALSE;
 
954
 
 
955
  /* get struct to see what is requested */
 
956
  structure = gst_caps_get_structure (caps, 0);
 
957
  if (!gst_structure_get_int (structure, "width", &width) ||
 
958
      !gst_structure_get_int (structure, "height", &height)) {
 
959
    GST_WARNING_OBJECT (vdp_sink, "invalid caps for buffer allocation %"
 
960
        GST_PTR_FORMAT, caps);
 
961
    ret = GST_FLOW_NOT_NEGOTIATED;
 
962
    goto beach;
 
963
  }
 
964
 
 
965
  /* We take the flow_lock because the window might go away */
 
966
  g_mutex_lock (vdp_sink->flow_lock);
 
967
  if (!vdp_sink->window) {
 
968
    g_mutex_unlock (vdp_sink->flow_lock);
 
969
    goto alloc;
 
970
  }
 
971
 
 
972
  /* What is our geometry */
 
973
  gst_vdp_sink_window_update_geometry (vdp_sink, vdp_sink->window);
 
974
  w_width = vdp_sink->window->width;
 
975
  w_height = vdp_sink->window->height;
 
976
 
 
977
  g_mutex_unlock (vdp_sink->flow_lock);
 
978
 
 
979
  /* We would like another geometry */
 
980
  if (width != w_width || height != w_height) {
 
981
    GstCaps *new_caps, *allowed_caps, *desired_caps;
 
982
    GstStructure *desired_struct;
 
983
 
 
984
    /* make a copy of the incomming caps to create the new
 
985
     * suggestion. We can't use make_writable because we might
 
986
     * then destroy the original caps which we still need when the
 
987
     * peer does not accept the suggestion. */
 
988
    new_caps = gst_caps_copy (caps);
 
989
    desired_struct = gst_caps_get_structure (new_caps, 0);
 
990
 
 
991
    GST_DEBUG ("we would love to receive a %dx%d video", w_width, w_height);
 
992
    gst_structure_set (desired_struct, "width", G_TYPE_INT, w_width, NULL);
 
993
    gst_structure_set (desired_struct, "height", G_TYPE_INT, w_height, NULL);
 
994
 
 
995
    allowed_caps = gst_pad_get_caps (GST_BASE_SINK_PAD (vdp_sink));
 
996
    desired_caps = gst_caps_intersect (new_caps, allowed_caps);
 
997
 
 
998
    gst_caps_unref (new_caps);
 
999
    gst_caps_unref (allowed_caps);
 
1000
 
 
1001
    /* see if peer accepts our new suggestion, if there is no peer, this 
 
1002
     * function returns true. */
 
1003
    if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (vdp_sink), desired_caps)) {
 
1004
      /* we will not alloc a buffer of the new suggested caps. Make sure
 
1005
       * we also unref this new caps after we set it on the buffer. */
 
1006
      alloc_caps = desired_caps;
 
1007
      alloc_unref = TRUE;
 
1008
      width = w_width;
 
1009
      height = w_height;
 
1010
      GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
 
1011
          desired_caps);
 
1012
    } else {
 
1013
      GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
 
1014
          desired_caps);
 
1015
      /* we alloc a buffer with the original incomming caps already in the
 
1016
       * width and height variables */
 
1017
    }
 
1018
  }
 
1019
 
 
1020
alloc:
 
1021
  ret = gst_vdp_sink_get_output_buffer (vdp_sink, alloc_caps, buf);
 
1022
 
 
1023
  /* could be our new reffed suggestion or the original unreffed caps */
 
1024
  if (alloc_unref)
 
1025
    gst_caps_unref (alloc_caps);
 
1026
 
 
1027
beach:
 
1028
  return ret;
 
1029
}
 
1030
 
 
1031
/* Interfaces stuff */
 
1032
 
 
1033
static gboolean
 
1034
gst_vdp_sink_interface_supported (GstImplementsInterface * iface, GType type)
 
1035
{
 
1036
  g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
 
1037
  return TRUE;
 
1038
}
 
1039
 
 
1040
static void
 
1041
gst_vdp_sink_interface_init (GstImplementsInterfaceClass * klass)
 
1042
{
 
1043
  klass->supported = gst_vdp_sink_interface_supported;
 
1044
}
 
1045
 
 
1046
static void
 
1047
gst_vdp_sink_navigation_send_event (GstNavigation * navigation,
 
1048
    GstStructure * structure)
 
1049
{
 
1050
  VdpSink *vdp_sink = GST_VDP_SINK (navigation);
 
1051
  GstEvent *event;
 
1052
  gint x_offset, y_offset;
 
1053
  gdouble x, y;
 
1054
  GstPad *pad = NULL;
 
1055
 
 
1056
  event = gst_event_new_navigation (structure);
 
1057
 
 
1058
  /* We are not converting the pointer coordinates as there's no hardware
 
1059
     scaling done here. The only possible scaling is done by videoscale and
 
1060
     videoscale will have to catch those events and tranform the coordinates
 
1061
     to match the applied scaling. So here we just add the offset if the image
 
1062
     is centered in the window.  */
 
1063
 
 
1064
  /* We take the flow_lock while we look at the window */
 
1065
  g_mutex_lock (vdp_sink->flow_lock);
 
1066
 
 
1067
  if (!vdp_sink->window) {
 
1068
    g_mutex_unlock (vdp_sink->flow_lock);
 
1069
    return;
 
1070
  }
 
1071
 
 
1072
  x_offset = vdp_sink->window->width - GST_VIDEO_SINK_WIDTH (vdp_sink);
 
1073
  y_offset = vdp_sink->window->height - GST_VIDEO_SINK_HEIGHT (vdp_sink);
 
1074
 
 
1075
  g_mutex_unlock (vdp_sink->flow_lock);
 
1076
 
 
1077
  if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
 
1078
    x -= x_offset / 2;
 
1079
    gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
 
1080
  }
 
1081
  if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
 
1082
    y -= y_offset / 2;
 
1083
    gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
 
1084
  }
 
1085
 
 
1086
  pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (vdp_sink));
 
1087
 
 
1088
  if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
 
1089
    gst_pad_send_event (pad, event);
 
1090
 
 
1091
    gst_object_unref (pad);
 
1092
  }
 
1093
}
 
1094
 
 
1095
static void
 
1096
gst_vdp_sink_navigation_init (GstNavigationInterface * iface)
 
1097
{
 
1098
  iface->send_event = gst_vdp_sink_navigation_send_event;
 
1099
}
 
1100
 
 
1101
static void
 
1102
gst_vdp_sink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
 
1103
{
 
1104
  VdpSink *vdp_sink = GST_VDP_SINK (overlay);
 
1105
  GstVdpWindow *window = NULL;
 
1106
  XWindowAttributes attr;
 
1107
 
 
1108
  /* We acquire the stream lock while setting this window in the element.
 
1109
     We are basically cleaning tons of stuff replacing the old window, putting
 
1110
     images while we do that would surely crash */
 
1111
  g_mutex_lock (vdp_sink->flow_lock);
 
1112
 
 
1113
  /* If we already use that window return */
 
1114
  if (vdp_sink->window && (xwindow_id == vdp_sink->window->win)) {
 
1115
    g_mutex_unlock (vdp_sink->flow_lock);
 
1116
    return;
 
1117
  }
 
1118
 
 
1119
  /* If the element has not initialized the X11 context try to do so */
 
1120
  if (!vdp_sink->device
 
1121
      && !(vdp_sink->device = gst_vdp_sink_setup_device (vdp_sink))) {
 
1122
    g_mutex_unlock (vdp_sink->flow_lock);
 
1123
    /* we have thrown a GST_ELEMENT_ERROR now */
 
1124
    return;
 
1125
  }
 
1126
 
 
1127
  /* If a window is there already we destroy it */
 
1128
  if (vdp_sink->window) {
 
1129
    gst_vdp_sink_window_destroy (vdp_sink, vdp_sink->window);
 
1130
    vdp_sink->window = NULL;
 
1131
  }
 
1132
 
 
1133
  /* If the xid is 0 we go back to an internal window */
 
1134
  if (xwindow_id == 0) {
 
1135
    /* If no width/height caps nego did not happen window will be created
 
1136
       during caps nego then */
 
1137
    if (GST_VIDEO_SINK_WIDTH (vdp_sink) && GST_VIDEO_SINK_HEIGHT (vdp_sink)) {
 
1138
      window = gst_vdp_sink_window_new (vdp_sink,
 
1139
          GST_VIDEO_SINK_WIDTH (vdp_sink), GST_VIDEO_SINK_HEIGHT (vdp_sink));
 
1140
    }
 
1141
  } else {
 
1142
    window = g_new0 (GstVdpWindow, 1);
 
1143
 
 
1144
    window->win = xwindow_id;
 
1145
 
 
1146
    /* We get window geometry, set the event we want to receive,
 
1147
       and create a GC */
 
1148
    g_mutex_lock (vdp_sink->x_lock);
 
1149
    XGetWindowAttributes (vdp_sink->device->display, window->win, &attr);
 
1150
    window->width = attr.width;
 
1151
    window->height = attr.height;
 
1152
    window->internal = FALSE;
 
1153
    if (vdp_sink->handle_events) {
 
1154
      XSelectInput (vdp_sink->device->display, window->win, ExposureMask |
 
1155
          StructureNotifyMask | PointerMotionMask | KeyPressMask |
 
1156
          KeyReleaseMask);
 
1157
    }
 
1158
 
 
1159
    g_mutex_unlock (vdp_sink->x_lock);
 
1160
  }
 
1161
 
 
1162
  if (window)
 
1163
    vdp_sink->window = window;
 
1164
 
 
1165
  g_mutex_unlock (vdp_sink->flow_lock);
 
1166
}
 
1167
 
 
1168
static void
 
1169
gst_vdp_sink_expose (GstXOverlay * overlay)
 
1170
{
 
1171
  gst_vdp_sink_show_frame (GST_BASE_SINK (overlay), NULL);
 
1172
}
 
1173
 
 
1174
static void
 
1175
gst_vdp_sink_set_event_handling (GstXOverlay * overlay, gboolean handle_events)
 
1176
{
 
1177
  VdpSink *vdp_sink = GST_VDP_SINK (overlay);
 
1178
 
 
1179
  vdp_sink->handle_events = handle_events;
 
1180
 
 
1181
  g_mutex_lock (vdp_sink->flow_lock);
 
1182
 
 
1183
  if (G_UNLIKELY (!vdp_sink->window)) {
 
1184
    g_mutex_unlock (vdp_sink->flow_lock);
 
1185
    return;
 
1186
  }
 
1187
 
 
1188
  g_mutex_lock (vdp_sink->x_lock);
 
1189
 
 
1190
  if (handle_events) {
 
1191
    if (vdp_sink->window->internal) {
 
1192
      XSelectInput (vdp_sink->device->display, vdp_sink->window->win,
 
1193
          ExposureMask | StructureNotifyMask | PointerMotionMask |
 
1194
          KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
 
1195
    } else {
 
1196
      XSelectInput (vdp_sink->device->display, vdp_sink->window->win,
 
1197
          ExposureMask | StructureNotifyMask | PointerMotionMask |
 
1198
          KeyPressMask | KeyReleaseMask);
 
1199
    }
 
1200
  } else {
 
1201
    XSelectInput (vdp_sink->device->display, vdp_sink->window->win, 0);
 
1202
  }
 
1203
 
 
1204
  g_mutex_unlock (vdp_sink->x_lock);
 
1205
 
 
1206
  g_mutex_unlock (vdp_sink->flow_lock);
 
1207
}
 
1208
 
 
1209
static void
 
1210
gst_vdp_sink_xoverlay_init (GstXOverlayClass * iface)
 
1211
{
 
1212
  iface->set_xwindow_id = gst_vdp_sink_set_xwindow_id;
 
1213
  iface->expose = gst_vdp_sink_expose;
 
1214
  iface->handle_events = gst_vdp_sink_set_event_handling;
 
1215
}
 
1216
 
 
1217
/* =========================================== */
 
1218
/*                                             */
 
1219
/*              Init & Class init              */
 
1220
/*                                             */
 
1221
/* =========================================== */
 
1222
 
 
1223
static void
 
1224
gst_vdp_sink_set_property (GObject * object, guint prop_id,
 
1225
    const GValue * value, GParamSpec * pspec)
 
1226
{
 
1227
  VdpSink *vdp_sink;
 
1228
 
 
1229
  g_return_if_fail (GST_IS_VDP_SINK (object));
 
1230
 
 
1231
  vdp_sink = GST_VDP_SINK (object);
 
1232
 
 
1233
  switch (prop_id) {
 
1234
    case PROP_DISPLAY:
 
1235
      vdp_sink->display_name = g_strdup (g_value_get_string (value));
 
1236
      break;
 
1237
    case PROP_SYNCHRONOUS:
 
1238
      vdp_sink->synchronous = g_value_get_boolean (value);
 
1239
      if (vdp_sink->device) {
 
1240
        GST_DEBUG_OBJECT (vdp_sink, "XSynchronize called with %s",
 
1241
            vdp_sink->synchronous ? "TRUE" : "FALSE");
 
1242
        g_mutex_lock (vdp_sink->x_lock);
 
1243
        XSynchronize (vdp_sink->device->display, vdp_sink->synchronous);
 
1244
        g_mutex_unlock (vdp_sink->x_lock);
 
1245
      }
 
1246
      break;
 
1247
    case PROP_PIXEL_ASPECT_RATIO:
 
1248
    {
 
1249
      GValue *tmp;
 
1250
 
 
1251
      tmp = g_new0 (GValue, 1);
 
1252
      g_value_init (tmp, GST_TYPE_FRACTION);
 
1253
 
 
1254
      if (!g_value_transform (value, tmp)) {
 
1255
        GST_WARNING_OBJECT (vdp_sink,
 
1256
            "Could not transform string to aspect ratio");
 
1257
        g_free (tmp);
 
1258
      } else {
 
1259
        GST_DEBUG_OBJECT (vdp_sink, "set PAR to %d/%d",
 
1260
            gst_value_get_fraction_numerator (tmp),
 
1261
            gst_value_get_fraction_denominator (tmp));
 
1262
        g_free (vdp_sink->par);
 
1263
        vdp_sink->par = tmp;
 
1264
      }
 
1265
    }
 
1266
      break;
 
1267
    case PROP_HANDLE_EVENTS:
 
1268
      gst_vdp_sink_set_event_handling (GST_X_OVERLAY (vdp_sink),
 
1269
          g_value_get_boolean (value));
 
1270
      break;
 
1271
    case PROP_HANDLE_EXPOSE:
 
1272
      vdp_sink->handle_expose = g_value_get_boolean (value);
 
1273
      break;
 
1274
    default:
 
1275
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1276
      break;
 
1277
  }
 
1278
}
 
1279
 
 
1280
static void
 
1281
gst_vdp_sink_get_property (GObject * object, guint prop_id,
 
1282
    GValue * value, GParamSpec * pspec)
 
1283
{
 
1284
  VdpSink *vdp_sink;
 
1285
 
 
1286
  g_return_if_fail (GST_IS_VDP_SINK (object));
 
1287
 
 
1288
  vdp_sink = GST_VDP_SINK (object);
 
1289
 
 
1290
  switch (prop_id) {
 
1291
    case PROP_DISPLAY:
 
1292
      g_value_set_string (value, vdp_sink->display_name);
 
1293
      break;
 
1294
    case PROP_SYNCHRONOUS:
 
1295
      g_value_set_boolean (value, vdp_sink->synchronous);
 
1296
      break;
 
1297
    case PROP_PIXEL_ASPECT_RATIO:
 
1298
      if (vdp_sink->par)
 
1299
        g_value_transform (vdp_sink->par, value);
 
1300
      break;
 
1301
    case PROP_HANDLE_EVENTS:
 
1302
      g_value_set_boolean (value, vdp_sink->handle_events);
 
1303
      break;
 
1304
    case PROP_HANDLE_EXPOSE:
 
1305
      g_value_set_boolean (value, vdp_sink->handle_expose);
 
1306
      break;
 
1307
    default:
 
1308
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
1309
      break;
 
1310
  }
 
1311
}
 
1312
 
 
1313
static void
 
1314
gst_vdp_sink_finalize (GObject * object)
 
1315
{
 
1316
  VdpSink *vdp_sink;
 
1317
 
 
1318
  vdp_sink = GST_VDP_SINK (object);
 
1319
 
 
1320
  if (vdp_sink->display_name) {
 
1321
    g_free (vdp_sink->display_name);
 
1322
    vdp_sink->display_name = NULL;
 
1323
  }
 
1324
  if (vdp_sink->par) {
 
1325
    g_free (vdp_sink->par);
 
1326
    vdp_sink->par = NULL;
 
1327
  }
 
1328
  if (vdp_sink->x_lock) {
 
1329
    g_mutex_free (vdp_sink->x_lock);
 
1330
    vdp_sink->x_lock = NULL;
 
1331
  }
 
1332
  if (vdp_sink->flow_lock) {
 
1333
    g_mutex_free (vdp_sink->flow_lock);
 
1334
    vdp_sink->flow_lock = NULL;
 
1335
  }
 
1336
 
 
1337
  g_free (vdp_sink->media_title);
 
1338
 
 
1339
  G_OBJECT_CLASS (parent_class)->finalize (object);
 
1340
}
 
1341
 
 
1342
static void
 
1343
gst_vdp_sink_init (VdpSink * vdp_sink)
 
1344
{
 
1345
  vdp_sink->device = NULL;
 
1346
 
 
1347
  vdp_sink->display_name = NULL;
 
1348
  vdp_sink->par = NULL;
 
1349
 
 
1350
  vdp_sink->x_lock = g_mutex_new ();
 
1351
  vdp_sink->flow_lock = g_mutex_new ();
 
1352
 
 
1353
  vdp_sink->synchronous = FALSE;
 
1354
  vdp_sink->handle_events = TRUE;
 
1355
  vdp_sink->handle_expose = TRUE;
 
1356
}
 
1357
 
 
1358
static void
 
1359
gst_vdp_sink_base_init (gpointer g_class)
 
1360
{
 
1361
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
1362
 
 
1363
  gst_element_class_set_details_simple (element_class,
 
1364
      "VDPAU Sink",
 
1365
      "Sink/Video",
 
1366
      "VDPAU Sink", "Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>");
 
1367
 
 
1368
  gst_element_class_add_pad_template (element_class,
 
1369
      gst_static_pad_template_get (&sink_template));
 
1370
}
 
1371
 
 
1372
static void
 
1373
gst_vdp_sink_class_init (VdpSinkClass * klass)
 
1374
{
 
1375
  GObjectClass *gobject_class;
 
1376
  GstElementClass *gstelement_class;
 
1377
  GstBaseSinkClass *gstbasesink_class;
 
1378
 
 
1379
  gobject_class = (GObjectClass *) klass;
 
1380
  gstelement_class = (GstElementClass *) klass;
 
1381
  gstbasesink_class = (GstBaseSinkClass *) klass;
 
1382
 
 
1383
  parent_class = g_type_class_peek_parent (klass);
 
1384
 
 
1385
  gobject_class->finalize = gst_vdp_sink_finalize;
 
1386
  gobject_class->set_property = gst_vdp_sink_set_property;
 
1387
  gobject_class->get_property = gst_vdp_sink_get_property;
 
1388
 
 
1389
  g_object_class_install_property (gobject_class, PROP_DISPLAY,
 
1390
      g_param_spec_string ("display", "Display", "X Display name",
 
1391
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
1392
  g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
 
1393
      g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
 
1394
          "the X display in synchronous mode. (used only for debugging)", FALSE,
 
1395
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
1396
  g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
 
1397
      g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
 
1398
          "The pixel aspect ratio of the device", "1/1",
 
1399
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
1400
  g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
 
1401
      g_param_spec_boolean ("handle-events", "Handle XEvents",
 
1402
          "When enabled, XEvents will be selected and handled", TRUE,
 
1403
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
1404
  g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
 
1405
      g_param_spec_boolean ("handle-expose", "Handle expose",
 
1406
          "When enabled, "
 
1407
          "the current frame will always be drawn in response to X Expose "
 
1408
          "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
1409
 
 
1410
  gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_vdp_sink_start);
 
1411
  gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_vdp_sink_stop);
 
1412
  gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_vdp_sink_getcaps);
 
1413
  gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_vdp_sink_setcaps);
 
1414
  gstbasesink_class->buffer_alloc =
 
1415
      GST_DEBUG_FUNCPTR (gst_vdp_sink_buffer_alloc);
 
1416
  gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_vdp_sink_get_times);
 
1417
  gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_vdp_sink_show_frame);
 
1418
  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_vdp_sink_show_frame);
 
1419
  gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_vdp_sink_event);
 
1420
}
 
1421
 
 
1422
/* ============================================================= */
 
1423
/*                                                               */
 
1424
/*                       Public Methods                          */
 
1425
/*                                                               */
 
1426
/* ============================================================= */
 
1427
 
 
1428
/* =========================================== */
 
1429
/*                                             */
 
1430
/*          Object typing & Creation           */
 
1431
/*                                             */
 
1432
/* =========================================== */
 
1433
 
 
1434
GType
 
1435
gst_vdp_sink_get_type (void)
 
1436
{
 
1437
  static GType vdp_sink_type = 0;
 
1438
 
 
1439
  if (!vdp_sink_type) {
 
1440
    static const GTypeInfo vdp_sink_info = {
 
1441
      sizeof (VdpSinkClass),
 
1442
      gst_vdp_sink_base_init,
 
1443
      NULL,
 
1444
      (GClassInitFunc) gst_vdp_sink_class_init,
 
1445
      NULL,
 
1446
      NULL,
 
1447
      sizeof (VdpSink),
 
1448
      0,
 
1449
      (GInstanceInitFunc) gst_vdp_sink_init,
 
1450
    };
 
1451
    static const GInterfaceInfo iface_info = {
 
1452
      (GInterfaceInitFunc) gst_vdp_sink_interface_init,
 
1453
      NULL,
 
1454
      NULL,
 
1455
    };
 
1456
    static const GInterfaceInfo navigation_info = {
 
1457
      (GInterfaceInitFunc) gst_vdp_sink_navigation_init,
 
1458
      NULL,
 
1459
      NULL,
 
1460
    };
 
1461
    static const GInterfaceInfo overlay_info = {
 
1462
      (GInterfaceInitFunc) gst_vdp_sink_xoverlay_init,
 
1463
      NULL,
 
1464
      NULL,
 
1465
    };
 
1466
 
 
1467
    vdp_sink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
 
1468
        "VdpSink", &vdp_sink_info, 0);
 
1469
 
 
1470
    g_type_add_interface_static (vdp_sink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
 
1471
        &iface_info);
 
1472
    g_type_add_interface_static (vdp_sink_type, GST_TYPE_NAVIGATION,
 
1473
        &navigation_info);
 
1474
    g_type_add_interface_static (vdp_sink_type, GST_TYPE_X_OVERLAY,
 
1475
        &overlay_info);
 
1476
  }
 
1477
 
 
1478
  DEBUG_INIT ();
 
1479
 
 
1480
  return vdp_sink_type;
 
1481
}