blob: 6389fd0454c90dfb07167072a1d0519a7b24b71d [file] [log] [blame]
* 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 "rtc_base/checks.h"
#include "rtc_base/logging.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
KeyFrameRequestSender* key_frame_request_sender,
LossNotificationSender* loss_notification_sender)
: key_frame_request_sender_(key_frame_request_sender),
current_frame_potentially_decodable_(true) {
LossNotificationController::~LossNotificationController() = default;
void LossNotificationController::OnReceivedPacket(const VCMPacket& packet) {
if (!packet.generic_descriptor) {
RTC_LOG(LS_WARNING) << "Generic frame descriptor missing. Buggy remote? "
"Misconfigured local?";
// Ignore repeated or reordered packets.
// TODO( Handle packet reordering.
if (last_received_seq_num_ &&
!AheadOf(packet.seqNum, *last_received_seq_num_)) {
DiscardOldInformation(); // Prevent memory overconsumption.
const bool seq_num_gap =
last_received_seq_num_ &&
packet.seqNum != static_cast<uint16_t>(*last_received_seq_num_ + 1u);
last_received_seq_num_ = packet.seqNum;
if (packet.generic_descriptor->FirstPacketInSubFrame()) {
const uint16_t frame_id = packet.generic_descriptor->FrameId();
const int64_t unwrapped_frame_id = frame_id_unwrapper_.Unwrap(frame_id);
// Ignore repeated or reordered frames.
// TODO(TODO( Handle frame reordering.
if (last_received_unwrapped_frame_id_ &&
unwrapped_frame_id <= *last_received_unwrapped_frame_id_) {
RTC_LOG(LS_WARNING) << "Repeated or reordered frame ID (" << frame_id
<< ").";
last_received_unwrapped_frame_id_ = unwrapped_frame_id;
const bool intra_frame =
// Generic Frame Descriptor does not current allow us to distinguish
// whether an intra frame is a key frame.
// We therefore assume all intra frames are key frames.
const bool key_frame = intra_frame;
if (key_frame) {
// 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.
current_frame_potentially_decodable_ = true;
} else {
const bool all_dependencies_decodable = AllDependenciesDecodable(
current_frame_potentially_decodable_ = all_dependencies_decodable;
if (seq_num_gap || !current_frame_potentially_decodable_) {
HandleLoss(packet.seqNum, 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(packet.seqNum, false);
void LossNotificationController::OnAssembledFrame(
uint16_t first_seq_num,
uint16_t frame_id,
bool discardable,
rtc::ArrayView<const uint16_t> frame_dependency_diffs) {
DiscardOldInformation(); // Prevent memory overconsumption.
if (discardable) {
const int64_t unwrapped_frame_id = frame_id_unwrapper_.Unwrap(frame_id);
if (!AllDependenciesDecodable(unwrapped_frame_id, frame_dependency_diffs)) {
const auto it = decodable_unwrapped_frame_ids_.insert(unwrapped_frame_id);
void LossNotificationController::DiscardOldInformation() {
constexpr size_t kExpectedKeyFrameIntervalFrames = 3000;
constexpr size_t kMaxSize = 2 * kExpectedKeyFrameIntervalFrames;
constexpr size_t kTargetSize = kExpectedKeyFrameIntervalFrames;
PareDown(&decodable_unwrapped_frame_ids_, kMaxSize, kTargetSize);
bool LossNotificationController::AllDependenciesDecodable(
int64_t unwrapped_frame_id,
rtc::ArrayView<const uint16_t> frame_dependency_diffs) const {
// 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 (uint16_t frame_dependency_diff : frame_dependency_diffs) {
const int64_t unwrapped_ref_frame_id =
unwrapped_frame_id - frame_dependency_diff;
const auto ref_frame_it =
if (ref_frame_it == decodable_unwrapped_frame_ids_.end()) {
// Reference frame not decodable.
return false;
return true;
void LossNotificationController::HandleLoss(uint16_t last_received_seq_num,
bool decodability_flag) {
if (last_decodable_non_discardable_) {
last_decodable_non_discardable_->first_seq_num, last_received_seq_num,
decodability_flag, /*buffering_allowed=*/true);
} else {
} // namespace webrtc