blob: 644f04f58aa24140d308cc46330bfb51dd6ab9d2 [file] [log] [blame]
// 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_image_decode_accelerator_worker.h"
#include "base/task/thread_pool.h"
#include "string.h"
#include <utility>
#include "base/bind.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/trace_event/trace_event.h"
#include "gpu/config/gpu_finch_features.h"
#include "media/gpu/macros.h"
#include "media/gpu/vaapi/va_surface.h"
#include "media/gpu/vaapi/vaapi_image_decoder.h"
#include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
#include "media/gpu/vaapi/vaapi_webp_decoder.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "media/parsers/webp_parser.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#include "ui/gfx/native_pixmap_handle.h"
namespace media {
namespace {
bool IsJpegImage(base::span<const uint8_t> encoded_data) {
if (encoded_data.size() < 3u)
return false;
return memcmp("\xFF\xD8\xFF", encoded_data.data(), 3u) == 0;
}
// Uses |decoder| to decode the image corresponding to |encoded_data|.
// |decode_cb| is called when finished or when an error is encountered. We don't
// support decoding to scale, so |output_size| is only used for tracing.
void DecodeTask(
VaapiImageDecoder* decoder,
std::vector<uint8_t> encoded_data,
const gfx::Size& output_size,
gpu::ImageDecodeAcceleratorWorker::CompletedDecodeCB decode_cb) {
TRACE_EVENT2("jpeg", "VaapiImageDecodeAcceleratorWorker::DecodeTask",
"encoded_bytes", encoded_data.size(), "output_size",
output_size.ToString());
gpu::ImageDecodeAcceleratorWorker::CompletedDecodeCB scoped_decode_callback =
mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(decode_cb),
nullptr);
// Decode into a VAAPI surface.
if (!decoder) {
DVLOGF(1) << "No decoder is available for supplied image";
return;
}
VaapiImageDecodeStatus status = decoder->Decode(encoded_data);
if (status != VaapiImageDecodeStatus::kSuccess) {
DVLOGF(1) << "Failed to decode - status = "
<< static_cast<uint32_t>(status);
return;
}
// Export the decode result as a NativePixmap.
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
decoder->ExportAsNativePixmapDmaBuf(&status);
if (status != VaapiImageDecodeStatus::kSuccess) {
DVLOGF(1) << "Failed to export surface - status = "
<< static_cast<uint32_t>(status);
return;
}
DCHECK(exported_pixmap);
DCHECK(exported_pixmap->pixmap);
if (exported_pixmap->pixmap->GetBufferSize() != output_size) {
DVLOGF(1) << "Scaling is not supported";
return;
}
// Output the decoded data.
gfx::NativePixmapHandle pixmap_handle =
exported_pixmap->pixmap->ExportHandle();
// If a dup() failed while exporting the handle, we would get no planes.
if (pixmap_handle.planes.empty()) {
DVLOGF(1) << "Could not export the NativePixmapHandle";
return;
}
auto result =
std::make_unique<gpu::ImageDecodeAcceleratorWorker::DecodeResult>();
result->handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
result->handle.native_pixmap_handle = std::move(pixmap_handle);
result->visible_size = exported_pixmap->pixmap->GetBufferSize();
result->buffer_format = exported_pixmap->pixmap->GetBufferFormat();
result->buffer_byte_size = exported_pixmap->byte_size;
result->yuv_color_space = decoder->GetYUVColorSpace();
std::move(scoped_decode_callback).Run(std::move(result));
}
} // namespace
// static
std::unique_ptr<VaapiImageDecodeAcceleratorWorker>
VaapiImageDecodeAcceleratorWorker::Create() {
// TODO(crbug.com/988123): revisit the
// Media.VaapiImageDecodeAcceleratorWorker.VAAPIError UMA to be able to record
// WebP and JPEG failures separately.
const auto uma_cb =
base::BindRepeating(&ReportVaapiErrorToUMA,
"Media.VaapiImageDecodeAcceleratorWorker.VAAPIError");
VaapiImageDecoderVector decoders;
auto jpeg_decoder = std::make_unique<VaapiJpegDecoder>();
// TODO(crbug.com/974438): we can't advertise accelerated image decoding in
// AMD until we support VAAPI surfaces with multiple buffer objects.
if (VaapiWrapper::GetImplementationType() != VAImplementation::kMesaGallium &&
jpeg_decoder->Initialize(uma_cb)) {
decoders.push_back(std::move(jpeg_decoder));
}
auto webp_decoder = std::make_unique<VaapiWebPDecoder>();
if (webp_decoder->Initialize(uma_cb))
decoders.push_back(std::move(webp_decoder));
// If there are no decoders due to disabled flags or initialization failure,
// return nullptr.
if (decoders.empty())
return nullptr;
return base::WrapUnique(
new VaapiImageDecodeAcceleratorWorker(std::move(decoders)));
}
VaapiImageDecodeAcceleratorWorker::VaapiImageDecodeAcceleratorWorker(
VaapiImageDecoderVector decoders) {
DETACH_FROM_SEQUENCE(io_sequence_checker_);
decoder_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner({});
DCHECK(decoder_task_runner_);
DCHECK(!decoders.empty());
for (auto& decoder : decoders) {
supported_profiles_.push_back(decoder->GetSupportedProfile());
const gpu::ImageDecodeAcceleratorType type = decoder->GetType();
decoders_[type] = std::move(decoder);
}
}
VaapiImageDecodeAcceleratorWorker::~VaapiImageDecodeAcceleratorWorker() {
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
DCHECK(decoder_task_runner_);
for (auto& decoder : decoders_)
decoder_task_runner_->DeleteSoon(FROM_HERE, std::move(decoder.second));
}
gpu::ImageDecodeAcceleratorSupportedProfiles
VaapiImageDecodeAcceleratorWorker::GetSupportedProfiles() {
return supported_profiles_;
}
VaapiImageDecoder* VaapiImageDecodeAcceleratorWorker::GetDecoderForImage(
const std::vector<uint8_t>& encoded_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_sequence_checker_);
auto result = decoders_.end();
if (base::FeatureList::IsEnabled(
features::kVaapiJpegImageDecodeAcceleration) &&
IsJpegImage(encoded_data)) {
result = decoders_.find(gpu::ImageDecodeAcceleratorType::kJpeg);
} else if (base::FeatureList::IsEnabled(
features::kVaapiWebPImageDecodeAcceleration) &&
IsLossyWebPImage(encoded_data)) {
result = decoders_.find(gpu::ImageDecodeAcceleratorType::kWebP);
}
return result == decoders_.end() ? nullptr : result->second.get();
}
void VaapiImageDecodeAcceleratorWorker::Decode(
std::vector<uint8_t> encoded_data,
const gfx::Size& output_size,
CompletedDecodeCB decode_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(io_sequence_checker_);
DCHECK(decoder_task_runner_);
// We defer checking for a null |decoder| until DecodeTask() because the
// gpu::ImageDecodeAcceleratorWorker interface mandates that the callback be
// called asynchronously.
VaapiImageDecoder* decoder = GetDecoderForImage(encoded_data);
decoder_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DecodeTask, decoder, std::move(encoded_data),
output_size, std::move(decode_cb)));
}
} // namespace media