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.
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/child/child_process.h"
14 #include "content/renderer/media/media_stream_video_source.h"
15 #include "content/renderer/media/media_stream_video_track.h"
16 #include "content/renderer/media/mock_media_constraint_factory.h"
17 #include "content/renderer/media/mock_media_stream_video_sink.h"
18 #include "content/renderer/media/mock_media_stream_video_source.h"
19 #include "media/base/video_frame.h"
20 #include "testing/gtest/include/gtest/gtest.h"
24 ACTION_P(RunClosure, closure) {
28 class MediaStreamVideoSourceTest
29 : public ::testing::Test {
31 MediaStreamVideoSourceTest()
32 : child_process_(new ChildProcess()),
33 number_of_successful_constraints_applied_(0),
34 number_of_failed_constraints_applied_(0),
35 mock_source_(new MockMediaStreamVideoSource(true)) {
36 media::VideoCaptureFormats formats;
37 formats.push_back(media::VideoCaptureFormat(
38 gfx::Size(1280, 720), 30, media::PIXEL_FORMAT_I420));
39 formats.push_back(media::VideoCaptureFormat(
40 gfx::Size(640, 480), 30, media::PIXEL_FORMAT_I420));
41 formats.push_back(media::VideoCaptureFormat(
42 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420));
43 formats.push_back(media::VideoCaptureFormat(
44 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420));
45 mock_source_->SetSupportedFormats(formats);
46 webkit_source_.initialize(base::UTF8ToUTF16("dummy_source_id"),
47 blink::WebMediaStreamSource::TypeVideo,
48 base::UTF8ToUTF16("dummy_source_name"));
49 webkit_source_.setExtraData(mock_source_);
53 // Create a track that's associated with |webkit_source_|.
54 blink::WebMediaStreamTrack CreateTrack(
55 const std::string& id,
56 const blink::WebMediaConstraints& constraints) {
58 return MediaStreamVideoTrack::CreateVideoTrack(
59 mock_source_, constraints,
61 &MediaStreamVideoSourceTest::OnConstraintsApplied,
62 base::Unretained(this)),
66 blink::WebMediaStreamTrack CreateTrackAndStartSource(
67 const blink::WebMediaConstraints& constraints,
70 int expected_frame_rate) {
71 blink::WebMediaStreamTrack track = CreateTrack("123", constraints);
73 mock_source_->CompleteGetSupportedFormats();
74 const media::VideoCaptureParams& format = mock_source()->start_params();
75 EXPECT_EQ(expected_width, format.requested_format.frame_size.width());
76 EXPECT_EQ(expected_height, format.requested_format.frame_size.height());
77 EXPECT_EQ(expected_frame_rate, format.requested_format.frame_rate);
79 EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
80 mock_source_->StartMockedSource();
81 // Once the source has started successfully we expect that the
82 // ConstraintsCallback in MediaStreamSource::AddTrack completes.
83 EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
87 int NumberOfSuccessConstraintsCallbacks() const {
88 return number_of_successful_constraints_applied_;
91 int NumberOfFailedConstraintsCallbacks() const {
92 return number_of_failed_constraints_applied_;
95 MockMediaStreamVideoSource* mock_source() { return mock_source_; }
97 // Test that the source crops to the requested max width and
98 // height even though the camera delivers a larger frame.
99 void TestSourceCropFrame(int capture_width,
101 const blink::WebMediaConstraints& constraints,
103 int expected_height) {
104 // Expect the source to start capture with the supported resolution.
105 blink::WebMediaStreamTrack track =
106 CreateTrackAndStartSource(constraints, capture_width, capture_height,
109 MockMediaStreamVideoSink sink;
110 MediaStreamVideoSink::AddToVideoTrack(
111 &sink, sink.GetDeliverFrameCB(), track);
112 DeliverVideoFrameAndWaitForRenderer(capture_width, capture_height, &sink);
113 EXPECT_EQ(1, sink.number_of_frames());
115 // Expect the delivered frame to be cropped.
116 EXPECT_EQ(expected_height, sink.frame_size().height());
117 EXPECT_EQ(expected_width, sink.frame_size().width());
118 MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
121 void DeliverVideoFrameAndWaitForRenderer(int width, int height,
122 MockMediaStreamVideoSink* sink) {
123 base::RunLoop run_loop;
124 base::Closure quit_closure = run_loop.QuitClosure();
125 EXPECT_CALL(*sink, OnVideoFrame()).WillOnce(
126 RunClosure(quit_closure));
127 scoped_refptr<media::VideoFrame> frame =
128 media::VideoFrame::CreateBlackFrame(gfx::Size(width, height));
129 mock_source()->DeliverVideoFrame(frame);
134 void ReleaseTrackAndSourceOnAddTrackCallback(
135 const blink::WebMediaStreamTrack& track_to_release) {
136 track_to_release_ = track_to_release;
140 void OnConstraintsApplied(MediaStreamSource* source, bool success) {
141 ASSERT_EQ(source, webkit_source_.extraData());
144 ++number_of_successful_constraints_applied_;
146 ++number_of_failed_constraints_applied_;
148 if (!track_to_release_.isNull()) {
150 webkit_source_.reset();
151 track_to_release_.reset();
154 scoped_ptr<ChildProcess> child_process_;
155 base::MessageLoopForUI message_loop_;
156 blink::WebMediaStreamTrack track_to_release_;
157 int number_of_successful_constraints_applied_;
158 int number_of_failed_constraints_applied_;
159 blink::WebMediaStreamSource webkit_source_;
160 // |mock_source_| is owned by |webkit_source_|.
161 MockMediaStreamVideoSource* mock_source_;
164 TEST_F(MediaStreamVideoSourceTest, AddTrackAndStartSource) {
165 blink::WebMediaConstraints constraints;
166 constraints.initialize();
167 blink::WebMediaStreamTrack track = CreateTrack("123", constraints);
168 mock_source()->CompleteGetSupportedFormats();
169 mock_source()->StartMockedSource();
170 EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
173 TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeSourceStarts) {
174 blink::WebMediaConstraints constraints;
175 constraints.initialize();
176 blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints);
177 mock_source()->CompleteGetSupportedFormats();
178 blink::WebMediaStreamTrack track2 = CreateTrack("123", constraints);
179 EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
180 mock_source()->StartMockedSource();
181 EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
184 TEST_F(MediaStreamVideoSourceTest, AddTrackAfterSourceStarts) {
185 blink::WebMediaConstraints constraints;
186 constraints.initialize();
187 blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints);
188 mock_source()->CompleteGetSupportedFormats();
189 mock_source()->StartMockedSource();
190 EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
191 blink::WebMediaStreamTrack track2 = CreateTrack("123", constraints);
192 EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
195 TEST_F(MediaStreamVideoSourceTest, AddTrackAndFailToStartSource) {
196 blink::WebMediaConstraints constraints;
197 constraints.initialize();
198 blink::WebMediaStreamTrack track = CreateTrack("123", constraints);
199 mock_source()->CompleteGetSupportedFormats();
200 mock_source()->FailToStartMockedSource();
201 EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
204 TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeGetSupportedFormats) {
205 blink::WebMediaConstraints constraints;
206 constraints.initialize();
207 blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints);
208 blink::WebMediaStreamTrack track2 = CreateTrack("123", constraints);
209 mock_source()->CompleteGetSupportedFormats();
210 mock_source()->StartMockedSource();
211 EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
214 // Test that the capture output is CIF if we set max constraints to CIF.
215 // and the capture device support CIF.
216 TEST_F(MediaStreamVideoSourceTest, MandatoryConstraintCif5Fps) {
217 MockMediaConstraintFactory factory;
218 factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 352);
219 factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 288);
220 factory.AddMandatory(MediaStreamVideoSource::kMaxFrameRate, 5);
222 CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 352, 288, 5);
225 // Test that the capture output is 720P if the camera support it and the
226 // optional constraint is set to 720P.
227 TEST_F(MediaStreamVideoSourceTest, MandatoryMinVgaOptional720P) {
228 MockMediaConstraintFactory factory;
229 factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 640);
230 factory.AddMandatory(MediaStreamVideoSource::kMinHeight, 480);
231 factory.AddOptional(MediaStreamVideoSource::kMinWidth, 1280);
232 factory.AddOptional(MediaStreamVideoSource::kMinAspectRatio,
235 CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 1280, 720, 30);
238 // Test that the capture output have aspect ratio 4:3 if a mandatory constraint
239 // require it even if an optional constraint request a higher resolution
240 // that don't have this aspect ratio.
241 TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio4To3) {
242 MockMediaConstraintFactory factory;
243 factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 640);
244 factory.AddMandatory(MediaStreamVideoSource::kMinHeight, 480);
245 factory.AddMandatory(MediaStreamVideoSource::kMaxAspectRatio,
247 factory.AddOptional(MediaStreamVideoSource::kMinWidth, 1280);
249 CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 640, 480, 30);
252 // Test that AddTrack fail if the mandatory aspect ratio
253 // is set higher than supported.
254 TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatioTooHigh) {
255 MockMediaConstraintFactory factory;
256 factory.AddMandatory(MediaStreamVideoSource::kMinAspectRatio, 2);
257 blink::WebMediaStreamTrack track = CreateTrack(
258 "123", factory.CreateWebMediaConstraints());
259 mock_source()->CompleteGetSupportedFormats();
260 EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
263 // Test that its safe to release the last reference of a blink track and the
264 // source during the callback if adding a track succeeds.
265 TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnSuccessCallBack) {
266 MockMediaConstraintFactory factory;
268 blink::WebMediaStreamTrack track =
269 CreateTrack("123", factory.CreateWebMediaConstraints());
270 ReleaseTrackAndSourceOnAddTrackCallback(track);
272 mock_source()->CompleteGetSupportedFormats();
273 mock_source()->StartMockedSource();
274 EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
277 // Test that its safe to release the last reference of a blink track and the
278 // source during the callback if adding a track fails.
279 TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnFailureCallBack) {
280 MockMediaConstraintFactory factory;
281 factory.AddMandatory(MediaStreamVideoSource::kMinAspectRatio, 2);
283 blink::WebMediaStreamTrack track =
284 CreateTrack("123", factory.CreateWebMediaConstraints());
285 ReleaseTrackAndSourceOnAddTrackCallback(track);
287 mock_source()->CompleteGetSupportedFormats();
288 EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
291 // Test that the source ignores an optional aspect ratio that is higher than
293 TEST_F(MediaStreamVideoSourceTest, OptionalAspectRatioTooHigh) {
294 MockMediaConstraintFactory factory;
295 factory.AddOptional(MediaStreamVideoSource::kMinAspectRatio, 2);
296 blink::WebMediaStreamTrack track = CreateTrack(
297 "123", factory.CreateWebMediaConstraints());
298 mock_source()->CompleteGetSupportedFormats();
300 const media::VideoCaptureParams& params = mock_source()->start_params();
301 double aspect_ratio =
302 static_cast<double>(params.requested_format.frame_size.width()) /
303 params.requested_format.frame_size.height();
304 EXPECT_LT(aspect_ratio, 2);
307 // Test that the source starts video with the default resolution if the
308 // that is the only supported.
309 TEST_F(MediaStreamVideoSourceTest, DefaultCapability) {
310 media::VideoCaptureFormats formats;
311 formats.push_back(media::VideoCaptureFormat(
312 gfx::Size(MediaStreamVideoSource::kDefaultWidth,
313 MediaStreamVideoSource::kDefaultHeight),
314 MediaStreamVideoSource::kDefaultFrameRate,
315 media::PIXEL_FORMAT_I420));
316 mock_source()->SetSupportedFormats(formats);
318 blink::WebMediaConstraints constraints;
319 constraints.initialize();
320 CreateTrackAndStartSource(constraints,
321 MediaStreamVideoSource::kDefaultWidth,
322 MediaStreamVideoSource::kDefaultHeight,
326 TEST_F(MediaStreamVideoSourceTest, InvalidMandatoryConstraint) {
327 MockMediaConstraintFactory factory;
328 factory.AddMandatory("weird key", 640);
329 blink::WebMediaStreamTrack track = CreateTrack(
330 "123", factory.CreateWebMediaConstraints());
331 mock_source()->CompleteGetSupportedFormats();
332 EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
335 // Test that the source ignores an unknown optional constraint.
336 TEST_F(MediaStreamVideoSourceTest, InvalidOptionalConstraint) {
337 MockMediaConstraintFactory factory;
338 factory.AddOptional("weird key", 640);
340 CreateTrackAndStartSource(factory.CreateWebMediaConstraints(),
341 MediaStreamVideoSource::kDefaultWidth,
342 MediaStreamVideoSource::kDefaultHeight,
346 // Tests that the source starts video with the max width and height set by
347 // constraints for screencast.
348 TEST_F(MediaStreamVideoSourceTest, ScreencastResolutionWithConstraint) {
349 media::VideoCaptureFormats formats;
350 formats.push_back(media::VideoCaptureFormat(
351 gfx::Size(480, 270), 30, media::PIXEL_FORMAT_I420));
352 mock_source()->SetSupportedFormats(formats);
353 MockMediaConstraintFactory factory;
354 factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 480);
355 factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 270);
357 blink::WebMediaStreamTrack track = CreateTrackAndStartSource(
358 factory.CreateWebMediaConstraints(), 480, 270, 30);
359 EXPECT_EQ(480, mock_source()->max_requested_height());
360 EXPECT_EQ(270, mock_source()->max_requested_width());
363 // Test that optional constraints are applied in order.
364 TEST_F(MediaStreamVideoSourceTest, OptionalConstraints) {
365 MockMediaConstraintFactory factory;
366 // Min width of 2056 pixels can not be fulfilled.
367 factory.AddOptional(MediaStreamVideoSource::kMinWidth, 2056);
368 factory.AddOptional(MediaStreamVideoSource::kMinWidth, 641);
369 // Since min width is set to 641 pixels, max width 640 can not be fulfilled.
370 factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 640);
371 CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 1280, 720, 30);
374 // Test that the source crops to the requested max width and
375 // height even though the camera delivers a larger frame.
376 TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrameOptional640360) {
377 MockMediaConstraintFactory factory;
378 factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 640);
379 factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 360);
380 TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 640, 360);
383 TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrameMandatory640360) {
384 MockMediaConstraintFactory factory;
385 factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 640);
386 factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 360);
387 TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 640, 360);
390 TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrameMandatory732489) {
391 MockMediaConstraintFactory factory;
392 factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 732);
393 factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 489);
394 factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 732);
395 factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 489);
396 TestSourceCropFrame(1280, 720, factory.CreateWebMediaConstraints(), 732, 489);
399 // Test that the source crops to the requested max width and
400 // height even though the requested frame has odd size.
401 TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrame637359) {
402 MockMediaConstraintFactory factory;
403 factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 637);
404 factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 359);
405 TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 637, 359);
408 TEST_F(MediaStreamVideoSourceTest, DeliverSmallerSizeWhenTooLargeMax) {
409 MockMediaConstraintFactory factory;
410 factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 1920);
411 factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 1080);
412 factory.AddOptional(MediaStreamVideoSource::kMinWidth, 1280);
413 factory.AddOptional(MediaStreamVideoSource::kMinHeight, 720);
414 TestSourceCropFrame(1280, 720, factory.CreateWebMediaConstraints(),
418 // Test that a source can change the frame resolution on the fly and that
419 // tracks sinks get the new frame size unless constraints force the frame to be
421 TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
422 MockMediaConstraintFactory factory;
423 factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 800);
424 factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 700);
426 // Expect the source to start capture with the supported resolution.
427 blink::WebMediaStreamTrack track =
428 CreateTrackAndStartSource(factory.CreateWebMediaConstraints(),
431 MockMediaStreamVideoSink sink;
432 MediaStreamVideoSink::AddToVideoTrack(
433 &sink, sink.GetDeliverFrameCB(), track);
434 EXPECT_EQ(0, sink.number_of_frames());
435 DeliverVideoFrameAndWaitForRenderer(320, 240, &sink);
436 EXPECT_EQ(1, sink.number_of_frames());
437 // Expect the delivered frame to be passed unchanged since its smaller than
439 EXPECT_EQ(320, sink.frame_size().width());
440 EXPECT_EQ(240, sink.frame_size().height());
442 DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
443 EXPECT_EQ(2, sink.number_of_frames());
444 // Expect the delivered frame to be passed unchanged since its smaller than
446 EXPECT_EQ(640, sink.frame_size().width());
447 EXPECT_EQ(480, sink.frame_size().height());
449 DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink);
451 EXPECT_EQ(3, sink.number_of_frames());
452 // Expect a frame to be cropped since its larger than max requested.
453 EXPECT_EQ(800, sink.frame_size().width());
454 EXPECT_EQ(700, sink.frame_size().height());
456 MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
459 TEST_F(MediaStreamVideoSourceTest, IsConstraintSupported) {
460 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
461 MediaStreamVideoSource::kMaxFrameRate));
462 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
463 MediaStreamVideoSource::kMinFrameRate));
464 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
465 MediaStreamVideoSource::kMaxWidth));
466 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
467 MediaStreamVideoSource::kMinWidth));
468 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
469 MediaStreamVideoSource::kMaxHeight));
470 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
471 MediaStreamVideoSource::kMinHeight));
472 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
473 MediaStreamVideoSource::kMaxAspectRatio));
474 EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported(
475 MediaStreamVideoSource::kMinAspectRatio));
477 EXPECT_FALSE(MediaStreamVideoSource::IsConstraintSupported(
478 "something unsupported"));
481 } // namespace content