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/bind_helpers.h"
7#include "base/callback.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/threading/thread_checker_impl.h"
12#include "content/child/child_process.h"
13#include "content/renderer/media/media_stream_video_track.h"
14#include "content/renderer/media/mock_media_stream_video_sink.h"
15#include "content/renderer/media/mock_media_stream_video_source.h"
16#include "media/base/video_frame.h"
17#include "testing/gtest/include/gtest/gtest.h"
18#include "third_party/WebKit/public/web/WebHeap.h"
19
20namespace content {
21
22const uint8 kBlackValue = 0x00;
23const uint8 kColorValue = 0xAB;
24
25ACTION_P(RunClosure, closure) {
26  closure.Run();
27}
28
29class MediaStreamVideoTrackTest : public ::testing::Test {
30 public:
31  MediaStreamVideoTrackTest()
32      : child_process_(new ChildProcess()),
33        mock_source_(new MockMediaStreamVideoSource(false)),
34        source_started_(false) {
35    blink_source_.initialize(base::UTF8ToUTF16("dummy_source_id"),
36                              blink::WebMediaStreamSource::TypeVideo,
37                              base::UTF8ToUTF16("dummy_source_name"));
38    blink_source_.setExtraData(mock_source_);
39  }
40
41  virtual ~MediaStreamVideoTrackTest() {
42  }
43
44  virtual void TearDown() OVERRIDE {
45    blink_source_.reset();
46    blink::WebHeap::collectAllGarbageForTesting();
47  }
48
49  void DeliverVideoFrameAndWaitForRenderer(MockMediaStreamVideoSink* sink) {
50    base::RunLoop run_loop;
51    base::Closure quit_closure = run_loop.QuitClosure();
52    EXPECT_CALL(*sink, OnVideoFrame()).WillOnce(
53        RunClosure(quit_closure));
54    scoped_refptr<media::VideoFrame> frame =
55        media::VideoFrame::CreateColorFrame(
56            gfx::Size(MediaStreamVideoSource::kDefaultWidth,
57                      MediaStreamVideoSource::kDefaultHeight),
58            kColorValue, kColorValue, kColorValue, base::TimeDelta());
59    mock_source()->DeliverVideoFrame(frame);
60    run_loop.Run();
61  }
62
63 protected:
64  base::MessageLoop* io_message_loop() const {
65    return child_process_->io_message_loop();
66  }
67
68  // Create a track that's associated with |mock_source_|.
69  blink::WebMediaStreamTrack CreateTrack() {
70    blink::WebMediaConstraints constraints;
71    constraints.initialize();
72    bool enabled = true;
73    blink::WebMediaStreamTrack track =
74        MediaStreamVideoTrack::CreateVideoTrack(
75            mock_source_, constraints,
76            MediaStreamSource::ConstraintsCallback(), enabled);
77    if (!source_started_) {
78      mock_source_->StartMockedSource();
79      source_started_ = true;
80    }
81    return track;
82  }
83
84  MockMediaStreamVideoSource* mock_source() { return mock_source_; }
85  const blink::WebMediaStreamSource& blink_source() const {
86    return blink_source_;
87  }
88
89 private:
90  base::MessageLoopForUI message_loop_;
91  scoped_ptr<ChildProcess> child_process_;
92  blink::WebMediaStreamSource blink_source_;
93  // |mock_source_| is owned by |webkit_source_|.
94  MockMediaStreamVideoSource* mock_source_;
95  bool source_started_;
96};
97
98TEST_F(MediaStreamVideoTrackTest, AddAndRemoveSink) {
99  MockMediaStreamVideoSink sink;
100  blink::WebMediaStreamTrack track = CreateTrack();
101  MediaStreamVideoSink::AddToVideoTrack(
102      &sink, sink.GetDeliverFrameCB(), track);
103
104  DeliverVideoFrameAndWaitForRenderer(&sink);
105  EXPECT_EQ(1, sink.number_of_frames());
106
107  DeliverVideoFrameAndWaitForRenderer(&sink);
108
109  MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
110
111  scoped_refptr<media::VideoFrame> frame =
112      media::VideoFrame::CreateBlackFrame(
113          gfx::Size(MediaStreamVideoSource::kDefaultWidth,
114                    MediaStreamVideoSource::kDefaultHeight));
115  mock_source()->DeliverVideoFrame(frame);
116  // Wait for the IO thread to complete delivering frames.
117  io_message_loop()->RunUntilIdle();
118  EXPECT_EQ(2, sink.number_of_frames());
119}
120
121class CheckThreadHelper {
122 public:
123  CheckThreadHelper(base::Closure callback, bool* correct)
124      : callback_(callback),
125        correct_(correct) {
126  }
127
128  ~CheckThreadHelper() {
129    *correct_ = thread_checker_.CalledOnValidThread();
130    callback_.Run();
131  }
132
133 private:
134  base::Closure callback_;
135  bool* correct_;
136  base::ThreadCheckerImpl thread_checker_;
137};
138
139void CheckThreadVideoFrameReceiver(
140    CheckThreadHelper* helper,
141    const scoped_refptr<media::VideoFrame>& frame,
142    const media::VideoCaptureFormat& format,
143    const base::TimeTicks& estimated_capture_time) {
144  // Do nothing.
145}
146
147// Checks that the callback given to the track is reset on the right thread.
148TEST_F(MediaStreamVideoTrackTest, ResetCallbackOnThread) {
149  MockMediaStreamVideoSink sink;
150  blink::WebMediaStreamTrack track = CreateTrack();
151
152  base::RunLoop run_loop;
153  bool correct = false;
154  MediaStreamVideoSink::AddToVideoTrack(
155      &sink,
156      base::Bind(
157          &CheckThreadVideoFrameReceiver,
158          base::Owned(new CheckThreadHelper(run_loop.QuitClosure(), &correct))),
159      track);
160  MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
161  run_loop.Run();
162  EXPECT_TRUE(correct) << "Not called on correct thread.";
163}
164
165TEST_F(MediaStreamVideoTrackTest, SetEnabled) {
166  MockMediaStreamVideoSink sink;
167  blink::WebMediaStreamTrack track = CreateTrack();
168  MediaStreamVideoSink::AddToVideoTrack(
169      &sink, sink.GetDeliverFrameCB(), track);
170
171  MediaStreamVideoTrack* video_track =
172      MediaStreamVideoTrack::GetVideoTrack(track);
173
174  DeliverVideoFrameAndWaitForRenderer(&sink);
175  EXPECT_EQ(1, sink.number_of_frames());
176  EXPECT_EQ(kColorValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
177
178  video_track->SetEnabled(false);
179  EXPECT_FALSE(sink.enabled());
180
181  DeliverVideoFrameAndWaitForRenderer(&sink);
182  EXPECT_EQ(2, sink.number_of_frames());
183  EXPECT_EQ(kBlackValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
184
185  video_track->SetEnabled(true);
186  EXPECT_TRUE(sink.enabled());
187  DeliverVideoFrameAndWaitForRenderer(&sink);
188  EXPECT_EQ(3, sink.number_of_frames());
189  EXPECT_EQ(kColorValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
190  MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
191}
192
193TEST_F(MediaStreamVideoTrackTest, SourceStopped) {
194  MockMediaStreamVideoSink sink;
195  blink::WebMediaStreamTrack track = CreateTrack();
196  MediaStreamVideoSink::AddToVideoTrack(
197      &sink, sink.GetDeliverFrameCB(), track);
198  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink.state());
199
200  mock_source()->StopSource();
201  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink.state());
202  MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
203}
204
205TEST_F(MediaStreamVideoTrackTest, StopLastTrack) {
206  MockMediaStreamVideoSink sink1;
207  blink::WebMediaStreamTrack track1 = CreateTrack();
208  MediaStreamVideoSink::AddToVideoTrack(
209      &sink1, sink1.GetDeliverFrameCB(), track1);
210  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink1.state());
211
212  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
213            blink_source().readyState());
214
215  MockMediaStreamVideoSink sink2;
216  blink::WebMediaStreamTrack track2 = CreateTrack();
217  MediaStreamVideoSink::AddToVideoTrack(
218      &sink2, sink2.GetDeliverFrameCB(), track2);
219  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink2.state());
220
221  MediaStreamVideoTrack* native_track1 =
222      MediaStreamVideoTrack::GetVideoTrack(track1);
223  native_track1->Stop();
224  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink1.state());
225  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
226              blink_source().readyState());
227  MediaStreamVideoSink::RemoveFromVideoTrack(&sink1, track1);
228
229  MediaStreamVideoTrack* native_track2 =
230        MediaStreamVideoTrack::GetVideoTrack(track2);
231  native_track2->Stop();
232  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink2.state());
233  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded,
234            blink_source().readyState());
235  MediaStreamVideoSink::RemoveFromVideoTrack(&sink2, track2);
236}
237
238}  // namespace content
239