2
* Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>
3
* Copyright (C) 2005 Julien Moutte <julien@moutte.net>
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.
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.
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.
26
#include <gst/interfaces/navigation.h>
27
#include <gst/interfaces/xoverlay.h>
29
/* Debugging category */
30
#include <gst/gstinfo.h>
32
#include "gstvdpoutputbuffer.h"
35
#include "gstvdpsink.h"
37
GST_DEBUG_CATEGORY_STATIC (gst_vdp_sink_debug);
38
#define GST_CAT_DEFAULT gst_vdp_sink_debug
43
unsigned long functions;
44
unsigned long decorations;
48
MotifWmHints, MwmHints;
50
#define MWM_HINTS_DECORATIONS (1L << 1)
52
static void gst_vdp_sink_expose (GstXOverlay * overlay);
59
PROP_PIXEL_ASPECT_RATIO,
64
static GstVideoSinkClass *parent_class = NULL;
66
/* the capabilities of the inputs and outputs.
68
* describe the real formats here.
70
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
73
GST_STATIC_CAPS (GST_VDP_OUTPUT_CAPS));
75
#define DEBUG_INIT(bla) \
76
GST_DEBUG_CATEGORY_INIT (gst_vdp_sink_debug, "vdpausink", 0, "VDPAU video sink");
78
/* ============================================================= */
82
/* ============================================================= */
87
gst_vdp_sink_window_decorate (VdpSink * vdp_sink, GstVdpWindow * window)
89
Atom hints_atom = None;
92
g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), FALSE);
93
g_return_val_if_fail (window != NULL, FALSE);
95
g_mutex_lock (vdp_sink->x_lock);
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);
103
hints = g_malloc0 (sizeof (MotifWmHints));
105
hints->flags |= MWM_HINTS_DECORATIONS;
106
hints->decorations = 1 << 0;
108
XChangeProperty (vdp_sink->device->display, window->win,
109
hints_atom, hints_atom, 32, PropModeReplace,
110
(guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
112
XSync (vdp_sink->device->display, FALSE);
114
g_mutex_unlock (vdp_sink->x_lock);
122
gst_vdp_sink_window_set_title (VdpSink * vdp_sink,
123
GstVdpWindow * window, const gchar * media_title)
126
g_free (vdp_sink->media_title);
127
vdp_sink->media_title = g_strdup (media_title);
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;
137
/* set application name as a title */
138
app_name = g_get_application_name ();
140
if (app_name && vdp_sink->media_title) {
141
title = title_mem = g_strconcat (vdp_sink->media_title, " : ",
143
} else if (app_name) {
145
} else if (vdp_sink->media_title) {
146
title = vdp_sink->media_title;
150
if ((XStringListToTextProperty (((char **) &title), 1,
152
XSetWMName (vdp_sink->device->display, window->win, &xproperty);
160
/* This function handles a GstVdpWindow creation */
161
static GstVdpWindow *
162
gst_vdp_sink_window_new (VdpSink * vdp_sink, gint width, gint height)
164
GstVdpWindow *window = NULL;
165
GstVdpDevice *device = vdp_sink->device;
172
VdpColor color = { 0, };
174
g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), NULL);
176
window = g_new0 (GstVdpWindow, 1);
178
window->width = width;
179
window->height = height;
180
window->internal = TRUE;
182
g_mutex_lock (vdp_sink->x_lock);
184
screen_num = DefaultScreen (device->display);
185
root = DefaultRootWindow (device->display);
186
black = XBlackPixel (device->display, screen_num);
188
window->win = XCreateSimpleWindow (vdp_sink->device->display,
189
root, 0, 0, window->width, window->height, 0, 0, black);
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);
195
/* set application name as a title */
196
gst_vdp_sink_window_set_title (vdp_sink, window, NULL);
198
if (vdp_sink->handle_events) {
201
XSelectInput (vdp_sink->device->display, window->win, ExposureMask |
202
StructureNotifyMask | PointerMotionMask | KeyPressMask |
203
KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
205
/* Tell the window manager we'd like delete client messages instead of
208
XInternAtom (vdp_sink->device->display, "WM_DELETE_WINDOW", False);
209
(void) XSetWMProtocols (vdp_sink->device->display, window->win, &wm_delete,
213
XMapRaised (vdp_sink->device->display, window->win);
215
XSync (vdp_sink->device->display, FALSE);
217
g_mutex_unlock (vdp_sink->x_lock);
219
gst_vdp_sink_window_decorate (vdp_sink, window);
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)));
231
device->vdp_presentation_queue_create (device->device, window->target,
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)));
241
device->vdp_presentation_queue_set_background_color (window->queue,
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)));
250
gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (vdp_sink), window->win);
255
/* This function destroys a GstVdpWindow */
257
gst_vdp_sink_window_destroy (VdpSink * vdp_sink, GstVdpWindow * window)
259
g_return_if_fail (window != NULL);
260
g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
262
g_mutex_lock (vdp_sink->x_lock);
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);
268
XSelectInput (vdp_sink->device->display, window->win, 0);
270
XSync (vdp_sink->device->display, FALSE);
272
g_mutex_unlock (vdp_sink->x_lock);
278
gst_vdp_sink_window_update_geometry (VdpSink * vdp_sink, GstVdpWindow * window)
280
XWindowAttributes attr;
282
g_return_if_fail (window != NULL);
283
g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
285
/* Update the window geometry */
286
g_mutex_lock (vdp_sink->x_lock);
288
XGetWindowAttributes (vdp_sink->device->display, window->win, &attr);
290
window->width = attr.width;
291
window->height = attr.height;
293
g_mutex_unlock (vdp_sink->x_lock);
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
300
gst_vdp_sink_handle_xevents (VdpSink * vdp_sink)
303
guint pointer_x = 0, pointer_y = 0;
304
gboolean pointer_moved = FALSE;
305
gboolean exposed = FALSE, configured = FALSE;
307
g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
309
/* Then we get all pointer motion events, only the last position is
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);
320
pointer_x = e.xmotion.x;
321
pointer_y = e.xmotion.y;
322
pointer_moved = TRUE;
327
g_mutex_lock (vdp_sink->flow_lock);
328
g_mutex_lock (vdp_sink->x_lock);
332
g_mutex_unlock (vdp_sink->x_lock);
333
g_mutex_unlock (vdp_sink->flow_lock);
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);
340
g_mutex_lock (vdp_sink->flow_lock);
341
g_mutex_lock (vdp_sink->x_lock);
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)) {
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);
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);
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);
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);
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;
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);
390
gst_navigation_send_key_event (GST_NAVIGATION (vdp_sink),
391
e.type == KeyPress ? "key-press" : "key-release", "unknown");
395
GST_DEBUG_OBJECT (vdp_sink, "vdp_sink unhandled X event (%d)", e.type);
397
g_mutex_lock (vdp_sink->flow_lock);
398
g_mutex_lock (vdp_sink->x_lock);
401
while (XCheckWindowEvent (vdp_sink->device->display,
402
vdp_sink->window->win, ExposureMask | StructureNotifyMask, &e)) {
407
case ConfigureNotify:
415
if (vdp_sink->handle_expose && (exposed || configured)) {
416
g_mutex_unlock (vdp_sink->x_lock);
417
g_mutex_unlock (vdp_sink->flow_lock);
419
gst_vdp_sink_expose (GST_X_OVERLAY (vdp_sink));
421
g_mutex_lock (vdp_sink->flow_lock);
422
g_mutex_lock (vdp_sink->x_lock);
425
/* Handle Display events */
426
while (XPending (vdp_sink->device->display)) {
427
XNextEvent (vdp_sink->device->display, &e);
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));
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);
452
g_mutex_unlock (vdp_sink->x_lock);
453
g_mutex_unlock (vdp_sink->flow_lock);
457
gst_vdp_sink_event_thread (VdpSink * vdp_sink)
459
g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), NULL);
461
GST_OBJECT_LOCK (vdp_sink);
462
while (vdp_sink->running) {
463
GST_OBJECT_UNLOCK (vdp_sink);
465
if (vdp_sink->window) {
466
gst_vdp_sink_handle_xevents (vdp_sink);
470
GST_OBJECT_LOCK (vdp_sink);
472
GST_OBJECT_UNLOCK (vdp_sink);
477
/* This function calculates the pixel aspect ratio */
479
gst_vdp_sink_calculate_par (Display * display)
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 */
492
gint widthmm, heightmm;
499
#define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
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);
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);
512
/* DirectFB's X in 720x576 reports the physical dimensions wrong, so
514
if (width == 720 && height == 576) {
515
ratio = 4.0 * 576 / (3.0 * 720);
517
GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
519
/* now find the one from par[][2] with the lowest delta to the real one */
523
for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
524
gdouble this_delta = DELTA (i);
526
if (this_delta < delta) {
532
GST_DEBUG ("Decided on index %d (%d/%d)", index,
533
par[index][0], par[index][1]);
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));
546
gst_vdp_sink_get_allowed_caps (GstVdpDevice * device, GValue * par)
551
caps = gst_vdp_output_buffer_get_allowed_caps (device);
554
par = gst_vdp_sink_calculate_par (device->display);
556
for (i = 0; i < gst_caps_get_size (caps); i++) {
557
GstStructure *structure;
559
structure = gst_caps_get_structure (caps, i);
560
gst_structure_set_value (structure, "pixel-aspect-ratio", par);
566
static GstVdpDevice *
567
gst_vdp_sink_setup_device (VdpSink * vdp_sink)
569
GstVdpDevice *device;
571
device = gst_vdp_get_device (vdp_sink->display_name);
575
vdp_sink->caps = gst_vdp_sink_get_allowed_caps (device, vdp_sink->par);
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);
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);
591
gst_vdp_sink_start (GstBaseSink * bsink)
593
VdpSink *vdp_sink = GST_VDP_SINK (bsink);
596
vdp_sink->window = NULL;
597
vdp_sink->cur_image = NULL;
599
vdp_sink->event_thread = NULL;
604
GST_OBJECT_LOCK (vdp_sink);
605
if (!vdp_sink->device) {
606
if (!(vdp_sink->device = gst_vdp_sink_setup_device (vdp_sink)))
609
GST_OBJECT_UNLOCK (vdp_sink);
615
gst_vdp_device_clear (VdpSink * vdp_sink)
617
g_return_if_fail (GST_IS_VDP_SINK (vdp_sink));
619
GST_OBJECT_LOCK (vdp_sink);
620
if (vdp_sink->device == NULL) {
621
GST_OBJECT_UNLOCK (vdp_sink);
624
GST_OBJECT_UNLOCK (vdp_sink);
626
g_mutex_lock (vdp_sink->x_lock);
628
g_object_unref (vdp_sink->device);
629
vdp_sink->device = NULL;
631
g_mutex_unlock (vdp_sink->x_lock);
635
gst_vdp_sink_stop (GstBaseSink * bsink)
637
VdpSink *vdp_sink = GST_VDP_SINK (bsink);
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);
644
if (vdp_sink->cur_image) {
645
gst_buffer_unref (GST_BUFFER_CAST (vdp_sink->cur_image));
646
vdp_sink->cur_image = NULL;
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;
654
g_mutex_unlock (vdp_sink->flow_lock);
656
gst_vdp_device_clear (vdp_sink);
664
gst_vdp_sink_getcaps (GstBaseSink * bsink)
669
vdp_sink = GST_VDP_SINK (bsink);
672
caps = gst_caps_copy (vdp_sink->caps);
674
caps = gst_static_pad_template_get_caps (&sink_template);
680
gst_vdp_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
683
GstCaps *allowed_caps;
685
GstStructure *structure;
686
GstCaps *intersection;
687
gint new_width, new_height;
690
vdp_sink = GST_VDP_SINK (bsink);
692
GST_OBJECT_LOCK (vdp_sink);
693
if (!vdp_sink->device)
695
GST_OBJECT_UNLOCK (vdp_sink);
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);
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);
706
GST_DEBUG_OBJECT (vdp_sink, "intersection returned %" GST_PTR_FORMAT,
708
if (gst_caps_is_empty (intersection)) {
709
gst_caps_unref (intersection);
713
gst_caps_unref (intersection);
715
structure = gst_caps_get_structure (caps, 0);
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);
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);
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));
735
g_mutex_unlock (vdp_sink->flow_lock);
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."));
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));
751
g_mutex_unlock (vdp_sink->flow_lock);
757
gst_vdp_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
758
GstClockTime * start, GstClockTime * end)
762
vdp_sink = GST_VDP_SINK (bsink);
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);
769
if (vdp_sink->fps_n > 0) {
771
gst_util_uint64_scale_int (GST_SECOND, vdp_sink->fps_d,
779
gst_vdp_sink_show_frame (GstBaseSink * bsink, GstBuffer * outbuf)
781
VdpSink *vdp_sink = GST_VDP_SINK (bsink);
782
GstVdpOutputBuffer *prev_image = NULL;
784
GstVdpDevice *device;
786
g_return_val_if_fail (GST_IS_VDP_SINK (vdp_sink), FALSE);
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);
792
if (G_UNLIKELY (vdp_sink->window == NULL)) {
793
g_mutex_unlock (vdp_sink->flow_lock);
794
return GST_FLOW_ERROR;
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);
802
GST_LOG_OBJECT (vdp_sink, "reffing %p as our current image", outbuf);
803
vdp_sink->cur_image = gst_buffer_ref (outbuf);
806
/* Expose sends a NULL image, we take the latest frame */
808
if (vdp_sink->cur_image) {
809
outbuf = vdp_sink->cur_image;
811
g_mutex_unlock (vdp_sink->flow_lock);
816
gst_vdp_sink_window_update_geometry (vdp_sink, vdp_sink->window);
818
g_mutex_lock (vdp_sink->x_lock);
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)));
829
g_mutex_unlock (vdp_sink->x_lock);
830
g_mutex_unlock (vdp_sink->flow_lock);
831
return GST_FLOW_ERROR;
837
/* block till the previous surface has been displayed */
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)));
847
g_mutex_unlock (vdp_sink->x_lock);
848
g_mutex_unlock (vdp_sink->flow_lock);
849
return GST_FLOW_ERROR;
851
gst_buffer_unref (GST_BUFFER (prev_image));
854
XSync (vdp_sink->device->display, FALSE);
856
g_mutex_unlock (vdp_sink->x_lock);
857
g_mutex_unlock (vdp_sink->flow_lock);
864
gst_vdp_sink_event (GstBaseSink * sink, GstEvent * event)
866
VdpSink *vdp_sink = GST_VDP_SINK (sink);
868
switch (GST_EVENT_TYPE (event)) {
873
gst_event_parse_tag (event, &l);
874
gst_tag_list_get_string (l, GST_TAG_TITLE, &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);
887
if (GST_BASE_SINK_CLASS (parent_class)->event)
888
return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
894
gst_vdp_sink_get_output_buffer (VdpSink * vdp_sink, GstCaps * caps,
897
GstStructure *structure;
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;
910
*buf = GST_BUFFER (gst_vdp_output_buffer_new (vdp_sink->device,
911
rgba_format, width, height));
913
return GST_FLOW_ERROR;
916
gst_buffer_set_caps (*buf, caps);
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.
928
* We try to do reverse negotiation when our geometry changes and we like a
932
gst_vdp_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
933
GstCaps * caps, GstBuffer ** buf)
936
GstStructure *structure = NULL;
937
GstFlowReturn ret = GST_FLOW_OK;
939
gboolean alloc_unref = FALSE;
941
gint w_width, w_height;
943
vdp_sink = GST_VDP_SINK (bsink);
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);
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. */
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;
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);
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;
977
g_mutex_unlock (vdp_sink->flow_lock);
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;
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);
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);
995
allowed_caps = gst_pad_get_caps (GST_BASE_SINK_PAD (vdp_sink));
996
desired_caps = gst_caps_intersect (new_caps, allowed_caps);
998
gst_caps_unref (new_caps);
999
gst_caps_unref (allowed_caps);
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;
1010
GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1013
GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1015
/* we alloc a buffer with the original incomming caps already in the
1016
* width and height variables */
1021
ret = gst_vdp_sink_get_output_buffer (vdp_sink, alloc_caps, buf);
1023
/* could be our new reffed suggestion or the original unreffed caps */
1025
gst_caps_unref (alloc_caps);
1031
/* Interfaces stuff */
1034
gst_vdp_sink_interface_supported (GstImplementsInterface * iface, GType type)
1036
g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1041
gst_vdp_sink_interface_init (GstImplementsInterfaceClass * klass)
1043
klass->supported = gst_vdp_sink_interface_supported;
1047
gst_vdp_sink_navigation_send_event (GstNavigation * navigation,
1048
GstStructure * structure)
1050
VdpSink *vdp_sink = GST_VDP_SINK (navigation);
1052
gint x_offset, y_offset;
1056
event = gst_event_new_navigation (structure);
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. */
1064
/* We take the flow_lock while we look at the window */
1065
g_mutex_lock (vdp_sink->flow_lock);
1067
if (!vdp_sink->window) {
1068
g_mutex_unlock (vdp_sink->flow_lock);
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);
1075
g_mutex_unlock (vdp_sink->flow_lock);
1077
if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1079
gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1081
if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1083
gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1086
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (vdp_sink));
1088
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1089
gst_pad_send_event (pad, event);
1091
gst_object_unref (pad);
1096
gst_vdp_sink_navigation_init (GstNavigationInterface * iface)
1098
iface->send_event = gst_vdp_sink_navigation_send_event;
1102
gst_vdp_sink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1104
VdpSink *vdp_sink = GST_VDP_SINK (overlay);
1105
GstVdpWindow *window = NULL;
1106
XWindowAttributes attr;
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);
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);
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 */
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;
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));
1142
window = g_new0 (GstVdpWindow, 1);
1144
window->win = xwindow_id;
1146
/* We get window geometry, set the event we want to receive,
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 |
1159
g_mutex_unlock (vdp_sink->x_lock);
1163
vdp_sink->window = window;
1165
g_mutex_unlock (vdp_sink->flow_lock);
1169
gst_vdp_sink_expose (GstXOverlay * overlay)
1171
gst_vdp_sink_show_frame (GST_BASE_SINK (overlay), NULL);
1175
gst_vdp_sink_set_event_handling (GstXOverlay * overlay, gboolean handle_events)
1177
VdpSink *vdp_sink = GST_VDP_SINK (overlay);
1179
vdp_sink->handle_events = handle_events;
1181
g_mutex_lock (vdp_sink->flow_lock);
1183
if (G_UNLIKELY (!vdp_sink->window)) {
1184
g_mutex_unlock (vdp_sink->flow_lock);
1188
g_mutex_lock (vdp_sink->x_lock);
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);
1196
XSelectInput (vdp_sink->device->display, vdp_sink->window->win,
1197
ExposureMask | StructureNotifyMask | PointerMotionMask |
1198
KeyPressMask | KeyReleaseMask);
1201
XSelectInput (vdp_sink->device->display, vdp_sink->window->win, 0);
1204
g_mutex_unlock (vdp_sink->x_lock);
1206
g_mutex_unlock (vdp_sink->flow_lock);
1210
gst_vdp_sink_xoverlay_init (GstXOverlayClass * iface)
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;
1217
/* =========================================== */
1219
/* Init & Class init */
1221
/* =========================================== */
1224
gst_vdp_sink_set_property (GObject * object, guint prop_id,
1225
const GValue * value, GParamSpec * pspec)
1229
g_return_if_fail (GST_IS_VDP_SINK (object));
1231
vdp_sink = GST_VDP_SINK (object);
1235
vdp_sink->display_name = g_strdup (g_value_get_string (value));
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);
1247
case PROP_PIXEL_ASPECT_RATIO:
1251
tmp = g_new0 (GValue, 1);
1252
g_value_init (tmp, GST_TYPE_FRACTION);
1254
if (!g_value_transform (value, tmp)) {
1255
GST_WARNING_OBJECT (vdp_sink,
1256
"Could not transform string to aspect ratio");
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;
1267
case PROP_HANDLE_EVENTS:
1268
gst_vdp_sink_set_event_handling (GST_X_OVERLAY (vdp_sink),
1269
g_value_get_boolean (value));
1271
case PROP_HANDLE_EXPOSE:
1272
vdp_sink->handle_expose = g_value_get_boolean (value);
1275
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1281
gst_vdp_sink_get_property (GObject * object, guint prop_id,
1282
GValue * value, GParamSpec * pspec)
1286
g_return_if_fail (GST_IS_VDP_SINK (object));
1288
vdp_sink = GST_VDP_SINK (object);
1292
g_value_set_string (value, vdp_sink->display_name);
1294
case PROP_SYNCHRONOUS:
1295
g_value_set_boolean (value, vdp_sink->synchronous);
1297
case PROP_PIXEL_ASPECT_RATIO:
1299
g_value_transform (vdp_sink->par, value);
1301
case PROP_HANDLE_EVENTS:
1302
g_value_set_boolean (value, vdp_sink->handle_events);
1304
case PROP_HANDLE_EXPOSE:
1305
g_value_set_boolean (value, vdp_sink->handle_expose);
1308
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1314
gst_vdp_sink_finalize (GObject * object)
1318
vdp_sink = GST_VDP_SINK (object);
1320
if (vdp_sink->display_name) {
1321
g_free (vdp_sink->display_name);
1322
vdp_sink->display_name = NULL;
1324
if (vdp_sink->par) {
1325
g_free (vdp_sink->par);
1326
vdp_sink->par = NULL;
1328
if (vdp_sink->x_lock) {
1329
g_mutex_free (vdp_sink->x_lock);
1330
vdp_sink->x_lock = NULL;
1332
if (vdp_sink->flow_lock) {
1333
g_mutex_free (vdp_sink->flow_lock);
1334
vdp_sink->flow_lock = NULL;
1337
g_free (vdp_sink->media_title);
1339
G_OBJECT_CLASS (parent_class)->finalize (object);
1343
gst_vdp_sink_init (VdpSink * vdp_sink)
1345
vdp_sink->device = NULL;
1347
vdp_sink->display_name = NULL;
1348
vdp_sink->par = NULL;
1350
vdp_sink->x_lock = g_mutex_new ();
1351
vdp_sink->flow_lock = g_mutex_new ();
1353
vdp_sink->synchronous = FALSE;
1354
vdp_sink->handle_events = TRUE;
1355
vdp_sink->handle_expose = TRUE;
1359
gst_vdp_sink_base_init (gpointer g_class)
1361
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1363
gst_element_class_set_details_simple (element_class,
1366
"VDPAU Sink", "Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>");
1368
gst_element_class_add_pad_template (element_class,
1369
gst_static_pad_template_get (&sink_template));
1373
gst_vdp_sink_class_init (VdpSinkClass * klass)
1375
GObjectClass *gobject_class;
1376
GstElementClass *gstelement_class;
1377
GstBaseSinkClass *gstbasesink_class;
1379
gobject_class = (GObjectClass *) klass;
1380
gstelement_class = (GstElementClass *) klass;
1381
gstbasesink_class = (GstBaseSinkClass *) klass;
1383
parent_class = g_type_class_peek_parent (klass);
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;
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",
1407
"the current frame will always be drawn in response to X Expose "
1408
"events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
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);
1422
/* ============================================================= */
1424
/* Public Methods */
1426
/* ============================================================= */
1428
/* =========================================== */
1430
/* Object typing & Creation */
1432
/* =========================================== */
1435
gst_vdp_sink_get_type (void)
1437
static GType vdp_sink_type = 0;
1439
if (!vdp_sink_type) {
1440
static const GTypeInfo vdp_sink_info = {
1441
sizeof (VdpSinkClass),
1442
gst_vdp_sink_base_init,
1444
(GClassInitFunc) gst_vdp_sink_class_init,
1449
(GInstanceInitFunc) gst_vdp_sink_init,
1451
static const GInterfaceInfo iface_info = {
1452
(GInterfaceInitFunc) gst_vdp_sink_interface_init,
1456
static const GInterfaceInfo navigation_info = {
1457
(GInterfaceInitFunc) gst_vdp_sink_navigation_init,
1461
static const GInterfaceInfo overlay_info = {
1462
(GInterfaceInitFunc) gst_vdp_sink_xoverlay_init,
1467
vdp_sink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
1468
"VdpSink", &vdp_sink_info, 0);
1470
g_type_add_interface_static (vdp_sink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
1472
g_type_add_interface_static (vdp_sink_type, GST_TYPE_NAVIGATION,
1474
g_type_add_interface_static (vdp_sink_type, GST_TYPE_X_OVERLAY,
1480
return vdp_sink_type;