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
3 // found in the LICENSE file.
6 #include "base/callback_helpers.h"
7 #include "base/location.h"
8 #include "base/run_loop.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/child/child_process.h"
12 #include "content/public/renderer/media_stream_video_sink.h"
13 #include "content/renderer/media/media_stream_video_capturer_source.h"
14 #include "content/renderer/media/media_stream_video_track.h"
15 #include "content/renderer/media/mock_media_constraint_factory.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/WebKit/public/web/WebHeap.h"
22 using ::testing::InSequence
;
23 using ::testing::Invoke
;
24 using ::testing::WithArgs
;
28 class MockVideoCapturerSource
: public media::VideoCapturerSource
{
30 MockVideoCapturerSource() {
31 ON_CALL(*this, GetCurrentSupportedFormats(_
, _
, _
, _
))
32 .WillByDefault(WithArgs
<3>(
33 Invoke(this, &MockVideoCapturerSource::EnumerateDeviceFormats
)));
36 MOCK_METHOD4(GetCurrentSupportedFormats
,
37 void(int max_requested_width
,
38 int max_requested_height
,
39 double max_requested_frame_rate
,
40 const VideoCaptureDeviceFormatsCB
& callback
));
41 MOCK_METHOD3(StartCapture
,
42 void(const media::VideoCaptureParams
& params
,
43 const VideoCaptureDeliverFrameCB
& new_frame_callback
,
44 const RunningCallback
& running_callback
));
45 MOCK_METHOD0(StopCapture
, void());
47 void EnumerateDeviceFormats(const VideoCaptureDeviceFormatsCB
& callback
) {
48 media::VideoCaptureFormat
kFormatSmall(gfx::Size(640, 480), 30.0,
49 media::PIXEL_FORMAT_I420
);
50 media::VideoCaptureFormat
kFormatLarge(gfx::Size(1920, 1080), 30.0,
51 media::PIXEL_FORMAT_I420
);
52 media::VideoCaptureFormats formats
;
53 formats
.push_back(kFormatSmall
);
54 formats
.push_back(kFormatLarge
);
55 callback
.Run(formats
);
60 class MediaStreamVideoCapturerSourceTest
: public testing::Test
{
62 MediaStreamVideoCapturerSourceTest()
63 : child_process_(new ChildProcess()),
66 source_stopped_(false) {}
68 void TearDown() override
{
69 webkit_source_
.reset();
70 blink::WebHeap::collectAllGarbageForTesting();
73 void InitWithDeviceInfo(const StreamDeviceInfo
& device_info
) {
74 scoped_ptr
<MockVideoCapturerSource
> delegate(new MockVideoCapturerSource());
75 delegate_
= delegate
.get();
76 source_
= new MediaStreamVideoCapturerSource(
77 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped
,
78 base::Unretained(this)),
80 source_
->SetDeviceInfo(device_info
);
82 webkit_source_
.initialize(base::UTF8ToUTF16("dummy_source_id"),
83 blink::WebMediaStreamSource::TypeVideo
,
84 base::UTF8ToUTF16("dummy_source_name"),
87 webkit_source_
.setExtraData(source_
);
88 webkit_source_id_
= webkit_source_
.id();
91 MockMediaConstraintFactory
* constraint_factory() {
92 return &constraint_factory_
;
95 blink::WebMediaStreamTrack
StartSource() {
97 // CreateVideoTrack will trigger OnConstraintsApplied.
98 return MediaStreamVideoTrack::CreateVideoTrack(
99 source_
, constraint_factory_
.CreateWebMediaConstraints(),
100 base::Bind(&MediaStreamVideoCapturerSourceTest::OnConstraintsApplied
,
101 base::Unretained(this)),
105 MockVideoCapturerSource
& mock_delegate() { return *delegate_
; }
107 const char* GetPowerLineFrequencyForTesting() const {
108 return source_
->GetPowerLineFrequencyForTesting();
111 void OnSourceStopped(const blink::WebMediaStreamSource
& source
) {
112 source_stopped_
= true;
113 EXPECT_EQ(source
.id(), webkit_source_id_
);
115 void OnStarted(bool result
) { source_
->OnStarted(result
); }
118 void OnConstraintsApplied(MediaStreamSource
* source
,
119 MediaStreamRequestResult result
,
120 const blink::WebString
& result_name
) {}
122 // A ChildProcess and a MessageLoopForUI are both needed to fool the Tracks
123 // and Sources below into believing they are on the right threads.
124 base::MessageLoopForUI message_loop_
;
125 scoped_ptr
<ChildProcess
> child_process_
;
127 blink::WebMediaStreamSource webkit_source_
;
128 MediaStreamVideoCapturerSource
* source_
; // owned by |webkit_source_|.
129 MockVideoCapturerSource
* delegate_
; // owned by |source|.
130 blink::WebString webkit_source_id_
;
131 bool source_stopped_
;
132 MockMediaConstraintFactory constraint_factory_
;
135 TEST_F(MediaStreamVideoCapturerSourceTest
, TabCaptureFixedResolutionByDefault
) {
136 StreamDeviceInfo device_info
;
137 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
138 InitWithDeviceInfo(device_info
);
140 // No constraints are being provided to the implementation, so expect only
142 media::VideoCaptureParams expected_params
;
143 expected_params
.requested_format
.frame_size
.SetSize(
144 MediaStreamVideoSource::kDefaultWidth
,
145 MediaStreamVideoSource::kDefaultHeight
);
146 expected_params
.requested_format
.frame_rate
=
147 MediaStreamVideoSource::kDefaultFrameRate
;
148 expected_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
149 expected_params
.resolution_change_policy
=
150 media::RESOLUTION_POLICY_FIXED_RESOLUTION
;
153 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
154 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
));
155 blink::WebMediaStreamTrack track
= StartSource();
156 // When the track goes out of scope, the source will be stopped.
157 EXPECT_CALL(mock_delegate(), StopCapture());
160 TEST_F(MediaStreamVideoCapturerSourceTest
,
161 DesktopCaptureAllowAnyResolutionChangeByDefault
) {
162 StreamDeviceInfo device_info
;
163 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
164 InitWithDeviceInfo(device_info
);
166 // No constraints are being provided to the implementation, so expect only
168 media::VideoCaptureParams expected_params
;
169 expected_params
.requested_format
.frame_size
.SetSize(
170 MediaStreamVideoSource::kDefaultWidth
,
171 MediaStreamVideoSource::kDefaultHeight
);
172 expected_params
.requested_format
.frame_rate
=
173 MediaStreamVideoSource::kDefaultFrameRate
;
174 expected_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
175 expected_params
.resolution_change_policy
=
176 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
179 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
180 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
));
181 blink::WebMediaStreamTrack track
= StartSource();
182 // When the track goes out of scope, the source will be stopped.
183 EXPECT_CALL(mock_delegate(), StopCapture());
186 TEST_F(MediaStreamVideoCapturerSourceTest
,
187 TabCaptureConstraintsImplyFixedAspectRatio
) {
188 StreamDeviceInfo device_info
;
189 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
190 InitWithDeviceInfo(device_info
);
192 // Specify max and min size constraints that have the same ~16:9 aspect ratio.
193 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxWidth
, 1920);
194 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxHeight
, 1080);
195 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinWidth
, 854);
196 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinHeight
, 480);
197 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxFrameRate
,
200 media::VideoCaptureParams expected_params
;
201 expected_params
.requested_format
.frame_size
.SetSize(1920, 1080);
202 expected_params
.requested_format
.frame_rate
= 60.0;
203 expected_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
204 expected_params
.resolution_change_policy
=
205 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
;
208 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
212 testing::Field(&media::VideoCaptureParams::resolution_change_policy
,
213 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
),
216 blink::WebMediaStreamTrack track
= StartSource();
217 // When the track goes out of scope, the source will be stopped.
218 EXPECT_CALL(mock_delegate(), StopCapture());
221 TEST_F(MediaStreamVideoCapturerSourceTest
,
222 TabCaptureConstraintsImplyAllowingAnyResolutionChange
) {
223 StreamDeviceInfo device_info
;
224 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
225 InitWithDeviceInfo(device_info
);
227 // Specify max and min size constraints with different aspect ratios.
228 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxWidth
, 1920);
229 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxHeight
, 1080);
230 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinWidth
, 0);
231 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinHeight
, 0);
232 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxFrameRate
,
235 media::VideoCaptureParams expected_params
;
236 expected_params
.requested_format
.frame_size
.SetSize(1920, 1080);
237 expected_params
.requested_format
.frame_rate
= 60.0;
238 expected_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
239 expected_params
.resolution_change_policy
=
240 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
243 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
247 testing::Field(&media::VideoCaptureParams::resolution_change_policy
,
248 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
),
251 blink::WebMediaStreamTrack track
= StartSource();
252 // When the track goes out of scope, the source will be stopped.
253 EXPECT_CALL(mock_delegate(), StopCapture());
256 TEST_F(MediaStreamVideoCapturerSourceTest
,
257 DeviceCaptureConstraintsSupportPowerLineFrequency
) {
258 for (int frequency
= -100; frequency
< 100; ++frequency
) {
259 StreamDeviceInfo device_info
;
260 device_info
.device
.type
= MEDIA_DEVICE_VIDEO_CAPTURE
;
261 InitWithDeviceInfo(device_info
);
262 constraint_factory_
= MockMediaConstraintFactory();
264 constraint_factory()->AddOptional(GetPowerLineFrequencyForTesting(),
267 media::VideoCaptureParams expected_params
;
268 expected_params
.requested_format
.frame_size
.SetSize(
269 MediaStreamVideoSource::kDefaultWidth
,
270 MediaStreamVideoSource::kDefaultHeight
);
271 expected_params
.requested_format
.frame_rate
=
272 MediaStreamVideoSource::kDefaultFrameRate
;
273 expected_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
274 expected_params
.resolution_change_policy
=
275 media::RESOLUTION_POLICY_FIXED_RESOLUTION
;
276 if (frequency
== 50) {
277 expected_params
.power_line_frequency
=
278 media::PowerLineFrequency::FREQUENCY_50HZ
;
279 } else if (frequency
== 60) {
280 expected_params
.power_line_frequency
=
281 media::PowerLineFrequency::FREQUENCY_60HZ
;
283 expected_params
.power_line_frequency
=
284 media::PowerLineFrequency::FREQUENCY_DEFAULT
;
288 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
289 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
));
290 blink::WebMediaStreamTrack track
= StartSource();
291 // When the track goes out of scope, the source will be stopped.
292 EXPECT_CALL(mock_delegate(), StopCapture());
296 TEST_F(MediaStreamVideoCapturerSourceTest
,
297 InvalidPowerLineFrequencyHandledProperly
) {
298 // Test out other varieties of invalid input, like non-numeric strings.
299 StreamDeviceInfo device_info
;
300 device_info
.device
.type
= MEDIA_DEVICE_VIDEO_CAPTURE
;
301 InitWithDeviceInfo(device_info
);
302 constraint_factory_
= MockMediaConstraintFactory();
304 constraint_factory()->AddOptional(GetPowerLineFrequencyForTesting(),
305 std::string("this is not a frequency"));
307 media::VideoCaptureParams expected_params
;
308 expected_params
.requested_format
.frame_size
.SetSize(
309 MediaStreamVideoSource::kDefaultWidth
,
310 MediaStreamVideoSource::kDefaultHeight
);
311 expected_params
.requested_format
.frame_rate
=
312 MediaStreamVideoSource::kDefaultFrameRate
;
313 expected_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
314 expected_params
.resolution_change_policy
=
315 media::RESOLUTION_POLICY_FIXED_RESOLUTION
;
316 // Invalid frequencies should result in default setting.
317 expected_params
.power_line_frequency
=
318 media::PowerLineFrequency::FREQUENCY_DEFAULT
;
321 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
322 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
));
323 blink::WebMediaStreamTrack track
= StartSource();
324 // When the track goes out of scope, the source will be stopped.
325 EXPECT_CALL(mock_delegate(), StopCapture());
328 TEST_F(MediaStreamVideoCapturerSourceTest
, Ended
) {
329 scoped_ptr
<MockVideoCapturerSource
> delegate(new MockVideoCapturerSource());
330 delegate_
= delegate
.get();
331 source_
= new MediaStreamVideoCapturerSource(
332 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped
,
333 base::Unretained(this)),
335 webkit_source_
.initialize(base::UTF8ToUTF16("dummy_source_id"),
336 blink::WebMediaStreamSource::TypeVideo
,
337 base::UTF8ToUTF16("dummy_source_name"),
338 false /* remote */, true /* readonly */);
339 webkit_source_
.setExtraData(source_
);
340 webkit_source_id_
= webkit_source_
.id();
343 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
344 EXPECT_CALL(mock_delegate(), StartCapture(_
, _
, _
));
345 blink::WebMediaStreamTrack track
= StartSource();
346 message_loop_
.RunUntilIdle();
349 message_loop_
.RunUntilIdle();
350 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive
,
351 webkit_source_
.readyState());
353 EXPECT_FALSE(source_stopped_
);
355 EXPECT_CALL(mock_delegate(), StopCapture());
357 message_loop_
.RunUntilIdle();
358 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded
,
359 webkit_source_
.readyState());
360 // Verify that MediaStreamSource::SourceStoppedCallback has been triggered.
361 EXPECT_TRUE(source_stopped_
);
364 class FakeMediaStreamVideoSink
: public MediaStreamVideoSink
{
366 FakeMediaStreamVideoSink(base::TimeTicks
* capture_time
,
367 media::VideoFrameMetadata
* metadata
,
368 base::Closure got_frame_cb
)
369 : capture_time_(capture_time
),
371 got_frame_cb_(got_frame_cb
) {}
373 void OnVideoFrame(const scoped_refptr
<media::VideoFrame
>& frame
,
374 base::TimeTicks capture_time
) {
375 *capture_time_
= capture_time
;
377 base::DictionaryValue tmp
;
378 frame
->metadata()->MergeInternalValuesInto(&tmp
);
379 metadata_
->MergeInternalValuesFrom(tmp
);
380 base::ResetAndReturn(&got_frame_cb_
).Run();
384 base::TimeTicks
* const capture_time_
;
385 media::VideoFrameMetadata
* const metadata_
;
386 base::Closure got_frame_cb_
;
389 TEST_F(MediaStreamVideoCapturerSourceTest
, CaptureTimeAndMetadataPlumbing
) {
390 StreamDeviceInfo device_info
;
391 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
392 InitWithDeviceInfo(device_info
);
394 VideoCaptureDeliverFrameCB deliver_frame_cb
;
395 media::VideoCapturerSource::RunningCallback running_cb
;
398 EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_
, _
, _
, _
));
399 EXPECT_CALL(mock_delegate(), StartCapture(_
, _
, _
))
400 .WillOnce(testing::DoAll(testing::SaveArg
<1>(&deliver_frame_cb
),
401 testing::SaveArg
<2>(&running_cb
)));
402 EXPECT_CALL(mock_delegate(), StopCapture());
403 blink::WebMediaStreamTrack track
= StartSource();
404 running_cb
.Run(true);
406 base::RunLoop run_loop
;
407 base::TimeTicks reference_capture_time
=
408 base::TimeTicks::FromInternalValue(60013);
409 base::TimeTicks capture_time
;
410 media::VideoFrameMetadata metadata
;
411 FakeMediaStreamVideoSink
fake_sink(
412 &capture_time
, &metadata
,
413 media::BindToCurrentLoop(run_loop
.QuitClosure()));
414 FakeMediaStreamVideoSink::AddToVideoTrack(
415 &fake_sink
, base::Bind(&FakeMediaStreamVideoSink::OnVideoFrame
,
416 base::Unretained(&fake_sink
)),
418 const scoped_refptr
<media::VideoFrame
> frame
=
419 media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
420 frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
, 30.0);
421 child_process_
->io_task_runner()->PostTask(
422 FROM_HERE
, base::Bind(deliver_frame_cb
, frame
, reference_capture_time
));
424 FakeMediaStreamVideoSink::RemoveFromVideoTrack(&fake_sink
, track
);
425 EXPECT_EQ(reference_capture_time
, capture_time
);
426 double metadata_value
;
427 EXPECT_TRUE(metadata
.GetDouble(media::VideoFrameMetadata::FRAME_RATE
,
429 EXPECT_EQ(30.0, metadata_value
);
432 } // namespace content