OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/capture/video/video_capture_device.h" | 5 #include "media/capture/video/video_capture_device.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <memory> | 10 #include <memory> |
11 #include <utility> | 11 #include <utility> |
12 | 12 |
13 #include "base/bind.h" | 13 #include "base/bind.h" |
14 #include "base/bind_helpers.h" | 14 #include "base/bind_helpers.h" |
15 #include "base/memory/ref_counted.h" | 15 #include "base/memory/ref_counted.h" |
16 #include "base/run_loop.h" | 16 #include "base/run_loop.h" |
17 #include "base/single_thread_task_runner.h" | 17 #include "base/single_thread_task_runner.h" |
18 #include "base/test/test_timeouts.h" | 18 #include "base/test/test_timeouts.h" |
19 #include "base/threading/thread.h" | 19 #include "base/threading/thread.h" |
20 #include "base/threading/thread_task_runner_handle.h" | 20 #include "base/threading/thread_task_runner_handle.h" |
21 #include "build/build_config.h" | 21 #include "build/build_config.h" |
22 #include "media/base/bind_to_current_loop.h" | |
23 #include "media/base/video_capture_types.h" | 22 #include "media/base/video_capture_types.h" |
24 #include "media/capture/video/video_capture_device_factory.h" | 23 #include "media/capture/video/video_capture_device_factory.h" |
25 #include "testing/gmock/include/gmock/gmock.h" | 24 #include "testing/gmock/include/gmock/gmock.h" |
26 #include "testing/gtest/include/gtest/gtest.h" | 25 #include "testing/gtest/include/gtest/gtest.h" |
27 | 26 |
28 #if defined(OS_WIN) | 27 #if defined(OS_WIN) |
29 #include "base/win/scoped_com_initializer.h" | 28 #include "base/win/scoped_com_initializer.h" |
30 #include "base/win/windows_version.h" // For fine-grained suppression. | 29 #include "base/win/windows_version.h" // For fine-grained suppression. |
31 #include "media/capture/video/win/video_capture_device_factory_win.h" | 30 #include "media/capture/video/win/video_capture_device_factory_win.h" |
32 #endif | 31 #endif |
33 | 32 |
34 #if defined(OS_MACOSX) | 33 #if defined(OS_MACOSX) |
35 #include "media/base/mac/avfoundation_glue.h" | 34 #include "media/base/mac/avfoundation_glue.h" |
36 #include "media/capture/video/mac/video_capture_device_factory_mac.h" | 35 #include "media/capture/video/mac/video_capture_device_factory_mac.h" |
37 #endif | 36 #endif |
38 | 37 |
39 #if defined(OS_ANDROID) | 38 #if defined(OS_ANDROID) |
40 #include "base/android/jni_android.h" | 39 #include "base/android/jni_android.h" |
41 #include "media/capture/video/android/video_capture_device_android.h" | 40 #include "media/capture/video/android/video_capture_device_android.h" |
42 #endif | 41 #endif |
43 | 42 |
44 #if defined(OS_MACOSX) | 43 #if defined(OS_MACOSX) |
45 // Mac will always give you the size you ask for and this case will fail. | 44 // Mac will always give you the size you ask for and this case will fail. |
46 #define MAYBE_AllocateBadSize DISABLED_AllocateBadSize | 45 #define MAYBE_AllocateBadSize DISABLED_AllocateBadSize |
47 // We will always get YUYV from the Mac AVFoundation implementations. | 46 // We will always get YUYV from the Mac AVFoundation implementations. |
48 #define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg | 47 #define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg |
49 #define MAYBE_TakePhoto TakePhoto | |
50 #elif defined(OS_WIN) | 48 #elif defined(OS_WIN) |
51 #define MAYBE_AllocateBadSize AllocateBadSize | 49 #define MAYBE_AllocateBadSize AllocateBadSize |
52 #define MAYBE_CaptureMjpeg CaptureMjpeg | 50 #define MAYBE_CaptureMjpeg CaptureMjpeg |
53 #define MAYBE_TakePhoto DISABLED_TakePhoto | |
54 #elif defined(OS_ANDROID) | 51 #elif defined(OS_ANDROID) |
55 // TODO(wjia): enable those tests on Android. | 52 // TODO(wjia): enable those tests on Android. |
56 // On Android, native camera (JAVA) delivers frames on UI thread which is the | 53 // On Android, native camera (JAVA) delivers frames on UI thread which is the |
57 // main thread for tests. This results in no frame received by | 54 // main thread for tests. This results in no frame received by |
58 // VideoCaptureAndroid. | 55 // VideoCaptureAndroid. |
59 #define MAYBE_AllocateBadSize DISABLED_AllocateBadSize | 56 #define MAYBE_AllocateBadSize DISABLED_AllocateBadSize |
60 #define DeAllocateCameraWhileRunning DISABLED_DeAllocateCameraWhileRunning | 57 #define DeAllocateCameraWhileRunning DISABLED_DeAllocateCameraWhileRunning |
61 #define DeAllocateCameraWhileRunning DISABLED_DeAllocateCameraWhileRunning | 58 #define DeAllocateCameraWhileRunning DISABLED_DeAllocateCameraWhileRunning |
62 #define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg | 59 #define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg |
63 #define MAYBE_TakePhoto DISABLED_TakePhoto | |
64 #elif defined(OS_LINUX) | 60 #elif defined(OS_LINUX) |
65 // AllocateBadSize will hang when a real camera is attached and if more than one | 61 // AllocateBadSize will hang when a real camera is attached and if more than one |
66 // test is trying to use the camera (even across processes). Do NOT renable | 62 // test is trying to use the camera (even across processes). Do NOT renable |
67 // this test without fixing the many bugs associated with it: | 63 // this test without fixing the many bugs associated with it: |
68 // http://crbug.com/94134 http://crbug.com/137260 http://crbug.com/417824 | 64 // http://crbug.com/94134 http://crbug.com/137260 http://crbug.com/417824 |
69 #define MAYBE_AllocateBadSize DISABLED_AllocateBadSize | 65 #define MAYBE_AllocateBadSize DISABLED_AllocateBadSize |
70 #define MAYBE_CaptureMjpeg CaptureMjpeg | 66 #define MAYBE_CaptureMjpeg CaptureMjpeg |
71 #define MAYBE_TakePhoto DISABLED_TakePhoto | |
72 #else | 67 #else |
73 #define MAYBE_AllocateBadSize AllocateBadSize | 68 #define MAYBE_AllocateBadSize AllocateBadSize |
74 #define MAYBE_CaptureMjpeg CaptureMjpeg | 69 #define MAYBE_CaptureMjpeg CaptureMjpeg |
75 #define MAYBE_TakePhoto DISABLED_TakePhoto | |
76 #endif | 70 #endif |
77 | 71 |
78 using ::testing::_; | 72 using ::testing::_; |
79 using ::testing::SaveArg; | 73 using ::testing::SaveArg; |
80 | 74 |
81 namespace media { | 75 namespace media { |
82 namespace { | 76 namespace { |
83 | 77 |
84 class MockVideoCaptureClient : public VideoCaptureDevice::Client { | 78 class MockVideoCaptureClient : public VideoCaptureDevice::Client { |
85 public: | 79 public: |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 DoResurrectLastOutputBuffer(); | 129 DoResurrectLastOutputBuffer(); |
136 NOTREACHED() << "This should never be called"; | 130 NOTREACHED() << "This should never be called"; |
137 return std::unique_ptr<Buffer>(); | 131 return std::unique_ptr<Buffer>(); |
138 } | 132 } |
139 | 133 |
140 private: | 134 private: |
141 scoped_refptr<base::SingleThreadTaskRunner> main_thread_; | 135 scoped_refptr<base::SingleThreadTaskRunner> main_thread_; |
142 base::Callback<void(const VideoCaptureFormat&)> frame_cb_; | 136 base::Callback<void(const VideoCaptureFormat&)> frame_cb_; |
143 }; | 137 }; |
144 | 138 |
145 class MockImageCaptureClient : public base::RefCounted<MockImageCaptureClient> { | |
146 public: | |
147 // GMock doesn't support move-only arguments, so we use this forward method. | |
148 void DoOnPhotoTaken(mojo::String mime_type, mojo::Array<uint8_t> data) { | |
149 EXPECT_STREQ("image/jpeg", mime_type.storage().c_str()); | |
150 ASSERT_GT(data.size(), 4u); | |
151 // Check some bytes that univocally identify |data| as a JPEG File. | |
152 // https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#File_format_st
ructure | |
153 EXPECT_EQ(0xFF, data[0]); // First SOI byte | |
154 EXPECT_EQ(0xD8, data[1]); // Second SOI byte | |
155 EXPECT_EQ(0xFF, data[2]); // First JFIF-APP0 byte | |
156 EXPECT_EQ(0xE0, data[3]); // Second JFIF-APP0 byte | |
157 OnCorrectPhotoTaken(); | |
158 } | |
159 MOCK_METHOD0(OnCorrectPhotoTaken, void(void)); | |
160 MOCK_METHOD1( | |
161 OnTakePhotoFailure, | |
162 void(const base::Callback<void(mojo::String, mojo::Array<uint8_t>)>&)); | |
163 | |
164 private: | |
165 friend class base::RefCounted<MockImageCaptureClient>; | |
166 virtual ~MockImageCaptureClient() {} | |
167 }; | |
168 | |
169 class DeviceEnumerationListener | 139 class DeviceEnumerationListener |
170 : public base::RefCounted<DeviceEnumerationListener> { | 140 : public base::RefCounted<DeviceEnumerationListener> { |
171 public: | 141 public: |
172 MOCK_METHOD1(OnEnumeratedDevicesCallbackPtr, | 142 MOCK_METHOD1(OnEnumeratedDevicesCallbackPtr, |
173 void(VideoCaptureDevice::Names* names)); | 143 void(VideoCaptureDevice::Names* names)); |
174 // GMock doesn't support move-only arguments, so we use this forward method. | 144 // GMock doesn't support move-only arguments, so we use this forward method. |
175 void OnEnumeratedDevicesCallback( | 145 void OnEnumeratedDevicesCallback( |
176 std::unique_ptr<VideoCaptureDevice::Names> names) { | 146 std::unique_ptr<VideoCaptureDevice::Names> names) { |
177 OnEnumeratedDevicesCallbackPtr(names.release()); | 147 OnEnumeratedDevicesCallbackPtr(names.release()); |
178 } | 148 } |
179 | 149 |
180 private: | 150 private: |
181 friend class base::RefCounted<DeviceEnumerationListener>; | 151 friend class base::RefCounted<DeviceEnumerationListener>; |
182 virtual ~DeviceEnumerationListener() {} | 152 virtual ~DeviceEnumerationListener() {} |
183 }; | 153 }; |
184 | 154 |
185 } // namespace | 155 } // namespace |
186 | 156 |
187 class VideoCaptureDeviceTest : public testing::TestWithParam<gfx::Size> { | 157 class VideoCaptureDeviceTest : public testing::TestWithParam<gfx::Size> { |
188 protected: | 158 protected: |
189 typedef VideoCaptureDevice::Client Client; | 159 typedef VideoCaptureDevice::Client Client; |
190 | 160 |
191 VideoCaptureDeviceTest() | 161 VideoCaptureDeviceTest() |
192 : loop_(new base::MessageLoop()), | 162 : loop_(new base::MessageLoop()), |
193 video_capture_client_(new MockVideoCaptureClient( | 163 video_capture_client_(new MockVideoCaptureClient( |
194 base::Bind(&VideoCaptureDeviceTest::OnFrameCaptured, | 164 base::Bind(&VideoCaptureDeviceTest::OnFrameCaptured, |
195 base::Unretained(this)))), | 165 base::Unretained(this)))), |
196 device_enumeration_listener_(new DeviceEnumerationListener()), | |
197 image_capture_client_(new MockImageCaptureClient()), | |
198 video_capture_device_factory_(VideoCaptureDeviceFactory::CreateFactory( | 166 video_capture_device_factory_(VideoCaptureDeviceFactory::CreateFactory( |
199 base::ThreadTaskRunnerHandle::Get())) {} | 167 base::ThreadTaskRunnerHandle::Get())) { |
| 168 device_enumeration_listener_ = new DeviceEnumerationListener(); |
| 169 } |
200 | 170 |
201 void SetUp() override { | 171 void SetUp() override { |
202 #if defined(OS_ANDROID) | 172 #if defined(OS_ANDROID) |
203 VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice( | 173 VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice( |
204 base::android::AttachCurrentThread()); | 174 base::android::AttachCurrentThread()); |
205 #endif | 175 #endif |
206 #if defined(OS_MACOSX) | 176 #if defined(OS_MACOSX) |
207 AVFoundationGlue::InitializeAVFoundation(); | 177 AVFoundationGlue::InitializeAVFoundation(); |
208 #endif | 178 #endif |
209 EXPECT_CALL(*video_capture_client_, DoReserveOutputBuffer()).Times(0); | 179 EXPECT_CALL(*video_capture_client_, DoReserveOutputBuffer()).Times(0); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 DVLOG(1) << "Size " << size.ToString() << " is not supported."; | 248 DVLOG(1) << "Size " << size.ToString() << " is not supported."; |
279 return false; | 249 return false; |
280 } | 250 } |
281 return true; | 251 return true; |
282 } | 252 } |
283 | 253 |
284 #if defined(OS_WIN) | 254 #if defined(OS_WIN) |
285 base::win::ScopedCOMInitializer initialize_com_; | 255 base::win::ScopedCOMInitializer initialize_com_; |
286 #endif | 256 #endif |
287 std::unique_ptr<VideoCaptureDevice::Names> names_; | 257 std::unique_ptr<VideoCaptureDevice::Names> names_; |
288 const std::unique_ptr<base::MessageLoop> loop_; | 258 std::unique_ptr<base::MessageLoop> loop_; |
289 std::unique_ptr<base::RunLoop> run_loop_; | 259 std::unique_ptr<base::RunLoop> run_loop_; |
290 std::unique_ptr<MockVideoCaptureClient> video_capture_client_; | 260 std::unique_ptr<MockVideoCaptureClient> video_capture_client_; |
291 const scoped_refptr<DeviceEnumerationListener> device_enumeration_listener_; | 261 scoped_refptr<DeviceEnumerationListener> device_enumeration_listener_; |
292 const scoped_refptr<MockImageCaptureClient> image_capture_client_; | |
293 VideoCaptureFormat last_format_; | 262 VideoCaptureFormat last_format_; |
294 const std::unique_ptr<VideoCaptureDeviceFactory> | 263 std::unique_ptr<VideoCaptureDeviceFactory> video_capture_device_factory_; |
295 video_capture_device_factory_; | |
296 }; | 264 }; |
297 | 265 |
298 // Cause hangs on Windows Debug. http://crbug.com/417824 | 266 // Cause hangs on Windows Debug. http://crbug.com/417824 |
299 #if defined(OS_WIN) && !defined(NDEBUG) | 267 #if defined(OS_WIN) && !defined(NDEBUG) |
300 #define MAYBE_OpenInvalidDevice DISABLED_OpenInvalidDevice | 268 #define MAYBE_OpenInvalidDevice DISABLED_OpenInvalidDevice |
301 #else | 269 #else |
302 #define MAYBE_OpenInvalidDevice OpenInvalidDevice | 270 #define MAYBE_OpenInvalidDevice OpenInvalidDevice |
303 #endif | 271 #endif |
304 | 272 |
305 TEST_F(VideoCaptureDeviceTest, MAYBE_OpenInvalidDevice) { | 273 TEST_F(VideoCaptureDeviceTest, MAYBE_OpenInvalidDevice) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 std::unique_ptr<VideoCaptureDevice> device( | 318 std::unique_ptr<VideoCaptureDevice> device( |
351 video_capture_device_factory_->Create(names_->front())); | 319 video_capture_device_factory_->Create(names_->front())); |
352 ASSERT_TRUE(device); | 320 ASSERT_TRUE(device); |
353 DVLOG(1) << names_->front().id(); | 321 DVLOG(1) << names_->front().id(); |
354 | 322 |
355 EXPECT_CALL(*video_capture_client_, OnError(_, _)).Times(0); | 323 EXPECT_CALL(*video_capture_client_, OnError(_, _)).Times(0); |
356 | 324 |
357 VideoCaptureParams capture_params; | 325 VideoCaptureParams capture_params; |
358 capture_params.requested_format.frame_size.SetSize(width, height); | 326 capture_params.requested_format.frame_size.SetSize(width, height); |
359 capture_params.requested_format.frame_rate = 30.0f; | 327 capture_params.requested_format.frame_rate = 30.0f; |
360 capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420; | 328 capture_params.requested_format.pixel_format = |
| 329 PIXEL_FORMAT_I420; |
361 device->AllocateAndStart(capture_params, std::move(video_capture_client_)); | 330 device->AllocateAndStart(capture_params, std::move(video_capture_client_)); |
362 // Get captured video frames. | 331 // Get captured video frames. |
363 WaitForCapturedFrame(); | 332 WaitForCapturedFrame(); |
364 EXPECT_EQ(last_format().frame_size.width(), width); | 333 EXPECT_EQ(last_format().frame_size.width(), width); |
365 EXPECT_EQ(last_format().frame_size.height(), height); | 334 EXPECT_EQ(last_format().frame_size.height(), height); |
366 if (last_format().pixel_format != PIXEL_FORMAT_MJPEG) | 335 if (last_format().pixel_format != PIXEL_FORMAT_MJPEG) |
367 EXPECT_EQ(size.GetArea(), last_format().frame_size.GetArea()); | 336 EXPECT_EQ(size.GetArea(), last_format().frame_size.GetArea()); |
368 device->StopAndDeAllocate(); | 337 device->StopAndDeAllocate(); |
369 } | 338 } |
370 | 339 |
(...skipping 14 matching lines...) Expand all Loading... |
385 std::unique_ptr<VideoCaptureDevice> device( | 354 std::unique_ptr<VideoCaptureDevice> device( |
386 video_capture_device_factory_->Create(names_->front())); | 355 video_capture_device_factory_->Create(names_->front())); |
387 ASSERT_TRUE(device); | 356 ASSERT_TRUE(device); |
388 | 357 |
389 EXPECT_CALL(*video_capture_client_, OnError(_, _)).Times(0); | 358 EXPECT_CALL(*video_capture_client_, OnError(_, _)).Times(0); |
390 | 359 |
391 const gfx::Size input_size(640, 480); | 360 const gfx::Size input_size(640, 480); |
392 VideoCaptureParams capture_params; | 361 VideoCaptureParams capture_params; |
393 capture_params.requested_format.frame_size.SetSize(637, 472); | 362 capture_params.requested_format.frame_size.SetSize(637, 472); |
394 capture_params.requested_format.frame_rate = 35; | 363 capture_params.requested_format.frame_rate = 35; |
395 capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420; | 364 capture_params.requested_format.pixel_format = |
| 365 PIXEL_FORMAT_I420; |
396 device->AllocateAndStart(capture_params, std::move(video_capture_client_)); | 366 device->AllocateAndStart(capture_params, std::move(video_capture_client_)); |
397 WaitForCapturedFrame(); | 367 WaitForCapturedFrame(); |
398 device->StopAndDeAllocate(); | 368 device->StopAndDeAllocate(); |
399 EXPECT_EQ(last_format().frame_size.width(), input_size.width()); | 369 EXPECT_EQ(last_format().frame_size.width(), input_size.width()); |
400 EXPECT_EQ(last_format().frame_size.height(), input_size.height()); | 370 EXPECT_EQ(last_format().frame_size.height(), input_size.height()); |
401 if (last_format().pixel_format != PIXEL_FORMAT_MJPEG) | 371 if (last_format().pixel_format != PIXEL_FORMAT_MJPEG) |
402 EXPECT_EQ(input_size.GetArea(), last_format().frame_size.GetArea()); | 372 EXPECT_EQ(input_size.GetArea(), last_format().frame_size.GetArea()); |
403 } | 373 } |
404 | 374 |
405 // Cause hangs on Windows, Linux. Fails Android. http://crbug.com/417824 | 375 // Cause hangs on Windows, Linux. Fails Android. http://crbug.com/417824 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 EXPECT_GE(static_cast<size_t>(1280 * 720), | 477 EXPECT_GE(static_cast<size_t>(1280 * 720), |
508 last_format().ImageAllocationSize()); | 478 last_format().ImageAllocationSize()); |
509 device->StopAndDeAllocate(); | 479 device->StopAndDeAllocate(); |
510 } | 480 } |
511 | 481 |
512 TEST_F(VideoCaptureDeviceTest, GetDeviceSupportedFormats) { | 482 TEST_F(VideoCaptureDeviceTest, GetDeviceSupportedFormats) { |
513 // Use PIXEL_FORMAT_MAX to iterate all device names for testing | 483 // Use PIXEL_FORMAT_MAX to iterate all device names for testing |
514 // GetDeviceSupportedFormats(). | 484 // GetDeviceSupportedFormats(). |
515 std::unique_ptr<VideoCaptureDevice::Name> name = | 485 std::unique_ptr<VideoCaptureDevice::Name> name = |
516 GetFirstDeviceNameSupportingPixelFormat(PIXEL_FORMAT_MAX); | 486 GetFirstDeviceNameSupportingPixelFormat(PIXEL_FORMAT_MAX); |
517 // Verify no camera returned for PIXEL_FORMAT_MAX. Nothing else to test here | 487 // Verify no camera returned for PIXEL_FORMAT_MAX. Nothing else |
| 488 // to test here |
518 // since we cannot forecast the hardware capabilities. | 489 // since we cannot forecast the hardware capabilities. |
519 ASSERT_FALSE(name); | 490 ASSERT_FALSE(name); |
520 } | 491 } |
521 | 492 |
522 // Start the camera and take a photo. | |
523 TEST_F(VideoCaptureDeviceTest, MAYBE_TakePhoto) { | |
524 names_ = EnumerateDevices(); | |
525 if (names_->empty()) { | |
526 VLOG(1) << "No camera available. Exiting test."; | |
527 return; | |
528 } | |
529 std::unique_ptr<VideoCaptureDevice> device( | |
530 video_capture_device_factory_->Create(names_->front())); | |
531 ASSERT_TRUE(device); | |
532 | |
533 EXPECT_CALL(*video_capture_client_, OnError(_, _)).Times(0); | |
534 | |
535 VideoCaptureParams capture_params; | |
536 capture_params.requested_format.frame_size.SetSize(640, 480); | |
537 capture_params.requested_format.frame_rate = 30; | |
538 capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420; | |
539 device->AllocateAndStart(capture_params, std::move(video_capture_client_)); | |
540 WaitForCapturedFrame(); | |
541 | |
542 VideoCaptureDevice::TakePhotoCallback scoped_callback( | |
543 base::Bind(&MockImageCaptureClient::DoOnPhotoTaken, | |
544 image_capture_client_), | |
545 media::BindToCurrentLoop(base::Bind( | |
546 &MockImageCaptureClient::OnTakePhotoFailure, image_capture_client_))); | |
547 | |
548 EXPECT_CALL(*image_capture_client_.get(), OnCorrectPhotoTaken()).Times(1); | |
549 device->TakePhoto(std::move(scoped_callback)); | |
550 WaitForCapturedFrame(); | |
551 | |
552 device->StopAndDeAllocate(); | |
553 } | |
554 | |
555 }; // namespace media | 493 }; // namespace media |
OLD | NEW |