Make nack history configurable.

This allows for `config_.rtp.nack.rtp_history_ms` to be modified
without deleting and recreating video receive streams.

Bug: webrtc:11993
Change-Id: I8ba132b22fe0e6de03d1c42fc38a570cbe138817
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/269301
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37701}
This commit is contained in:
Tommi 2022-08-05 16:21:54 +02:00 committed by WebRTC LUCI CQ
parent cfe1ab7ed2
commit 3900f21702
11 changed files with 149 additions and 65 deletions

View File

@ -298,6 +298,11 @@ class VideoReceiveStreamInterface : public MediaReceiveStreamInterface {
// thread. // thread.
virtual void SetLossNotificationEnabled(bool enabled) = 0; virtual void SetLossNotificationEnabled(bool enabled) = 0;
// Modify `rtp.nack.rtp_history_ms` post construction. Setting this value
// to 0 disables nack.
// Must be called on the packet delivery thread.
virtual void SetNackHistory(TimeDelta history) = 0;
protected: protected:
virtual ~VideoReceiveStreamInterface() {} virtual ~VideoReceiveStreamInterface() {}
}; };

View File

@ -293,6 +293,10 @@ class FakeVideoReceiveStream final
config_.rtp.lntf.enabled = enabled; config_.rtp.lntf.enabled = enabled;
} }
void SetNackHistory(webrtc::TimeDelta history) override {
config_.rtp.nack.rtp_history_ms = history.ms();
}
void Start() override; void Start() override;
void Stop() override; void Stop() override;

View File

@ -2991,26 +2991,32 @@ bool WebRtcVideoChannel::WebRtcVideoReceiveStream::ReconfigureCodecs(
stream_->SetLossNotificationEnabled(has_lntf); stream_->SetLossNotificationEnabled(has_lntf);
} }
int new_history_ms = config_.rtp.nack.rtp_history_ms;
const int rtp_history_ms = HasNack(codec.codec) ? kNackHistoryMs : 0; const int rtp_history_ms = HasNack(codec.codec) ? kNackHistoryMs : 0;
if (rtp_history_ms != config_.rtp.nack.rtp_history_ms) { if (rtp_history_ms != config_.rtp.nack.rtp_history_ms) {
config_.rtp.nack.rtp_history_ms = rtp_history_ms; new_history_ms = rtp_history_ms;
recreate_needed = true;
} }
// The rtx-time parameter can be used to override the hardcoded default for // The rtx-time parameter can be used to override the hardcoded default for
// the NACK buffer length. // the NACK buffer length.
if (codec.rtx_time != -1 && config_.rtp.nack.rtp_history_ms != 0) { if (codec.rtx_time != -1 && new_history_ms != 0) {
config_.rtp.nack.rtp_history_ms = codec.rtx_time; new_history_ms = codec.rtx_time;
recreate_needed = true; }
if (config_.rtp.nack.rtp_history_ms != new_history_ms) {
config_.rtp.nack.rtp_history_ms = new_history_ms;
stream_->SetNackHistory(webrtc::TimeDelta::Millis(new_history_ms));
} }
const bool has_rtr = HasRrtr(codec.codec); const bool has_rtr = HasRrtr(codec.codec);
if (has_rtr != config_.rtp.rtcp_xr.receiver_reference_time_report) { if (has_rtr != config_.rtp.rtcp_xr.receiver_reference_time_report) {
// TODO(tommi): Look into if/when this happens in practice.
config_.rtp.rtcp_xr.receiver_reference_time_report = has_rtr; config_.rtp.rtcp_xr.receiver_reference_time_report = has_rtr;
recreate_needed = true; recreate_needed = true;
} }
if (codec.ulpfec.red_rtx_payload_type != -1) { if (codec.ulpfec.red_rtx_payload_type != -1) {
// TODO(tommi): Look into if/when this happens in practice.
rtx_associated_payload_types[codec.ulpfec.red_rtx_payload_type] = rtx_associated_payload_types[codec.ulpfec.red_rtx_payload_type] =
codec.ulpfec.red_payload_type; codec.ulpfec.red_payload_type;
} }
@ -3068,22 +3074,8 @@ void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFeedbackParameters(
int nack_history_ms = int nack_history_ms =
nack_enabled ? rtx_time != -1 ? rtx_time : kNackHistoryMs : 0; nack_enabled ? rtx_time != -1 ? rtx_time : kNackHistoryMs : 0;
if (config_.rtp.nack.rtp_history_ms == nack_history_ms) {
RTC_LOG(LS_INFO)
<< "Ignoring call to SetFeedbackParameters because parameters are "
"unchanged; nack="
<< nack_enabled << ", rtx_time=" << rtx_time;
return;
}
RTC_LOG_F(LS_INFO) << "(recv) because of SetFeedbackParameters; nack="
<< nack_enabled << ". rtp_history_ms "
<< config_.rtp.nack.rtp_history_ms << "->"
<< nack_history_ms;
config_.rtp.nack.rtp_history_ms = nack_history_ms; config_.rtp.nack.rtp_history_ms = nack_history_ms;
stream_->SetNackHistory(webrtc::TimeDelta::Millis(nack_history_ms));
RecreateReceiveStream();
} }
void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFlexFecPayload( void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFlexFecPayload(

View File

@ -173,6 +173,14 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
jitter_estimator_.UpdateRtt(TimeDelta::Millis(max_rtt_ms)); jitter_estimator_.UpdateRtt(TimeDelta::Millis(max_rtt_ms));
} }
void SetMaxWaits(TimeDelta max_wait_for_keyframe,
TimeDelta max_wait_for_frame) override {
RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
timeout_tracker_.SetTimeouts(
{.max_wait_for_keyframe = max_wait_for_keyframe,
.max_wait_for_frame = max_wait_for_frame});
}
void StartNextDecode(bool keyframe_required) override { void StartNextDecode(bool keyframe_required) override {
if (!worker_queue_->IsCurrent()) { if (!worker_queue_->IsCurrent()) {
worker_queue_->PostTask(SafeTask( worker_queue_->PostTask(SafeTask(

View File

@ -59,6 +59,8 @@ class FrameBufferProxy {
std::unique_ptr<EncodedFrame> frame) = 0; std::unique_ptr<EncodedFrame> frame) = 0;
virtual void UpdateRtt(int64_t max_rtt_ms) = 0; virtual void UpdateRtt(int64_t max_rtt_ms) = 0;
virtual int Size() = 0; virtual int Size() = 0;
virtual void SetMaxWaits(TimeDelta max_wait_for_keyframe,
TimeDelta max_wait_for_frame) = 0;
// Run on either the worker thread or the decode thread. // Run on either the worker thread or the decode thread.
virtual void StartNextDecode(bool keyframe_required) = 0; virtual void StartNextDecode(bool keyframe_required) = 0;

View File

@ -56,6 +56,8 @@ namespace {
constexpr int kPacketBufferStartSize = 512; constexpr int kPacketBufferStartSize = 512;
constexpr int kPacketBufferMaxSize = 2048; constexpr int kPacketBufferMaxSize = 2048;
constexpr int kMaxPacketAgeToNack = 450;
int PacketBufferMaxSize(const FieldTrialsView& field_trials) { int PacketBufferMaxSize(const FieldTrialsView& field_trials) {
// The group here must be a positive power of 2, in which case that is used as // The group here must be a positive power of 2, in which case that is used as
// size. All other values shall result in the default value being used. // size. All other values shall result in the default value being used.
@ -105,12 +107,12 @@ std::unique_ptr<ModuleRtpRtcpImpl2> CreateRtpRtcpModule(
std::unique_ptr<NackRequester> MaybeConstructNackModule( std::unique_ptr<NackRequester> MaybeConstructNackModule(
TaskQueueBase* current_queue, TaskQueueBase* current_queue,
NackPeriodicProcessor* nack_periodic_processor, NackPeriodicProcessor* nack_periodic_processor,
const VideoReceiveStreamInterface::Config& config, const NackConfig& nack,
Clock* clock, Clock* clock,
NackSender* nack_sender, NackSender* nack_sender,
KeyFrameRequestSender* keyframe_request_sender, KeyFrameRequestSender* keyframe_request_sender,
const FieldTrialsView& field_trials) { const FieldTrialsView& field_trials) {
if (config.rtp.nack.rtp_history_ms == 0) if (nack.rtp_history_ms == 0)
return nullptr; return nullptr;
// TODO(bugs.webrtc.org/12420): pass rtp_history_ms to the nack module. // TODO(bugs.webrtc.org/12420): pass rtp_history_ms to the nack module.
@ -223,6 +225,7 @@ RtpVideoStreamReceiver2::RtpVideoStreamReceiver2(
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer, rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
const FieldTrialsView& field_trials) const FieldTrialsView& field_trials)
: field_trials_(field_trials), : field_trials_(field_trials),
worker_queue_(current_queue),
clock_(clock), clock_(clock),
config_(*config), config_(*config),
packet_router_(packet_router), packet_router_(packet_router),
@ -246,6 +249,7 @@ RtpVideoStreamReceiver2::RtpVideoStreamReceiver2(
rtcp_cname_callback, rtcp_cname_callback,
config_.rtp.rtcp_xr.receiver_reference_time_report, config_.rtp.rtcp_xr.receiver_reference_time_report,
config_.rtp.local_ssrc)), config_.rtp.local_ssrc)),
nack_periodic_processor_(nack_periodic_processor),
complete_frame_callback_(complete_frame_callback), complete_frame_callback_(complete_frame_callback),
keyframe_request_method_(config_.rtp.keyframe_method), keyframe_request_method_(config_.rtp.keyframe_method),
// TODO(bugs.webrtc.org/10336): Let `rtcp_feedback_buffer_` communicate // TODO(bugs.webrtc.org/10336): Let `rtcp_feedback_buffer_` communicate
@ -253,7 +257,7 @@ RtpVideoStreamReceiver2::RtpVideoStreamReceiver2(
rtcp_feedback_buffer_(this, this, this), rtcp_feedback_buffer_(this, this, this),
nack_module_(MaybeConstructNackModule(current_queue, nack_module_(MaybeConstructNackModule(current_queue,
nack_periodic_processor, nack_periodic_processor,
config_, config_.rtp.nack,
clock_, clock_,
&rtcp_feedback_buffer_, &rtcp_feedback_buffer_,
&rtcp_feedback_buffer_, &rtcp_feedback_buffer_,
@ -280,7 +284,6 @@ RtpVideoStreamReceiver2::RtpVideoStreamReceiver2(
rtp_rtcp_->SetRemoteSSRC(config_.rtp.remote_ssrc); rtp_rtcp_->SetRemoteSSRC(config_.rtp.remote_ssrc);
if (config_.rtp.nack.rtp_history_ms > 0) { if (config_.rtp.nack.rtp_history_ms > 0) {
static constexpr int kMaxPacketAgeToNack = 450;
rtp_receive_statistics_->SetMaxReorderingThreshold(config_.rtp.remote_ssrc, rtp_receive_statistics_->SetMaxReorderingThreshold(config_.rtp.remote_ssrc,
kMaxPacketAgeToNack); kMaxPacketAgeToNack);
} }
@ -704,10 +707,6 @@ bool RtpVideoStreamReceiver2::IsUlpfecEnabled() const {
return config_.rtp.ulpfec_payload_type != -1; return config_.rtp.ulpfec_payload_type != -1;
} }
bool RtpVideoStreamReceiver2::IsRetransmissionsEnabled() const {
return config_.rtp.nack.rtp_history_ms > 0;
}
bool RtpVideoStreamReceiver2::IsDecryptable() const { bool RtpVideoStreamReceiver2::IsDecryptable() const {
RTC_DCHECK_RUN_ON(&worker_task_checker_); RTC_DCHECK_RUN_ON(&worker_task_checker_);
return frames_decryptable_; return frames_decryptable_;
@ -919,7 +918,7 @@ const RtpHeaderExtensionMap& RtpVideoStreamReceiver2::GetRtpExtensions() const {
} }
void RtpVideoStreamReceiver2::UpdateRtt(int64_t max_rtt_ms) { void RtpVideoStreamReceiver2::UpdateRtt(int64_t max_rtt_ms) {
RTC_DCHECK_RUN_ON(&worker_task_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
if (nack_module_) if (nack_module_)
nack_module_->UpdateRtt(max_rtt_ms); nack_module_->UpdateRtt(max_rtt_ms);
} }
@ -952,6 +951,21 @@ void RtpVideoStreamReceiver2::SetLossNotificationEnabled(bool enabled) {
} }
} }
void RtpVideoStreamReceiver2::SetNackHistory(TimeDelta history) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
if (history.ms() == 0) {
nack_module_.reset();
} else if (!nack_module_) {
nack_module_ = std::make_unique<NackRequester>(
worker_queue_, nack_periodic_processor_, clock_, &rtcp_feedback_buffer_,
&rtcp_feedback_buffer_, field_trials_);
}
rtp_receive_statistics_->SetMaxReorderingThreshold(
config_.rtp.remote_ssrc,
history.ms() > 0 ? kMaxPacketAgeToNack : kDefaultMaxReorderingThreshold);
}
absl::optional<int64_t> RtpVideoStreamReceiver2::LastReceivedPacketMs() const { absl::optional<int64_t> RtpVideoStreamReceiver2::LastReceivedPacketMs() const {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
if (last_received_rtp_system_time_) { if (last_received_rtp_system_time_) {
@ -978,7 +992,7 @@ void RtpVideoStreamReceiver2::ManageFrame(
void RtpVideoStreamReceiver2::ReceivePacket(const RtpPacketReceived& packet) { void RtpVideoStreamReceiver2::ReceivePacket(const RtpPacketReceived& packet) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
RTC_DCHECK_RUN_ON(&worker_task_checker_);
if (packet.payload_size() == 0) { if (packet.payload_size() == 0) {
// Padding or keep-alive packet. // Padding or keep-alive packet.
// TODO(nisse): Could drop empty packets earlier, but need to figure out how // TODO(nisse): Could drop empty packets earlier, but need to figure out how

View File

@ -149,7 +149,6 @@ class RtpVideoStreamReceiver2 : public LossNotificationSender,
bool buffering_allowed) override; bool buffering_allowed) override;
bool IsUlpfecEnabled() const; bool IsUlpfecEnabled() const;
bool IsRetransmissionsEnabled() const;
// Returns true if a decryptor is attached and frames can be decrypted. // Returns true if a decryptor is attached and frames can be decrypted.
// Updated by OnDecryptionStatusChangeCallback. Note this refers to Frame // Updated by OnDecryptionStatusChangeCallback. Note this refers to Frame
@ -198,6 +197,8 @@ class RtpVideoStreamReceiver2 : public LossNotificationSender,
// thread. // thread.
void SetLossNotificationEnabled(bool enabled); void SetLossNotificationEnabled(bool enabled);
void SetNackHistory(TimeDelta history);
absl::optional<int64_t> LastReceivedPacketMs() const; absl::optional<int64_t> LastReceivedPacketMs() const;
absl::optional<int64_t> LastReceivedKeyframePacketMs() const; absl::optional<int64_t> LastReceivedKeyframePacketMs() const;
@ -305,6 +306,7 @@ class RtpVideoStreamReceiver2 : public LossNotificationSender,
RTC_RUN_ON(packet_sequence_checker_); RTC_RUN_ON(packet_sequence_checker_);
const FieldTrialsView& field_trials_; const FieldTrialsView& field_trials_;
TaskQueueBase* const worker_queue_;
Clock* const clock_; Clock* const clock_;
// Ownership of this object lies with VideoReceiveStreamInterface, which owns // Ownership of this object lies with VideoReceiveStreamInterface, which owns
// `this`. // `this`.
@ -337,11 +339,15 @@ class RtpVideoStreamReceiver2 : public LossNotificationSender,
const std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_; const std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_;
NackPeriodicProcessor* const nack_periodic_processor_;
OnCompleteFrameCallback* complete_frame_callback_; OnCompleteFrameCallback* complete_frame_callback_;
const KeyFrameReqMethod keyframe_request_method_; const KeyFrameReqMethod keyframe_request_method_;
RtcpFeedbackBuffer rtcp_feedback_buffer_; RtcpFeedbackBuffer rtcp_feedback_buffer_;
const std::unique_ptr<NackRequester> nack_module_; // TODO(tommi): Consider absl::optional<NackRequester> instead of unique_ptr
// since nack is usually configured.
std::unique_ptr<NackRequester> nack_module_
RTC_GUARDED_BY(packet_sequence_checker_);
std::unique_ptr<LossNotificationController> loss_notification_controller_ std::unique_ptr<LossNotificationController> loss_notification_controller_
RTC_GUARDED_BY(packet_sequence_checker_); RTC_GUARDED_BY(packet_sequence_checker_);

View File

@ -182,17 +182,12 @@ std::string OptionalDelayToLogString(const absl::optional<TimeDelta> opt) {
} // namespace } // namespace
TimeDelta DetermineMaxWaitForFrame( TimeDelta DetermineMaxWaitForFrame(TimeDelta rtp_history, bool is_keyframe) {
const VideoReceiveStreamInterface::Config& config,
bool is_keyframe) {
// A (arbitrary) conversion factor between the remotely signalled NACK buffer // A (arbitrary) conversion factor between the remotely signalled NACK buffer
// time (if not present defaults to 1000ms) and the maximum time we wait for a // time (if not present defaults to 1000ms) and the maximum time we wait for a
// remote frame. Chosen to not change existing defaults when using not // remote frame. Chosen to not change existing defaults when using not
// rtx-time. // rtx-time.
const int conversion_factor = 3; const int conversion_factor = 3;
const TimeDelta rtp_history =
TimeDelta::Millis(config.rtp.nack.rtp_history_ms);
if (rtp_history > TimeDelta::Zero() && if (rtp_history > TimeDelta::Zero() &&
conversion_factor * rtp_history < kMaxWaitForFrame) { conversion_factor * rtp_history < kMaxWaitForFrame) {
return is_keyframe ? rtp_history : conversion_factor * rtp_history; return is_keyframe ? rtp_history : conversion_factor * rtp_history;
@ -238,8 +233,12 @@ VideoReceiveStream2::VideoReceiveStream2(
std::move(config_.frame_transformer), std::move(config_.frame_transformer),
call->trials()), call->trials()),
rtp_stream_sync_(call->worker_thread(), this), rtp_stream_sync_(call->worker_thread(), this),
max_wait_for_keyframe_(DetermineMaxWaitForFrame(config_, true)), max_wait_for_keyframe_(DetermineMaxWaitForFrame(
max_wait_for_frame_(DetermineMaxWaitForFrame(config_, false)), TimeDelta::Millis(config_.rtp.nack.rtp_history_ms),
true)),
max_wait_for_frame_(DetermineMaxWaitForFrame(
TimeDelta::Millis(config_.rtp.nack.rtp_history_ms),
false)),
maximum_pre_stream_decoders_("max", kDefaultMaximumPreStreamDecoders), maximum_pre_stream_decoders_("max", kDefaultMaximumPreStreamDecoders),
decode_sync_(decode_sync), decode_sync_(decode_sync),
decode_queue_(task_queue_factory_->CreateTaskQueue( decode_queue_(task_queue_factory_->CreateTaskQueue(
@ -355,8 +354,7 @@ void VideoReceiveStream2::Start() {
const bool protected_by_fec = config_.rtp.protected_by_flexfec || const bool protected_by_fec = config_.rtp.protected_by_flexfec ||
rtp_video_stream_receiver_.IsUlpfecEnabled(); rtp_video_stream_receiver_.IsUlpfecEnabled();
if (rtp_video_stream_receiver_.IsRetransmissionsEnabled() && if (config_.rtp.nack.rtp_history_ms > 0 && protected_by_fec) {
protected_by_fec) {
frame_buffer_->SetProtectionMode(kProtectionNackFEC); frame_buffer_->SetProtectionMode(kProtectionNackFEC);
} }
@ -524,6 +522,36 @@ void VideoReceiveStream2::SetLossNotificationEnabled(bool enabled) {
rtp_video_stream_receiver_.SetLossNotificationEnabled(enabled); rtp_video_stream_receiver_.SetLossNotificationEnabled(enabled);
} }
void VideoReceiveStream2::SetNackHistory(TimeDelta history) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
RTC_DCHECK_GE(history.ms(), 0);
if (config_.rtp.nack.rtp_history_ms == history.ms())
return;
// TODO(tommi): Stop using the config struct for the internal state.
const_cast<int&>(config_.rtp.nack.rtp_history_ms) = history.ms();
const bool protected_by_fec = config_.rtp.protected_by_flexfec ||
rtp_video_stream_receiver_.IsUlpfecEnabled();
frame_buffer_->SetProtectionMode(history.ms() > 0 && protected_by_fec
? kProtectionNackFEC
: kProtectionNack);
rtp_video_stream_receiver_.SetNackHistory(history);
TimeDelta max_wait_for_keyframe = DetermineMaxWaitForFrame(history, true);
TimeDelta max_wait_for_frame = DetermineMaxWaitForFrame(history, false);
decode_queue_.PostTask([this, max_wait_for_keyframe, max_wait_for_frame]() {
RTC_DCHECK_RUN_ON(&decode_queue_);
max_wait_for_keyframe_ = max_wait_for_keyframe;
max_wait_for_frame_ = max_wait_for_frame;
});
frame_buffer_->SetMaxWaits(max_wait_for_keyframe, max_wait_for_frame);
}
void VideoReceiveStream2::CreateAndRegisterExternalDecoder( void VideoReceiveStream2::CreateAndRegisterExternalDecoder(
const Decoder& decoder) { const Decoder& decoder) {
TRACE_EVENT0("webrtc", TRACE_EVENT0("webrtc",
@ -765,6 +793,7 @@ bool VideoReceiveStream2::SetMinimumPlayoutDelay(int delay_ms) {
} }
TimeDelta VideoReceiveStream2::GetMaxWait() const { TimeDelta VideoReceiveStream2::GetMaxWait() const {
RTC_DCHECK_RUN_ON(&decode_queue_);
return keyframe_required_ ? max_wait_for_keyframe_ : max_wait_for_frame_; return keyframe_required_ ? max_wait_for_keyframe_ : max_wait_for_frame_;
} }
@ -780,10 +809,11 @@ void VideoReceiveStream2::OnDecodableFrameTimeout(TimeDelta wait_time) {
RTC_DCHECK_RUN_ON(&decode_queue_); RTC_DCHECK_RUN_ON(&decode_queue_);
Timestamp now = clock_->CurrentTime(); Timestamp now = clock_->CurrentTime();
// TODO(bugs.webrtc.org/11993): PostTask to the network thread. // TODO(bugs.webrtc.org/11993): PostTask to the network thread.
call_->worker_thread()->PostTask( call_->worker_thread()->PostTask(SafeTask(
SafeTask(task_safety_.flag(), [this, wait_time, now] { task_safety_.flag(),
[this, wait_time, now, max_wait_for_keyframe = max_wait_for_keyframe_] {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
HandleFrameBufferTimeout(now, wait_time); HandleFrameBufferTimeout(now, wait_time, max_wait_for_keyframe);
decode_queue_.PostTask([this] { decode_queue_.PostTask([this] {
RTC_DCHECK_RUN_ON(&decode_queue_); RTC_DCHECK_RUN_ON(&decode_queue_);
@ -849,15 +879,16 @@ void VideoReceiveStream2::HandleEncodedFrame(
call_->worker_thread()->PostTask(SafeTask( call_->worker_thread()->PostTask(SafeTask(
task_safety_.flag(), task_safety_.flag(),
[this, now, received_frame_is_keyframe, force_request_key_frame, [this, now, received_frame_is_keyframe, force_request_key_frame,
decoded_frame_picture_id, keyframe_request_is_due]() { decoded_frame_picture_id, keyframe_request_is_due,
max_wait_for_keyframe = max_wait_for_keyframe_]() {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
if (decoded_frame_picture_id != -1) if (decoded_frame_picture_id != -1)
rtp_video_stream_receiver_.FrameDecoded(decoded_frame_picture_id); rtp_video_stream_receiver_.FrameDecoded(decoded_frame_picture_id);
HandleKeyFrameGeneration(received_frame_is_keyframe, now, HandleKeyFrameGeneration(
force_request_key_frame, received_frame_is_keyframe, now, force_request_key_frame,
keyframe_request_is_due); keyframe_request_is_due, max_wait_for_keyframe);
})); }));
} }
} }
@ -926,7 +957,8 @@ void VideoReceiveStream2::HandleKeyFrameGeneration(
bool received_frame_is_keyframe, bool received_frame_is_keyframe,
Timestamp now, Timestamp now,
bool always_request_key_frame, bool always_request_key_frame,
bool keyframe_request_is_due) { bool keyframe_request_is_due,
TimeDelta max_wait_for_keyframe) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
bool request_key_frame = always_request_key_frame; bool request_key_frame = always_request_key_frame;
@ -935,7 +967,7 @@ void VideoReceiveStream2::HandleKeyFrameGeneration(
if (received_frame_is_keyframe) { if (received_frame_is_keyframe) {
keyframe_generation_requested_ = false; keyframe_generation_requested_ = false;
} else if (keyframe_request_is_due) { } else if (keyframe_request_is_due) {
if (!IsReceivingKeyFrame(now)) { if (!IsReceivingKeyFrame(now, max_wait_for_keyframe)) {
request_key_frame = true; request_key_frame = true;
} }
} else { } else {
@ -951,9 +983,12 @@ void VideoReceiveStream2::HandleKeyFrameGeneration(
} }
} }
void VideoReceiveStream2::HandleFrameBufferTimeout(Timestamp now, void VideoReceiveStream2::HandleFrameBufferTimeout(
TimeDelta wait) { Timestamp now,
TimeDelta wait,
TimeDelta max_wait_for_keyframe) {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
absl::optional<int64_t> last_packet_ms = absl::optional<int64_t> last_packet_ms =
rtp_video_stream_receiver_.LastReceivedPacketMs(); rtp_video_stream_receiver_.LastReceivedPacketMs();
@ -966,7 +1001,7 @@ void VideoReceiveStream2::HandleFrameBufferTimeout(Timestamp now,
if (!stream_is_active) if (!stream_is_active)
stats_proxy_.OnStreamInactive(); stats_proxy_.OnStreamInactive();
if (stream_is_active && !IsReceivingKeyFrame(now) && if (stream_is_active && !IsReceivingKeyFrame(now, max_wait_for_keyframe) &&
(!config_.crypto_options.sframe.require_frame_encryption || (!config_.crypto_options.sframe.require_frame_encryption ||
rtp_video_stream_receiver_.IsDecryptable())) { rtp_video_stream_receiver_.IsDecryptable())) {
RTC_LOG(LS_WARNING) << "No decodable frame in " << wait RTC_LOG(LS_WARNING) << "No decodable frame in " << wait
@ -975,16 +1010,18 @@ void VideoReceiveStream2::HandleFrameBufferTimeout(Timestamp now,
} }
} }
bool VideoReceiveStream2::IsReceivingKeyFrame(Timestamp now) const { bool VideoReceiveStream2::IsReceivingKeyFrame(
Timestamp now,
TimeDelta max_wait_for_keyframe) const {
RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK_RUN_ON(&packet_sequence_checker_);
absl::optional<int64_t> last_keyframe_packet_ms = absl::optional<int64_t> last_keyframe_packet_ms =
rtp_video_stream_receiver_.LastReceivedKeyframePacketMs(); rtp_video_stream_receiver_.LastReceivedKeyframePacketMs();
// If we recently have been receiving packets belonging to a keyframe then // If we recently have been receiving packets belonging to a keyframe then
// we assume a keyframe is currently being received. // we assume a keyframe is currently being received.
bool receiving_keyframe = last_keyframe_packet_ms && bool receiving_keyframe =
now - Timestamp::Millis(*last_keyframe_packet_ms) < last_keyframe_packet_ms &&
max_wait_for_keyframe_; now - Timestamp::Millis(*last_keyframe_packet_ms) < max_wait_for_keyframe;
return receiving_keyframe; return receiving_keyframe;
} }

View File

@ -148,6 +148,7 @@ class VideoReceiveStream2
void SetRtcpMode(RtcpMode mode) override; void SetRtcpMode(RtcpMode mode) override;
void SetFlexFecProtection(RtpPacketSinkInterface* flexfec_sink) override; void SetFlexFecProtection(RtpPacketSinkInterface* flexfec_sink) override;
void SetLossNotificationEnabled(bool enabled) override; void SetLossNotificationEnabled(bool enabled) override;
void SetNackHistory(TimeDelta history) override;
webrtc::VideoReceiveStreamInterface::Stats GetStats() const override; webrtc::VideoReceiveStreamInterface::Stats GetStats() const override;
@ -195,7 +196,9 @@ class VideoReceiveStream2
TimeDelta GetMaxWait() const RTC_RUN_ON(decode_queue_); TimeDelta GetMaxWait() const RTC_RUN_ON(decode_queue_);
void HandleEncodedFrame(std::unique_ptr<EncodedFrame> frame) void HandleEncodedFrame(std::unique_ptr<EncodedFrame> frame)
RTC_RUN_ON(decode_queue_); RTC_RUN_ON(decode_queue_);
void HandleFrameBufferTimeout(Timestamp now, TimeDelta wait) void HandleFrameBufferTimeout(Timestamp now,
TimeDelta wait,
TimeDelta max_wait_for_keyframe)
RTC_RUN_ON(packet_sequence_checker_); RTC_RUN_ON(packet_sequence_checker_);
void UpdatePlayoutDelays() const void UpdatePlayoutDelays() const
RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_sequence_checker_); RTC_EXCLUSIVE_LOCKS_REQUIRED(worker_sequence_checker_);
@ -203,9 +206,11 @@ class VideoReceiveStream2
void HandleKeyFrameGeneration(bool received_frame_is_keyframe, void HandleKeyFrameGeneration(bool received_frame_is_keyframe,
Timestamp now, Timestamp now,
bool always_request_key_frame, bool always_request_key_frame,
bool keyframe_request_is_due) bool keyframe_request_is_due,
TimeDelta max_wait_for_keyframe)
RTC_RUN_ON(packet_sequence_checker_); RTC_RUN_ON(packet_sequence_checker_);
bool IsReceivingKeyFrame(Timestamp timestamp) const bool IsReceivingKeyFrame(Timestamp timestamp,
TimeDelta max_wait_for_keyframe) const
RTC_RUN_ON(packet_sequence_checker_); RTC_RUN_ON(packet_sequence_checker_);
int DecodeAndMaybeDispatchEncodedFrame(std::unique_ptr<EncodedFrame> frame) int DecodeAndMaybeDispatchEncodedFrame(std::unique_ptr<EncodedFrame> frame)
RTC_RUN_ON(decode_queue_); RTC_RUN_ON(decode_queue_);
@ -274,8 +279,8 @@ class VideoReceiveStream2
RTC_GUARDED_BY(worker_sequence_checker_); RTC_GUARDED_BY(worker_sequence_checker_);
// Keyframe request intervals are configurable through field trials. // Keyframe request intervals are configurable through field trials.
const TimeDelta max_wait_for_keyframe_; TimeDelta max_wait_for_keyframe_ RTC_GUARDED_BY(decode_queue_);
const TimeDelta max_wait_for_frame_; TimeDelta max_wait_for_frame_ RTC_GUARDED_BY(decode_queue_);
// All of them tries to change current min_playout_delay on `timing_` but // All of them tries to change current min_playout_delay on `timing_` but
// source of the change request is different in each case. Among them the // source of the change request is different in each case. Among them the

View File

@ -40,6 +40,7 @@ TimeDelta VideoReceiveStreamTimeoutTracker::TimeUntilTimeout() const {
} }
void VideoReceiveStreamTimeoutTracker::Start(bool waiting_for_keyframe) { void VideoReceiveStreamTimeoutTracker::Start(bool waiting_for_keyframe) {
RTC_DCHECK_RUN_ON(bookkeeping_queue_);
RTC_DCHECK(!timeout_task_.Running()); RTC_DCHECK(!timeout_task_.Running());
waiting_for_keyframe_ = waiting_for_keyframe; waiting_for_keyframe_ = waiting_for_keyframe;
TimeDelta timeout_delay = TimeoutForNextFrame(); TimeDelta timeout_delay = TimeoutForNextFrame();
@ -55,6 +56,7 @@ void VideoReceiveStreamTimeoutTracker::Stop() {
} }
void VideoReceiveStreamTimeoutTracker::SetWaitingForKeyframe() { void VideoReceiveStreamTimeoutTracker::SetWaitingForKeyframe() {
RTC_DCHECK_RUN_ON(bookkeeping_queue_);
waiting_for_keyframe_ = true; waiting_for_keyframe_ = true;
TimeDelta timeout_delay = TimeoutForNextFrame(); TimeDelta timeout_delay = TimeoutForNextFrame();
if (clock_->CurrentTime() + timeout_delay < timeout_) { if (clock_->CurrentTime() + timeout_delay < timeout_) {
@ -64,6 +66,7 @@ void VideoReceiveStreamTimeoutTracker::SetWaitingForKeyframe() {
} }
void VideoReceiveStreamTimeoutTracker::OnEncodedFrameReleased() { void VideoReceiveStreamTimeoutTracker::OnEncodedFrameReleased() {
RTC_DCHECK_RUN_ON(bookkeeping_queue_);
// If we were waiting for a keyframe, then it has just been released. // If we were waiting for a keyframe, then it has just been released.
waiting_for_keyframe_ = false; waiting_for_keyframe_ = false;
last_frame_ = clock_->CurrentTime(); last_frame_ = clock_->CurrentTime();
@ -71,6 +74,7 @@ void VideoReceiveStreamTimeoutTracker::OnEncodedFrameReleased() {
} }
TimeDelta VideoReceiveStreamTimeoutTracker::HandleTimeoutTask() { TimeDelta VideoReceiveStreamTimeoutTracker::HandleTimeoutTask() {
RTC_DCHECK_RUN_ON(bookkeeping_queue_);
Timestamp now = clock_->CurrentTime(); Timestamp now = clock_->CurrentTime();
// `timeout_` is hit and we have timed out. Schedule the next timeout at // `timeout_` is hit and we have timed out. Schedule the next timeout at
// the timeout delay. // the timeout delay.
@ -86,4 +90,9 @@ TimeDelta VideoReceiveStreamTimeoutTracker::HandleTimeoutTask() {
return timeout_ - now; return timeout_ - now;
} }
void VideoReceiveStreamTimeoutTracker::SetTimeouts(Timeouts timeouts) {
RTC_DCHECK_RUN_ON(bookkeeping_queue_);
timeouts_ = timeouts;
}
} // namespace webrtc } // namespace webrtc

View File

@ -46,8 +46,10 @@ class VideoReceiveStreamTimeoutTracker {
void OnEncodedFrameReleased(); void OnEncodedFrameReleased();
TimeDelta TimeUntilTimeout() const; TimeDelta TimeUntilTimeout() const;
void SetTimeouts(Timeouts timeouts);
private: private:
TimeDelta TimeoutForNextFrame() const { TimeDelta TimeoutForNextFrame() const RTC_RUN_ON(bookkeeping_queue_) {
return waiting_for_keyframe_ ? timeouts_.max_wait_for_keyframe return waiting_for_keyframe_ ? timeouts_.max_wait_for_keyframe
: timeouts_.max_wait_for_frame; : timeouts_.max_wait_for_frame;
} }
@ -55,7 +57,7 @@ class VideoReceiveStreamTimeoutTracker {
Clock* const clock_; Clock* const clock_;
TaskQueueBase* const bookkeeping_queue_; TaskQueueBase* const bookkeeping_queue_;
const Timeouts timeouts_; Timeouts timeouts_ RTC_GUARDED_BY(bookkeeping_queue_);
const TimeoutCallback timeout_cb_; const TimeoutCallback timeout_cb_;
RepeatingTaskHandle timeout_task_; RepeatingTaskHandle timeout_task_;