VideoReceiveStream: Enable encoded frame sink.
This change ultimately enables wiring up VideoRtpReceiver::OnGenerateKeyFrame and OnEncodedSinkEnabled into internal::VideoReceiveStream so that encoded frames can flow to sinks installed in VideoTrackSourceInterface. Bug: chromium:1013590 Change-Id: I0779932c251a2159880a39b2d42d5ce439cc88e6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/161090 Commit-Queue: Markus Handell <handellm@webrtc.org> Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29988}
This commit is contained in:
parent
05e4d08e35
commit
269ac81a86
@ -153,6 +153,11 @@ class RTC_EXPORT EncodedImage {
|
||||
capacity_ = 0;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<EncodedImageBufferInterface> GetEncodedData() const {
|
||||
RTC_DCHECK(buffer_ == nullptr);
|
||||
return encoded_data_;
|
||||
}
|
||||
|
||||
// TODO(nisse): Delete, provide only read-only access to the buffer.
|
||||
uint8_t* data() {
|
||||
return buffer_ ? buffer_
|
||||
|
||||
@ -292,6 +292,7 @@ rtc_library("video_stream_api") {
|
||||
"../api/crypto:frame_encryptor_interface",
|
||||
"../api/crypto:options",
|
||||
"../api/transport/rtp:rtp_source",
|
||||
"../api/video:recordable_encoded_frame",
|
||||
"../api/video:video_frame",
|
||||
"../api/video:video_rtp_headers",
|
||||
"../api/video:video_stream_encoder",
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/call/transport.h"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "api/rtp_headers.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/transport/rtp/rtp_source.h"
|
||||
#include "api/video/recordable_encoded_frame.h"
|
||||
#include "api/video/video_content_type.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "api/video/video_sink_interface.h"
|
||||
@ -39,6 +41,26 @@ class VideoDecoderFactory;
|
||||
|
||||
class VideoReceiveStream {
|
||||
public:
|
||||
// Class for handling moving in/out recording state.
|
||||
struct RecordingState {
|
||||
RecordingState() = default;
|
||||
explicit RecordingState(
|
||||
std::function<void(const RecordableEncodedFrame&)> callback)
|
||||
: callback(std::move(callback)) {}
|
||||
|
||||
// Callback stored from the VideoReceiveStream. The VideoReceiveStream
|
||||
// client should not interpret the attribute.
|
||||
std::function<void(const RecordableEncodedFrame&)> callback;
|
||||
// Memento of internal state in VideoReceiveStream, recording wether
|
||||
// we're currently causing generation of a keyframe from the sender. Needed
|
||||
// to avoid sending double keyframe requests. The VideoReceiveStream client
|
||||
// should not interpret the attribute.
|
||||
bool keyframe_needed = false;
|
||||
// Memento of when a keyframe request was last sent. The VideoReceiveStream
|
||||
// client should not interpret the attribute.
|
||||
absl::optional<int64_t> last_keyframe_request_ms;
|
||||
};
|
||||
|
||||
// TODO(mflodman) Move all these settings to VideoDecoder and move the
|
||||
// declaration to common_types.h.
|
||||
struct Decoder {
|
||||
@ -275,6 +297,21 @@ class VideoReceiveStream {
|
||||
virtual void SetFrameDecryptor(
|
||||
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) = 0;
|
||||
|
||||
// Sets and returns recording state. The old state is moved out
|
||||
// of the video receive stream and returned to the caller, and |state|
|
||||
// is moved in. If the state's callback is set, it will be called with
|
||||
// recordable encoded frames as they arrive.
|
||||
// If |generate_key_frame| is true, the method will generate a key frame.
|
||||
// When the function returns, it's guaranteed that all old callouts
|
||||
// to the returned callback has ceased.
|
||||
// Note: the client should not interpret the returned state's attributes, but
|
||||
// instead treat it as opaque data.
|
||||
virtual RecordingState SetAndGetRecordingState(RecordingState state,
|
||||
bool generate_key_frame) = 0;
|
||||
|
||||
// Cause eventual generation of a key frame from the sender.
|
||||
virtual void GenerateKeyFrame() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~VideoReceiveStream() {}
|
||||
};
|
||||
|
||||
@ -229,6 +229,12 @@ class FakeVideoReceiveStream final : public webrtc::VideoReceiveStream {
|
||||
void SetFrameDecryptor(rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
|
||||
frame_decryptor) override {}
|
||||
|
||||
RecordingState SetAndGetRecordingState(RecordingState state,
|
||||
bool generate_key_frame) override {
|
||||
return RecordingState();
|
||||
}
|
||||
void GenerateKeyFrame() override {}
|
||||
|
||||
private:
|
||||
// webrtc::VideoReceiveStream implementation.
|
||||
void Start() override;
|
||||
|
||||
@ -54,7 +54,9 @@ class VCMEncodedFrame : protected EncodedImage {
|
||||
|
||||
using EncodedImage::ColorSpace;
|
||||
using EncodedImage::data;
|
||||
using EncodedImage::GetEncodedData;
|
||||
using EncodedImage::PacketInfos;
|
||||
using EncodedImage::Retain;
|
||||
using EncodedImage::set_size;
|
||||
using EncodedImage::SetColorSpace;
|
||||
using EncodedImage::SetEncodedData;
|
||||
@ -75,6 +77,12 @@ class VCMEncodedFrame : protected EncodedImage {
|
||||
* Get frame type
|
||||
*/
|
||||
webrtc::VideoFrameType FrameType() const { return _frameType; }
|
||||
/**
|
||||
* Set frame type
|
||||
*/
|
||||
void SetFrameType(webrtc::VideoFrameType frame_type) {
|
||||
_frameType = frame_type;
|
||||
}
|
||||
/**
|
||||
* Get frame rotation
|
||||
*/
|
||||
|
||||
@ -64,6 +64,7 @@ rtc_library("video") {
|
||||
"../api/task_queue",
|
||||
"../api/transport/media:media_transport_interface",
|
||||
"../api/video:encoded_image",
|
||||
"../api/video:recordable_encoded_frame",
|
||||
"../api/video:video_bitrate_allocation",
|
||||
"../api/video:video_bitrate_allocator",
|
||||
"../api/video:video_codec_constants",
|
||||
@ -614,6 +615,7 @@ if (rtc_include_tests) {
|
||||
"../modules/utility",
|
||||
"../modules/video_coding",
|
||||
"../modules/video_coding:codec_globals_headers",
|
||||
"../modules/video_coding:encoded_frame",
|
||||
"../modules/video_coding:video_codec_interface",
|
||||
"../modules/video_coding:video_coding_utility",
|
||||
"../modules/video_coding:webrtc_h264",
|
||||
@ -645,6 +647,7 @@ if (rtc_include_tests) {
|
||||
"../test:test_common",
|
||||
"../test:test_support",
|
||||
"../test:video_test_common",
|
||||
"../test/time_controller:time_controller",
|
||||
"//testing/gtest",
|
||||
"//third_party/abseil-cpp/absl/algorithm:container",
|
||||
"//third_party/abseil-cpp/absl/memory",
|
||||
|
||||
@ -54,6 +54,10 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace internal {
|
||||
constexpr int VideoReceiveStream::kMaxWaitForKeyFrameMs;
|
||||
} // namespace internal
|
||||
|
||||
namespace {
|
||||
|
||||
using video_coding::EncodedFrame;
|
||||
@ -62,9 +66,53 @@ using ReturnReason = video_coding::FrameBuffer::ReturnReason;
|
||||
constexpr int kMinBaseMinimumDelayMs = 0;
|
||||
constexpr int kMaxBaseMinimumDelayMs = 10000;
|
||||
|
||||
constexpr int kMaxWaitForKeyFrameMs = 200;
|
||||
constexpr int kMaxWaitForFrameMs = 3000;
|
||||
|
||||
// Concrete instance of RecordableEncodedFrame wrapping needed content
|
||||
// from video_coding::EncodedFrame.
|
||||
class WebRtcRecordableEncodedFrame : public RecordableEncodedFrame {
|
||||
public:
|
||||
explicit WebRtcRecordableEncodedFrame(const EncodedFrame& frame)
|
||||
: buffer_(frame.GetEncodedData()),
|
||||
render_time_ms_(frame.RenderTime()),
|
||||
codec_(frame.CodecSpecific()->codecType),
|
||||
is_key_frame_(frame.FrameType() == VideoFrameType::kVideoFrameKey),
|
||||
resolution_{frame.EncodedImage()._encodedWidth,
|
||||
frame.EncodedImage()._encodedHeight} {
|
||||
if (frame.ColorSpace()) {
|
||||
color_space_ = *frame.ColorSpace();
|
||||
}
|
||||
}
|
||||
|
||||
// VideoEncodedSinkInterface::FrameBuffer
|
||||
rtc::scoped_refptr<const EncodedImageBufferInterface> encoded_buffer()
|
||||
const override {
|
||||
return buffer_;
|
||||
}
|
||||
|
||||
absl::optional<webrtc::ColorSpace> color_space() const override {
|
||||
return color_space_;
|
||||
}
|
||||
|
||||
VideoCodecType codec() const override { return codec_; }
|
||||
|
||||
bool is_key_frame() const override { return is_key_frame_; }
|
||||
|
||||
EncodedResolution resolution() const override { return resolution_; }
|
||||
|
||||
Timestamp render_time() const override {
|
||||
return Timestamp::ms(render_time_ms_);
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<EncodedImageBufferInterface> buffer_;
|
||||
int64_t render_time_ms_;
|
||||
VideoCodecType codec_;
|
||||
bool is_key_frame_;
|
||||
EncodedResolution resolution_;
|
||||
absl::optional<webrtc::ColorSpace> color_space_;
|
||||
};
|
||||
|
||||
VideoCodec CreateDecoderVideoCodec(const VideoReceiveStream::Decoder& decoder) {
|
||||
VideoCodec codec;
|
||||
memset(&codec, 0, sizeof(codec));
|
||||
@ -501,7 +549,7 @@ void VideoReceiveStream::OnCompleteFrame(
|
||||
std::unique_ptr<video_coding::EncodedFrame> frame) {
|
||||
RTC_DCHECK_RUN_ON(&network_sequence_checker_);
|
||||
// TODO(https://bugs.webrtc.org/9974): Consider removing this workaround.
|
||||
int64_t time_now_ms = rtc::TimeMillis();
|
||||
int64_t time_now_ms = clock_->TimeInMilliseconds();
|
||||
if (last_complete_frame_time_ms_ > 0 &&
|
||||
time_now_ms - last_complete_frame_time_ms_ > kInactiveStreamThresholdMs) {
|
||||
frame_buffer_->Clear();
|
||||
@ -607,7 +655,8 @@ void VideoReceiveStream::HandleEncodedFrame(
|
||||
}
|
||||
}
|
||||
stats_proxy_.OnPreDecode(frame->CodecSpecific()->codecType, qp);
|
||||
|
||||
HandleKeyFrameGeneration(frame->FrameType() == VideoFrameType::kVideoFrameKey,
|
||||
now_ms);
|
||||
int decode_result = video_receiver_.Decode(frame.get());
|
||||
if (decode_result == WEBRTC_VIDEO_CODEC_OK ||
|
||||
decode_result == WEBRTC_VIDEO_CODEC_OK_REQUEST_KEYFRAME) {
|
||||
@ -624,14 +673,35 @@ void VideoReceiveStream::HandleEncodedFrame(
|
||||
// has been fixed.
|
||||
RequestKeyFrame(now_ms);
|
||||
}
|
||||
|
||||
if (encoded_frame_buffer_function_) {
|
||||
frame->Retain();
|
||||
encoded_frame_buffer_function_(WebRtcRecordableEncodedFrame(*frame));
|
||||
}
|
||||
}
|
||||
|
||||
void VideoReceiveStream::HandleKeyFrameGeneration(
|
||||
bool received_frame_is_keyframe,
|
||||
int64_t now_ms) {
|
||||
// Repeat sending keyframe requests if we've requested a keyframe.
|
||||
if (!keyframe_generation_requested_) {
|
||||
return;
|
||||
}
|
||||
if (received_frame_is_keyframe) {
|
||||
keyframe_generation_requested_ = false;
|
||||
} else if (last_keyframe_request_ms_ + max_wait_for_keyframe_ms_ <= now_ms) {
|
||||
if (!IsReceivingKeyFrame(now_ms)) {
|
||||
RequestKeyFrame(now_ms);
|
||||
}
|
||||
} else {
|
||||
// It hasn't been long enough since the last keyframe request, do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
void VideoReceiveStream::HandleFrameBufferTimeout() {
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
absl::optional<int64_t> last_packet_ms =
|
||||
rtp_video_stream_receiver_.LastReceivedPacketMs();
|
||||
absl::optional<int64_t> last_keyframe_packet_ms =
|
||||
rtp_video_stream_receiver_.LastReceivedKeyframePacketMs();
|
||||
|
||||
// To avoid spamming keyframe requests for a stream that is not active we
|
||||
// check if we have received a packet within the last 5 seconds.
|
||||
@ -639,13 +709,7 @@ void VideoReceiveStream::HandleFrameBufferTimeout() {
|
||||
if (!stream_is_active)
|
||||
stats_proxy_.OnStreamInactive();
|
||||
|
||||
// If we recently have been receiving packets belonging to a keyframe then
|
||||
// we assume a keyframe is currently being received.
|
||||
bool receiving_keyframe =
|
||||
last_keyframe_packet_ms &&
|
||||
now_ms - *last_keyframe_packet_ms < max_wait_for_keyframe_ms_;
|
||||
|
||||
if (stream_is_active && !receiving_keyframe &&
|
||||
if (stream_is_active && !IsReceivingKeyFrame(now_ms) &&
|
||||
(!config_.crypto_options.sframe.require_frame_encryption ||
|
||||
rtp_video_stream_receiver_.IsDecryptable())) {
|
||||
RTC_LOG(LS_WARNING) << "No decodable frame in " << GetWaitMs()
|
||||
@ -654,6 +718,18 @@ void VideoReceiveStream::HandleFrameBufferTimeout() {
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoReceiveStream::IsReceivingKeyFrame(int64_t timestamp_ms) const {
|
||||
absl::optional<int64_t> last_keyframe_packet_ms =
|
||||
rtp_video_stream_receiver_.LastReceivedKeyframePacketMs();
|
||||
|
||||
// If we recently have been receiving packets belonging to a keyframe then
|
||||
// we assume a keyframe is currently being received.
|
||||
bool receiving_keyframe =
|
||||
last_keyframe_packet_ms &&
|
||||
timestamp_ms - *last_keyframe_packet_ms < max_wait_for_keyframe_ms_;
|
||||
return receiving_keyframe;
|
||||
}
|
||||
|
||||
void VideoReceiveStream::UpdatePlayoutDelays() const {
|
||||
const int minimum_delay_ms =
|
||||
std::max({frame_minimum_playout_delay_ms_, base_minimum_playout_delay_ms_,
|
||||
@ -672,5 +748,42 @@ std::vector<webrtc::RtpSource> VideoReceiveStream::GetSources() const {
|
||||
return source_tracker_.GetSources();
|
||||
}
|
||||
|
||||
VideoReceiveStream::RecordingState VideoReceiveStream::SetAndGetRecordingState(
|
||||
RecordingState state,
|
||||
bool generate_key_frame) {
|
||||
RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
|
||||
rtc::Event event;
|
||||
RecordingState old_state;
|
||||
decode_queue_.PostTask([this, &event, &old_state, generate_key_frame,
|
||||
state = std::move(state)] {
|
||||
RTC_DCHECK_RUN_ON(&decode_queue_);
|
||||
// Save old state.
|
||||
old_state.callback = std::move(encoded_frame_buffer_function_);
|
||||
old_state.keyframe_needed = keyframe_generation_requested_;
|
||||
old_state.last_keyframe_request_ms = last_keyframe_request_ms_;
|
||||
|
||||
// Set new state.
|
||||
encoded_frame_buffer_function_ = std::move(state.callback);
|
||||
if (generate_key_frame) {
|
||||
RequestKeyFrame(clock_->TimeInMilliseconds());
|
||||
keyframe_generation_requested_ = true;
|
||||
} else {
|
||||
keyframe_generation_requested_ = state.keyframe_needed;
|
||||
last_keyframe_request_ms_ = state.last_keyframe_request_ms.value_or(0);
|
||||
}
|
||||
event.Set();
|
||||
});
|
||||
event.Wait(rtc::Event::kForever);
|
||||
return old_state;
|
||||
}
|
||||
|
||||
void VideoReceiveStream::GenerateKeyFrame() {
|
||||
decode_queue_.PostTask([this]() {
|
||||
RTC_DCHECK_RUN_ON(&decode_queue_);
|
||||
RequestKeyFrame(clock_->TimeInMilliseconds());
|
||||
keyframe_generation_requested_ = true;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace webrtc
|
||||
|
||||
@ -15,6 +15,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "api/transport/media/media_transport_interface.h"
|
||||
#include "api/video/recordable_encoded_frame.h"
|
||||
#include "call/rtp_packet_sink_interface.h"
|
||||
#include "call/syncable.h"
|
||||
#include "call/video_receive_stream.h"
|
||||
@ -50,6 +52,10 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
public Syncable,
|
||||
public CallStatsObserver {
|
||||
public:
|
||||
// The default number of milliseconds to pass before re-requesting a key frame
|
||||
// to be sent.
|
||||
static constexpr int kMaxWaitForKeyFrameMs = 200;
|
||||
|
||||
VideoReceiveStream(TaskQueueFactory* task_queue_factory,
|
||||
RtpStreamReceiverControllerInterface* receiver_controller,
|
||||
int num_cpu_cores,
|
||||
@ -123,15 +129,23 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
|
||||
std::vector<webrtc::RtpSource> GetSources() const override;
|
||||
|
||||
RecordingState SetAndGetRecordingState(RecordingState state,
|
||||
bool generate_key_frame) override;
|
||||
void GenerateKeyFrame() override;
|
||||
|
||||
private:
|
||||
int64_t GetWaitMs() const;
|
||||
void StartNextDecode() RTC_RUN_ON(decode_queue_);
|
||||
void HandleEncodedFrame(std::unique_ptr<video_coding::EncodedFrame> frame);
|
||||
void HandleFrameBufferTimeout();
|
||||
|
||||
void HandleEncodedFrame(std::unique_ptr<video_coding::EncodedFrame> frame)
|
||||
RTC_RUN_ON(decode_queue_);
|
||||
void HandleFrameBufferTimeout() RTC_RUN_ON(decode_queue_);
|
||||
void UpdatePlayoutDelays() const
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(playout_delay_lock_);
|
||||
void RequestKeyFrame(int64_t timestamp_ms);
|
||||
void RequestKeyFrame(int64_t timestamp_ms) RTC_RUN_ON(decode_queue_);
|
||||
void HandleKeyFrameGeneration(bool received_frame_is_keyframe, int64_t now_ms)
|
||||
RTC_RUN_ON(decode_queue_);
|
||||
bool IsReceivingKeyFrame(int64_t timestamp_ms) const
|
||||
RTC_RUN_ON(decode_queue_);
|
||||
|
||||
void UpdateHistograms();
|
||||
|
||||
@ -207,6 +221,12 @@ class VideoReceiveStream : public webrtc::VideoReceiveStream,
|
||||
// Maximum delay as decided by the RTP playout delay extension.
|
||||
int frame_maximum_playout_delay_ms_ RTC_GUARDED_BY(playout_delay_lock_) = -1;
|
||||
|
||||
// Function that is triggered with encoded frames, if not empty.
|
||||
std::function<void(const RecordableEncodedFrame&)>
|
||||
encoded_frame_buffer_function_ RTC_GUARDED_BY(decode_queue_);
|
||||
// Set to true while we're requesting keyframes but not yet received one.
|
||||
bool keyframe_generation_requested_ RTC_GUARDED_BY(decode_queue_) = false;
|
||||
|
||||
// Defined last so they are destroyed before all other members.
|
||||
rtc::TaskQueue decode_queue_;
|
||||
};
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "modules/pacing/packet_router.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "modules/utility/include/process_thread.h"
|
||||
#include "modules/video_coding/encoded_frame.h"
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/event.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
@ -31,6 +32,7 @@
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/time_controller/simulated_time_controller.h"
|
||||
#include "test/video_decoder_proxy_factory.h"
|
||||
#include "video/call_stats.h"
|
||||
|
||||
@ -239,7 +241,6 @@ class VideoReceiveStreamTestWithFakeDecoder : public ::testing::Test {
|
||||
call_stats_(Clock::GetRealTimeClock(), process_thread_.get()) {}
|
||||
|
||||
void SetUp() {
|
||||
constexpr int kDefaultNumCpuCores = 2;
|
||||
config_.rtp.remote_ssrc = 1111;
|
||||
config_.rtp.local_ssrc = 2222;
|
||||
config_.renderer = &fake_renderer_;
|
||||
@ -249,12 +250,18 @@ class VideoReceiveStreamTestWithFakeDecoder : public ::testing::Test {
|
||||
fake_decoder.decoder_factory = &fake_decoder_factory_;
|
||||
config_.decoders.push_back(fake_decoder);
|
||||
clock_ = Clock::GetRealTimeClock();
|
||||
timing_ = new VCMTiming(clock_);
|
||||
ReCreateReceiveStream(VideoReceiveStream::RecordingState());
|
||||
}
|
||||
|
||||
void ReCreateReceiveStream(VideoReceiveStream::RecordingState state) {
|
||||
constexpr int kDefaultNumCpuCores = 2;
|
||||
video_receive_stream_ = nullptr;
|
||||
timing_ = new VCMTiming(clock_);
|
||||
video_receive_stream_.reset(new webrtc::internal::VideoReceiveStream(
|
||||
task_queue_factory_.get(), &rtp_stream_receiver_controller_,
|
||||
kDefaultNumCpuCores, &packet_router_, config_.Copy(),
|
||||
process_thread_.get(), &call_stats_, clock_, timing_));
|
||||
video_receive_stream_->SetAndGetRecordingState(std::move(state), false);
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -391,4 +398,160 @@ TEST_F(VideoReceiveStreamTestWithFakeDecoder, RenderedFrameUpdatesGetSources) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<FrameObjectFake> MakeFrame(VideoFrameType frame_type,
|
||||
int picture_id) {
|
||||
auto frame = std::make_unique<FrameObjectFake>();
|
||||
frame->SetPayloadType(99);
|
||||
frame->id.picture_id = picture_id;
|
||||
frame->SetFrameType(frame_type);
|
||||
return frame;
|
||||
}
|
||||
|
||||
TEST_F(VideoReceiveStreamTestWithFakeDecoder,
|
||||
PassesFrameWhenEncodedFramesCallbackSet) {
|
||||
testing::MockFunction<void(const RecordableEncodedFrame&)> callback;
|
||||
video_receive_stream_->Start();
|
||||
// Expect a keyframe request to be generated
|
||||
EXPECT_CALL(mock_transport_, SendRtcp);
|
||||
EXPECT_CALL(callback, Call);
|
||||
video_receive_stream_->SetAndGetRecordingState(
|
||||
VideoReceiveStream::RecordingState(callback.AsStdFunction()), true);
|
||||
video_receive_stream_->OnCompleteFrame(
|
||||
MakeFrame(VideoFrameType::kVideoFrameKey, 0));
|
||||
EXPECT_TRUE(fake_renderer_.WaitForRenderedFrame(kDefaultTimeOutMs));
|
||||
video_receive_stream_->Stop();
|
||||
}
|
||||
|
||||
TEST_F(VideoReceiveStreamTestWithFakeDecoder,
|
||||
MovesEncodedFrameDispatchStateWhenReCreating) {
|
||||
testing::MockFunction<void(const RecordableEncodedFrame&)> callback;
|
||||
video_receive_stream_->Start();
|
||||
// Expect a key frame request over RTCP.
|
||||
EXPECT_CALL(mock_transport_, SendRtcp).Times(1);
|
||||
video_receive_stream_->SetAndGetRecordingState(
|
||||
VideoReceiveStream::RecordingState(callback.AsStdFunction()), true);
|
||||
video_receive_stream_->Stop();
|
||||
VideoReceiveStream::RecordingState old_state =
|
||||
video_receive_stream_->SetAndGetRecordingState(
|
||||
VideoReceiveStream::RecordingState(), false);
|
||||
ReCreateReceiveStream(std::move(old_state));
|
||||
video_receive_stream_->Stop();
|
||||
}
|
||||
|
||||
class VideoReceiveStreamTestWithSimulatedClock : public ::testing::Test {
|
||||
public:
|
||||
class FakeDecoder2 : public test::FakeDecoder {
|
||||
public:
|
||||
explicit FakeDecoder2(std::function<void()> decode_callback)
|
||||
: callback_(decode_callback) {}
|
||||
|
||||
int32_t Decode(const EncodedImage& input,
|
||||
bool missing_frames,
|
||||
int64_t render_time_ms) override {
|
||||
int32_t result =
|
||||
FakeDecoder::Decode(input, missing_frames, render_time_ms);
|
||||
callback_();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> callback_;
|
||||
};
|
||||
|
||||
static VideoReceiveStream::Config GetConfig(
|
||||
Transport* transport,
|
||||
VideoDecoderFactory* decoder_factory,
|
||||
rtc::VideoSinkInterface<webrtc::VideoFrame>* renderer) {
|
||||
VideoReceiveStream::Config config(transport);
|
||||
config.rtp.remote_ssrc = 1111;
|
||||
config.rtp.local_ssrc = 2222;
|
||||
config.renderer = renderer;
|
||||
VideoReceiveStream::Decoder fake_decoder;
|
||||
fake_decoder.payload_type = 99;
|
||||
fake_decoder.video_format = SdpVideoFormat("VP8");
|
||||
fake_decoder.decoder_factory = decoder_factory;
|
||||
config.decoders.push_back(fake_decoder);
|
||||
return config;
|
||||
}
|
||||
|
||||
VideoReceiveStreamTestWithSimulatedClock()
|
||||
: time_controller_(Timestamp::ms(4711)),
|
||||
fake_decoder_factory_([this] {
|
||||
return std::make_unique<FakeDecoder2>([this] { OnFrameDecoded(); });
|
||||
}),
|
||||
process_thread_(time_controller_.CreateProcessThread("ProcessThread")),
|
||||
config_(GetConfig(&mock_transport_,
|
||||
&fake_decoder_factory_,
|
||||
&fake_renderer_)),
|
||||
call_stats_(time_controller_.GetClock(), process_thread_.get()),
|
||||
video_receive_stream_(time_controller_.GetTaskQueueFactory(),
|
||||
&rtp_stream_receiver_controller_,
|
||||
/*num_cores=*/2,
|
||||
&packet_router_,
|
||||
config_.Copy(),
|
||||
process_thread_.get(),
|
||||
&call_stats_,
|
||||
time_controller_.GetClock(),
|
||||
new VCMTiming(time_controller_.GetClock())) {
|
||||
time_controller_.InvokeWithControlledYield(
|
||||
[this] { video_receive_stream_.Start(); });
|
||||
}
|
||||
|
||||
~VideoReceiveStreamTestWithSimulatedClock() {
|
||||
time_controller_.InvokeWithControlledYield(
|
||||
[this] { video_receive_stream_.Stop(); });
|
||||
}
|
||||
|
||||
void OnFrameDecoded() { event_->Set(); }
|
||||
|
||||
void PassEncodedFrameAndWait(
|
||||
std::unique_ptr<video_coding::EncodedFrame> frame) {
|
||||
time_controller_.InvokeWithControlledYield([this, &frame] {
|
||||
event_ = std::make_unique<rtc::Event>();
|
||||
// This call will eventually end up in the Decoded method where the
|
||||
// event is set.
|
||||
video_receive_stream_.OnCompleteFrame(std::move(frame));
|
||||
event_->Wait(rtc::Event::kForever);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
GlobalSimulatedTimeController time_controller_;
|
||||
test::FunctionVideoDecoderFactory fake_decoder_factory_;
|
||||
std::unique_ptr<ProcessThread> process_thread_;
|
||||
MockTransport mock_transport_;
|
||||
cricket::FakeVideoRenderer fake_renderer_;
|
||||
VideoReceiveStream::Config config_;
|
||||
CallStats call_stats_;
|
||||
PacketRouter packet_router_;
|
||||
RtpStreamReceiverController rtp_stream_receiver_controller_;
|
||||
webrtc::internal::VideoReceiveStream video_receive_stream_;
|
||||
std::unique_ptr<rtc::Event> event_;
|
||||
};
|
||||
|
||||
TEST_F(VideoReceiveStreamTestWithSimulatedClock,
|
||||
RequestsKeyFramesUntilKeyFrameReceived) {
|
||||
auto tick =
|
||||
TimeDelta::ms(internal::VideoReceiveStream::kMaxWaitForKeyFrameMs / 2);
|
||||
EXPECT_CALL(mock_transport_, SendRtcp).Times(1);
|
||||
video_receive_stream_.GenerateKeyFrame();
|
||||
PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 0));
|
||||
time_controller_.Sleep(tick);
|
||||
PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 1));
|
||||
testing::Mock::VerifyAndClearExpectations(&mock_transport_);
|
||||
|
||||
// T+200ms: still no key frame received, expect key frame request sent again.
|
||||
EXPECT_CALL(mock_transport_, SendRtcp).Times(1);
|
||||
time_controller_.Sleep(tick);
|
||||
PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 2));
|
||||
testing::Mock::VerifyAndClearExpectations(&mock_transport_);
|
||||
|
||||
// T+200ms: now send a key frame - we should not observe new key frame
|
||||
// requests after this.
|
||||
EXPECT_CALL(mock_transport_, SendRtcp).Times(0);
|
||||
PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameKey, 3));
|
||||
time_controller_.Sleep(2 * tick);
|
||||
PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 4));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user