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