Add estimatedPlayoutTimestamp to RTCInboundRTPStreamStats.
https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-estimatedplayouttimestamp
Partial implementation: currently only populated when a/v sync is enabled.
Bug: webrtc:7065
Change-Id: I8595cc848d080d7c3bef152462a9becf0e5a2196
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/155621
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29581}
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 190693c..c6291c7 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -206,6 +206,9 @@
stats.audio_level = channel_receive_->GetSpeechOutputLevelFullRange();
stats.total_output_energy = channel_receive_->GetTotalOutputEnergy();
stats.total_output_duration = channel_receive_->GetTotalOutputDuration();
+ stats.estimated_playout_ntp_timestamp_ms =
+ channel_receive_->GetCurrentEstimatedPlayoutNtpTimestampMs(
+ rtc::TimeMillis());
// Get jitter buffer and total delay (alg + jitter + playout) stats.
auto ns = channel_receive_->GetNetworkStatistics();
@@ -310,9 +313,18 @@
return info;
}
-uint32_t AudioReceiveStream::GetPlayoutTimestamp() const {
+bool AudioReceiveStream::GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
+ int64_t* time_ms) const {
// Called on video capture thread.
- return channel_receive_->GetPlayoutTimestamp();
+ return channel_receive_->GetPlayoutRtpTimestamp(rtp_timestamp, time_ms);
+}
+
+void AudioReceiveStream::SetEstimatedPlayoutNtpTimestampMs(
+ int64_t ntp_timestamp_ms,
+ int64_t time_ms) {
+ // Called on video capture thread.
+ channel_receive_->SetEstimatedPlayoutNtpTimestampMs(ntp_timestamp_ms,
+ time_ms);
}
void AudioReceiveStream::SetMinimumPlayoutDelay(int delay_ms) {
diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h
index 86301a3..26bcf63 100644
--- a/audio/audio_receive_stream.h
+++ b/audio/audio_receive_stream.h
@@ -87,7 +87,10 @@
// Syncable
int id() const override;
absl::optional<Syncable::Info> GetInfo() const override;
- uint32_t GetPlayoutTimestamp() const override;
+ bool GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
+ int64_t* time_ms) const override;
+ void SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms,
+ int64_t time_ms) override;
void SetMinimumPlayoutDelay(int delay_ms) override;
void AssociateSendStream(AudioSendStream* send_stream);
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index ae6605c..473b387 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -62,6 +62,7 @@
const unsigned int kSpeechOutputLevel = 99;
const double kTotalOutputEnergy = 0.25;
const double kTotalOutputDuration = 0.5;
+const int64_t kPlayoutNtpTimestampMs = 5678;
const CallReceiveStatistics kCallStats = {678, 234, -12, 567, 78, 890, 123};
const std::pair<int, SdpAudioFormat> kReceiveCodec = {
@@ -145,6 +146,8 @@
.WillOnce(Return(kAudioDecodeStats));
EXPECT_CALL(*channel_receive_, GetReceiveCodec())
.WillOnce(Return(kReceiveCodec));
+ EXPECT_CALL(*channel_receive_, GetCurrentEstimatedPlayoutNtpTimestampMs(_))
+ .WillOnce(Return(kPlayoutNtpTimestampMs));
}
private:
@@ -315,6 +318,7 @@
stats.decoding_muted_output);
EXPECT_EQ(kCallStats.capture_start_ntp_time_ms_,
stats.capture_start_ntp_time_ms);
+ EXPECT_EQ(kPlayoutNtpTimestampMs, stats.estimated_playout_ntp_timestamp_ms);
}
TEST(AudioReceiveStreamTest, SetGain) {
diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc
index fa1463a..7fe41a1 100644
--- a/audio/channel_receive.cc
+++ b/audio/channel_receive.cc
@@ -141,7 +141,12 @@
// Audio+Video Sync.
uint32_t GetDelayEstimate() const override;
void SetMinimumPlayoutDelay(int delayMs) override;
- uint32_t GetPlayoutTimestamp() const override;
+ bool GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
+ int64_t* time_ms) const override;
+ void SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms,
+ int64_t time_ms) override;
+ absl::optional<int64_t> GetCurrentEstimatedPlayoutNtpTimestampMs(
+ int64_t now_ms) const override;
// Audio quality.
bool SetBaseMinimumPlayoutDelayMs(int delay_ms) override;
@@ -178,7 +183,7 @@
size_t packet_length,
const RTPHeader& header);
int ResendPackets(const uint16_t* sequence_numbers, int length);
- void UpdatePlayoutTimestamp(bool rtcp);
+ void UpdatePlayoutTimestamp(bool rtcp, int64_t now_ms);
int GetRtpTimestampRateHz() const;
int64_t GetRTT() const;
@@ -242,7 +247,13 @@
rtc::CriticalSection video_sync_lock_;
uint32_t playout_timestamp_rtp_ RTC_GUARDED_BY(video_sync_lock_);
+ absl::optional<int64_t> playout_timestamp_rtp_time_ms_
+ RTC_GUARDED_BY(video_sync_lock_);
uint32_t playout_delay_ms_ RTC_GUARDED_BY(video_sync_lock_);
+ absl::optional<int64_t> playout_timestamp_ntp_
+ RTC_GUARDED_BY(video_sync_lock_);
+ absl::optional<int64_t> playout_timestamp_ntp_time_ms_
+ RTC_GUARDED_BY(video_sync_lock_);
rtc::CriticalSection ts_stats_lock_;
@@ -573,7 +584,7 @@
}
// Store playout timestamp for the received RTP packet
- UpdatePlayoutTimestamp(false);
+ UpdatePlayoutTimestamp(false, now_ms);
const auto& it = payload_type_frequencies_.find(packet.PayloadType());
if (it == payload_type_frequencies_.end())
@@ -638,7 +649,7 @@
// May be called on either worker thread or network thread.
void ChannelReceive::ReceivedRTCPPacket(const uint8_t* data, size_t length) {
// Store playout timestamp for the received RTCP packet
- UpdatePlayoutTimestamp(true);
+ UpdatePlayoutTimestamp(true, rtc::TimeMillis());
// Deliver RTCP packet to RTP/RTCP module for parsing
_rtpRtcpModule->IncomingRtcpPacket(data, length);
@@ -806,14 +817,38 @@
}
}
-uint32_t ChannelReceive::GetPlayoutTimestamp() const {
+bool ChannelReceive::GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
+ int64_t* time_ms) const {
RTC_DCHECK_RUNS_SERIALIZED(&video_capture_thread_race_checker_);
{
rtc::CritScope lock(&video_sync_lock_);
- return playout_timestamp_rtp_;
+ if (!playout_timestamp_rtp_time_ms_)
+ return false;
+ *rtp_timestamp = playout_timestamp_rtp_;
+ *time_ms = playout_timestamp_rtp_time_ms_.value();
+ return true;
}
}
+void ChannelReceive::SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms,
+ int64_t time_ms) {
+ RTC_DCHECK_RUNS_SERIALIZED(&video_capture_thread_race_checker_);
+ rtc::CritScope lock(&video_sync_lock_);
+ playout_timestamp_ntp_ = ntp_timestamp_ms;
+ playout_timestamp_ntp_time_ms_ = time_ms;
+}
+
+absl::optional<int64_t>
+ChannelReceive::GetCurrentEstimatedPlayoutNtpTimestampMs(int64_t now_ms) const {
+ RTC_DCHECK(worker_thread_checker_.IsCurrent());
+ rtc::CritScope lock(&video_sync_lock_);
+ if (!playout_timestamp_ntp_ || !playout_timestamp_ntp_time_ms_)
+ return absl::nullopt;
+
+ int64_t elapsed_ms = now_ms - *playout_timestamp_ntp_time_ms_;
+ return *playout_timestamp_ntp_ + elapsed_ms;
+}
+
bool ChannelReceive::SetBaseMinimumPlayoutDelayMs(int delay_ms) {
return acm_receiver_.SetBaseMinimumDelayMs(delay_ms);
}
@@ -841,7 +876,7 @@
return info;
}
-void ChannelReceive::UpdatePlayoutTimestamp(bool rtcp) {
+void ChannelReceive::UpdatePlayoutTimestamp(bool rtcp, int64_t now_ms) {
jitter_buffer_playout_timestamp_ = acm_receiver_.GetPlayoutTimestamp();
if (!jitter_buffer_playout_timestamp_) {
@@ -868,6 +903,7 @@
rtc::CritScope lock(&video_sync_lock_);
if (!rtcp) {
playout_timestamp_rtp_ = playout_timestamp;
+ playout_timestamp_rtp_time_ms_ = now_ms;
}
playout_delay_ms_ = delay_ms;
}
diff --git a/audio/channel_receive.h b/audio/channel_receive.h
index 5f71ea3..fb79dc2 100644
--- a/audio/channel_receive.h
+++ b/audio/channel_receive.h
@@ -105,7 +105,12 @@
// Audio+Video Sync.
virtual uint32_t GetDelayEstimate() const = 0;
virtual void SetMinimumPlayoutDelay(int delay_ms) = 0;
- virtual uint32_t GetPlayoutTimestamp() const = 0;
+ virtual bool GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp,
+ int64_t* time_ms) const = 0;
+ virtual void SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms,
+ int64_t time_ms) = 0;
+ virtual absl::optional<int64_t> GetCurrentEstimatedPlayoutNtpTimestampMs(
+ int64_t now_ms) const = 0;
// Audio quality.
// Base minimum delay sets lower bound on minimum delay value which
diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h
index e666bf2..d61bc89 100644
--- a/audio/mock_voe_channel_proxy.h
+++ b/audio/mock_voe_channel_proxy.h
@@ -49,7 +49,12 @@
MOCK_CONST_METHOD0(PreferredSampleRate, int());
MOCK_METHOD1(SetAssociatedSendChannel,
void(const voe::ChannelSendInterface* send_channel));
- MOCK_CONST_METHOD0(GetPlayoutTimestamp, uint32_t());
+ MOCK_CONST_METHOD2(GetPlayoutRtpTimestamp,
+ bool(uint32_t* rtp_timestamp, int64_t* time_ms));
+ MOCK_METHOD2(SetEstimatedPlayoutNtpTimestampMs,
+ void(int64_t ntp_timestamp_ms, int64_t time_ms));
+ MOCK_CONST_METHOD1(GetCurrentEstimatedPlayoutNtpTimestampMs,
+ absl::optional<int64_t>(int64_t now_ms));
MOCK_CONST_METHOD0(GetSyncInfo, absl::optional<Syncable::Info>());
MOCK_METHOD1(SetMinimumPlayoutDelay, void(int delay_ms));
MOCK_METHOD1(SetBaseMinimumPlayoutDelayMs, bool(int delay_ms));