Add RTP timestamp to contributing sources

RTP timestamp was recently added to contributing sources in the WebRTC
specification. This CL implements that change in WebRTC.

Bug: webrtc:10650
Change-Id: Ic0ccfbea7049a5b66063fa6cf60d01d5bd713132
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/137515
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28020}
This commit is contained in:
Johannes Kron 2019-05-21 13:19:22 +02:00 committed by Commit Bot
parent afb8d5cdae
commit b5d918324c
7 changed files with 125 additions and 61 deletions

View File

@ -12,12 +12,24 @@
namespace webrtc {
RtpSource::RtpSource(int64_t timestamp_ms,
uint32_t source_id,
RtpSourceType source_type,
absl::optional<uint8_t> audio_level,
uint32_t rtp_timestamp)
: timestamp_ms_(timestamp_ms),
source_id_(source_id),
source_type_(source_type),
audio_level_(audio_level),
rtp_timestamp_(rtp_timestamp) {}
RtpSource::RtpSource(int64_t timestamp_ms,
uint32_t source_id,
RtpSourceType source_type)
: timestamp_ms_(timestamp_ms),
source_id_(source_id),
source_type_(source_type) {}
source_type_(source_type),
rtp_timestamp_(0) {}
RtpSource::RtpSource(int64_t timestamp_ms,
uint32_t source_id,
@ -26,7 +38,8 @@ RtpSource::RtpSource(int64_t timestamp_ms,
: timestamp_ms_(timestamp_ms),
source_id_(source_id),
source_type_(source_type),
audio_level_(audio_level) {}
audio_level_(audio_level),
rtp_timestamp_(0) {}
RtpSource::RtpSource(const RtpSource&) = default;
RtpSource& RtpSource::operator=(const RtpSource&) = default;

View File

@ -24,6 +24,7 @@
#include "api/proxy.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "rtc_base/deprecation.h"
#include "rtc_base/ref_count.h"
namespace webrtc {
@ -36,13 +37,23 @@ enum class RtpSourceType {
class RtpSource {
public:
RtpSource() = delete;
RtpSource(int64_t timestamp_ms,
uint32_t source_id,
RtpSourceType source_type);
RtpSource(int64_t timestamp_ms,
uint32_t source_id,
RtpSourceType source_type,
uint8_t audio_level);
absl::optional<uint8_t> audio_level,
uint32_t rtp_timestamp);
// DEPRECATED: Will be removed after 2019-07-31.
RTC_DEPRECATED RtpSource(int64_t timestamp_ms,
uint32_t source_id,
RtpSourceType source_type);
// DEPRECATED: Will be removed after 2019-07-31.
RTC_DEPRECATED RtpSource(int64_t timestamp_ms,
uint32_t source_id,
RtpSourceType source_type,
uint8_t audio_level);
RtpSource(const RtpSource&);
RtpSource& operator=(const RtpSource&);
~RtpSource();
@ -64,9 +75,12 @@ class RtpSource {
audio_level_ = level;
}
uint32_t rtp_timestamp() const { return rtp_timestamp_; }
bool operator==(const RtpSource& o) const {
return timestamp_ms_ == o.timestamp_ms() && source_id_ == o.source_id() &&
source_type_ == o.source_type() && audio_level_ == o.audio_level_;
source_type_ == o.source_type() && audio_level_ == o.audio_level_ &&
rtp_timestamp_ == o.rtp_timestamp();
}
private:
@ -74,6 +88,7 @@ class RtpSource {
uint32_t source_id_;
RtpSourceType source_type_;
absl::optional<uint8_t> audio_level_;
uint32_t rtp_timestamp_;
};
class RtpReceiverObserverInterface {

View File

@ -564,9 +564,10 @@ std::vector<webrtc::RtpSource> ChannelReceive::GetSources() const {
sources = contributing_sources_.GetSources(now_ms);
if (last_received_rtp_system_time_ms_ >=
now_ms - ContributingSources::kHistoryMs) {
RTC_DCHECK(last_received_rtp_timestamp_.has_value());
sources.emplace_back(*last_received_rtp_system_time_ms_, remote_ssrc_,
RtpSourceType::SSRC);
sources.back().set_audio_level(last_received_rtp_audio_level_);
RtpSourceType::SSRC, last_received_rtp_audio_level_,
*last_received_rtp_timestamp_);
}
}
return sources;
@ -599,7 +600,8 @@ void ChannelReceive::OnRtpPacket(const RtpPacketReceived& packet) {
std::vector<uint32_t> csrcs = packet.Csrcs();
contributing_sources_.Update(
now_ms, csrcs,
has_audio_level ? absl::optional<uint8_t>(audio_level) : absl::nullopt);
has_audio_level ? absl::optional<uint8_t>(audio_level) : absl::nullopt,
packet.Timestamp());
}
// Store playout timestamp for the received RTP packet

View File

@ -26,8 +26,9 @@ ContributingSources::~ContributingSources() = default;
void ContributingSources::Update(int64_t now_ms,
rtc::ArrayView<const uint32_t> csrcs,
absl::optional<uint8_t> audio_level) {
Entry entry = { now_ms, audio_level };
absl::optional<uint8_t> audio_level,
uint32_t rtp_timestamp) {
Entry entry = {now_ms, audio_level, rtp_timestamp};
for (uint32_t csrc : csrcs) {
active_csrcs_[csrc] = entry;
}
@ -47,14 +48,9 @@ std::vector<RtpSource> ContributingSources::GetSources(int64_t now_ms) const {
std::vector<RtpSource> sources;
for (auto& record : active_csrcs_) {
if (record.second.last_seen_ms >= now_ms - kHistoryMs) {
if (record.second.audio_level.has_value()) {
sources.emplace_back(record.second.last_seen_ms, record.first,
RtpSourceType::CSRC,
*record.second.audio_level);
} else {
sources.emplace_back(record.second.last_seen_ms, record.first,
RtpSourceType::CSRC);
}
sources.emplace_back(record.second.last_seen_ms, record.first,
RtpSourceType::CSRC, record.second.audio_level,
record.second.rtp_timestamp);
}
}
@ -76,7 +72,10 @@ void ContributingSources::DeleteOldEntries(int64_t now_ms) {
ContributingSources::Entry::Entry() = default;
ContributingSources::Entry::Entry(int64_t timestamp_ms,
absl::optional<uint8_t> audio_level_arg)
: last_seen_ms(timestamp_ms), audio_level(audio_level_arg) {}
absl::optional<uint8_t> audio_level_arg,
uint32_t rtp_timestamp)
: last_seen_ms(timestamp_ms),
audio_level(audio_level_arg),
rtp_timestamp(rtp_timestamp) {}
} // namespace webrtc

View File

@ -32,8 +32,10 @@ class ContributingSources {
ContributingSources();
~ContributingSources();
void Update(int64_t now_ms, rtc::ArrayView<const uint32_t> csrcs,
absl::optional<uint8_t> audio_level);
void Update(int64_t now_ms,
rtc::ArrayView<const uint32_t> csrcs,
absl::optional<uint8_t> audio_level,
uint32_t rtp_timestamp);
// Returns contributing sources seen the last 10 s.
std::vector<RtpSource> GetSources(int64_t now_ms) const;
@ -41,10 +43,13 @@ class ContributingSources {
private:
struct Entry {
Entry();
Entry(int64_t timestamp_ms, absl::optional<uint8_t> audio_level);
Entry(int64_t timestamp_ms,
absl::optional<uint8_t> audio_level,
uint32_t rtp_timestamp);
int64_t last_seen_ms;
absl::optional<uint8_t> audio_level;
uint32_t rtp_timestamp;
};
void DeleteOldEntries(int64_t now_ms);

View File

@ -23,6 +23,9 @@ using ::testing::UnorderedElementsAre;
constexpr uint32_t kCsrc1 = 111;
constexpr uint32_t kCsrc2 = 222;
constexpr uint32_t kCsrc3 = 333;
constexpr uint32_t kRtpTimestamp1 = 314;
constexpr uint32_t kRtpTimestamp2 = 315;
constexpr uint32_t kRtpTimestamp3 = 316;
} // namespace
@ -30,11 +33,13 @@ TEST(ContributingSourcesTest, RecordSources) {
ContributingSources csrcs;
constexpr uint32_t kCsrcs[] = {kCsrc1, kCsrc2};
constexpr int64_t kTime1 = 10;
csrcs.Update(kTime1, kCsrcs, absl::nullopt);
csrcs.Update(kTime1, kCsrcs, absl::nullopt, kRtpTimestamp1);
EXPECT_THAT(
csrcs.GetSources(kTime1),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1)));
}
TEST(ContributingSourcesTest, UpdateSources) {
@ -45,17 +50,22 @@ TEST(ContributingSourcesTest, UpdateSources) {
constexpr uint32_t kCsrcs2[] = {kCsrc2, kCsrc3};
constexpr int64_t kTime1 = 10;
constexpr int64_t kTime2 = kTime1 + 5 * rtc::kNumMillisecsPerSec;
csrcs.Update(kTime1, kCsrcs1, absl::nullopt);
csrcs.Update(kTime1, kCsrcs1, absl::nullopt, kRtpTimestamp1);
EXPECT_THAT(
csrcs.GetSources(kTime1),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
csrcs.Update(kTime2, kCsrcs2, absl::nullopt);
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1)));
csrcs.Update(kTime2, kCsrcs2, absl::nullopt, kRtpTimestamp2);
EXPECT_THAT(
csrcs.GetSources(kTime2),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC),
RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC)));
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1),
RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp2),
RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp2)));
}
TEST(ContributingSourcesTest, ReturnRecentOnly) {
@ -65,16 +75,20 @@ TEST(ContributingSourcesTest, ReturnRecentOnly) {
constexpr int64_t kTime1 = 10;
constexpr int64_t kTime2 = kTime1 + 5 * rtc::kNumMillisecsPerSec;
constexpr int64_t kTime3 = kTime1 + 12 * rtc::kNumMillisecsPerSec;
csrcs.Update(kTime1, kCsrcs1, absl::nullopt);
csrcs.Update(kTime1, kCsrcs1, absl::nullopt, kRtpTimestamp1);
EXPECT_THAT(
csrcs.GetSources(kTime1),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
csrcs.Update(kTime2, kCsrcs2, absl::nullopt);
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1)));
csrcs.Update(kTime2, kCsrcs2, absl::nullopt, kRtpTimestamp2);
EXPECT_THAT(
csrcs.GetSources(kTime3),
UnorderedElementsAre(RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC),
RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC)));
UnorderedElementsAre(RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp2),
RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp2)));
}
TEST(ContributingSourcesTest, PurgeOldSources) {
@ -84,46 +98,58 @@ TEST(ContributingSourcesTest, PurgeOldSources) {
constexpr int64_t kTime1 = 10;
constexpr int64_t kTime2 = kTime1 + 10 * rtc::kNumMillisecsPerSec;
constexpr int64_t kTime3 = kTime1 + 20 * rtc::kNumMillisecsPerSec;
csrcs.Update(kTime1, kCsrcs1, absl::nullopt);
csrcs.Update(kTime1, kCsrcs1, absl::nullopt, kRtpTimestamp1);
EXPECT_THAT(
csrcs.GetSources(kTime2),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC)));
csrcs.Update(kTime2, kCsrcs2, absl::nullopt);
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1)));
csrcs.Update(kTime2, kCsrcs2, absl::nullopt, kRtpTimestamp2);
EXPECT_THAT(
csrcs.GetSources(kTime2),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC),
RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC)));
csrcs.Update(kTime3, kCsrcs2, absl::nullopt);
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp1),
RtpSource(kTime2, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp2),
RtpSource(kTime2, kCsrc3, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp2)));
csrcs.Update(kTime3, kCsrcs2, absl::nullopt, kRtpTimestamp3);
EXPECT_THAT(
csrcs.GetSources(kTime3),
UnorderedElementsAre(RtpSource(kTime3, kCsrc2, RtpSourceType::CSRC),
RtpSource(kTime3, kCsrc3, RtpSourceType::CSRC)));
UnorderedElementsAre(RtpSource(kTime3, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp3),
RtpSource(kTime3, kCsrc3, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp3)));
// Query at an earlier time; check that old sources really have been purged
// and don't reappear.
EXPECT_THAT(
csrcs.GetSources(kTime2),
UnorderedElementsAre(RtpSource(kTime3, kCsrc2, RtpSourceType::CSRC),
RtpSource(kTime3, kCsrc3, RtpSourceType::CSRC)));
UnorderedElementsAre(RtpSource(kTime3, kCsrc2, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp3),
RtpSource(kTime3, kCsrc3, RtpSourceType::CSRC,
absl::nullopt, kRtpTimestamp3)));
}
TEST(ContributingSourcesTest, AudioLevel) {
ContributingSources csrcs;
constexpr uint32_t kCsrcs[] = {kCsrc1, kCsrc2};
constexpr int64_t kTime1 = 10;
csrcs.Update(kTime1, kCsrcs, 47);
csrcs.Update(kTime1, kCsrcs, 47, kRtpTimestamp1);
EXPECT_THAT(
csrcs.GetSources(kTime1),
UnorderedElementsAre(RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC, 47),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC, 47)));
UnorderedElementsAre(
RtpSource(kTime1, kCsrc1, RtpSourceType::CSRC, 47, kRtpTimestamp1),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC, 47, kRtpTimestamp1)));
constexpr uint32_t kCsrcsSubset[] = {kCsrc1};
csrcs.Update(kTime1 + 1, kCsrcsSubset, absl::nullopt);
csrcs.Update(kTime1 + 1, kCsrcsSubset, absl::nullopt, kRtpTimestamp2);
EXPECT_THAT(
csrcs.GetSources(kTime1 + 1),
UnorderedElementsAre(RtpSource(kTime1 + 1, kCsrc1, RtpSourceType::CSRC),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC, 47)));
UnorderedElementsAre(
RtpSource(kTime1 + 1, kCsrc1, RtpSourceType::CSRC, absl::nullopt,
kRtpTimestamp2),
RtpSource(kTime1, kCsrc2, RtpSourceType::CSRC, 47, kRtpTimestamp1)));
}
} // namespace webrtc

View File

@ -326,7 +326,8 @@ void RtpVideoStreamReceiver::OnRtpPacket(const RtpPacketReceived& packet) {
std::vector<uint32_t> csrcs = packet.Csrcs();
contributing_sources_.Update(now_ms, csrcs,
/* audio level */ absl::nullopt);
/* audio level */ absl::nullopt,
packet.Timestamp());
}
// Periodically log the RTP header of incoming packets.
if (now_ms - last_packet_log_ms_ > kPacketLogIntervalMs) {
@ -781,8 +782,11 @@ std::vector<webrtc::RtpSource> RtpVideoStreamReceiver::GetSources() const {
sources = contributing_sources_.GetSources(now_ms);
if (last_received_rtp_system_time_ms_ >=
now_ms - ContributingSources::kHistoryMs) {
RTC_DCHECK(last_received_rtp_timestamp_.has_value());
sources.emplace_back(*last_received_rtp_system_time_ms_,
config_.rtp.remote_ssrc, RtpSourceType::SSRC);
config_.rtp.remote_ssrc, RtpSourceType::SSRC,
/* audio_level */ absl::nullopt,
*last_received_rtp_timestamp_);
}
}
return sources;