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