diff options
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/loss_notification_controller.cc')
-rw-r--r-- | third_party/libwebrtc/modules/video_coding/loss_notification_controller.cc | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/loss_notification_controller.cc b/third_party/libwebrtc/modules/video_coding/loss_notification_controller.cc new file mode 100644 index 0000000000..3377ab5a76 --- /dev/null +++ b/third_party/libwebrtc/modules/video_coding/loss_notification_controller.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2019 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 "modules/video_coding/loss_notification_controller.h" + +#include <stdint.h> + +#include "api/array_view.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/sequence_number_util.h" + +namespace webrtc { +namespace { +// Keep a container's size no higher than `max_allowed_size`, by paring its size +// down to `target_size` whenever it has more than `max_allowed_size` elements. +template <typename Container> +void PareDown(Container* container, + size_t max_allowed_size, + size_t target_size) { + if (container->size() > max_allowed_size) { + const size_t entries_to_delete = container->size() - target_size; + auto erase_to = container->begin(); + std::advance(erase_to, entries_to_delete); + container->erase(container->begin(), erase_to); + RTC_DCHECK_EQ(container->size(), target_size); + } +} +} // namespace + +LossNotificationController::LossNotificationController( + KeyFrameRequestSender* key_frame_request_sender, + LossNotificationSender* loss_notification_sender) + : key_frame_request_sender_(key_frame_request_sender), + loss_notification_sender_(loss_notification_sender), + current_frame_potentially_decodable_(true) { + RTC_DCHECK(key_frame_request_sender_); + RTC_DCHECK(loss_notification_sender_); +} + +LossNotificationController::~LossNotificationController() = default; + +void LossNotificationController::OnReceivedPacket( + uint16_t rtp_seq_num, + const LossNotificationController::FrameDetails* frame) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + // Ignore repeated or reordered packets. + // TODO(bugs.webrtc.org/10336): Handle packet reordering. + if (last_received_seq_num_ && + !AheadOf(rtp_seq_num, *last_received_seq_num_)) { + return; + } + + DiscardOldInformation(); // Prevent memory overconsumption. + + const bool seq_num_gap = + last_received_seq_num_ && + rtp_seq_num != static_cast<uint16_t>(*last_received_seq_num_ + 1u); + + last_received_seq_num_ = rtp_seq_num; + + // `frame` is not nullptr iff the packet is the first packet in the frame. + if (frame != nullptr) { + // Ignore repeated or reordered frames. + // TODO(bugs.webrtc.org/10336): Handle frame reordering. + if (last_received_frame_id_.has_value() && + frame->frame_id <= last_received_frame_id_.value()) { + RTC_LOG(LS_WARNING) << "Repeated or reordered frame ID (" + << frame->frame_id << ")."; + return; + } + + last_received_frame_id_ = frame->frame_id; + + if (frame->is_keyframe) { + // Subsequent frames may not rely on frames before the key frame. + // Note that upon receiving a key frame, we do not issue a loss + // notification on RTP sequence number gap, unless that gap spanned + // the key frame itself. This is because any loss which occurred before + // the key frame is no longer relevant. + decodable_frame_ids_.clear(); + current_frame_potentially_decodable_ = true; + } else { + const bool all_dependencies_decodable = + AllDependenciesDecodable(frame->frame_dependencies); + current_frame_potentially_decodable_ = all_dependencies_decodable; + if (seq_num_gap || !current_frame_potentially_decodable_) { + HandleLoss(rtp_seq_num, current_frame_potentially_decodable_); + } + } + } else if (seq_num_gap || !current_frame_potentially_decodable_) { + current_frame_potentially_decodable_ = false; + // We allow sending multiple loss notifications for a single frame + // even if only one of its packets is lost. We do this because the bigger + // the frame, the more likely it is to be non-discardable, and therefore + // the more robust we wish to be to loss of the feedback messages. + HandleLoss(rtp_seq_num, false); + } +} + +void LossNotificationController::OnAssembledFrame( + uint16_t first_seq_num, + int64_t frame_id, + bool discardable, + rtc::ArrayView<const int64_t> frame_dependencies) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + DiscardOldInformation(); // Prevent memory overconsumption. + + if (discardable) { + return; + } + + if (!AllDependenciesDecodable(frame_dependencies)) { + return; + } + + last_decodable_non_discardable_.emplace(first_seq_num); + const auto it = decodable_frame_ids_.insert(frame_id); + RTC_DCHECK(it.second); +} + +void LossNotificationController::DiscardOldInformation() { + constexpr size_t kExpectedKeyFrameIntervalFrames = 3000; + constexpr size_t kMaxSize = 2 * kExpectedKeyFrameIntervalFrames; + constexpr size_t kTargetSize = kExpectedKeyFrameIntervalFrames; + PareDown(&decodable_frame_ids_, kMaxSize, kTargetSize); +} + +bool LossNotificationController::AllDependenciesDecodable( + rtc::ArrayView<const int64_t> frame_dependencies) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + // Due to packet reordering, frame buffering and asynchronous decoders, it is + // infeasible to make reliable conclusions on the decodability of a frame + // immediately when it arrives. We use the following assumptions: + // * Intra frames are decodable. + // * Inter frames are decodable if all of their references were decodable. + // One possibility that is ignored, is that the packet may be corrupt. + for (int64_t ref_frame_id : frame_dependencies) { + const auto ref_frame_it = decodable_frame_ids_.find(ref_frame_id); + if (ref_frame_it == decodable_frame_ids_.end()) { + // Reference frame not decodable. + return false; + } + } + + return true; +} + +void LossNotificationController::HandleLoss(uint16_t last_received_seq_num, + bool decodability_flag) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + + if (last_decodable_non_discardable_) { + RTC_DCHECK(AheadOf(last_received_seq_num, + last_decodable_non_discardable_->first_seq_num)); + loss_notification_sender_->SendLossNotification( + last_decodable_non_discardable_->first_seq_num, last_received_seq_num, + decodability_flag, /*buffering_allowed=*/true); + } else { + key_frame_request_sender_->RequestKeyFrame(); + } +} +} // namespace webrtc |