vdpau_wrapper.c revision a4f78def
1/* 2 * Copyright (c) 2008-2015 NVIDIA Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#ifdef HAVE_CONFIG_H 25#include "config.h" 26#endif 27 28#include <dlfcn.h> 29#include <limits.h> 30#include <pthread.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <unistd.h> 35#include <vdpau/vdpau_x11.h> 36#if DRI2 37#include "mesa_dri2.h" 38#include <X11/Xlib.h> 39#endif 40#include "util.h" 41 42typedef void SetDllHandle( 43 void * driver_dll_handle 44); 45 46static void * _vdp_backend_dll; 47static void * _vdp_trace_dll; 48static void * _vdp_driver_dll; 49static VdpDeviceCreateX11 * _vdp_imp_device_create_x11_proc; 50 51#if defined(__GNUC__) 52 53static void _vdp_close_driver(void) __attribute__((destructor)); 54 55#endif 56 57#if DEBUG 58 59static void _vdp_wrapper_error_breakpoint(char const * file, int line, char const * function) 60{ 61 fprintf(stderr, "VDPAU wrapper: Error detected at %s:%d %s()\n", file, line, function); 62} 63 64#define _VDP_ERROR_BREAKPOINT() _vdp_wrapper_error_breakpoint(__FILE__, __LINE__, __FUNCTION__) 65 66#else 67 68#define _VDP_ERROR_BREAKPOINT() 69 70#endif 71 72#define DRIVER_FALLBACK_LIB_FORMAT "libvdpau_%s.so" 73#define DRIVER_LIB_FORMAT "%s/libvdpau_%s.so.1" 74 75static char * _vdp_get_driver_name_from_dri2( 76 Display * display, 77 int screen 78) 79{ 80 char * driver_name = NULL; 81#if DRI2 82 Window root = RootWindow(display, screen); 83 int event_base, error_base; 84 int major, minor; 85 char * device_name; 86 87 if (!_vdp_DRI2QueryExtension(display, &event_base, &error_base)) { 88 return NULL; 89 } 90 91 if (!_vdp_DRI2QueryVersion(display, &major, &minor) || 92 (major < 1 || (major == 1 && minor < 2))) { 93 _vdp_DRI2RemoveExtension(display); 94 return NULL; 95 } 96 97 if (!_vdp_DRI2Connect(display, root, &driver_name, &device_name)) { 98 _vdp_DRI2RemoveExtension(display); 99 return NULL; 100 } 101 102 XFree(device_name); 103 _vdp_DRI2RemoveExtension(display); 104#else 105 (void) display; (void) screen; 106#endif /* DRI2 */ 107 return driver_name; 108} 109 110static VdpStatus _vdp_open_driver( 111 Display * display, 112 int screen) 113{ 114 char const * vdpau_driver; 115 char * vdpau_driver_dri2 = NULL; 116 const char * vdpau_driver_path = NULL; 117 char vdpau_driver_lib[PATH_MAX]; 118 char const * vdpau_trace; 119 char const * func_name; 120 121 vdpau_driver = secure_getenv("VDPAU_DRIVER"); 122 if (vdpau_driver) { 123 if (strchr(vdpau_driver, '/')) { 124 vdpau_driver = NULL; 125 } 126 } 127 if (!vdpau_driver) { 128 vdpau_driver = vdpau_driver_dri2 = 129 _vdp_get_driver_name_from_dri2(display, screen); 130 } 131 if (!vdpau_driver) { 132 vdpau_driver = "nvidia"; 133 } 134 135 /* Don't allow setuid apps to use VDPAU_DRIVER_PATH */ 136 vdpau_driver_path = secure_getenv("VDPAU_DRIVER_PATH"); 137 if (vdpau_driver_path && 138 snprintf(vdpau_driver_lib, sizeof(vdpau_driver_lib), 139 DRIVER_LIB_FORMAT, vdpau_driver_path, vdpau_driver) < 140 sizeof(vdpau_driver_lib)) { 141 _vdp_driver_dll = dlopen(vdpau_driver_lib, RTLD_NOW | RTLD_GLOBAL); 142 } 143 144 /* Fallback to VDPAU_MODULEDIR when VDPAU_DRIVER_PATH is not set, 145 * or if we fail to create the driver path/dlopen the library. */ 146 if (!_vdp_driver_dll) { 147 if (snprintf(vdpau_driver_lib, sizeof(vdpau_driver_lib), 148 DRIVER_LIB_FORMAT, VDPAU_MODULEDIR, vdpau_driver) >= 149 sizeof(vdpau_driver_lib)) { 150 fprintf(stderr, "Failed to construct driver path: path too long\n"); 151 if (vdpau_driver_dri2) { 152 XFree(vdpau_driver_dri2); 153 vdpau_driver_dri2 = NULL; 154 } 155 _VDP_ERROR_BREAKPOINT(); 156 return VDP_STATUS_NO_IMPLEMENTATION; 157 } 158 else { 159 _vdp_driver_dll = dlopen(vdpau_driver_lib, RTLD_NOW | RTLD_GLOBAL); 160 } 161 } 162 163 if (!_vdp_driver_dll) { 164 /* Try again using the old path, which is guaranteed to fit in PATH_MAX 165 * if the complete path fit above. */ 166 snprintf(vdpau_driver_lib, sizeof(vdpau_driver_lib), 167 DRIVER_FALLBACK_LIB_FORMAT, vdpau_driver); 168 _vdp_driver_dll = dlopen(vdpau_driver_lib, RTLD_NOW | RTLD_GLOBAL); 169 } 170 171 if (vdpau_driver_dri2) { 172 XFree(vdpau_driver_dri2); 173 vdpau_driver_dri2 = NULL; 174 } 175 176 if (!_vdp_driver_dll) { 177 fprintf(stderr, "Failed to open VDPAU backend %s\n", dlerror()); 178 _VDP_ERROR_BREAKPOINT(); 179 return VDP_STATUS_NO_IMPLEMENTATION; 180 } 181 182 _vdp_backend_dll = _vdp_driver_dll; 183 184 vdpau_trace = secure_getenv("VDPAU_TRACE"); 185 if (vdpau_trace && atoi(vdpau_trace)) { 186 SetDllHandle * set_dll_handle; 187 188 _vdp_trace_dll = dlopen(VDPAU_MODULEDIR "/libvdpau_trace.so.1", 189 RTLD_NOW | RTLD_GLOBAL); 190 if (!_vdp_trace_dll) { 191 fprintf(stderr, "Failed to open VDPAU trace library %s\n", dlerror()); 192 _VDP_ERROR_BREAKPOINT(); 193 return VDP_STATUS_NO_IMPLEMENTATION; 194 } 195 196 set_dll_handle = (SetDllHandle*)dlsym( 197 _vdp_trace_dll, 198 "vdp_trace_set_backend_handle" 199 ); 200 if (!set_dll_handle) { 201 fprintf(stderr, "%s\n", dlerror()); 202 _VDP_ERROR_BREAKPOINT(); 203 return VDP_STATUS_NO_IMPLEMENTATION; 204 } 205 206 set_dll_handle(_vdp_backend_dll); 207 208 _vdp_backend_dll = _vdp_trace_dll; 209 210 func_name = "vdp_trace_device_create_x11"; 211 } 212 else { 213 func_name = "vdp_imp_device_create_x11"; 214 } 215 216 _vdp_imp_device_create_x11_proc = (VdpDeviceCreateX11*)dlsym( 217 _vdp_backend_dll, 218 func_name 219 ); 220 if (!_vdp_imp_device_create_x11_proc) { 221 fprintf(stderr, "%s\n", dlerror()); 222 _VDP_ERROR_BREAKPOINT(); 223 return VDP_STATUS_NO_IMPLEMENTATION; 224 } 225 226 return VDP_STATUS_OK; 227} 228 229static void _vdp_close_driver(void) 230{ 231 if (_vdp_driver_dll) { 232 dlclose(_vdp_driver_dll); 233 _vdp_driver_dll = NULL; 234 } 235 if (_vdp_trace_dll) { 236 dlclose(_vdp_trace_dll); 237 _vdp_trace_dll = NULL; 238 } 239 _vdp_backend_dll = NULL; 240 _vdp_imp_device_create_x11_proc = NULL; 241} 242 243static VdpGetProcAddress * _imp_get_proc_address; 244static VdpVideoSurfacePutBitsYCbCr * _imp_vid_put_bits_y_cb_cr; 245static VdpPresentationQueueSetBackgroundColor * _imp_pq_set_bg_color; 246static int _running_under_flash; 247static int _enable_flash_uv_swap = 1; 248static int _disable_flash_pq_bg_color = 1; 249 250static VdpStatus vid_put_bits_y_cb_cr_swapped( 251 VdpVideoSurface surface, 252 VdpYCbCrFormat source_ycbcr_format, 253 void const * const * source_data, 254 uint32_t const * source_pitches 255) 256{ 257 void const * data_reordered[3]; 258 void const * const * data; 259 260 if (source_ycbcr_format == VDP_YCBCR_FORMAT_YV12) { 261 data_reordered[0] = source_data[0]; 262 data_reordered[1] = source_data[2]; 263 data_reordered[2] = source_data[1]; 264 /* 265 * source_pitches[1] and source_pitches[2] should be equal, 266 * so no need to re-order. 267 */ 268 data = data_reordered; 269 } 270 else { 271 data = source_data; 272 } 273 274 return _imp_vid_put_bits_y_cb_cr( 275 surface, 276 source_ycbcr_format, 277 data, 278 source_pitches 279 ); 280} 281 282static VdpStatus pq_set_bg_color_noop( 283 VdpPresentationQueue presentation_queue, 284 VdpColor * const background_color 285) 286{ 287 (void) presentation_queue; (void) background_color; 288 return VDP_STATUS_OK; 289} 290 291static VdpStatus vdp_wrapper_get_proc_address( 292 VdpDevice device, 293 VdpFuncId function_id, 294 /* output parameters follow */ 295 void * * function_pointer 296) 297{ 298 VdpStatus status; 299 300 status = _imp_get_proc_address(device, function_id, function_pointer); 301 if (status != VDP_STATUS_OK) { 302 return status; 303 } 304 305 if (_running_under_flash) { 306 switch (function_id) { 307 case VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR: 308 if (_enable_flash_uv_swap) { 309 _imp_vid_put_bits_y_cb_cr = *function_pointer; 310 *function_pointer = vid_put_bits_y_cb_cr_swapped; 311 } 312 break; 313 case VDP_FUNC_ID_PRESENTATION_QUEUE_SET_BACKGROUND_COLOR: 314 if (_disable_flash_pq_bg_color) { 315 _imp_pq_set_bg_color = *function_pointer; 316 *function_pointer = pq_set_bg_color_noop; 317 } 318 break; 319 default: 320 break; 321 } 322 } 323 324 return VDP_STATUS_OK; 325} 326 327static void init_running_under_flash(void) 328{ 329 FILE *fp; 330 char buffer[1024]; 331 int ret, i; 332 333 fp = fopen("/proc/self/cmdline", "r"); 334 if (!fp) { 335 return; 336 } 337 ret = fread(buffer, 1, sizeof(buffer) - 1, fp); 338 fclose(fp); 339 if (ret < 0) { 340 return; 341 } 342 /* 343 * Sometimes the file contains null between arguments. Wipe these out so 344 * strstr doesn't stop early. 345 */ 346 for (i = 0; i < ret; i++) { 347 if (buffer[i] == '\0') { 348 buffer[i] = 'x'; 349 } 350 } 351 buffer[ret] = '\0'; 352 353 if (strstr(buffer, "libflashplayer") != NULL) { 354 _running_under_flash = 1; 355 } 356} 357 358static void init_config(void) 359{ 360 FILE *fp; 361 char buffer[1024]; 362 363 fp = fopen(VDPAU_SYSCONFDIR "/vdpau_wrapper.cfg", "r"); 364 if (!fp) { 365 return; 366 } 367 368 while (fgets(buffer, sizeof(buffer), fp) != NULL) { 369 char * equals = strchr(buffer, '='); 370 char * param; 371 372 if (equals == NULL) { 373 continue; 374 } 375 376 *equals = '\0'; 377 param = equals + 1; 378 379 if (!strcmp(buffer, "enable_flash_uv_swap")) { 380 _enable_flash_uv_swap = atoi(param); 381 } 382 else if (!strcmp(buffer, "disable_flash_pq_bg_color")) { 383 _disable_flash_pq_bg_color = atoi(param); 384 } 385 } 386 387 fclose(fp); 388} 389 390static void init_fixes(void) 391{ 392 init_running_under_flash(); 393 init_config(); 394} 395 396VdpStatus vdp_device_create_x11( 397 Display * display, 398 int screen, 399 /* output parameters follow */ 400 VdpDevice * device, 401 VdpGetProcAddress * * get_proc_address 402) 403{ 404 static pthread_once_t once = PTHREAD_ONCE_INIT; 405 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 406 VdpGetProcAddress *gpa; 407 VdpStatus status = VDP_STATUS_OK; 408 409 pthread_once(&once, init_fixes); 410 411 pthread_mutex_lock(&lock); 412 if (!_vdp_imp_device_create_x11_proc) { 413 status = _vdp_open_driver(display, screen); 414 if (status != VDP_STATUS_OK) 415 _vdp_close_driver(); 416 } 417 pthread_mutex_unlock(&lock); 418 419 if (status != VDP_STATUS_OK) 420 return status; 421 422 status = _vdp_imp_device_create_x11_proc(display, screen, device, &gpa); 423 if (status != VDP_STATUS_OK) { 424 return status; 425 } 426 427 *get_proc_address = vdp_wrapper_get_proc_address; 428 429 pthread_mutex_lock(&lock); 430 if (_imp_get_proc_address != gpa) { 431 if (_imp_get_proc_address == NULL) 432 _imp_get_proc_address = gpa; 433 else 434 /* Currently the wrapper can only deal with one back-end. 435 * This should never happen, but better safe than sorry. */ 436 status = VDP_STATUS_NO_IMPLEMENTATION; 437 } 438 pthread_mutex_unlock(&lock); 439 440 if (status != VDP_STATUS_OK) { 441 void *pv; 442 443 if (gpa(*device, VDP_FUNC_ID_DEVICE_DESTROY, &pv) == VDP_STATUS_OK) { 444 VdpDeviceDestroy *device_destroy = pv; 445 446 device_destroy(*device); 447 } 448 } 449 450 return status; 451} 452