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. 4 5#include "base/bind.h" 6#include "base/callback_helpers.h" 7#include "base/message_loop/message_loop.h" 8#include "base/run_loop.h" 9#include "base/strings/utf_string_conversions.h" 10#include "content/child/child_process.h" 11#include "content/public/renderer/media_stream_video_sink.h" 12#include "content/renderer/media/media_stream_video_capturer_source.h" 13#include "content/renderer/media/media_stream_video_track.h" 14#include "content/renderer/media/mock_media_constraint_factory.h" 15#include "media/base/bind_to_current_loop.h" 16#include "testing/gmock/include/gmock/gmock.h" 17#include "testing/gtest/include/gtest/gtest.h" 18#include "third_party/WebKit/public/web/WebHeap.h" 19 20namespace content { 21 22class MockVideoCapturerDelegate : public VideoCapturerDelegate { 23 public: 24 explicit MockVideoCapturerDelegate(const StreamDeviceInfo& device_info) 25 : VideoCapturerDelegate(device_info) {} 26 27 MOCK_METHOD3(StartCapture, 28 void(const media::VideoCaptureParams& params, 29 const VideoCaptureDeliverFrameCB& new_frame_callback, 30 const RunningCallback& running_callback)); 31 MOCK_METHOD0(StopCapture, void()); 32 33 private: 34 virtual ~MockVideoCapturerDelegate() {} 35}; 36 37class MediaStreamVideoCapturerSourceTest : public testing::Test { 38 public: 39 MediaStreamVideoCapturerSourceTest() 40 : child_process_(new ChildProcess()), 41 source_(NULL), 42 source_stopped_(false) { 43 } 44 45 virtual void TearDown() OVERRIDE { 46 webkit_source_.reset(); 47 blink::WebHeap::collectAllGarbageForTesting(); 48 } 49 50 void InitWithDeviceInfo(const StreamDeviceInfo& device_info) { 51 delegate_ = new MockVideoCapturerDelegate(device_info); 52 source_ = new MediaStreamVideoCapturerSource( 53 device_info, 54 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped, 55 base::Unretained(this)), 56 delegate_); 57 58 webkit_source_.initialize(base::UTF8ToUTF16("dummy_source_id"), 59 blink::WebMediaStreamSource::TypeVideo, 60 base::UTF8ToUTF16("dummy_source_name")); 61 webkit_source_.setExtraData(source_); 62 webkit_source_id_ = webkit_source_.id(); 63 } 64 65 blink::WebMediaStreamTrack StartSource() { 66 MockMediaConstraintFactory factory; 67 bool enabled = true; 68 // CreateVideoTrack will trigger OnConstraintsApplied. 69 return MediaStreamVideoTrack::CreateVideoTrack( 70 source_, factory.CreateWebMediaConstraints(), 71 base::Bind( 72 &MediaStreamVideoCapturerSourceTest::OnConstraintsApplied, 73 base::Unretained(this)), 74 enabled); 75 } 76 77 MockVideoCapturerDelegate& mock_delegate() { 78 return *static_cast<MockVideoCapturerDelegate*>(delegate_.get()); 79 } 80 81 void OnSourceStopped(const blink::WebMediaStreamSource& source) { 82 source_stopped_ = true; 83 EXPECT_EQ(source.id(), webkit_source_id_); 84 } 85 86 protected: 87 void OnConstraintsApplied(MediaStreamSource* source, 88 MediaStreamRequestResult result, 89 const blink::WebString& result_name) { 90 } 91 92 base::MessageLoopForUI message_loop_; 93 scoped_ptr<ChildProcess> child_process_; 94 blink::WebMediaStreamSource webkit_source_; 95 MediaStreamVideoCapturerSource* source_; // owned by webkit_source. 96 scoped_refptr<VideoCapturerDelegate> delegate_; 97 blink::WebString webkit_source_id_; 98 bool source_stopped_; 99}; 100 101TEST_F(MediaStreamVideoCapturerSourceTest, TabCaptureAllowResolutionChange) { 102 StreamDeviceInfo device_info; 103 device_info.device.type = MEDIA_TAB_VIDEO_CAPTURE; 104 InitWithDeviceInfo(device_info); 105 106 EXPECT_CALL(mock_delegate(), StartCapture( 107 testing::Field(&media::VideoCaptureParams::resolution_change_policy, 108 media::RESOLUTION_POLICY_DYNAMIC_WITHIN_LIMIT), 109 testing::_, 110 testing::_)).Times(1); 111 blink::WebMediaStreamTrack track = StartSource(); 112 // When the track goes out of scope, the source will be stopped. 113 EXPECT_CALL(mock_delegate(), StopCapture()); 114} 115 116TEST_F(MediaStreamVideoCapturerSourceTest, 117 DesktopCaptureAllowResolutionChange) { 118 StreamDeviceInfo device_info; 119 device_info.device.type = MEDIA_DESKTOP_VIDEO_CAPTURE; 120 InitWithDeviceInfo(device_info); 121 122 EXPECT_CALL(mock_delegate(), StartCapture( 123 testing::Field(&media::VideoCaptureParams::resolution_change_policy, 124 media::RESOLUTION_POLICY_DYNAMIC_WITHIN_LIMIT), 125 testing::_, 126 testing::_)).Times(1); 127 blink::WebMediaStreamTrack track = StartSource(); 128 // When the track goes out of scope, the source will be stopped. 129 EXPECT_CALL(mock_delegate(), StopCapture()); 130} 131 132TEST_F(MediaStreamVideoCapturerSourceTest, Ended) { 133 StreamDeviceInfo device_info; 134 device_info.device.type = MEDIA_DESKTOP_VIDEO_CAPTURE; 135 delegate_ = new VideoCapturerDelegate(device_info); 136 source_ = new MediaStreamVideoCapturerSource( 137 device_info, 138 base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped, 139 base::Unretained(this)), 140 delegate_); 141 webkit_source_.initialize(base::UTF8ToUTF16("dummy_source_id"), 142 blink::WebMediaStreamSource::TypeVideo, 143 base::UTF8ToUTF16("dummy_source_name")); 144 webkit_source_.setExtraData(source_); 145 webkit_source_id_ = webkit_source_.id(); 146 blink::WebMediaStreamTrack track = StartSource(); 147 message_loop_.RunUntilIdle(); 148 149 delegate_->OnStateUpdateOnRenderThread(VIDEO_CAPTURE_STATE_STARTED); 150 message_loop_.RunUntilIdle(); 151 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, 152 webkit_source_.readyState()); 153 154 EXPECT_FALSE(source_stopped_); 155 delegate_->OnStateUpdateOnRenderThread(VIDEO_CAPTURE_STATE_ERROR); 156 message_loop_.RunUntilIdle(); 157 EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, 158 webkit_source_.readyState()); 159 // Verify that MediaStreamSource::SourceStoppedCallback has been triggered. 160 EXPECT_TRUE(source_stopped_); 161} 162 163class FakeMediaStreamVideoSink : public MediaStreamVideoSink { 164 public: 165 FakeMediaStreamVideoSink(base::TimeTicks* capture_time, 166 base::Closure got_frame_cb) 167 : capture_time_(capture_time), 168 got_frame_cb_(got_frame_cb) { 169 } 170 171 void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame, 172 const media::VideoCaptureFormat& format, 173 const base::TimeTicks& capture_time) { 174 *capture_time_ = capture_time; 175 base::ResetAndReturn(&got_frame_cb_).Run(); 176 } 177 178 private: 179 base::TimeTicks* capture_time_; 180 base::Closure got_frame_cb_; 181}; 182 183TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTime) { 184 StreamDeviceInfo device_info; 185 device_info.device.type = MEDIA_DESKTOP_VIDEO_CAPTURE; 186 InitWithDeviceInfo(device_info); 187 188 VideoCaptureDeliverFrameCB deliver_frame_cb; 189 VideoCapturerDelegate::RunningCallback running_cb; 190 191 EXPECT_CALL(mock_delegate(), StartCapture( 192 testing::_, 193 testing::_, 194 testing::_)) 195 .Times(1) 196 .WillOnce(testing::DoAll(testing::SaveArg<1>(&deliver_frame_cb), 197 testing::SaveArg<2>(&running_cb))); 198 EXPECT_CALL(mock_delegate(), StopCapture()); 199 blink::WebMediaStreamTrack track = StartSource(); 200 running_cb.Run(MEDIA_DEVICE_OK); 201 202 base::RunLoop run_loop; 203 base::TimeTicks reference_capture_time = 204 base::TimeTicks::FromInternalValue(60013); 205 base::TimeTicks capture_time; 206 FakeMediaStreamVideoSink fake_sink( 207 &capture_time, 208 media::BindToCurrentLoop(run_loop.QuitClosure())); 209 FakeMediaStreamVideoSink::AddToVideoTrack( 210 &fake_sink, 211 base::Bind(&FakeMediaStreamVideoSink::OnVideoFrame, 212 base::Unretained(&fake_sink)), 213 track); 214 child_process_->io_message_loop()->PostTask( 215 FROM_HERE, 216 base::Bind(deliver_frame_cb, 217 media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2)), 218 media::VideoCaptureFormat(), 219 reference_capture_time)); 220 run_loop.Run(); 221 FakeMediaStreamVideoSink::RemoveFromVideoTrack(&fake_sink, track); 222 EXPECT_EQ(reference_capture_time, capture_time); 223} 224 225} // namespace content 226