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
17using content_common_gpu_media::kModuleVa;
18using content_common_gpu_media::InitializeStubs;
19using content_common_gpu_media::StubPathMap;
20
21// libva-x11 depends on libva, so dlopen libva-x11 is enough
22static 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
46namespace content {
47
48// Config attributes common for both encode and decode.
49static const VAConfigAttrib kCommonVAConfigAttribs[] = {
50    {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420},
51};
52
53// Attributes required for encode.
54static const VAConfigAttrib kEncodeVAConfigAttribs[] = {
55    {VAConfigAttribRateControl, VA_RC_CBR},
56    {VAConfigAttribEncPackedHeaders,
57     VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE},
58};
59
60struct ProfileMap {
61  media::VideoCodecProfile profile;
62  VAProfile va_profile;
63};
64
65// A map between VideoCodecProfile and VAProfile.
66static 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
74static 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.
91static 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
125VASurface::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
131VASurface::~VASurface() {
132  release_cb_.Run(va_surface_id_);
133}
134
135VaapiWrapper::VaapiWrapper()
136    : va_display_(NULL),
137      va_config_id_(VA_INVALID_ID),
138      va_context_id_(VA_INVALID_ID) {
139}
140
141VaapiWrapper::~VaapiWrapper() {
142  DestroyPendingBuffers();
143  DestroyCodedBuffers();
144  DestroySurfaces();
145  Deinitialize();
146}
147
148scoped_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
162std::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
190void 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
203bool 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
232bool 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
253bool 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
283bool 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
309bool 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
345void 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
362bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
363  return (major_version_ < major) ||
364      (major_version_ == major && minor_version_ < minor);
365}
366
367bool 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
407void 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
426bool 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
452bool 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
489void 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
506bool 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
521void 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
534bool 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
571bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
572  bool result = Execute(va_surface_id);
573  DestroyPendingBuffers();
574  return result;
575}
576
577bool 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
596bool 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
622void 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
632static void DestroyVAImage(VADisplay va_display, VAImage image) {
633  if (image.image_id != VA_INVALID_ID)
634    vaDestroyImage(va_display, image.image_id);
635}
636
637bool 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
686bool 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
737bool VaapiWrapper::PostSandboxInitialization() {
738  StubPathMap paths;
739  paths[kModuleVa].push_back(kVaLib);
740
741  return InitializeStubs(paths);
742}
743
744}  // namespace content
745