OLD | NEW |
---|---|
(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 } | |
OLD | NEW |