Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / common / gpu / media / jpeg_decode_accelerator_unittest.cc
blob1752cb13fc67a602b4a249c9bc647b09be0f8369
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This has to be included first.
6 // See http://code.google.com/p/googletest/issues/detail?id=371
7 #include "testing/gtest/include/gtest/gtest.h"
9 #include "base/at_exit.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_split.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
20 #include "media/base/test_data_util.h"
21 #include "media/filters/jpeg_parser.h"
22 #include "media/video/jpeg_decode_accelerator.h"
23 #include "third_party/libyuv/include/libyuv.h"
24 #include "ui/gfx/codec/jpeg_codec.h"
26 #if defined(OS_CHROMEOS)
27 #if defined(USE_V4L2_CODEC)
28 #include "content/common/gpu/media/v4l2_device.h"
29 #include "content/common/gpu/media/v4l2_jpeg_decode_accelerator.h"
30 #endif
31 #if defined(ARCH_CPU_X86_FAMILY)
32 #include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
33 #include "content/common/gpu/media/vaapi_wrapper.h"
34 #endif
35 #endif
37 using media::JpegDecodeAccelerator;
39 namespace content {
40 namespace {
42 // Default test image file.
43 const base::FilePath::CharType* kDefaultJpegFilename =
44 FILE_PATH_LITERAL("peach_pi-1280x720.jpg");
45 // Decide to save decode results to files or not. Output files will be saved
46 // in the same directory with unittest. File name is like input file but
47 // changing the extension to "yuv".
48 bool g_save_to_file = false;
49 // Threshold for mean absolute difference of hardware and software decode.
50 // Absolute difference is to calculate the difference between each pixel in two
51 // images. This is used for measuring of the similarity of two images.
52 const double kDecodeSimilarityThreshold = 1.0;
54 // Environment to create test data for all test cases.
55 class JpegDecodeAcceleratorTestEnvironment;
56 JpegDecodeAcceleratorTestEnvironment* g_env;
58 struct TestImageFile {
59 explicit TestImageFile(const base::FilePath::StringType& filename)
60 : filename(filename) {}
62 base::FilePath::StringType filename;
64 // The input content of |filename|.
65 std::string data_str;
67 media::JpegParseResult parse_result;
68 gfx::Size coded_size;
69 size_t output_size;
72 enum ClientState {
73 CS_CREATED,
74 CS_INITIALIZED,
75 CS_DECODE_PASS,
76 CS_ERROR,
79 class JpegClient : public JpegDecodeAccelerator::Client {
80 public:
81 JpegClient(const std::vector<TestImageFile*>& test_image_files,
82 ClientStateNotification<ClientState>* note);
83 ~JpegClient() override;
84 void CreateJpegDecoder();
85 void DestroyJpegDecoder();
86 void StartDecode(int32_t bitstream_buffer_id);
88 // JpegDecodeAccelerator::Client implementation.
89 void VideoFrameReady(int32_t bitstream_buffer_id) override;
90 void NotifyError(int32_t bitstream_buffer_id,
91 JpegDecodeAccelerator::Error error) override;
93 private:
94 void PrepareMemory(int32_t bitstream_buffer_id);
95 void SetState(ClientState new_state);
96 void SaveToFile(int32_t bitstream_buffer_id);
97 bool GetSoftwareDecodeResult(int32_t bitstream_buffer_id);
99 // Calculate mean absolute difference of hardware and software decode results
100 // to check the similarity.
101 double GetMeanAbsoluteDifference(int32_t bitstream_buffer_id);
103 // JpegClient doesn't own |test_image_files_|.
104 const std::vector<TestImageFile*>& test_image_files_;
106 scoped_ptr<JpegDecodeAccelerator> decoder_;
107 ClientState state_;
109 // Used to notify another thread about the state. JpegClient does not own
110 // this.
111 ClientStateNotification<ClientState>* note_;
113 // Mapped memory of input file.
114 scoped_ptr<base::SharedMemory> in_shm_;
115 // Mapped memory of output buffer from hardware decoder.
116 scoped_ptr<base::SharedMemory> hw_out_shm_;
117 // Mapped memory of output buffer from software decoder.
118 scoped_ptr<base::SharedMemory> sw_out_shm_;
120 DISALLOW_COPY_AND_ASSIGN(JpegClient);
123 JpegClient::JpegClient(const std::vector<TestImageFile*>& test_image_files,
124 ClientStateNotification<ClientState>* note)
125 : test_image_files_(test_image_files), state_(CS_CREATED), note_(note) {
128 JpegClient::~JpegClient() {
131 void JpegClient::CreateJpegDecoder() {
132 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
133 decoder_.reset(
134 new VaapiJpegDecodeAccelerator(base::ThreadTaskRunnerHandle::Get()));
135 #elif defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC)
136 scoped_refptr<V4L2Device> device =
137 V4L2Device::Create(V4L2Device::kJpegDecoder);
138 if (!device.get()) {
139 LOG(ERROR) << "V4L2Device::Create failed";
140 SetState(CS_ERROR);
141 return;
143 decoder_.reset(new V4L2JpegDecodeAccelerator(
144 device, base::ThreadTaskRunnerHandle::Get()));
145 #else
146 #error The JpegDecodeAccelerator is not supported on this platform.
147 #endif
148 if (!decoder_->Initialize(this)) {
149 LOG(ERROR) << "JpegDecodeAccelerator::Initialize() failed";
150 SetState(CS_ERROR);
151 return;
153 SetState(CS_INITIALIZED);
156 void JpegClient::DestroyJpegDecoder() {
157 decoder_.reset();
160 void JpegClient::VideoFrameReady(int32_t bitstream_buffer_id) {
161 if (g_save_to_file) {
162 SaveToFile(bitstream_buffer_id);
165 if (!GetSoftwareDecodeResult(bitstream_buffer_id)) {
166 SetState(CS_ERROR);
167 return;
169 double difference = GetMeanAbsoluteDifference(bitstream_buffer_id);
170 if (difference <= kDecodeSimilarityThreshold) {
171 SetState(CS_DECODE_PASS);
172 } else {
173 LOG(ERROR) << "The mean absolute difference between software and hardware "
174 "decode is " << difference;
175 SetState(CS_ERROR);
179 void JpegClient::NotifyError(int32_t bitstream_buffer_id,
180 JpegDecodeAccelerator::Error error) {
181 LOG(ERROR) << "Notifying of error " << error << " for buffer id "
182 << bitstream_buffer_id;
183 SetState(CS_ERROR);
186 void JpegClient::PrepareMemory(int32_t bitstream_buffer_id) {
187 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
189 size_t input_size = image_file->data_str.size();
190 if (!in_shm_.get() || input_size > in_shm_->mapped_size()) {
191 in_shm_.reset(new base::SharedMemory);
192 CHECK(in_shm_->CreateAndMapAnonymous(input_size));
194 memcpy(in_shm_->memory(), image_file->data_str.data(), input_size);
196 if (!hw_out_shm_.get() ||
197 image_file->output_size > hw_out_shm_->mapped_size()) {
198 hw_out_shm_.reset(new base::SharedMemory);
199 CHECK(hw_out_shm_->CreateAndMapAnonymous(image_file->output_size));
201 memset(hw_out_shm_->memory(), 0, image_file->output_size);
203 if (!sw_out_shm_.get() ||
204 image_file->output_size > sw_out_shm_->mapped_size()) {
205 sw_out_shm_.reset(new base::SharedMemory);
206 CHECK(sw_out_shm_->CreateAndMapAnonymous(image_file->output_size));
208 memset(sw_out_shm_->memory(), 0, image_file->output_size);
211 void JpegClient::SetState(ClientState new_state) {
212 DVLOG(2) << "Changing state " << state_ << "->" << new_state;
213 note_->Notify(new_state);
214 state_ = new_state;
217 void JpegClient::SaveToFile(int32_t bitstream_buffer_id) {
218 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
220 base::FilePath in_filename(image_file->filename);
221 base::FilePath out_filename = in_filename.ReplaceExtension(".yuv");
222 int size = base::checked_cast<int>(image_file->output_size);
223 ASSERT_EQ(size,
224 base::WriteFile(out_filename,
225 static_cast<char*>(hw_out_shm_->memory()), size));
228 double JpegClient::GetMeanAbsoluteDifference(int32_t bitstream_buffer_id) {
229 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
231 double total_difference = 0;
232 uint8_t* hw_ptr = static_cast<uint8_t*>(hw_out_shm_->memory());
233 uint8_t* sw_ptr = static_cast<uint8_t*>(sw_out_shm_->memory());
234 for (size_t i = 0; i < image_file->output_size; i++)
235 total_difference += std::abs(hw_ptr[i] - sw_ptr[i]);
236 return total_difference / image_file->output_size;
239 void JpegClient::StartDecode(int32_t bitstream_buffer_id) {
240 DCHECK_LT(static_cast<size_t>(bitstream_buffer_id), test_image_files_.size());
241 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
243 PrepareMemory(bitstream_buffer_id);
245 size_t output_size = media::VideoFrame::AllocationSize(
246 media::PIXEL_FORMAT_I420, image_file->coded_size);
248 base::SharedMemoryHandle dup_handle;
249 dup_handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
250 media::BitstreamBuffer bitstream_buffer(bitstream_buffer_id, dup_handle,
251 image_file->data_str.size());
252 scoped_refptr<media::VideoFrame> out_frame_ =
253 media::VideoFrame::WrapExternalSharedMemory(
254 media::PIXEL_FORMAT_I420,
255 image_file->coded_size,
256 gfx::Rect(image_file->coded_size),
257 image_file->coded_size,
258 static_cast<uint8_t*>(hw_out_shm_->memory()),
259 output_size,
260 hw_out_shm_->handle(),
262 base::TimeDelta());
263 CHECK(out_frame_.get());
264 decoder_->Decode(bitstream_buffer, out_frame_);
267 bool JpegClient::GetSoftwareDecodeResult(int32_t bitstream_buffer_id) {
268 media::VideoPixelFormat format = media::PIXEL_FORMAT_I420;
269 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
271 uint8_t* yplane = static_cast<uint8_t*>(sw_out_shm_->memory());
272 uint8_t* uplane =
273 yplane +
274 media::VideoFrame::PlaneSize(format, media::VideoFrame::kYPlane,
275 image_file->coded_size).GetArea();
276 uint8_t* vplane =
277 uplane +
278 media::VideoFrame::PlaneSize(format, media::VideoFrame::kUPlane,
279 image_file->coded_size).GetArea();
280 int yplane_stride = image_file->coded_size.width();
281 int uv_plane_stride = yplane_stride / 2;
283 if (libyuv::ConvertToI420(
284 static_cast<uint8_t*>(in_shm_->memory()),
285 image_file->data_str.size(),
286 yplane,
287 yplane_stride,
288 uplane,
289 uv_plane_stride,
290 vplane,
291 uv_plane_stride,
294 image_file->coded_size.width(),
295 image_file->coded_size.height(),
296 image_file->coded_size.width(),
297 image_file->coded_size.height(),
298 libyuv::kRotate0,
299 libyuv::FOURCC_MJPG) != 0) {
300 LOG(ERROR) << "Software decode " << image_file->filename << " failed.";
301 return false;
303 return true;
306 class JpegDecodeAcceleratorTestEnvironment : public ::testing::Environment {
307 public:
308 JpegDecodeAcceleratorTestEnvironment(
309 const base::FilePath::CharType* jpeg_filenames) {
310 user_jpeg_filenames_ =
311 jpeg_filenames ? jpeg_filenames: kDefaultJpegFilename;
313 void SetUp() override;
314 void TearDown() override;
316 // Create all black test image with |width| and |height| size.
317 bool CreateTestJpegImage(int width, int height, base::FilePath* filename);
319 // Read image from |filename| to |image_data|.
320 void ReadTestJpegImage(base::FilePath& filename, TestImageFile* image_data);
322 // Parsed data of |test_1280x720_jpeg_file_|.
323 scoped_ptr<TestImageFile> image_data_1280x720_black_;
324 // Parsed data of |test_640x368_jpeg_file_|.
325 scoped_ptr<TestImageFile> image_data_640x368_black_;
326 // Parsed data of "peach_pi-1280x720.jpg".
327 scoped_ptr<TestImageFile> image_data_1280x720_default_;
328 // Parsed data of failure image.
329 scoped_ptr<TestImageFile> image_data_invalid_;
330 // Parsed data from command line.
331 ScopedVector<TestImageFile> image_data_user_;
333 private:
334 const base::FilePath::CharType* user_jpeg_filenames_;
336 // Used for InputSizeChange test case. The image size should be smaller than
337 // |kDefaultJpegFilename|.
338 base::FilePath test_1280x720_jpeg_file_;
339 // Used for ResolutionChange test case.
340 base::FilePath test_640x368_jpeg_file_;
343 void JpegDecodeAcceleratorTestEnvironment::SetUp() {
344 ASSERT_TRUE(CreateTestJpegImage(1280, 720, &test_1280x720_jpeg_file_));
345 ASSERT_TRUE(CreateTestJpegImage(640, 368, &test_640x368_jpeg_file_));
347 image_data_1280x720_black_.reset(
348 new TestImageFile(test_1280x720_jpeg_file_.value()));
349 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(test_1280x720_jpeg_file_,
350 image_data_1280x720_black_.get()));
352 image_data_640x368_black_.reset(
353 new TestImageFile(test_640x368_jpeg_file_.value()));
354 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(test_640x368_jpeg_file_,
355 image_data_640x368_black_.get()));
357 base::FilePath default_jpeg_file =
358 media::GetTestDataFilePath(kDefaultJpegFilename);
359 image_data_1280x720_default_.reset(new TestImageFile(kDefaultJpegFilename));
360 ASSERT_NO_FATAL_FAILURE(
361 ReadTestJpegImage(default_jpeg_file, image_data_1280x720_default_.get()));
363 image_data_invalid_.reset(new TestImageFile("failure.jpg"));
364 image_data_invalid_->data_str.resize(100, 0);
365 image_data_invalid_->coded_size.SetSize(1280, 720);
366 image_data_invalid_->output_size = media::VideoFrame::AllocationSize(
367 media::PIXEL_FORMAT_I420, image_data_invalid_->coded_size);
369 // |user_jpeg_filenames_| may include many files and use ';' as delimiter.
370 std::vector<base::FilePath::StringType> filenames;
371 base::SplitString(user_jpeg_filenames_, ';', &filenames);
372 for (const auto& filename : filenames) {
373 base::FilePath input_file = media::GetTestDataFilePath(filename);
374 TestImageFile* image_data = new TestImageFile(filename);
375 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(input_file, image_data));
376 image_data_user_.push_back(image_data);
380 void JpegDecodeAcceleratorTestEnvironment::TearDown() {
381 base::DeleteFile(test_1280x720_jpeg_file_, false);
382 base::DeleteFile(test_640x368_jpeg_file_, false);
385 bool JpegDecodeAcceleratorTestEnvironment::CreateTestJpegImage(
386 int width,
387 int height,
388 base::FilePath* filename) {
389 gfx::Size coded_size(width, height);
390 const int kBytesPerPixel = 3;
391 const int kJpegQuality = 100;
392 std::vector<unsigned char> input_buffer(width * height * kBytesPerPixel);
393 std::vector<unsigned char> encoded;
394 if (!gfx::JPEGCodec::Encode(&input_buffer[0], gfx::JPEGCodec::FORMAT_RGB,
395 width, height, width * kBytesPerPixel,
396 kJpegQuality, &encoded)) {
397 return false;
400 CHECK(base::CreateTemporaryFile(filename));
401 EXPECT_TRUE(base::AppendToFile(
402 *filename, reinterpret_cast<char*>(&encoded[0]), encoded.size()));
403 return true;
406 void JpegDecodeAcceleratorTestEnvironment::ReadTestJpegImage(
407 base::FilePath& input_file, TestImageFile* image_data) {
408 ASSERT_TRUE(base::ReadFileToString(input_file, &image_data->data_str));
410 ASSERT_TRUE(media::ParseJpegPicture(
411 reinterpret_cast<const uint8_t*>(image_data->data_str.data()),
412 image_data->data_str.size(),
413 &image_data->parse_result));
414 image_data->coded_size.SetSize(
415 image_data->parse_result.frame_header.coded_width,
416 image_data->parse_result.frame_header.coded_height);
417 image_data->output_size = media::VideoFrame::AllocationSize(
418 media::PIXEL_FORMAT_I420, image_data->coded_size);
421 class JpegDecodeAcceleratorTest : public ::testing::Test {
422 protected:
423 JpegDecodeAcceleratorTest() {}
425 void TestDecode(size_t num_concurrent_decoders);
427 // The elements of |test_image_files_| are owned by
428 // JpegDecodeAcceleratorTestEnvironment.
429 std::vector<TestImageFile*> test_image_files_;
430 std::vector<ClientState> expected_status_;
432 protected:
433 DISALLOW_COPY_AND_ASSIGN(JpegDecodeAcceleratorTest);
436 void JpegDecodeAcceleratorTest::TestDecode(size_t num_concurrent_decoders) {
437 CHECK(test_image_files_.size() == expected_status_.size());
438 base::Thread decoder_thread("DecoderThread");
439 ASSERT_TRUE(decoder_thread.Start());
441 ScopedVector<ClientStateNotification<ClientState>> notes;
442 ScopedVector<JpegClient> clients;
444 for (size_t i = 0; i < num_concurrent_decoders; i++) {
445 notes.push_back(new ClientStateNotification<ClientState>());
446 clients.push_back(new JpegClient(test_image_files_, notes.back()));
447 decoder_thread.task_runner()->PostTask(
448 FROM_HERE, base::Bind(&JpegClient::CreateJpegDecoder,
449 base::Unretained(clients.back())));
450 ASSERT_EQ(notes[i]->Wait(), CS_INITIALIZED);
453 for (size_t index = 0; index < test_image_files_.size(); index++) {
454 for (size_t i = 0; i < num_concurrent_decoders; i++) {
455 decoder_thread.task_runner()->PostTask(
456 FROM_HERE, base::Bind(&JpegClient::StartDecode,
457 base::Unretained(clients[i]),
458 index));
460 for (size_t i = 0; i < num_concurrent_decoders; i++) {
461 ASSERT_EQ(notes[i]->Wait(), expected_status_[index]);
465 for (size_t i = 0; i < num_concurrent_decoders; i++) {
466 decoder_thread.task_runner()->PostTask(
467 FROM_HERE, base::Bind(&JpegClient::DestroyJpegDecoder,
468 base::Unretained(clients[i])));
470 decoder_thread.Stop();
473 TEST_F(JpegDecodeAcceleratorTest, SimpleDecode) {
474 for (const auto& image : g_env->image_data_user_) {
475 test_image_files_.push_back(image);
476 expected_status_.push_back(CS_DECODE_PASS);
478 TestDecode(1);
481 TEST_F(JpegDecodeAcceleratorTest, MultipleDecoders) {
482 for (const auto& image : g_env->image_data_user_) {
483 test_image_files_.push_back(image);
484 expected_status_.push_back(CS_DECODE_PASS);
486 TestDecode(3);
489 TEST_F(JpegDecodeAcceleratorTest, InputSizeChange) {
490 // The size of |image_data_1280x720_black_| is smaller than
491 // |image_data_1280x720_default_|.
492 test_image_files_.push_back(g_env->image_data_1280x720_black_.get());
493 test_image_files_.push_back(g_env->image_data_1280x720_default_.get());
494 test_image_files_.push_back(g_env->image_data_1280x720_black_.get());
495 for (size_t i = 0; i < test_image_files_.size(); i++)
496 expected_status_.push_back(CS_DECODE_PASS);
497 TestDecode(1);
500 TEST_F(JpegDecodeAcceleratorTest, ResolutionChange) {
501 test_image_files_.push_back(g_env->image_data_640x368_black_.get());
502 test_image_files_.push_back(g_env->image_data_1280x720_default_.get());
503 test_image_files_.push_back(g_env->image_data_640x368_black_.get());
504 for (size_t i = 0; i < test_image_files_.size(); i++)
505 expected_status_.push_back(CS_DECODE_PASS);
506 TestDecode(1);
509 TEST_F(JpegDecodeAcceleratorTest, FailureJpeg) {
510 test_image_files_.push_back(g_env->image_data_invalid_.get());
511 expected_status_.push_back(CS_ERROR);
512 TestDecode(1);
515 TEST_F(JpegDecodeAcceleratorTest, KeepDecodeAfterFailure) {
516 test_image_files_.push_back(g_env->image_data_invalid_.get());
517 test_image_files_.push_back(g_env->image_data_1280x720_default_.get());
518 expected_status_.push_back(CS_ERROR);
519 expected_status_.push_back(CS_DECODE_PASS);
520 TestDecode(1);
523 } // namespace
524 } // namespace content
526 int main(int argc, char** argv) {
527 testing::InitGoogleTest(&argc, argv);
528 base::CommandLine::Init(argc, argv);
529 base::ShadowingAtExitManager at_exit_manager;
531 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
532 DCHECK(cmd_line);
534 const base::FilePath::CharType* jpeg_filenames = nullptr;
535 base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
536 for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
537 it != switches.end(); ++it) {
538 // jpeg_filenames can include one or many files and use ';' as delimiter.
539 if (it->first == "jpeg_filenames") {
540 jpeg_filenames = it->second.c_str();
541 continue;
543 if (it->first == "save_to_file") {
544 content::g_save_to_file = true;
545 continue;
547 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
549 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
550 content::VaapiWrapper::PreSandboxInitialization();
551 #endif
553 content::g_env =
554 reinterpret_cast<content::JpegDecodeAcceleratorTestEnvironment*>(
555 testing::AddGlobalTestEnvironment(
556 new content::JpegDecodeAcceleratorTestEnvironment(
557 jpeg_filenames)));
559 return RUN_ALL_TESTS();