Commit e3e9a8c1 authored by Andres Calderon Jaramillo's avatar Andres Calderon Jaramillo Committed by Commit Bot

media: Make the VaapiJpegDecodeAcceleratorWorker output the result.

This CL makes the VaapiJpegDecodeAcceleratorWorker extract the result of
a JPEG decode as a DmaBuf and return it to whoever requested the decode.

Currently, the visible size of an image is not readily available when
the result of a decode is exported as a NativePixmapDmaBuf. Therefore,
most of this CL plumbs this visible size because it's needed so that it
can be returned to the rest of the rendering pipeline after an image is
decoded.

The plumbing consists in keeping track of the visible size inside the
ScopedVASurface that refers to the decode result. This is then used when
either 1) exporting the surface as a NativePixmapDmaBuf, and
2) extracting a VAImage out of the surface.

The JPEG decoder unit tests are correspondingly adapted so that odd
images are only tested when exporting decodes as DmaBufs. This is
because odd images are only expected in this path (as opposed to the
camera capture path which may use VAImages, and odd sizes seem to
misbehave in that case).

Test: tests for the worker are added in media_unittests.
Bug: 868400
Change-Id: Icb78d9c7f5b3c9213e36c885cf9ff16480d1c545
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1657568
Commit-Queue: Andres Calderon Jaramillo <andrescj@chromium.org>
Reviewed-by: default avatarDaniele Castagna <dcastagna@chromium.org>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#681175}
parent afb0c7d8
......@@ -162,12 +162,15 @@ source_set("vaapi_test_utils") {
source_set("unit_test") {
testonly = true
sources = [
"vaapi_jpeg_decode_accelerator_worker_unittest.cc",
"vaapi_video_decode_accelerator_unittest.cc",
]
deps = [
":vaapi",
"//base",
"//base/test:test_support",
"//gpu:test_support",
"//gpu/ipc/service",
"//media/gpu:common",
"//mojo/core/embedder",
"//testing/gmock",
......
......@@ -52,13 +52,8 @@ bool CompareImages(const DecodedImage& reference_image,
return false;
// Uses the reference image's size as the ground truth.
// Note the use of > instead of !=. This is to handle the case in the Intel
// iHD driver where, for example, the size of an image is 1280x720 while the
// size of the VAAPI surface is 1280x736 because of additional alignment. See
// https://git.io/fj6nA.
const gfx::Size image_size = reference_image.size;
if (image_size.width() > hw_decoded_image.size.width() ||
image_size.height() > hw_decoded_image.size.height()) {
if (image_size != hw_decoded_image.size) {
DLOG(ERROR) << "Wrong expected software decoded image size, "
<< image_size.ToString() << " versus VaAPI provided "
<< hw_decoded_image.size.ToString();
......
......@@ -89,7 +89,7 @@ VaapiImageDecoder::GetSupportedProfile() const {
return profile;
}
scoped_refptr<gfx::NativePixmapDmaBuf>
std::unique_ptr<NativePixmapAndSizeInfo>
VaapiImageDecoder::ExportAsNativePixmapDmaBuf(VaapiImageDecodeStatus* status) {
DCHECK(status);
......@@ -108,24 +108,18 @@ VaapiImageDecoder::ExportAsNativePixmapDmaBuf(VaapiImageDecodeStatus* status) {
}
DCHECK(temp_scoped_va_surface->IsValid());
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
vaapi_wrapper_->ExportVASurfaceAsNativePixmapDmaBuf(
temp_scoped_va_surface->id());
if (!pixmap) {
*temp_scoped_va_surface);
if (!exported_pixmap) {
*status = VaapiImageDecodeStatus::kCannotExportSurface;
return nullptr;
}
// In Intel's iHD driver the size requested for the surface may be different
// than the buffer size of the NativePixmap because of additional alignment.
// See https://git.io/fj6nA.
DCHECK_LE(temp_scoped_va_surface->size().width(),
pixmap->GetBufferSize().width());
DCHECK_LE(temp_scoped_va_surface->size().height(),
pixmap->GetBufferSize().height());
DCHECK_EQ(temp_scoped_va_surface->size(),
exported_pixmap->pixmap->GetBufferSize());
*status = VaapiImageDecodeStatus::kSuccess;
return pixmap;
return exported_pixmap;
}
} // namespace media
......@@ -23,6 +23,7 @@ class NativePixmapDmaBuf;
namespace media {
struct NativePixmapAndSizeInfo;
class ScopedVASurface;
class VaapiWrapper;
......@@ -59,7 +60,7 @@ class VaapiImageDecoder {
// Initializes |vaapi_wrapper_| in kDecode mode with the
// appropriate VAAPI profile and |error_uma_cb| for error reporting.
bool Initialize(const base::RepeatingClosure& error_uma_cb);
virtual bool Initialize(const base::RepeatingClosure& error_uma_cb);
// Decodes a picture. It will fill VA-API parameters and call the
// corresponding VA-API methods according to the image in |encoded_image|.
......@@ -68,22 +69,24 @@ class VaapiImageDecoder {
// destruction of this class. Returns a VaapiImageDecodeStatus that will
// indicate whether the decode succeeded or the reason it failed. Note that
// the internal ScopedVASurface is destroyed on failure.
VaapiImageDecodeStatus Decode(base::span<const uint8_t> encoded_image);
virtual VaapiImageDecodeStatus Decode(
base::span<const uint8_t> encoded_image);
// Returns a pointer to the internally managed ScopedVASurface.
const ScopedVASurface* GetScopedVASurface() const;
virtual const ScopedVASurface* GetScopedVASurface() const;
// Returns the type of image supported by this decoder.
virtual gpu::ImageDecodeAcceleratorType GetType() const = 0;
// Returns the image profile supported by this decoder.
gpu::ImageDecodeAcceleratorSupportedProfile GetSupportedProfile() const;
virtual gpu::ImageDecodeAcceleratorSupportedProfile GetSupportedProfile()
const;
// Exports the decoded data from the last Decode() call as a
// gfx::NativePixmapDmaBuf. Returns nullptr on failure and sets *|status| to
// the reason for failure. On success, the image decoder gives up ownership of
// the buffer underlying the NativePixmapDmaBuf.
scoped_refptr<gfx::NativePixmapDmaBuf> ExportAsNativePixmapDmaBuf(
virtual std::unique_ptr<NativePixmapAndSizeInfo> ExportAsNativePixmapDmaBuf(
VaapiImageDecodeStatus* status);
protected:
......
......@@ -4,8 +4,6 @@
#include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.h"
#include <va/va.h>
#include <utility>
#include "base/bind.h"
......@@ -21,9 +19,12 @@
#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_utils.h"
#include "media/gpu/vaapi/vaapi_wrapper.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 {
......@@ -44,7 +45,7 @@ void ReportToVAJDAWorkerDecoderFailureUMA(VAJDAWorkerDecoderFailure failure) {
// |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(
VaapiJpegDecoder* decoder,
VaapiImageDecoder* decoder,
std::vector<uint8_t> encoded_data,
const gfx::Size& output_size,
gpu::ImageDecodeAcceleratorWorker::CompletedDecodeCB decode_cb) {
......@@ -54,6 +55,8 @@ void DecodeTask(
gpu::ImageDecodeAcceleratorWorker::CompletedDecodeCB scoped_decode_callback =
mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(decode_cb),
nullptr);
// Decode into a VAAPI surface.
DCHECK(decoder);
VaapiImageDecodeStatus status = decoder->Decode(
base::make_span<const uint8_t>(encoded_data.data(), encoded_data.size()));
......@@ -62,17 +65,38 @@ void DecodeTask(
<< static_cast<uint32_t>(status);
return;
}
std::unique_ptr<ScopedVAImage> scoped_image =
decoder->GetImage(VA_FOURCC_RGBX /* preferred_image_fourcc */, &status);
// Export the decode result as a NativePixmap.
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
decoder->ExportAsNativePixmapDmaBuf(&status);
if (status != VaapiImageDecodeStatus::kSuccess) {
DVLOGF(1) << "Failed to get image - status = "
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;
}
// TODO(crbug.com/868400): output the decoded data.
DCHECK(scoped_image);
std::move(scoped_decode_callback).Run(nullptr);
// 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;
std::move(scoped_decode_callback).Run(std::move(result));
}
} // namespace
......
......@@ -47,13 +47,13 @@ class VaapiJpegDecodeAcceleratorWorker
CompletedDecodeCB decode_cb) override;
private:
friend class VaapiJpegDecodeAcceleratorWorkerTest;
explicit VaapiJpegDecodeAcceleratorWorker(
std::unique_ptr<VaapiJpegDecoder> decoder);
// We delegate the decoding to |decoder_| which is constructed on the ctor and
// then used and destroyed on |decoder_task_runner_| (unless initialization
// failed, in which case it doesn't matter where it's destroyed since no tasks
// using |decoder_| should have been posted to |decoder_task_runner_|).
// We delegate the decoding to |decoder_| which is used and destroyed on
// |decoder_task_runner_|.
std::unique_ptr<VaapiJpegDecoder> decoder_;
scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_;
......
// 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 <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "base/bind.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_task_environment.h"
#include "gpu/ipc/service/image_decode_accelerator_worker.h"
#include "media/gpu/vaapi/vaapi_image_decoder.h"
#include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator_worker.h"
#include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#include "ui/gfx/native_pixmap_handle.h"
using testing::_;
using testing::AllOf;
using testing::InSequence;
using testing::IsNull;
using testing::NotNull;
using testing::Property;
using testing::Return;
using testing::StrictMock;
namespace media {
namespace {
constexpr gfx::BufferFormat kFormatForDecodes = gfx::BufferFormat::YVU_420;
class MockNativePixmapDmaBuf : public gfx::NativePixmapDmaBuf {
public:
MockNativePixmapDmaBuf(const gfx::Size& size)
: gfx::NativePixmapDmaBuf(size,
kFormatForDecodes,
gfx::NativePixmapHandle()) {}
gfx::NativePixmapHandle ExportHandle() override {
gfx::NativePixmapHandle handle{};
DCHECK_EQ(gfx::BufferFormat::YVU_420, GetBufferFormat());
handle.planes = std::vector<gfx::NativePixmapPlane>(3u);
return handle;
}
protected:
~MockNativePixmapDmaBuf() override = default;
};
class MockVaapiJpegDecoder : public VaapiJpegDecoder {
public:
MockVaapiJpegDecoder() = default;
~MockVaapiJpegDecoder() override = default;
MOCK_METHOD1(Initialize, bool(const base::RepeatingClosure&));
MOCK_METHOD1(Decode, VaapiImageDecodeStatus(base::span<const uint8_t>));
MOCK_CONST_METHOD0(GetScopedVASurface, const ScopedVASurface*());
MOCK_CONST_METHOD0(GetType, gpu::ImageDecodeAcceleratorType());
MOCK_CONST_METHOD0(GetSupportedProfile,
gpu::ImageDecodeAcceleratorSupportedProfile());
MOCK_METHOD1(
ExportAsNativePixmapDmaBuf,
std::unique_ptr<NativePixmapAndSizeInfo>(VaapiImageDecodeStatus*));
MOCK_METHOD1(AllocateVASurfaceAndSubmitVABuffers,
VaapiImageDecodeStatus(base::span<const uint8_t>));
};
} // namespace
class VaapiJpegDecodeAcceleratorWorkerTest : public testing::Test {
public:
VaapiJpegDecodeAcceleratorWorkerTest()
: worker_(std::make_unique<StrictMock<MockVaapiJpegDecoder>>()) {}
MockVaapiJpegDecoder* decoder() const {
return static_cast<MockVaapiJpegDecoder*>(worker_.decoder_.get());
}
MOCK_METHOD1(
OnDecodeCompleted,
void(std::unique_ptr<gpu::ImageDecodeAcceleratorWorker::DecodeResult>));
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
VaapiJpegDecodeAcceleratorWorker worker_;
DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecodeAcceleratorWorkerTest);
};
ACTION_P2(ExportAsNativePixmapDmaBufSuccessfully,
va_surface_resolution,
visible_size) {
*arg0 = VaapiImageDecodeStatus::kSuccess;
auto exported_pixmap = std::make_unique<NativePixmapAndSizeInfo>();
exported_pixmap->va_surface_resolution = va_surface_resolution;
exported_pixmap->byte_size = 1u;
exported_pixmap->pixmap =
base::MakeRefCounted<MockNativePixmapDmaBuf>(visible_size);
return exported_pixmap;
}
TEST_F(VaapiJpegDecodeAcceleratorWorkerTest, DecodeSuccess) {
constexpr gfx::Size kVaSurfaceResolution(128, 256);
constexpr gfx::Size kVisibleSize(120, 250);
std::vector<uint8_t> encoded_data = {1u, 2u, 3u};
{
InSequence sequence;
EXPECT_CALL(*decoder(),
Decode(AllOf(Property(&base::span<const uint8_t>::data,
encoded_data.data()),
Property(&base::span<const uint8_t>::size,
encoded_data.size())) /* encoded_data */))
.WillOnce(Return(VaapiImageDecodeStatus::kSuccess));
EXPECT_CALL(*decoder(), ExportAsNativePixmapDmaBuf(NotNull() /* status */))
.WillOnce(ExportAsNativePixmapDmaBufSuccessfully(kVaSurfaceResolution,
kVisibleSize));
EXPECT_CALL(*this, OnDecodeCompleted(NotNull()));
}
worker_.Decode(
std::move(encoded_data), kVisibleSize,
base::BindOnce(&VaapiJpegDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
scoped_task_environment_.RunUntilIdle();
}
TEST_F(VaapiJpegDecodeAcceleratorWorkerTest, DecodeFails) {
constexpr gfx::Size kVisibleSize(120, 250);
std::vector<uint8_t> encoded_data = {1u, 2u, 3u};
{
InSequence sequence;
EXPECT_CALL(*decoder(),
Decode(AllOf(Property(&base::span<const uint8_t>::data,
encoded_data.data()),
Property(&base::span<const uint8_t>::size,
encoded_data.size())) /* encoded_data */))
.WillOnce(Return(VaapiImageDecodeStatus::kExecuteDecodeFailed));
EXPECT_CALL(*this, OnDecodeCompleted(IsNull()));
}
worker_.Decode(
std::move(encoded_data), kVisibleSize,
base::BindOnce(&VaapiJpegDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
scoped_task_environment_.RunUntilIdle();
}
} // namespace media
......@@ -241,20 +241,28 @@ VaapiImageDecodeStatus VaapiJpegDecoder::AllocateVASurfaceAndSubmitVABuffers(
}
// Prepare the VaSurface for decoding.
const gfx::Size new_coded_size(
base::strict_cast<int>(parse_result.frame_header.coded_width),
base::strict_cast<int>(parse_result.frame_header.coded_height));
const gfx::Size new_visible_size(
base::strict_cast<int>(parse_result.frame_header.visible_width),
base::strict_cast<int>(parse_result.frame_header.visible_height));
DCHECK(!scoped_va_context_and_surface_ ||
scoped_va_context_and_surface_->IsValid());
if (!scoped_va_context_and_surface_ ||
new_coded_size != scoped_va_context_and_surface_->size() ||
new_visible_size != scoped_va_context_and_surface_->size() ||
picture_va_rt_format != scoped_va_context_and_surface_->format()) {
scoped_va_context_and_surface_.reset();
scoped_va_context_and_surface_ =
ScopedVAContextAndSurface(vaapi_wrapper_
->CreateContextAndScopedVASurface(
picture_va_rt_format, new_coded_size)
.release());
// We'll request a surface of |new_coded_size| from the VAAPI, but we will
// keep track of the |new_visible_size| inside the ScopedVASurface so that
// when we create a VAImage or export the surface as a NativePixmapDmaBuf,
// we can report the size that clients should be using to read the contents.
const gfx::Size new_coded_size(
base::strict_cast<int>(parse_result.frame_header.coded_width),
base::strict_cast<int>(parse_result.frame_header.coded_height));
scoped_va_context_and_surface_.reset(
vaapi_wrapper_
->CreateContextAndScopedVASurface(picture_va_rt_format,
new_coded_size, new_visible_size)
.release());
if (!scoped_va_context_and_surface_) {
VLOGF(1) << "CreateContextAndScopedVASurface() failed";
return VaapiImageDecodeStatus::kSurfaceCreationFailed;
......@@ -330,6 +338,18 @@ std::unique_ptr<ScopedVAImage> VaapiJpegDecoder::GetImage(
return nullptr;
}
VAImageFormat image_format{.fourcc = image_fourcc};
// In at least one driver, the VPP seems to have problems if we request a
// VAImage with odd dimensions. Rather than debugging the issue in depth, we
// disable support for odd dimensions since the VAImage path is only expected
// to be used in camera captures (and we don't expect JPEGs with odd
// dimensions in that path).
if ((scoped_va_context_and_surface_->size().width() & 1) ||
(scoped_va_context_and_surface_->size().height() & 1)) {
VLOGF(1) << "Getting images with odd dimensions is not supported";
*status = VaapiImageDecodeStatus::kCannotGetImage;
NOTREACHED();
return nullptr;
}
auto scoped_image = vaapi_wrapper_->CreateVaImage(
scoped_va_context_and_surface_->id(), &image_format,
scoped_va_context_and_surface_->size());
......
......@@ -42,6 +42,7 @@
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
......@@ -55,14 +56,18 @@ using DecodedImagePtr = std::unique_ptr<vaapi_test_utils::DecodedImage>;
constexpr const char* kYuv422Filename = "pixel-1280x720.jpg";
constexpr const char* kYuv420Filename = "pixel-1280x720-yuv420.jpg";
constexpr const char* kYuv444Filename = "pixel-1280x720-yuv444.jpg";
constexpr const char* kOddHeightImageFilename = "peach_pi-40x23.jpg";
constexpr const char* kOddWidthImageFilename = "peach_pi-41x22.jpg";
constexpr const char* kOddDimensionsImageFilename = "peach_pi-41x23.jpg";
constexpr const char* kOddHeightImageFilename = "pixel-40x23-yuv420.jpg";
constexpr const char* kOddWidthImageFilename = "pixel-41x22-yuv420.jpg";
constexpr const char* kOddDimensionsImageFilename = "pixel-41x23-yuv420.jpg";
const vaapi_test_utils::TestParam kTestCases[] = {
const vaapi_test_utils::TestParam kVAImageTestCases[] = {
{"YUV422", kYuv422Filename},
{"YUV420", kYuv420Filename},
{"YUV444", kYuv444Filename},
};
const vaapi_test_utils::TestParam kDmaBufTestCases[] = {
{"YUV420", kYuv420Filename},
{"OddHeightImage40x23", kOddHeightImageFilename},
{"OddWidthImage41x22", kOddWidthImageFilename},
{"OddDimensionsImage41x23", kOddDimensionsImageFilename},
......@@ -266,7 +271,7 @@ class VaapiJpegDecoderTest
base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status = nullptr);
scoped_refptr<gfx::NativePixmapDmaBuf> DecodeToNativePixmapDmaBuf(
std::unique_ptr<NativePixmapAndSizeInfo> DecodeToNativePixmapDmaBuf(
base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status = nullptr);
......@@ -317,7 +322,7 @@ std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
return Decode(encoded_image, VA_FOURCC_I420, status);
}
scoped_refptr<gfx::NativePixmapDmaBuf>
std::unique_ptr<NativePixmapAndSizeInfo>
VaapiJpegDecoderTest::DecodeToNativePixmapDmaBuf(
base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status) {
......@@ -325,18 +330,19 @@ VaapiJpegDecoderTest::DecodeToNativePixmapDmaBuf(
EXPECT_EQ(!!decoder_.GetScopedVASurface(),
decode_status == VaapiImageDecodeStatus::kSuccess);
// Still try to get the pixmap when decode fails.
VaapiImageDecodeStatus pixmap_status;
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
decoder_.ExportAsNativePixmapDmaBuf(&pixmap_status);
EXPECT_EQ(!!pixmap, pixmap_status == VaapiImageDecodeStatus::kSuccess);
// Still try to export the surface when decode fails.
VaapiImageDecodeStatus export_status;
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
decoder_.ExportAsNativePixmapDmaBuf(&export_status);
EXPECT_EQ(!!exported_pixmap,
export_status == VaapiImageDecodeStatus::kSuccess);
// Return the first fail status.
if (status) {
*status = decode_status != VaapiImageDecodeStatus::kSuccess ? decode_status
: pixmap_status;
: export_status;
}
return pixmap;
return exported_pixmap;
}
// The intention of this test is to ensure that the workarounds added in
......@@ -431,9 +437,9 @@ TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
ASSERT_TRUE(decoder_.GetScopedVASurface());
EXPECT_TRUE(decoder_.GetScopedVASurface()->IsValid());
EXPECT_EQ(decoder_.GetScopedVASurface()->size().width(),
base::strict_cast<int>(parse_result.frame_header.coded_width));
base::strict_cast<int>(parse_result.frame_header.visible_width));
EXPECT_EQ(decoder_.GetScopedVASurface()->size().height(),
base::strict_cast<int>(parse_result.frame_header.coded_height));
base::strict_cast<int>(parse_result.frame_header.visible_height));
EXPECT_EQ(rt_format, decoder_.GetScopedVASurface()->format());
const uint32_t actual_fourcc = scoped_image->image()->format.fourcc;
// TODO(andrescj): CompareImages() only supports I420, NV12, YUY2, and YUYV.
......@@ -515,8 +521,16 @@ TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) {
}
}
class VaapiJpegDecoderWithDmaBufsTest : public VaapiJpegDecoderTest {
public:
VaapiJpegDecoderWithDmaBufsTest() = default;
~VaapiJpegDecoderWithDmaBufsTest() override = default;
// TODO(andrescj): move DecodeToNativePixmapDmaBuf() to here.
};
// TODO(andrescj): test other JPEG formats besides YUV 4:2:0.
TEST_F(VaapiJpegDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) {
TEST_P(VaapiJpegDecoderWithDmaBufsTest, DecodeSucceeds) {
if (base::StartsWith(VaapiWrapper::GetVendorStringForTesting(),
"Mesa Gallium driver", base::CompareCase::SENSITIVE)) {
// TODO(crbug.com/974438): until we support surfaces with multiple buffer
......@@ -530,18 +544,30 @@ TEST_F(VaapiJpegDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) {
GTEST_SKIP();
}
base::FilePath input_file = FindTestDataFilePath(kYuv420Filename);
base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
std::string jpeg_data;
ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
<< "failed to read input data from " << input_file.value();
const auto encoded_image = base::make_span<const uint8_t>(
reinterpret_cast<const uint8_t*>(jpeg_data.data()), jpeg_data.size());
VaapiImageDecodeStatus status;
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
DecodeToNativePixmapDmaBuf(encoded_image, &status);
ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, status);
EXPECT_FALSE(decoder_.GetScopedVASurface());
ASSERT_TRUE(pixmap);
ASSERT_TRUE(exported_pixmap);
ASSERT_TRUE(exported_pixmap->pixmap);
// Make sure the visible area is contained by the surface.
EXPECT_FALSE(exported_pixmap->va_surface_resolution.IsEmpty());
EXPECT_FALSE(exported_pixmap->pixmap->GetBufferSize().IsEmpty());
ASSERT_TRUE(
gfx::Rect(exported_pixmap->va_surface_resolution)
.Contains(gfx::Rect(exported_pixmap->pixmap->GetBufferSize())));
// TODO(andrescj): we could get a better lower bound based on the dimensions
// and the format.
ASSERT_GT(exported_pixmap->byte_size, 0u);
// After exporting the surface, we should not be able to obtain a VAImage with
// the decoded data.
......@@ -558,14 +584,18 @@ TEST_F(VaapiJpegDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) {
// think that it is mapping a YVU_420, but it's actually mapping a YUV_420.
//
// TODO(andrescj): revisit this once crrev.com/c/1573718 lands.
gfx::NativePixmapHandle handle = pixmap->ExportHandle();
if (pixmap->GetBufferFormat() == gfx::BufferFormat::YVU_420)
gfx::NativePixmapHandle handle = exported_pixmap->pixmap->ExportHandle();
ASSERT_EQ(gfx::NumberOfPlanesForLinearBufferFormat(
exported_pixmap->pixmap->GetBufferFormat()),
handle.planes.size());
if (exported_pixmap->pixmap->GetBufferFormat() == gfx::BufferFormat::YVU_420)
std::swap(handle.planes[1], handle.planes[2]);
LocalGpuMemoryBufferManager gpu_memory_buffer_manager;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
gpu_memory_buffer_manager.ImportDmaBuf(handle, pixmap->GetBufferSize(),
pixmap->GetBufferFormat());
gpu_memory_buffer_manager.ImportDmaBuf(
handle, exported_pixmap->pixmap->GetBufferSize(),
exported_pixmap->pixmap->GetBufferFormat());
ASSERT_TRUE(gpu_memory_buffer);
ASSERT_TRUE(gpu_memory_buffer->Map());
vaapi_test_utils::DecodedImage decoded_image{};
......@@ -723,7 +753,12 @@ TEST_F(VaapiJpegDecoderTest, DecodeFails) {
INSTANTIATE_TEST_SUITE_P(,
VaapiJpegDecoderTest,
testing::ValuesIn(kTestCases),
testing::ValuesIn(kVAImageTestCases),
vaapi_test_utils::TestParamToString);
INSTANTIATE_TEST_SUITE_P(,
VaapiJpegDecoderWithDmaBufsTest,
testing::ValuesIn(kDmaBufTestCases),
vaapi_test_utils::TestParamToString);
} // namespace media
......@@ -154,11 +154,25 @@ bool VaapiMjpegDecodeAccelerator::OutputPictureOnTaskRunner(
TRACE_EVENT1("jpeg", "VaapiMjpegDecodeAccelerator::OutputPictureOnTaskRunner",
"input_buffer_id", input_buffer_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;
}
// Copy image content from VAImage to VideoFrame. If the image is not in the
// I420 format we'll have to convert it.
DCHECK(scoped_image);
auto* mem = static_cast<uint8_t*>(scoped_image->va_buffer()->data());
const VAImage* image = scoped_image->image();
DCHECK_GE(base::strict_cast<int>(image->width),
video_frame->coded_size().width());
DCHECK_GE(base::strict_cast<int>(image->height),
......@@ -183,8 +197,7 @@ bool VaapiMjpegDecodeAccelerator::OutputPictureOnTaskRunner(
if (libyuv::I420Copy(src_y, src_y_stride, src_u, src_u_stride, src_v,
src_v_stride, dst_y, dst_y_stride, dst_u,
dst_u_stride, dst_v, dst_v_stride,
video_frame->coded_size().width(),
video_frame->coded_size().height())) {
visible_size.width(), visible_size.height())) {
VLOGF(1) << "I420Copy failed";
return false;
}
......@@ -197,8 +210,7 @@ bool VaapiMjpegDecodeAccelerator::OutputPictureOnTaskRunner(
const size_t src_yuy2_stride = image->pitches[0];
if (libyuv::YUY2ToI420(src_yuy2, src_yuy2_stride, dst_y, dst_y_stride,
dst_u, dst_u_stride, dst_v, dst_v_stride,
video_frame->coded_size().width(),
video_frame->coded_size().height())) {
visible_size.width(), visible_size.height())) {
VLOGF(1) << "YUY2ToI420 failed";
return false;
}
......
......@@ -16,6 +16,7 @@
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
#include "base/test/gtest_util.h"
#include "media/gpu/vaapi/vaapi_utils.h"
......@@ -134,6 +135,44 @@ TEST_F(VaapiUtilsTest, ScopedVASurface) {
EXPECT_EQ(coded_size, scoped_va_surface->size());
}
// This test exercises the creation of a ScopedVASurface where the requested
// size and the visible size are different.
TEST_F(VaapiUtilsTest, ScopedVASurfaceWithVisibleSize) {
const gfx::Size coded_size(64, 64);
const gfx::Size visible_size(60, 60);
auto scoped_va_surface = vaapi_wrapper_->CreateContextAndScopedVASurface(
VA_RT_FORMAT_YUV420, coded_size, visible_size);
ASSERT_TRUE(scoped_va_surface);
EXPECT_TRUE(scoped_va_surface->IsValid());
EXPECT_EQ(VA_RT_FORMAT_YUV420,
base::checked_cast<int>(scoped_va_surface->format()));
EXPECT_EQ(visible_size, scoped_va_surface->size());
// Export the surface to query its actual size.
if (base::StartsWith(VaapiWrapper::GetVendorStringForTesting(),
"Mesa Gallium driver", base::CompareCase::SENSITIVE)) {
// TODO(andrescj): don't skip this once
// ExportVASurfaceAsNativePixmapDmaBuf() supports multiple BOs.
return;
}
if (base::StartsWith(VaapiWrapper::GetVendorStringForTesting(),
"Intel i965 driver", base::CompareCase::SENSITIVE)) {
// TODO(b/135705575): not sure why the following is failing in this driver.
// Probably because the surface is never actually allocated because no
// decoding work is being done. Revisit this once the correct offsets are
// exported.
return;
}
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
vaapi_wrapper_->ExportVASurfaceAsNativePixmapDmaBuf(*scoped_va_surface);
ASSERT_TRUE(exported_pixmap);
ASSERT_TRUE(exported_pixmap->pixmap);
EXPECT_LE(coded_size.width(), exported_pixmap->va_surface_resolution.width());
EXPECT_LE(coded_size.height(),
exported_pixmap->va_surface_resolution.height());
}
// This test exercises the creation of a ScopedVASurface with an invalid
// size.
TEST_F(VaapiUtilsTest, ScopedVASurfaceInvalidSizeRequest) {
......
......@@ -31,7 +31,9 @@
#include "media/parsers/vp8_parser.h"
#include "media/parsers/webp_parser.h"
#include "third_party/libwebp/src/webp/decode.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
......@@ -103,26 +105,27 @@ class VaapiWebPDecoderTest
return GetTestDataFilePath(file_name);
}
scoped_refptr<gfx::NativePixmapDmaBuf> DecodeToNativePixmapDmaBuf(
std::unique_ptr<NativePixmapAndSizeInfo> Decode(
base::span<const uint8_t> encoded_image,
VaapiImageDecodeStatus* status = nullptr) {
const VaapiImageDecodeStatus decode_status = decoder_.Decode(encoded_image);
EXPECT_EQ(!!decoder_.GetScopedVASurface(),
decode_status == VaapiImageDecodeStatus::kSuccess);
// Still try to get the pixmap when decode fails.
VaapiImageDecodeStatus pixmap_status;
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
decoder_.ExportAsNativePixmapDmaBuf(&pixmap_status);
EXPECT_EQ(!!pixmap, pixmap_status == VaapiImageDecodeStatus::kSuccess);
// Still try to export the surface when decode fails.
VaapiImageDecodeStatus export_status;
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
decoder_.ExportAsNativePixmapDmaBuf(&export_status);
EXPECT_EQ(!!exported_pixmap,
export_status == VaapiImageDecodeStatus::kSuccess);
// Return the first fail status.
if (status) {
*status = decode_status != VaapiImageDecodeStatus::kSuccess
? decode_status
: pixmap_status;
: export_status;
}
return pixmap;
return exported_pixmap;
}
protected:
......@@ -144,18 +147,36 @@ TEST_P(VaapiWebPDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) {
VAProfileVP8Version0_3, VA_RT_FORMAT_YUV420));
VaapiImageDecodeStatus status;
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap =
DecodeToNativePixmapDmaBuf(encoded_image, &status);
std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
Decode(encoded_image, &status);
ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, status);
EXPECT_FALSE(decoder_.GetScopedVASurface());
ASSERT_TRUE(pixmap);
ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, pixmap->GetBufferFormat());
ASSERT_TRUE(exported_pixmap);
ASSERT_TRUE(exported_pixmap->pixmap);
ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR,
exported_pixmap->pixmap->GetBufferFormat());
// Make sure the visible area is contained by the surface.
EXPECT_FALSE(exported_pixmap->va_surface_resolution.IsEmpty());
EXPECT_FALSE(exported_pixmap->pixmap->GetBufferSize().IsEmpty());
ASSERT_TRUE(
gfx::Rect(exported_pixmap->va_surface_resolution)
.Contains(gfx::Rect(exported_pixmap->pixmap->GetBufferSize())));
// TODO(andrescj): we could get a better lower bound based on the dimensions
// and the format.
ASSERT_GT(exported_pixmap->byte_size, 0u);
gfx::NativePixmapHandle handle = exported_pixmap->pixmap->ExportHandle();
ASSERT_EQ(gfx::NumberOfPlanesForLinearBufferFormat(
exported_pixmap->pixmap->GetBufferFormat()),
handle.planes.size());
gfx::NativePixmapHandle handle = pixmap->ExportHandle();
LocalGpuMemoryBufferManager gpu_memory_buffer_manager;
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
gpu_memory_buffer_manager.ImportDmaBuf(handle, pixmap->GetBufferSize(),
pixmap->GetBufferFormat());
gpu_memory_buffer_manager.ImportDmaBuf(
handle, exported_pixmap->pixmap->GetBufferSize(),
exported_pixmap->pixmap->GetBufferFormat());
ASSERT_TRUE(gpu_memory_buffer);
ASSERT_TRUE(gpu_memory_buffer->Map());
ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR,
......
......@@ -46,6 +46,7 @@
#include "third_party/libyuv/include/libyuv.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#include "ui/gfx/native_pixmap.h"
#include "ui/gfx/native_pixmap_handle.h"
......@@ -1036,6 +1037,10 @@ bool VASupportedImageFormats::InitSupportedImageFormats_Locked() {
} // namespace
NativePixmapAndSizeInfo::NativePixmapAndSizeInfo() = default;
NativePixmapAndSizeInfo::~NativePixmapAndSizeInfo() = default;
// static
const std::string& VaapiWrapper::GetVendorStringForTesting() {
return VADisplayState::Get()->va_vendor_string();
......@@ -1302,7 +1307,8 @@ bool VaapiWrapper::CreateContextAndSurfaces(
std::unique_ptr<ScopedVASurface> VaapiWrapper::CreateContextAndScopedVASurface(
unsigned int va_format,
const gfx::Size& size) {
const gfx::Size& size,
const base::Optional<gfx::Size>& visible_size) {
if (va_context_id_ != VA_INVALID_ID) {
LOG(ERROR) << "The current context should be destroyed before creating a "
"new one";
......@@ -1310,7 +1316,7 @@ std::unique_ptr<ScopedVASurface> VaapiWrapper::CreateContextAndScopedVASurface(
}
std::unique_ptr<ScopedVASurface> scoped_va_surface =
CreateScopedVASurface(va_format, size);
CreateScopedVASurface(va_format, size, visible_size);
if (!scoped_va_surface)
return nullptr;
......@@ -1405,15 +1411,22 @@ scoped_refptr<VASurface> VaapiWrapper::CreateVASurfaceForPixmap(
base::BindOnce(&VaapiWrapper::DestroySurface, this));
}
scoped_refptr<gfx::NativePixmapDmaBuf>
VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBuf(VASurfaceID va_surface_id) {
std::unique_ptr<NativePixmapAndSizeInfo>
VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBuf(
const ScopedVASurface& scoped_va_surface) {
if (!scoped_va_surface.IsValid()) {
LOG(ERROR) << "Cannot export an invalid surface";
return nullptr;
}
VADRMPRIMESurfaceDescriptor descriptor;
{
base::AutoLock auto_lock(*va_lock_);
VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
VAStatus va_res = vaSyncSurface(va_display_, scoped_va_surface.id());
VA_SUCCESS_OR_RETURN(va_res, "Cannot sync VASurface", nullptr);
va_res = vaExportSurfaceHandle(
va_display_, va_surface_id, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
va_display_, scoped_va_surface.id(),
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
VA_EXPORT_SURFACE_READ_ONLY | VA_EXPORT_SURFACE_SEPARATE_LAYERS,
&descriptor);
VA_SUCCESS_OR_RETURN(va_res, "Failed to export VASurface", nullptr);
......@@ -1426,6 +1439,7 @@ VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBuf(VASurfaceID va_surface_id) {
// work in AMD.
if (descriptor.num_objects != 1u) {
DVLOG(1) << "Only surface descriptors with one bo are supported";
NOTREACHED();
return nullptr;
}
base::ScopedFD bo_fd(descriptor.objects[0].fd);
......@@ -1480,10 +1494,23 @@ VaapiWrapper::ExportVASurfaceAsNativePixmapDmaBuf(VASurfaceID va_surface_id) {
std::swap(handle.planes[1], handle.planes[2]);
}
return base::MakeRefCounted<gfx::NativePixmapDmaBuf>(
auto exported_pixmap = std::make_unique<NativePixmapAndSizeInfo>();
exported_pixmap->va_surface_resolution =
gfx::Size(base::checked_cast<int>(descriptor.width),
base::checked_cast<int>(descriptor.height)),
buffer_format, std::move(handle));
base::checked_cast<int>(descriptor.height));
exported_pixmap->byte_size =
base::strict_cast<size_t>(descriptor.objects[0].size);
if (!gfx::Rect(exported_pixmap->va_surface_resolution)
.Contains(gfx::Rect(scoped_va_surface.size()))) {
LOG(ERROR) << "A " << scoped_va_surface.size().ToString()
<< " ScopedVASurface cannot be contained by a "
<< exported_pixmap->va_surface_resolution.ToString()
<< " buffer";
return nullptr;
}
exported_pixmap->pixmap = base::MakeRefCounted<gfx::NativePixmapDmaBuf>(
scoped_va_surface.size(), buffer_format, std::move(handle));
return exported_pixmap;
}
bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
......@@ -2002,7 +2029,8 @@ bool VaapiWrapper::CreateSurfaces(unsigned int va_format,
std::unique_ptr<ScopedVASurface> VaapiWrapper::CreateScopedVASurface(
unsigned int va_rt_format,
const gfx::Size& size) {
const gfx::Size& size,
const base::Optional<gfx::Size>& visible_size) {
if (kInvalidVaRtFormat == va_rt_format) {
LOG(ERROR) << "Invalid VA RT format to CreateScopedVASurface";
return nullptr;
......@@ -2024,8 +2052,10 @@ std::unique_ptr<ScopedVASurface> VaapiWrapper::CreateScopedVASurface(
DCHECK_NE(VA_INVALID_ID, va_surface_id)
<< "Invalid VA surface id after vaCreateSurfaces";
DCHECK(!visible_size.has_value() || !visible_size->IsEmpty());
auto scoped_va_surface = std::make_unique<ScopedVASurface>(
this, va_surface_id, size, va_rt_format);
this, va_surface_id, visible_size.has_value() ? *visible_size : size,
va_rt_format);
DCHECK(scoped_va_surface);
DCHECK(scoped_va_surface->IsValid());
......
......@@ -14,6 +14,7 @@
#include <stdint.h>
#include <va/va.h>
#include <memory>
#include <set>
#include <string>
#include <vector>
......@@ -23,6 +24,7 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "media/gpu/media_gpu_export.h"
......@@ -48,6 +50,30 @@ class ScopedVAImage;
class ScopedVASurface;
class VideoFrame;
// This struct holds a NativePixmapDmaBuf, usually the result of exporting a VA
// surface, and some associated size information needed to tell clients about
// the underlying buffer.
struct NativePixmapAndSizeInfo {
NativePixmapAndSizeInfo();
~NativePixmapAndSizeInfo();
// The VA-API internal buffer dimensions, which may be different than the
// dimensions requested at the time of creation of the surface (but always
// larger than or equal to those). This can be used for validation in, e.g.,
// testing.
gfx::Size va_surface_resolution;
// The size of the underlying Buffer Object. A use case for this is when an
// image decode is requested and the caller needs to know the size of the
// allocated buffer for caching purposes.
size_t byte_size = 0u;
// Contains the information needed to use the surface in a graphics API,
// including the visible size (|pixmap|->GetBufferSize()) which should be no
// larger than |va_surface_resolution|.
scoped_refptr<gfx::NativePixmapDmaBuf> pixmap;
};
// This class handles VA-API calls and ensures proper locking of VA-API calls
// to libva, the userspace shim to the HW codec driver. libva is not
// thread-safe, so we have to perform locking ourselves. This class is fully
......@@ -159,13 +185,15 @@ class MEDIA_GPU_EXPORT VaapiWrapper
size_t num_surfaces,
std::vector<VASurfaceID>* va_surfaces);
// Creates a single VASurfaceID of |va_format| and |size| and, if successful,
// creates a |va_context_id_| of the same size. Returns a ScopedVASurface
// containing the created VASurfaceID, the |va_format|, and |size|, or nullptr
// if creation failed.
// Creates a single ScopedVASurface of |va_format| and |size| and, if
// successful, creates a |va_context_id_| of the same size. Returns nullptr if
// creation failed. If |visible_size| is supplied, the returned
// ScopedVASurface's size is set to it. Otherwise, it's set to |size| (refer
// to CreateScopedVASurface() for details).
std::unique_ptr<ScopedVASurface> CreateContextAndScopedVASurface(
unsigned int va_format,
const gfx::Size& size);
const gfx::Size& size,
const base::Optional<gfx::Size>& visible_size = base::nullopt);
// Releases the |va_surfaces| and destroys |va_context_id_|.
virtual void DestroyContextAndSurfaces(std::vector<VASurfaceID> va_surfaces);
......@@ -178,20 +206,27 @@ class MEDIA_GPU_EXPORT VaapiWrapper
// Destroys the context identified by |va_context_id_|.
void DestroyContext();
// Tries to allocate a VA surface of size |size| and |va_rt_format|.
// Returns a self-cleaning ScopedVASurface or nullptr if creation failed.
// Requests a VA surface of size |size| and |va_rt_format|. Returns a
// self-cleaning ScopedVASurface or nullptr if creation failed. If
// |visible_size| is supplied, the returned ScopedVASurface's size is set to
// it: for example, we may want to request a 16x16 surface to decode a 13x12
// JPEG: we may want to keep track of the visible size 13x12 inside the
// ScopedVASurface to inform the surface's users that that's the only region
// with meaningful content. If |visible_size| is not supplied, we store |size|
// in the returned ScopedVASurface.
std::unique_ptr<ScopedVASurface> CreateScopedVASurface(
unsigned int va_rt_format,
const gfx::Size& size);
const gfx::Size& size,
const base::Optional<gfx::Size>& visible_size = base::nullopt);
// Creates a self-releasing VASurface from |pixmap|. The ownership of the
// surface is transferred to the caller.
scoped_refptr<VASurface> CreateVASurfaceForPixmap(
const scoped_refptr<gfx::NativePixmap>& pixmap);
// Syncs and exports the VA surface identified by |va_surface_id| as a
// gfx::NativePixmapDmaBuf. Currently, the only VAAPI surface pixel formats
// supported are VA_FOURCC_IMC3 and VA_FOURCC_NV12.
// Syncs and exports |va_surface| as a gfx::NativePixmapDmaBuf. Currently, the
// only VAAPI surface pixel formats supported are VA_FOURCC_IMC3 and
// VA_FOURCC_NV12.
//
// Notes:
//
......@@ -205,8 +240,8 @@ class MEDIA_GPU_EXPORT VaapiWrapper
// gfx::BufferFormat::YUV_420_BIPLANAR.
//
// Returns nullptr on failure.
scoped_refptr<gfx::NativePixmapDmaBuf> ExportVASurfaceAsNativePixmapDmaBuf(
VASurfaceID va_surface_id);
std::unique_ptr<NativePixmapAndSizeInfo> ExportVASurfaceAsNativePixmapDmaBuf(
const ScopedVASurface& va_surface);
// Submit parameters or slice data of |va_buffer_type|, copying them from
// |buffer| of size |size|, into HW codec. The data in |buffer| is no
......
......@@ -881,6 +881,33 @@ convert pixel-1280x720.jpg -sampling-factor 4:2:0 -define jpeg:optimize-coding=f
Then, using a hex editor, the Huffman table sections were removed from the
resulting file.
#### pixel-40x23-yuv420.jpg
A version of pixel-1280x720-yuv420.jpg resized to 40x23 (so that the height is
odd) using:
```
convert pixel-1280x720-yuv420.jpg -resize 40x23\! -define jpeg:optimize-coding=false pixel-40x23-yuv420.jpg
```
Then, using a hex editor, the Huffman table sections were removed from the
resulting file.
#### pixel-41x22-yuv420.jpg
A version of pixel-1280x720-yuv420.jpg resized to 41x22 (so that the width is
odd) using:
```
convert pixel-1280x720-yuv420.jpg -resize 41x22\! -define jpeg:optimize-coding=false pixel-41x22-yuv420.jpg
```
Then, using a hex editor, the Huffman table sections were removed from the
resulting file.
#### pixel-41x23-yuv420.jpg
A version of pixel-1280x720-yuv420.jpg resized to 41x23 (so that both dimensions
are odd) using:
```
convert pixel-1280x720-yuv420.jpg -resize 41x23\! -define jpeg:optimize-coding=false pixel-41x23-yuv420.jpg
```
Then, using a hex editor, the Huffman table sections were removed from the
resulting file.
#### pixel-1280x720-yuv444.jpg
A version of pixel-1280x720.jpg converted to 4:4:4 subsampling using:
```
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment