| /* | |
| * 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 <cstddef> | |
| #include <cstdint> | |
| #include <span> | |
| #include "api/sequence_checker.h" | |
| #include "modules/include/module_common_types.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, | |
| std::span<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( | |
| std::span<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 |