blob: 3377ab5a76597d3a25c5003ece161bc751f83cdf [file] [log] [blame]
Elad Alon10874b22019-02-21 16:25:40 +01001/*
2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "modules/video_coding/loss_notification_controller.h"
12
Danil Chapovalov159c4142020-01-23 15:17:52 +010013#include <stdint.h>
14
15#include "api/array_view.h"
Elad Alon10874b22019-02-21 16:25:40 +010016#include "rtc_base/checks.h"
17#include "rtc_base/logging.h"
Danil Chapovalov159c4142020-01-23 15:17:52 +010018#include "rtc_base/numerics/sequence_number_util.h"
Elad Alon10874b22019-02-21 16:25:40 +010019
20namespace webrtc {
21namespace {
Artem Titovdcd7fc72021-08-09 13:02:57 +020022// Keep a container's size no higher than `max_allowed_size`, by paring its size
23// down to `target_size` whenever it has more than `max_allowed_size` elements.
Elad Alon10874b22019-02-21 16:25:40 +010024template <typename Container>
25void PareDown(Container* container,
26 size_t max_allowed_size,
27 size_t target_size) {
28 if (container->size() > max_allowed_size) {
29 const size_t entries_to_delete = container->size() - target_size;
30 auto erase_to = container->begin();
31 std::advance(erase_to, entries_to_delete);
32 container->erase(container->begin(), erase_to);
33 RTC_DCHECK_EQ(container->size(), target_size);
34 }
35}
36} // namespace
37
38LossNotificationController::LossNotificationController(
39 KeyFrameRequestSender* key_frame_request_sender,
40 LossNotificationSender* loss_notification_sender)
41 : key_frame_request_sender_(key_frame_request_sender),
42 loss_notification_sender_(loss_notification_sender),
43 current_frame_potentially_decodable_(true) {
44 RTC_DCHECK(key_frame_request_sender_);
45 RTC_DCHECK(loss_notification_sender_);
46}
47
48LossNotificationController::~LossNotificationController() = default;
49
Niels Möllera7401422019-09-13 14:18:58 +020050void LossNotificationController::OnReceivedPacket(
51 uint16_t rtp_seq_num,
Danil Chapovalov159c4142020-01-23 15:17:52 +010052 const LossNotificationController::FrameDetails* frame) {
Sebastian Janssonb55015e2019-04-09 13:44:04 +020053 RTC_DCHECK_RUN_ON(&sequence_checker_);
Elad Alon10874b22019-02-21 16:25:40 +010054
Elad Alon10874b22019-02-21 16:25:40 +010055 // Ignore repeated or reordered packets.
56 // TODO(bugs.webrtc.org/10336): Handle packet reordering.
57 if (last_received_seq_num_ &&
Niels Möllera7401422019-09-13 14:18:58 +020058 !AheadOf(rtp_seq_num, *last_received_seq_num_)) {
Elad Alon10874b22019-02-21 16:25:40 +010059 return;
60 }
61
62 DiscardOldInformation(); // Prevent memory overconsumption.
63
64 const bool seq_num_gap =
65 last_received_seq_num_ &&
Niels Möllera7401422019-09-13 14:18:58 +020066 rtp_seq_num != static_cast<uint16_t>(*last_received_seq_num_ + 1u);
Elad Alon10874b22019-02-21 16:25:40 +010067
Niels Möllera7401422019-09-13 14:18:58 +020068 last_received_seq_num_ = rtp_seq_num;
Elad Alon10874b22019-02-21 16:25:40 +010069
Artem Titovdcd7fc72021-08-09 13:02:57 +020070 // `frame` is not nullptr iff the packet is the first packet in the frame.
Danil Chapovalov159c4142020-01-23 15:17:52 +010071 if (frame != nullptr) {
Elad Alon10874b22019-02-21 16:25:40 +010072 // Ignore repeated or reordered frames.
Danil Chapovalov159c4142020-01-23 15:17:52 +010073 // TODO(bugs.webrtc.org/10336): Handle frame reordering.
74 if (last_received_frame_id_.has_value() &&
75 frame->frame_id <= last_received_frame_id_.value()) {
76 RTC_LOG(LS_WARNING) << "Repeated or reordered frame ID ("
77 << frame->frame_id << ").";
Elad Alon10874b22019-02-21 16:25:40 +010078 return;
79 }
80
Danil Chapovalov159c4142020-01-23 15:17:52 +010081 last_received_frame_id_ = frame->frame_id;
Elad Alon10874b22019-02-21 16:25:40 +010082
Danil Chapovalov159c4142020-01-23 15:17:52 +010083 if (frame->is_keyframe) {
Elad Alon10874b22019-02-21 16:25:40 +010084 // Subsequent frames may not rely on frames before the key frame.
Elad Alonb6ef99b2019-04-10 16:37:07 +020085 // Note that upon receiving a key frame, we do not issue a loss
86 // notification on RTP sequence number gap, unless that gap spanned
87 // the key frame itself. This is because any loss which occurred before
88 // the key frame is no longer relevant.
Danil Chapovalov159c4142020-01-23 15:17:52 +010089 decodable_frame_ids_.clear();
Elad Alon10874b22019-02-21 16:25:40 +010090 current_frame_potentially_decodable_ = true;
91 } else {
Danil Chapovalov159c4142020-01-23 15:17:52 +010092 const bool all_dependencies_decodable =
93 AllDependenciesDecodable(frame->frame_dependencies);
Elad Alon10874b22019-02-21 16:25:40 +010094 current_frame_potentially_decodable_ = all_dependencies_decodable;
95 if (seq_num_gap || !current_frame_potentially_decodable_) {
Niels Möllera7401422019-09-13 14:18:58 +020096 HandleLoss(rtp_seq_num, current_frame_potentially_decodable_);
Elad Alon10874b22019-02-21 16:25:40 +010097 }
98 }
99 } else if (seq_num_gap || !current_frame_potentially_decodable_) {
100 current_frame_potentially_decodable_ = false;
101 // We allow sending multiple loss notifications for a single frame
102 // even if only one of its packets is lost. We do this because the bigger
103 // the frame, the more likely it is to be non-discardable, and therefore
104 // the more robust we wish to be to loss of the feedback messages.
Niels Möllera7401422019-09-13 14:18:58 +0200105 HandleLoss(rtp_seq_num, false);
Elad Alon10874b22019-02-21 16:25:40 +0100106 }
107}
108
109void LossNotificationController::OnAssembledFrame(
110 uint16_t first_seq_num,
Danil Chapovalov159c4142020-01-23 15:17:52 +0100111 int64_t frame_id,
Elad Alon10874b22019-02-21 16:25:40 +0100112 bool discardable,
Danil Chapovalov159c4142020-01-23 15:17:52 +0100113 rtc::ArrayView<const int64_t> frame_dependencies) {
Sebastian Janssonb55015e2019-04-09 13:44:04 +0200114 RTC_DCHECK_RUN_ON(&sequence_checker_);
Elad Alon10874b22019-02-21 16:25:40 +0100115
116 DiscardOldInformation(); // Prevent memory overconsumption.
117
118 if (discardable) {
119 return;
120 }
121
Danil Chapovalov159c4142020-01-23 15:17:52 +0100122 if (!AllDependenciesDecodable(frame_dependencies)) {
Elad Alon10874b22019-02-21 16:25:40 +0100123 return;
124 }
125
126 last_decodable_non_discardable_.emplace(first_seq_num);
Danil Chapovalov159c4142020-01-23 15:17:52 +0100127 const auto it = decodable_frame_ids_.insert(frame_id);
Elad Alon10874b22019-02-21 16:25:40 +0100128 RTC_DCHECK(it.second);
129}
130
131void LossNotificationController::DiscardOldInformation() {
132 constexpr size_t kExpectedKeyFrameIntervalFrames = 3000;
133 constexpr size_t kMaxSize = 2 * kExpectedKeyFrameIntervalFrames;
134 constexpr size_t kTargetSize = kExpectedKeyFrameIntervalFrames;
Danil Chapovalov159c4142020-01-23 15:17:52 +0100135 PareDown(&decodable_frame_ids_, kMaxSize, kTargetSize);
Elad Alon10874b22019-02-21 16:25:40 +0100136}
137
138bool LossNotificationController::AllDependenciesDecodable(
Danil Chapovalov159c4142020-01-23 15:17:52 +0100139 rtc::ArrayView<const int64_t> frame_dependencies) const {
Sebastian Janssonb55015e2019-04-09 13:44:04 +0200140 RTC_DCHECK_RUN_ON(&sequence_checker_);
Elad Alon10874b22019-02-21 16:25:40 +0100141
142 // Due to packet reordering, frame buffering and asynchronous decoders, it is
143 // infeasible to make reliable conclusions on the decodability of a frame
144 // immediately when it arrives. We use the following assumptions:
145 // * Intra frames are decodable.
146 // * Inter frames are decodable if all of their references were decodable.
147 // One possibility that is ignored, is that the packet may be corrupt.
Danil Chapovalov159c4142020-01-23 15:17:52 +0100148 for (int64_t ref_frame_id : frame_dependencies) {
149 const auto ref_frame_it = decodable_frame_ids_.find(ref_frame_id);
150 if (ref_frame_it == decodable_frame_ids_.end()) {
Elad Alon10874b22019-02-21 16:25:40 +0100151 // Reference frame not decodable.
152 return false;
153 }
154 }
155
156 return true;
157}
158
159void LossNotificationController::HandleLoss(uint16_t last_received_seq_num,
160 bool decodability_flag) {
Sebastian Janssonb55015e2019-04-09 13:44:04 +0200161 RTC_DCHECK_RUN_ON(&sequence_checker_);
Elad Alon10874b22019-02-21 16:25:40 +0100162
163 if (last_decodable_non_discardable_) {
164 RTC_DCHECK(AheadOf(last_received_seq_num,
165 last_decodable_non_discardable_->first_seq_num));
166 loss_notification_sender_->SendLossNotification(
167 last_decodable_non_discardable_->first_seq_num, last_received_seq_num,
Elad Alone86af2c2019-06-03 14:37:50 +0200168 decodability_flag, /*buffering_allowed=*/true);
Elad Alon10874b22019-02-21 16:25:40 +0100169 } else {
170 key_frame_request_sender_->RequestKeyFrame();
171 }
172}
173} // namespace webrtc