blob: 53c1635f78e8df950349dcd9ff2a22d9e21cb0a3 [file] [log] [blame] [edit]
/*
* 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_simulator.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
#include "absl/algorithm/container.h"
#include "absl/base/nullability.h"
#include "absl/container/flat_hash_map.h"
#include "api/environment/environment.h"
#include "api/sequence_checker.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "api/video/encoded_frame.h"
#include "logging/rtc_event_log/rtc_event_log_parser.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/sequence_number_unwrapper.h"
#include "rtc_base/thread_annotations.h"
#include "video/timing/simulator/assembler.h"
#include "video/timing/simulator/decodability_tracker.h"
#include "video/timing/simulator/rtc_event_log_driver.h"
namespace webrtc::video_timing_simulator {
namespace {
// Observes the `Assembler` and `DecodabilityTracker` in order to collect
// frame metadata for decodable frames.
class DecodableFrameCollector : public AssemblerEvents,
public DecodabilityTrackerEvents {
public:
DecodableFrameCollector(const Environment& env, uint32_t ssrc)
: env_(env), ssrc_(ssrc) {
RTC_DCHECK_NE(ssrc_, 0);
}
~DecodableFrameCollector() override = default;
DecodableFrameCollector(const DecodableFrameCollector&) = delete;
DecodableFrameCollector& operator=(const DecodableFrameCollector&) = delete;
// Implements `AssemblerEvents`.
void OnAssembledFrame(const EncodedFrame& assembled_frame) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
Timestamp now = env_.clock().CurrentTime();
if (!creation_timestamp_) {
creation_timestamp_ = now;
}
int64_t frame_id = assembled_frame.Id();
if (frames_.contains(frame_id)) {
RTC_LOG(LS_WARNING) << "Assembled frame_id=" << frame_id
<< " on ssrc=" << ssrc_
<< " had already been collected. Dropping it."
<< " (simulated_ts=" << env_.clock().CurrentTime()
<< ")";
return;
}
auto& frame = frames_[frame_id];
RTC_DCHECK(!assembled_frame.PacketInfos().empty());
frame.num_packets = static_cast<int>(assembled_frame.PacketInfos().size());
frame.size = DataSize::Bytes(assembled_frame.size());
frame.unwrapped_rtp_timestamp =
rtp_timestamp_unwrapper_.Unwrap(assembled_frame.RtpTimestamp());
frame.assembled_timestamp = now;
}
// Implements `DecodabilityTrackerEvents`.
void OnDecodableFrame(const EncodedFrame& decodable_frame) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
int64_t frame_id = decodable_frame.Id();
auto it = frames_.find(frame_id);
if (it == frames_.end()) {
RTC_LOG(LS_WARNING)
<< "Decodable frame_id=" << frame_id << " on ssrc=" << ssrc_
<< " had no assembly information collected. Dropping it."
<< " (simulated_ts=" << env_.clock().CurrentTime() << ")";
return;
}
auto& frame = it->second;
frame.decodable_timestamp = env_.clock().CurrentTime();
}
DecodabilitySimulator::Stream GetStream() const {
RTC_DCHECK_RUN_ON(&sequence_checker_);
DecodabilitySimulator::Stream stream;
stream.ssrc = ssrc_;
if (creation_timestamp_) {
stream.creation_timestamp = *creation_timestamp_;
}
stream.frames.reserve(frames_.size());
for (const auto& [key, value] : frames_) {
stream.frames.push_back(value);
}
absl::c_sort(stream.frames);
return stream;
}
private:
SequenceChecker sequence_checker_;
const Environment env_;
const uint32_t ssrc_;
std::optional<Timestamp> creation_timestamp_
RTC_GUARDED_BY(sequence_checker_);
SeqNumUnwrapper<uint32_t> rtp_timestamp_unwrapper_
RTC_GUARDED_BY(sequence_checker_);
absl::flat_hash_map</*frame_id*/ int64_t, DecodabilitySimulator::Frame>
frames_ RTC_GUARDED_BY(sequence_checker_);
};
// Combines all objects needed to perform decodability simulation of a single
// stream. Inserts the streams results to the `results` pointer when `Close()`
// is called (at the end of simulation).
class DecodabilitySimulatorStream : public RtcEventLogDriver::StreamInterface {
public:
DecodabilitySimulatorStream(const Environment& env,
uint32_t ssrc,
DecodabilitySimulator::Results* absl_nonnull
results)
: collector_(env, ssrc),
tracker_(env, DecodabilityTracker::Config{.ssrc = ssrc}, &collector_),
assembler_(env, ssrc, &collector_, &tracker_),
results_(*results) {
RTC_DCHECK_RUN_ON(&sequence_checker_);
tracker_.SetDecodedFrameIdCallback(&assembler_);
}
~DecodabilitySimulatorStream() override = default;
// Implements `RtcEventLogDriver::StreamInterface`.
void InsertPacket(const RtpPacketReceived& rtp_packet) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
assembler_.InsertPacket(rtp_packet);
}
void Close() override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
DecodabilitySimulator::Stream stream = collector_.GetStream();
if (!stream.IsEmpty()) {
RTC_DCHECK_NE(stream.ssrc, 0u);
results_.streams.push_back(stream);
}
}
private:
SequenceChecker sequence_checker_;
DecodableFrameCollector collector_ RTC_GUARDED_BY(sequence_checker_);
DecodabilityTracker tracker_ RTC_GUARDED_BY(sequence_checker_);
Assembler assembler_ RTC_GUARDED_BY(sequence_checker_);
DecodabilitySimulator::Results& results_;
};
} // namespace
DecodabilitySimulator::Results DecodabilitySimulator::Simulate(
const ParsedRtcEventLog& parsed_log) const {
// Outputs.
Results results;
// Simulation.
auto stream_factory = [&results](const Environment& env, uint32_t ssrc) {
return std::make_unique<DecodabilitySimulatorStream>(env, ssrc, &results);
};
// In order to keep the decodability data clean, we do not reuse streams.
// Decodability should not be a function of any field trials, so we pass the
// empty string here.
RtcEventLogDriver rtc_event_log_simulator(
{.reuse_streams = false}, &parsed_log,
/*field_trials_string=*/"", std::move(stream_factory));
rtc_event_log_simulator.Simulate();
// Return.
absl::c_sort(results.streams);
return results;
}
} // namespace webrtc::video_timing_simulator