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:
parent
72e6cb0b3f
commit
d5e2f215d6
@ -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>*)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user