2 * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
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.
11 #include "modules/video_coding/loss_notification_controller.h"
15 #include "api/array_view.h"
16 #include "rtc_base/checks.h"
17 #include "rtc_base/logging.h"
18 #include "rtc_base/numerics/sequence_number_util.h"
22 // 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.
24 template <typename Container
>
25 void PareDown(Container
* container
,
26 size_t max_allowed_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
);
38 LossNotificationController::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_
);
48 LossNotificationController::~LossNotificationController() = default;
50 void LossNotificationController::OnReceivedPacket(
52 const LossNotificationController::FrameDetails
* frame
) {
53 RTC_DCHECK_RUN_ON(&sequence_checker_
);
55 // Ignore repeated or reordered packets.
56 // TODO(bugs.webrtc.org/10336): Handle packet reordering.
57 if (last_received_seq_num_
&&
58 !AheadOf(rtp_seq_num
, *last_received_seq_num_
)) {
62 DiscardOldInformation(); // Prevent memory overconsumption.
64 const bool seq_num_gap
=
65 last_received_seq_num_
&&
66 rtp_seq_num
!= static_cast<uint16_t>(*last_received_seq_num_
+ 1u);
68 last_received_seq_num_
= rtp_seq_num
;
70 // `frame` is not nullptr iff the packet is the first packet in the frame.
71 if (frame
!= nullptr) {
72 // Ignore repeated or reordered frames.
73 // 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
<< ").";
81 last_received_frame_id_
= frame
->frame_id
;
83 if (frame
->is_keyframe
) {
84 // Subsequent frames may not rely on frames before the key frame.
85 // 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.
89 decodable_frame_ids_
.clear();
90 current_frame_potentially_decodable_
= true;
92 const bool all_dependencies_decodable
=
93 AllDependenciesDecodable(frame
->frame_dependencies
);
94 current_frame_potentially_decodable_
= all_dependencies_decodable
;
95 if (seq_num_gap
|| !current_frame_potentially_decodable_
) {
96 HandleLoss(rtp_seq_num
, current_frame_potentially_decodable_
);
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.
105 HandleLoss(rtp_seq_num
, false);
109 void LossNotificationController::OnAssembledFrame(
110 uint16_t first_seq_num
,
113 rtc::ArrayView
<const int64_t> frame_dependencies
) {
114 RTC_DCHECK_RUN_ON(&sequence_checker_
);
116 DiscardOldInformation(); // Prevent memory overconsumption.
122 if (!AllDependenciesDecodable(frame_dependencies
)) {
126 last_decodable_non_discardable_
.emplace(first_seq_num
);
127 const auto it
= decodable_frame_ids_
.insert(frame_id
);
128 RTC_DCHECK(it
.second
);
131 void LossNotificationController::DiscardOldInformation() {
132 constexpr size_t kExpectedKeyFrameIntervalFrames
= 3000;
133 constexpr size_t kMaxSize
= 2 * kExpectedKeyFrameIntervalFrames
;
134 constexpr size_t kTargetSize
= kExpectedKeyFrameIntervalFrames
;
135 PareDown(&decodable_frame_ids_
, kMaxSize
, kTargetSize
);
138 bool LossNotificationController::AllDependenciesDecodable(
139 rtc::ArrayView
<const int64_t> frame_dependencies
) const {
140 RTC_DCHECK_RUN_ON(&sequence_checker_
);
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.
148 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()) {
151 // Reference frame not decodable.
159 void LossNotificationController::HandleLoss(uint16_t last_received_seq_num
,
160 bool decodability_flag
) {
161 RTC_DCHECK_RUN_ON(&sequence_checker_
);
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
,
168 decodability_flag
, /*buffering_allowed=*/true);
170 key_frame_request_sender_
->RequestKeyFrame();
173 } // namespace webrtc