VideoRtpTrackSource: implement encoded source methods.

This change implements the methods in VideoTrackSourceInterface
that are related to encoded output.

Bug: chromium:1013590
Change-Id: Id9ddbc00a7098e9b44cee1517c69002865a5fb33
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/159926
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29912}
This commit is contained in:
Markus Handell 2019-11-26 09:30:08 +01:00 committed by Commit Bot
parent 72e6cb0b3f
commit d5e2f215d6
6 changed files with 242 additions and 9 deletions

View File

@ -35,7 +35,7 @@ PROXY_WORKER_METHOD1(void, RemoveSink, rtc::VideoSinkInterface<VideoFrame>*)
PROXY_METHOD1(void, RegisterObserver, ObserverInterface*)
PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*)
PROXY_CONSTMETHOD0(bool, SupportsEncodedOutput)
PROXY_METHOD0(void, GenerateKeyFrame)
PROXY_WORKER_METHOD0(void, GenerateKeyFrame)
PROXY_WORKER_METHOD1(void,
AddEncodedSink,
rtc::VideoSinkInterface<RecordableEncodedFrame>*)

View File

@ -42,7 +42,7 @@ VideoRtpReceiver::VideoRtpReceiver(
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>& streams)
: worker_thread_(worker_thread),
id_(receiver_id),
source_(new RefCountedObject<VideoRtpTrackSource>()),
source_(new RefCountedObject<VideoRtpTrackSource>(this)),
track_(VideoTrackProxy::Create(
rtc::Thread::Current(),
worker_thread,
@ -66,6 +66,9 @@ VideoRtpReceiver::~VideoRtpReceiver() {
// Since cricket::VideoRenderer is not reference counted,
// we need to remove it from the channel before we are deleted.
Stop();
// Make sure we can't be called by the |source_| anymore.
worker_thread_->Invoke<void>(RTC_FROM_HERE,
[this] { source_->ClearCallback(); });
}
std::vector<std::string> VideoRtpReceiver::stream_ids() const {
@ -246,4 +249,12 @@ std::vector<RtpSource> VideoRtpReceiver::GetSources() const {
RTC_FROM_HERE, [&] { return media_channel_->GetSources(*ssrc_); });
}
void VideoRtpReceiver::OnGenerateKeyFrame() {
RTC_DCHECK_RUN_ON(worker_thread_);
}
void VideoRtpReceiver::OnEncodedSinkEnabled(bool enable) {
RTC_DCHECK_RUN_ON(worker_thread_);
}
} // namespace webrtc

View File

@ -35,7 +35,8 @@
namespace webrtc {
class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInternal> {
class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInternal>,
public VideoRtpTrackSource::Callback {
public:
// An SSRC of 0 will create a receiver that will match the first SSRC it
// sees. Must be called on signaling thread.
@ -112,7 +113,12 @@ class VideoRtpReceiver : public rtc::RefCountedObject<RtpReceiverInternal> {
void RestartMediaChannel(absl::optional<uint32_t> ssrc);
bool SetSink(rtc::VideoSinkInterface<VideoFrame>* sink);
// VideoRtpTrackSource::Callback
void OnGenerateKeyFrame() override;
void OnEncodedSinkEnabled(bool enable) override;
rtc::Thread* const worker_thread_;
const std::string id_;
cricket::VideoMediaChannel* media_channel_ = nullptr;
absl::optional<uint32_t> ssrc_;

View File

@ -12,8 +12,15 @@
namespace webrtc {
VideoRtpTrackSource::VideoRtpTrackSource()
: VideoTrackSource(true /* remote */) {}
VideoRtpTrackSource::VideoRtpTrackSource(Callback* callback)
: VideoTrackSource(true /* remote */), callback_(callback) {
worker_sequence_checker_.Detach();
}
void VideoRtpTrackSource::ClearCallback() {
RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
callback_ = nullptr;
}
rtc::VideoSourceInterface<VideoFrame>* VideoRtpTrackSource::source() {
return &broadcaster_;
@ -22,4 +29,57 @@ rtc::VideoSinkInterface<VideoFrame>* VideoRtpTrackSource::sink() {
return &broadcaster_;
}
void VideoRtpTrackSource::BroadcastRecordableEncodedFrame(
const RecordableEncodedFrame& frame) const {
rtc::CritScope cs(&mu_);
for (rtc::VideoSinkInterface<RecordableEncodedFrame>* sink : encoded_sinks_) {
sink->OnFrame(frame);
}
}
bool VideoRtpTrackSource::SupportsEncodedOutput() const {
return true;
}
void VideoRtpTrackSource::GenerateKeyFrame() {
RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
if (callback_) {
callback_->OnGenerateKeyFrame();
}
}
void VideoRtpTrackSource::AddEncodedSink(
rtc::VideoSinkInterface<RecordableEncodedFrame>* sink) {
RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
RTC_DCHECK(sink);
size_t size = 0;
{
rtc::CritScope cs(&mu_);
RTC_DCHECK(std::find(encoded_sinks_.begin(), encoded_sinks_.end(), sink) ==
encoded_sinks_.end());
encoded_sinks_.push_back(sink);
size = encoded_sinks_.size();
}
if (size == 1 && callback_) {
callback_->OnEncodedSinkEnabled(true);
}
}
void VideoRtpTrackSource::RemoveEncodedSink(
rtc::VideoSinkInterface<RecordableEncodedFrame>* sink) {
RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
size_t size = 0;
{
rtc::CritScope cs(&mu_);
auto it = std::find(encoded_sinks_.begin(), encoded_sinks_.end(), sink);
if (it != encoded_sinks_.end()) {
encoded_sinks_.erase(it);
}
size = encoded_sinks_.size();
}
if (size == 0 && callback_) {
callback_->OnEncodedSinkEnabled(false);
}
}
} // namespace webrtc

View File

@ -11,25 +11,71 @@
#ifndef PC_VIDEO_RTP_TRACK_SOURCE_H_
#define PC_VIDEO_RTP_TRACK_SOURCE_H_
#include <vector>
#include "media/base/video_broadcaster.h"
#include "pc/video_track_source.h"
#include "rtc_base/callback.h"
#include "rtc_base/critical_section.h"
namespace webrtc {
// Video track source in use by VideoRtpReceiver
class VideoRtpTrackSource : public VideoTrackSource {
public:
VideoRtpTrackSource();
class Callback {
public:
virtual ~Callback() = default;
// Called when a keyframe should be generated
virtual void OnGenerateKeyFrame() = 0;
// Called when the implementor should eventually start to serve encoded
// frames using BroadcastEncodedFrameBuffer.
// The implementor should cause a keyframe to be eventually generated.
virtual void OnEncodedSinkEnabled(bool enable) = 0;
};
explicit VideoRtpTrackSource(Callback* callback);
// Call before the object implementing Callback finishes it's destructor. No
// more callbacks will be fired after completion. Must be called on the
// worker thread
void ClearCallback();
// Call to broadcast an encoded frame to registered sinks.
// This method can be called on any thread or queue.
void BroadcastRecordableEncodedFrame(
const RecordableEncodedFrame& frame) const;
// VideoTrackSource
rtc::VideoSourceInterface<VideoFrame>* source() override;
rtc::VideoSinkInterface<VideoFrame>* sink();
// Returns true. This method can be called on any thread.
bool SupportsEncodedOutput() const override;
// Generates a key frame. Must be called on the worker thread.
void GenerateKeyFrame() override;
// Adds an encoded sink. Must be called on the worker thread.
void AddEncodedSink(
rtc::VideoSinkInterface<RecordableEncodedFrame>* sink) override;
// Removes an encoded sink. Must be called on the worker thread.
void RemoveEncodedSink(
rtc::VideoSinkInterface<RecordableEncodedFrame>* sink) override;
private:
SequenceChecker worker_sequence_checker_;
// |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_;
rtc::CriticalSection mu_;
std::vector<rtc::VideoSinkInterface<RecordableEncodedFrame>*> encoded_sinks_
RTC_GUARDED_BY(mu_);
Callback* callback_ RTC_GUARDED_BY(worker_sequence_checker_);
RTC_DISALLOW_COPY_AND_ASSIGN(VideoRtpTrackSource);
};

View File

@ -17,10 +17,120 @@
namespace webrtc {
namespace {
TEST(VideoRtpTrackSourceTest, CreatesWithRemoteAtttributeSet) {
class MockCallback : public VideoRtpTrackSource::Callback {
public:
MOCK_METHOD0(OnGenerateKeyFrame, void());
MOCK_METHOD1(OnEncodedSinkEnabled, void(bool));
};
class MockSink : public rtc::VideoSinkInterface<RecordableEncodedFrame> {
public:
MOCK_METHOD1(OnFrame, void(const RecordableEncodedFrame&));
};
rtc::scoped_refptr<VideoRtpTrackSource> MakeSource(
VideoRtpTrackSource::Callback* callback) {
rtc::scoped_refptr<VideoRtpTrackSource> source(
new rtc::RefCountedObject<VideoRtpTrackSource>());
EXPECT_TRUE(source->remote());
new rtc::RefCountedObject<VideoRtpTrackSource>(callback));
return source;
}
TEST(VideoRtpTrackSourceTest, CreatesWithRemoteAtttributeSet) {
EXPECT_TRUE(MakeSource(nullptr)->remote());
}
TEST(VideoRtpTrackSourceTest, EnablesEncodingOutputOnAddingSink) {
MockCallback mock_callback;
EXPECT_CALL(mock_callback, OnGenerateKeyFrame).Times(0);
auto source = MakeSource(&mock_callback);
MockSink sink;
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(true));
source->AddEncodedSink(&sink);
}
TEST(VideoRtpTrackSourceTest, EnablesEncodingOutputOnceOnAddingTwoSinks) {
MockCallback mock_callback;
EXPECT_CALL(mock_callback, OnGenerateKeyFrame).Times(0);
auto source = MakeSource(&mock_callback);
MockSink sink;
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(true)).Times(1);
source->AddEncodedSink(&sink);
MockSink sink2;
source->AddEncodedSink(&sink2);
}
TEST(VideoRtpTrackSourceTest, DisablesEncodingOutputOnOneSinkRemoved) {
MockCallback mock_callback;
EXPECT_CALL(mock_callback, OnGenerateKeyFrame).Times(0);
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(true));
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(false)).Times(0);
auto source = MakeSource(&mock_callback);
MockSink sink;
source->AddEncodedSink(&sink);
testing::Mock::VerifyAndClearExpectations(&mock_callback);
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(false));
source->RemoveEncodedSink(&sink);
}
TEST(VideoRtpTrackSourceTest, DisablesEncodingOutputOnLastSinkRemoved) {
MockCallback mock_callback;
EXPECT_CALL(mock_callback, OnGenerateKeyFrame).Times(0);
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(true));
auto source = MakeSource(&mock_callback);
MockSink sink;
source->AddEncodedSink(&sink);
MockSink sink2;
source->AddEncodedSink(&sink2);
source->RemoveEncodedSink(&sink);
testing::Mock::VerifyAndClearExpectations(&mock_callback);
EXPECT_CALL(mock_callback, OnEncodedSinkEnabled(false));
source->RemoveEncodedSink(&sink2);
}
TEST(VideoRtpTrackSourceTest, GeneratesKeyFrameWhenRequested) {
MockCallback mock_callback;
auto source = MakeSource(&mock_callback);
EXPECT_CALL(mock_callback, OnGenerateKeyFrame);
source->GenerateKeyFrame();
}
TEST(VideoRtpTrackSourceTest, NoCallbacksAfterClearedCallback) {
testing::StrictMock<MockCallback> mock_callback;
auto source = MakeSource(&mock_callback);
source->ClearCallback();
MockSink sink;
source->AddEncodedSink(&sink);
source->GenerateKeyFrame();
source->RemoveEncodedSink(&sink);
}
class TestFrame : public RecordableEncodedFrame {
public:
rtc::scoped_refptr<const webrtc::EncodedImageBufferInterface> encoded_buffer()
const override {
return nullptr;
}
absl::optional<webrtc::ColorSpace> color_space() const override {
return absl::nullopt;
}
VideoCodecType codec() const override { return kVideoCodecGeneric; }
bool is_key_frame() const override { return false; }
EncodedResolution resolution() const override {
return EncodedResolution{0, 0};
}
Timestamp render_time() const override { return Timestamp::ms(0); }
};
TEST(VideoRtpTrackSourceTest, BroadcastsFrames) {
auto source = MakeSource(nullptr);
MockSink sink;
source->AddEncodedSink(&sink);
MockSink sink2;
source->AddEncodedSink(&sink2);
TestFrame frame;
EXPECT_CALL(sink, OnFrame);
EXPECT_CALL(sink2, OnFrame);
source->BroadcastRecordableEncodedFrame(frame);
}
} // namespace