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