summaryrefslogtreecommitdiff
path: root/chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc')
-rw-r--r--chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc363
1 files changed, 179 insertions, 184 deletions
diff --git a/chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc b/chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
index f436784d6ca..cf551b2f827 100644
--- a/chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
+++ b/chromium/media/gpu/vaapi/vaapi_mjpeg_decode_accelerator.cc
@@ -24,14 +24,15 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
-#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/bitstream_buffer.h"
#include "media/base/format_utils.h"
#include "media/base/unaligned_shared_memory.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_layout.h"
+#include "media/base/video_util.h"
#include "media/gpu/chromeos/fourcc.h"
+#include "media/gpu/chromeos/libyuv_image_processor_backend.h"
#include "media/gpu/chromeos/platform_video_frame_utils.h"
#include "media/gpu/macros.h"
#include "media/gpu/vaapi/va_surface.h"
@@ -39,7 +40,6 @@
#include "media/gpu/vaapi/vaapi_utils.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/libyuv/include/libyuv.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
@@ -48,15 +48,15 @@ namespace media {
namespace {
-static void ReportToVAJDAResponseToClientUMA(
+void ReportToVAJDAResponseToClientUMA(
chromeos_camera::MjpegDecodeAccelerator::Error response) {
UMA_HISTOGRAM_ENUMERATION(
"Media.VAJDA.ResponseToClient", response,
chromeos_camera::MjpegDecodeAccelerator::Error::MJDA_ERROR_CODE_MAX + 1);
}
-static chromeos_camera::MjpegDecodeAccelerator::Error
-VaapiJpegDecodeStatusToError(VaapiImageDecodeStatus status) {
+chromeos_camera::MjpegDecodeAccelerator::Error VaapiJpegDecodeStatusToError(
+ VaapiImageDecodeStatus status) {
switch (status) {
case VaapiImageDecodeStatus::kSuccess:
return chromeos_camera::MjpegDecodeAccelerator::Error::NO_ERRORS;
@@ -69,7 +69,7 @@ VaapiJpegDecodeStatusToError(VaapiImageDecodeStatus status) {
}
}
-static bool VerifyDataSize(const VAImage* image) {
+bool VerifyDataSize(const VAImage* image) {
const gfx::Size dimensions(base::strict_cast<int>(image->width),
base::strict_cast<int>(image->height));
size_t min_size = 0;
@@ -85,6 +85,7 @@ static bool VerifyDataSize(const VAImage* image) {
}
return base::strict_cast<size_t>(image->data_size) >= min_size;
}
+
} // namespace
void VaapiMjpegDecodeAccelerator::NotifyError(int32_t task_id, Error error) {
@@ -119,12 +120,13 @@ VaapiMjpegDecodeAccelerator::VaapiMjpegDecodeAccelerator(
decoder_thread_("VaapiMjpegDecoderThread"),
weak_this_factory_(this) {}
-// Destroy |decoder_| and |vpp_vaapi_wrapper_| on |decoder_thread_|.
+// Some members expect to be destroyed on the |decoder_thread_|.
void VaapiMjpegDecodeAccelerator::CleanUpOnDecoderThread() {
DCHECK(decoder_task_runner_->BelongsToCurrentThread());
DCHECK(vpp_vaapi_wrapper_->HasOneRef());
vpp_vaapi_wrapper_.reset();
decoder_.reset();
+ image_processor_.reset();
}
VaapiMjpegDecodeAccelerator::~VaapiMjpegDecodeAccelerator() {
@@ -153,6 +155,7 @@ void VaapiMjpegDecodeAccelerator::InitializeOnDecoderTaskRunner(
"Media.VaapiMjpegDecodeAccelerator.VAAPIError"))) {
VLOGF(1) << "Failed initializing |decoder_|";
std::move(init_cb).Run(false);
+ return;
}
vpp_vaapi_wrapper_ = VaapiWrapper::Create(
@@ -163,12 +166,14 @@ void VaapiMjpegDecodeAccelerator::InitializeOnDecoderTaskRunner(
if (!vpp_vaapi_wrapper_) {
VLOGF(1) << "Failed initializing VAAPI for VPP";
std::move(init_cb).Run(false);
+ return;
}
// Size is irrelevant for a VPP context.
if (!vpp_vaapi_wrapper_->CreateContext(gfx::Size())) {
VLOGF(1) << "Failed to create context for VPP";
std::move(init_cb).Run(false);
+ return;
}
std::move(init_cb).Run(true);
@@ -183,9 +188,9 @@ void VaapiMjpegDecodeAccelerator::InitializeOnTaskRunner(
if (!decoder_thread_.Start()) {
VLOGF(1) << "Failed to start decoding thread.";
std::move(init_cb).Run(false);
+ return;
}
decoder_task_runner_ = decoder_thread_.task_runner();
- gpu_memory_buffer_support_ = std::make_unique<gpu::GpuMemoryBufferSupport>();
// base::Unretained() is fine here because we control |decoder_task_runner_|
// lifetime.
@@ -211,179 +216,142 @@ void VaapiMjpegDecodeAccelerator::InitializeAsync(
BindToCurrentLoop(std::move(init_cb))));
}
+void VaapiMjpegDecodeAccelerator::CreateImageProcessor(
+ const VideoFrame* src_frame,
+ const VideoFrame* dst_frame) {
+ DCHECK(decoder_task_runner_->BelongsToCurrentThread());
+
+ // The fourcc of |src_frame| will be either Fourcc(YUYV) or Fourcc(YU12) based
+ // on the implementation of OutputPictureLibYuvOnTaskRunner(). The fourcc of
+ // |dst_frame| should have been validated in DecodeImpl().
+ const auto src_fourcc = Fourcc::FromVideoPixelFormat(src_frame->format());
+ DCHECK(src_fourcc.has_value());
+ const auto dst_fourcc = Fourcc::FromVideoPixelFormat(dst_frame->format());
+ DCHECK(dst_fourcc.has_value());
+ const ImageProcessorBackend::PortConfig input_config(
+ *src_fourcc, src_frame->coded_size(), src_frame->layout().planes(),
+ src_frame->visible_rect(), {src_frame->storage_type()});
+ const ImageProcessorBackend::PortConfig output_config(
+ *dst_fourcc, dst_frame->coded_size(), dst_frame->layout().planes(),
+ dst_frame->visible_rect(), {dst_frame->storage_type()});
+ if (image_processor_ && image_processor_->input_config() == input_config &&
+ image_processor_->output_config() == output_config) {
+ return;
+ }
+
+ // The error callback is posted to the same thread that
+ // LibYUVImageProcessorBackend::Create() is called on
+ // (i.e., |decoder_thread_|) and we control the lifetime of |decoder_thread_|.
+ // Therefore, base::Unretained(this) is safe.
+ image_processor_ = LibYUVImageProcessorBackend::Create(
+ input_config, output_config, {ImageProcessorBackend::OutputMode::IMPORT},
+ VIDEO_ROTATION_0,
+ base::BindRepeating(&VaapiMjpegDecodeAccelerator::OnImageProcessorError,
+ base::Unretained(this)),
+ decoder_task_runner_);
+}
+
bool VaapiMjpegDecodeAccelerator::OutputPictureLibYuvOnTaskRunner(
+ int32_t task_id,
std::unique_ptr<ScopedVAImage> scoped_image,
- int32_t input_buffer_id,
- scoped_refptr<VideoFrame> video_frame) {
+ scoped_refptr<VideoFrame> video_frame,
+ const gfx::Rect& crop_rect) {
DCHECK(decoder_task_runner_->BelongsToCurrentThread());
- TRACE_EVENT1("jpeg", __func__, "input_buffer_id", input_buffer_id);
+ TRACE_EVENT1("jpeg", __func__, "task_id", task_id);
DCHECK(scoped_image);
const VAImage* image = scoped_image->image();
-
- // For camera captures, we assume that the visible size is the same as the
- // coded size.
- DCHECK_EQ(video_frame->visible_rect().size(), video_frame->coded_size());
- DCHECK_EQ(0, video_frame->visible_rect().x());
- DCHECK_EQ(0, video_frame->visible_rect().y());
- DCHECK(decoder_->GetScopedVASurface());
- const gfx::Size visible_size(base::strict_cast<int>(image->width),
- base::strict_cast<int>(image->height));
- if (visible_size != video_frame->visible_rect().size()) {
- VLOGF(1) << "The decoded visible size is not the same as the video frame's";
- return false;
- }
-
- // The decoded image size is aligned up to JPEG MCU size, so it may be larger
- // than |video_frame|'s visible size.
- if (base::strict_cast<int>(image->width) < visible_size.width() ||
- base::strict_cast<int>(image->height) < visible_size.height()) {
- VLOGF(1) << "Decoded image size is smaller than output frame size";
- return false;
- }
DCHECK(VerifyDataSize(image));
+ const gfx::Size src_size(base::strict_cast<int>(image->width),
+ base::strict_cast<int>(image->height));
+ DCHECK(gfx::Rect(src_size).Contains(crop_rect));
- // Extract source pointers and strides.
- auto* const mem =
- static_cast<const uint8_t*>(scoped_image->va_buffer()->data());
- std::array<const uint8_t*, VideoFrame::kMaxPlanes> src_ptrs{};
- std::array<int, VideoFrame::kMaxPlanes> src_strides{};
- for (uint32_t i = 0; i < image->num_planes; i++) {
- src_ptrs[i] = mem + image->offsets[i];
+ // Wrap |image| into VideoFrame.
+ std::vector<int32_t> strides(image->num_planes);
+ for (uint32_t i = 0; i < image->num_planes; ++i) {
if (!base::CheckedNumeric<uint32_t>(image->pitches[i])
- .AssignIfValid(&src_strides[i])) {
- VLOGF(1) << "Can't extract the strides";
- return false;
- }
- }
-
- // Extract destination pointers and strides.
- std::array<uint8_t*, VideoFrame::kMaxPlanes> dst_ptrs{};
- std::array<int, VideoFrame::kMaxPlanes> dst_strides{};
- base::ScopedClosureRunner buffer_unmapper;
- if (video_frame->HasDmaBufs()) {
- // Dmabuf-backed frame needs to be mapped for SW access.
- DCHECK(gpu_memory_buffer_support_);
- absl::optional<gfx::BufferFormat> gfx_format =
- VideoPixelFormatToGfxBufferFormat(video_frame->format());
- if (!gfx_format) {
- VLOGF(1) << "Unsupported format: " << video_frame->format();
+ .AssignIfValid(&strides[i])) {
+ VLOGF(1) << "Invalid VAImage stride " << image->pitches[i]
+ << " for plane " << i;
return false;
}
- auto gmb_handle = CreateGpuMemoryBufferHandle(video_frame.get());
- DCHECK(!gmb_handle.is_null());
- std::unique_ptr<gpu::GpuMemoryBufferImpl> gmb =
- gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
- std::move(gmb_handle), video_frame->coded_size(), *gfx_format,
- gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, base::DoNothing());
- if (!gmb) {
- VLOGF(1) << "Failed to create GPU memory buffer";
- return false;
- }
- if (!gmb->Map()) {
- VLOGF(1) << "Failed to map GPU memory buffer";
- return false;
- }
- for (size_t i = 0; i < video_frame->layout().num_planes(); i++) {
- dst_ptrs[i] = static_cast<uint8_t*>(gmb->memory(i));
- dst_strides[i] = gmb->stride(i);
- }
- buffer_unmapper.ReplaceClosure(
- base::BindOnce(&gpu::GpuMemoryBufferImpl::Unmap, std::move(gmb)));
- } else {
- DCHECK(video_frame->IsMappable());
- for (size_t i = 0; i < video_frame->layout().num_planes(); i++) {
- dst_ptrs[i] = video_frame->visible_data(i);
- dst_strides[i] = video_frame->stride(i);
- }
}
-
+ auto* const data = static_cast<uint8_t*>(scoped_image->va_buffer()->data());
+ scoped_refptr<VideoFrame> src_frame;
switch (image->format.fourcc) {
- case VA_FOURCC_I420:
- DCHECK_EQ(image->num_planes, 3u);
- switch (video_frame->format()) {
- case PIXEL_FORMAT_I420:
- DCHECK_EQ(video_frame->layout().num_planes(), 3u);
- if (libyuv::I420Copy(src_ptrs[0], src_strides[0], src_ptrs[1],
- src_strides[1], src_ptrs[2], src_strides[2],
- dst_ptrs[0], dst_strides[0], dst_ptrs[1],
- dst_strides[1], dst_ptrs[2], dst_strides[2],
- visible_size.width(), visible_size.height())) {
- VLOGF(1) << "I420Copy failed";
- return false;
- }
- break;
- case PIXEL_FORMAT_NV12:
- DCHECK_EQ(video_frame->layout().num_planes(), 2u);
- if (libyuv::I420ToNV12(src_ptrs[0], src_strides[0], src_ptrs[1],
- src_strides[1], src_ptrs[2], src_strides[2],
- dst_ptrs[0], dst_strides[0], dst_ptrs[1],
- dst_strides[1], visible_size.width(),
- visible_size.height())) {
- VLOGF(1) << "I420ToNV12 failed";
- return false;
- }
- break;
- default:
- VLOGF(1) << "Can't convert image from I420 to "
- << video_frame->format();
- return false;
+ case VA_FOURCC_YUY2:
+ case VA_FOURCC('Y', 'U', 'Y', 'V'): {
+ auto layout = VideoFrameLayout::CreateWithStrides(PIXEL_FORMAT_YUY2,
+ src_size, strides);
+ if (!layout.has_value()) {
+ VLOGF(1) << "Failed to create video frame layout";
+ return false;
}
+ src_frame = VideoFrame::WrapExternalDataWithLayout(
+ *layout, crop_rect, crop_rect.size(), data + image->offsets[0],
+ base::strict_cast<size_t>(image->data_size), base::TimeDelta());
break;
- case VA_FOURCC_YUY2:
- case VA_FOURCC('Y', 'U', 'Y', 'V'):
- DCHECK_EQ(image->num_planes, 1u);
- switch (video_frame->format()) {
- case PIXEL_FORMAT_I420:
- DCHECK_EQ(video_frame->layout().num_planes(), 3u);
- if (libyuv::YUY2ToI420(src_ptrs[0], src_strides[0], dst_ptrs[0],
- dst_strides[0], dst_ptrs[1], dst_strides[1],
- dst_ptrs[2], dst_strides[2],
- visible_size.width(), visible_size.height())) {
- VLOGF(1) << "YUY2ToI420 failed";
- return false;
- }
- break;
- case PIXEL_FORMAT_NV12:
- DCHECK_EQ(video_frame->layout().num_planes(), 2u);
- if (libyuv::YUY2ToNV12(src_ptrs[0], src_strides[0], dst_ptrs[0],
- dst_strides[0], dst_ptrs[1], dst_strides[1],
- visible_size.width(), visible_size.height())) {
- VLOGF(1) << "YUY2ToNV12 failed";
- return false;
- }
- break;
- default:
- VLOGF(1) << "Can't convert image from YUYV to "
- << video_frame->format();
- return false;
+ }
+ case VA_FOURCC_I420: {
+ auto layout = VideoFrameLayout::CreateWithStrides(PIXEL_FORMAT_I420,
+ src_size, strides);
+ if (!layout.has_value()) {
+ VLOGF(1) << "Failed to create video frame layout";
+ return false;
}
+ src_frame = VideoFrame::WrapExternalYuvDataWithLayout(
+ *layout, crop_rect, crop_rect.size(), data + image->offsets[0],
+ data + image->offsets[1], data + image->offsets[2],
+ base::TimeDelta());
break;
+ }
default:
- VLOGF(1) << "Can't convert image from "
- << FourccToString(image->format.fourcc) << " to "
- << video_frame->format();
+ VLOGF(1) << "Unsupported VA image format: "
+ << FourccToString(image->format.fourcc);
return false;
}
+ if (!src_frame) {
+ VLOGF(1) << "Failed to create video frame";
+ return false;
+ }
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VaapiMjpegDecodeAccelerator::VideoFrameReady,
- weak_this_factory_.GetWeakPtr(), input_buffer_id));
-
+ CreateImageProcessor(src_frame.get(), video_frame.get());
+ if (!image_processor_) {
+ VLOGF(1) << "Failed to create image processor";
+ return false;
+ }
+ image_processor_->Process(
+ std::move(src_frame), std::move(video_frame),
+ base::BindOnce(
+ [](scoped_refptr<base::SingleThreadTaskRunner> runner,
+ base::OnceClosure cb, scoped_refptr<VideoFrame> frame) {
+ runner->PostTask(FROM_HERE, std::move(cb));
+ },
+ task_runner_,
+ base::BindOnce(&VaapiMjpegDecodeAccelerator::VideoFrameReady,
+ weak_this_factory_.GetWeakPtr(), task_id)));
return true;
}
+void VaapiMjpegDecodeAccelerator::OnImageProcessorError() {
+ DCHECK(decoder_task_runner_->BelongsToCurrentThread());
+ VLOGF(1) << "Failed to process frames using the libyuv image processor";
+ NotifyError(kInvalidTaskId, PLATFORM_FAILURE);
+ image_processor_.reset();
+}
+
bool VaapiMjpegDecodeAccelerator::OutputPictureVppOnTaskRunner(
+ int32_t task_id,
const ScopedVASurface* surface,
- int32_t input_buffer_id,
- scoped_refptr<VideoFrame> video_frame) {
+ scoped_refptr<VideoFrame> video_frame,
+ const gfx::Rect& crop_rect) {
DCHECK(decoder_task_runner_->BelongsToCurrentThread());
DCHECK(surface);
DCHECK(video_frame);
+ DCHECK(gfx::Rect(surface->size()).Contains(crop_rect));
- TRACE_EVENT1("jpeg", __func__, "input_buffer_id", input_buffer_id);
+ TRACE_EVENT1("jpeg", __func__, "task_id", task_id);
scoped_refptr<gfx::NativePixmap> pixmap =
CreateNativePixmapDmaBuf(video_frame.get());
@@ -395,50 +363,36 @@ bool VaapiMjpegDecodeAccelerator::OutputPictureVppOnTaskRunner(
// Bind a VA surface to |video_frame|.
scoped_refptr<VASurface> output_surface =
vpp_vaapi_wrapper_->CreateVASurfaceForPixmap(std::move(pixmap));
-
if (!output_surface) {
VLOGF(1) << "Cannot create VA surface for output buffer";
return false;
}
- // Use VPP to blit the visible size region within |surface| into
- // |output_surface|. BlitSurface() does scaling not cropping when source and
- // destination sizes don't match, so we manipulate the sizes of surfaces to
- // effectively do the cropping.
- const gfx::Size& blit_size = video_frame->visible_rect().size();
- if (surface->size().width() < blit_size.width() ||
- surface->size().height() < blit_size.height()) {
- VLOGF(1) << "Decoded surface size is smaller than target size";
- return false;
- }
scoped_refptr<VASurface> src_surface = base::MakeRefCounted<VASurface>(
- surface->id(), blit_size, surface->format(),
- base::DoNothing() /* release_cb */);
- scoped_refptr<VASurface> dst_surface = base::MakeRefCounted<VASurface>(
- output_surface->id(), blit_size, output_surface->format(),
- base::DoNothing() /* release_cb */);
+ surface->id(), surface->size(), surface->format(),
+ /*release_cb=*/base::DoNothing());
// We should call vaSyncSurface() when passing surface between contexts. See:
// https://lists.01.org/pipermail/intel-vaapi-media/2019-June/000131.html
- if (!vpp_vaapi_wrapper_->SyncSurface(src_surface->id())) {
+ if (!vpp_vaapi_wrapper_->SyncSurface(surface->id())) {
VLOGF(1) << "Cannot sync VPP input surface";
return false;
}
- if (!vpp_vaapi_wrapper_->BlitSurface(*src_surface, *dst_surface)) {
+ if (!vpp_vaapi_wrapper_->BlitSurface(*src_surface, *output_surface,
+ crop_rect)) {
VLOGF(1) << "Cannot convert decoded image into output buffer";
return false;
}
// Sync target surface since the buffer is returning to client.
- if (!vpp_vaapi_wrapper_->SyncSurface(dst_surface->id())) {
+ if (!vpp_vaapi_wrapper_->SyncSurface(output_surface->id())) {
VLOGF(1) << "Cannot sync VPP output surface";
return false;
}
task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VaapiMjpegDecodeAccelerator::VideoFrameReady,
- weak_this_factory_.GetWeakPtr(), input_buffer_id));
+ FROM_HERE, base::BindOnce(&VaapiMjpegDecodeAccelerator::VideoFrameReady,
+ weak_this_factory_.GetWeakPtr(), task_id));
return true;
}
@@ -490,11 +444,9 @@ void VaapiMjpegDecodeAccelerator::DecodeImpl(
int32_t task_id,
base::span<const uint8_t> src_image,
scoped_refptr<VideoFrame> dst_frame) {
- // TODO(andrescj): validate that the video frame's visible size is the same as
- // the parsed JPEG's visible size when it is returned from Decode(), and
- // remove the size checks in OutputPicture*().
VaapiImageDecodeStatus status = decoder_->Decode(src_image);
if (status != VaapiImageDecodeStatus::kSuccess) {
+ VLOGF(1) << "Failed to decode JPEG image";
NotifyError(task_id, VaapiJpegDecodeStatusToError(status));
return;
}
@@ -502,30 +454,71 @@ void VaapiMjpegDecodeAccelerator::DecodeImpl(
DCHECK(surface);
DCHECK(surface->IsValid());
+ // For camera captures, we assume that the visible size is the same as the
+ // coded size.
+ if (dst_frame->visible_rect().size() != dst_frame->coded_size() ||
+ dst_frame->visible_rect().x() != 0 ||
+ dst_frame->visible_rect().y() != 0) {
+ VLOGF(1)
+ << "The video frame visible size should be the same as the coded size";
+ NotifyError(task_id, INVALID_ARGUMENT);
+ return;
+ }
+
+ // Note that |surface->size()| is the visible size of the JPEG image. The
+ // underlying VASurface size (coded size) can be larger because of alignments.
+ if (surface->size().width() < dst_frame->visible_rect().width() ||
+ surface->size().height() < dst_frame->visible_rect().height()) {
+ VLOGF(1) << "Invalid JPEG image and video frame sizes: "
+ << surface->size().ToString() << ", "
+ << dst_frame->visible_rect().size().ToString();
+ NotifyError(task_id, INVALID_ARGUMENT);
+ return;
+ }
+
// For DMA-buf backed |dst_frame|, we will import it as a VA surface and use
// VPP to convert the decoded |surface| into it, if the formats and sizes are
// supported.
- const auto video_frame_fourcc =
+ const auto dst_frame_fourcc =
Fourcc::FromVideoPixelFormat(dst_frame->format());
- if (!video_frame_fourcc) {
+ if (!dst_frame_fourcc) {
VLOGF(1) << "Unsupported video frame format: " << dst_frame->format();
NotifyError(task_id, PLATFORM_FAILURE);
return;
}
- const auto video_frame_va_fourcc = video_frame_fourcc->ToVAFourCC();
- if (!video_frame_va_fourcc) {
+ const auto dst_frame_va_fourcc = dst_frame_fourcc->ToVAFourCC();
+ if (!dst_frame_va_fourcc) {
VLOGF(1) << "Unsupported video frame format: " << dst_frame->format();
NotifyError(task_id, PLATFORM_FAILURE);
return;
}
+
+ // Crop and scale the decoded image into |dst_frame|.
+ // The VPP is known to have some problems with odd-sized buffers, so we
+ // request a crop rectangle whose dimensions are aligned to 2.
+ const gfx::Rect crop_rect = CropSizeForScalingToTarget(
+ surface->size(), dst_frame->visible_rect().size(), /*alignment=*/2u);
+ if (crop_rect.IsEmpty()) {
+ VLOGF(1) << "Failed to calculate crop rectangle for "
+ << surface->size().ToString() << " to "
+ << dst_frame->visible_rect().size().ToString();
+ NotifyError(task_id, PLATFORM_FAILURE);
+ return;
+ }
+
// TODO(kamesan): move HasDmaBufs() to DCHECK when we deprecate
// shared-memory-backed video frame.
+ // Check all the sizes involved until we figure out the definition of min/max
+ // resolutions in the VPP profile (b/195312242).
if (dst_frame->HasDmaBufs() &&
VaapiWrapper::IsVppResolutionAllowed(surface->size()) &&
+ VaapiWrapper::IsVppResolutionAllowed(crop_rect.size()) &&
+ VaapiWrapper::IsVppResolutionAllowed(dst_frame->visible_rect().size()) &&
VaapiWrapper::IsVppSupportedForJpegDecodedSurfaceToFourCC(
- surface->format(), *video_frame_va_fourcc)) {
- if (!OutputPictureVppOnTaskRunner(surface, task_id, std::move(dst_frame))) {
+ surface->format(), *dst_frame_va_fourcc)) {
+ if (!OutputPictureVppOnTaskRunner(task_id, surface, std::move(dst_frame),
+ crop_rect)) {
VLOGF(1) << "Output picture using VPP failed";
NotifyError(task_id, PLATFORM_FAILURE);
}
@@ -537,13 +530,15 @@ void VaapiMjpegDecodeAccelerator::DecodeImpl(
// 2. VPP doesn't support the format conversion. This is intended for AMD
// VAAPI driver whose VPP only supports converting decoded 4:2:0 JPEGs.
std::unique_ptr<ScopedVAImage> image =
- decoder_->GetImage(*video_frame_va_fourcc, &status);
+ decoder_->GetImage(*dst_frame_va_fourcc, &status);
if (status != VaapiImageDecodeStatus::kSuccess) {
NotifyError(task_id, VaapiJpegDecodeStatusToError(status));
return;
}
- if (!OutputPictureLibYuvOnTaskRunner(std::move(image), task_id,
- std::move(dst_frame))) {
+ DCHECK_EQ(image->image()->width, surface->size().width());
+ DCHECK_EQ(image->image()->height, surface->size().height());
+ if (!OutputPictureLibYuvOnTaskRunner(task_id, std::move(image),
+ std::move(dst_frame), crop_rect)) {
VLOGF(1) << "Output picture using libyuv failed";
NotifyError(task_id, PLATFORM_FAILURE);
}