// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_checker_impl.h"
+#include "content/child/child_process.h"
#include "content/renderer/media/media_stream_video_track.h"
-#include "content/renderer/media/mock_media_stream_dependency_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"
namespace content {
-class MockVideoSink : public MediaStreamVideoSink {
- public:
- MockVideoSink()
- : number_of_frames_(0), enabled_(true),
- state_(blink::WebMediaStreamSource::ReadyStateLive) {
- }
-
- virtual void OnVideoFrame(
- const scoped_refptr<media::VideoFrame>& frame) OVERRIDE {
- ++number_of_frames_;
- }
-
- virtual void OnReadyStateChanged(
- blink::WebMediaStreamSource::ReadyState state) OVERRIDE {
- state_ = state;
- }
-
- virtual void OnEnabledChanged(bool enabled) OVERRIDE {
- enabled_ = enabled;
- }
-
- int number_of_frames() const { return number_of_frames_; }
- bool enabled() const { return enabled_; }
- blink::WebMediaStreamSource::ReadyState state() const { return state_; }
-
- private:
- int number_of_frames_;
- bool enabled_;
- blink::WebMediaStreamSource::ReadyState state_;
-};
+ACTION_P(RunClosure, closure) {
+ closure.Run();
+}
class MediaStreamVideoTrackTest : public ::testing::Test {
public:
MediaStreamVideoTrackTest()
- : mock_source_(new MockMediaStreamVideoSource(&factory_, false)) {
- webkit_source_.initialize(base::UTF8ToUTF16("dummy_source_id"),
+ : child_process_(new ChildProcess()),
+ mock_source_(new MockMediaStreamVideoSource(false)),
+ source_started_(false) {
+ blink_source_.initialize(base::UTF8ToUTF16("dummy_source_id"),
blink::WebMediaStreamSource::TypeVideo,
base::UTF8ToUTF16("dummy_source_name"));
- webkit_source_.setExtraData(mock_source_);
+ blink_source_.setExtraData(mock_source_);
+ }
+
+ virtual ~MediaStreamVideoTrackTest() {
+ }
+
+ void DeliverVideoFrameAndWaitForRenderer(MockMediaStreamVideoSink* sink) {
+ base::RunLoop run_loop;
+ base::Closure quit_closure = run_loop.QuitClosure();
+ EXPECT_CALL(*sink, OnVideoFrame()).WillOnce(
+ RunClosure(quit_closure));
+ scoped_refptr<media::VideoFrame> frame =
+ media::VideoFrame::CreateBlackFrame(
+ gfx::Size(MediaStreamVideoSource::kDefaultWidth,
+ MediaStreamVideoSource::kDefaultHeight));
+ mock_source()->DeliverVideoFrame(frame);
+ run_loop.Run();
}
protected:
+ base::MessageLoop* io_message_loop() const {
+ return child_process_->io_message_loop();
+ }
+
// Create a track that's associated with |mock_source_|.
blink::WebMediaStreamTrack CreateTrack() {
blink::WebMediaConstraints constraints;
blink::WebMediaStreamTrack track =
MediaStreamVideoTrack::CreateVideoTrack(
mock_source_, constraints,
- MediaStreamSource::ConstraintsCallback(), enabled, &factory_);
- mock_source_->StartMockedSource();
-
+ MediaStreamSource::ConstraintsCallback(), enabled);
+ if (!source_started_) {
+ mock_source_->StartMockedSource();
+ source_started_ = true;
+ }
return track;
}
MockMediaStreamVideoSource* mock_source() { return mock_source_; }
+ const blink::WebMediaStreamSource& blink_source() const {
+ return blink_source_;
+ }
private:
- MockMediaStreamDependencyFactory factory_;
- blink::WebMediaStreamSource webkit_source_;
+ base::MessageLoopForUI message_loop_;
+ scoped_ptr<ChildProcess> child_process_;
+ blink::WebMediaStreamSource blink_source_;
// |mock_source_| is owned by |webkit_source_|.
MockMediaStreamVideoSource* mock_source_;
+ bool source_started_;
};
-TEST_F(MediaStreamVideoTrackTest, GetAdapter) {
- blink::WebMediaStreamTrack track = CreateTrack();
- MediaStreamVideoTrack* video_track =
- MediaStreamVideoTrack::GetVideoTrack(track);
- EXPECT_TRUE(video_track->GetVideoAdapter() != NULL);
-}
-
TEST_F(MediaStreamVideoTrackTest, AddAndRemoveSink) {
- MockVideoSink sink;
+ MockMediaStreamVideoSink sink;
blink::WebMediaStreamTrack track = CreateTrack();
- MediaStreamVideoSink::AddToVideoTrack(&sink, track);
+ MediaStreamVideoSink::AddToVideoTrack(
+ &sink, sink.GetDeliverFrameCB(), track);
+
+ DeliverVideoFrameAndWaitForRenderer(&sink);
+ EXPECT_EQ(1, sink.number_of_frames());
+
+ DeliverVideoFrameAndWaitForRenderer(&sink);
+
+ MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
- MediaStreamVideoTrack* video_track =
- MediaStreamVideoTrack::GetVideoTrack(track);
scoped_refptr<media::VideoFrame> frame =
media::VideoFrame::CreateBlackFrame(
gfx::Size(MediaStreamVideoSource::kDefaultWidth,
MediaStreamVideoSource::kDefaultHeight));
- video_track->OnVideoFrame(frame);
- EXPECT_EQ(1, sink.number_of_frames());
- video_track->OnVideoFrame(frame);
+ mock_source()->DeliverVideoFrame(frame);
+ // Wait for the IO thread to complete delivering frames.
+ io_message_loop()->RunUntilIdle();
EXPECT_EQ(2, sink.number_of_frames());
+}
+class CheckThreadHelper {
+ public:
+ CheckThreadHelper(base::Closure callback, bool* correct)
+ : callback_(callback),
+ correct_(correct) {
+ }
+
+ ~CheckThreadHelper() {
+ *correct_ = thread_checker_.CalledOnValidThread();
+ callback_.Run();
+ }
+
+ private:
+ base::Closure callback_;
+ bool* correct_;
+ base::ThreadCheckerImpl thread_checker_;
+};
+
+void CheckThreadVideoFrameReceiver(
+ CheckThreadHelper* helper,
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format) {
+ // Do nothing.
+}
+
+// Checks that the callback given to the track is reset on the right thread.
+TEST_F(MediaStreamVideoTrackTest, ResetCallbackOnThread) {
+ MockMediaStreamVideoSink sink;
+ blink::WebMediaStreamTrack track = CreateTrack();
+
+ base::RunLoop run_loop;
+ bool correct = false;
+ MediaStreamVideoSink::AddToVideoTrack(
+ &sink,
+ base::Bind(
+ &CheckThreadVideoFrameReceiver,
+ base::Owned(new CheckThreadHelper(run_loop.QuitClosure(), &correct))),
+ track);
MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
- video_track->OnVideoFrame(frame);
- EXPECT_EQ(2, sink.number_of_frames());
+ run_loop.Run();
+ EXPECT_TRUE(correct) << "Not called on correct thread.";
}
TEST_F(MediaStreamVideoTrackTest, SetEnabled) {
- MockVideoSink sink;
+ MockMediaStreamVideoSink sink;
blink::WebMediaStreamTrack track = CreateTrack();
- MediaStreamVideoSink::AddToVideoTrack(&sink, track);
+ MediaStreamVideoSink::AddToVideoTrack(
+ &sink, sink.GetDeliverFrameCB(), track);
MediaStreamVideoTrack* video_track =
MediaStreamVideoTrack::GetVideoTrack(track);
- scoped_refptr<media::VideoFrame> frame =
- media::VideoFrame::CreateBlackFrame(
- gfx::Size(MediaStreamVideoSource::kDefaultWidth,
- MediaStreamVideoSource::kDefaultHeight));
- video_track->OnVideoFrame(frame);
+
+ DeliverVideoFrameAndWaitForRenderer(&sink);
EXPECT_EQ(1, sink.number_of_frames());
video_track->SetEnabled(false);
EXPECT_FALSE(sink.enabled());
- video_track->OnVideoFrame(frame);
+
+ scoped_refptr<media::VideoFrame> frame =
+ media::VideoFrame::CreateBlackFrame(
+ gfx::Size(MediaStreamVideoSource::kDefaultWidth,
+ MediaStreamVideoSource::kDefaultHeight));
+ mock_source()->DeliverVideoFrame(frame);
+ // Wait for the IO thread to complete delivering frames.
+ io_message_loop()->RunUntilIdle();
EXPECT_EQ(1, sink.number_of_frames());
video_track->SetEnabled(true);
EXPECT_TRUE(sink.enabled());
- video_track->OnVideoFrame(frame);
+ mock_source()->DeliverVideoFrame(frame);
+ DeliverVideoFrameAndWaitForRenderer(&sink);
EXPECT_EQ(2, sink.number_of_frames());
MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
}
TEST_F(MediaStreamVideoTrackTest, SourceStopped) {
- MockVideoSink sink;
+ MockMediaStreamVideoSink sink;
blink::WebMediaStreamTrack track = CreateTrack();
- MediaStreamVideoSink::AddToVideoTrack(&sink, track);
+ MediaStreamVideoSink::AddToVideoTrack(
+ &sink, sink.GetDeliverFrameCB(), track);
EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink.state());
mock_source()->StopSource();
MediaStreamVideoSink::RemoveFromVideoTrack(&sink, track);
}
+TEST_F(MediaStreamVideoTrackTest, StopLastTrack) {
+ MockMediaStreamVideoSink sink1;
+ blink::WebMediaStreamTrack track1 = CreateTrack();
+ MediaStreamVideoSink::AddToVideoTrack(
+ &sink1, sink1.GetDeliverFrameCB(), track1);
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink1.state());
+
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
+ blink_source().readyState());
+
+ MockMediaStreamVideoSink sink2;
+ blink::WebMediaStreamTrack track2 = CreateTrack();
+ MediaStreamVideoSink::AddToVideoTrack(
+ &sink2, sink2.GetDeliverFrameCB(), track2);
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink2.state());
+
+ MediaStreamVideoTrack* native_track1 =
+ MediaStreamVideoTrack::GetVideoTrack(track1);
+ native_track1->Stop();
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink1.state());
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
+ blink_source().readyState());
+ MediaStreamVideoSink::RemoveFromVideoTrack(&sink1, track1);
+
+ MediaStreamVideoTrack* native_track2 =
+ MediaStreamVideoTrack::GetVideoTrack(track2);
+ native_track2->Stop();
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink2.state());
+ EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded,
+ blink_source().readyState());
+ MediaStreamVideoSink::RemoveFromVideoTrack(&sink2, track2);
+}
+
} // namespace content