OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 <array> |
| 6 |
5 #include "base/bind.h" | 7 #include "base/bind.h" |
6 #include "base/callback.h" | 8 #include "base/callback.h" |
7 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/memory/ptr_util.h" |
8 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
9 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
10 #include "base/run_loop.h" | 13 #include "base/run_loop.h" |
11 #include "content/child/child_process.h" | 14 #include "content/child/child_process.h" |
| 15 #include "content/common/media/video_capture_messages.h" |
12 #include "content/renderer/media/video_capture_impl.h" | 16 #include "content/renderer/media/video_capture_impl.h" |
13 #include "content/renderer/media/video_capture_impl_manager.h" | 17 #include "content/renderer/media/video_capture_impl_manager.h" |
14 #include "content/renderer/media/video_capture_message_filter.h" | 18 #include "content/renderer/media/video_capture_message_filter.h" |
15 #include "media/base/bind_to_current_loop.h" | 19 #include "media/base/bind_to_current_loop.h" |
16 #include "testing/gmock/include/gmock/gmock.h" | 20 #include "testing/gmock/include/gmock/gmock.h" |
17 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" |
18 | 22 |
19 using ::testing::_; | 23 using ::testing::_; |
20 using ::testing::DoAll; | 24 using ::testing::DoAll; |
21 using ::testing::SaveArg; | 25 using ::testing::SaveArg; |
22 using media::BindToCurrentLoop; | 26 using media::BindToCurrentLoop; |
23 | 27 |
24 namespace content { | 28 namespace content { |
25 | 29 |
26 ACTION_P(RunClosure, closure) { | 30 ACTION_P(RunClosure, closure) { |
27 closure.Run(); | 31 closure.Run(); |
28 } | 32 } |
29 | 33 |
| 34 namespace { |
| 35 |
| 36 // Callback interface to be implemented by |
| 37 // VideoCaptureImplManagerTest. MockVideoCaptureImpl intercepts IPC messages and |
| 38 // calls these methods to simulate what the VideoCaptureHost would do. |
| 39 class PauseResumeCallback { |
| 40 public: |
| 41 PauseResumeCallback() {} |
| 42 virtual ~PauseResumeCallback() {} |
| 43 |
| 44 virtual void OnPaused(media::VideoCaptureSessionId session_id) = 0; |
| 45 virtual void OnResumed(media::VideoCaptureSessionId session_id) = 0; |
| 46 }; |
| 47 |
30 class MockVideoCaptureImpl : public VideoCaptureImpl { | 48 class MockVideoCaptureImpl : public VideoCaptureImpl { |
31 public: | 49 public: |
32 MockVideoCaptureImpl(media::VideoCaptureSessionId session_id, | 50 MockVideoCaptureImpl(media::VideoCaptureSessionId session_id, |
33 VideoCaptureMessageFilter* filter, | 51 VideoCaptureMessageFilter* filter, |
| 52 PauseResumeCallback* pause_callback, |
34 base::Closure destruct_callback) | 53 base::Closure destruct_callback) |
35 : VideoCaptureImpl(session_id, filter), | 54 : VideoCaptureImpl(session_id, filter), |
36 destruct_callback_(destruct_callback) { | 55 pause_callback_(pause_callback), destruct_callback_(destruct_callback) { |
37 } | 56 } |
38 | 57 |
39 ~MockVideoCaptureImpl() override { destruct_callback_.Run(); } | 58 ~MockVideoCaptureImpl() override { destruct_callback_.Run(); } |
40 | 59 |
| 60 void Send(IPC::Message* message) override { |
| 61 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureImpl, *message) |
| 62 IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Pause, DevicePauseCapture) |
| 63 IPC_MESSAGE_HANDLER(VideoCaptureHostMsg_Resume, DeviceResumeCapture) |
| 64 default: |
| 65 VideoCaptureImpl::Send(message); |
| 66 return; |
| 67 IPC_END_MESSAGE_MAP() |
| 68 delete message; |
| 69 } |
| 70 |
41 private: | 71 private: |
| 72 void DevicePauseCapture(int device_id) { |
| 73 pause_callback_->OnPaused(session_id()); |
| 74 } |
| 75 |
| 76 void DeviceResumeCapture(int device_id, |
| 77 media::VideoCaptureSessionId session_id, |
| 78 const media::VideoCaptureParams& params) { |
| 79 pause_callback_->OnResumed(session_id); |
| 80 } |
| 81 |
| 82 PauseResumeCallback* const pause_callback_; |
42 base::Closure destruct_callback_; | 83 base::Closure destruct_callback_; |
43 | 84 |
44 DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImpl); | 85 DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImpl); |
45 }; | 86 }; |
46 | 87 |
47 class MockVideoCaptureImplManager : public VideoCaptureImplManager { | 88 class MockVideoCaptureImplManager : public VideoCaptureImplManager { |
48 public: | 89 public: |
49 explicit MockVideoCaptureImplManager( | 90 MockVideoCaptureImplManager(PauseResumeCallback* pause_callback, |
50 base::Closure destruct_video_capture_callback) | 91 base::Closure destruct_video_capture_callback) |
51 : destruct_video_capture_callback_( | 92 : pause_callback_(pause_callback), |
| 93 destruct_video_capture_callback_( |
52 destruct_video_capture_callback) {} | 94 destruct_video_capture_callback) {} |
53 ~MockVideoCaptureImplManager() override {} | 95 ~MockVideoCaptureImplManager() override {} |
54 | 96 |
55 protected: | 97 protected: |
56 VideoCaptureImpl* CreateVideoCaptureImplForTesting( | 98 std::unique_ptr<VideoCaptureImpl> CreateVideoCaptureImplForTesting( |
57 media::VideoCaptureSessionId id, | 99 media::VideoCaptureSessionId id, |
58 VideoCaptureMessageFilter* filter) const override { | 100 VideoCaptureMessageFilter* filter) const override { |
59 return new MockVideoCaptureImpl(id, | 101 return base::MakeUnique<MockVideoCaptureImpl>( |
60 filter, | 102 id, filter, pause_callback_, destruct_video_capture_callback_); |
61 destruct_video_capture_callback_); | |
62 } | 103 } |
63 | 104 |
64 private: | 105 private: |
| 106 PauseResumeCallback* const pause_callback_; |
65 base::Closure destruct_video_capture_callback_; | 107 base::Closure destruct_video_capture_callback_; |
66 | 108 |
67 DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImplManager); | 109 DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImplManager); |
68 }; | 110 }; |
69 | 111 |
70 class VideoCaptureImplManagerTest : public ::testing::Test { | 112 } // namespace |
| 113 |
| 114 class VideoCaptureImplManagerTest |
| 115 : public ::testing::Test, public PauseResumeCallback { |
71 public: | 116 public: |
72 VideoCaptureImplManagerTest() | 117 VideoCaptureImplManagerTest() |
73 : manager_(new MockVideoCaptureImplManager( | 118 : manager_(new MockVideoCaptureImplManager( |
74 BindToCurrentLoop(cleanup_run_loop_.QuitClosure()))) { | 119 this, BindToCurrentLoop(cleanup_run_loop_.QuitClosure()))) { |
75 params_.requested_format = media::VideoCaptureFormat( | 120 params_.requested_format = media::VideoCaptureFormat( |
76 gfx::Size(176, 144), 30, media::PIXEL_FORMAT_I420); | 121 gfx::Size(176, 144), 30, media::PIXEL_FORMAT_I420); |
77 child_process_.reset(new ChildProcess()); | 122 child_process_.reset(new ChildProcess()); |
78 } | 123 } |
79 | 124 |
80 void FakeChannelSetup() { | 125 void FakeChannelSetup() { |
81 scoped_refptr<base::SingleThreadTaskRunner> task_runner = | 126 scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
82 child_process_->io_task_runner(); | 127 child_process_->io_task_runner(); |
83 if (!task_runner->BelongsToCurrentThread()) { | 128 if (!task_runner->BelongsToCurrentThread()) { |
84 task_runner->PostTask( | 129 task_runner->PostTask( |
85 FROM_HERE, base::Bind(&VideoCaptureImplManagerTest::FakeChannelSetup, | 130 FROM_HERE, base::Bind(&VideoCaptureImplManagerTest::FakeChannelSetup, |
86 base::Unretained(this))); | 131 base::Unretained(this))); |
87 return; | 132 return; |
88 } | 133 } |
89 manager_->video_capture_message_filter()->OnFilterAdded(NULL); | 134 manager_->video_capture_message_filter()->OnFilterAdded(NULL); |
90 } | 135 } |
91 | 136 |
92 protected: | 137 protected: |
| 138 static constexpr size_t kNumClients = 3; |
| 139 |
| 140 std::array<base::Closure, kNumClients> StartCaptureForAllClients( |
| 141 bool same_session_id) { |
| 142 base::RunLoop run_loop; |
| 143 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 144 EXPECT_CALL(*this, OnStarted(_)).Times(kNumClients - 1) |
| 145 .RetiresOnSaturation(); |
| 146 EXPECT_CALL(*this, OnStarted(_)).WillOnce(RunClosure(quit_closure)) |
| 147 .RetiresOnSaturation(); |
| 148 std::array<base::Closure, kNumClients> stop_callbacks; |
| 149 for (size_t i = 0; i < kNumClients; ++i) |
| 150 stop_callbacks[i] = StartCapture( |
| 151 same_session_id ? 0 : static_cast<media::VideoCaptureSessionId>(i), |
| 152 params_); |
| 153 FakeChannelSetup(); |
| 154 run_loop.Run(); |
| 155 return stop_callbacks; |
| 156 } |
| 157 |
| 158 void StopCaptureForAllClients( |
| 159 std::array<base::Closure, kNumClients>* stop_callbacks) { |
| 160 base::RunLoop run_loop; |
| 161 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 162 EXPECT_CALL(*this, OnStopped(_)).Times(kNumClients - 1) |
| 163 .RetiresOnSaturation(); |
| 164 EXPECT_CALL(*this, OnStopped(_)).WillOnce(RunClosure(quit_closure)) |
| 165 .RetiresOnSaturation(); |
| 166 for (size_t i = 0; i < kNumClients; ++i) |
| 167 (*stop_callbacks)[i].Run(); |
| 168 run_loop.Run(); |
| 169 } |
| 170 |
93 MOCK_METHOD2(OnFrameReady, | 171 MOCK_METHOD2(OnFrameReady, |
94 void(const scoped_refptr<media::VideoFrame>&, | 172 void(const scoped_refptr<media::VideoFrame>&, |
95 base::TimeTicks estimated_capture_time)); | 173 base::TimeTicks estimated_capture_time)); |
96 MOCK_METHOD0(OnStarted, void()); | 174 MOCK_METHOD1(OnStarted, void(media::VideoCaptureSessionId id)); |
97 MOCK_METHOD0(OnStopped, void()); | 175 MOCK_METHOD1(OnStopped, void(media::VideoCaptureSessionId id)); |
| 176 MOCK_METHOD1(OnPaused, void(media::VideoCaptureSessionId id)); |
| 177 MOCK_METHOD1(OnResumed, void(media::VideoCaptureSessionId id)); |
98 | 178 |
99 void OnStateUpdate(VideoCaptureState state) { | 179 void OnStateUpdate(media::VideoCaptureSessionId id, VideoCaptureState state) { |
100 switch (state) { | 180 switch (state) { |
101 case VIDEO_CAPTURE_STATE_STARTED: | 181 case VIDEO_CAPTURE_STATE_STARTED: |
102 OnStarted(); | 182 OnStarted(id); |
103 break; | 183 break; |
104 case VIDEO_CAPTURE_STATE_STOPPED: | 184 case VIDEO_CAPTURE_STATE_STOPPED: |
105 OnStopped(); | 185 OnStopped(id); |
106 break; | 186 break; |
107 default: | 187 default: |
108 NOTREACHED(); | 188 NOTREACHED(); |
109 } | 189 } |
110 } | 190 } |
111 | 191 |
112 base::Closure StartCapture(const media::VideoCaptureParams& params) { | 192 base::Closure StartCapture(media::VideoCaptureSessionId id, |
| 193 const media::VideoCaptureParams& params) { |
113 return manager_->StartCapture( | 194 return manager_->StartCapture( |
114 0, params, base::Bind(&VideoCaptureImplManagerTest::OnStateUpdate, | 195 id, params, base::Bind(&VideoCaptureImplManagerTest::OnStateUpdate, |
115 base::Unretained(this)), | 196 base::Unretained(this), id), |
116 base::Bind(&VideoCaptureImplManagerTest::OnFrameReady, | 197 base::Bind(&VideoCaptureImplManagerTest::OnFrameReady, |
117 base::Unretained(this))); | 198 base::Unretained(this))); |
118 } | 199 } |
119 | 200 |
120 base::MessageLoop message_loop_; | 201 base::MessageLoop message_loop_; |
121 std::unique_ptr<ChildProcess> child_process_; | 202 std::unique_ptr<ChildProcess> child_process_; |
122 media::VideoCaptureParams params_; | 203 media::VideoCaptureParams params_; |
123 base::RunLoop cleanup_run_loop_; | 204 base::RunLoop cleanup_run_loop_; |
124 std::unique_ptr<MockVideoCaptureImplManager> manager_; | 205 std::unique_ptr<MockVideoCaptureImplManager> manager_; |
125 | 206 |
126 private: | 207 private: |
127 DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManagerTest); | 208 DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManagerTest); |
128 }; | 209 }; |
129 | 210 |
130 // Multiple clients with the same session id. There is only one | 211 // Multiple clients with the same session id. There is only one |
131 // media::VideoCapture object. | 212 // media::VideoCapture object. |
132 TEST_F(VideoCaptureImplManagerTest, MultipleClients) { | 213 TEST_F(VideoCaptureImplManagerTest, MultipleClients) { |
133 base::Closure release_cb1 = manager_->UseDevice(0); | 214 std::array<base::Closure, kNumClients> release_callbacks; |
134 base::Closure release_cb2 = manager_->UseDevice(0); | 215 for (size_t i = 0; i < kNumClients; ++i) |
135 base::Closure stop_cb1, stop_cb2; | 216 release_callbacks[i] = manager_->UseDevice(0); |
136 { | 217 std::array<base::Closure, kNumClients> stop_callbacks = |
137 base::RunLoop run_loop; | 218 StartCaptureForAllClients(true); |
138 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); | 219 StopCaptureForAllClients(&stop_callbacks); |
139 EXPECT_CALL(*this, OnStarted()).WillOnce(RunClosure(quit_closure)); | 220 for (size_t i = 0; i < kNumClients; ++i) |
140 EXPECT_CALL(*this, OnStarted()).RetiresOnSaturation(); | 221 release_callbacks[i].Run(); |
141 stop_cb1 = StartCapture(params_); | |
142 stop_cb2 = StartCapture(params_); | |
143 FakeChannelSetup(); | |
144 run_loop.Run(); | |
145 } | |
146 | |
147 { | |
148 base::RunLoop run_loop; | |
149 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); | |
150 EXPECT_CALL(*this, OnStopped()).WillOnce(RunClosure(quit_closure)); | |
151 EXPECT_CALL(*this, OnStopped()).RetiresOnSaturation(); | |
152 stop_cb1.Run(); | |
153 stop_cb2.Run(); | |
154 run_loop.Run(); | |
155 } | |
156 | |
157 release_cb1.Run(); | |
158 release_cb2.Run(); | |
159 cleanup_run_loop_.Run(); | 222 cleanup_run_loop_.Run(); |
160 } | 223 } |
161 | 224 |
162 TEST_F(VideoCaptureImplManagerTest, NoLeak) { | 225 TEST_F(VideoCaptureImplManagerTest, NoLeak) { |
163 manager_->UseDevice(0).Reset(); | 226 manager_->UseDevice(0).Reset(); |
164 manager_.reset(); | 227 manager_.reset(); |
165 cleanup_run_loop_.Run(); | 228 cleanup_run_loop_.Run(); |
166 } | 229 } |
167 | 230 |
| 231 TEST_F(VideoCaptureImplManagerTest, SuspendAndResumeSessions) { |
| 232 std::array<base::Closure, kNumClients> release_callbacks; |
| 233 for (size_t i = 0; i < kNumClients; ++i) { |
| 234 release_callbacks[i] = |
| 235 manager_->UseDevice(static_cast<media::VideoCaptureSessionId>(i)); |
| 236 } |
| 237 std::array<base::Closure, kNumClients> stop_callbacks = |
| 238 StartCaptureForAllClients(false); |
| 239 |
| 240 // Call SuspendDevices(true) to suspend all clients, and expect all to be |
| 241 // paused. |
| 242 { |
| 243 base::RunLoop run_loop; |
| 244 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 245 EXPECT_CALL(*this, OnPaused(0)).Times(1).RetiresOnSaturation(); |
| 246 EXPECT_CALL(*this, OnPaused(1)).Times(1).RetiresOnSaturation(); |
| 247 EXPECT_CALL(*this, OnPaused(2)).WillOnce(RunClosure(quit_closure)) |
| 248 .RetiresOnSaturation(); |
| 249 manager_->SuspendDevices(true); |
| 250 run_loop.Run(); |
| 251 } |
| 252 |
| 253 // Call SuspendDevices(false) and expect all to be resumed. |
| 254 { |
| 255 base::RunLoop run_loop; |
| 256 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 257 EXPECT_CALL(*this, OnResumed(0)).Times(1).RetiresOnSaturation(); |
| 258 EXPECT_CALL(*this, OnResumed(1)).Times(1).RetiresOnSaturation(); |
| 259 EXPECT_CALL(*this, OnResumed(2)).WillOnce(RunClosure(quit_closure)) |
| 260 .RetiresOnSaturation(); |
| 261 manager_->SuspendDevices(false); |
| 262 run_loop.Run(); |
| 263 } |
| 264 |
| 265 // Suspend just the first client and expect just the first client to be |
| 266 // paused. |
| 267 { |
| 268 base::RunLoop run_loop; |
| 269 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 270 EXPECT_CALL(*this, OnPaused(0)).WillOnce(RunClosure(quit_closure)) |
| 271 .RetiresOnSaturation(); |
| 272 manager_->Suspend(0); |
| 273 run_loop.Run(); |
| 274 } |
| 275 |
| 276 // Now call SuspendDevices(true) again, and expect just the second and third |
| 277 // clients to be paused. |
| 278 { |
| 279 base::RunLoop run_loop; |
| 280 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 281 EXPECT_CALL(*this, OnPaused(1)).Times(1).RetiresOnSaturation(); |
| 282 EXPECT_CALL(*this, OnPaused(2)).WillOnce(RunClosure(quit_closure)) |
| 283 .RetiresOnSaturation(); |
| 284 manager_->SuspendDevices(true); |
| 285 run_loop.Run(); |
| 286 } |
| 287 |
| 288 // Resume just the first client, but it should not resume because all devices |
| 289 // are supposed to be suspended. |
| 290 { |
| 291 manager_->Resume(0); |
| 292 base::RunLoop().RunUntilIdle(); |
| 293 } |
| 294 |
| 295 // Now, call SuspendDevices(false) and expect all to be resumed. |
| 296 { |
| 297 base::RunLoop run_loop; |
| 298 base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); |
| 299 EXPECT_CALL(*this, OnResumed(0)).Times(1).RetiresOnSaturation(); |
| 300 EXPECT_CALL(*this, OnResumed(1)).Times(1).RetiresOnSaturation(); |
| 301 EXPECT_CALL(*this, OnResumed(2)).WillOnce(RunClosure(quit_closure)) |
| 302 .RetiresOnSaturation(); |
| 303 manager_->SuspendDevices(false); |
| 304 run_loop.Run(); |
| 305 } |
| 306 |
| 307 StopCaptureForAllClients(&stop_callbacks); |
| 308 for (size_t i = 0; i < kNumClients; ++i) |
| 309 release_callbacks[i].Run(); |
| 310 cleanup_run_loop_.Run(); |
| 311 } |
| 312 |
168 } // namespace content | 313 } // namespace content |
OLD | NEW |