Change VideoRtpReceiver to create remote VideoTrack and VideoTrackSource.
This enabled us to be able to remove VideoTrack::GetSink and RemoteVideoCapturer. Since video frames from the decoder is delivered on a media engine internal thread, VideoBroadCaster must be made thread safe. BUG=webrtc:5426 R=deadbeef@webrtc.org, pthatcher@webrtc.org Review URL: https://codereview.webrtc.org/1765423005 . Cr-Commit-Position: refs/heads/master@{#11944}
This commit is contained in:
@ -37,14 +37,13 @@
|
||||
'fakemetricsobserver.h',
|
||||
'jsepsessiondescription_unittest.cc',
|
||||
'localaudiosource_unittest.cc',
|
||||
'mediaconstraintsinterface_unittest.cc',
|
||||
'mediaconstraintsinterface_unittest.cc',
|
||||
'mediastream_unittest.cc',
|
||||
'peerconnection_unittest.cc',
|
||||
'peerconnectionendtoend_unittest.cc',
|
||||
'peerconnectionfactory_unittest.cc',
|
||||
'peerconnectioninterface_unittest.cc',
|
||||
# 'peerconnectionproxy_unittest.cc',
|
||||
'remotevideocapturer_unittest.cc',
|
||||
'rtpsenderreceiver_unittest.cc',
|
||||
'statscollector_unittest.cc',
|
||||
'test/fakeaudiocapturemodule.cc',
|
||||
|
||||
@ -177,26 +177,6 @@ class VideoTrackInterface
|
||||
|
||||
virtual VideoTrackSourceInterface* GetSource() const = 0;
|
||||
|
||||
// Return the track input sink. I.e., frames sent to this sink are
|
||||
// propagated to all renderers registered with the track. The
|
||||
// returned sink must not change between calls. Currently, this
|
||||
// method is used for remote tracks (VideoRtpReceiver); further
|
||||
// refactoring is planned for this path, it's unclear if this method
|
||||
// belongs here long term.
|
||||
|
||||
// We do this instead of simply implementing the
|
||||
// VideoSourceInterface directly, because if we did the latter, we'd
|
||||
// need an OnFrame method in VideoTrackProxy, with a thread jump on
|
||||
// each call.
|
||||
|
||||
// TODO(nisse): It has a default implementation so that mock
|
||||
// objects, in particular, chrome's MockWebRtcVideoTrack, doesn't
|
||||
// need to know about it. Consider removing the implementation (or
|
||||
// this comment) after refactoring dust settles.
|
||||
virtual rtc::VideoSinkInterface<cricket::VideoFrame>* GetSink() {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual ~VideoTrackInterface() {}
|
||||
};
|
||||
|
||||
@ -54,7 +54,6 @@ BEGIN_PROXY_MAP(VideoTrack)
|
||||
const rtc::VideoSinkWants&)
|
||||
PROXY_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<cricket::VideoFrame>*)
|
||||
PROXY_CONSTMETHOD0(VideoTrackSourceInterface*, GetSource)
|
||||
PROXY_METHOD0(rtc::VideoSinkInterface<cricket::VideoFrame>*, GetSink)
|
||||
|
||||
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
|
||||
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
|
||||
|
||||
@ -381,10 +381,8 @@ namespace webrtc {
|
||||
// Factory class for creating remote MediaStreams and MediaStreamTracks.
|
||||
class RemoteMediaStreamFactory {
|
||||
public:
|
||||
explicit RemoteMediaStreamFactory(rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread)
|
||||
: signaling_thread_(signaling_thread),
|
||||
worker_thread_(worker_thread) {}
|
||||
explicit RemoteMediaStreamFactory(rtc::Thread* signaling_thread)
|
||||
: signaling_thread_(signaling_thread) {}
|
||||
|
||||
rtc::scoped_refptr<MediaStreamInterface> CreateMediaStream(
|
||||
const std::string& stream_label) {
|
||||
@ -400,15 +398,6 @@ class RemoteMediaStreamFactory {
|
||||
stream, track_id, RemoteAudioSource::Create(ssrc, provider));
|
||||
}
|
||||
|
||||
VideoTrackInterface* AddVideoTrack(webrtc::MediaStreamInterface* stream,
|
||||
const std::string& track_id) {
|
||||
return AddTrack<VideoTrackInterface, VideoTrack, VideoTrackProxy>(
|
||||
stream, track_id,
|
||||
VideoCapturerTrackSource::Create(
|
||||
worker_thread_, new RemoteVideoCapturer(), nullptr, true)
|
||||
.get());
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename TI, typename T, typename TP, typename S>
|
||||
TI* AddTrack(MediaStreamInterface* stream,
|
||||
@ -424,7 +413,6 @@ class RemoteMediaStreamFactory {
|
||||
}
|
||||
|
||||
rtc::Thread* signaling_thread_;
|
||||
rtc::Thread* worker_thread_;
|
||||
};
|
||||
|
||||
bool ExtractMediaSessionOptions(
|
||||
@ -611,8 +599,8 @@ bool PeerConnection::Initialize(
|
||||
|
||||
media_controller_.reset(factory_->CreateMediaController(media_config));
|
||||
|
||||
remote_stream_factory_.reset(new RemoteMediaStreamFactory(
|
||||
factory_->signaling_thread(), factory_->worker_thread()));
|
||||
remote_stream_factory_.reset(
|
||||
new RemoteMediaStreamFactory(factory_->signaling_thread()));
|
||||
|
||||
session_.reset(
|
||||
new WebRtcSession(media_controller_.get(), factory_->signaling_thread(),
|
||||
@ -1325,11 +1313,12 @@ void PeerConnection::CreateAudioReceiver(MediaStreamInterface* stream,
|
||||
}
|
||||
|
||||
void PeerConnection::CreateVideoReceiver(MediaStreamInterface* stream,
|
||||
VideoTrackInterface* video_track,
|
||||
const std::string& track_id,
|
||||
uint32_t ssrc) {
|
||||
receivers_.push_back(RtpReceiverProxy::Create(
|
||||
signaling_thread(),
|
||||
new VideoRtpReceiver(video_track, ssrc, session_.get())));
|
||||
VideoRtpReceiver* video_receiver = new VideoRtpReceiver(
|
||||
stream, track_id, factory_->worker_thread(), ssrc, session_.get());
|
||||
receivers_.push_back(
|
||||
RtpReceiverProxy::Create(signaling_thread(), video_receiver));
|
||||
}
|
||||
|
||||
// TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote
|
||||
@ -1677,9 +1666,7 @@ void PeerConnection::OnRemoteTrackSeen(const std::string& stream_label,
|
||||
ssrc, session_.get(), stream, track_id);
|
||||
CreateAudioReceiver(stream, audio_track, ssrc);
|
||||
} else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
|
||||
VideoTrackInterface* video_track =
|
||||
remote_stream_factory_->AddVideoTrack(stream, track_id);
|
||||
CreateVideoReceiver(stream, video_track, ssrc);
|
||||
CreateVideoReceiver(stream, track_id, ssrc);
|
||||
} else {
|
||||
RTC_DCHECK(false && "Invalid media type");
|
||||
}
|
||||
@ -1702,8 +1689,9 @@ void PeerConnection::OnRemoteTrackRemoved(const std::string& stream_label,
|
||||
rtc::scoped_refptr<VideoTrackInterface> video_track =
|
||||
stream->FindVideoTrack(track_id);
|
||||
if (video_track) {
|
||||
video_track->set_state(webrtc::MediaStreamTrackInterface::kEnded);
|
||||
stream->RemoveTrack(video_track);
|
||||
// Stopping or destroying a VideoRtpReceiver will end the
|
||||
// VideoRtpReceiver::track().
|
||||
DestroyVideoReceiver(stream, video_track);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -27,6 +27,7 @@ namespace webrtc {
|
||||
|
||||
class MediaStreamObserver;
|
||||
class RemoteMediaStreamFactory;
|
||||
class VideoRtpReceiver;
|
||||
|
||||
// Populates |session_options| from |rtc_options|, and returns true if options
|
||||
// are valid.
|
||||
@ -167,8 +168,9 @@ class PeerConnection : public PeerConnectionInterface,
|
||||
void CreateAudioReceiver(MediaStreamInterface* stream,
|
||||
AudioTrackInterface* audio_track,
|
||||
uint32_t ssrc);
|
||||
|
||||
void CreateVideoReceiver(MediaStreamInterface* stream,
|
||||
VideoTrackInterface* video_track,
|
||||
const std::string& track_id,
|
||||
uint32_t ssrc);
|
||||
void DestroyAudioReceiver(MediaStreamInterface* stream,
|
||||
AudioTrackInterface* audio_track);
|
||||
|
||||
@ -10,69 +10,4 @@
|
||||
|
||||
#include "webrtc/api/remotevideocapturer.h"
|
||||
|
||||
#include "webrtc/base/logging.h"
|
||||
#include "webrtc/media/base/videoframe.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
RemoteVideoCapturer::RemoteVideoCapturer() {}
|
||||
|
||||
RemoteVideoCapturer::~RemoteVideoCapturer() {}
|
||||
|
||||
cricket::CaptureState RemoteVideoCapturer::Start(
|
||||
const cricket::VideoFormat& capture_format) {
|
||||
if (capture_state() == cricket::CS_RUNNING) {
|
||||
LOG(LS_WARNING)
|
||||
<< "RemoteVideoCapturer::Start called when it's already started.";
|
||||
return capture_state();
|
||||
}
|
||||
|
||||
LOG(LS_INFO) << "RemoteVideoCapturer::Start";
|
||||
SetCaptureFormat(&capture_format);
|
||||
return cricket::CS_RUNNING;
|
||||
}
|
||||
|
||||
void RemoteVideoCapturer::Stop() {
|
||||
if (capture_state() == cricket::CS_STOPPED) {
|
||||
LOG(LS_WARNING)
|
||||
<< "RemoteVideoCapturer::Stop called when it's already stopped.";
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LS_INFO) << "RemoteVideoCapturer::Stop";
|
||||
SetCaptureFormat(NULL);
|
||||
SetCaptureState(cricket::CS_STOPPED);
|
||||
}
|
||||
|
||||
bool RemoteVideoCapturer::IsRunning() {
|
||||
return capture_state() == cricket::CS_RUNNING;
|
||||
}
|
||||
|
||||
bool RemoteVideoCapturer::GetPreferredFourccs(std::vector<uint32_t>* fourccs) {
|
||||
if (!fourccs)
|
||||
return false;
|
||||
fourccs->push_back(cricket::FOURCC_I420);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteVideoCapturer::GetBestCaptureFormat(
|
||||
const cricket::VideoFormat& desired, cricket::VideoFormat* best_format) {
|
||||
if (!best_format) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// RemoteVideoCapturer does not support capability enumeration.
|
||||
// Use the desired format as the best format.
|
||||
best_format->width = desired.width;
|
||||
best_format->height = desired.height;
|
||||
best_format->fourcc = cricket::FOURCC_I420;
|
||||
best_format->interval = desired.interval;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteVideoCapturer::IsScreencast() const {
|
||||
// TODO(ronghuawu): what about remote screencast stream.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
// TODO(perkj): Remove this file once Chrome gyp file doesn't depend on it.
|
||||
|
||||
@ -11,38 +11,6 @@
|
||||
#ifndef WEBRTC_API_REMOTEVIDEOCAPTURER_H_
|
||||
#define WEBRTC_API_REMOTEVIDEOCAPTURER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/api/mediastreaminterface.h"
|
||||
#include "webrtc/media/base/videocapturer.h"
|
||||
#include "webrtc/media/base/videorenderer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// RemoteVideoCapturer implements a simple cricket::VideoCapturer which
|
||||
// gets decoded remote video frames from media channel.
|
||||
// It's used as the remote video source's VideoCapturer so that the remote video
|
||||
// can be used as a cricket::VideoCapturer and in that way a remote video stream
|
||||
// can implement the MediaStreamSourceInterface.
|
||||
class RemoteVideoCapturer : public cricket::VideoCapturer {
|
||||
public:
|
||||
RemoteVideoCapturer();
|
||||
virtual ~RemoteVideoCapturer();
|
||||
|
||||
// cricket::VideoCapturer implementation.
|
||||
cricket::CaptureState Start(
|
||||
const cricket::VideoFormat& capture_format) override;
|
||||
void Stop() override;
|
||||
bool IsRunning() override;
|
||||
bool GetPreferredFourccs(std::vector<uint32_t>* fourccs) override;
|
||||
bool GetBestCaptureFormat(const cricket::VideoFormat& desired,
|
||||
cricket::VideoFormat* best_format) override;
|
||||
bool IsScreencast() const override;
|
||||
|
||||
private:
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RemoteVideoCapturer);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
// TODO(perkj): Remove this file once Chrome gyp file doesn't depend on it.
|
||||
|
||||
#endif // WEBRTC_API_REMOTEVIDEOCAPTURER_H_
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/api/remotevideocapturer.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/media/engine/webrtcvideoframe.h"
|
||||
|
||||
using cricket::CaptureState;
|
||||
using cricket::VideoCapturer;
|
||||
using cricket::VideoFormat;
|
||||
using cricket::VideoFormatPod;
|
||||
using cricket::VideoFrame;
|
||||
|
||||
static const int kMaxWaitMs = 1000;
|
||||
static const VideoFormatPod kTestFormat =
|
||||
{640, 480, FPS_TO_INTERVAL(30), cricket::FOURCC_ANY};
|
||||
|
||||
class RemoteVideoCapturerTest : public testing::Test,
|
||||
public sigslot::has_slots<> {
|
||||
protected:
|
||||
RemoteVideoCapturerTest()
|
||||
: captured_frame_num_(0),
|
||||
capture_state_(cricket::CS_STOPPED) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
capturer_.SignalStateChange.connect(
|
||||
this, &RemoteVideoCapturerTest::OnStateChange);
|
||||
}
|
||||
|
||||
~RemoteVideoCapturerTest() {
|
||||
capturer_.SignalStateChange.disconnect(this);
|
||||
}
|
||||
|
||||
int captured_frame_num() const {
|
||||
return captured_frame_num_;
|
||||
}
|
||||
|
||||
CaptureState capture_state() const {
|
||||
return capture_state_;
|
||||
}
|
||||
|
||||
webrtc::RemoteVideoCapturer capturer_;
|
||||
|
||||
private:
|
||||
void OnStateChange(VideoCapturer* capturer,
|
||||
CaptureState capture_state) {
|
||||
EXPECT_EQ(&capturer_, capturer);
|
||||
capture_state_ = capture_state;
|
||||
}
|
||||
|
||||
int captured_frame_num_;
|
||||
CaptureState capture_state_;
|
||||
};
|
||||
|
||||
TEST_F(RemoteVideoCapturerTest, StartStop) {
|
||||
// Start
|
||||
EXPECT_TRUE(
|
||||
capturer_.StartCapturing(VideoFormat(kTestFormat)));
|
||||
EXPECT_TRUE_WAIT((cricket::CS_RUNNING == capture_state()), kMaxWaitMs);
|
||||
EXPECT_EQ(VideoFormat(kTestFormat),
|
||||
*capturer_.GetCaptureFormat());
|
||||
EXPECT_TRUE(capturer_.IsRunning());
|
||||
|
||||
// Stop
|
||||
capturer_.Stop();
|
||||
EXPECT_TRUE_WAIT((cricket::CS_STOPPED == capture_state()), kMaxWaitMs);
|
||||
EXPECT_TRUE(NULL == capturer_.GetCaptureFormat());
|
||||
}
|
||||
|
||||
TEST_F(RemoteVideoCapturerTest, GetPreferredFourccs) {
|
||||
EXPECT_FALSE(capturer_.GetPreferredFourccs(NULL));
|
||||
|
||||
std::vector<uint32_t> fourccs;
|
||||
EXPECT_TRUE(capturer_.GetPreferredFourccs(&fourccs));
|
||||
EXPECT_EQ(1u, fourccs.size());
|
||||
EXPECT_EQ(cricket::FOURCC_I420, fourccs.at(0));
|
||||
}
|
||||
|
||||
TEST_F(RemoteVideoCapturerTest, GetBestCaptureFormat) {
|
||||
VideoFormat desired = VideoFormat(kTestFormat);
|
||||
EXPECT_FALSE(capturer_.GetBestCaptureFormat(desired, NULL));
|
||||
|
||||
VideoFormat expected_format = VideoFormat(kTestFormat);
|
||||
expected_format.fourcc = cricket::FOURCC_I420;
|
||||
VideoFormat best_format;
|
||||
EXPECT_TRUE(capturer_.GetBestCaptureFormat(desired, &best_format));
|
||||
EXPECT_EQ(expected_format, best_format);
|
||||
}
|
||||
@ -10,7 +10,8 @@
|
||||
|
||||
#include "webrtc/api/rtpreceiver.h"
|
||||
|
||||
#include "webrtc/api/videosourceinterface.h"
|
||||
#include "webrtc/api/mediastreamtrackproxy.h"
|
||||
#include "webrtc/api/videotrack.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -65,11 +66,27 @@ void AudioRtpReceiver::Reconfigure() {
|
||||
provider_->SetAudioPlayout(ssrc_, track_->enabled());
|
||||
}
|
||||
|
||||
VideoRtpReceiver::VideoRtpReceiver(VideoTrackInterface* track,
|
||||
VideoRtpReceiver::VideoRtpReceiver(MediaStreamInterface* stream,
|
||||
const std::string& track_id,
|
||||
rtc::Thread* worker_thread,
|
||||
uint32_t ssrc,
|
||||
VideoProviderInterface* provider)
|
||||
: id_(track->id()), track_(track), ssrc_(ssrc), provider_(provider) {
|
||||
provider_->SetVideoPlayout(ssrc_, true, track_->GetSink());
|
||||
: id_(track_id),
|
||||
ssrc_(ssrc),
|
||||
provider_(provider),
|
||||
source_(new RefCountedObject<VideoTrackSource>(&broadcaster_,
|
||||
worker_thread,
|
||||
true /* remote */)),
|
||||
track_(VideoTrackProxy::Create(
|
||||
rtc::Thread::Current(),
|
||||
VideoTrack::Create(track_id, source_.get()))) {
|
||||
source_->SetState(MediaSourceInterface::kLive);
|
||||
// TODO(perkj): It should be enough to set the source state. All tracks
|
||||
// belonging to the same source should get its state from the source.
|
||||
// I.e. if a track has been cloned from a remote source.
|
||||
track_->set_state(webrtc::MediaStreamTrackInterface::kLive);
|
||||
provider_->SetVideoPlayout(ssrc_, true, &broadcaster_);
|
||||
stream->AddTrack(track_);
|
||||
}
|
||||
|
||||
VideoRtpReceiver::~VideoRtpReceiver() {
|
||||
@ -83,6 +100,11 @@ void VideoRtpReceiver::Stop() {
|
||||
if (!provider_) {
|
||||
return;
|
||||
}
|
||||
source_->SetState(MediaSourceInterface::kEnded);
|
||||
source_->OnSourceDestroyed();
|
||||
// TODO(perkj): It should be enough to set the source state. All tracks
|
||||
// belonging to the same source should get its state from the source.
|
||||
track_->set_state(MediaStreamTrackInterface::kEnded);
|
||||
provider_->SetVideoPlayout(ssrc_, false, nullptr);
|
||||
provider_ = nullptr;
|
||||
}
|
||||
|
||||
@ -19,7 +19,9 @@
|
||||
|
||||
#include "webrtc/api/mediastreamprovider.h"
|
||||
#include "webrtc/api/rtpreceiverinterface.h"
|
||||
#include "webrtc/api/videotracksource.h"
|
||||
#include "webrtc/base/basictypes.h"
|
||||
#include "webrtc/media/base/videobroadcaster.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -60,12 +62,18 @@ class AudioRtpReceiver : public ObserverInterface,
|
||||
|
||||
class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInterface> {
|
||||
public:
|
||||
VideoRtpReceiver(VideoTrackInterface* track,
|
||||
VideoRtpReceiver(MediaStreamInterface* stream,
|
||||
const std::string& track_id,
|
||||
rtc::Thread* worker_thread,
|
||||
uint32_t ssrc,
|
||||
VideoProviderInterface* provider);
|
||||
|
||||
virtual ~VideoRtpReceiver();
|
||||
|
||||
rtc::scoped_refptr<VideoTrackInterface> video_track() const {
|
||||
return track_.get();
|
||||
}
|
||||
|
||||
// RtpReceiverInterface implementation
|
||||
rtc::scoped_refptr<MediaStreamTrackInterface> track() const override {
|
||||
return track_.get();
|
||||
@ -77,9 +85,16 @@ class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInterface> {
|
||||
|
||||
private:
|
||||
std::string id_;
|
||||
rtc::scoped_refptr<VideoTrackInterface> track_;
|
||||
uint32_t ssrc_;
|
||||
VideoProviderInterface* provider_;
|
||||
// |broadcaster_| is needed since the decoder can only handle one sink.
|
||||
// It might be better if the decoder can handle multiple sinks and consider
|
||||
// the VideoSinkWants.
|
||||
rtc::VideoBroadcaster broadcaster_;
|
||||
// |source_| is held here to be able to change the state of the source when
|
||||
// the VideoRtpReceiver is stopped.
|
||||
rtc::scoped_refptr<VideoTrackSource> source_;
|
||||
rtc::scoped_refptr<VideoTrackInterface> track_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -150,12 +150,11 @@ class RtpSenderReceiverTest : public testing::Test {
|
||||
}
|
||||
|
||||
void CreateVideoRtpReceiver() {
|
||||
AddVideoTrack(true);
|
||||
EXPECT_CALL(video_provider_,
|
||||
SetVideoPlayout(kVideoSsrc, true,
|
||||
video_track_->GetSink()));
|
||||
video_rtp_receiver_ = new VideoRtpReceiver(stream_->GetVideoTracks()[0],
|
||||
kVideoSsrc, &video_provider_);
|
||||
EXPECT_CALL(video_provider_, SetVideoPlayout(kVideoSsrc, true, _));
|
||||
video_rtp_receiver_ =
|
||||
new VideoRtpReceiver(stream_, kVideoTrackId, rtc::Thread::Current(),
|
||||
kVideoSsrc, &video_provider_);
|
||||
video_track_ = video_rtp_receiver_->video_track();
|
||||
}
|
||||
|
||||
void DestroyAudioRtpReceiver() {
|
||||
@ -244,6 +243,20 @@ TEST_F(RtpSenderReceiverTest, LocalVideoTrackDisable) {
|
||||
DestroyVideoRtpSender();
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, RemoteVideoTrackState) {
|
||||
CreateVideoRtpReceiver();
|
||||
|
||||
EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, video_track_->state());
|
||||
EXPECT_EQ(webrtc::MediaSourceInterface::kLive,
|
||||
video_track_->GetSource()->state());
|
||||
|
||||
DestroyVideoRtpReceiver();
|
||||
|
||||
EXPECT_EQ(webrtc::MediaStreamTrackInterface::kEnded, video_track_->state());
|
||||
EXPECT_EQ(webrtc::MediaSourceInterface::kEnded,
|
||||
video_track_->GetSource()->state());
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, RemoteVideoTrackDisable) {
|
||||
CreateVideoRtpReceiver();
|
||||
|
||||
|
||||
@ -182,27 +182,6 @@ TEST_F(VideoCapturerTrackSourceTest, StopRestart) {
|
||||
source_->Stop();
|
||||
}
|
||||
|
||||
// Test start stop with a remote VideoSource - the video source that has a
|
||||
// RemoteVideoCapturer and takes video frames from FrameInput.
|
||||
TEST_F(VideoCapturerTrackSourceTest, StartStopRemote) {
|
||||
source_ = VideoCapturerTrackSource::Create(
|
||||
rtc::Thread::Current(), new webrtc::RemoteVideoCapturer(), NULL, true);
|
||||
|
||||
ASSERT_TRUE(source_.get() != NULL);
|
||||
EXPECT_TRUE(NULL != source_->GetVideoCapturer());
|
||||
|
||||
state_observer_.reset(new StateObserver(source_));
|
||||
source_->RegisterObserver(state_observer_.get());
|
||||
source_->AddOrUpdateSink(&renderer_, rtc::VideoSinkWants());
|
||||
|
||||
EXPECT_EQ_WAIT(MediaSourceInterface::kLive, state_observer_->state(),
|
||||
kMaxWaitMs);
|
||||
|
||||
source_->GetVideoCapturer()->Stop();
|
||||
EXPECT_EQ_WAIT(MediaSourceInterface::kEnded, state_observer_->state(),
|
||||
kMaxWaitMs);
|
||||
}
|
||||
|
||||
// Test that a VideoSource transition to kEnded if the capture device
|
||||
// fails.
|
||||
TEST_F(VideoCapturerTrackSourceTest, CameraFailed) {
|
||||
|
||||
@ -50,10 +50,6 @@ void VideoTrack::RemoveSink(
|
||||
renderers_.RemoveSink(sink);
|
||||
}
|
||||
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* VideoTrack::GetSink() {
|
||||
return &renderers_;
|
||||
}
|
||||
|
||||
bool VideoTrack::set_enabled(bool enable) {
|
||||
renderers_.SetEnabled(enable);
|
||||
return MediaStreamTrack<VideoTrackInterface>::set_enabled(enable);
|
||||
|
||||
@ -33,7 +33,6 @@ class VideoTrack : public MediaStreamTrack<VideoTrackInterface> {
|
||||
virtual VideoTrackSourceInterface* GetSource() const {
|
||||
return video_source_.get();
|
||||
}
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* GetSink() override;
|
||||
virtual bool set_enabled(bool enable);
|
||||
virtual std::string kind() const;
|
||||
|
||||
|
||||
@ -10,42 +10,37 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/api/remotevideocapturer.h"
|
||||
#include "webrtc/api/test/fakevideotrackrenderer.h"
|
||||
#include "webrtc/api/videocapturertracksource.h"
|
||||
#include "webrtc/api/videotrack.h"
|
||||
#include "webrtc/base/gunit.h"
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/media/base/fakevideocapturer.h"
|
||||
#include "webrtc/media/base/fakemediaengine.h"
|
||||
#include "webrtc/media/engine/webrtcvideoframe.h"
|
||||
#include "webrtc/pc/channelmanager.h"
|
||||
|
||||
using webrtc::FakeVideoTrackRenderer;
|
||||
using webrtc::FakeVideoTrackRendererOld;
|
||||
using webrtc::VideoCapturerTrackSource;
|
||||
using webrtc::VideoTrackSource;
|
||||
using webrtc::VideoTrack;
|
||||
using webrtc::VideoTrackInterface;
|
||||
|
||||
namespace {
|
||||
|
||||
class WebRtcVideoTestFrame : public cricket::WebRtcVideoFrame {
|
||||
public:
|
||||
using cricket::WebRtcVideoFrame::SetRotation;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class VideoTrackTest : public testing::Test {
|
||||
public:
|
||||
VideoTrackTest() {
|
||||
static const char kVideoTrackId[] = "track_id";
|
||||
video_track_ = VideoTrack::Create(
|
||||
kVideoTrackId, VideoCapturerTrackSource::Create(
|
||||
rtc::Thread::Current(),
|
||||
new webrtc::RemoteVideoCapturer(), NULL, true));
|
||||
kVideoTrackId,
|
||||
new rtc::RefCountedObject<VideoTrackSource>(
|
||||
&capturer_, rtc::Thread::Current(), true /* remote */));
|
||||
capturer_.Start(
|
||||
cricket::VideoFormat(640, 480, cricket::VideoFormat::FpsToInterval(30),
|
||||
cricket::FOURCC_I420));
|
||||
}
|
||||
|
||||
protected:
|
||||
cricket::FakeVideoCapturer capturer_;
|
||||
rtc::scoped_refptr<VideoTrackInterface> video_track_;
|
||||
};
|
||||
|
||||
@ -56,35 +51,18 @@ TEST_F(VideoTrackTest, RenderVideo) {
|
||||
rtc::scoped_ptr<FakeVideoTrackRenderer> renderer_1(
|
||||
new FakeVideoTrackRenderer(video_track_.get()));
|
||||
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* renderer_input =
|
||||
video_track_->GetSink();
|
||||
ASSERT_FALSE(renderer_input == NULL);
|
||||
|
||||
cricket::WebRtcVideoFrame frame;
|
||||
frame.InitToBlack(123, 123, 0);
|
||||
renderer_input->OnFrame(frame);
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(1, renderer_1->num_rendered_frames());
|
||||
|
||||
EXPECT_EQ(123, renderer_1->width());
|
||||
EXPECT_EQ(123, renderer_1->height());
|
||||
|
||||
// FakeVideoTrackRenderer register itself to |video_track_|
|
||||
rtc::scoped_ptr<FakeVideoTrackRenderer> renderer_2(
|
||||
new FakeVideoTrackRenderer(video_track_.get()));
|
||||
|
||||
renderer_input->OnFrame(frame);
|
||||
|
||||
EXPECT_EQ(123, renderer_1->width());
|
||||
EXPECT_EQ(123, renderer_1->height());
|
||||
EXPECT_EQ(123, renderer_2->width());
|
||||
EXPECT_EQ(123, renderer_2->height());
|
||||
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(2, renderer_1->num_rendered_frames());
|
||||
EXPECT_EQ(1, renderer_2->num_rendered_frames());
|
||||
|
||||
video_track_->RemoveSink(renderer_1.get());
|
||||
renderer_input->OnFrame(frame);
|
||||
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(2, renderer_1->num_rendered_frames());
|
||||
EXPECT_EQ(2, renderer_2->num_rendered_frames());
|
||||
}
|
||||
@ -97,35 +75,19 @@ TEST_F(VideoTrackTest, RenderVideoOld) {
|
||||
rtc::scoped_ptr<FakeVideoTrackRendererOld> renderer_1(
|
||||
new FakeVideoTrackRendererOld(video_track_.get()));
|
||||
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* renderer_input =
|
||||
video_track_->GetSink();
|
||||
ASSERT_FALSE(renderer_input == NULL);
|
||||
|
||||
cricket::WebRtcVideoFrame frame;
|
||||
frame.InitToBlack(123, 123, 0);
|
||||
renderer_input->OnFrame(frame);
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(1, renderer_1->num_rendered_frames());
|
||||
|
||||
EXPECT_EQ(123, renderer_1->width());
|
||||
EXPECT_EQ(123, renderer_1->height());
|
||||
|
||||
// FakeVideoTrackRenderer register itself to |video_track_|
|
||||
rtc::scoped_ptr<FakeVideoTrackRenderer> renderer_2(
|
||||
new FakeVideoTrackRenderer(video_track_.get()));
|
||||
|
||||
renderer_input->OnFrame(frame);
|
||||
|
||||
EXPECT_EQ(123, renderer_1->width());
|
||||
EXPECT_EQ(123, renderer_1->height());
|
||||
EXPECT_EQ(123, renderer_2->width());
|
||||
EXPECT_EQ(123, renderer_2->height());
|
||||
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(2, renderer_1->num_rendered_frames());
|
||||
EXPECT_EQ(1, renderer_2->num_rendered_frames());
|
||||
|
||||
video_track_->RemoveRenderer(renderer_1.get());
|
||||
renderer_input->OnFrame(frame);
|
||||
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(2, renderer_1->num_rendered_frames());
|
||||
EXPECT_EQ(2, renderer_2->num_rendered_frames());
|
||||
}
|
||||
@ -135,32 +97,17 @@ TEST_F(VideoTrackTest, DisableTrackBlackout) {
|
||||
rtc::scoped_ptr<FakeVideoTrackRenderer> renderer(
|
||||
new FakeVideoTrackRenderer(video_track_.get()));
|
||||
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* renderer_input =
|
||||
video_track_->GetSink();
|
||||
ASSERT_FALSE(renderer_input == NULL);
|
||||
|
||||
cricket::WebRtcVideoFrame frame;
|
||||
frame.InitToBlack(100, 200, 0);
|
||||
// Make it not all-black
|
||||
frame.GetUPlane()[0] = 0;
|
||||
|
||||
renderer_input->OnFrame(frame);
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(1, renderer->num_rendered_frames());
|
||||
EXPECT_FALSE(renderer->black_frame());
|
||||
EXPECT_EQ(100, renderer->width());
|
||||
EXPECT_EQ(200, renderer->height());
|
||||
|
||||
video_track_->set_enabled(false);
|
||||
renderer_input->OnFrame(frame);
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(2, renderer->num_rendered_frames());
|
||||
EXPECT_TRUE(renderer->black_frame());
|
||||
EXPECT_EQ(100, renderer->width());
|
||||
EXPECT_EQ(200, renderer->height());
|
||||
|
||||
video_track_->set_enabled(true);
|
||||
renderer_input->OnFrame(frame);
|
||||
capturer_.CaptureFrame();
|
||||
EXPECT_EQ(3, renderer->num_rendered_frames());
|
||||
EXPECT_FALSE(renderer->black_frame());
|
||||
EXPECT_EQ(100, renderer->width());
|
||||
EXPECT_EQ(200, renderer->height());
|
||||
}
|
||||
|
||||
@ -32,9 +32,16 @@ void VideoTrackSource::SetState(SourceState new_state) {
|
||||
}
|
||||
}
|
||||
|
||||
void VideoTrackSource::OnSourceDestroyed() {
|
||||
source_ = nullptr;
|
||||
}
|
||||
|
||||
void VideoTrackSource::AddOrUpdateSink(
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
|
||||
const rtc::VideoSinkWants& wants) {
|
||||
if (!source_) {
|
||||
return;
|
||||
}
|
||||
worker_thread_->Invoke<void>(rtc::Bind(
|
||||
&rtc::VideoSourceInterface<cricket::VideoFrame>::AddOrUpdateSink, source_,
|
||||
sink, wants));
|
||||
@ -42,6 +49,9 @@ void VideoTrackSource::AddOrUpdateSink(
|
||||
|
||||
void VideoTrackSource::RemoveSink(
|
||||
rtc::VideoSinkInterface<cricket::VideoFrame>* sink) {
|
||||
if (!source_) {
|
||||
return;
|
||||
}
|
||||
worker_thread_->Invoke<void>(
|
||||
rtc::Bind(&rtc::VideoSourceInterface<cricket::VideoFrame>::RemoveSink,
|
||||
source_, sink));
|
||||
|
||||
@ -24,21 +24,27 @@ class VideoTrackSource : public Notifier<VideoTrackSourceInterface> {
|
||||
VideoTrackSource(rtc::VideoSourceInterface<cricket::VideoFrame>* source,
|
||||
rtc::Thread* worker_thread,
|
||||
bool remote);
|
||||
|
||||
void SetState(SourceState new_state);
|
||||
// OnSourceDestroyed clears this instance pointer to |source_|. It is useful
|
||||
// when the underlying rtc::VideoSourceInterface is destroyed before the
|
||||
// reference counted VideoTrackSource.
|
||||
void OnSourceDestroyed();
|
||||
|
||||
SourceState state() const override { return state_; }
|
||||
bool remote() const override { return remote_; }
|
||||
|
||||
void Stop() override{};
|
||||
void Restart() override{};
|
||||
|
||||
virtual bool is_screencast() const { return false; };
|
||||
virtual bool needs_denoising() const { return false; };
|
||||
virtual bool is_screencast() const { return false; }
|
||||
virtual bool needs_denoising() const { return false; }
|
||||
|
||||
void AddOrUpdateSink(rtc::VideoSinkInterface<cricket::VideoFrame>* sink,
|
||||
const rtc::VideoSinkWants& wants) override;
|
||||
void RemoveSink(rtc::VideoSinkInterface<cricket::VideoFrame>* sink) override;
|
||||
|
||||
cricket::VideoCapturer* GetVideoCapturer() override { return nullptr; }
|
||||
|
||||
protected:
|
||||
rtc::Thread* worker_thread() { return worker_thread_; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user