Commit b17ead43 authored by kcwu's avatar kcwu Committed by Commit bot

Add JPEG decoder for VAAPI JPEG decode acceleration

BUG=335778
TEST=build and run vaapi_jpeg_decoder_unittest

Review URL: https://codereview.chromium.org/825843002

Cr-Commit-Position: refs/heads/master@{#313881}
parent f2343a58
......@@ -9,6 +9,7 @@ VAStatus vaBeginPicture(VADisplay dpy, VAContextID context, VASurfaceID render_t
VAStatus vaCreateBuffer(VADisplay dpy, VAContextID context, VABufferType type, unsigned int size, unsigned int num_elements, void *data, VABufferID *buf_id);
VAStatus vaCreateConfig(VADisplay dpy, VAProfile profile, VAEntrypoint entrypoint, VAConfigAttrib *attrib_list, int num_attribs, VAConfigID *config_id);
VAStatus vaCreateContext(VADisplay dpy, VAConfigID config_id, int picture_width, int picture_height, int flag, VASurfaceID *render_targets, int num_render_targets, VAContextID *context);
VAStatus vaCreateImage(VADisplay dpy, VAImageFormat *format, int width, int height, VAImage *image);
VAStatus vaCreateSurfaces(VADisplay dpy, unsigned int format, unsigned int width, unsigned int height, VASurfaceID *surfaces, unsigned int num_surfaces, VASurfaceAttrib *attrib_list, unsigned int num_attribs);
VAStatus vaDeriveImage(VADisplay dpy, VASurfaceID surface, VAImage *image);
VAStatus vaDestroyBuffer(VADisplay dpy, VABufferID buffer_id);
......@@ -20,6 +21,7 @@ int vaDisplayIsValid(VADisplay dpy);
VAStatus vaEndPicture(VADisplay dpy, VAContextID context);
const char *vaErrorStr(VAStatus error_status);
VAStatus vaGetConfigAttributes(VADisplay dpy, VAProfile profile, VAEntrypoint entrypoint, VAConfigAttrib *attrib_list, int num_attribs);
VAStatus vaGetImage(VADisplay dpy, VASurfaceID surface, int x, int y, unsigned int width, unsigned int height, VAImageID image);
VAStatus vaInitialize(VADisplay dpy, int *major_version, int *minor_version);
VAStatus vaMapBuffer(VADisplay dpy, VABufferID buf_id, void **pbuf);
int vaMaxNumEntrypoints (VADisplay dpy);
......
......@@ -111,8 +111,8 @@ bool VaapiH264DecoderLoop::Initialize(base::FilePath input_file,
media::VideoCodecProfile profile = media::H264PROFILE_BASELINE;
base::Closure report_error_cb =
base::Bind(&LogOnError, VaapiH264Decoder::VAAPI_ERROR);
wrapper_ =
VaapiWrapper::Create(VaapiWrapper::kDecode, profile, report_error_cb);
wrapper_ = VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kDecode, profile,
report_error_cb);
if (!wrapper_.get()) {
LOG(ERROR) << "Can't create vaapi wrapper";
return false;
......@@ -247,14 +247,14 @@ void VaapiH264DecoderLoop::OutputPicture(
VAImage image;
void* mem;
if (!wrapper_->GetVaImageForTesting(va_surface->id(), &image, &mem)) {
if (!wrapper_->GetDerivedVaImage(va_surface->id(), &image, &mem)) {
LOG(ERROR) << "Cannot get VAImage.";
return;
}
if (image.format.fourcc != VA_FOURCC_NV12) {
LOG(ERROR) << "Unexpected image format: " << image.format.fourcc;
wrapper_->ReturnVaImageForTesting(&image);
wrapper_->ReturnVaImage(&image);
return;
}
......@@ -269,7 +269,7 @@ void VaapiH264DecoderLoop::OutputPicture(
LOG(ERROR) << "Cannot convert image to I420.";
}
wrapper_->ReturnVaImageForTesting(&image);
wrapper_->ReturnVaImage(&image);
}
void VaapiH264DecoderLoop::RecycleSurface(VASurfaceID va_surface_id) {
......
This diff is collapsed.
// Copyright 2015 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.
#ifndef CONTENT_COMMON_GPU_MEDIA_VAAPI_JPEG_DECODER_H_
#define CONTENT_COMMON_GPU_MEDIA_VAAPI_JPEG_DECODER_H_
#include "base/macros.h"
#include "content/common/content_export.h"
#include "content/common/gpu/media/vaapi_wrapper.h"
namespace media {
struct JpegParseResult;
} // namespace media
namespace content {
// A JPEG decoder that utilizes VA-API hardware video decode acceleration on
// Intel systems. Provides functionality to allow plugging VAAPI HW
// acceleration into the JpegDecodeAccelerator framework.
//
// Clients of this class are expected to manage VA surfaces created via
// VaapiWrapper, parse JPEG picture via media::ParseJpegPicture, and then pass
// them to this class.
class CONTENT_EXPORT VaapiJpegDecoder {
public:
// Decode a JPEG picture. It will fill VA-API parameters and call
// corresponding VA-API methods according to parsed JPEG result
// |parse_result|. Decoded data will be outputted to the given |va_surface|.
// Return false on failure.
// |vaapi_wrapper| should be initialized in kDecode mode with
// VAProfileJPEGBaseline profile.
// |va_surface| should be created with size at least as large as the picture
// size.
static bool Decode(VaapiWrapper* vaapi_wrapper,
const media::JpegParseResult& parse_result,
VASurfaceID va_surface);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(VaapiJpegDecoder);
};
} // namespace content
#endif // CONTENT_COMMON_GPU_MEDIA_VAAPI_JPEG_DECODER_H_
// Copyright 2015 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 <string>
// This has to be included first.
// See http://code.google.com/p/googletest/issues/detail?id=371
#include "testing/gtest/include/gtest/gtest.h"
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/md5.h"
#include "base/path_service.h"
#include "base/strings/string_piece.h"
#include "content/common/gpu/media/vaapi_jpeg_decoder.h"
#include "media/base/test_data_util.h"
#include "media/base/video_frame.h"
#include "media/filters/jpeg_parser.h"
namespace content {
namespace {
const char* kTestFilename = "pixel-1280x720.jpg";
const char* kExpectedMd5Sum = "6e9e1716073c9a9a1282e3f0e0dab743";
void LogOnError() {
LOG(FATAL) << "Oh noes! Decoder failed";
}
class VaapiJpegDecoderTest : public ::testing::Test {
protected:
VaapiJpegDecoderTest() {}
void SetUp() override {
base::Closure report_error_cb = base::Bind(&LogOnError);
wrapper_ = VaapiWrapper::Create(VaapiWrapper::kDecode,
VAProfileJPEGBaseline, report_error_cb);
ASSERT_TRUE(wrapper_);
base::FilePath input_file = media::GetTestDataFilePath(kTestFilename);
ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data_))
<< "failed to read input data from " << input_file.value();
}
void TearDown() override { wrapper_.reset(); }
bool VerifyDecode(const media::JpegParseResult& parse_result,
const std::string& md5sum);
protected:
scoped_ptr<VaapiWrapper> wrapper_;
std::string jpeg_data_;
};
bool VaapiJpegDecoderTest::VerifyDecode(
const media::JpegParseResult& parse_result,
const std::string& expected_md5sum) {
gfx::Size size(parse_result.frame_header.visible_width,
parse_result.frame_header.visible_height);
std::vector<VASurfaceID> va_surfaces;
if (!wrapper_->CreateSurfaces(size, 1, &va_surfaces))
return false;
if (!VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])) {
LOG(ERROR) << "Decode failed";
return false;
}
VAImage image;
VAImageFormat format;
const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0');
memset(&image, 0, sizeof(image));
memset(&format, 0, sizeof(format));
format.fourcc = kI420Fourcc;
format.byte_order = VA_LSB_FIRST;
format.bits_per_pixel = 12; // 12 for I420
void* mem;
if (!wrapper_->GetVaImage(va_surfaces[0], &format, size, &image, &mem)) {
LOG(ERROR) << "Cannot get VAImage";
return false;
}
EXPECT_EQ(kI420Fourcc, image.format.fourcc);
base::StringPiece result(
reinterpret_cast<const char*>(mem),
media::VideoFrame::AllocationSize(media::VideoFrame::I420, size));
EXPECT_EQ(expected_md5sum, base::MD5String(result));
wrapper_->ReturnVaImage(&image);
return true;
}
TEST_F(VaapiJpegDecoderTest, DecodeSuccess) {
media::JpegParseResult parse_result;
ASSERT_TRUE(media::ParseJpegPicture(
reinterpret_cast<const uint8_t*>(jpeg_data_.data()), jpeg_data_.size(),
&parse_result));
EXPECT_TRUE(VerifyDecode(parse_result, kExpectedMd5Sum));
}
TEST_F(VaapiJpegDecoderTest, DecodeFail) {
media::JpegParseResult parse_result;
ASSERT_TRUE(media::ParseJpegPicture(
reinterpret_cast<const uint8_t*>(jpeg_data_.data()), jpeg_data_.size(),
&parse_result));
// Not supported by VAAPI.
parse_result.frame_header.num_components = 1;
parse_result.scan.num_components = 1;
gfx::Size size(parse_result.frame_header.visible_width,
parse_result.frame_header.visible_height);
std::vector<VASurfaceID> va_surfaces;
ASSERT_TRUE(wrapper_->CreateSurfaces(size, 1, &va_surfaces));
EXPECT_FALSE(
VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0]));
}
} // namespace
} // namespace content
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
base::AtExitManager exit_manager;
return RUN_ALL_TESTS();
}
......@@ -119,9 +119,8 @@ bool VaapiVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
}
#endif // USE_X11
vaapi_wrapper_ = VaapiWrapper::Create(
VaapiWrapper::kDecode,
profile,
vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec(
VaapiWrapper::kDecode, profile,
base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR));
if (!vaapi_wrapper_.get()) {
......
......@@ -214,8 +214,8 @@ bool VaapiVideoEncodeAccelerator::Initialize(
UpdateRates(initial_bitrate, kDefaultFramerate);
vaapi_wrapper_ = VaapiWrapper::Create(VaapiWrapper::kEncode,
output_profile,
vaapi_wrapper_ =
VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile,
base::Bind(&ReportToUMA, VAAPI_ERROR));
if (!vaapi_wrapper_.get()) {
LOG(ERROR) << "Failed initializing VAAPI";
......
......@@ -163,13 +163,35 @@ VaapiWrapper::~VaapiWrapper() {
}
scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
CodecMode mode,
VAProfile va_profile,
const base::Closure& report_error_to_uma_cb) {
scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
if (!vaapi_wrapper->VaInitialize(report_error_to_uma_cb))
return nullptr;
if (!vaapi_wrapper->Initialize(mode, va_profile))
return nullptr;
return vaapi_wrapper.Pass();
}
scoped_ptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec(
CodecMode mode,
media::VideoCodecProfile profile,
const base::Closure& report_error_to_uma_cb) {
scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
if (!vaapi_wrapper->Initialize(mode, profile, report_error_to_uma_cb))
vaapi_wrapper.reset();
if (!vaapi_wrapper->VaInitialize(report_error_to_uma_cb))
return nullptr;
std::vector<VAProfile> supported_va_profiles;
if (!vaapi_wrapper->GetSupportedVaProfiles(&supported_va_profiles))
return nullptr;
VAProfile va_profile = ProfileToVAProfile(profile, supported_va_profiles);
if (!vaapi_wrapper->Initialize(mode, va_profile))
return nullptr;
return vaapi_wrapper.Pass();
}
......@@ -338,15 +360,7 @@ bool VaapiWrapper::AreAttribsSupported(
return true;
}
bool VaapiWrapper::Initialize(CodecMode mode,
media::VideoCodecProfile profile,
const base::Closure& report_error_to_uma_cb) {
if (!VaInitialize(report_error_to_uma_cb))
return false;
std::vector<VAProfile> supported_va_profiles;
if (!GetSupportedVaProfiles(&supported_va_profiles))
return false;
VAProfile va_profile = ProfileToVAProfile(profile, supported_va_profiles);
bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) {
if (va_profile == VAProfileNone) {
DVLOG(1) << "Unsupported profile";
return false;
......@@ -665,9 +679,9 @@ bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
}
#endif // USE_X11
bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
VAImage* image,
void** mem) {
bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id,
VAImage* image,
void** mem) {
base::AutoLock auto_lock(va_lock_);
VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
......@@ -691,7 +705,40 @@ bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
return false;
}
void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) {
bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id,
VAImageFormat* format,
const gfx::Size& size,
VAImage* image,
void** mem) {
base::AutoLock auto_lock(va_lock_);
VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
va_res =
vaCreateImage(va_display_, format, size.width(), size.height(), image);
VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false);
va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(),
size.height(), image->image_id);
VA_LOG_ON_ERROR(va_res, "vaGetImage failed");
if (va_res == VA_STATUS_SUCCESS) {
// Map the VAImage into memory
va_res = vaMapBuffer(va_display_, image->buf, mem);
VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
}
if (va_res != VA_STATUS_SUCCESS) {
va_res = vaDestroyImage(va_display_, image->image_id);
VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
return false;
}
return true;
}
void VaapiWrapper::ReturnVaImage(VAImage* image) {
base::AutoLock auto_lock(va_lock_);
VAStatus va_res = vaUnmapBuffer(va_display_, image->buf);
......
......@@ -45,9 +45,19 @@ class CONTENT_EXPORT VaapiWrapper {
kEncode,
};
// Create VaapiWrapper for VAProfile.
// |report_error_to_uma_cb| will be called independently from reporting
// errors to clients via method return values.
static scoped_ptr<VaapiWrapper> Create(
CodecMode mode,
VAProfile profile,
const base::Closure& report_error_to_uma_cb);
// Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile
// |profile| to VAProfile.
// |report_error_to_uma_cb| will be called independently from reporting
// errors to clients via method return values.
static scoped_ptr<VaapiWrapper> CreateForVideoCodec(
CodecMode mode,
media::VideoCodecProfile profile,
const base::Closure& report_error_to_uma_cb);
......@@ -118,16 +128,29 @@ class CONTENT_EXPORT VaapiWrapper {
// Returns true if the VAAPI version is less than the specified version.
bool VAAPIVersionLessThan(int major, int minor);
// Get a VAImage from a VASurface and map it into memory. The VAImage should
// be released using the ReturnVaImage function. Returns true when successful.
// This is intended for testing only.
bool GetVaImageForTesting(VASurfaceID va_surface_id,
VAImage* image,
void** mem);
// Get a VAImage from a VASurface and map it into memory. The size and format
// are derived from the surface. Use GetVaImage() instead if |format| or
// |size| are different from surface internal representation. The VAImage
// should be released using the ReturnVaImage function. Returns true when
// successful.
bool GetDerivedVaImage(VASurfaceID va_surface_id, VAImage* image, void** mem);
// Get a VAImage from a VASurface |va_surface_id| and map it into memory with
// given |format| and |size|. The output is |image| and the mapped memory is
// |mem|. If |format| doesn't equal to the internal format, the underlying
// implementation will do format conversion if supported. |size| should be
// smaller than or equal to the surface. If |size| is smaller, the image will
// be cropped. The VAImage should be released using the ReturnVaImage
// function. Returns true when successful.
bool GetVaImage(VASurfaceID va_surface_id,
VAImageFormat* format,
const gfx::Size& size,
VAImage* image,
void** mem);
// Release the VAImage (and the associated memory mapping) obtained from
// GetVaImage(). This is intended for testing only.
void ReturnVaImageForTesting(VAImage* image);
// GetVaImage() or GetDerivedVaImage().
void ReturnVaImage(VAImage* image);
// Upload contents of |frame| into |va_surface_id| for encode.
bool UploadVideoFrameToSurface(const scoped_refptr<media::VideoFrame>& frame,
......@@ -162,9 +185,7 @@ class CONTENT_EXPORT VaapiWrapper {
private:
VaapiWrapper();
bool Initialize(CodecMode mode,
media::VideoCodecProfile profile,
const base::Closure& report_error__to_uma_cb);
bool Initialize(CodecMode mode, VAProfile va_profile);
void Deinitialize();
bool VaInitialize(const base::Closure& report_error_to_uma_cb);
bool GetSupportedVaProfiles(std::vector<VAProfile>* profiles);
......
......@@ -870,6 +870,8 @@
'common/gpu/media/vaapi_h264_decoder.h',
'common/gpu/media/vaapi_h264_dpb.cc',
'common/gpu/media/vaapi_h264_dpb.h',
'common/gpu/media/vaapi_jpeg_decoder.cc',
'common/gpu/media/vaapi_jpeg_decoder.h',
'common/gpu/media/vaapi_picture.cc',
'common/gpu/media/vaapi_picture.h',
'common/gpu/media/vaapi_video_decode_accelerator.cc',
......
......@@ -1657,6 +1657,34 @@
}],
],
},
{
'target_name': 'vaapi_jpeg_decoder_unittest',
'type': '<(gtest_target_type)',
'dependencies': [
'content.gyp:content_common',
'../base/base.gyp:base',
'../media/media.gyp:media',
'../media/media.gyp:media_test_support',
'../testing/gtest.gyp:gtest',
],
'sources': [
'common/gpu/media/vaapi_jpeg_decoder_unittest.cc',
],
'include_dirs': [
'<(DEPTH)/third_party/libva',
],
'conditions': [
['use_x11==1', {
'dependencies': [
'../build/linux/system.gyp:x11',
]
}, {
'dependencies': [
'../build/linux/system.gyp:libdrm',
]
}],
],
}
]
}],
['chromeos==1', {
......
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