Add a tracker for RTCRtpContributingSource and RTCRtpSynchronizationSource.
This change adds a new SourceTracker class that can do spec-compliant tracking of RTCRtpContributingSource and RTCRtpSynchronizationSource when frames are delivered to the RTCRtpReceiver's MediaStreamTrack for playout. It will replace the existing spec-incompliant ContributingSources. Bug: webrtc:10545 webrtc:10668 Change-Id: I961adaba09d6337f2f36b301a4fabcd20de65271 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/140948 Commit-Queue: Chen Xing <chxg@google.com> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28249}
This commit is contained in:
@ -180,6 +180,8 @@ rtc_static_library("rtp_rtcp") {
|
||||
"source/rtp_sequence_number_map.h",
|
||||
"source/rtp_utility.cc",
|
||||
"source/rtp_utility.h",
|
||||
"source/source_tracker.cc",
|
||||
"source/source_tracker.h",
|
||||
"source/time_util.cc",
|
||||
"source/time_util.h",
|
||||
"source/tmmbr_help.cc",
|
||||
@ -209,6 +211,7 @@ rtc_static_library("rtp_rtcp") {
|
||||
"../../api:function_view",
|
||||
"../../api:libjingle_peerconnection_api",
|
||||
"../../api:rtp_headers",
|
||||
"../../api:rtp_packet_info",
|
||||
"../../api:scoped_refptr",
|
||||
"../../api:transport_api",
|
||||
"../../api/audio_codecs:audio_codecs_api",
|
||||
@ -434,6 +437,7 @@ if (rtc_include_tests) {
|
||||
"source/rtp_sender_video_unittest.cc",
|
||||
"source/rtp_sequence_number_map_unittest.cc",
|
||||
"source/rtp_utility_unittest.cc",
|
||||
"source/source_tracker_unittest.cc",
|
||||
"source/time_util_unittest.cc",
|
||||
"source/ulpfec_generator_unittest.cc",
|
||||
"source/ulpfec_header_reader_writer_unittest.cc",
|
||||
@ -449,6 +453,8 @@ if (rtc_include_tests) {
|
||||
"../..:webrtc_common",
|
||||
"../../api:array_view",
|
||||
"../../api:libjingle_peerconnection_api",
|
||||
"../../api:rtp_headers",
|
||||
"../../api:rtp_packet_info",
|
||||
"../../api:scoped_refptr",
|
||||
"../../api:transport_api",
|
||||
"../../api/transport:field_trial_based_config",
|
||||
|
96
modules/rtp_rtcp/source/source_tracker.cc
Normal file
96
modules/rtp_rtcp/source/source_tracker.cc
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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/rtp_rtcp/source/source_tracker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
constexpr int64_t SourceTracker::kTimeoutMs;
|
||||
|
||||
SourceTracker::SourceTracker(Clock* clock) : clock_(clock) {}
|
||||
|
||||
void SourceTracker::OnFrameDelivered(const RtpPacketInfos& packet_infos) {
|
||||
if (packet_infos.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
rtc::CritScope lock_scope(&lock_);
|
||||
|
||||
for (const auto& packet_info : packet_infos) {
|
||||
for (uint32_t csrc : packet_info.csrcs()) {
|
||||
SourceKey key(RtpSourceType::CSRC, csrc);
|
||||
SourceEntry& entry = UpdateEntry(key);
|
||||
|
||||
entry.timestamp_ms = now_ms;
|
||||
entry.audio_level = packet_info.audio_level();
|
||||
entry.rtp_timestamp = packet_info.rtp_timestamp();
|
||||
}
|
||||
|
||||
SourceKey key(RtpSourceType::SSRC, packet_info.ssrc());
|
||||
SourceEntry& entry = UpdateEntry(key);
|
||||
|
||||
entry.timestamp_ms = now_ms;
|
||||
entry.audio_level = packet_info.audio_level();
|
||||
entry.rtp_timestamp = packet_info.rtp_timestamp();
|
||||
}
|
||||
|
||||
PruneEntries(now_ms);
|
||||
}
|
||||
|
||||
std::vector<RtpSource> SourceTracker::GetSources() const {
|
||||
std::vector<RtpSource> sources;
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
rtc::CritScope lock_scope(&lock_);
|
||||
|
||||
PruneEntries(now_ms);
|
||||
|
||||
for (const auto& pair : list_) {
|
||||
const SourceKey& key = pair.first;
|
||||
const SourceEntry& entry = pair.second;
|
||||
|
||||
sources.emplace_back(entry.timestamp_ms, key.source, key.source_type,
|
||||
entry.audio_level, entry.rtp_timestamp);
|
||||
}
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
SourceTracker::SourceEntry& SourceTracker::UpdateEntry(const SourceKey& key) {
|
||||
// We intentionally do |find() + emplace()|, instead of checking the return
|
||||
// value of |emplace()|, for performance reasons. It's much more likely for
|
||||
// the key to already exist than for it not to.
|
||||
auto map_it = map_.find(key);
|
||||
if (map_it == map_.end()) {
|
||||
// Insert a new entry at the front of the list.
|
||||
list_.emplace_front(key, SourceEntry());
|
||||
map_.emplace(key, list_.begin());
|
||||
} else if (map_it->second != list_.begin()) {
|
||||
// Move the old entry to the front of the list.
|
||||
list_.splice(list_.begin(), list_, map_it->second);
|
||||
}
|
||||
|
||||
return list_.front().second;
|
||||
}
|
||||
|
||||
void SourceTracker::PruneEntries(int64_t now_ms) const {
|
||||
int64_t prune_ms = now_ms - kTimeoutMs;
|
||||
|
||||
while (!list_.empty() && list_.back().second.timestamp_ms < prune_ms) {
|
||||
map_.erase(list_.back().first);
|
||||
list_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
125
modules/rtp_rtcp/source/source_tracker.h
Normal file
125
modules/rtp_rtcp/source/source_tracker.h
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_RTP_RTCP_SOURCE_SOURCE_TRACKER_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_SOURCE_TRACKER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/rtp_packet_infos.h"
|
||||
#include "api/rtp_receiver_interface.h"
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
//
|
||||
// Tracker for `RTCRtpContributingSource` and `RTCRtpSynchronizationSource`:
|
||||
// - https://w3c.github.io/webrtc-pc/#dom-rtcrtpcontributingsource
|
||||
// - https://w3c.github.io/webrtc-pc/#dom-rtcrtpsynchronizationsource
|
||||
//
|
||||
class SourceTracker {
|
||||
public:
|
||||
// Amount of time before the entry associated with an update is removed. See:
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getcontributingsources
|
||||
static constexpr int64_t kTimeoutMs = 10000; // 10 seconds
|
||||
|
||||
explicit SourceTracker(Clock* clock);
|
||||
|
||||
SourceTracker(const SourceTracker& other) = delete;
|
||||
SourceTracker(SourceTracker&& other) = delete;
|
||||
SourceTracker& operator=(const SourceTracker& other) = delete;
|
||||
SourceTracker& operator=(SourceTracker&& other) = delete;
|
||||
|
||||
// Updates the source entries when a frame is delivered to the
|
||||
// RTCRtpReceiver's MediaStreamTrack.
|
||||
void OnFrameDelivered(const RtpPacketInfos& packet_infos);
|
||||
|
||||
// Returns an |RtpSource| for each unique SSRC and CSRC identifier updated in
|
||||
// the last |kTimeoutMs| milliseconds. Entries appear in reverse chronological
|
||||
// order (i.e. with the most recently updated entries appearing first).
|
||||
std::vector<RtpSource> GetSources() const;
|
||||
|
||||
private:
|
||||
struct SourceKey {
|
||||
SourceKey(RtpSourceType source_type, uint32_t source)
|
||||
: source_type(source_type), source(source) {}
|
||||
|
||||
// Type of |source|.
|
||||
RtpSourceType source_type;
|
||||
|
||||
// CSRC or SSRC identifier of the contributing or synchronization source.
|
||||
uint32_t source;
|
||||
};
|
||||
|
||||
struct SourceKeyComparator {
|
||||
bool operator()(const SourceKey& lhs, const SourceKey& rhs) const {
|
||||
return (lhs.source_type == rhs.source_type) && (lhs.source == rhs.source);
|
||||
}
|
||||
};
|
||||
|
||||
struct SourceKeyHasher {
|
||||
size_t operator()(const SourceKey& value) const {
|
||||
return static_cast<size_t>(value.source_type) +
|
||||
static_cast<size_t>(value.source) * 11076425802534262905ULL;
|
||||
}
|
||||
};
|
||||
|
||||
struct SourceEntry {
|
||||
// Timestamp indicating the most recent time a frame from an RTP packet,
|
||||
// originating from this source, was delivered to the RTCRtpReceiver's
|
||||
// MediaStreamTrack. Its reference clock is the outer class's |clock_|.
|
||||
int64_t timestamp_ms;
|
||||
|
||||
// Audio level from an RFC 6464 or RFC 6465 header extension received with
|
||||
// the most recent packet used to assemble the frame associated with
|
||||
// |timestamp_ms|. May be absent. Only relevant for audio receivers. See the
|
||||
// specs for `RTCRtpContributingSource` for more info.
|
||||
absl::optional<uint8_t> audio_level;
|
||||
|
||||
// RTP timestamp of the most recent packet used to assemble the frame
|
||||
// associated with |timestamp_ms|.
|
||||
uint32_t rtp_timestamp;
|
||||
};
|
||||
|
||||
using SourceList = std::list<std::pair<const SourceKey, SourceEntry>>;
|
||||
using SourceMap = std::unordered_map<SourceKey,
|
||||
SourceList::iterator,
|
||||
SourceKeyHasher,
|
||||
SourceKeyComparator>;
|
||||
|
||||
// Updates an entry by creating it (if it didn't previously exist) and moving
|
||||
// it to the front of the list. Returns a reference to the entry.
|
||||
SourceEntry& UpdateEntry(const SourceKey& key)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
|
||||
// Removes entries that have timed out. Marked as "const" so that we can do
|
||||
// pruning in getters.
|
||||
void PruneEntries(int64_t now_ms) const RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
|
||||
Clock* const clock_;
|
||||
rtc::CriticalSection lock_;
|
||||
|
||||
// Entries are stored in reverse chronological order (i.e. with the most
|
||||
// recently updated entries appearing first). Mutability is needed for timeout
|
||||
// pruning in const functions.
|
||||
mutable SourceList list_ RTC_GUARDED_BY(lock_);
|
||||
mutable SourceMap map_ RTC_GUARDED_BY(lock_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_SOURCE_TRACKER_H_
|
336
modules/rtp_rtcp/source/source_tracker_unittest.cc
Normal file
336
modules/rtp_rtcp/source/source_tracker_unittest.cc
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* 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/rtp_rtcp/source/source_tracker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/rtp_headers.h"
|
||||
#include "api/rtp_packet_info.h"
|
||||
#include "api/rtp_packet_infos.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::testing::Combine;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::ElementsAreArray;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::TestWithParam;
|
||||
using ::testing::Values;
|
||||
|
||||
constexpr size_t kPacketInfosCountMax = 5;
|
||||
|
||||
// Simple "guaranteed to be correct" re-implementation of |SourceTracker| for
|
||||
// dual-implementation testing purposes.
|
||||
class ExpectedSourceTracker {
|
||||
public:
|
||||
explicit ExpectedSourceTracker(Clock* clock) : clock_(clock) {}
|
||||
|
||||
void OnFrameDelivered(const RtpPacketInfos& packet_infos) {
|
||||
const int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
for (const auto& packet_info : packet_infos) {
|
||||
for (const auto& csrc : packet_info.csrcs()) {
|
||||
entries_.emplace_front(now_ms, csrc, RtpSourceType::CSRC,
|
||||
packet_info.audio_level(),
|
||||
packet_info.rtp_timestamp());
|
||||
}
|
||||
|
||||
entries_.emplace_front(now_ms, packet_info.ssrc(), RtpSourceType::SSRC,
|
||||
packet_info.audio_level(),
|
||||
packet_info.rtp_timestamp());
|
||||
}
|
||||
|
||||
PruneEntries(now_ms);
|
||||
}
|
||||
|
||||
std::vector<RtpSource> GetSources() const {
|
||||
PruneEntries(clock_->TimeInMilliseconds());
|
||||
|
||||
return std::vector<RtpSource>(entries_.begin(), entries_.end());
|
||||
}
|
||||
|
||||
private:
|
||||
void PruneEntries(int64_t now_ms) const {
|
||||
const int64_t prune_ms = now_ms - 10000; // 10 seconds
|
||||
|
||||
std::set<std::pair<RtpSourceType, uint32_t>> seen;
|
||||
|
||||
auto it = entries_.begin();
|
||||
auto end = entries_.end();
|
||||
while (it != end) {
|
||||
auto next = it;
|
||||
++next;
|
||||
|
||||
auto key = std::make_pair(it->source_type(), it->source_id());
|
||||
if (!seen.insert(key).second || it->timestamp_ms() < prune_ms) {
|
||||
entries_.erase(it);
|
||||
}
|
||||
|
||||
it = next;
|
||||
}
|
||||
}
|
||||
|
||||
Clock* const clock_;
|
||||
|
||||
mutable std::list<RtpSource> entries_;
|
||||
};
|
||||
|
||||
class SourceTrackerRandomTest
|
||||
: public TestWithParam<std::tuple<uint32_t, uint32_t>> {
|
||||
protected:
|
||||
SourceTrackerRandomTest()
|
||||
: ssrcs_count_(std::get<0>(GetParam())),
|
||||
csrcs_count_(std::get<1>(GetParam())),
|
||||
generator_(42) {}
|
||||
|
||||
RtpPacketInfos GeneratePacketInfos() {
|
||||
size_t count = std::uniform_int_distribution<size_t>(
|
||||
1, kPacketInfosCountMax)(generator_);
|
||||
|
||||
RtpPacketInfos::vector_type packet_infos;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
packet_infos.emplace_back(GenerateSsrc(), GenerateCsrcs(),
|
||||
GenerateSequenceNumber(),
|
||||
GenerateRtpTimestamp(), GenerateAudioLevel(),
|
||||
GenerateReceiveTimeMs());
|
||||
}
|
||||
|
||||
return RtpPacketInfos(std::move(packet_infos));
|
||||
}
|
||||
|
||||
int64_t GenerateClockAdvanceTimeMilliseconds() {
|
||||
double roll = std::uniform_real_distribution<double>(0.0, 1.0)(generator_);
|
||||
|
||||
if (roll < 0.05) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (roll < 0.08) {
|
||||
return SourceTracker::kTimeoutMs - 1;
|
||||
}
|
||||
|
||||
if (roll < 0.11) {
|
||||
return SourceTracker::kTimeoutMs;
|
||||
}
|
||||
|
||||
if (roll < 0.19) {
|
||||
return std::uniform_int_distribution<int64_t>(
|
||||
SourceTracker::kTimeoutMs,
|
||||
SourceTracker::kTimeoutMs * 1000)(generator_);
|
||||
}
|
||||
|
||||
return std::uniform_int_distribution<int64_t>(
|
||||
1, SourceTracker::kTimeoutMs - 1)(generator_);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t GenerateSsrc() {
|
||||
return std::uniform_int_distribution<uint32_t>(1, ssrcs_count_)(generator_);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> GenerateCsrcs() {
|
||||
std::vector<uint32_t> csrcs;
|
||||
for (size_t i = 1; i <= csrcs_count_ && csrcs.size() < kRtpCsrcSize; ++i) {
|
||||
if (std::bernoulli_distribution(0.5)(generator_)) {
|
||||
csrcs.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
return csrcs;
|
||||
}
|
||||
|
||||
uint16_t GenerateSequenceNumber() {
|
||||
return std::uniform_int_distribution<uint16_t>()(generator_);
|
||||
}
|
||||
|
||||
uint32_t GenerateRtpTimestamp() {
|
||||
return std::uniform_int_distribution<uint32_t>()(generator_);
|
||||
}
|
||||
|
||||
absl::optional<uint8_t> GenerateAudioLevel() {
|
||||
if (std::bernoulli_distribution(0.25)(generator_)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// Workaround for std::uniform_int_distribution<uint8_t> not being allowed.
|
||||
return static_cast<uint8_t>(
|
||||
std::uniform_int_distribution<uint16_t>()(generator_));
|
||||
}
|
||||
|
||||
int64_t GenerateReceiveTimeMs() {
|
||||
return std::uniform_int_distribution<int64_t>()(generator_);
|
||||
}
|
||||
|
||||
const uint32_t ssrcs_count_;
|
||||
const uint32_t csrcs_count_;
|
||||
|
||||
std::mt19937 generator_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_P(SourceTrackerRandomTest, RandomOperations) {
|
||||
constexpr size_t kIterationsCount = 200;
|
||||
|
||||
SimulatedClock clock(1000000000000ULL);
|
||||
SourceTracker actual_tracker(&clock);
|
||||
ExpectedSourceTracker expected_tracker(&clock);
|
||||
|
||||
ASSERT_THAT(actual_tracker.GetSources(), IsEmpty());
|
||||
ASSERT_THAT(expected_tracker.GetSources(), IsEmpty());
|
||||
|
||||
for (size_t i = 0; i < kIterationsCount; ++i) {
|
||||
RtpPacketInfos packet_infos = GeneratePacketInfos();
|
||||
|
||||
actual_tracker.OnFrameDelivered(packet_infos);
|
||||
expected_tracker.OnFrameDelivered(packet_infos);
|
||||
|
||||
clock.AdvanceTimeMilliseconds(GenerateClockAdvanceTimeMilliseconds());
|
||||
|
||||
ASSERT_THAT(actual_tracker.GetSources(),
|
||||
ElementsAreArray(expected_tracker.GetSources()));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(,
|
||||
SourceTrackerRandomTest,
|
||||
Combine(/*ssrcs_count_=*/Values(1, 2, 4),
|
||||
/*csrcs_count_=*/Values(0, 1, 3, 7)));
|
||||
|
||||
TEST(SourceTrackerTest, StartEmpty) {
|
||||
SimulatedClock clock(1000000000000ULL);
|
||||
SourceTracker tracker(&clock);
|
||||
|
||||
EXPECT_THAT(tracker.GetSources(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST(SourceTrackerTest, OnFrameDeliveredRecordsSources) {
|
||||
constexpr uint32_t kSsrc = 10;
|
||||
constexpr uint32_t kCsrcs[] = {20, 21};
|
||||
constexpr uint16_t kSequenceNumber = 30;
|
||||
constexpr uint32_t kRtpTimestamp = 40;
|
||||
constexpr absl::optional<uint8_t> kAudioLevel = 50;
|
||||
constexpr int64_t kReceiveTimeMs = 60;
|
||||
|
||||
SimulatedClock clock(1000000000000ULL);
|
||||
SourceTracker tracker(&clock);
|
||||
|
||||
tracker.OnFrameDelivered(RtpPacketInfos(
|
||||
{RtpPacketInfo(kSsrc, {kCsrcs[0], kCsrcs[1]}, kSequenceNumber,
|
||||
kRtpTimestamp, kAudioLevel, kReceiveTimeMs)}));
|
||||
|
||||
int64_t timestamp_ms = clock.TimeInMilliseconds();
|
||||
|
||||
EXPECT_THAT(
|
||||
tracker.GetSources(),
|
||||
ElementsAre(RtpSource(timestamp_ms, kSsrc, RtpSourceType::SSRC,
|
||||
kAudioLevel, kRtpTimestamp),
|
||||
RtpSource(timestamp_ms, kCsrcs[1], RtpSourceType::CSRC,
|
||||
kAudioLevel, kRtpTimestamp),
|
||||
RtpSource(timestamp_ms, kCsrcs[0], RtpSourceType::CSRC,
|
||||
kAudioLevel, kRtpTimestamp)));
|
||||
}
|
||||
|
||||
TEST(SourceTrackerTest, OnFrameDeliveredUpdatesSources) {
|
||||
constexpr uint32_t kSsrc = 10;
|
||||
constexpr uint32_t kCsrcs0 = 20;
|
||||
constexpr uint32_t kCsrcs1 = 21;
|
||||
constexpr uint32_t kCsrcs2 = 22;
|
||||
constexpr uint16_t kSequenceNumber0 = 30;
|
||||
constexpr uint16_t kSequenceNumber1 = 31;
|
||||
constexpr uint32_t kRtpTimestamp0 = 40;
|
||||
constexpr uint32_t kRtpTimestamp1 = 41;
|
||||
constexpr absl::optional<uint8_t> kAudioLevel0 = 50;
|
||||
constexpr absl::optional<uint8_t> kAudioLevel1 = absl::nullopt;
|
||||
constexpr int64_t kReceiveTimeMs0 = 60;
|
||||
constexpr int64_t kReceiveTimeMs1 = 61;
|
||||
|
||||
SimulatedClock clock(1000000000000ULL);
|
||||
SourceTracker tracker(&clock);
|
||||
|
||||
tracker.OnFrameDelivered(RtpPacketInfos(
|
||||
{RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs1}, kSequenceNumber0,
|
||||
kRtpTimestamp0, kAudioLevel0, kReceiveTimeMs0)}));
|
||||
|
||||
int64_t timestamp_ms_0 = clock.TimeInMilliseconds();
|
||||
|
||||
clock.AdvanceTimeMilliseconds(17);
|
||||
|
||||
tracker.OnFrameDelivered(RtpPacketInfos(
|
||||
{RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs2}, kSequenceNumber1,
|
||||
kRtpTimestamp1, kAudioLevel1, kReceiveTimeMs1)}));
|
||||
|
||||
int64_t timestamp_ms_1 = clock.TimeInMilliseconds();
|
||||
|
||||
EXPECT_THAT(
|
||||
tracker.GetSources(),
|
||||
ElementsAre(RtpSource(timestamp_ms_1, kSsrc, RtpSourceType::SSRC,
|
||||
kAudioLevel1, kRtpTimestamp1),
|
||||
RtpSource(timestamp_ms_1, kCsrcs2, RtpSourceType::CSRC,
|
||||
kAudioLevel1, kRtpTimestamp1),
|
||||
RtpSource(timestamp_ms_1, kCsrcs0, RtpSourceType::CSRC,
|
||||
kAudioLevel1, kRtpTimestamp1),
|
||||
RtpSource(timestamp_ms_0, kCsrcs1, RtpSourceType::CSRC,
|
||||
kAudioLevel0, kRtpTimestamp0)));
|
||||
}
|
||||
|
||||
TEST(SourceTrackerTest, TimedOutSourcesAreRemoved) {
|
||||
constexpr uint32_t kSsrc = 10;
|
||||
constexpr uint32_t kCsrcs0 = 20;
|
||||
constexpr uint32_t kCsrcs1 = 21;
|
||||
constexpr uint32_t kCsrcs2 = 22;
|
||||
constexpr uint16_t kSequenceNumber0 = 30;
|
||||
constexpr uint16_t kSequenceNumber1 = 31;
|
||||
constexpr uint32_t kRtpTimestamp0 = 40;
|
||||
constexpr uint32_t kRtpTimestamp1 = 41;
|
||||
constexpr absl::optional<uint8_t> kAudioLevel0 = 50;
|
||||
constexpr absl::optional<uint8_t> kAudioLevel1 = absl::nullopt;
|
||||
constexpr int64_t kReceiveTimeMs0 = 60;
|
||||
constexpr int64_t kReceiveTimeMs1 = 61;
|
||||
|
||||
SimulatedClock clock(1000000000000ULL);
|
||||
SourceTracker tracker(&clock);
|
||||
|
||||
tracker.OnFrameDelivered(RtpPacketInfos(
|
||||
{RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs1}, kSequenceNumber0,
|
||||
kRtpTimestamp0, kAudioLevel0, kReceiveTimeMs0)}));
|
||||
|
||||
clock.AdvanceTimeMilliseconds(17);
|
||||
|
||||
tracker.OnFrameDelivered(RtpPacketInfos(
|
||||
{RtpPacketInfo(kSsrc, {kCsrcs0, kCsrcs2}, kSequenceNumber1,
|
||||
kRtpTimestamp1, kAudioLevel1, kReceiveTimeMs1)}));
|
||||
|
||||
int64_t timestamp_ms_1 = clock.TimeInMilliseconds();
|
||||
|
||||
clock.AdvanceTimeMilliseconds(SourceTracker::kTimeoutMs);
|
||||
|
||||
EXPECT_THAT(
|
||||
tracker.GetSources(),
|
||||
ElementsAre(RtpSource(timestamp_ms_1, kSsrc, RtpSourceType::SSRC,
|
||||
kAudioLevel1, kRtpTimestamp1),
|
||||
RtpSource(timestamp_ms_1, kCsrcs2, RtpSourceType::CSRC,
|
||||
kAudioLevel1, kRtpTimestamp1),
|
||||
RtpSource(timestamp_ms_1, kCsrcs0, RtpSourceType::CSRC,
|
||||
kAudioLevel1, kRtpTimestamp1)));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
Reference in New Issue
Block a user