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"
25 class MockVideoCapturerDelegate
: public VideoCapturerDelegate
{
27 explicit MockVideoCapturerDelegate(const StreamDeviceInfo
& device_info
)
28 : VideoCapturerDelegate(device_info
) {}
32 void(const media::VideoCaptureParams
& params
,
33 const VideoCaptureDeliverFrameCB
& new_frame_callback
,
34 scoped_refptr
<base::SingleThreadTaskRunner
>
35 frame_callback_task_runner
,
36 const RunningCallback
& running_callback
));
37 MOCK_METHOD0(StopCapture
, void());
40 class MediaStreamVideoCapturerSourceTest
: public testing::Test
{
42 MediaStreamVideoCapturerSourceTest()
43 : child_process_(new ChildProcess()),
46 source_stopped_(false) {
49 void TearDown() override
{
50 webkit_source_
.reset();
51 blink::WebHeap::collectAllGarbageForTesting();
54 void InitWithDeviceInfo(const StreamDeviceInfo
& device_info
) {
55 scoped_ptr
<MockVideoCapturerDelegate
> delegate(
56 new MockVideoCapturerDelegate(device_info
));
57 delegate_
= delegate
.get();
58 source_
= new MediaStreamVideoCapturerSource(
59 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped
,
60 base::Unretained(this)),
62 source_
->SetDeviceInfo(device_info
);
64 webkit_source_
.initialize(base::UTF8ToUTF16("dummy_source_id"),
65 blink::WebMediaStreamSource::TypeVideo
,
66 base::UTF8ToUTF16("dummy_source_name"),
67 false /* remote */ , true /* readonly */);
68 webkit_source_
.setExtraData(source_
);
69 webkit_source_id_
= webkit_source_
.id();
72 MockMediaConstraintFactory
* constraint_factory() {
73 return &constraint_factory_
;
76 blink::WebMediaStreamTrack
StartSource() {
78 // CreateVideoTrack will trigger OnConstraintsApplied.
79 return MediaStreamVideoTrack::CreateVideoTrack(
80 source_
, constraint_factory_
.CreateWebMediaConstraints(),
82 &MediaStreamVideoCapturerSourceTest::OnConstraintsApplied
,
83 base::Unretained(this)),
87 MockVideoCapturerDelegate
& mock_delegate() {
91 void OnSourceStopped(const blink::WebMediaStreamSource
& source
) {
92 source_stopped_
= true;
93 EXPECT_EQ(source
.id(), webkit_source_id_
);
97 void OnConstraintsApplied(MediaStreamSource
* source
,
98 MediaStreamRequestResult result
,
99 const blink::WebString
& result_name
) {
102 base::MessageLoopForUI message_loop_
;
103 scoped_ptr
<ChildProcess
> child_process_
;
104 blink::WebMediaStreamSource webkit_source_
;
105 MediaStreamVideoCapturerSource
* source_
; // owned by |webkit_source_|.
106 MockVideoCapturerDelegate
* delegate_
; // owned by |source|.
107 blink::WebString webkit_source_id_
;
108 bool source_stopped_
;
109 MockMediaConstraintFactory constraint_factory_
;
112 TEST_F(MediaStreamVideoCapturerSourceTest
, TabCaptureFixedResolutionByDefault
) {
113 StreamDeviceInfo device_info
;
114 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
115 InitWithDeviceInfo(device_info
);
117 // No constraints are being provided to the implementation, so expect only
119 media::VideoCaptureParams expected_params
;
120 expected_params
.requested_format
.frame_size
.SetSize(
121 MediaStreamVideoSource::kDefaultWidth
,
122 MediaStreamVideoSource::kDefaultHeight
);
123 expected_params
.requested_format
.frame_rate
=
124 MediaStreamVideoSource::kDefaultFrameRate
;
125 expected_params
.requested_format
.pixel_format
=
126 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
127 expected_params
.resolution_change_policy
=
128 media::RESOLUTION_POLICY_FIXED_RESOLUTION
;
130 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
, _
)).Times(1);
131 blink::WebMediaStreamTrack track
= StartSource();
132 // When the track goes out of scope, the source will be stopped.
133 EXPECT_CALL(mock_delegate(), StopCapture());
136 TEST_F(MediaStreamVideoCapturerSourceTest
,
137 DesktopCaptureAllowAnyResolutionChangeByDefault
) {
138 StreamDeviceInfo device_info
;
139 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
140 InitWithDeviceInfo(device_info
);
142 // No constraints are being provided to the implementation, so expect only
144 media::VideoCaptureParams expected_params
;
145 expected_params
.requested_format
.frame_size
.SetSize(
146 MediaStreamVideoSource::kDefaultWidth
,
147 MediaStreamVideoSource::kDefaultHeight
);
148 expected_params
.requested_format
.frame_rate
=
149 MediaStreamVideoSource::kDefaultFrameRate
;
150 expected_params
.requested_format
.pixel_format
=
151 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
152 expected_params
.resolution_change_policy
=
153 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
155 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
, _
)).Times(1);
156 blink::WebMediaStreamTrack track
= StartSource();
157 // When the track goes out of scope, the source will be stopped.
158 EXPECT_CALL(mock_delegate(), StopCapture());
161 TEST_F(MediaStreamVideoCapturerSourceTest
,
162 TabCaptureConstraintsImplyFixedAspectRatio
) {
163 StreamDeviceInfo device_info
;
164 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
165 InitWithDeviceInfo(device_info
);
167 // Specify max and min size constraints that have the same ~16:9 aspect ratio.
168 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxWidth
, 1920);
169 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxHeight
, 1080);
170 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinWidth
, 854);
171 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinHeight
, 480);
172 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxFrameRate
,
175 media::VideoCaptureParams expected_params
;
176 expected_params
.requested_format
.frame_size
.SetSize(1920, 1080);
177 expected_params
.requested_format
.frame_rate
= 60.0;
178 expected_params
.requested_format
.pixel_format
=
179 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
180 expected_params
.resolution_change_policy
=
181 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
;
186 testing::Field(&media::VideoCaptureParams::resolution_change_policy
,
187 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
),
190 blink::WebMediaStreamTrack track
= StartSource();
191 // When the track goes out of scope, the source will be stopped.
192 EXPECT_CALL(mock_delegate(), StopCapture());
195 TEST_F(MediaStreamVideoCapturerSourceTest
,
196 TabCaptureConstraintsImplyAllowingAnyResolutionChange
) {
197 StreamDeviceInfo device_info
;
198 device_info
.device
.type
= MEDIA_TAB_VIDEO_CAPTURE
;
199 InitWithDeviceInfo(device_info
);
201 // Specify max and min size constraints with different aspect ratios.
202 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxWidth
, 1920);
203 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxHeight
, 1080);
204 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinWidth
, 0);
205 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMinHeight
, 0);
206 constraint_factory()->AddMandatory(MediaStreamVideoSource::kMaxFrameRate
,
209 media::VideoCaptureParams expected_params
;
210 expected_params
.requested_format
.frame_size
.SetSize(1920, 1080);
211 expected_params
.requested_format
.frame_rate
= 60.0;
212 expected_params
.requested_format
.pixel_format
=
213 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
214 expected_params
.resolution_change_policy
=
215 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
217 EXPECT_CALL(mock_delegate(), StartCapture(expected_params
, _
, _
, _
)).Times(1);
218 blink::WebMediaStreamTrack track
= StartSource();
219 // When the track goes out of scope, the source will be stopped.
220 EXPECT_CALL(mock_delegate(), StopCapture());
223 TEST_F(MediaStreamVideoCapturerSourceTest
, Ended
) {
224 StreamDeviceInfo device_info
;
225 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
226 scoped_ptr
<VideoCapturerDelegate
> delegate(
227 new VideoCapturerDelegate(device_info
));
228 VideoCapturerDelegate
* delegate_ptr
= delegate
.get();
229 source_
= new MediaStreamVideoCapturerSource(
230 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped
,
231 base::Unretained(this)),
233 source_
->SetDeviceInfo(device_info
);
234 webkit_source_
.initialize(base::UTF8ToUTF16("dummy_source_id"),
235 blink::WebMediaStreamSource::TypeVideo
,
236 base::UTF8ToUTF16("dummy_source_name"),
237 false /* remote */ , true /* readonly */);
238 webkit_source_
.setExtraData(source_
);
239 webkit_source_id_
= webkit_source_
.id();
240 blink::WebMediaStreamTrack track
= StartSource();
241 message_loop_
.RunUntilIdle();
243 delegate_ptr
->OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED
);
244 message_loop_
.RunUntilIdle();
245 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive
,
246 webkit_source_
.readyState());
248 EXPECT_FALSE(source_stopped_
);
249 delegate_ptr
->OnStateUpdate(VIDEO_CAPTURE_STATE_ERROR
);
250 message_loop_
.RunUntilIdle();
251 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded
,
252 webkit_source_
.readyState());
253 // Verify that MediaStreamSource::SourceStoppedCallback has been triggered.
254 EXPECT_TRUE(source_stopped_
);
257 class FakeMediaStreamVideoSink
: public MediaStreamVideoSink
{
259 FakeMediaStreamVideoSink(base::TimeTicks
* capture_time
,
260 media::VideoFrameMetadata
* metadata
,
261 base::Closure got_frame_cb
)
262 : capture_time_(capture_time
),
264 got_frame_cb_(got_frame_cb
) {
267 void OnVideoFrame(const scoped_refptr
<media::VideoFrame
>& frame
,
268 const base::TimeTicks
& capture_time
) {
269 *capture_time_
= capture_time
;
271 base::DictionaryValue tmp
;
272 frame
->metadata()->MergeInternalValuesInto(&tmp
);
273 metadata_
->MergeInternalValuesFrom(tmp
);
274 base::ResetAndReturn(&got_frame_cb_
).Run();
278 base::TimeTicks
* const capture_time_
;
279 media::VideoFrameMetadata
* const metadata_
;
280 base::Closure got_frame_cb_
;
283 TEST_F(MediaStreamVideoCapturerSourceTest
, CaptureTimeAndMetadataPlumbing
) {
284 StreamDeviceInfo device_info
;
285 device_info
.device
.type
= MEDIA_DESKTOP_VIDEO_CAPTURE
;
286 InitWithDeviceInfo(device_info
);
288 VideoCaptureDeliverFrameCB deliver_frame_cb
;
289 VideoCapturerDelegate::RunningCallback running_cb
;
291 EXPECT_CALL(mock_delegate(), StartCapture(_
, _
, _
, _
))
293 .WillOnce(testing::DoAll(testing::SaveArg
<1>(&deliver_frame_cb
),
294 testing::SaveArg
<3>(&running_cb
)));
295 EXPECT_CALL(mock_delegate(), StopCapture());
296 blink::WebMediaStreamTrack track
= StartSource();
297 running_cb
.Run(true);
299 base::RunLoop run_loop
;
300 base::TimeTicks reference_capture_time
=
301 base::TimeTicks::FromInternalValue(60013);
302 base::TimeTicks capture_time
;
303 media::VideoFrameMetadata metadata
;
304 FakeMediaStreamVideoSink
fake_sink(
307 media::BindToCurrentLoop(run_loop
.QuitClosure()));
308 FakeMediaStreamVideoSink::AddToVideoTrack(
310 base::Bind(&FakeMediaStreamVideoSink::OnVideoFrame
,
311 base::Unretained(&fake_sink
)),
313 const scoped_refptr
<media::VideoFrame
> frame
=
314 media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
315 frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
, 30.0);
316 child_process_
->io_task_runner()->PostTask(
317 FROM_HERE
, base::Bind(deliver_frame_cb
, frame
, reference_capture_time
));
319 FakeMediaStreamVideoSink::RemoveFromVideoTrack(&fake_sink
, track
);
320 EXPECT_EQ(reference_capture_time
, capture_time
);
321 double metadata_value
;
322 EXPECT_TRUE(metadata
.GetDouble(media::VideoFrameMetadata::FRAME_RATE
,
324 EXPECT_EQ(30.0, metadata_value
);
327 } // namespace content