| /* | |
| * Copyright (c) 2025 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 "video/timing/simulator/decodability_tracker.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "absl/base/nullability.h" | |
| #include "absl/container/inlined_vector.h" | |
| #include "api/environment/environment.h" | |
| #include "api/sequence_checker.h" | |
| #include "api/video/encoded_frame.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/logging.h" | |
| #include "video/timing/simulator/assembler.h" | |
| namespace webrtc::video_timing_simulator { | |
| // `FrameBuffer` configuration. | |
| // Default values taken from video_stream_buffer_controller.cc. | |
| constexpr int kMaxFrameBufferSize = 800; | |
| constexpr int kMaxFrameBufferHistory = 1 << 13; | |
| DecodabilityTracker::DecodabilityTracker(const Environment& env, | |
| const Config& config, | |
| DecodabilityTrackerEvents* absl_nonnull | |
| observer) | |
| : env_(env), | |
| config_(config), | |
| frame_buffer_(kMaxFrameBufferSize, | |
| kMaxFrameBufferHistory, | |
| env.field_trials()), | |
| observer_(*observer), | |
| decoded_frame_id_cb_(nullptr) { | |
| RTC_DCHECK_RUN_ON(&sequence_checker_); | |
| // Validation. | |
| RTC_DCHECK_NE(config.ssrc, 0u); | |
| } | |
| DecodabilityTracker::~DecodabilityTracker() { | |
| RTC_DCHECK_RUN_ON(&sequence_checker_); | |
| } | |
| void DecodabilityTracker::SetDecodedFrameIdCallback( | |
| DecodedFrameIdCallback* absl_nonnull decoded_frame_id_cb) { | |
| RTC_DCHECK_RUN_ON(&sequence_checker_); | |
| decoded_frame_id_cb_ = decoded_frame_id_cb; | |
| } | |
| void DecodabilityTracker::OnAssembledFrame( | |
| std::unique_ptr<EncodedFrame> assembled_frame) { | |
| RTC_DCHECK_RUN_ON(&sequence_checker_); | |
| RTC_CHECK(decoded_frame_id_cb_) << "Callback must be set before running"; | |
| if (!frame_buffer_.InsertFrame(std::move(assembled_frame))) { | |
| RTC_LOG(LS_ERROR) << "FrameBuffer insertion error for ssrc=" << config_.ssrc | |
| << " (simulated_ts=" << env_.clock().CurrentTime() << ")"; | |
| } | |
| // The insertion of `assembled_frame` may have made one or many frames | |
| // "continuous" (indirectly decodable). Iterate through all of these to get | |
| // get all decodable frames out of the buffer. | |
| // TODO: b/423646186 - Consider handling reordered higher temporal layers | |
| // better (right now they would be fast-forwarded over). This would likely | |
| // be done by introducing a lag between insertion and extraction, where the | |
| // lag duration is set as a (large) multiple of some typical network RTT. | |
| while (frame_buffer_.DecodableTemporalUnitsInfo()) { | |
| absl::InlinedVector<std::unique_ptr<webrtc::EncodedFrame>, 4> | |
| next_decodable_frames = | |
| frame_buffer_.ExtractNextDecodableTemporalUnit(); | |
| // TODO: b/423646186 - Improve the handling of inter-layer predicted frames | |
| // here. See `CombineAndDeleteFrames` in frame_helpers.cc. | |
| for (std::unique_ptr<EncodedFrame>& encoded_frame : next_decodable_frames) { | |
| observer_.OnDecodableFrame(*encoded_frame); | |
| decoded_frame_id_cb_->OnDecodedFrameId(encoded_frame->Id()); | |
| encoded_frame.reset(); // Just to be explicit. | |
| } | |
| } | |
| } | |
| } // namespace webrtc::video_timing_simulator |