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 "content/common/gpu/media/vaapi_wrapper.h" 6 7 #include <dlfcn.h> 8 9 #include "base/bind.h" 10 #include "base/logging.h" 11 #include "base/numerics/safe_conversions.h" 12 // Auto-generated for dlopen libva libraries 13 #include "content/common/gpu/media/va_stubs.h" 14 15 using content_common_gpu_media::kModuleVa; 16 using content_common_gpu_media::InitializeStubs; 17 using content_common_gpu_media::StubPathMap; 18 19 // libva-x11 depends on libva, so dlopen libva-x11 is enough 20 static const base::FilePath::CharType kVaLib[] = 21 FILE_PATH_LITERAL("libva-x11.so.1"); 22 23 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ 24 do { \ 25 DVLOG(1) << err_msg \ 26 << " VA error: " << vaErrorStr(va_error); \ 27 report_error_to_uma_cb_.Run(); \ 28 } while (0) 29 30 #define VA_LOG_ON_ERROR(va_error, err_msg) \ 31 do { \ 32 if ((va_error) != VA_STATUS_SUCCESS) \ 33 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ 34 } while (0) 35 36 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ 37 do { \ 38 if ((va_error) != VA_STATUS_SUCCESS) { \ 39 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ 40 return (ret); \ 41 } \ 42 } while (0) 43 44 namespace content { 45 46 // Maps Profile enum values to VaProfile values. 47 static VAProfile ProfileToVAProfile( 48 media::VideoCodecProfile profile, 49 const std::vector<VAProfile>& supported_profiles) { 50 51 VAProfile va_profile = VAProfileNone; 52 53 switch (profile) { 54 case media::H264PROFILE_BASELINE: 55 va_profile = VAProfileH264Baseline; 56 break; 57 case media::H264PROFILE_MAIN: 58 va_profile = VAProfileH264Main; 59 break; 60 // TODO(posciak): See if we can/want support other variants 61 // of media::H264PROFILE_HIGH*. 62 case media::H264PROFILE_HIGH: 63 va_profile = VAProfileH264High; 64 break; 65 default: 66 break; 67 } 68 69 bool supported = std::find(supported_profiles.begin(), 70 supported_profiles.end(), 71 va_profile) != supported_profiles.end(); 72 73 if (!supported && va_profile == VAProfileH264Baseline) { 74 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips 75 // the information whether the profile is constrained or not, so we have no 76 // way to know here. Try for baseline first, but if it is not supported, 77 // try constrained baseline and hope this is what it actually is 78 // (which in practice is true for a great majority of cases). 79 if (std::find(supported_profiles.begin(), 80 supported_profiles.end(), 81 VAProfileH264ConstrainedBaseline) != 82 supported_profiles.end()) { 83 va_profile = VAProfileH264ConstrainedBaseline; 84 DVLOG(1) << "Falling back to constrained baseline profile."; 85 } 86 } 87 88 return va_profile; 89 } 90 91 VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb) 92 : va_surface_id_(va_surface_id), 93 release_cb_(release_cb) { 94 DCHECK(!release_cb_.is_null()); 95 } 96 97 VASurface::~VASurface() { 98 release_cb_.Run(va_surface_id_); 99 } 100 101 VaapiWrapper::VaapiWrapper() 102 : va_display_(NULL), 103 va_config_id_(VA_INVALID_ID), 104 va_context_id_(VA_INVALID_ID) { 105 } 106 107 VaapiWrapper::~VaapiWrapper() { 108 DestroyPendingBuffers(); 109 DestroySurfaces(); 110 Deinitialize(); 111 } 112 113 scoped_ptr<VaapiWrapper> VaapiWrapper::Create( 114 media::VideoCodecProfile profile, 115 Display* x_display, 116 const base::Closure& report_error_to_uma_cb) { 117 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper()); 118 119 if (!vaapi_wrapper->Initialize(profile, x_display, report_error_to_uma_cb)) 120 vaapi_wrapper.reset(); 121 122 return vaapi_wrapper.Pass(); 123 } 124 125 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { 126 VADisplayAttribute item = {VADisplayAttribRenderMode, 127 1, // At least support '_LOCAL_OVERLAY'. 128 -1, // The maximum possible support 'ALL'. 129 VA_RENDER_MODE_LOCAL_GPU, 130 VA_DISPLAY_ATTRIB_SETTABLE}; 131 132 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); 133 if (va_res != VA_STATUS_SUCCESS) 134 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; 135 } 136 137 bool VaapiWrapper::Initialize(media::VideoCodecProfile profile, 138 Display* x_display, 139 const base::Closure& report_error_to_uma_cb) { 140 static bool vaapi_functions_initialized = PostSandboxInitialization(); 141 if (!vaapi_functions_initialized) { 142 DVLOG(1) << "Failed to initialize VAAPI libs"; 143 return false; 144 } 145 146 report_error_to_uma_cb_ = report_error_to_uma_cb; 147 148 base::AutoLock auto_lock(va_lock_); 149 150 va_display_ = vaGetDisplay(x_display); 151 if (!vaDisplayIsValid(va_display_)) { 152 DVLOG(1) << "Could not get a valid VA display"; 153 return false; 154 } 155 156 VAStatus va_res = vaInitialize(va_display_, &major_version_, &minor_version_); 157 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false); 158 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_; 159 160 if (VAAPIVersionLessThan(0, 34)) { 161 DVLOG(1) << "VAAPI version < 0.34 is not supported."; 162 return false; 163 } 164 165 // Query the driver for supported profiles. 166 int max_profiles = vaMaxNumProfiles(va_display_); 167 std::vector<VAProfile> supported_profiles( 168 base::checked_cast<size_t>(max_profiles)); 169 170 int num_supported_profiles; 171 va_res = vaQueryConfigProfiles( 172 va_display_, &supported_profiles[0], &num_supported_profiles); 173 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); 174 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { 175 DVLOG(1) << "vaQueryConfigProfiles returned: " << num_supported_profiles; 176 return false; 177 } 178 179 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles)); 180 181 VAProfile va_profile = ProfileToVAProfile(profile, supported_profiles); 182 if (va_profile == VAProfileNone) { 183 DVLOG(1) << "Unsupported profile"; 184 return false; 185 } 186 187 VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0}; 188 const VAEntrypoint kEntrypoint = VAEntrypointVLD; 189 va_res = vaGetConfigAttributes(va_display_, va_profile, kEntrypoint, 190 &attrib, 1); 191 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); 192 193 if (!(attrib.value & VA_RT_FORMAT_YUV420)) { 194 DVLOG(1) << "YUV420 not supported by this VAAPI implementation"; 195 return false; 196 } 197 198 TryToSetVADisplayAttributeToLocalGPU(); 199 200 va_res = vaCreateConfig(va_display_, va_profile, kEntrypoint, 201 &attrib, 1, &va_config_id_); 202 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); 203 204 return true; 205 } 206 207 void VaapiWrapper::Deinitialize() { 208 base::AutoLock auto_lock(va_lock_); 209 210 if (va_config_id_ != VA_INVALID_ID) { 211 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); 212 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); 213 } 214 215 if (va_display_) { 216 VAStatus va_res = vaTerminate(va_display_); 217 VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); 218 } 219 220 va_config_id_ = VA_INVALID_ID; 221 va_display_ = NULL; 222 } 223 224 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) { 225 return (major_version_ < major) || 226 (major_version_ == major && minor_version_ < minor); 227 } 228 229 bool VaapiWrapper::CreateSurfaces(gfx::Size size, 230 size_t num_surfaces, 231 std::vector<VASurfaceID>* va_surfaces) { 232 base::AutoLock auto_lock(va_lock_); 233 DVLOG(2) << "Creating " << num_surfaces << " surfaces"; 234 235 DCHECK(va_surfaces->empty()); 236 DCHECK(va_surface_ids_.empty()); 237 va_surface_ids_.resize(num_surfaces); 238 239 // Allocate surfaces in driver. 240 VAStatus va_res = vaCreateSurfaces(va_display_, 241 VA_RT_FORMAT_YUV420, 242 size.width(), size.height(), 243 &va_surface_ids_[0], 244 va_surface_ids_.size(), 245 NULL, 0); 246 247 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); 248 if (va_res != VA_STATUS_SUCCESS) { 249 va_surface_ids_.clear(); 250 return false; 251 } 252 253 // And create a context associated with them. 254 va_res = vaCreateContext(va_display_, va_config_id_, 255 size.width(), size.height(), VA_PROGRESSIVE, 256 &va_surface_ids_[0], va_surface_ids_.size(), 257 &va_context_id_); 258 259 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); 260 if (va_res != VA_STATUS_SUCCESS) { 261 DestroySurfaces(); 262 return false; 263 } 264 265 *va_surfaces = va_surface_ids_; 266 return true; 267 } 268 269 void VaapiWrapper::DestroySurfaces() { 270 base::AutoLock auto_lock(va_lock_); 271 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; 272 273 if (va_context_id_ != VA_INVALID_ID) { 274 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); 275 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); 276 } 277 278 if (!va_surface_ids_.empty()) { 279 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], 280 va_surface_ids_.size()); 281 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); 282 } 283 284 va_surface_ids_.clear(); 285 va_context_id_ = VA_INVALID_ID; 286 } 287 288 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, 289 size_t size, 290 void* buffer) { 291 base::AutoLock auto_lock(va_lock_); 292 293 VABufferID buffer_id; 294 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, 295 va_buffer_type, size, 296 1, buffer, &buffer_id); 297 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); 298 299 switch (va_buffer_type) { 300 case VASliceParameterBufferType: 301 case VASliceDataBufferType: 302 pending_slice_bufs_.push_back(buffer_id); 303 break; 304 305 default: 306 pending_va_bufs_.push_back(buffer_id); 307 break; 308 } 309 310 return true; 311 } 312 313 void VaapiWrapper::DestroyPendingBuffers() { 314 base::AutoLock auto_lock(va_lock_); 315 316 for (size_t i = 0; i < pending_va_bufs_.size(); ++i) { 317 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_bufs_[i]); 318 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 319 } 320 321 for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) { 322 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_bufs_[i]); 323 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 324 } 325 326 pending_va_bufs_.clear(); 327 pending_slice_bufs_.clear(); 328 } 329 330 bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) { 331 base::AutoLock auto_lock(va_lock_); 332 333 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); 334 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); 335 DVLOG(4) << "Decoding into VA surface " << va_surface_id; 336 337 // Get ready to decode into surface. 338 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, 339 va_surface_id); 340 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); 341 342 // Commit parameter and slice buffers. 343 va_res = vaRenderPicture(va_display_, va_context_id_, 344 &pending_va_bufs_[0], pending_va_bufs_.size()); 345 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); 346 347 va_res = vaRenderPicture(va_display_, va_context_id_, 348 &pending_slice_bufs_[0], 349 pending_slice_bufs_.size()); 350 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); 351 352 // Instruct HW decoder to start processing committed buffers (decode this 353 // picture). This does not block until the end of decode. 354 va_res = vaEndPicture(va_display_, va_context_id_); 355 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); 356 357 return true; 358 } 359 360 bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) { 361 bool result = SubmitDecode(va_surface_id); 362 DestroyPendingBuffers(); 363 return result; 364 } 365 366 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, 367 Pixmap x_pixmap, 368 gfx::Size dest_size) { 369 base::AutoLock auto_lock(va_lock_); 370 371 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); 372 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); 373 374 // Put the data into an X Pixmap. 375 va_res = vaPutSurface(va_display_, 376 va_surface_id, 377 x_pixmap, 378 0, 0, dest_size.width(), dest_size.height(), 379 0, 0, dest_size.width(), dest_size.height(), 380 NULL, 0, 0); 381 VA_SUCCESS_OR_RETURN(va_res, "Failed putting decode surface to pixmap", 382 false); 383 return true; 384 } 385 386 bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id, 387 VAImage* image, 388 void** mem) { 389 base::AutoLock auto_lock(va_lock_); 390 391 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); 392 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); 393 394 // Derive a VAImage from the VASurface 395 va_res = vaDeriveImage(va_display_, va_surface_id, image); 396 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed"); 397 if (va_res != VA_STATUS_SUCCESS) 398 return false; 399 400 // Map the VAImage into memory 401 va_res = vaMapBuffer(va_display_, image->buf, mem); 402 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); 403 if (va_res == VA_STATUS_SUCCESS) 404 return true; 405 406 vaDestroyImage(va_display_, image->image_id); 407 return false; 408 } 409 410 void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) { 411 base::AutoLock auto_lock(va_lock_); 412 413 vaUnmapBuffer(va_display_, image->buf); 414 vaDestroyImage(va_display_, image->image_id); 415 } 416 417 // static 418 bool VaapiWrapper::PostSandboxInitialization() { 419 StubPathMap paths; 420 paths[kModuleVa].push_back(kVaLib); 421 422 return InitializeStubs(paths); 423 } 424 425 } // namespace content 426