// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "content/child/child_process.h" #include "content/renderer/media/media_stream_video_source.h" #include "content/renderer/media/media_stream_video_track.h" #include "content/renderer/media/mock_media_constraint_factory.h" #include "content/renderer/media/mock_media_stream_video_sink.h" #include "content/renderer/media/mock_media_stream_video_source.h" #include "media/base/video_frame.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::DoAll; using ::testing::SaveArg; namespace content { ACTION_P(RunClosure, closure) { closure.Run(); } class MediaStreamVideoSourceTest : public ::testing::Test { public: MediaStreamVideoSourceTest() : child_process_(new ChildProcess()), number_of_successful_constraints_applied_(0), number_of_failed_constraints_applied_(0), result_(MEDIA_DEVICE_OK), result_name_(""), mock_source_(new MockMediaStreamVideoSource(true)) { media::VideoCaptureFormats formats; formats.push_back(media::VideoCaptureFormat( gfx::Size(1280, 720), 30, media::PIXEL_FORMAT_I420)); formats.push_back(media::VideoCaptureFormat( gfx::Size(640, 480), 30, media::PIXEL_FORMAT_I420)); formats.push_back(media::VideoCaptureFormat( gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420)); formats.push_back(media::VideoCaptureFormat( gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420)); mock_source_->SetSupportedFormats(formats); webkit_source_.initialize(base::UTF8ToUTF16("dummy_source_id"), blink::WebMediaStreamSource::TypeVideo, base::UTF8ToUTF16("dummy_source_name")); webkit_source_.setExtraData(mock_source_); } protected: // Create a track that's associated with |webkit_source_|. blink::WebMediaStreamTrack CreateTrack( const std::string& id, const blink::WebMediaConstraints& constraints) { bool enabled = true; return MediaStreamVideoTrack::CreateVideoTrack( mock_source_, constraints, base::Bind( &MediaStreamVideoSourceTest::OnConstraintsApplied, base::Unretained(this)), enabled); } blink::WebMediaStreamTrack CreateTrackAndStartSource( const blink::WebMediaConstraints& constraints, int expected_width, int expected_height, int expected_frame_rate) { blink::WebMediaStreamTrack track = CreateTrack("123", constraints); mock_source_->CompleteGetSupportedFormats(); const media::VideoCaptureParams& format = mock_source()->start_params(); EXPECT_EQ(expected_width, format.requested_format.frame_size.width()); EXPECT_EQ(expected_height, format.requested_format.frame_size.height()); EXPECT_EQ(expected_frame_rate, format.requested_format.frame_rate); EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks()); mock_source_->StartMockedSource(); // Once the source has started successfully we expect that the // ConstraintsCallback in MediaStreamSource::AddTrack completes. EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks()); return track; } int NumberOfSuccessConstraintsCallbacks() const { return number_of_successful_constraints_applied_; } int NumberOfFailedConstraintsCallbacks() const { return number_of_failed_constraints_applied_; } content::MediaStreamRequestResult error_type() const { return result_; } blink::WebString error_name() const { return result_name_; } MockMediaStreamVideoSource* mock_source() { return mock_source_; } // Test that the source crops/scales to the requested width and // height even though the camera delivers a larger frame. void TestSourceCropFrame(int capture_width, int capture_height, const blink::WebMediaConstraints& constraints, int expected_width, int expected_height) { // Expect the source to start capture with the supported resolution. blink::WebMediaStreamTrack track = CreateTrackAndStartSource(constraints, capture_width, capture_height, 30); MockMediaStreamVideoSink sink; MediaStreamVideoSink::AddToVideoTrack( &sink, sink.GetDeliverFrameCB(), track); DeliverVideoFrameAndWaitForRenderer(capture_width, capture_height, &sink); EXPECT_EQ(1, sink.number_of_frames()); // Expect the delivered frame to be cropped. EXPECT_EQ(expected_height, sink.frame_size().height()); EXPECT_EQ(expected_width, sink.frame_size().width()); MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track); } void DeliverVideoFrameAndWaitForRenderer(int width, int height, MockMediaStreamVideoSink* sink) { base::RunLoop run_loop; base::Closure quit_closure = run_loop.QuitClosure(); EXPECT_CALL(*sink, OnVideoFrame()).WillOnce( RunClosure(quit_closure)); scoped_refptr frame = media::VideoFrame::CreateBlackFrame(gfx::Size(width, height)); mock_source()->DeliverVideoFrame(frame); run_loop.Run(); } void DeliverVideoFrameAndWaitForTwoRenderers( int width, int height, MockMediaStreamVideoSink* sink1, MockMediaStreamVideoSink* sink2) { base::RunLoop run_loop; base::Closure quit_closure = run_loop.QuitClosure(); EXPECT_CALL(*sink1, OnVideoFrame()); EXPECT_CALL(*sink2, OnVideoFrame()).WillOnce( RunClosure(quit_closure)); scoped_refptr frame = media::VideoFrame::CreateBlackFrame(gfx::Size(width, height)); mock_source()->DeliverVideoFrame(frame); run_loop.Run(); } void TestTwoTracksWithDifferentConstraints( const blink::WebMediaConstraints& constraints1, const blink::WebMediaConstraints& constraints2, int capture_width, int capture_height, int expected_width1, int expected_height1, int expected_width2, int expected_height2) { blink::WebMediaStreamTrack track1 = CreateTrackAndStartSource(constraints1, capture_width, capture_height, MediaStreamVideoSource::kDefaultFrameRate); blink::WebMediaStreamTrack track2 = CreateTrack("dummy", constraints2); MockMediaStreamVideoSink sink1; MediaStreamVideoSink::AddToVideoTrack(&sink1, sink1.GetDeliverFrameCB(), track1); EXPECT_EQ(0, sink1.number_of_frames()); MockMediaStreamVideoSink sink2; MediaStreamVideoSink::AddToVideoTrack(&sink2, sink2.GetDeliverFrameCB(), track2); EXPECT_EQ(0, sink2.number_of_frames()); DeliverVideoFrameAndWaitForTwoRenderers(capture_width, capture_height, &sink1, &sink2); EXPECT_EQ(1, sink1.number_of_frames()); EXPECT_EQ(expected_width1, sink1.frame_size().width()); EXPECT_EQ(expected_height1, sink1.frame_size().height()); EXPECT_EQ(1, sink2.number_of_frames()); EXPECT_EQ(expected_width2, sink2.frame_size().width()); EXPECT_EQ(expected_height2, sink2.frame_size().height()); MediaStreamVideoSink::RemoveFromVideoTrack(&sink1, track1); MediaStreamVideoSink::RemoveFromVideoTrack(&sink2, track2); } void ReleaseTrackAndSourceOnAddTrackCallback( const blink::WebMediaStreamTrack& track_to_release) { track_to_release_ = track_to_release; } private: void OnConstraintsApplied(MediaStreamSource* source, MediaStreamRequestResult result, const blink::WebString& result_name) { ASSERT_EQ(source, webkit_source_.extraData()); if (result == MEDIA_DEVICE_OK) { ++number_of_successful_constraints_applied_; } else { result_ = result; result_name_ = result_name; ++number_of_failed_constraints_applied_; } if (!track_to_release_.isNull()) { mock_source_ = NULL; webkit_source_.reset(); track_to_release_.reset(); } } base::MessageLoopForUI message_loop_; scoped_ptr child_process_; blink::WebMediaStreamTrack track_to_release_; int number_of_successful_constraints_applied_; int number_of_failed_constraints_applied_; content::MediaStreamRequestResult result_; blink::WebString result_name_; blink::WebMediaStreamSource webkit_source_; // |mock_source_| is owned by |webkit_source_|. MockMediaStreamVideoSource* mock_source_; }; TEST_F(MediaStreamVideoSourceTest, AddTrackAndStartSource) { blink::WebMediaConstraints constraints; constraints.initialize(); blink::WebMediaStreamTrack track = CreateTrack("123", constraints); mock_source()->CompleteGetSupportedFormats(); mock_source()->StartMockedSource(); EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeSourceStarts) { blink::WebMediaConstraints constraints; constraints.initialize(); blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints); mock_source()->CompleteGetSupportedFormats(); blink::WebMediaStreamTrack track2 = CreateTrack("123", constraints); EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks()); mock_source()->StartMockedSource(); EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, AddTrackAfterSourceStarts) { blink::WebMediaConstraints constraints; constraints.initialize(); blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints); mock_source()->CompleteGetSupportedFormats(); mock_source()->StartMockedSource(); EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks()); blink::WebMediaStreamTrack track2 = CreateTrack("123", constraints); EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, AddTrackAndFailToStartSource) { blink::WebMediaConstraints constraints; constraints.initialize(); blink::WebMediaStreamTrack track = CreateTrack("123", constraints); mock_source()->CompleteGetSupportedFormats(); mock_source()->FailToStartMockedSource(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeGetSupportedFormats) { blink::WebMediaConstraints constraints; constraints.initialize(); blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints); blink::WebMediaStreamTrack track2 = CreateTrack("123", constraints); mock_source()->CompleteGetSupportedFormats(); mock_source()->StartMockedSource(); EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks()); } // Test that the capture output is CIF if we set max constraints to CIF. // and the capture device support CIF. TEST_F(MediaStreamVideoSourceTest, MandatoryConstraintCif5Fps) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 352); factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 288); factory.AddMandatory(MediaStreamVideoSource::kMaxFrameRate, 5); CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 352, 288, 5); } // Test that the capture output is 720P if the camera support it and the // optional constraint is set to 720P. TEST_F(MediaStreamVideoSourceTest, MandatoryMinVgaOptional720P) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 640); factory.AddMandatory(MediaStreamVideoSource::kMinHeight, 480); factory.AddOptional(MediaStreamVideoSource::kMinWidth, 1280); factory.AddOptional(MediaStreamVideoSource::kMinAspectRatio, 1280.0 / 720); CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 1280, 720, 30); } // Test that the capture output have aspect ratio 4:3 if a mandatory constraint // require it even if an optional constraint request a higher resolution // that don't have this aspect ratio. TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio4To3) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 640); factory.AddMandatory(MediaStreamVideoSource::kMinHeight, 480); factory.AddMandatory(MediaStreamVideoSource::kMaxAspectRatio, 640.0 / 480); factory.AddOptional(MediaStreamVideoSource::kMinWidth, 1280); TestSourceCropFrame(1280, 720, factory.CreateWebMediaConstraints(), 960, 720); } // Test that AddTrack succeeds if the mandatory min aspect ratio it set to 2. TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio2) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinAspectRatio, 2); TestSourceCropFrame(MediaStreamVideoSource::kDefaultWidth, MediaStreamVideoSource::kDefaultHeight, factory.CreateWebMediaConstraints(), 640, 320); } TEST_F(MediaStreamVideoSourceTest, MinAspectRatioLargerThanMaxAspectRatio) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinAspectRatio, 2); factory.AddMandatory(MediaStreamVideoSource::kMaxAspectRatio, 1); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, MaxAspectRatioZero) { MockMediaConstraintFactory factory; factory.AddOptional(MediaStreamVideoSource::kMaxAspectRatio, 0); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, MinWidthLargerThanMaxWidth) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 640); factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 320); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, MinHeightLargerThanMaxHeight) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinHeight, 480); factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 360); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } TEST_F(MediaStreamVideoSourceTest, MinFrameRateLargerThanMaxFrameRate) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinFrameRate, 25); factory.AddMandatory(MediaStreamVideoSource::kMaxFrameRate, 15); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } // Test that its safe to release the last reference of a blink track and the // source during the callback if adding a track succeeds. TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnSuccessCallBack) { MockMediaConstraintFactory factory; { blink::WebMediaStreamTrack track = CreateTrack("123", factory.CreateWebMediaConstraints()); ReleaseTrackAndSourceOnAddTrackCallback(track); } mock_source()->CompleteGetSupportedFormats(); mock_source()->StartMockedSource(); EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks()); } // Test that its safe to release the last reference of a blink track and the // source during the callback if adding a track fails. TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnFailureCallBack) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 99999); { blink::WebMediaStreamTrack track = CreateTrack("123", factory.CreateWebMediaConstraints()); ReleaseTrackAndSourceOnAddTrackCallback(track); } mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } // Test that the source ignores an optional aspect ratio that is higher than // supported. TEST_F(MediaStreamVideoSourceTest, OptionalAspectRatioTooHigh) { MockMediaConstraintFactory factory; factory.AddOptional(MediaStreamVideoSource::kMinAspectRatio, 2); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); const media::VideoCaptureParams& params = mock_source()->start_params(); double aspect_ratio = static_cast(params.requested_format.frame_size.width()) / params.requested_format.frame_size.height(); EXPECT_LT(aspect_ratio, 2); } // Test that the source starts video with the default resolution if the // that is the only supported. TEST_F(MediaStreamVideoSourceTest, DefaultCapability) { media::VideoCaptureFormats formats; formats.push_back(media::VideoCaptureFormat( gfx::Size(MediaStreamVideoSource::kDefaultWidth, MediaStreamVideoSource::kDefaultHeight), MediaStreamVideoSource::kDefaultFrameRate, media::PIXEL_FORMAT_I420)); mock_source()->SetSupportedFormats(formats); blink::WebMediaConstraints constraints; constraints.initialize(); CreateTrackAndStartSource(constraints, MediaStreamVideoSource::kDefaultWidth, MediaStreamVideoSource::kDefaultHeight, 30); } TEST_F(MediaStreamVideoSourceTest, InvalidMandatoryConstraint) { MockMediaConstraintFactory factory; factory.AddMandatory("weird key", 640); blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); EXPECT_EQ(MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED, error_type()); EXPECT_EQ("weird key", error_name()); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } // Test that the source ignores an unknown optional constraint. TEST_F(MediaStreamVideoSourceTest, InvalidOptionalConstraint) { MockMediaConstraintFactory factory; factory.AddOptional("weird key", 640); CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), MediaStreamVideoSource::kDefaultWidth, MediaStreamVideoSource::kDefaultHeight, 30); } // Tests that the source starts video with the max width and height set by // constraints for screencast. TEST_F(MediaStreamVideoSourceTest, ScreencastResolutionWithConstraint) { media::VideoCaptureFormats formats; formats.push_back(media::VideoCaptureFormat( gfx::Size(480, 270), 30, media::PIXEL_FORMAT_I420)); mock_source()->SetSupportedFormats(formats); MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 480); factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 270); blink::WebMediaStreamTrack track = CreateTrackAndStartSource( factory.CreateWebMediaConstraints(), 480, 270, 30); EXPECT_EQ(480, mock_source()->max_requested_height()); EXPECT_EQ(270, mock_source()->max_requested_width()); } // Test that optional constraints are applied in order. TEST_F(MediaStreamVideoSourceTest, OptionalConstraints) { MockMediaConstraintFactory factory; // Min width of 2056 pixels can not be fulfilled. factory.AddOptional(MediaStreamVideoSource::kMinWidth, 2056); factory.AddOptional(MediaStreamVideoSource::kMinWidth, 641); // Since min width is set to 641 pixels, max width 640 can not be fulfilled. factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 640); CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 1280, 720, 30); } // Test that the source crops to the requested max width and // height even though the camera delivers a larger frame. TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrameOptional640360) { MockMediaConstraintFactory factory; factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 640); factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 360); TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 640, 360); } TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrameMandatory640360) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 640); factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 360); TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 640, 360); } TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrameMandatory732489) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 732); factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 489); factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 732); factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 489); TestSourceCropFrame(1280, 720, factory.CreateWebMediaConstraints(), 732, 489); } // Test that the source crops to the requested max width and // height even though the requested frame has odd size. TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrame637359) { MockMediaConstraintFactory factory; factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 637); factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 359); TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 637, 359); } TEST_F(MediaStreamVideoSourceTest, DeliverCroppedVideoFrame320320) { MockMediaConstraintFactory factory; factory.AddMandatory(MediaStreamVideoSource::kMaxWidth, 320); factory.AddMandatory(MediaStreamVideoSource::kMaxHeight, 320); factory.AddMandatory(MediaStreamVideoSource::kMinHeight, 320); factory.AddMandatory(MediaStreamVideoSource::kMinWidth, 320); TestSourceCropFrame(640, 480, factory.CreateWebMediaConstraints(), 320, 320); } TEST_F(MediaStreamVideoSourceTest, DeliverSmallerSizeWhenTooLargeMax) { MockMediaConstraintFactory factory; factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 1920); factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 1080); factory.AddOptional(MediaStreamVideoSource::kMinWidth, 1280); factory.AddOptional(MediaStreamVideoSource::kMinHeight, 720); TestSourceCropFrame(1280, 720, factory.CreateWebMediaConstraints(), 1280, 720); } TEST_F(MediaStreamVideoSourceTest, TwoTracksWithVGAAndWVGA) { MockMediaConstraintFactory factory1; factory1.AddOptional(MediaStreamVideoSource::kMaxWidth, 640); factory1.AddOptional(MediaStreamVideoSource::kMaxHeight, 480); MockMediaConstraintFactory factory2; factory2.AddOptional(MediaStreamVideoSource::kMaxHeight, 360); TestTwoTracksWithDifferentConstraints(factory1.CreateWebMediaConstraints(), factory2.CreateWebMediaConstraints(), 640, 480, 640, 480, 640, 360); } TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndWVGA) { MockMediaConstraintFactory factory1; factory1.AddOptional(MediaStreamVideoSource::kMinWidth, 1280); factory1.AddOptional(MediaStreamVideoSource::kMinHeight, 720); MockMediaConstraintFactory factory2; factory2.AddMandatory(MediaStreamVideoSource::kMaxWidth, 640); factory2.AddMandatory(MediaStreamVideoSource::kMaxHeight, 360); TestTwoTracksWithDifferentConstraints(factory1.CreateWebMediaConstraints(), factory2.CreateWebMediaConstraints(), 1280, 720, 1280, 720, 640, 360); } TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndW700H700) { MockMediaConstraintFactory factory1; factory1.AddOptional(MediaStreamVideoSource::kMinWidth, 1280); factory1.AddOptional(MediaStreamVideoSource::kMinHeight, 720); MockMediaConstraintFactory factory2; factory2.AddMandatory(MediaStreamVideoSource::kMaxWidth, 700); factory2.AddMandatory(MediaStreamVideoSource::kMaxHeight, 700); TestTwoTracksWithDifferentConstraints(factory1.CreateWebMediaConstraints(), factory2.CreateWebMediaConstraints(), 1280, 720, 1280, 720, 700, 700); } TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndMaxAspectRatio4To3) { MockMediaConstraintFactory factory1; factory1.AddOptional(MediaStreamVideoSource::kMinWidth, 1280); factory1.AddOptional(MediaStreamVideoSource::kMinHeight, 720); MockMediaConstraintFactory factory2; factory2.AddMandatory(MediaStreamVideoSource::kMaxAspectRatio, 640.0 / 480); TestTwoTracksWithDifferentConstraints(factory1.CreateWebMediaConstraints(), factory2.CreateWebMediaConstraints(), 1280, 720, 1280, 720, 960, 720); } TEST_F(MediaStreamVideoSourceTest, TwoTracksWithVgaAndMinAspectRatio) { MockMediaConstraintFactory factory1; factory1.AddOptional(MediaStreamVideoSource::kMaxWidth, 640); factory1.AddOptional(MediaStreamVideoSource::kMaxHeight, 480); MockMediaConstraintFactory factory2; factory2.AddMandatory(MediaStreamVideoSource::kMinAspectRatio, 640.0 / 360); TestTwoTracksWithDifferentConstraints(factory1.CreateWebMediaConstraints(), factory2.CreateWebMediaConstraints(), 640, 480, 640, 480, 640, 360); } TEST_F(MediaStreamVideoSourceTest, TwoTracksWithSecondTrackFrameRateHigherThanFirst) { MockMediaConstraintFactory factory1; factory1.AddMandatory(MediaStreamVideoSource::kMinFrameRate, 15); factory1.AddMandatory(MediaStreamVideoSource::kMaxFrameRate, 15); blink::WebMediaStreamTrack track1 = CreateTrackAndStartSource(factory1.CreateWebMediaConstraints(), MediaStreamVideoSource::kDefaultWidth, MediaStreamVideoSource::kDefaultHeight, 15); MockMediaConstraintFactory factory2; factory2.AddMandatory(MediaStreamVideoSource::kMinFrameRate, 30); blink::WebMediaStreamTrack track2 = CreateTrack( "123", factory2.CreateWebMediaConstraints()); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } // Test that a source can change the frame resolution on the fly and that // tracks sinks get the new frame size unless constraints force the frame to be // cropped. TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) { MockMediaConstraintFactory factory; factory.AddOptional(MediaStreamVideoSource::kMaxWidth, 800); factory.AddOptional(MediaStreamVideoSource::kMaxHeight, 700); // Expect the source to start capture with the supported resolution. blink::WebMediaStreamTrack track = CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 640, 480, 30); MockMediaStreamVideoSink sink; MediaStreamVideoSink::AddToVideoTrack( &sink, sink.GetDeliverFrameCB(), track); EXPECT_EQ(0, sink.number_of_frames()); DeliverVideoFrameAndWaitForRenderer(320, 240, &sink); EXPECT_EQ(1, sink.number_of_frames()); // Expect the delivered frame to be passed unchanged since its smaller than // max requested. EXPECT_EQ(320, sink.frame_size().width()); EXPECT_EQ(240, sink.frame_size().height()); DeliverVideoFrameAndWaitForRenderer(640, 480, &sink); EXPECT_EQ(2, sink.number_of_frames()); // Expect the delivered frame to be passed unchanged since its smaller than // max requested. EXPECT_EQ(640, sink.frame_size().width()); EXPECT_EQ(480, sink.frame_size().height()); DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink); EXPECT_EQ(3, sink.number_of_frames()); // Expect a frame to be cropped since its larger than max requested. EXPECT_EQ(800, sink.frame_size().width()); EXPECT_EQ(700, sink.frame_size().height()); MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track); } TEST_F(MediaStreamVideoSourceTest, IsConstraintSupported) { EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMaxFrameRate)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMinFrameRate)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMaxWidth)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMinWidth)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMaxHeight)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMinHeight)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMaxAspectRatio)); EXPECT_TRUE(MediaStreamVideoSource::IsConstraintSupported( MediaStreamVideoSource::kMinAspectRatio)); EXPECT_FALSE(MediaStreamVideoSource::IsConstraintSupported( "something unsupported")); } // Test that the constraint negotiation can handle 0.0 fps as frame rate. TEST_F(MediaStreamVideoSourceTest, Use0FpsSupportedFormat) { media::VideoCaptureFormats formats; formats.push_back(media::VideoCaptureFormat( gfx::Size(640, 480), 0.0f, media::PIXEL_FORMAT_I420)); formats.push_back(media::VideoCaptureFormat( gfx::Size(320, 240), 0.0f, media::PIXEL_FORMAT_I420)); mock_source()->SetSupportedFormats(formats); blink::WebMediaConstraints constraints; constraints.initialize(); blink::WebMediaStreamTrack track = CreateTrack("123", constraints); mock_source()->CompleteGetSupportedFormats(); mock_source()->StartMockedSource(); EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks()); MockMediaStreamVideoSink sink; MediaStreamVideoSink::AddToVideoTrack( &sink, sink.GetDeliverFrameCB(), track); EXPECT_EQ(0, sink.number_of_frames()); DeliverVideoFrameAndWaitForRenderer(320, 240, &sink); EXPECT_EQ(1, sink.number_of_frames()); // Expect the delivered frame to be passed unchanged since its smaller than // max requested. EXPECT_EQ(320, sink.frame_size().width()); EXPECT_EQ(240, sink.frame_size().height()); MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track); } // Test that a source producing no frames calls back the MSVCS to tell so, and // this in turn tells the Track attached. Then start passing frames, and check // that in a reasonable time frame the muted state turns to false. TEST_F(MediaStreamVideoSourceTest, MutedSource) { MockMediaConstraintFactory factory; blink::WebMediaStreamTrack track = CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 640, 480, 30); MockMediaStreamVideoSink sink; MediaStreamVideoSink::AddToVideoTrack(&sink, sink.GetDeliverFrameCB(), track); EXPECT_EQ(MediaStreamTrack::GetTrack(track)->GetMutedState(), false); base::RunLoop run_loop; base::Closure quit_closure = run_loop.QuitClosure(); bool muted_state = false; EXPECT_CALL(*mock_source(), DoSetMutedState(_)) .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure))); run_loop.Run(); EXPECT_EQ(muted_state, true); // TODO(mcasas): When added, check |track|'s (WebMediaStreamTrack) Muted // attribute, should be true. In the meantime, check the MediaStreamTrack's. EXPECT_EQ(MediaStreamTrack::GetTrack(track)->GetMutedState(), true); EXPECT_CALL(*mock_source(), DoSetMutedState(_)) .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure))); // Mock frame delivery happens asynchronously, not according to the configured // frame rate, potentially many frames can pass before the muted state is // flipped. |kMaxFrameCount| is used as a reasonable high bound of this value. const int kMaxFrameCount = 10000; int i = 0; while (muted_state != false || ++i > kMaxFrameCount) DeliverVideoFrameAndWaitForRenderer(640, 480, &sink); EXPECT_EQ(muted_state, false); EXPECT_LT(i, kMaxFrameCount); EXPECT_EQ(MediaStreamTrack::GetTrack(track)->GetMutedState(), false); MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track); } // Test that a source producing no frames calls back the MSVCS to tell so, and // this in turn tells all the Tracks attached. TEST_F(MediaStreamVideoSourceTest, MutedSourceWithTwoTracks) { MockMediaConstraintFactory factory1; blink::WebMediaStreamTrack track1 = CreateTrackAndStartSource(factory1.CreateWebMediaConstraints(), MediaStreamVideoSource::kDefaultWidth, MediaStreamVideoSource::kDefaultHeight, 30); MockMediaConstraintFactory factory2; factory2.AddMandatory(MediaStreamVideoSource::kMaxFrameRate, 15); blink::WebMediaStreamTrack track2 = CreateTrack( "123", factory2.CreateWebMediaConstraints()); EXPECT_EQ(0, NumberOfFailedConstraintsCallbacks()); MockMediaStreamVideoSink sink1; MediaStreamVideoSink::AddToVideoTrack(&sink1, sink1.GetDeliverFrameCB(), track1); EXPECT_EQ(MediaStreamTrack::GetTrack(track1)->GetMutedState(), false); MockMediaStreamVideoSink sink2; MediaStreamVideoSink::AddToVideoTrack(&sink2, sink2.GetDeliverFrameCB(), track2); EXPECT_EQ(MediaStreamTrack::GetTrack(track2)->GetMutedState(), false); base::RunLoop run_loop; base::Closure quit_closure = run_loop.QuitClosure(); bool muted_state = false; EXPECT_CALL(*mock_source(), DoSetMutedState(_)) .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure))); run_loop.Run(); EXPECT_EQ(muted_state, true); // TODO(mcasas): When added, check |track|'s (WebMediaStreamTrack) Muted // attribute, should be true. In the meantime, check the MediaStreamTrack's. EXPECT_EQ(MediaStreamTrack::GetTrack(track1)->GetMutedState(), true); EXPECT_EQ(MediaStreamTrack::GetTrack(track2)->GetMutedState(), true); MediaStreamVideoSink::RemoveFromVideoTrack(&sink1, track1); MediaStreamVideoSink::RemoveFromVideoTrack(&sink2, track2); } } // namespace content