blob: a20316dfb9c555f3bb383f6e95ee26fe01214e66 [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_processor.h"
#include <stdint.h>
#include <va/va.h>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/task/post_task.h"
#include "media/base/video_frame.h"
#include "media/gpu/chromeos/fourcc.h"
#include "media/gpu/linux/platform_video_frame_utils.h"
#include "media/gpu/macros.h"
#include "media/gpu/vaapi/va_surface.h"
#include "media/gpu/vaapi/vaapi_utils.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "ui/gfx/native_pixmap.h"
namespace media {
namespace {
// UMA errors that the VaapiImageProcessor class reports.
enum class VaIPFailure {
kVaapiVppError = 0,
kMaxValue = kVaapiVppError,
};
void ReportToUMA(scoped_refptr<base::SequencedTaskRunner> task_runner,
base::RepeatingClosure error_cb,
VaIPFailure failure) {
base::UmaHistogramEnumeration("Media.VAIP.VppFailure", failure);
task_runner->PostTask(FROM_HERE, error_cb);
}
bool IsSupported(uint32_t input_va_fourcc,
uint32_t output_va_fourcc,
const gfx::Size& input_size,
const gfx::Size& output_size) {
if (!VaapiWrapper::IsVppFormatSupported(input_va_fourcc)) {
VLOGF(2) << "Unsupported input format: VA_FOURCC_"
<< FourccToString(input_va_fourcc);
return false;
}
if (!VaapiWrapper::IsVppFormatSupported(output_va_fourcc)) {
VLOGF(2) << "Unsupported output format: VA_FOURCC_"
<< FourccToString(output_va_fourcc);
return false;
}
if (!VaapiWrapper::IsVppResolutionAllowed(input_size)) {
VLOGF(2) << "Unsupported input size: " << input_size.ToString();
return false;
}
if (!VaapiWrapper::IsVppResolutionAllowed(output_size)) {
VLOGF(2) << "Unsupported output size: " << output_size.ToString();
return false;
}
return true;
}
} // namespace
// static
std::unique_ptr<VaapiImageProcessor> VaapiImageProcessor::Create(
const ImageProcessor::PortConfig& input_config,
const ImageProcessor::PortConfig& output_config,
const std::vector<ImageProcessor::OutputMode>& preferred_output_modes,
scoped_refptr<base::SequencedTaskRunner> client_task_runner,
const base::RepeatingClosure& error_cb) {
// VaapiImageProcessor supports ChromeOS only.
#if !defined(OS_CHROMEOS)
return nullptr;
#endif
if (!IsSupported(input_config.fourcc.ToVAFourCC(),
output_config.fourcc.ToVAFourCC(), input_config.size,
output_config.size)) {
return nullptr;
}
if (!base::Contains(input_config.preferred_storage_types,
VideoFrame::STORAGE_DMABUFS) &&
!base::Contains(input_config.preferred_storage_types,
VideoFrame::STORAGE_GPU_MEMORY_BUFFER)) {
VLOGF(2) << "VaapiImageProcessor supports Dmabuf-backed or GpuMemoryBuffer"
<< " based VideoFrame only for input";
return nullptr;
}
if (!base::Contains(output_config.preferred_storage_types,
VideoFrame::STORAGE_DMABUFS) &&
!base::Contains(output_config.preferred_storage_types,
VideoFrame::STORAGE_GPU_MEMORY_BUFFER)) {
VLOGF(2) << "VaapiImageProcessor supports Dmabuf-backed or GpuMemoryBuffer"
<< " based VideoFrame only for output";
return nullptr;
}
if (!base::Contains(preferred_output_modes, OutputMode::IMPORT)) {
VLOGF(2) << "VaapiImageProcessor only supports IMPORT mode.";
return nullptr;
}
auto vaapi_wrapper = VaapiWrapper::Create(
VaapiWrapper::kVideoProcess, VAProfileNone,
base::BindRepeating(&ReportToUMA, client_task_runner, error_cb,
VaIPFailure::kVaapiVppError));
if (!vaapi_wrapper) {
VLOGF(1) << "Failed to create VaapiWrapper";
return nullptr;
}
// Size is irrelevant for a VPP context.
if (!vaapi_wrapper->CreateContext(gfx::Size())) {
VLOGF(1) << "Failed to create context for VPP";
return nullptr;
}
// We should restrict the acceptable PortConfig for input and output both to
// the one returned by GetPlatformVideoFrameLayout(). However,
// ImageProcessorFactory interface doesn't provide information about what
// ImageProcessor will be used for. (e.g. format conversion after decoding and
// scaling before encoding). Thus we cannot execute
// GetPlatformVideoFrameLayout() with a proper gfx::BufferUsage.
// TODO(crbug.com/898423): Adjust layout once ImageProcessor provide the use
// scenario.
return base::WrapUnique(new VaapiImageProcessor(
input_config, output_config, std::move(vaapi_wrapper),
std::move(client_task_runner)));
}
VaapiImageProcessor::VaapiImageProcessor(
const ImageProcessor::PortConfig& input_config,
const ImageProcessor::PortConfig& output_config,
scoped_refptr<VaapiWrapper> vaapi_wrapper,
scoped_refptr<base::SequencedTaskRunner> client_task_runner)
: ImageProcessor(input_config,
output_config,
OutputMode::IMPORT,
std::move(client_task_runner)),
processor_task_runner_(base::CreateSequencedTaskRunner(
base::TaskTraits{base::ThreadPool()})),
vaapi_wrapper_(std::move(vaapi_wrapper)) {
DETACH_FROM_SEQUENCE(client_sequence_checker_);
DETACH_FROM_SEQUENCE(processor_sequence_checker_);
}
VaapiImageProcessor::~VaapiImageProcessor() {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
}
bool VaapiImageProcessor::ProcessInternal(
scoped_refptr<VideoFrame> input_frame,
scoped_refptr<VideoFrame> output_frame,
FrameReadyCB cb) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
// TODO(akahuang): Use WeakPtr to replace base::Unretained(this).
process_task_tracker_.PostTask(
processor_task_runner_.get(), FROM_HERE,
base::BindOnce(&VaapiImageProcessor::ProcessTask, base::Unretained(this),
std::move(input_frame), std::move(output_frame),
std::move(cb)));
return true;
}
void VaapiImageProcessor::ProcessTask(scoped_refptr<VideoFrame> input_frame,
scoped_refptr<VideoFrame> output_frame,
FrameReadyCB cb) {
DVLOGF(4);
DCHECK_CALLED_ON_VALID_SEQUENCE(processor_sequence_checker_);
auto src_va_surface =
vaapi_wrapper_->CreateVASurfaceForVideoFrame(input_frame.get());
auto dst_va_surface =
vaapi_wrapper_->CreateVASurfaceForVideoFrame(output_frame.get());
if (!src_va_surface || !dst_va_surface) {
// Failed to create VASurface for frames. |cb| isn't executed in the case.
return;
}
// VA-API performs pixel format conversion and scaling without any filters.
vaapi_wrapper_->BlitSurface(std::move(src_va_surface),
std::move(dst_va_surface));
client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), std::move(output_frame)));
}
bool VaapiImageProcessor::Reset() {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
process_task_tracker_.TryCancelAll();
return true;
}
} // namespace media