- add sources.
[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 <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