Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / common / gpu / media / jpeg_decode_accelerator_unittest.cc
blobfa640b1633e925a7271180b5abae955d29e4274a
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 visible_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 (!GetSoftwareDecodeResult(bitstream_buffer_id)) {
162 SetState(CS_ERROR);
163 return;
165 if (g_save_to_file) {
166 SaveToFile(bitstream_buffer_id);
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 LOG_ASSERT(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 LOG_ASSERT(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 LOG_ASSERT(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 base::SharedMemoryHandle dup_handle;
246 dup_handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
247 media::BitstreamBuffer bitstream_buffer(bitstream_buffer_id, dup_handle,
248 image_file->data_str.size());
249 scoped_refptr<media::VideoFrame> out_frame_ =
250 media::VideoFrame::WrapExternalSharedMemory(
251 media::PIXEL_FORMAT_I420,
252 image_file->visible_size,
253 gfx::Rect(image_file->visible_size),
254 image_file->visible_size,
255 static_cast<uint8_t*>(hw_out_shm_->memory()),
256 image_file->output_size,
257 hw_out_shm_->handle(),
259 base::TimeDelta());
260 LOG_ASSERT(out_frame_.get());
261 decoder_->Decode(bitstream_buffer, out_frame_);
264 bool JpegClient::GetSoftwareDecodeResult(int32_t bitstream_buffer_id) {
265 media::VideoPixelFormat format = media::PIXEL_FORMAT_I420;
266 TestImageFile* image_file = test_image_files_[bitstream_buffer_id];
268 uint8_t* yplane = static_cast<uint8_t*>(sw_out_shm_->memory());
269 uint8_t* uplane =
270 yplane +
271 media::VideoFrame::PlaneSize(format, media::VideoFrame::kYPlane,
272 image_file->visible_size).GetArea();
273 uint8_t* vplane =
274 uplane +
275 media::VideoFrame::PlaneSize(format, media::VideoFrame::kUPlane,
276 image_file->visible_size).GetArea();
277 int yplane_stride = image_file->visible_size.width();
278 int uv_plane_stride = yplane_stride / 2;
280 if (libyuv::ConvertToI420(
281 static_cast<uint8_t*>(in_shm_->memory()),
282 image_file->data_str.size(),
283 yplane,
284 yplane_stride,
285 uplane,
286 uv_plane_stride,
287 vplane,
288 uv_plane_stride,
291 image_file->visible_size.width(),
292 image_file->visible_size.height(),
293 image_file->visible_size.width(),
294 image_file->visible_size.height(),
295 libyuv::kRotate0,
296 libyuv::FOURCC_MJPG) != 0) {
297 LOG(ERROR) << "Software decode " << image_file->filename << " failed.";
298 return false;
300 return true;
303 class JpegDecodeAcceleratorTestEnvironment : public ::testing::Environment {
304 public:
305 JpegDecodeAcceleratorTestEnvironment(
306 const base::FilePath::CharType* jpeg_filenames) {
307 user_jpeg_filenames_ =
308 jpeg_filenames ? jpeg_filenames: kDefaultJpegFilename;
310 void SetUp() override;
311 void TearDown() override;
313 // Create all black test image with |width| and |height| size.
314 bool CreateTestJpegImage(int width, int height, base::FilePath* filename);
316 // Read image from |filename| to |image_data|.
317 void ReadTestJpegImage(base::FilePath& filename, TestImageFile* image_data);
319 // Parsed data of |test_1280x720_jpeg_file_|.
320 scoped_ptr<TestImageFile> image_data_1280x720_black_;
321 // Parsed data of |test_640x368_jpeg_file_|.
322 scoped_ptr<TestImageFile> image_data_640x368_black_;
323 // Parsed data of |test_640x360_jpeg_file_|.
324 scoped_ptr<TestImageFile> image_data_640x360_black_;
325 // Parsed data of "peach_pi-1280x720.jpg".
326 scoped_ptr<TestImageFile> image_data_1280x720_default_;
327 // Parsed data of failure image.
328 scoped_ptr<TestImageFile> image_data_invalid_;
329 // Parsed data from command line.
330 ScopedVector<TestImageFile> image_data_user_;
332 private:
333 const base::FilePath::CharType* user_jpeg_filenames_;
335 // Used for InputSizeChange test case. The image size should be smaller than
336 // |kDefaultJpegFilename|.
337 base::FilePath test_1280x720_jpeg_file_;
338 // Used for ResolutionChange test case.
339 base::FilePath test_640x368_jpeg_file_;
340 // Used for testing some drivers which will align the output resolution to a
341 // multiple of 16. 640x360 will be aligned to 640x368.
342 base::FilePath test_640x360_jpeg_file_;
345 void JpegDecodeAcceleratorTestEnvironment::SetUp() {
346 ASSERT_TRUE(CreateTestJpegImage(1280, 720, &test_1280x720_jpeg_file_));
347 ASSERT_TRUE(CreateTestJpegImage(640, 368, &test_640x368_jpeg_file_));
348 ASSERT_TRUE(CreateTestJpegImage(640, 360, &test_640x360_jpeg_file_));
350 image_data_1280x720_black_.reset(
351 new TestImageFile(test_1280x720_jpeg_file_.value()));
352 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(test_1280x720_jpeg_file_,
353 image_data_1280x720_black_.get()));
355 image_data_640x368_black_.reset(
356 new TestImageFile(test_640x368_jpeg_file_.value()));
357 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(test_640x368_jpeg_file_,
358 image_data_640x368_black_.get()));
360 image_data_640x360_black_.reset(
361 new TestImageFile(test_640x360_jpeg_file_.value()));
362 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(test_640x360_jpeg_file_,
363 image_data_640x360_black_.get()));
365 base::FilePath default_jpeg_file =
366 media::GetTestDataFilePath(kDefaultJpegFilename);
367 image_data_1280x720_default_.reset(new TestImageFile(kDefaultJpegFilename));
368 ASSERT_NO_FATAL_FAILURE(
369 ReadTestJpegImage(default_jpeg_file, image_data_1280x720_default_.get()));
371 image_data_invalid_.reset(new TestImageFile("failure.jpg"));
372 image_data_invalid_->data_str.resize(100, 0);
373 image_data_invalid_->visible_size.SetSize(1280, 720);
374 image_data_invalid_->output_size = media::VideoFrame::AllocationSize(
375 media::PIXEL_FORMAT_I420, image_data_invalid_->visible_size);
377 // |user_jpeg_filenames_| may include many files and use ';' as delimiter.
378 std::vector<base::FilePath::StringType> filenames = base::SplitString(
379 user_jpeg_filenames_, base::FilePath::StringType(1, ';'),
380 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
381 for (const auto& filename : filenames) {
382 base::FilePath input_file = media::GetTestDataFilePath(filename);
383 TestImageFile* image_data = new TestImageFile(filename);
384 ASSERT_NO_FATAL_FAILURE(ReadTestJpegImage(input_file, image_data));
385 image_data_user_.push_back(image_data);
389 void JpegDecodeAcceleratorTestEnvironment::TearDown() {
390 base::DeleteFile(test_1280x720_jpeg_file_, false);
391 base::DeleteFile(test_640x368_jpeg_file_, false);
392 base::DeleteFile(test_640x360_jpeg_file_, false);
395 bool JpegDecodeAcceleratorTestEnvironment::CreateTestJpegImage(
396 int width,
397 int height,
398 base::FilePath* filename) {
399 const int kBytesPerPixel = 3;
400 const int kJpegQuality = 100;
401 std::vector<unsigned char> input_buffer(width * height * kBytesPerPixel);
402 std::vector<unsigned char> encoded;
403 if (!gfx::JPEGCodec::Encode(&input_buffer[0], gfx::JPEGCodec::FORMAT_RGB,
404 width, height, width * kBytesPerPixel,
405 kJpegQuality, &encoded)) {
406 return false;
409 LOG_ASSERT(base::CreateTemporaryFile(filename));
410 EXPECT_TRUE(base::AppendToFile(
411 *filename, reinterpret_cast<char*>(&encoded[0]), encoded.size()));
412 return true;
415 void JpegDecodeAcceleratorTestEnvironment::ReadTestJpegImage(
416 base::FilePath& input_file, TestImageFile* image_data) {
417 ASSERT_TRUE(base::ReadFileToString(input_file, &image_data->data_str));
419 ASSERT_TRUE(media::ParseJpegPicture(
420 reinterpret_cast<const uint8_t*>(image_data->data_str.data()),
421 image_data->data_str.size(),
422 &image_data->parse_result));
423 image_data->visible_size.SetSize(
424 image_data->parse_result.frame_header.visible_width,
425 image_data->parse_result.frame_header.visible_height);
426 image_data->output_size = media::VideoFrame::AllocationSize(
427 media::PIXEL_FORMAT_I420, image_data->visible_size);
430 class JpegDecodeAcceleratorTest : public ::testing::Test {
431 protected:
432 JpegDecodeAcceleratorTest() {}
434 void TestDecode(size_t num_concurrent_decoders);
436 // The elements of |test_image_files_| are owned by
437 // JpegDecodeAcceleratorTestEnvironment.
438 std::vector<TestImageFile*> test_image_files_;
439 std::vector<ClientState> expected_status_;
441 protected:
442 DISALLOW_COPY_AND_ASSIGN(JpegDecodeAcceleratorTest);
445 void JpegDecodeAcceleratorTest::TestDecode(size_t num_concurrent_decoders) {
446 LOG_ASSERT(test_image_files_.size() == expected_status_.size());
447 base::Thread decoder_thread("DecoderThread");
448 ASSERT_TRUE(decoder_thread.Start());
450 ScopedVector<ClientStateNotification<ClientState>> notes;
451 ScopedVector<JpegClient> clients;
453 for (size_t i = 0; i < num_concurrent_decoders; i++) {
454 notes.push_back(new ClientStateNotification<ClientState>());
455 clients.push_back(new JpegClient(test_image_files_, notes.back()));
456 decoder_thread.task_runner()->PostTask(
457 FROM_HERE, base::Bind(&JpegClient::CreateJpegDecoder,
458 base::Unretained(clients.back())));
459 ASSERT_EQ(notes[i]->Wait(), CS_INITIALIZED);
462 for (size_t index = 0; index < test_image_files_.size(); index++) {
463 for (size_t i = 0; i < num_concurrent_decoders; i++) {
464 decoder_thread.task_runner()->PostTask(
465 FROM_HERE, base::Bind(&JpegClient::StartDecode,
466 base::Unretained(clients[i]),
467 index));
469 for (size_t i = 0; i < num_concurrent_decoders; i++) {
470 ASSERT_EQ(notes[i]->Wait(), expected_status_[index]);
474 for (size_t i = 0; i < num_concurrent_decoders; i++) {
475 decoder_thread.task_runner()->PostTask(
476 FROM_HERE, base::Bind(&JpegClient::DestroyJpegDecoder,
477 base::Unretained(clients[i])));
479 decoder_thread.Stop();
482 TEST_F(JpegDecodeAcceleratorTest, SimpleDecode) {
483 for (const auto& image : g_env->image_data_user_) {
484 test_image_files_.push_back(image);
485 expected_status_.push_back(CS_DECODE_PASS);
487 TestDecode(1);
490 TEST_F(JpegDecodeAcceleratorTest, MultipleDecoders) {
491 for (const auto& image : g_env->image_data_user_) {
492 test_image_files_.push_back(image);
493 expected_status_.push_back(CS_DECODE_PASS);
495 TestDecode(3);
498 TEST_F(JpegDecodeAcceleratorTest, InputSizeChange) {
499 // The size of |image_data_1280x720_black_| is smaller than
500 // |image_data_1280x720_default_|.
501 test_image_files_.push_back(g_env->image_data_1280x720_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_1280x720_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, ResolutionChange) {
510 test_image_files_.push_back(g_env->image_data_640x368_black_.get());
511 test_image_files_.push_back(g_env->image_data_1280x720_default_.get());
512 test_image_files_.push_back(g_env->image_data_640x368_black_.get());
513 for (size_t i = 0; i < test_image_files_.size(); i++)
514 expected_status_.push_back(CS_DECODE_PASS);
515 TestDecode(1);
518 TEST_F(JpegDecodeAcceleratorTest, CodedSizeAlignment) {
519 test_image_files_.push_back(g_env->image_data_640x360_black_.get());
520 expected_status_.push_back(CS_DECODE_PASS);
521 TestDecode(1);
524 TEST_F(JpegDecodeAcceleratorTest, FailureJpeg) {
525 test_image_files_.push_back(g_env->image_data_invalid_.get());
526 expected_status_.push_back(CS_ERROR);
527 TestDecode(1);
530 TEST_F(JpegDecodeAcceleratorTest, KeepDecodeAfterFailure) {
531 test_image_files_.push_back(g_env->image_data_invalid_.get());
532 test_image_files_.push_back(g_env->image_data_1280x720_default_.get());
533 expected_status_.push_back(CS_ERROR);
534 expected_status_.push_back(CS_DECODE_PASS);
535 TestDecode(1);
538 } // namespace
539 } // namespace content
541 int main(int argc, char** argv) {
542 testing::InitGoogleTest(&argc, argv);
543 base::CommandLine::Init(argc, argv);
544 base::ShadowingAtExitManager at_exit_manager;
546 // Needed to enable DVLOG through --vmodule.
547 logging::LoggingSettings settings;
548 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
549 LOG_ASSERT(logging::InitLogging(settings));
551 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
552 DCHECK(cmd_line);
554 const base::FilePath::CharType* jpeg_filenames = nullptr;
555 base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
556 for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
557 it != switches.end(); ++it) {
558 // jpeg_filenames can include one or many files and use ';' as delimiter.
559 if (it->first == "jpeg_filenames") {
560 jpeg_filenames = it->second.c_str();
561 continue;
563 if (it->first == "save_to_file") {
564 content::g_save_to_file = true;
565 continue;
567 if (it->first == "v" || it->first == "vmodule")
568 continue;
569 LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
571 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
572 content::VaapiWrapper::PreSandboxInitialization();
573 #endif
575 content::g_env =
576 reinterpret_cast<content::JpegDecodeAcceleratorTestEnvironment*>(
577 testing::AddGlobalTestEnvironment(
578 new content::JpegDecodeAcceleratorTestEnvironment(
579 jpeg_filenames)));
581 return RUN_ALL_TESTS();