Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(292)

Side by Side Diff: content/common/gpu/media/jpeg_decode_accelerator_unittest.cc

Issue 1125263005: MJPEG acceleration for V4L2 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address kcwu's comments Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
wuchengli 2015/06/11 11:08:54 Please make JDA unittest a separate CL. This file
henryhsu 2015/06/12 05:47:59 Done.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
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"
8
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
25 #if defined(OS_CHROMEOS)
26 #if defined(USE_V4L2_CODEC)
27 #include "content/common/gpu/media/v4l2_device.h"
28 #include "content/common/gpu/media/v4l2_jpeg_decode_accelerator.h"
29 #endif
30 #if defined(ARCH_CPU_X86_FAMILY)
31 #include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
32 #include "content/common/gpu/media/vaapi_wrapper.h"
33 #endif // defined(ARCH_CPU_X86_FAMILY)
34 #else
35 #error The JpegAccelerator tests are not supported on this platform.
36 #endif
37
38 using media::JpegDecodeAccelerator;
39
40 namespace content {
41 namespace {
42
43 const base::FilePath::CharType* g_test_image_data =
44 FILE_PATH_LITERAL("pi-1280x720.jpg");
45 bool g_save_to_file = false;
46 const size_t kDecodeThreshold = 5;
47
48 struct TestImageFile {
49 explicit TestImageFile(const base::FilePath::StringType& file_name)
50 : file_name(file_name) {
51 }
52
53 base::FilePath::StringType file_name;
54 gfx::Size coded_size;
55 media::JpegParseResult parse_result;
56 std::string data_str;
57 size_t output_size;
58 };
59
60 enum ClientState {
61 CS_CREATED,
62 CS_DECODER_SET,
63 CS_INITIALIZED,
64 CS_DECODING,
65 CS_DECODE_PASS,
66 CS_DECODE_FAIL,
67 CS_DESTROYED,
68 CS_ERROR,
69 };
70
71 class JpegClient : public JpegDecodeAccelerator::Client {
72 public:
73 JpegClient(const ScopedVector<TestImageFile>& test_image_files,
74 ClientStateNotification<ClientState>* note);
75 ~JpegClient() override;
76 void CreateJpegDecoder();
77 void DestroyJpegDecoder();
78 void StartDecode(int32_t bitstream_buffer_id);
79
80 // JpegDecodeAccelerator::Client implementation.
81 void VideoFrameReady(int32_t bitstream_buffer_id) override;
82 void NotifyError(int32_t bitstream_buffer_id,
83 JpegDecodeAccelerator::Error error) override;
84
85 private:
86 void PrepareMemory(int32_t bitstream_buffer_id);
87 void SetState(ClientState new_state);
88 void SaveToFile(int32_t bitstream_buffer_id);
89 bool GetSoftwareDecodeResult(int32_t bitstream_buffer_id);
90 size_t GetMaxGapFromHwSwResult(int32_t bitstream_buffer_id);
91
92 const ScopedVector<TestImageFile>& test_image_files_;
93
94 scoped_ptr<JpegDecodeAccelerator> decoder_;
95 ClientState state_;
96
97 // Used to notify another thread about the state. JpegClient does not own
98 // this.
99 ClientStateNotification<ClientState>* note_;
100
101 scoped_ptr<base::SharedMemory> in_shm_;
102 scoped_ptr<base::SharedMemory> hw_out_shm_;
103 scoped_ptr<base::SharedMemory> sw_out_shm_;
104 scoped_refptr<media::VideoFrame> out_frame_;
105 };
kcwu 2015/06/09 12:17:44 DISALLOW_COPY_AND_ASSIGN(JpegClient);
henryhsu 2015/06/12 05:48:00 Done.
106
107 JpegClient::JpegClient(const ScopedVector<TestImageFile>& test_image_files,
108 ClientStateNotification<ClientState>* note)
109 : test_image_files_(test_image_files),
110 state_(CS_CREATED),
111 note_(note) {
112
113 }
114
115 JpegClient::~JpegClient() {
116 }
117
118 void JpegClient::CreateJpegDecoder() {
119 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
120 decoder_.reset(
121 new VaapiJpegDecodeAccelerator(base::ThreadTaskRunnerHandle::Get()));
122 #elif defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC)
123 scoped_refptr<V4L2Device> device = V4L2Device::Create(
124 V4L2Device::kJpegDecoder);
125 if (!device.get()) {
126 LOG(ERROR) << "V4L2Device::Create failed";
127 SetState(CS_ERROR);
128 return;
129 }
130 decoder_.reset(new V4L2JpegDecodeAccelerator(
131 device,
132 base::ThreadTaskRunnerHandle::Get()));
133 #else
134 DVLOG(1) << "HW JPEG decode acceleration not available.";
135 SetState(CS_ERROR);
136 return;
137 #endif
138 SetState(CS_DECODER_SET);
139 if (!decoder_->Initialize(this)) {
140 LOG(ERROR) << "JpegDecodeAccelerator::Initialize() failed";
141 SetState(CS_ERROR);
142 return;
143 }
144 SetState(CS_INITIALIZED);
145 }
146
147 void JpegClient::DestroyJpegDecoder() {
148 if (decoder_.get())
149 decoder_.reset();
150 in_shm_.reset();
151 hw_out_shm_.reset();
152 SetState(CS_DESTROYED);
153 }
154
155 void JpegClient::VideoFrameReady(int32_t bitstream_buffer_id) {
156 if (g_save_to_file) {
157 SaveToFile(bitstream_buffer_id);
158 }
159 size_t gap = GetMaxGapFromHwSwResult(bitstream_buffer_id);
160 DVLOG(1) << "The maximum difference between software and hardware decode is"
161 << gap;
162 if (gap <= kDecodeThreshold)
163 SetState(CS_DECODE_PASS);
164 else
165 SetState(CS_DECODE_FAIL);
166 }
167
168 void JpegClient::NotifyError(int32_t bitstream_buffer_id,
169 JpegDecodeAccelerator::Error error) {
170 }
171
172 void JpegClient::PrepareMemory(int32_t bitstream_buffer_id) {
173 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
174
175 size_t input_size = image_file->data_str.size();
176 if (!in_shm_.get() || input_size > in_shm_->mapped_size()) {
177 in_shm_.reset(new base::SharedMemory);
178 CHECK(in_shm_->CreateAndMapAnonymous(input_size));
179 }
180 memcpy(in_shm_->memory(), image_file->data_str.data(), input_size);
181
182 if (!hw_out_shm_.get() ||
183 image_file->output_size > hw_out_shm_->mapped_size()) {
184 hw_out_shm_.reset(new base::SharedMemory);
185 CHECK(hw_out_shm_->CreateAndMapAnonymous(image_file->output_size));
186 }
187 memset(hw_out_shm_->memory(), 0, image_file->output_size);
188
189 if (!sw_out_shm_.get() ||
190 image_file->output_size > sw_out_shm_->mapped_size()) {
191 sw_out_shm_.reset(new base::SharedMemory);
192 CHECK(sw_out_shm_->CreateAndMapAnonymous(image_file->output_size));
193 }
194 memset(sw_out_shm_->memory(), 0, image_file->output_size);
195 }
196
197 void JpegClient::SetState(ClientState new_state) {
198 DVLOG(4) << "Changing state " << state_ << "->" << new_state;
199 note_->Notify(new_state);
200 state_ = new_state;
201 }
202
203 void JpegClient::SaveToFile(int32_t bitstream_buffer_id) {
204 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
205
206 base::FilePath filename(image_file->file_name);
207 base::FilePath out_filename = filename.ReplaceExtension(".yuv");
208 EXPECT_EQ(0, base::WriteFile(out_filename, NULL, 0));
209 EXPECT_TRUE(base::AppendToFile(
210 out_filename,
211 static_cast<char*>(hw_out_shm_->memory()),
212 base::checked_cast<int>(image_file->output_size)));
213 }
214
215 size_t JpegClient::GetMaxGapFromHwSwResult(int32_t bitstream_buffer_id) {
216 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
217
218 size_t gap = 0;
219 uint8* hw_ptr = reinterpret_cast<uint8*>(hw_out_shm_->memory());
220 uint8* sw_ptr = reinterpret_cast<uint8*>(sw_out_shm_->memory());
221 for (size_t i = 0; i < image_file->output_size; i++) {
222 if (std::abs(hw_ptr[i] - sw_ptr[i]) > gap)
223 gap = std::abs(hw_ptr[i] - sw_ptr[i]);
224 }
225 return gap;
226 }
227
228 void JpegClient::StartDecode(int32_t bitstream_buffer_id) {
229 DCHECK(static_cast<size_t>(bitstream_buffer_id) < test_image_files_.size());
230 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
231
232 SetState(CS_DECODING);
233 PrepareMemory(bitstream_buffer_id);
234 if (!GetSoftwareDecodeResult(bitstream_buffer_id)) {
235 SetState(CS_DECODE_FAIL);
236 return;
237 }
238
239 size_t output_size = media::VideoFrame::AllocationSize(
240 media::VideoFrame::I420, image_file->coded_size);
241
242 base::SharedMemoryHandle dup_handle;
243 CHECK(in_shm_->ShareToProcess(base::GetCurrentProcessHandle(), &dup_handle));
244 media::BitstreamBuffer bitstream_buffer(
245 bitstream_buffer_id, dup_handle, image_file->data_str.size());
246 out_frame_ = media::VideoFrame::WrapExternalSharedMemory(
247 media::VideoFrame::I420,
248 image_file->coded_size,
249 gfx::Rect(image_file->coded_size),
250 image_file->coded_size,
251 reinterpret_cast<uint8*>(hw_out_shm_->memory()),
252 output_size,
253 hw_out_shm_->handle(),
254 0,
255 base::TimeDelta());
256 DCHECK(out_frame_.get());
257 decoder_->Decode(bitstream_buffer, out_frame_);
258 }
259
260 bool JpegClient::GetSoftwareDecodeResult(int32_t bitstream_buffer_id) {
261 media::VideoFrame::Format format = media::VideoFrame::I420;
262 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
263
264 uint8 *yplane = reinterpret_cast<uint8*>(sw_out_shm_->memory());
kcwu 2015/06/09 12:17:44 "uint8* foo" instead of "uint8 *foo".
henryhsu 2015/06/12 05:48:00 Done.
265 uint8 *uplane = yplane + media::VideoFrame::PlaneAllocationSize(
266 format, media::VideoFrame::kYPlane, image_file->coded_size);
267 uint8 *vplane = uplane + media::VideoFrame::PlaneAllocationSize(
268 format, media::VideoFrame::kUPlane, image_file->coded_size);
269 int yplane_stride = image_file->coded_size.width();
270 int uv_plane_stride = yplane_stride / 2;
271
272 if (libyuv::ConvertToI420(
273 reinterpret_cast<uint8*>(in_shm_->memory()),
274 image_file->data_str.size(),
275 yplane,
276 yplane_stride,
277 uplane,
278 uv_plane_stride,
279 vplane,
280 uv_plane_stride,
281 0,
282 0,
283 image_file->coded_size.width(),
284 image_file->coded_size.height(),
285 image_file->coded_size.width(),
286 image_file->coded_size.height(),
287 libyuv::kRotate0,
288 libyuv::FOURCC_MJPG) != 0) {
289 LOG(ERROR) << "Software decode " << image_file->file_name << " failed.";
290 return false;
291 }
292 return true;
293 }
294
295 class JpegDecodeAcceleratorTest : public ::testing::Test {
296 protected:
297 JpegDecodeAcceleratorTest() {}
298 void SetUp() override;
299 void TearDown() override;
300
301 void ReadTestData(const base::FilePath::StringType& data);
302
303 ScopedVector<TestImageFile> test_image_files_;
304
305 protected:
306 // Required for Thread to work. Not used otherwise.
307 base::ShadowingAtExitManager at_exit_manager_;
308
309 DISALLOW_COPY_AND_ASSIGN(JpegDecodeAcceleratorTest);
310 };
311
312 void JpegDecodeAcceleratorTest::SetUp() {
313 ReadTestData(g_test_image_data);
314 }
315
316 void JpegDecodeAcceleratorTest::TearDown() {
317 }
318
319 void JpegDecodeAcceleratorTest::ReadTestData(
320 const base::FilePath::StringType& data) {
321 std::vector<base::FilePath::StringType> entries;
322 base::SplitString(data, ';', &entries);
323 CHECK_GE(entries.size(), 1U) << data;
324 for (size_t index = 0; index < entries.size(); ++index) {
325 TestImageFile* image_file = new TestImageFile(entries[index]);
326 // Read in the video data.
327 base::FilePath input_file = media::GetTestDataFilePath(entries[index]);
328 ASSERT_TRUE(base::ReadFileToString(input_file, &image_file->data_str))
329 << "failed to read input data from " << input_file.value();
330
331 ASSERT_TRUE(media::ParseJpegPicture(
332 reinterpret_cast<const uint8_t*>(image_file->data_str.data()),
333 image_file->data_str.size(),
334 &image_file->parse_result));
335 image_file->coded_size.SetSize(
336 image_file->parse_result.frame_header.coded_width,
337 image_file->parse_result.frame_header.coded_height);
338 image_file->output_size = media::VideoFrame::AllocationSize(
339 media::VideoFrame::I420, image_file->coded_size);
340 test_image_files_.push_back(image_file);
341 }
342 }
343
344 // Test parameters:
345 // - Number of concurrent Jpeg decoder.
346 // - Expected result.
347 class JpegDecodeAcceleratorParamTest
348 : public JpegDecodeAcceleratorTest,
349 public ::testing::WithParamInterface<base::Tuple<int, ClientState>> {
350 };
351
352 TEST_P(JpegDecodeAcceleratorParamTest, TestSimpleDecode) {
353 size_t num_concurrent_decoders = base::get<0>(GetParam());
354 ClientState expected_result = base::get<1>(GetParam());
355
356 base::Thread decoder_thread("DecoderThread");
357 ASSERT_TRUE(decoder_thread.Start());
358
359 ScopedVector<ClientStateNotification<ClientState> > notes;
kcwu 2015/06/09 12:17:44 s/> >/>>/
henryhsu 2015/06/12 05:47:59 Done.
360 ScopedVector<JpegClient> clients;
361
362 for (size_t i = 0; i < num_concurrent_decoders; i++) {
363 notes.push_back(new ClientStateNotification<ClientState>());
364 clients.push_back(new JpegClient(test_image_files_, notes.back()));
365 decoder_thread.task_runner()->PostTask(
366 FROM_HERE,
367 base::Bind(&JpegClient::CreateJpegDecoder,
368 base::Unretained(clients.back())));
369 }
370
371 for (size_t i = 0; i < num_concurrent_decoders; i++) {
372 ASSERT_EQ(notes[i]->Wait(), CS_DECODER_SET);
373 ASSERT_EQ(notes[i]->Wait(), CS_INITIALIZED);
374 }
375
376 for (size_t index = 0; index < test_image_files_.size(); index++) {
377 for (size_t i = 0; i < num_concurrent_decoders; i++) {
378 decoder_thread.task_runner()->PostTask(
379 FROM_HERE,
380 base::Bind(&JpegClient::StartDecode,
381 base::Unretained(clients[i]),
382 index));
383
384 }
385 }
386
387 for (size_t index = 0; index < test_image_files_.size(); index++) {
388 for (size_t i = 0; i < num_concurrent_decoders; i++) {
389 ASSERT_EQ(notes[i]->Wait(), CS_DECODING);
390 ASSERT_EQ(notes[i]->Wait(), expected_result);
391 }
392 }
393
394 for (size_t i = 0; i < num_concurrent_decoders; i++) {
395 decoder_thread.task_runner()->PostTask(
396 FROM_HERE,
397 base::Bind(&JpegClient::DestroyJpegDecoder,
398 base::Unretained(clients[i])));
399 ASSERT_EQ(notes[i]->Wait(), CS_DESTROYED);
400 }
401 decoder_thread.Stop();
402 }
403
404 INSTANTIATE_TEST_CASE_P(
405 DecodeSuccess, JpegDecodeAcceleratorParamTest,
406 ::testing::Values(base::MakeTuple(1, CS_DECODE_PASS)));
407
408 INSTANTIATE_TEST_CASE_P(
409 MultipleDecoder, JpegDecodeAcceleratorParamTest,
410 ::testing::Values(base::MakeTuple(3, CS_DECODE_PASS)));
411
412 } // namespace
413 } // namespace content
414
415 int main(int argc, char** argv) {
416 testing::InitGoogleTest(&argc, argv);
417 base::CommandLine::Init(argc, argv);
418 base::ShadowingAtExitManager at_exit_manager;
419
420 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
421 DCHECK(cmd_line);
422
423 base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
424 for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
425 it != switches.end(); ++it) {
426 if (it->first == "test_image_data") {
427 content::g_test_image_data = it->second.c_str();
428 continue;
429 }
430 if (it->first == "save_to_file") {
431 content::g_save_to_file = true;
432 continue;
433 }
434 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
435 }
436 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
437 content::VaapiWrapper::PreSandboxInitialization();
438 #endif
439 return RUN_ALL_TESTS();
440 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698