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