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

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

Powered by Google App Engine
This is Rietveld 408576698