|  | // Copyright 2019 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "media/gpu/vaapi/vaapi_webp_decoder.h" | 
|  |  | 
|  | #include <va/va.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/numerics/safe_conversions.h" | 
|  | #include "media/gpu/macros.h" | 
|  | #include "media/gpu/vaapi/vaapi_utils.h" | 
|  | #include "media/gpu/vaapi/vaapi_wrapper.h" | 
|  | #include "media/gpu/vp8_reference_frame_vector.h" | 
|  | #include "media/parsers/vp8_parser.h" | 
|  | #include "media/parsers/webp_parser.h" | 
|  | #include "ui/gfx/geometry/size.h" | 
|  |  | 
|  | namespace media { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr VAProfile kWebPVAProfile = VAProfileVP8Version0_3; | 
|  | constexpr unsigned int kWebPVARtFormat = VA_RT_FORMAT_YUV420; | 
|  |  | 
|  | static bool IsVaapiSupportedWebP(const Vp8FrameHeader& webp_header) { | 
|  | if (!VaapiWrapper::IsDecodingSupportedForInternalFormat(kWebPVAProfile, | 
|  | kWebPVARtFormat)) { | 
|  | DLOG(ERROR) << "The WebP's subsampling format is unsupported"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Validate the size. | 
|  | // TODO(crbug.com/984971): Make sure visible size and coded size are treated | 
|  | // similarly here: we don't currently know if we really have to provide the | 
|  | // coded size to the VAAPI. So far, it seems to work by just passing the | 
|  | // visible size, but we have to learn more, probably by looking into the | 
|  | // driver. If we need to pass the coded size, then when checking against the | 
|  | // min/max resolutions, we should use the coded size and not the visible size. | 
|  | const gfx::Size webp_size(base::strict_cast<int>(webp_header.width), | 
|  | base::strict_cast<int>(webp_header.height)); | 
|  | if (webp_size.IsEmpty()) { | 
|  | DLOG(ERROR) << "Width or height cannot be zero: " << webp_size.ToString(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | gfx::Size min_webp_resolution; | 
|  | if (!VaapiWrapper::GetDecodeMinResolution(kWebPVAProfile, | 
|  | &min_webp_resolution)) { | 
|  | DLOG(ERROR) << "Could not get the minimum resolution"; | 
|  | return false; | 
|  | } | 
|  | if (webp_size.width() < min_webp_resolution.width() || | 
|  | webp_size.height() < min_webp_resolution.height()) { | 
|  | DLOG(ERROR) << "VAAPI doesn't support size " << webp_size.ToString() | 
|  | << ": under minimum resolution " | 
|  | << min_webp_resolution.ToString(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | gfx::Size max_webp_resolution; | 
|  | if (!VaapiWrapper::GetDecodeMaxResolution(kWebPVAProfile, | 
|  | &max_webp_resolution)) { | 
|  | DLOG(ERROR) << "Could not get the maximum resolution"; | 
|  | return false; | 
|  | } | 
|  | if (webp_size.width() > max_webp_resolution.width() || | 
|  | webp_size.height() > max_webp_resolution.height()) { | 
|  | DLOG(ERROR) << "VAAPI doesn't support size " << webp_size.ToString() | 
|  | << ": above maximum resolution " | 
|  | << max_webp_resolution.ToString(); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | VaapiWebPDecoder::VaapiWebPDecoder() : VaapiImageDecoder(kWebPVAProfile) {} | 
|  |  | 
|  | VaapiWebPDecoder::~VaapiWebPDecoder() = default; | 
|  |  | 
|  | gpu::ImageDecodeAcceleratorType VaapiWebPDecoder::GetType() const { | 
|  | return gpu::ImageDecodeAcceleratorType::kWebP; | 
|  | } | 
|  |  | 
|  | SkYUVColorSpace VaapiWebPDecoder::GetYUVColorSpace() const { | 
|  | return SkYUVColorSpace::kRec601_SkYUVColorSpace; | 
|  | } | 
|  |  | 
|  | VaapiImageDecodeStatus VaapiWebPDecoder::AllocateVASurfaceAndSubmitVABuffers( | 
|  | base::span<const uint8_t> encoded_image) { | 
|  | DCHECK(vaapi_wrapper_); | 
|  | std::unique_ptr<Vp8FrameHeader> parse_result = ParseWebPImage(encoded_image); | 
|  | if (!parse_result) | 
|  | return VaapiImageDecodeStatus::kParseFailed; | 
|  |  | 
|  | // Make sure this WebP can be decoded. | 
|  | if (!IsVaapiSupportedWebP(*parse_result)) | 
|  | return VaapiImageDecodeStatus::kUnsupportedImage; | 
|  |  | 
|  | // Prepare the VaSurface for decoding. | 
|  | const gfx::Size new_visible_size( | 
|  | base::strict_cast<int>(parse_result->width), | 
|  | base::strict_cast<int>(parse_result->height)); | 
|  | DCHECK(!scoped_va_context_and_surface_ || | 
|  | scoped_va_context_and_surface_->IsValid()); | 
|  | DCHECK(!scoped_va_context_and_surface_ || | 
|  | (scoped_va_context_and_surface_->format() == kWebPVARtFormat)); | 
|  | if (!scoped_va_context_and_surface_ || | 
|  | new_visible_size != scoped_va_context_and_surface_->size()) { | 
|  | scoped_va_context_and_surface_.reset(); | 
|  | auto scoped_va_surfaces = vaapi_wrapper_->CreateContextAndScopedVASurfaces( | 
|  | kWebPVARtFormat, new_visible_size, | 
|  | {VaapiWrapper::SurfaceUsageHint::kGeneric}, 1u, | 
|  | /*visible_size=*/absl::nullopt); | 
|  | if (scoped_va_surfaces.empty()) { | 
|  | VLOGF(1) << "CreateContextAndScopedVASurface() failed"; | 
|  | return VaapiImageDecodeStatus::kSurfaceCreationFailed; | 
|  | } | 
|  |  | 
|  | scoped_va_context_and_surface_ = | 
|  | ScopedVAContextAndSurface(scoped_va_surfaces[0].release()); | 
|  | DCHECK(scoped_va_context_and_surface_->IsValid()); | 
|  | } | 
|  | DCHECK_NE(scoped_va_context_and_surface_->id(), VA_INVALID_SURFACE); | 
|  |  | 
|  | VAIQMatrixBufferVP8 iq_matrix_buf{}; | 
|  | VAProbabilityDataBufferVP8 prob_buf{}; | 
|  | VAPictureParameterBufferVP8 pic_param{}; | 
|  | VASliceParameterBufferVP8 slice_param{}; | 
|  | FillVP8DataStructures(*parse_result, Vp8ReferenceFrameVector(), | 
|  | &iq_matrix_buf, &prob_buf, &pic_param, &slice_param); | 
|  |  | 
|  | const bool success = vaapi_wrapper_->SubmitBuffers( | 
|  | {{VAIQMatrixBufferType, sizeof(iq_matrix_buf), &iq_matrix_buf}, | 
|  | {VAProbabilityBufferType, sizeof(prob_buf), &prob_buf}, | 
|  | {VAPictureParameterBufferType, sizeof(pic_param), &pic_param}, | 
|  | {VASliceParameterBufferType, sizeof(slice_param), &slice_param}, | 
|  | {VASliceDataBufferType, parse_result->frame_size, parse_result->data}}); | 
|  |  | 
|  | return success ? VaapiImageDecodeStatus::kSuccess | 
|  | : VaapiImageDecodeStatus::kSubmitVABuffersFailed; | 
|  | } | 
|  |  | 
|  | }  // namespace media |