зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1616875: Use timestamps for SSRC/CSRC statistics that are consistent with JS timestamps. r=ng
This means we won't have to re-implement all of our JSImpl workarounds in c++. Also, rename a variable in a way that made it easier to read this code. Differential Revision: https://phabricator.services.mozilla.com/D50395 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f995b16e1b
Коммит
2c26ca9900
|
@ -1,4 +1,5 @@
|
||||||
#include <RtpSourceObserver.h>
|
#include <RtpSourceObserver.h>
|
||||||
|
#include "RTCStatsReport.h"
|
||||||
#include "webrtc/modules/include/module_common_types.h"
|
#include "webrtc/modules/include/module_common_types.h"
|
||||||
#define GTEST_HAS_RTTI 0
|
#define GTEST_HAS_RTTI 0
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
@ -19,7 +20,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
|
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
|
||||||
const auto& e = history.mLatestEviction;
|
const auto& e = history.mLatestEviction;
|
||||||
EXPECT_FALSE(history.mHasEvictedEntry);
|
EXPECT_FALSE(history.mHasEvictedEntry);
|
||||||
EXPECT_EQ(e.jitterAdjustedTimestamp, 0);
|
EXPECT_EQ(e.predictedPlayoutTime, 0);
|
||||||
EXPECT_FALSE(e.hasAudioLevel);
|
EXPECT_FALSE(e.hasAudioLevel);
|
||||||
EXPECT_EQ(e.audioLevel, 0);
|
EXPECT_EQ(e.audioLevel, 0);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +41,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
auto entry = history.FindClosestNotAfter(i + jitter);
|
auto entry = history.FindClosestNotAfter(i + jitter);
|
||||||
ASSERT_NE(entry, nullptr);
|
ASSERT_NE(entry, nullptr);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
EXPECT_EQ(entry->jitterAdjustedTimestamp, i + jitter);
|
EXPECT_EQ(entry->predictedPlayoutTime, i + jitter);
|
||||||
EXPECT_EQ(entry->hasAudioLevel, hasAudioLevel);
|
EXPECT_EQ(entry->hasAudioLevel, hasAudioLevel);
|
||||||
EXPECT_EQ(entry->audioLevel, audioLevel);
|
EXPECT_EQ(entry->audioLevel, audioLevel);
|
||||||
}
|
}
|
||||||
|
@ -74,25 +75,25 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
history.Insert(timeNow, time1, 1, true, 0);
|
history.Insert(timeNow, time1, 1, true, 0);
|
||||||
// Check that the jitter window buffer hasn't been used
|
// Check that the jitter window buffer hasn't been used
|
||||||
EXPECT_TRUE(history.Empty());
|
EXPECT_TRUE(history.Empty());
|
||||||
ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time1);
|
ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time1);
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
|
|
||||||
// time2
|
// time2
|
||||||
history.Insert(timeNow, time2, 2, true, 0);
|
history.Insert(timeNow, time2, 2, true, 0);
|
||||||
EXPECT_TRUE(history.Empty());
|
EXPECT_TRUE(history.Empty());
|
||||||
ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time2);
|
ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time2);
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
|
|
||||||
// time3
|
// time3
|
||||||
history.Insert(timeNow, time3, 3, true, 0);
|
history.Insert(timeNow, time3, 3, true, 0);
|
||||||
EXPECT_TRUE(history.Empty());
|
EXPECT_TRUE(history.Empty());
|
||||||
ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time2);
|
ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time2);
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
|
|
||||||
// pruneTime0
|
// pruneTime0
|
||||||
history.Prune(pruneTime0);
|
history.Prune(pruneTime0);
|
||||||
EXPECT_TRUE(history.Empty());
|
EXPECT_TRUE(history.Empty());
|
||||||
ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time2);
|
ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time2);
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
|
|
||||||
// pruneTime1
|
// pruneTime1
|
||||||
|
@ -134,7 +135,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
|
ASSERT_EQ(history.mMaxJitterWindow, jitterWindow);
|
||||||
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
|
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(1));
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
ASSERT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time0);
|
ASSERT_EQ(history.mLatestEviction.predictedPlayoutTime, time0);
|
||||||
ASSERT_EQ(history.mLatestEviction.hasAudioLevel, false);
|
ASSERT_EQ(history.mLatestEviction.hasAudioLevel, false);
|
||||||
ASSERT_EQ(history.mLatestEviction.audioLevel, 1);
|
ASSERT_EQ(history.mLatestEviction.audioLevel, 1);
|
||||||
|
|
||||||
|
@ -143,7 +144,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
EXPECT_EQ(history.mMaxJitterWindow, jitterWindow);
|
EXPECT_EQ(history.mMaxJitterWindow, jitterWindow);
|
||||||
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
|
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
EXPECT_EQ(history.mLatestEviction.jitterAdjustedTimestamp, time1);
|
EXPECT_EQ(history.mLatestEviction.predictedPlayoutTime, time1);
|
||||||
EXPECT_EQ(history.mLatestEviction.hasAudioLevel, true);
|
EXPECT_EQ(history.mLatestEviction.hasAudioLevel, true);
|
||||||
EXPECT_EQ(history.mLatestEviction.audioLevel, 2);
|
EXPECT_EQ(history.mLatestEviction.audioLevel, 2);
|
||||||
}
|
}
|
||||||
|
@ -217,7 +218,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
history.Prune(timeNow + (jitter * 3) + 1);
|
history.Prune(timeNow + (jitter * 3) + 1);
|
||||||
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
|
EXPECT_EQ(history.mDetailedHistory.size(), static_cast<size_t>(0));
|
||||||
EXPECT_TRUE(history.mHasEvictedEntry);
|
EXPECT_TRUE(history.mHasEvictedEntry);
|
||||||
EXPECT_EQ(jitterAdjusted, history.mLatestEviction.jitterAdjustedTimestamp);
|
EXPECT_EQ(jitterAdjusted, history.mLatestEviction.predictedPlayoutTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observer tests that keys are properly handled
|
// Observer tests that keys are properly handled
|
||||||
|
@ -242,7 +243,8 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
|
|
||||||
// Observer a header with a single Csrc
|
// Observer a header with a single Csrc
|
||||||
void TestObserveOneCsrc() {
|
void TestObserveOneCsrc() {
|
||||||
RtpSourceObserver observer;
|
RtpSourceObserver observer =
|
||||||
|
RtpSourceObserver(dom::RTCStatsTimestampMaker());
|
||||||
webrtc::RTPHeader header;
|
webrtc::RTPHeader header;
|
||||||
constexpr unsigned int ssrc = 857265;
|
constexpr unsigned int ssrc = 857265;
|
||||||
constexpr unsigned int csrc = 3268365;
|
constexpr unsigned int csrc = 3268365;
|
||||||
|
@ -257,7 +259,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
// One for the SSRC, one for the CSRC
|
// One for the SSRC, one for the CSRC
|
||||||
EXPECT_EQ(observer.mRtpSources.size(), static_cast<size_t>(2));
|
EXPECT_EQ(observer.mRtpSources.size(), static_cast<size_t>(2));
|
||||||
nsTArray<dom::RTCRtpSourceEntry> outLevels;
|
nsTArray<dom::RTCRtpSourceEntry> outLevels;
|
||||||
observer.GetRtpSources(timestamp, outLevels);
|
observer.GetRtpSources(outLevels);
|
||||||
EXPECT_EQ(outLevels.Length(), static_cast<size_t>(2));
|
EXPECT_EQ(outLevels.Length(), static_cast<size_t>(2));
|
||||||
bool ssrcFound = false;
|
bool ssrcFound = false;
|
||||||
bool csrcFound = true;
|
bool csrcFound = true;
|
||||||
|
@ -278,7 +280,8 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
|
|
||||||
// Observer a header with two CSRCs
|
// Observer a header with two CSRCs
|
||||||
void TestObserveTwoCsrcs() {
|
void TestObserveTwoCsrcs() {
|
||||||
RtpSourceObserver observer;
|
RtpSourceObserver observer =
|
||||||
|
RtpSourceObserver(dom::RTCStatsTimestampMaker());
|
||||||
webrtc::RTPHeader header;
|
webrtc::RTPHeader header;
|
||||||
constexpr unsigned int ssrc = 239485;
|
constexpr unsigned int ssrc = 239485;
|
||||||
constexpr unsigned int csrc0 = 3425;
|
constexpr unsigned int csrc0 = 3425;
|
||||||
|
@ -295,7 +298,7 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
// One for the SSRC, two for the CSRCs
|
// One for the SSRC, two for the CSRCs
|
||||||
EXPECT_EQ(observer.mRtpSources.size(), static_cast<size_t>(3));
|
EXPECT_EQ(observer.mRtpSources.size(), static_cast<size_t>(3));
|
||||||
nsTArray<dom::RTCRtpSourceEntry> outLevels;
|
nsTArray<dom::RTCRtpSourceEntry> outLevels;
|
||||||
observer.GetRtpSources(timestamp, outLevels);
|
observer.GetRtpSources(outLevels);
|
||||||
EXPECT_EQ(outLevels.Length(), static_cast<size_t>(3));
|
EXPECT_EQ(outLevels.Length(), static_cast<size_t>(3));
|
||||||
bool ssrcFound = false;
|
bool ssrcFound = false;
|
||||||
bool csrc0Found = true;
|
bool csrc0Found = true;
|
||||||
|
@ -322,7 +325,8 @@ class RtpSourcesTest : public ::testing::Test {
|
||||||
|
|
||||||
// Observer a header with a CSRC with audio level extension
|
// Observer a header with a CSRC with audio level extension
|
||||||
void TestObserveCsrcWithAudioLevel() {
|
void TestObserveCsrcWithAudioLevel() {
|
||||||
RtpSourceObserver observer;
|
RtpSourceObserver observer =
|
||||||
|
RtpSourceObserver(dom::RTCStatsTimestampMaker());
|
||||||
webrtc::RTPHeader header;
|
webrtc::RTPHeader header;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -311,9 +311,9 @@ void WebrtcAudioConduit::SetRtcpEventObserver(
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebrtcAudioConduit::GetRtpSources(
|
void WebrtcAudioConduit::GetRtpSources(
|
||||||
const int64_t aTimeNow, nsTArray<dom::RTCRtpSourceEntry>& outSources) {
|
nsTArray<dom::RTCRtpSourceEntry>& outSources) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
return mRtpSourceObserver.GetRtpSources(aTimeNow, outSources);
|
return mRtpSourceObserver.GetRtpSources(outSources);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test-only: inserts a CSRC entry in a RtpSourceObserver's history for
|
// test-only: inserts a CSRC entry in a RtpSourceObserver's history for
|
||||||
|
|
|
@ -197,6 +197,7 @@ class WebrtcAudioConduit : public AudioSessionConduit,
|
||||||
mSendChannel(-1),
|
mSendChannel(-1),
|
||||||
mDtmfEnabled(false),
|
mDtmfEnabled(false),
|
||||||
mMutex("WebrtcAudioConduit::mMutex"),
|
mMutex("WebrtcAudioConduit::mMutex"),
|
||||||
|
mRtpSourceObserver(mCall->GetTimestampMaker()),
|
||||||
mStsThread(aStsThread) {}
|
mStsThread(aStsThread) {}
|
||||||
|
|
||||||
virtual ~WebrtcAudioConduit();
|
virtual ~WebrtcAudioConduit();
|
||||||
|
@ -241,8 +242,7 @@ class WebrtcAudioConduit : public AudioSessionConduit,
|
||||||
bool InsertDTMFTone(int channel, int eventCode, bool outOfBand, int lengthMs,
|
bool InsertDTMFTone(int channel, int eventCode, bool outOfBand, int lengthMs,
|
||||||
int attenuationDb) override;
|
int attenuationDb) override;
|
||||||
|
|
||||||
void GetRtpSources(const int64_t aTimeNow,
|
void GetRtpSources(nsTArray<dom::RTCRtpSourceEntry>& outSources) override;
|
||||||
nsTArray<dom::RTCRtpSourceEntry>& outSources) override;
|
|
||||||
|
|
||||||
void OnRtpPacket(const webrtc::RTPHeader& aRtpHeader,
|
void OnRtpPacket(const webrtc::RTPHeader& aRtpHeader,
|
||||||
const int64_t aTimestamp, const uint32_t aJitter) override;
|
const int64_t aTimestamp, const uint32_t aJitter) override;
|
||||||
|
|
|
@ -312,6 +312,10 @@ class WebRtcCallWrapper : public RefCounted<WebRtcCallWrapper> {
|
||||||
|
|
||||||
DOMHighResTimeStamp GetNow() const { return mTimestampMaker.GetNow(); }
|
DOMHighResTimeStamp GetNow() const { return mTimestampMaker.GetNow(); }
|
||||||
|
|
||||||
|
const dom::RTCStatsTimestampMaker& GetTimestampMaker() const {
|
||||||
|
return mTimestampMaker;
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(WebRtcCallWrapper)
|
MOZ_DECLARE_REFCOUNTED_TYPENAME(WebRtcCallWrapper)
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::AudioDecoderFactory> mDecoderFactory;
|
rtc::scoped_refptr<webrtc::AudioDecoderFactory> mDecoderFactory;
|
||||||
|
@ -597,8 +601,7 @@ class AudioSessionConduit : public MediaSessionConduit {
|
||||||
virtual bool InsertDTMFTone(int channel, int eventCode, bool outOfBand,
|
virtual bool InsertDTMFTone(int channel, int eventCode, bool outOfBand,
|
||||||
int lengthMs, int attenuationDb) = 0;
|
int lengthMs, int attenuationDb) = 0;
|
||||||
|
|
||||||
virtual void GetRtpSources(const int64_t aTimeNow,
|
virtual void GetRtpSources(nsTArray<dom::RTCRtpSourceEntry>& outSources) = 0;
|
||||||
nsTArray<dom::RTCRtpSourceEntry>& outSources) = 0;
|
|
||||||
};
|
};
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,21 +22,35 @@ double RtpSourceObserver::RtpSourceEntry::ToLinearAudioLevel() const {
|
||||||
return std::pow(10, -static_cast<double>(audioLevel) / 20);
|
return std::pow(10, -static_cast<double>(audioLevel) / 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
RtpSourceObserver::RtpSourceObserver()
|
RtpSourceObserver::RtpSourceObserver(
|
||||||
: mMaxJitterWindow(0), mLevelGuard("RtpSourceObserver::mLevelGuard") {}
|
const dom::RTCStatsTimestampMaker& aTimestampMaker)
|
||||||
|
: mMaxJitterWindow(0),
|
||||||
|
mLevelGuard("RtpSourceObserver::mLevelGuard"),
|
||||||
|
mTimestampMaker(aTimestampMaker) {}
|
||||||
|
|
||||||
void RtpSourceObserver::OnRtpPacket(const webrtc::RTPHeader& aHeader,
|
void RtpSourceObserver::OnRtpPacket(const webrtc::RTPHeader& aHeader,
|
||||||
const int64_t aTimestamp,
|
const int64_t aTimestamp,
|
||||||
const uint32_t aJitter) {
|
const uint32_t aJitter) {
|
||||||
MutexAutoLock lock(mLevelGuard);
|
MutexAutoLock lock(mLevelGuard);
|
||||||
{
|
{
|
||||||
|
// We assume that aTimestamp is not significantly in the past, and just get
|
||||||
|
// a JS timestamp for the current time instead of converting aTimestamp.
|
||||||
|
DOMHighResTimeStamp jsNow = mTimestampMaker.GetNow();
|
||||||
mMaxJitterWindow =
|
mMaxJitterWindow =
|
||||||
std::max(mMaxJitterWindow, static_cast<int64_t>(aJitter) * 2);
|
std::max(mMaxJitterWindow, static_cast<int64_t>(aJitter) * 2);
|
||||||
const auto jitterAdjusted = aTimestamp + aJitter;
|
// We are supposed to report the time at which this packet was played out,
|
||||||
|
// but we have only just received the packet. We try to guess when it will
|
||||||
|
// be played out.
|
||||||
|
// TODO: We need to move where we update these stats to MediaPipeline, where
|
||||||
|
// we send frames to the media track graph. In order to do that, we will
|
||||||
|
// need to have the ssrc (and csrc) for decoded frames, but we don't have
|
||||||
|
// that right now. Once we move this to the correct place, we will no longer
|
||||||
|
// need to keep anything but the most recent data.
|
||||||
|
const auto predictedPlayoutTime = jsNow + aJitter;
|
||||||
auto& hist = mRtpSources[GetKey(aHeader.ssrc, EntryType::Synchronization)];
|
auto& hist = mRtpSources[GetKey(aHeader.ssrc, EntryType::Synchronization)];
|
||||||
hist.Prune(aTimestamp);
|
hist.Prune(jsNow);
|
||||||
// ssrc-audio-level handling
|
// ssrc-audio-level handling
|
||||||
hist.Insert(aTimestamp, jitterAdjusted, aHeader.timestamp,
|
hist.Insert(jsNow, predictedPlayoutTime, aHeader.timestamp,
|
||||||
aHeader.extension.hasAudioLevel, aHeader.extension.audioLevel);
|
aHeader.extension.hasAudioLevel, aHeader.extension.audioLevel);
|
||||||
|
|
||||||
// csrc-audio-level handling
|
// csrc-audio-level handling
|
||||||
|
@ -44,27 +58,27 @@ void RtpSourceObserver::OnRtpPacket(const webrtc::RTPHeader& aHeader,
|
||||||
for (uint8_t i = 0; i < aHeader.numCSRCs; i++) {
|
for (uint8_t i = 0; i < aHeader.numCSRCs; i++) {
|
||||||
const uint32_t& csrc = aHeader.arrOfCSRCs[i];
|
const uint32_t& csrc = aHeader.arrOfCSRCs[i];
|
||||||
auto& hist = mRtpSources[GetKey(csrc, EntryType::Contributing)];
|
auto& hist = mRtpSources[GetKey(csrc, EntryType::Contributing)];
|
||||||
hist.Prune(aTimestamp);
|
hist.Prune(jsNow);
|
||||||
bool hasLevel = i < list.numAudioLevels;
|
bool hasLevel = i < list.numAudioLevels;
|
||||||
uint8_t level = hasLevel ? list.arrOfAudioLevels[i] : 0;
|
uint8_t level = hasLevel ? list.arrOfAudioLevels[i] : 0;
|
||||||
hist.Insert(aTimestamp, jitterAdjusted, aHeader.timestamp, hasLevel,
|
hist.Insert(jsNow, predictedPlayoutTime, aHeader.timestamp, hasLevel,
|
||||||
level);
|
level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpSourceObserver::GetRtpSources(
|
void RtpSourceObserver::GetRtpSources(
|
||||||
const int64_t aTimeNow,
|
|
||||||
nsTArray<dom::RTCRtpSourceEntry>& outSources) const {
|
nsTArray<dom::RTCRtpSourceEntry>& outSources) const {
|
||||||
MutexAutoLock lock(mLevelGuard);
|
MutexAutoLock lock(mLevelGuard);
|
||||||
outSources.Clear();
|
outSources.Clear();
|
||||||
for (const auto& it : mRtpSources) {
|
for (const auto& it : mRtpSources) {
|
||||||
const RtpSourceEntry* entry = it.second.FindClosestNotAfter(aTimeNow);
|
const RtpSourceEntry* entry =
|
||||||
|
it.second.FindClosestNotAfter(mTimestampMaker.GetNow());
|
||||||
if (entry) {
|
if (entry) {
|
||||||
dom::RTCRtpSourceEntry domEntry;
|
dom::RTCRtpSourceEntry domEntry;
|
||||||
domEntry.mSource = GetSourceFromKey(it.first);
|
domEntry.mSource = GetSourceFromKey(it.first);
|
||||||
domEntry.mSourceType = GetTypeFromKey(it.first);
|
domEntry.mSourceType = GetTypeFromKey(it.first);
|
||||||
domEntry.mTimestamp = entry->jitterAdjustedTimestamp;
|
domEntry.mTimestamp = entry->predictedPlayoutTime;
|
||||||
domEntry.mRtpTimestamp = entry->rtpTimestamp;
|
domEntry.mRtpTimestamp = entry->rtpTimestamp;
|
||||||
if (entry->hasAudioLevel) {
|
if (entry->hasAudioLevel) {
|
||||||
domEntry.mAudioLevel.Construct(entry->ToLinearAudioLevel());
|
domEntry.mAudioLevel.Construct(entry->ToLinearAudioLevel());
|
||||||
|
@ -74,10 +88,6 @@ void RtpSourceObserver::GetRtpSources(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t RtpSourceObserver::NowInReportClockTime() {
|
|
||||||
return webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
const RtpSourceObserver::RtpSourceEntry*
|
const RtpSourceObserver::RtpSourceEntry*
|
||||||
RtpSourceObserver::RtpSourceHistory::FindClosestNotAfter(int64_t aTime) const {
|
RtpSourceObserver::RtpSourceHistory::FindClosestNotAfter(int64_t aTime) const {
|
||||||
// This method scans the history for the entry whose timestamp is closest to a
|
// This method scans the history for the entry whose timestamp is closest to a
|
||||||
|
@ -88,7 +98,7 @@ RtpSourceObserver::RtpSourceHistory::FindClosestNotAfter(int64_t aTime) const {
|
||||||
auto lastFound = mDetailedHistory.cbegin();
|
auto lastFound = mDetailedHistory.cbegin();
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (const auto& it : mDetailedHistory) {
|
for (const auto& it : mDetailedHistory) {
|
||||||
if (it.second.jitterAdjustedTimestamp > aTime) {
|
if (it.second.predictedPlayoutTime > aTime) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// lastFound can't start before begin, so the first inc must be skipped
|
// lastFound can't start before begin, so the first inc must be skipped
|
||||||
|
@ -100,7 +110,7 @@ RtpSourceObserver::RtpSourceHistory::FindClosestNotAfter(int64_t aTime) const {
|
||||||
if (found) {
|
if (found) {
|
||||||
return &lastFound->second;
|
return &lastFound->second;
|
||||||
}
|
}
|
||||||
if (HasEvicted() && aTime >= mLatestEviction.jitterAdjustedTimestamp) {
|
if (HasEvicted() && aTime >= mLatestEviction.predictedPlayoutTime) {
|
||||||
return &mLatestEviction;
|
return &mLatestEviction;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -113,7 +123,7 @@ void RtpSourceObserver::RtpSourceHistory::Prune(const int64_t aTimeNow) {
|
||||||
// New lower bound of the map
|
// New lower bound of the map
|
||||||
auto lower = mDetailedHistory.begin();
|
auto lower = mDetailedHistory.begin();
|
||||||
for (auto& it : mDetailedHistory) {
|
for (auto& it : mDetailedHistory) {
|
||||||
if (it.second.jitterAdjustedTimestamp > aTimeT) {
|
if (it.second.predictedPlayoutTime > aTimeT) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +133,7 @@ void RtpSourceObserver::RtpSourceHistory::Prune(const int64_t aTimeNow) {
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
if (found) {
|
if (found) {
|
||||||
if (lower->second.jitterAdjustedTimestamp > aTimePrehistory) {
|
if (lower->second.predictedPlayoutTime > aTimePrehistory) {
|
||||||
mLatestEviction = lower->second;
|
mLatestEviction = lower->second;
|
||||||
mHasEvictedEntry = true;
|
mHasEvictedEntry = true;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +141,7 @@ void RtpSourceObserver::RtpSourceHistory::Prune(const int64_t aTimeNow) {
|
||||||
mDetailedHistory.erase(mDetailedHistory.begin(), lower);
|
mDetailedHistory.erase(mDetailedHistory.begin(), lower);
|
||||||
}
|
}
|
||||||
if (HasEvicted() &&
|
if (HasEvicted() &&
|
||||||
(mLatestEviction.jitterAdjustedTimestamp + kHistoryWindow) < aTimeNow) {
|
(mLatestEviction.predictedPlayoutTime + kHistoryWindow) < aTimeNow) {
|
||||||
mHasEvictedEntry = false;
|
mHasEvictedEntry = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +166,7 @@ RtpSourceObserver::RtpSourceEntry& RtpSourceObserver::RtpSourceHistory::Insert(
|
||||||
// x or x T J
|
// x or x T J
|
||||||
// |------Z-----|ABC| -> |------Z-----|ABC|
|
// |------Z-----|ABC| -> |------Z-----|ABC|
|
||||||
if ((aTimestamp + kHistoryWindow) < aTimeNow ||
|
if ((aTimestamp + kHistoryWindow) < aTimeNow ||
|
||||||
aTimestamp < mLatestEviction.jitterAdjustedTimestamp) {
|
aTimestamp < mLatestEviction.predictedPlayoutTime) {
|
||||||
return mPrehistory; // A.K.A. /dev/null
|
return mPrehistory; // A.K.A. /dev/null
|
||||||
}
|
}
|
||||||
mMaxJitterWindow = std::max(mMaxJitterWindow, (aTimestamp - aTimeNow) * 2);
|
mMaxJitterWindow = std::max(mMaxJitterWindow, (aTimestamp - aTimeNow) * 2);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "nsISupportsImpl.h"
|
#include "nsISupportsImpl.h"
|
||||||
#include "mozilla/dom/RTCRtpSourcesBinding.h"
|
#include "mozilla/dom/RTCRtpSourcesBinding.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/include/rtp_packet_observer.h"
|
#include "webrtc/modules/rtp_rtcp/include/rtp_packet_observer.h"
|
||||||
|
#include "RTCStatsReport.h"
|
||||||
|
|
||||||
// Unit Test class
|
// Unit Test class
|
||||||
namespace test {
|
namespace test {
|
||||||
|
@ -29,29 +30,21 @@ namespace mozilla {
|
||||||
*/
|
*/
|
||||||
class RtpSourceObserver : public webrtc::RtpPacketObserver {
|
class RtpSourceObserver : public webrtc::RtpPacketObserver {
|
||||||
public:
|
public:
|
||||||
RtpSourceObserver();
|
explicit RtpSourceObserver(
|
||||||
|
const dom::RTCStatsTimestampMaker& aTimestampMaker);
|
||||||
|
|
||||||
virtual ~RtpSourceObserver(){};
|
virtual ~RtpSourceObserver(){};
|
||||||
|
|
||||||
void OnRtpPacket(const webrtc::RTPHeader& aRtpHeader,
|
void OnRtpPacket(const webrtc::RTPHeader& aRtpHeader,
|
||||||
const int64_t aTimestamp, const uint32_t aJitter) override;
|
const int64_t aTimestamp, const uint32_t aJitter) override;
|
||||||
|
|
||||||
/* Get the local time in MS from the same clock source that is used
|
|
||||||
* to generate the capture timestamps. Use for computing the age of
|
|
||||||
* an entry relative to another clock, e.g. the JS
|
|
||||||
* @return time of now in MS
|
|
||||||
*/
|
|
||||||
static int64_t NowInReportClockTime();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the most recent 10 second window of CSRC and SSRC sources.
|
* Get the most recent 10 second window of CSRC and SSRC sources.
|
||||||
* @param aTimeNow the current report clock time, @see NowInReportClockTime.
|
|
||||||
* @param outLevels will be popluted with source entries
|
* @param outLevels will be popluted with source entries
|
||||||
* Note: this takes jitter into account when calculating the window so
|
* Note: this takes jitter into account when calculating the window so
|
||||||
* the window is actually [time - jitter - 10 sec .. time - jitter]
|
* the window is actually [time - jitter - 10 sec .. time - jitter]
|
||||||
*/
|
*/
|
||||||
void GetRtpSources(const int64_t aTimeNow,
|
void GetRtpSources(nsTArray<dom::RTCRtpSourceEntry>& outSources) const;
|
||||||
nsTArray<dom::RTCRtpSourceEntry>& outSources) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Note: these are pool allocated
|
// Note: these are pool allocated
|
||||||
|
@ -59,7 +52,7 @@ class RtpSourceObserver : public webrtc::RtpPacketObserver {
|
||||||
RtpSourceEntry() = default;
|
RtpSourceEntry() = default;
|
||||||
void Update(const int64_t aTimestamp, const uint32_t aRtpTimestamp,
|
void Update(const int64_t aTimestamp, const uint32_t aRtpTimestamp,
|
||||||
const bool aHasAudioLevel, const uint8_t aAudioLevel) {
|
const bool aHasAudioLevel, const uint8_t aAudioLevel) {
|
||||||
jitterAdjustedTimestamp = aTimestamp;
|
predictedPlayoutTime = aTimestamp;
|
||||||
rtpTimestamp = aRtpTimestamp;
|
rtpTimestamp = aRtpTimestamp;
|
||||||
// Audio level range is 0 - 127 inclusive
|
// Audio level range is 0 - 127 inclusive
|
||||||
hasAudioLevel = aHasAudioLevel && !(aAudioLevel & 0x80);
|
hasAudioLevel = aHasAudioLevel && !(aAudioLevel & 0x80);
|
||||||
|
@ -69,12 +62,19 @@ class RtpSourceObserver : public webrtc::RtpPacketObserver {
|
||||||
// outlined in the webrtc-pc spec.
|
// outlined in the webrtc-pc spec.
|
||||||
double ToLinearAudioLevel() const;
|
double ToLinearAudioLevel() const;
|
||||||
// Time this information was received + jitter
|
// Time this information was received + jitter
|
||||||
int64_t jitterAdjustedTimestamp = 0;
|
int64_t predictedPlayoutTime = 0;
|
||||||
// The original RTP timestamp in the received packet
|
// The original RTP timestamp in the received packet
|
||||||
uint32_t rtpTimestamp = 0;
|
uint32_t rtpTimestamp = 0;
|
||||||
bool hasAudioLevel = false;
|
bool hasAudioLevel = false;
|
||||||
uint8_t audioLevel = 0;
|
uint8_t audioLevel = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Why this is needed:
|
||||||
|
* We are supposed to only report source stats for packets that have already
|
||||||
|
* been rendered. Unfortunately, we only know when these packets are
|
||||||
|
* _received_ right now. So, we need to make a guess at when each packet will
|
||||||
|
* be rendered, and hide its statistics until the clock reaches that estimate.
|
||||||
|
*/
|
||||||
/* Maintains a history of packets for reporting with getContributingSources
|
/* Maintains a history of packets for reporting with getContributingSources
|
||||||
* and getSynchronizationSources. It is expected that entries will not always
|
* and getSynchronizationSources. It is expected that entries will not always
|
||||||
* be observed in chronological order, and that the correct entry for a query
|
* be observed in chronological order, and that the correct entry for a query
|
||||||
|
@ -166,6 +166,7 @@ class RtpSourceObserver : public webrtc::RtpPacketObserver {
|
||||||
int64_t mMaxJitterWindow;
|
int64_t mMaxJitterWindow;
|
||||||
// Guards statistics
|
// Guards statistics
|
||||||
mutable Mutex mLevelGuard;
|
mutable Mutex mLevelGuard;
|
||||||
|
dom::RTCStatsTimestampMaker mTimestampMaker;
|
||||||
|
|
||||||
// Unit test
|
// Unit test
|
||||||
friend test::RtpSourcesTest;
|
friend test::RtpSourcesTest;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче