1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <dlfcn.h> 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "content/common/gpu/media/vaapi_wrapper.h" 10 11 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ 12 do { \ 13 DVLOG(1) << err_msg \ 14 << " VA error: " << VAAPI_ErrorStr(va_error); \ 15 report_error_to_uma_cb_.Run(); \ 16 } while (0) 17 18 #define VA_LOG_ON_ERROR(va_error, err_msg) \ 19 do { \ 20 if ((va_error) != VA_STATUS_SUCCESS) \ 21 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ 22 } while (0) 23 24 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ 25 do { \ 26 if ((va_error) != VA_STATUS_SUCCESS) { \ 27 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ 28 return (ret); \ 29 } \ 30 } while (0) 31 32 namespace content { 33 34 static void *vaapi_handle = NULL; 35 static void *vaapi_x11_handle = NULL; 36 37 typedef VAStatus (*VaapiBeginPicture)(VADisplay dpy, 38 VAContextID context, 39 VASurfaceID render_target); 40 typedef VAStatus (*VaapiCreateBuffer)(VADisplay dpy, 41 VAContextID context, 42 VABufferType type, 43 unsigned int size, 44 unsigned int num_elements, 45 void *data, 46 VABufferID *buf_id); 47 typedef VAStatus (*VaapiCreateConfig)(VADisplay dpy, 48 VAProfile profile, 49 VAEntrypoint entrypoint, 50 VAConfigAttrib *attrib_list, 51 int num_attribs, 52 VAConfigID *config_id); 53 typedef VAStatus (*VaapiCreateContext)(VADisplay dpy, 54 VAConfigID config_id, 55 int picture_width, 56 int picture_height, 57 int flag, 58 VASurfaceID *render_targets, 59 int num_render_targets, 60 VAContextID *context); 61 // In VAAPI version < 0.34, vaCreateSurface has 6 parameters, but in VAAPI 62 // version >= 0.34, vaCreateSurface has 8 parameters. 63 // TODO(chihchung): Remove the old path once ChromeOS updates to 1.2.1. 64 typedef void *VaapiCreateSurfaces; 65 typedef VAStatus (*VaapiCreateSurfaces6)(VADisplay dpy, 66 int width, 67 int height, 68 int format, 69 int num_surfaces, 70 VASurfaceID *surfaces); 71 typedef VAStatus (*VaapiCreateSurfaces8)(VADisplay dpy, 72 unsigned int format, 73 unsigned int width, 74 unsigned int height, 75 VASurfaceID *surfaces, 76 unsigned int num_surfaces, 77 VASurfaceAttrib *attrib_list, 78 unsigned int num_attribs); 79 typedef VAStatus (*VaapiDestroyBuffer)(VADisplay dpy, VABufferID buffer_id); 80 typedef VAStatus (*VaapiDestroyConfig)(VADisplay dpy, VAConfigID config_id); 81 typedef VAStatus (*VaapiDestroyContext)(VADisplay dpy, VAContextID context); 82 typedef VAStatus (*VaapiDestroySurfaces)(VADisplay dpy, 83 VASurfaceID *surfaces, 84 int num_surfaces); 85 typedef int (*VaapiDisplayIsValid)(VADisplay dpy); 86 typedef VAStatus (*VaapiEndPicture)(VADisplay dpy, VAContextID context); 87 typedef const char* (*VaapiErrorStr)(VAStatus error_status); 88 typedef VAStatus (*VaapiGetConfigAttributes)(VADisplay dpy, 89 VAProfile profile, 90 VAEntrypoint entrypoint, 91 VAConfigAttrib *attrib_list, 92 int num_attribs); 93 typedef VADisplay (*VaapiGetDisplay)(Display *dpy); 94 typedef VAStatus (*VaapiInitialize)(VADisplay dpy, 95 int *major_version, 96 int *minor_version); 97 typedef VAStatus (*VaapiPutSurface)(VADisplay dpy, 98 VASurfaceID surface, 99 Drawable draw, 100 short srcx, 101 short srcy, 102 unsigned short srcw, 103 unsigned short srch, 104 short destx, 105 short desty, 106 unsigned short destw, 107 unsigned short desth, 108 VARectangle *cliprects, 109 unsigned int number_cliprects, 110 unsigned int flags); 111 typedef VAStatus (*VaapiRenderPicture)(VADisplay dpy, 112 VAContextID context, 113 VABufferID *buffers, 114 int num_buffers); 115 typedef VAStatus (*VaapiSyncSurface)(VADisplay dpy, VASurfaceID render_target); 116 typedef VAStatus (*VaapiTerminate)(VADisplay dpy); 117 118 #define VAAPI_SYM(name, handle) Vaapi##name VAAPI_##name = NULL 119 120 VAAPI_SYM(BeginPicture, vaapi_handle); 121 VAAPI_SYM(CreateBuffer, vaapi_handle); 122 VAAPI_SYM(CreateConfig, vaapi_handle); 123 VAAPI_SYM(CreateContext, vaapi_handle); 124 VAAPI_SYM(CreateSurfaces, vaapi_handle); 125 VAAPI_SYM(DestroyBuffer, vaapi_handle); 126 VAAPI_SYM(DestroyConfig, vaapi_handle); 127 VAAPI_SYM(DestroyContext, vaapi_handle); 128 VAAPI_SYM(DestroySurfaces, vaapi_handle); 129 VAAPI_SYM(DisplayIsValid, vaapi_handle); 130 VAAPI_SYM(EndPicture, vaapi_handle); 131 VAAPI_SYM(ErrorStr, vaapi_handle); 132 VAAPI_SYM(GetConfigAttributes, vaapi_handle); 133 VAAPI_SYM(GetDisplay, vaapi_x11_handle); 134 VAAPI_SYM(Initialize, vaapi_handle); 135 VAAPI_SYM(PutSurface, vaapi_x11_handle); 136 VAAPI_SYM(RenderPicture, vaapi_handle); 137 VAAPI_SYM(SyncSurface, vaapi_x11_handle); 138 VAAPI_SYM(Terminate, vaapi_handle); 139 140 #undef VAAPI_SYM 141 142 // Maps Profile enum values to VaProfile values. 143 static bool ProfileToVAProfile(media::VideoCodecProfile profile, 144 VAProfile* va_profile) { 145 switch (profile) { 146 case media::H264PROFILE_BASELINE: 147 *va_profile = VAProfileH264Baseline; 148 break; 149 case media::H264PROFILE_MAIN: 150 *va_profile = VAProfileH264Main; 151 break; 152 // TODO(posciak): See if we can/want support other variants 153 // of media::H264PROFILE_HIGH*. 154 case media::H264PROFILE_HIGH: 155 *va_profile = VAProfileH264High; 156 break; 157 default: 158 return false; 159 } 160 return true; 161 } 162 163 VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb) 164 : va_surface_id_(va_surface_id), 165 release_cb_(release_cb) { 166 DCHECK(!release_cb_.is_null()); 167 } 168 169 VASurface::~VASurface() { 170 release_cb_.Run(va_surface_id_); 171 } 172 173 VaapiWrapper::VaapiWrapper() 174 : va_display_(NULL), 175 va_config_id_(VA_INVALID_ID), 176 va_context_id_(VA_INVALID_ID) { 177 } 178 179 VaapiWrapper::~VaapiWrapper() { 180 DestroyPendingBuffers(); 181 DestroySurfaces(); 182 Deinitialize(); 183 } 184 185 scoped_ptr<VaapiWrapper> VaapiWrapper::Create( 186 media::VideoCodecProfile profile, 187 Display* x_display, 188 const base::Closure& report_error_to_uma_cb) { 189 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper()); 190 191 if (!vaapi_wrapper->Initialize(profile, x_display, report_error_to_uma_cb)) 192 vaapi_wrapper.reset(); 193 194 return vaapi_wrapper.Pass(); 195 } 196 197 bool VaapiWrapper::Initialize(media::VideoCodecProfile profile, 198 Display* x_display, 199 const base::Closure& report_error_to_uma_cb) { 200 static bool vaapi_functions_initialized = PostSandboxInitialization(); 201 if (!vaapi_functions_initialized) { 202 DVLOG(1) << "Failed to initialize VAAPI libs"; 203 return false; 204 } 205 206 report_error_to_uma_cb_ = report_error_to_uma_cb; 207 208 base::AutoLock auto_lock(va_lock_); 209 210 VAProfile va_profile; 211 if (!ProfileToVAProfile(profile, &va_profile)) { 212 DVLOG(1) << "Unsupported profile"; 213 return false; 214 } 215 216 va_display_ = VAAPI_GetDisplay(x_display); 217 if (!VAAPI_DisplayIsValid(va_display_)) { 218 DVLOG(1) << "Could not get a valid VA display"; 219 return false; 220 } 221 222 VAStatus va_res; 223 va_res = VAAPI_Initialize(va_display_, &major_version_, &minor_version_); 224 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false); 225 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_; 226 227 VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0}; 228 229 const VAEntrypoint kEntrypoint = VAEntrypointVLD; 230 va_res = VAAPI_GetConfigAttributes(va_display_, va_profile, kEntrypoint, 231 &attrib, 1); 232 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); 233 234 if (!(attrib.value & VA_RT_FORMAT_YUV420)) { 235 DVLOG(1) << "YUV420 not supported by this VAAPI implementation"; 236 return false; 237 } 238 239 va_res = VAAPI_CreateConfig(va_display_, va_profile, kEntrypoint, 240 &attrib, 1, &va_config_id_); 241 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); 242 243 return true; 244 } 245 246 void VaapiWrapper::Deinitialize() { 247 base::AutoLock auto_lock(va_lock_); 248 249 if (va_config_id_ != VA_INVALID_ID) { 250 VAStatus va_res = VAAPI_DestroyConfig(va_display_, va_config_id_); 251 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); 252 } 253 254 if (va_display_) { 255 VAStatus va_res = VAAPI_Terminate(va_display_); 256 VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); 257 } 258 259 va_config_id_ = VA_INVALID_ID; 260 va_display_ = NULL; 261 } 262 263 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) { 264 return (major_version_ < major) || 265 (major_version_ == major && minor_version_ < minor); 266 } 267 268 bool VaapiWrapper::CreateSurfaces(gfx::Size size, 269 size_t num_surfaces, 270 std::vector<VASurfaceID>* va_surfaces) { 271 base::AutoLock auto_lock(va_lock_); 272 DVLOG(2) << "Creating " << num_surfaces << " surfaces"; 273 274 DCHECK(va_surfaces->empty()); 275 DCHECK(va_surface_ids_.empty()); 276 va_surface_ids_.resize(num_surfaces); 277 278 // Allocate surfaces in driver. 279 VAStatus va_res; 280 if (VAAPIVersionLessThan(0, 34)) { 281 va_res = reinterpret_cast<VaapiCreateSurfaces6>(VAAPI_CreateSurfaces)( 282 va_display_, 283 size.width(), size.height(), 284 VA_RT_FORMAT_YUV420, 285 va_surface_ids_.size(), 286 &va_surface_ids_[0]); 287 } else { 288 va_res = reinterpret_cast<VaapiCreateSurfaces8>(VAAPI_CreateSurfaces)( 289 va_display_, 290 VA_RT_FORMAT_YUV420, 291 size.width(), size.height(), 292 &va_surface_ids_[0], 293 va_surface_ids_.size(), 294 NULL, 0); 295 } 296 297 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); 298 if (va_res != VA_STATUS_SUCCESS) { 299 va_surface_ids_.clear(); 300 return false; 301 } 302 303 // And create a context associated with them. 304 va_res = VAAPI_CreateContext(va_display_, va_config_id_, 305 size.width(), size.height(), VA_PROGRESSIVE, 306 &va_surface_ids_[0], va_surface_ids_.size(), 307 &va_context_id_); 308 309 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); 310 if (va_res != VA_STATUS_SUCCESS) { 311 DestroySurfaces(); 312 return false; 313 } 314 315 *va_surfaces = va_surface_ids_; 316 return true; 317 } 318 319 void VaapiWrapper::DestroySurfaces() { 320 base::AutoLock auto_lock(va_lock_); 321 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; 322 323 if (va_context_id_ != VA_INVALID_ID) { 324 VAStatus va_res = VAAPI_DestroyContext(va_display_, va_context_id_); 325 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); 326 } 327 328 if (!va_surface_ids_.empty()) { 329 VAStatus va_res = VAAPI_DestroySurfaces(va_display_, &va_surface_ids_[0], 330 va_surface_ids_.size()); 331 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); 332 } 333 334 va_surface_ids_.clear(); 335 va_context_id_ = VA_INVALID_ID; 336 } 337 338 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, 339 size_t size, 340 void* buffer) { 341 base::AutoLock auto_lock(va_lock_); 342 343 VABufferID buffer_id; 344 VAStatus va_res = VAAPI_CreateBuffer(va_display_, va_context_id_, 345 va_buffer_type, size, 346 1, buffer, &buffer_id); 347 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); 348 349 switch (va_buffer_type) { 350 case VASliceParameterBufferType: 351 case VASliceDataBufferType: 352 pending_slice_bufs_.push_back(buffer_id); 353 break; 354 355 default: 356 pending_va_bufs_.push_back(buffer_id); 357 break; 358 } 359 360 return true; 361 } 362 363 void VaapiWrapper::DestroyPendingBuffers() { 364 base::AutoLock auto_lock(va_lock_); 365 366 for (size_t i = 0; i < pending_va_bufs_.size(); ++i) { 367 VAStatus va_res = VAAPI_DestroyBuffer(va_display_, pending_va_bufs_[i]); 368 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 369 } 370 371 for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) { 372 VAStatus va_res = VAAPI_DestroyBuffer(va_display_, pending_slice_bufs_[i]); 373 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 374 } 375 376 pending_va_bufs_.clear(); 377 pending_slice_bufs_.clear(); 378 } 379 380 bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) { 381 base::AutoLock auto_lock(va_lock_); 382 383 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); 384 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); 385 DVLOG(4) << "Decoding into VA surface " << va_surface_id; 386 387 // Get ready to decode into surface. 388 VAStatus va_res = VAAPI_BeginPicture(va_display_, va_context_id_, 389 va_surface_id); 390 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); 391 392 // Commit parameter and slice buffers. 393 va_res = VAAPI_RenderPicture(va_display_, va_context_id_, 394 &pending_va_bufs_[0], pending_va_bufs_.size()); 395 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); 396 397 va_res = VAAPI_RenderPicture(va_display_, va_context_id_, 398 &pending_slice_bufs_[0], 399 pending_slice_bufs_.size()); 400 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); 401 402 // Instruct HW decoder to start processing committed buffers (decode this 403 // picture). This does not block until the end of decode. 404 va_res = VAAPI_EndPicture(va_display_, va_context_id_); 405 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); 406 407 return true; 408 } 409 410 bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) { 411 bool result = SubmitDecode(va_surface_id); 412 DestroyPendingBuffers(); 413 return result; 414 } 415 416 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, 417 Pixmap x_pixmap, 418 gfx::Size dest_size) { 419 base::AutoLock auto_lock(va_lock_); 420 421 VAStatus va_res = VAAPI_SyncSurface(va_display_, va_surface_id); 422 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); 423 424 // Put the data into an X Pixmap. 425 va_res = VAAPI_PutSurface(va_display_, 426 va_surface_id, 427 x_pixmap, 428 0, 0, dest_size.width(), dest_size.height(), 429 0, 0, dest_size.width(), dest_size.height(), 430 NULL, 0, 0); 431 VA_SUCCESS_OR_RETURN(va_res, "Failed putting decode surface to pixmap", 432 false); 433 return true; 434 } 435 436 // static 437 bool VaapiWrapper::PostSandboxInitialization() { 438 vaapi_handle = dlopen("libva.so.1", RTLD_NOW); 439 vaapi_x11_handle = dlopen("libva-x11.so.1", RTLD_NOW); 440 441 if (!vaapi_handle || !vaapi_x11_handle) 442 return false; 443 #define VAAPI_DLSYM_OR_RETURN_ON_ERROR(name, handle) \ 444 do { \ 445 VAAPI_##name = reinterpret_cast<Vaapi##name>(dlsym((handle), "va"#name)); \ 446 if (VAAPI_##name == NULL) { \ 447 DVLOG(1) << "Failed to dlsym va"#name; \ 448 return false; \ 449 } \ 450 } while (0) 451 452 VAAPI_DLSYM_OR_RETURN_ON_ERROR(BeginPicture, vaapi_handle); 453 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateBuffer, vaapi_handle); 454 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateConfig, vaapi_handle); 455 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateContext, vaapi_handle); 456 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateSurfaces, vaapi_handle); 457 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyBuffer, vaapi_handle); 458 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyConfig, vaapi_handle); 459 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyContext, vaapi_handle); 460 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroySurfaces, vaapi_handle); 461 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DisplayIsValid, vaapi_handle); 462 VAAPI_DLSYM_OR_RETURN_ON_ERROR(EndPicture, vaapi_handle); 463 VAAPI_DLSYM_OR_RETURN_ON_ERROR(ErrorStr, vaapi_handle); 464 VAAPI_DLSYM_OR_RETURN_ON_ERROR(GetConfigAttributes, vaapi_handle); 465 VAAPI_DLSYM_OR_RETURN_ON_ERROR(GetDisplay, vaapi_x11_handle); 466 VAAPI_DLSYM_OR_RETURN_ON_ERROR(Initialize, vaapi_handle); 467 VAAPI_DLSYM_OR_RETURN_ON_ERROR(PutSurface, vaapi_x11_handle); 468 VAAPI_DLSYM_OR_RETURN_ON_ERROR(RenderPicture, vaapi_handle); 469 VAAPI_DLSYM_OR_RETURN_ON_ERROR(SyncSurface, vaapi_handle); 470 VAAPI_DLSYM_OR_RETURN_ON_ERROR(Terminate, vaapi_handle); 471 #undef VAAPI_DLSYM 472 473 return true; 474 } 475 476 } // namespace content 477