| /* | 
 |  *  Copyright (c) 2021 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 "api/video/frame_buffer.h" | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "absl/algorithm/container.h" | 
 | #include "absl/container/inlined_vector.h" | 
 | #include "rtc_base/logging.h" | 
 | #include "rtc_base/numerics/sequence_number_util.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 | bool ValidReferences(const EncodedFrame& frame) { | 
 |   // All references must point backwards, and duplicates are not allowed. | 
 |   for (size_t i = 0; i < frame.num_references; ++i) { | 
 |     if (frame.references[i] >= frame.Id()) | 
 |       return false; | 
 |  | 
 |     for (size_t j = i + 1; j < frame.num_references; ++j) { | 
 |       if (frame.references[i] == frame.references[j]) | 
 |         return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Since FrameBuffer::FrameInfo is private it can't be used in the function | 
 | // signature, hence the FrameIteratorT type. | 
 | template <typename FrameIteratorT> | 
 | rtc::ArrayView<const int64_t> GetReferences(const FrameIteratorT& it) { | 
 |   return {it->second.encoded_frame->references, | 
 |           std::min<size_t>(it->second.encoded_frame->num_references, | 
 |                            EncodedFrame::kMaxFrameReferences)}; | 
 | } | 
 |  | 
 | template <typename FrameIteratorT> | 
 | int64_t GetFrameId(const FrameIteratorT& it) { | 
 |   return it->first; | 
 | } | 
 |  | 
 | template <typename FrameIteratorT> | 
 | uint32_t GetTimestamp(const FrameIteratorT& it) { | 
 |   return it->second.encoded_frame->RtpTimestamp(); | 
 | } | 
 |  | 
 | template <typename FrameIteratorT> | 
 | bool IsLastFrameInTemporalUnit(const FrameIteratorT& it) { | 
 |   return it->second.encoded_frame->is_last_spatial_layer; | 
 | } | 
 | }  // namespace | 
 |  | 
 | FrameBuffer::FrameBuffer(int max_size, | 
 |                          int max_decode_history, | 
 |                          const FieldTrialsView& field_trials) | 
 |     : legacy_frame_id_jump_behavior_( | 
 |           !field_trials.IsDisabled("WebRTC-LegacyFrameIdJumpBehavior")), | 
 |       max_size_(max_size), | 
 |       decoded_frame_history_(max_decode_history) {} | 
 |  | 
 | bool FrameBuffer::InsertFrame(std::unique_ptr<EncodedFrame> frame) { | 
 |   if (!ValidReferences(*frame)) { | 
 |     RTC_DLOG(LS_WARNING) << "Frame " << frame->Id() | 
 |                          << " has invalid references, dropping frame."; | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (frame->Id() <= decoded_frame_history_.GetLastDecodedFrameId()) { | 
 |     if (legacy_frame_id_jump_behavior_ && frame->is_keyframe() && | 
 |         AheadOf(frame->RtpTimestamp(), | 
 |                 *decoded_frame_history_.GetLastDecodedFrameTimestamp())) { | 
 |       RTC_DLOG(LS_WARNING) | 
 |           << "Keyframe " << frame->Id() | 
 |           << " has newer timestamp but older picture id, clearing buffer."; | 
 |       Clear(); | 
 |     } else { | 
 |       // Already decoded past this frame. | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   if (frames_.size() == max_size_) { | 
 |     if (frame->is_keyframe()) { | 
 |       RTC_DLOG(LS_WARNING) << "Keyframe " << frame->Id() | 
 |                            << " inserted into full buffer, clearing buffer."; | 
 |       Clear(); | 
 |     } else { | 
 |       // No space for this frame. | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   const int64_t frame_id = frame->Id(); | 
 |   auto insert_res = frames_.emplace(frame_id, FrameInfo{std::move(frame)}); | 
 |   if (!insert_res.second) { | 
 |     // Frame has already been inserted. | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (frames_.size() == max_size_) { | 
 |     RTC_DLOG(LS_WARNING) << "Frame " << frame_id | 
 |                          << " inserted, buffer is now full."; | 
 |   } | 
 |  | 
 |   PropagateContinuity(insert_res.first); | 
 |   FindNextAndLastDecodableTemporalUnit(); | 
 |   return true; | 
 | } | 
 |  | 
 | absl::InlinedVector<std::unique_ptr<EncodedFrame>, 4> | 
 | FrameBuffer::ExtractNextDecodableTemporalUnit() { | 
 |   absl::InlinedVector<std::unique_ptr<EncodedFrame>, 4> res; | 
 |   if (!next_decodable_temporal_unit_) { | 
 |     return res; | 
 |   } | 
 |  | 
 |   auto end_it = std::next(next_decodable_temporal_unit_->last_frame); | 
 |   for (auto it = next_decodable_temporal_unit_->first_frame; it != end_it; | 
 |        ++it) { | 
 |     decoded_frame_history_.InsertDecoded(GetFrameId(it), GetTimestamp(it)); | 
 |     res.push_back(std::move(it->second.encoded_frame)); | 
 |   } | 
 |  | 
 |   DropNextDecodableTemporalUnit(); | 
 |   return res; | 
 | } | 
 |  | 
 | void FrameBuffer::DropNextDecodableTemporalUnit() { | 
 |   if (!next_decodable_temporal_unit_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   auto end_it = std::next(next_decodable_temporal_unit_->last_frame); | 
 |   num_dropped_frames_ += std::count_if( | 
 |       frames_.begin(), end_it, | 
 |       [](const auto& f) { return f.second.encoded_frame != nullptr; }); | 
 |  | 
 |   frames_.erase(frames_.begin(), end_it); | 
 |   FindNextAndLastDecodableTemporalUnit(); | 
 | } | 
 |  | 
 | absl::optional<int64_t> FrameBuffer::LastContinuousFrameId() const { | 
 |   return last_continuous_frame_id_; | 
 | } | 
 |  | 
 | absl::optional<int64_t> FrameBuffer::LastContinuousTemporalUnitFrameId() const { | 
 |   return last_continuous_temporal_unit_frame_id_; | 
 | } | 
 |  | 
 | absl::optional<FrameBuffer::DecodabilityInfo> | 
 | FrameBuffer::DecodableTemporalUnitsInfo() const { | 
 |   return decodable_temporal_units_info_; | 
 | } | 
 |  | 
 | int FrameBuffer::GetTotalNumberOfContinuousTemporalUnits() const { | 
 |   return num_continuous_temporal_units_; | 
 | } | 
 | int FrameBuffer::GetTotalNumberOfDroppedFrames() const { | 
 |   return num_dropped_frames_; | 
 | } | 
 |  | 
 | size_t FrameBuffer::CurrentSize() const { | 
 |   return frames_.size(); | 
 | } | 
 |  | 
 | bool FrameBuffer::IsContinuous(const FrameIterator& it) const { | 
 |   for (int64_t reference : GetReferences(it)) { | 
 |     if (decoded_frame_history_.WasDecoded(reference)) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     auto reference_frame_it = frames_.find(reference); | 
 |     if (reference_frame_it != frames_.end() && | 
 |         reference_frame_it->second.continuous) { | 
 |       continue; | 
 |     } | 
 |  | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void FrameBuffer::PropagateContinuity(const FrameIterator& frame_it) { | 
 |   for (auto it = frame_it; it != frames_.end(); ++it) { | 
 |     if (!it->second.continuous) { | 
 |       if (IsContinuous(it)) { | 
 |         it->second.continuous = true; | 
 |         if (last_continuous_frame_id_ < GetFrameId(it)) { | 
 |           last_continuous_frame_id_ = GetFrameId(it); | 
 |         } | 
 |         if (IsLastFrameInTemporalUnit(it)) { | 
 |           num_continuous_temporal_units_++; | 
 |           if (last_continuous_temporal_unit_frame_id_ < GetFrameId(it)) { | 
 |             last_continuous_temporal_unit_frame_id_ = GetFrameId(it); | 
 |           } | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void FrameBuffer::FindNextAndLastDecodableTemporalUnit() { | 
 |   next_decodable_temporal_unit_.reset(); | 
 |   decodable_temporal_units_info_.reset(); | 
 |  | 
 |   if (!last_continuous_temporal_unit_frame_id_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   FrameIterator first_frame_it = frames_.begin(); | 
 |   FrameIterator last_frame_it = frames_.begin(); | 
 |   absl::InlinedVector<int64_t, 4> frames_in_temporal_unit; | 
 |   uint32_t last_decodable_temporal_unit_timestamp; | 
 |   for (auto frame_it = frames_.begin(); frame_it != frames_.end();) { | 
 |     if (GetFrameId(frame_it) > *last_continuous_temporal_unit_frame_id_) { | 
 |       break; | 
 |     } | 
 |  | 
 |     if (GetTimestamp(frame_it) != GetTimestamp(first_frame_it)) { | 
 |       frames_in_temporal_unit.clear(); | 
 |       first_frame_it = frame_it; | 
 |     } | 
 |  | 
 |     frames_in_temporal_unit.push_back(GetFrameId(frame_it)); | 
 |  | 
 |     last_frame_it = frame_it++; | 
 |  | 
 |     if (IsLastFrameInTemporalUnit(last_frame_it)) { | 
 |       bool temporal_unit_decodable = true; | 
 |       for (auto it = first_frame_it; it != frame_it && temporal_unit_decodable; | 
 |            ++it) { | 
 |         for (int64_t reference : GetReferences(it)) { | 
 |           if (!decoded_frame_history_.WasDecoded(reference) && | 
 |               !absl::c_linear_search(frames_in_temporal_unit, reference)) { | 
 |             // A frame in the temporal unit has a non-decoded reference outside | 
 |             // the temporal unit, so it's not yet ready to be decoded. | 
 |             temporal_unit_decodable = false; | 
 |             break; | 
 |           } | 
 |         } | 
 |       } | 
 |  | 
 |       if (temporal_unit_decodable) { | 
 |         if (!next_decodable_temporal_unit_) { | 
 |           next_decodable_temporal_unit_ = {first_frame_it, last_frame_it}; | 
 |         } | 
 |  | 
 |         last_decodable_temporal_unit_timestamp = GetTimestamp(first_frame_it); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (next_decodable_temporal_unit_) { | 
 |     decodable_temporal_units_info_ = { | 
 |         .next_rtp_timestamp = | 
 |             GetTimestamp(next_decodable_temporal_unit_->first_frame), | 
 |         .last_rtp_timestamp = last_decodable_temporal_unit_timestamp}; | 
 |   } | 
 | } | 
 |  | 
 | void FrameBuffer::Clear() { | 
 |   frames_.clear(); | 
 |   next_decodable_temporal_unit_.reset(); | 
 |   decodable_temporal_units_info_.reset(); | 
 |   last_continuous_frame_id_.reset(); | 
 |   last_continuous_temporal_unit_frame_id_.reset(); | 
 |   decoded_frame_history_.Clear(); | 
 | } | 
 |  | 
 | }  // namespace webrtc |