Upstream version 10.39.225.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
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/numerics/safe_conversions.h"
13 // Auto-generated for dlopen libva libraries
14 #include "content/common/gpu/media/va_stubs.h"
15 #include "third_party/libyuv/include/libyuv.h"
16
17 using content_common_gpu_media::kModuleVa;
18 using content_common_gpu_media::InitializeStubs;
19 using content_common_gpu_media::StubPathMap;
20
21 // libva-x11 depends on libva, so dlopen libva-x11 is enough
22 static const base::FilePath::CharType kVaLib[] =
23     FILE_PATH_LITERAL("libva-x11.so.1");
24
25 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg)         \
26   do {                                                     \
27     DVLOG(1) << err_msg                                    \
28              << " VA error: " << vaErrorStr(va_error);     \
29     report_error_to_uma_cb_.Run();                         \
30   } while (0)
31
32 #define VA_LOG_ON_ERROR(va_error, err_msg)                 \
33   do {                                                     \
34     if ((va_error) != VA_STATUS_SUCCESS)                   \
35       LOG_VA_ERROR_AND_REPORT(va_error, err_msg);          \
36   } while (0)
37
38 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret)       \
39   do {                                                     \
40     if ((va_error) != VA_STATUS_SUCCESS) {                 \
41       LOG_VA_ERROR_AND_REPORT(va_error, err_msg);          \
42       return (ret);                                        \
43     }                                                      \
44   } while (0)
45
46 namespace content {
47
48 // Config attributes common for both encode and decode.
49 static const VAConfigAttrib kCommonVAConfigAttribs[] = {
50     {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420},
51 };
52
53 // Attributes required for encode.
54 static const VAConfigAttrib kEncodeVAConfigAttribs[] = {
55     {VAConfigAttribRateControl, VA_RC_CBR},
56     {VAConfigAttribEncPackedHeaders,
57      VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE},
58 };
59
60 struct ProfileMap {
61   media::VideoCodecProfile profile;
62   VAProfile va_profile;
63 };
64
65 // A map between VideoCodecProfile and VAProfile.
66 static const ProfileMap kProfileMap[] = {
67     {media::H264PROFILE_BASELINE, VAProfileH264Baseline},
68     {media::H264PROFILE_MAIN, VAProfileH264Main},
69     // TODO(posciak): See if we can/want support other variants of
70     // media::H264PROFILE_HIGH*.
71     {media::H264PROFILE_HIGH, VAProfileH264High},
72 };
73
74 static std::vector<VAConfigAttrib> GetRequiredAttribs(
75     VaapiWrapper::CodecMode mode) {
76   std::vector<VAConfigAttrib> required_attribs;
77   required_attribs.insert(
78       required_attribs.end(),
79       kCommonVAConfigAttribs,
80       kCommonVAConfigAttribs + arraysize(kCommonVAConfigAttribs));
81   if (mode == VaapiWrapper::kEncode) {
82     required_attribs.insert(
83         required_attribs.end(),
84         kEncodeVAConfigAttribs,
85         kEncodeVAConfigAttribs + arraysize(kEncodeVAConfigAttribs));
86   }
87   return required_attribs;
88 }
89
90 // Maps Profile enum values to VaProfile values.
91 static VAProfile ProfileToVAProfile(
92     media::VideoCodecProfile profile,
93     const std::vector<VAProfile>& supported_profiles) {
94
95   VAProfile va_profile = VAProfileNone;
96   for (size_t i = 0; i < arraysize(kProfileMap); i++) {
97     if (kProfileMap[i].profile == profile) {
98       va_profile = kProfileMap[i].va_profile;
99       break;
100     }
101   }
102
103   bool supported = std::find(supported_profiles.begin(),
104                              supported_profiles.end(),
105                              va_profile) != supported_profiles.end();
106
107   if (!supported && va_profile == VAProfileH264Baseline) {
108     // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
109     // the information whether the profile is constrained or not, so we have no
110     // way to know here. Try for baseline first, but if it is not supported,
111     // try constrained baseline and hope this is what it actually is
112     // (which in practice is true for a great majority of cases).
113     if (std::find(supported_profiles.begin(),
114                   supported_profiles.end(),
115                   VAProfileH264ConstrainedBaseline) !=
116         supported_profiles.end()) {
117       va_profile = VAProfileH264ConstrainedBaseline;
118       DVLOG(1) << "Falling back to constrained baseline profile.";
119     }
120   }
121
122   return va_profile;
123 }
124
125 VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb)
126     : va_surface_id_(va_surface_id),
127       release_cb_(release_cb) {
128   DCHECK(!release_cb_.is_null());
129 }
130
131 VASurface::~VASurface() {
132   release_cb_.Run(va_surface_id_);
133 }
134
135 VaapiWrapper::VaapiWrapper()
136     : va_display_(NULL),
137       va_config_id_(VA_INVALID_ID),
138       va_context_id_(VA_INVALID_ID) {
139 }
140
141 VaapiWrapper::~VaapiWrapper() {
142   DestroyPendingBuffers();
143   DestroyCodedBuffers();
144   DestroySurfaces();
145   Deinitialize();
146 }
147
148 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
149     CodecMode mode,
150     media::VideoCodecProfile profile,
151     Display* x_display,
152     const base::Closure& report_error_to_uma_cb) {
153   scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
154
155   if (!vaapi_wrapper->Initialize(
156           mode, profile, x_display, report_error_to_uma_cb))
157     vaapi_wrapper.reset();
158
159   return vaapi_wrapper.Pass();
160 }
161
162 std::vector<media::VideoCodecProfile> VaapiWrapper::GetSupportedEncodeProfiles(
163     Display* x_display,
164     const base::Closure& report_error_to_uma_cb) {
165   std::vector<media::VideoCodecProfile> supported_profiles;
166
167   scoped_ptr<VaapiWrapper> wrapper(new VaapiWrapper());
168   if (!wrapper->VaInitialize(x_display, report_error_to_uma_cb)) {
169     return supported_profiles;
170   }
171
172   std::vector<VAProfile> va_profiles;
173   if (!wrapper->GetSupportedVaProfiles(&va_profiles))
174     return supported_profiles;
175
176   std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(kEncode);
177   for (size_t i = 0; i < arraysize(kProfileMap); i++) {
178     VAProfile va_profile =
179         ProfileToVAProfile(kProfileMap[i].profile, va_profiles);
180     if (va_profile != VAProfileNone &&
181         wrapper->IsEntrypointSupported(va_profile, VAEntrypointEncSlice) &&
182         wrapper->AreAttribsSupported(
183             va_profile, VAEntrypointEncSlice, required_attribs)) {
184       supported_profiles.push_back(kProfileMap[i].profile);
185     }
186   }
187   return supported_profiles;
188 }
189
190 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
191   base::AutoLock auto_lock(va_lock_);
192   VADisplayAttribute item = {VADisplayAttribRenderMode,
193                              1,  // At least support '_LOCAL_OVERLAY'.
194                              -1,  // The maximum possible support 'ALL'.
195                              VA_RENDER_MODE_LOCAL_GPU,
196                              VA_DISPLAY_ATTRIB_SETTABLE};
197
198   VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1);
199   if (va_res != VA_STATUS_SUCCESS)
200     DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
201 }
202
203 bool VaapiWrapper::VaInitialize(Display* x_display,
204                                 const base::Closure& report_error_to_uma_cb) {
205   static bool vaapi_functions_initialized = PostSandboxInitialization();
206   if (!vaapi_functions_initialized) {
207     DVLOG(1) << "Failed to initialize VAAPI libs";
208     return false;
209   }
210
211   report_error_to_uma_cb_ = report_error_to_uma_cb;
212
213   base::AutoLock auto_lock(va_lock_);
214
215   va_display_ = vaGetDisplay(x_display);
216   if (!vaDisplayIsValid(va_display_)) {
217     DVLOG(1) << "Could not get a valid VA display";
218     return false;
219   }
220
221   VAStatus va_res = vaInitialize(va_display_, &major_version_, &minor_version_);
222   VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false);
223   DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
224
225   if (VAAPIVersionLessThan(0, 34)) {
226     DVLOG(1) << "VAAPI version < 0.34 is not supported.";
227     return false;
228   }
229   return true;
230 }
231
232 bool VaapiWrapper::GetSupportedVaProfiles(std::vector<VAProfile>* profiles) {
233   base::AutoLock auto_lock(va_lock_);
234   // Query the driver for supported profiles.
235   int max_profiles = vaMaxNumProfiles(va_display_);
236   std::vector<VAProfile> supported_profiles(
237       base::checked_cast<size_t>(max_profiles));
238
239   int num_supported_profiles;
240   VAStatus va_res = vaQueryConfigProfiles(
241       va_display_, &supported_profiles[0], &num_supported_profiles);
242   VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false);
243   if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) {
244     DVLOG(1) << "vaQueryConfigProfiles returned: " << num_supported_profiles;
245     return false;
246   }
247
248   supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles));
249   *profiles = supported_profiles;
250   return true;
251 }
252
253 bool VaapiWrapper::IsEntrypointSupported(VAProfile va_profile,
254                                          VAEntrypoint entrypoint) {
255   base::AutoLock auto_lock(va_lock_);
256   // Query the driver for supported entrypoints.
257   int max_entrypoints = vaMaxNumEntrypoints(va_display_);
258   std::vector<VAEntrypoint> supported_entrypoints(
259       base::checked_cast<size_t>(max_entrypoints));
260
261   int num_supported_entrypoints;
262   VAStatus va_res = vaQueryConfigEntrypoints(va_display_,
263                                              va_profile,
264                                              &supported_entrypoints[0],
265                                              &num_supported_entrypoints);
266   VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false);
267   if (num_supported_entrypoints < 0 ||
268       num_supported_entrypoints > max_entrypoints) {
269     DVLOG(1) << "vaQueryConfigEntrypoints returned: "
270              << num_supported_entrypoints;
271     return false;
272   }
273
274   if (std::find(supported_entrypoints.begin(),
275                 supported_entrypoints.end(),
276                 entrypoint) == supported_entrypoints.end()) {
277     DVLOG(1) << "Unsupported entrypoint";
278     return false;
279   }
280   return true;
281 }
282
283 bool VaapiWrapper::AreAttribsSupported(
284     VAProfile va_profile,
285     VAEntrypoint entrypoint,
286     const std::vector<VAConfigAttrib>& required_attribs) {
287   base::AutoLock auto_lock(va_lock_);
288   // Query the driver for required attributes.
289   std::vector<VAConfigAttrib> attribs = required_attribs;
290   for (size_t i = 0; i < required_attribs.size(); ++i)
291     attribs[i].value = 0;
292
293   VAStatus va_res = vaGetConfigAttributes(
294       va_display_, va_profile, entrypoint, &attribs[0], attribs.size());
295   VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
296
297   for (size_t i = 0; i < required_attribs.size(); ++i) {
298     if (attribs[i].type != required_attribs[i].type ||
299         (attribs[i].value & required_attribs[i].value) !=
300             required_attribs[i].value) {
301       DVLOG(1) << "Unsupported value " << required_attribs[i].value
302                << " for attribute type " << required_attribs[i].type;
303       return false;
304     }
305   }
306   return true;
307 }
308
309 bool VaapiWrapper::Initialize(CodecMode mode,
310                               media::VideoCodecProfile profile,
311                               Display* x_display,
312                               const base::Closure& report_error_to_uma_cb) {
313   if (!VaInitialize(x_display, report_error_to_uma_cb))
314     return false;
315   std::vector<VAProfile> supported_va_profiles;
316   if (!GetSupportedVaProfiles(&supported_va_profiles))
317     return false;
318   VAProfile va_profile = ProfileToVAProfile(profile, supported_va_profiles);
319   if (va_profile == VAProfileNone) {
320     DVLOG(1) << "Unsupported profile";
321     return false;
322   }
323   VAEntrypoint entrypoint =
324       (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD);
325   if (!IsEntrypointSupported(va_profile, entrypoint))
326     return false;
327   std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode);
328   if (!AreAttribsSupported(va_profile, entrypoint, required_attribs))
329     return false;
330
331   TryToSetVADisplayAttributeToLocalGPU();
332
333   base::AutoLock auto_lock(va_lock_);
334   VAStatus va_res = vaCreateConfig(va_display_,
335                                    va_profile,
336                                    entrypoint,
337                                    &required_attribs[0],
338                                    required_attribs.size(),
339                                    &va_config_id_);
340   VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
341
342   return true;
343 }
344
345 void VaapiWrapper::Deinitialize() {
346   base::AutoLock auto_lock(va_lock_);
347
348   if (va_config_id_ != VA_INVALID_ID) {
349     VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_);
350     VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
351   }
352
353   if (va_display_) {
354     VAStatus va_res = vaTerminate(va_display_);
355     VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
356   }
357
358   va_config_id_ = VA_INVALID_ID;
359   va_display_ = NULL;
360 }
361
362 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
363   return (major_version_ < major) ||
364       (major_version_ == major && minor_version_ < minor);
365 }
366
367 bool VaapiWrapper::CreateSurfaces(gfx::Size size,
368                                   size_t num_surfaces,
369                                   std::vector<VASurfaceID>* va_surfaces) {
370   base::AutoLock auto_lock(va_lock_);
371   DVLOG(2) << "Creating " << num_surfaces << " surfaces";
372
373   DCHECK(va_surfaces->empty());
374   DCHECK(va_surface_ids_.empty());
375   va_surface_ids_.resize(num_surfaces);
376
377   // Allocate surfaces in driver.
378   VAStatus va_res = vaCreateSurfaces(va_display_,
379                                      VA_RT_FORMAT_YUV420,
380                                      size.width(), size.height(),
381                                      &va_surface_ids_[0],
382                                      va_surface_ids_.size(),
383                                      NULL, 0);
384
385   VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
386   if (va_res != VA_STATUS_SUCCESS) {
387     va_surface_ids_.clear();
388     return false;
389   }
390
391   // And create a context associated with them.
392   va_res = vaCreateContext(va_display_, va_config_id_,
393                            size.width(), size.height(), VA_PROGRESSIVE,
394                            &va_surface_ids_[0], va_surface_ids_.size(),
395                            &va_context_id_);
396
397   VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
398   if (va_res != VA_STATUS_SUCCESS) {
399     DestroySurfaces();
400     return false;
401   }
402
403   *va_surfaces = va_surface_ids_;
404   return true;
405 }
406
407 void VaapiWrapper::DestroySurfaces() {
408   base::AutoLock auto_lock(va_lock_);
409   DVLOG(2) << "Destroying " << va_surface_ids_.size()  << " surfaces";
410
411   if (va_context_id_ != VA_INVALID_ID) {
412     VAStatus va_res = vaDestroyContext(va_display_, va_context_id_);
413     VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
414   }
415
416   if (!va_surface_ids_.empty()) {
417     VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0],
418                                         va_surface_ids_.size());
419     VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
420   }
421
422   va_surface_ids_.clear();
423   va_context_id_ = VA_INVALID_ID;
424 }
425
426 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
427                                 size_t size,
428                                 void* buffer) {
429   base::AutoLock auto_lock(va_lock_);
430
431   VABufferID buffer_id;
432   VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_,
433                                    va_buffer_type, size,
434                                    1, buffer, &buffer_id);
435   VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
436
437   switch (va_buffer_type) {
438     case VASliceParameterBufferType:
439     case VASliceDataBufferType:
440     case VAEncSliceParameterBufferType:
441       pending_slice_bufs_.push_back(buffer_id);
442       break;
443
444     default:
445       pending_va_bufs_.push_back(buffer_id);
446       break;
447   }
448
449   return true;
450 }
451
452 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
453     VAEncMiscParameterType misc_param_type,
454     size_t size,
455     void* buffer) {
456   base::AutoLock auto_lock(va_lock_);
457
458   VABufferID buffer_id;
459   VAStatus va_res = vaCreateBuffer(va_display_,
460                                    va_context_id_,
461                                    VAEncMiscParameterBufferType,
462                                    sizeof(VAEncMiscParameterBuffer) + size,
463                                    1,
464                                    NULL,
465                                    &buffer_id);
466   VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
467
468   void* data_ptr = NULL;
469   va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr);
470   VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
471   if (va_res != VA_STATUS_SUCCESS) {
472     vaDestroyBuffer(va_display_, buffer_id);
473     return false;
474   }
475
476   DCHECK(data_ptr);
477
478   VAEncMiscParameterBuffer* misc_param =
479       reinterpret_cast<VAEncMiscParameterBuffer*>(data_ptr);
480   misc_param->type = misc_param_type;
481   memcpy(misc_param->data, buffer, size);
482   va_res = vaUnmapBuffer(va_display_, buffer_id);
483   VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
484
485   pending_va_bufs_.push_back(buffer_id);
486   return true;
487 }
488
489 void VaapiWrapper::DestroyPendingBuffers() {
490   base::AutoLock auto_lock(va_lock_);
491
492   for (size_t i = 0; i < pending_va_bufs_.size(); ++i) {
493     VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_bufs_[i]);
494     VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
495   }
496
497   for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) {
498     VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_bufs_[i]);
499     VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
500   }
501
502   pending_va_bufs_.clear();
503   pending_slice_bufs_.clear();
504 }
505
506 bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) {
507   base::AutoLock auto_lock(va_lock_);
508   VAStatus va_res = vaCreateBuffer(va_display_,
509                                    va_context_id_,
510                                    VAEncCodedBufferType,
511                                    size,
512                                    1,
513                                    NULL,
514                                    buffer_id);
515   VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false);
516
517   DCHECK(coded_buffers_.insert(*buffer_id).second);
518   return true;
519 }
520
521 void VaapiWrapper::DestroyCodedBuffers() {
522   base::AutoLock auto_lock(va_lock_);
523
524   for (std::set<VABufferID>::const_iterator iter = coded_buffers_.begin();
525        iter != coded_buffers_.end();
526        ++iter) {
527     VAStatus va_res = vaDestroyBuffer(va_display_, *iter);
528     VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
529   }
530
531   coded_buffers_.clear();
532 }
533
534 bool VaapiWrapper::Execute(VASurfaceID va_surface_id) {
535   base::AutoLock auto_lock(va_lock_);
536
537   DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
538   DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
539   DVLOG(4) << "Target VA surface " << va_surface_id;
540
541   // Get ready to execute for given surface.
542   VAStatus va_res = vaBeginPicture(va_display_, va_context_id_,
543                                    va_surface_id);
544   VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
545
546   if (pending_va_bufs_.size() > 0) {
547     // Commit parameter and slice buffers.
548     va_res = vaRenderPicture(va_display_,
549                              va_context_id_,
550                              &pending_va_bufs_[0],
551                              pending_va_bufs_.size());
552     VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
553   }
554
555   if (pending_slice_bufs_.size() > 0) {
556     va_res = vaRenderPicture(va_display_,
557                              va_context_id_,
558                              &pending_slice_bufs_[0],
559                              pending_slice_bufs_.size());
560     VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
561   }
562
563   // Instruct HW codec to start processing committed buffers.
564   // Does not block and the job is not finished after this returns.
565   va_res = vaEndPicture(va_display_, va_context_id_);
566   VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
567
568   return true;
569 }
570
571 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
572   bool result = Execute(va_surface_id);
573   DestroyPendingBuffers();
574   return result;
575 }
576
577 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
578                                         Pixmap x_pixmap,
579                                         gfx::Size dest_size) {
580   base::AutoLock auto_lock(va_lock_);
581
582   VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
583   VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
584
585   // Put the data into an X Pixmap.
586   va_res = vaPutSurface(va_display_,
587                         va_surface_id,
588                         x_pixmap,
589                         0, 0, dest_size.width(), dest_size.height(),
590                         0, 0, dest_size.width(), dest_size.height(),
591                         NULL, 0, 0);
592   VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false);
593   return true;
594 }
595
596 bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
597                                         VAImage* image,
598                                         void** mem) {
599   base::AutoLock auto_lock(va_lock_);
600
601   VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
602   VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
603
604   // Derive a VAImage from the VASurface
605   va_res = vaDeriveImage(va_display_, va_surface_id, image);
606   VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed");
607   if (va_res != VA_STATUS_SUCCESS)
608     return false;
609
610   // Map the VAImage into memory
611   va_res = vaMapBuffer(va_display_, image->buf, mem);
612   VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
613   if (va_res == VA_STATUS_SUCCESS)
614     return true;
615
616   va_res = vaDestroyImage(va_display_, image->image_id);
617   VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
618
619   return false;
620 }
621
622 void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) {
623   base::AutoLock auto_lock(va_lock_);
624
625   VAStatus va_res = vaUnmapBuffer(va_display_, image->buf);
626   VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
627
628   va_res = vaDestroyImage(va_display_, image->image_id);
629   VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
630 }
631
632 static void DestroyVAImage(VADisplay va_display, VAImage image) {
633   if (image.image_id != VA_INVALID_ID)
634     vaDestroyImage(va_display, image.image_id);
635 }
636
637 bool VaapiWrapper::UploadVideoFrameToSurface(
638     const scoped_refptr<media::VideoFrame>& frame,
639     VASurfaceID va_surface_id) {
640   base::AutoLock auto_lock(va_lock_);
641
642   VAImage image;
643   VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image);
644   VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false);
645   base::ScopedClosureRunner vaimage_deleter(
646       base::Bind(&DestroyVAImage, va_display_, image));
647
648   if (image.format.fourcc != VA_FOURCC_NV12) {
649     DVLOG(1) << "Unsupported image format: " << image.format.fourcc;
650     return false;
651   }
652
653   if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) {
654     DVLOG(1) << "Buffer too small to fit the frame.";
655     return false;
656   }
657
658   void* image_ptr = NULL;
659   va_res = vaMapBuffer(va_display_, image.buf, &image_ptr);
660   VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
661   DCHECK(image_ptr);
662
663   int ret = 0;
664   {
665     base::AutoUnlock auto_unlock(va_lock_);
666     ret = libyuv::I420ToNV12(frame->data(media::VideoFrame::kYPlane),
667                              frame->stride(media::VideoFrame::kYPlane),
668                              frame->data(media::VideoFrame::kUPlane),
669                              frame->stride(media::VideoFrame::kUPlane),
670                              frame->data(media::VideoFrame::kVPlane),
671                              frame->stride(media::VideoFrame::kVPlane),
672                              static_cast<uint8*>(image_ptr) + image.offsets[0],
673                              image.pitches[0],
674                              static_cast<uint8*>(image_ptr) + image.offsets[1],
675                              image.pitches[1],
676                              image.width,
677                              image.height);
678   }
679
680   va_res = vaUnmapBuffer(va_display_, image.buf);
681   VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
682
683   return ret == 0;
684 }
685
686 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
687                                                  VASurfaceID sync_surface_id,
688                                                  uint8* target_ptr,
689                                                  size_t target_size,
690                                                  size_t* coded_data_size) {
691   base::AutoLock auto_lock(va_lock_);
692
693   VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id);
694   VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
695
696   VACodedBufferSegment* buffer_segment = NULL;
697   va_res = vaMapBuffer(
698       va_display_, buffer_id, reinterpret_cast<void**>(&buffer_segment));
699   VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
700   DCHECK(target_ptr);
701
702   {
703     base::AutoUnlock auto_unlock(va_lock_);
704     *coded_data_size = 0;
705
706     while (buffer_segment) {
707       DCHECK(buffer_segment->buf);
708
709       if (buffer_segment->size > target_size) {
710         DVLOG(1) << "Insufficient output buffer size";
711         break;
712       }
713
714       memcpy(target_ptr, buffer_segment->buf, buffer_segment->size);
715
716       target_ptr += buffer_segment->size;
717       *coded_data_size += buffer_segment->size;
718       target_size -= buffer_segment->size;
719
720       buffer_segment =
721           reinterpret_cast<VACodedBufferSegment*>(buffer_segment->next);
722     }
723   }
724
725   va_res = vaUnmapBuffer(va_display_, buffer_id);
726   VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
727
728   va_res = vaDestroyBuffer(va_display_, buffer_id);
729   VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
730
731   DCHECK(coded_buffers_.erase(buffer_id));
732
733   return buffer_segment == NULL;
734 }
735
736 // static
737 bool VaapiWrapper::PostSandboxInitialization() {
738   StubPathMap paths;
739   paths[kModuleVa].push_back(kVaLib);
740
741   return InitializeStubs(paths);
742 }
743
744 }  // namespace content