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"
23 class MockVideoCapturerDelegate
: public VideoCapturerDelegate
{
25 explicit MockVideoCapturerDelegate(const StreamDeviceInfo
& device_info
)
26 : VideoCapturerDelegate(device_info
) {}
30 void(const media::VideoCaptureParams
& params
,
31 const VideoCaptureDeliverFrameCB
& new_frame_callback
,
32 scoped_refptr
<base::SingleThreadTaskRunner
>
33 frame_callback_task_runner
,
34 const RunningCallback
& running_callback
));
35 MOCK_METHOD0(StopCapture
, void());
38 class MediaStreamVideoCapturerSourceTest
: public testing::Test
{
40 MediaStreamVideoCapturerSourceTest()
41 : child_process_(new ChildProcess()),
44 source_stopped_(false) {
47 void TearDown() override
{
48 webkit_source_
.reset();
49 blink::WebHeap::collectAllGarbageForTesting();
52 void InitWithDeviceInfo(const StreamDeviceInfo
& device_info
) {
53 scoped_ptr
<MockVideoCapturerDelegate
> delegate(
54 new MockVideoCapturerDelegate(device_info
));
55 delegate_
= delegate
.get();
56 source_
= new MediaStreamVideoCapturerSource(
57 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped
,
58 base::Unretained(this)),
60 source_
->SetDeviceInfo(device_info
);
62 webkit_source_
.initialize(base::UTF8ToUTF16("dummy_source_id"),
63 blink::WebMediaStreamSource::TypeVideo
,
64 base::UTF8ToUTF16("dummy_source_name"),
65 false /* remote */ , true /* readonly */);
66 webkit_source_
.setExtraData(source_
);
67 webkit_source_id_
= webkit_source_
.id();
70 MockMediaConstraintFactory
* constraint_factory() {
71 return &constraint_factory_
;
74 blink::WebMediaStreamTrack
StartSource() {
76 // CreateVideoTrack will trigger OnConstraintsApplied.
77 return MediaStreamVideoTrack::CreateVideoTrack(
78 source_
, constraint_factory_
.CreateWebMediaConstraints(),
80 &MediaStreamVideoCapturerSourceTest::OnConstraintsApplied
,
81 base::Unretained(this)),
85 MockVideoCapturerDelegate
& mock_delegate() {
89 void OnSourceStopped(const blink::WebMediaStreamSource
& source
) {
90 source_stopped_
= true;
91 EXPECT_EQ(source
.id(), webkit_source_id_
);
95 void OnConstraintsApplied(MediaStreamSource
* source
,
96 MediaStreamRequestResult result
,
97 const blink::WebString
& result_name
) {
100 base::MessageLoopForUI message_loop_
;
101 scoped_ptr
<ChildProcess
> child_process_
;
102 blink::WebMediaStreamSource webkit_source_
;
103 MediaStreamVideoCapturerSource
* source_
; // owned by |webkit_source_|.
104 MockVideoCapturerDelegate
* delegate_
; // owned by |source|.
105 blink::WebString webkit_source_id_
;
106 bool source_stopped_
;
107 MockMediaConstraintFactory constraint_factory_
;
110 TEST_F(MediaStreamVideoCapturerSourceTest
, TabCaptureFixedResolutionByDefault
) {
111 StreamDeviceInfo device_info
;
112 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
113 InitWithDeviceInfo(device_info
);
115 // No constraints are being provided to the implementation, so expect only
117 media::VideoCaptureParams expected_params
;
118 expected_params
.requested_format
.frame_size
.SetSize(
119 MediaStreamVideoSource::kDefaultWidth
,
120 MediaStreamVideoSource::kDefaultHeight
);
121 expected_params
.requested_format
.frame_rate
=
122 MediaStreamVideoSource::kDefaultFrameRate
;
123 expected_params
.requested_format
.pixel_format
=
124 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
125 expected_params
.resolution_change_policy
=
126 media::RESOLUTION_POLICY_FIXED_RESOLUTION
;
128 EXPECT_CALL(mock_delegate(), StartCapture(
132 testing::_
)).Times(1);
133 blink::WebMediaStreamTrack track
= StartSource();
134 // When the track goes out of scope, the source will be stopped.
135 EXPECT_CALL(mock_delegate(), StopCapture());
138 TEST_F(MediaStreamVideoCapturerSourceTest
,
139 DesktopCaptureAllowAnyResolutionChangeByDefault
) {
140 StreamDeviceInfo device_info
;
141 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
142 InitWithDeviceInfo(device_info
);
144 // No constraints are being provided to the implementation, so expect only
146 media::VideoCaptureParams expected_params
;
147 expected_params
.requested_format
.frame_size
.SetSize(
148 MediaStreamVideoSource::kDefaultWidth
,
149 MediaStreamVideoSource::kDefaultHeight
);
150 expected_params
.requested_format
.frame_rate
=
151 MediaStreamVideoSource::kDefaultFrameRate
;
152 expected_params
.requested_format
.pixel_format
=
153 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
154 expected_params
.resolution_change_policy
=
155 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
157 EXPECT_CALL(mock_delegate(), StartCapture(
161 testing::_
)).Times(1);
162 blink::WebMediaStreamTrack track
= StartSource();
163 // When the track goes out of scope, the source will be stopped.
164 EXPECT_CALL(mock_delegate(), StopCapture());
167 TEST_F(MediaStreamVideoCapturerSourceTest
,
168 TabCaptureConstraintsImplyFixedAspectRatio
) {
169 StreamDeviceInfo device_info
;
170 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
171 InitWithDeviceInfo(device_info
);
173 // Specify max and min size constraints that have the same ~16:9 aspect ratio.
174 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxWidth
, 1920);
175 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxHeight
, 1080);
176 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinWidth
, 854);
177 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinHeight
, 480);
178 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxFrameRate
,
181 media::VideoCaptureParams expected_params
;
182 expected_params
.requested_format
.frame_size
.SetSize(1920, 1080);
183 expected_params
.requested_format
.frame_rate
= 60.0;
184 expected_params
.requested_format
.pixel_format
=
185 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
186 expected_params
.resolution_change_policy
=
187 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
;
189 EXPECT_CALL(mock_delegate(), StartCapture(
190 testing::Field(&media::VideoCaptureParams::resolution_change_policy
,
191 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
),
194 testing::_
)).Times(1);
195 blink::WebMediaStreamTrack track
= StartSource();
196 // When the track goes out of scope, the source will be stopped.
197 EXPECT_CALL(mock_delegate(), StopCapture());
200 TEST_F(MediaStreamVideoCapturerSourceTest
,
201 TabCaptureConstraintsImplyAllowingAnyResolutionChange
) {
202 StreamDeviceInfo device_info
;
203 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
204 InitWithDeviceInfo(device_info
);
206 // Specify max and min size constraints with different aspect ratios.
207 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxWidth
, 1920);
208 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxHeight
, 1080);
209 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinWidth
, 0);
210 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinHeight
, 0);
211 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxFrameRate
,
214 media::VideoCaptureParams expected_params
;
215 expected_params
.requested_format
.frame_size
.SetSize(1920, 1080);
216 expected_params
.requested_format
.frame_rate
= 60.0;
217 expected_params
.requested_format
.pixel_format
=
218 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
219 expected_params
.resolution_change_policy
=
220 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
222 EXPECT_CALL(mock_delegate(), StartCapture(
226 testing::_
)).Times(1);
227 blink::WebMediaStreamTrack track
= StartSource();
228 // When the track goes out of scope, the source will be stopped.
229 EXPECT_CALL(mock_delegate(), StopCapture());
232 TEST_F(MediaStreamVideoCapturerSourceTest
, Ended
) {
233 StreamDeviceInfo device_info
;
234 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
235 scoped_ptr
<VideoCapturerDelegate
> delegate(
236 new VideoCapturerDelegate(device_info
));
237 VideoCapturerDelegate
* delegate_ptr
= delegate
.get();
238 source_
= new MediaStreamVideoCapturerSource(
239 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped
,
240 base::Unretained(this)),
242 source_
->SetDeviceInfo(device_info
);
243 webkit_source_
.initialize(base::UTF8ToUTF16("dummy_source_id"),
244 blink::WebMediaStreamSource::TypeVideo
,
245 base::UTF8ToUTF16("dummy_source_name"),
246 false /* remote */ , true /* readonly */);
247 webkit_source_
.setExtraData(source_
);
248 webkit_source_id_
= webkit_source_
.id();
249 blink::WebMediaStreamTrack track
= StartSource();
250 message_loop_
.RunUntilIdle();
252 delegate_ptr
->OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED
);
253 message_loop_
.RunUntilIdle();
254 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive
,
255 webkit_source_
.readyState());
257 EXPECT_FALSE(source_stopped_
);
258 delegate_ptr
->OnStateUpdate(VIDEO_CAPTURE_STATE_ERROR
);
259 message_loop_
.RunUntilIdle();
260 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded
,
261 webkit_source_
.readyState());
262 // Verify that MediaStreamSource::SourceStoppedCallback has been triggered.
263 EXPECT_TRUE(source_stopped_
);
266 class FakeMediaStreamVideoSink
: public MediaStreamVideoSink
{
268 FakeMediaStreamVideoSink(base::TimeTicks
* capture_time
,
269 media::VideoFrameMetadata
* metadata
,
270 base::Closure got_frame_cb
)
271 : capture_time_(capture_time
),
273 got_frame_cb_(got_frame_cb
) {
276 void OnVideoFrame(const scoped_refptr
<media::VideoFrame
>& frame
,
277 const base::TimeTicks
& capture_time
) {
278 *capture_time_
= capture_time
;
280 base::DictionaryValue tmp
;
281 frame
->metadata()->MergeInternalValuesInto(&tmp
);
282 metadata_
->MergeInternalValuesFrom(tmp
);
283 base::ResetAndReturn(&got_frame_cb_
).Run();
287 base::TimeTicks
* const capture_time_
;
288 media::VideoFrameMetadata
* const metadata_
;
289 base::Closure got_frame_cb_
;
292 TEST_F(MediaStreamVideoCapturerSourceTest
, CaptureTimeAndMetadataPlumbing
) {
293 StreamDeviceInfo device_info
;
294 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
295 InitWithDeviceInfo(device_info
);
297 VideoCaptureDeliverFrameCB deliver_frame_cb
;
298 VideoCapturerDelegate::RunningCallback running_cb
;
300 EXPECT_CALL(mock_delegate(), StartCapture(
306 .WillOnce(testing::DoAll(testing::SaveArg
<1>(&deliver_frame_cb
),
307 testing::SaveArg
<3>(&running_cb
)));
308 EXPECT_CALL(mock_delegate(), StopCapture());
309 blink::WebMediaStreamTrack track
= StartSource();
310 running_cb
.Run(true);
312 base::RunLoop run_loop
;
313 base::TimeTicks reference_capture_time
=
314 base::TimeTicks::FromInternalValue(60013);
315 base::TimeTicks capture_time
;
316 media::VideoFrameMetadata metadata
;
317 FakeMediaStreamVideoSink
fake_sink(
320 media::BindToCurrentLoop(run_loop
.QuitClosure()));
321 FakeMediaStreamVideoSink::AddToVideoTrack(
323 base::Bind(&FakeMediaStreamVideoSink::OnVideoFrame
,
324 base::Unretained(&fake_sink
)),
326 const scoped_refptr
<media::VideoFrame
> frame
=
327 media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
328 frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
, 30.0);
329 child_process_
->io_task_runner()->PostTask(
330 FROM_HERE
, base::Bind(deliver_frame_cb
, frame
, reference_capture_time
));
332 FakeMediaStreamVideoSink::RemoveFromVideoTrack(&fake_sink
, track
);
333 EXPECT_EQ(reference_capture_time
, capture_time
);
334 double metadata_value
;
335 EXPECT_TRUE(metadata
.GetDouble(media::VideoFrameMetadata::FRAME_RATE
,
337 EXPECT_EQ(30.0, metadata_value
);
340 } // namespace content