Bug 1348657 - implement framesEncoded, pliCount, nackCount and firCount for webrtc stats r=jib,smaug

MozReview-Commit-ID: E873mbcrlLL

--HG--
extra : rebase_source : ca6f5d7ab0490948aaed1ae793ed5906149b7236
This commit is contained in:
Nico Grunbaum 2017-03-21 21:52:06 -07:00
parent 92eea71fe1
commit af67a2fb4c
14 changed files with 203 additions and 40 deletions

View File

@ -14,24 +14,24 @@ var statsExpectedByType = {
"inbound-rtp": {
expected: ["id", "timestamp", "type", "ssrc", "isRemote", "mediaType",
"packetsReceived", "packetsLost", "bytesReceived", "jitter",],
optional: ["mozRtt", "remoteId",],
optional: ["mozRtt", "remoteId", "nackCount",],
videoOnly: ["discardedPackets", "framerateStdDev", "framerateMean",
"bitrateMean", "bitrateStdDev",],
"bitrateMean", "bitrateStdDev", "firCount", "pliCount",],
unimplemented: ["mediaTrackId", "transportId", "codecId", "framesDecoded",
"packetsDiscarded", "associateStatsId", "firCount", "pliCount",
"nackCount", "sliCount", "qpSum", "packetsRepaired", "fractionLost",
"packetsDiscarded", "associateStatsId",
"sliCount", "qpSum", "packetsRepaired", "fractionLost",
"burstPacketsLost", "burstLossCount", "burstDiscardCount",
"gapDiscardRate", "gapLossRate",],
},
"outbound-rtp": {
expected: ["id", "timestamp", "type", "ssrc", "isRemote", "mediaType",
"packetsSent", "bytesSent", "remoteId",],
optional: ["remoteId",],
optional: ["remoteId", "nackCount",],
videoOnly: ["droppedFrames", "bitrateMean", "bitrateStdDev",
"framerateMean", "framerateStdDev",],
"framerateMean", "framerateStdDev", "framesEncoded", "firCount",
"pliCount",],
unimplemented: ["mediaTrackId", "transportId", "codecId",
"framesEncoded", "firCount", "pliCount", "nackCount", "sliCount",
"qpSum", "roundTripTime", "targetBitrate",],
"sliCount", "qpSum", "roundTripTime", "targetBitrate",],
},
"codec": { skip: true },
"peer-connection": { skip: true },
@ -136,6 +136,22 @@ var pedanticChecks = report => {
"remote object has local object as it's own remote object.");
}
// nackCount
if (!stat.inner.isRemote) {
ok(stat.nackCount >= 0, stat.type + ".nackCount is sane.");
}
if (!stat.inner.isRemote && stat.inner.mediaType == "video") {
// firCount
ok(stat.firCount >= 0 && stat.firCount < 100,
stat.type + ".firCount is a sane number for a short test. value="
+ stat.firCount);
// pliCount
ok(stat.pliCount >= 0 && stat.pliCount < 100,
stat.type + ".pliCount is a sane number for a short test. value="
+ stat.pliCount);
}
}
if (stat.type == "inbound-rtp") {
@ -194,7 +210,7 @@ var pedanticChecks = report => {
// Local video only stats
//
if (stat.inner.isRemote || stat.inner.mediaType != "video") {
expectations.videoOnly.forEach(field => {
expectations.localVideoOnly.forEach(field => {
if (stat.inner.isRemote) {
ok(stat[field] === undefined, stat.type + " does not have field "
+ field + " when isRemote is true");
@ -204,8 +220,8 @@ var pedanticChecks = report => {
}
});
} else {
expectations.videoOnly.forEach(field => {
ok(stat[field] !== undefined, stat.type + " has field " + field
expectations.localVideoOnly.forEach(field => {
ok(stat.inner[field] !== undefined, stat.type + " has field " + field
+ " when mediaType is video");
});
// discardedPackets
@ -272,7 +288,7 @@ var pedanticChecks = report => {
// Local video only stats
//
if (stat.inner.isRemote || stat.inner.mediaType != "video") {
expectations.videoOnly.forEach(field => {
expectations.localVideoOnly.forEach(field => {
if (stat.inner.isRemote) {
ok(stat[field] === undefined, stat.type + " does not have field "
+ field + " when isRemote is true");
@ -282,9 +298,9 @@ var pedanticChecks = report => {
}
});
} else {
expectations.videoOnly.forEach(field => {
ok(stat[field] !== undefined, stat.type + " has field " + field
+ " when mediaType is video");
expectations.localVideoOnly.forEach(field => {
ok(stat.inner[field] !== undefined, stat.type + " has field " + field
+ " when mediaType is video and isRemote is false");
});
// bitrateMean
@ -323,7 +339,11 @@ var pedanticChecks = report => {
ok(stat.droppedFrames >= 0,
stat.type + ".droppedFrames is not negative. value="
+ stat.droppedFrames);
}
// framesEncoded
ok(stat.framesEncoded >= 0 && stat.framesEncoded < 100000, stat.type
+ ".framesEncoded is a sane number for a short test. value="
+ stat.framesEncoded);
}
//

View File

@ -381,6 +381,10 @@ struct ParamTraits<mozilla::dom::RTCOutboundRTPStreamStats>
WriteParam(aMsg, aParam.mDroppedFrames);
WriteParam(aMsg, aParam.mPacketsSent);
WriteParam(aMsg, aParam.mTargetBitrate);
WriteParam(aMsg, aParam.mFramesEncoded);
WriteParam(aMsg, aParam.mFirCount);
WriteParam(aMsg, aParam.mNackCount);
WriteParam(aMsg, aParam.mPliCount);
WriteRTCRTPStreamStats(aMsg, aParam);
WriteRTCStats(aMsg, aParam);
}
@ -391,6 +395,10 @@ struct ParamTraits<mozilla::dom::RTCOutboundRTPStreamStats>
!ReadParam(aMsg, aIter, &(aResult->mDroppedFrames)) ||
!ReadParam(aMsg, aIter, &(aResult->mPacketsSent)) ||
!ReadParam(aMsg, aIter, &(aResult->mTargetBitrate)) ||
!ReadParam(aMsg, aIter, &(aResult->mFramesEncoded)) ||
!ReadParam(aMsg, aIter, &(aResult->mFirCount)) ||
!ReadParam(aMsg, aIter, &(aResult->mNackCount)) ||
!ReadParam(aMsg, aIter, &(aResult->mPliCount)) ||
!ReadRTCRTPStreamStats(aMsg, aIter, aResult) ||
!ReadRTCStats(aMsg, aIter, aResult)) {
return false;

View File

@ -34,11 +34,17 @@ dictionary RTCRTPStreamStats : RTCStats {
DOMString transportId;
DOMString codecId;
// Video encoder/decoder measurements (absent for rtcp)
// Video encoder/decoder measurements, not present in RTCP case
double bitrateMean;
double bitrateStdDev;
double framerateMean;
double framerateStdDev;
// Local only measurements, RTCP related but not communicated via RTCP. Not
// present in RTCP case.
unsigned long firCount;
unsigned long pliCount;
unsigned long nackCount;
};
dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
@ -50,7 +56,7 @@ dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
long mozJitterBufferDelay;
long mozRtt;
// Video decoder measurement (absent in rtcp case)
// Video decoder measurement, not present in RTCP case
unsigned long discardedPackets;
};
@ -59,8 +65,9 @@ dictionary RTCOutboundRTPStreamStats : RTCRTPStreamStats {
unsigned long long bytesSent;
double targetBitrate; // config encoder bitrate target of this SSRC in bits/s
// Video encoder measurement (absent in rtcp case)
// Video encoder measurements, not present in RTCP case
unsigned long droppedFrames;
unsigned long framesEncoded;
};
dictionary RTCMediaStreamTrackStats : RTCStats {

View File

@ -165,6 +165,12 @@ bool WebrtcAudioConduit::SetLocalCNAME(const char* cname)
return !mPtrRTP->SetRTCP_CNAME(mChannel, temp);
}
bool WebrtcAudioConduit::GetPacketTypeStats(
webrtc::RtcpPacketTypeCounter* aPacketCounts)
{
return !mPtrVoERTP_RTCP->GetRTCPPacketTypeCounters(mChannel, *aPacketCounts);
}
bool WebrtcAudioConduit::GetAVStats(int32_t* jitterBufferDelayMs,
int32_t* playoutBufferDelayMs,
int32_t* avSyncOffsetMs) {

View File

@ -201,11 +201,16 @@ public:
}
bool GetRemoteSSRC(unsigned int* ssrc) override;
bool SetLocalCNAME(const char* cname) override;
bool
GetPacketTypeStats(webrtc::RtcpPacketTypeCounter* aPacketCounts) override;
bool GetVideoEncoderStats(double* framerateMean,
double* framerateStdDev,
double* bitrateMean,
double* bitrateStdDev,
uint32_t* droppedFrames) override
uint32_t* droppedFrames,
uint32_t* framesEncoded) override
{
return false;
}

View File

@ -251,11 +251,16 @@ public:
/**
* Functions returning stats needed by w3c stats model.
*/
virtual bool
GetPacketTypeStats(webrtc::RtcpPacketTypeCounter* aPacketCounts) = 0;
virtual bool GetVideoEncoderStats(double* framerateMean,
double* framerateStdDev,
double* bitrateMean,
double* bitrateStdDev,
uint32_t* droppedFrames) = 0;
uint32_t* droppedFrames,
uint32_t* framesEncoded) = 0;
virtual bool GetVideoDecoderStats(double* framerateMean,
double* framerateStdDev,
double* bitrateMean,

View File

@ -120,12 +120,15 @@ WebrtcVideoConduit::SendStreamStatistics::Update(
if (!aStats.substreams.empty()) {
const webrtc::FrameCounts& fc =
aStats.substreams.begin()->second.frame_counts;
CSFLogVerbose(logTag, "%s: framerate: %u, bitrate: %u, dropped frames delta: %u",
__FUNCTION__, aStats.encode_frame_rate, aStats.media_bitrate_bps,
(mSentFrames - (fc.key_frames + fc.delta_frames)) - mDroppedFrames);
mDroppedFrames = mSentFrames - (fc.key_frames + fc.delta_frames);
mFramesEncoded = fc.key_frames + fc.delta_frames;
CSFLogVerbose(logTag,
"%s: framerate: %u, bitrate: %u, dropped frames delta: %u",
__FUNCTION__, aStats.encode_frame_rate,
aStats.media_bitrate_bps,
mFramesDeliveredToEncoder - mFramesEncoded - mDroppedFrames);
mDroppedFrames = mFramesDeliveredToEncoder - mFramesEncoded;
} else {
CSFLogVerbose(logTag, "%s aStats.substreams is empty", __FUNCTION__);
CSFLogVerbose(logTag, "%s stats.substreams is empty", __FUNCTION__);
}
};
@ -211,11 +214,21 @@ WebrtcVideoConduit::WebrtcVideoConduit(RefPtr<WebRtcCallWrapper> aCall)
CSFLogDebug(logTag, "StreamStats polling scheduled for VideoConduit: %p", aClosure);
auto self = static_cast<WebrtcVideoConduit*>(aClosure);
MutexAutoLock lock(self->mCodecMutex);
MOZ_ASSERT(!self->mEngineTransmitting || !self->mEngineReceiving,
"Video conduit is not both receiving and transmitting");
if (self->mEngineTransmitting && self->mSendStream) {
self->mSendStreamStats.Update(self->mSendStream->GetStats());
const auto& stats = self->mSendStream->GetStats();
self->mSendStreamStats.Update(stats);
if (stats.substreams.empty()) {
return;
}
self->mPacketCounts =
stats.substreams.begin()->second.rtcp_packet_type_counts;
}
if (self->mEngineReceiving && self->mRecvStream) {
self->mRecvStreamStats.Update(self->mRecvStream->GetStats());
const auto& stats = self->mRecvStream->GetStats();
self->mRecvStreamStats.Update(stats);
self->mPacketCounts = stats.rtcp_packet_type_counts;
}
};
mVideoStatsTimer->InitWithFuncCallback(
@ -746,12 +759,27 @@ WebrtcVideoConduit::GetRemoteSSRC(unsigned int* ssrc)
return true;
}
bool
WebrtcVideoConduit::GetPacketTypeStats(
webrtc::RtcpPacketTypeCounter* aPacketCounts)
{
MutexAutoLock lock(mCodecMutex);
if ((!mEngineTransmitting || !mSendStream) // Not transmitting
&& (!mEngineReceiving || !mRecvStream)) // And not receiving
{
return false;
}
*aPacketCounts = mPacketCounts;
return true;
}
bool
WebrtcVideoConduit::GetVideoEncoderStats(double* framerateMean,
double* framerateStdDev,
double* bitrateMean,
double* bitrateStdDev,
uint32_t* droppedFrames)
uint32_t* droppedFrames,
uint32_t* framesEncoded)
{
{
MutexAutoLock lock(mCodecMutex);
@ -761,6 +789,7 @@ WebrtcVideoConduit::GetVideoEncoderStats(double* framerateMean,
mSendStreamStats.GetVideoStreamStats(*framerateMean, *framerateStdDev,
*bitrateMean, *bitrateStdDev);
mSendStreamStats.DroppedFrames(*droppedFrames);
*framesEncoded = mSendStreamStats.FramesEncoded();
return true;
}
}
@ -1774,7 +1803,7 @@ WebrtcVideoConduit::SendVideoFrame(webrtc::VideoFrame& frame)
}
}
mSendStreamStats.SentFrame();
mSendStreamStats.FrameDeliveredToEncoder();
CSFLogDebug(logTag, "%s Inserted a frame", __FUNCTION__);
return kMediaConduitNoError;
}

View File

@ -297,15 +297,19 @@ public:
std::vector<unsigned int> GetLocalSSRCs() const override;
bool SetLocalSSRCs(const std::vector<unsigned int> & ssrcs) override;
bool GetRemoteSSRC(unsigned int* ssrc) override;
bool SetRemoteSSRC(unsigned int ssrc) override;
bool SetLocalCNAME(const char* cname) override;
bool
GetPacketTypeStats(webrtc::RtcpPacketTypeCounter* aPacketCounts) override;
bool GetVideoEncoderStats(double* framerateMean,
double* framerateStdDev,
double* bitrateMean,
double* bitrateStdDev,
uint32_t* droppedFrames) override;
uint32_t* droppedFrames,
uint32_t* framesEncoded) override;
bool GetVideoDecoderStats(double* framerateMean,
double* framerateStdDev,
double* bitrateMean,
@ -360,16 +364,21 @@ private:
* @param aOutDroppedFrames: the number of dropped frames
*/
void DroppedFrames(uint32_t& aOutDroppedFrames) const;
/**
* Returns the number of frames that have been encoded so far
*/
uint32_t FramesEncoded() const {
return mFramesEncoded;
}
void Update(const webrtc::VideoSendStream::Stats& aStats);
/**
* Call once for every frame delivered for encoding
*/
void SentFrame() {
++mSentFrames;
}
void FrameDeliveredToEncoder() { ++mFramesDeliveredToEncoder; }
private:
uint32_t mDroppedFrames = 0;
mozilla::Atomic<int32_t> mSentFrames;
uint32_t mFramesEncoded = 0;
mozilla::Atomic<int32_t> mFramesDeliveredToEncoder;
};
/** Statistics for receiving streams
@ -457,12 +466,14 @@ private:
//Local database of currently applied receive codecs
nsTArray<UniquePtr<VideoCodecConfig>> mRecvCodecList;
// protects mCurSendCodecConfig, mInReconfig,mVideoSend/RecvStreamStats, mSend/RecvStreams
// protects mCurSendCodecConfig, mInReconfig,mVideoSend/RecvStreamStats, mSend/RecvStreams, mPacketCounts
Mutex mCodecMutex;
nsAutoPtr<VideoCodecConfig> mCurSendCodecConfig;
bool mInReconfig;
SendStreamStatistics mSendStreamStats;
ReceiveStreamStatistics mRecvStreamStats;
webrtc::RtcpPacketTypeCounter mPacketCounts;
// Must call webrtc::Call::DestroyVideoReceive/SendStream to delete these:
webrtc::VideoReceiveStream* mRecvStream;
webrtc::VideoSendStream* mSendStream;

View File

@ -3660,6 +3660,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
s.mPacketsSent.Construct(mp.rtp_packets_sent());
s.mBytesSent.Construct(mp.rtp_bytes_sent());
// Fill in packet type statistics
webrtc::RtcpPacketTypeCounter counters;
if (mp.Conduit()->GetPacketTypeStats(&counters)) {
s.mNackCount.Construct(counters.nack_packets);
// Fill in video only packet type stats
if (!isAudio) {
s.mFirCount.Construct(counters.fir_packets);
s.mPliCount.Construct(counters.pli_packets);
}
}
// Lastly, fill in video encoder stats if this is video
if (!isAudio) {
double framerateMean;
@ -3667,16 +3678,19 @@ PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
double bitrateMean;
double bitrateStdDev;
uint32_t droppedFrames;
uint32_t framesEncoded;
if (mp.Conduit()->GetVideoEncoderStats(&framerateMean,
&framerateStdDev,
&bitrateMean,
&bitrateStdDev,
&droppedFrames)) {
&droppedFrames,
&framesEncoded)) {
s.mFramerateMean.Construct(framerateMean);
s.mFramerateStdDev.Construct(framerateStdDev);
s.mBitrateMean.Construct(bitrateMean);
s.mBitrateStdDev.Construct(bitrateStdDev);
s.mDroppedFrames.Construct(droppedFrames);
s.mFramesEncoded.Construct(framesEncoded);
}
}
query->report->mOutboundRTPStreamStats.Value().AppendElement(s,
@ -3748,6 +3762,16 @@ PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
s.mMozAvSyncDelay.Construct(avSyncDelta);
}
}
// Fill in packet type statistics
webrtc::RtcpPacketTypeCounter counters;
if (mp.Conduit()->GetPacketTypeStats(&counters)) {
s.mNackCount.Construct(counters.nack_packets);
// Fill in video only packet type stats
if (!isAudio) {
s.mFirCount.Construct(counters.fir_packets);
s.mPliCount.Construct(counters.pli_packets);
}
}
// Lastly, fill in video decoder stats if this is video
if (!isAudio) {
double framerateMean;

View File

@ -155,7 +155,8 @@ struct ChannelStatistics : public RtcpStatistics {
};
// Statistics callback, called at each generation of a new RTCP report block.
class StatisticsProxy : public RtcpStatisticsCallback {
class StatisticsProxy : public RtcpStatisticsCallback,
public RtcpPacketTypeCounterObserver {
public:
StatisticsProxy(uint32_t ssrc)
: stats_lock_(CriticalSectionWrapper::CreateCriticalSection()),
@ -186,6 +187,20 @@ class StatisticsProxy : public RtcpStatisticsCallback {
return stats_;
}
void RtcpPacketTypesCounterUpdated(uint32_t ssrc,
const RtcpPacketTypeCounter& packet_counter) override {
CriticalSectionScoped cs(stats_lock_.get());
if (ssrc != ssrc_) {
return;
}
packet_counter_ = packet_counter;
};
void GetPacketTypeCounter(RtcpPacketTypeCounter& aPacketTypeCounter) {
CriticalSectionScoped cs(stats_lock_.get());
aPacketTypeCounter = packet_counter_;
}
private:
// StatisticsUpdated calls are triggered from threads in the RTP module,
// while GetStats calls can be triggered from the public voice engine API,
@ -193,6 +208,7 @@ class StatisticsProxy : public RtcpStatisticsCallback {
rtc::scoped_ptr<CriticalSectionWrapper> stats_lock_;
uint32_t ssrc_;
ChannelStatistics stats_;
RtcpPacketTypeCounter packet_counter_;
};
class VoERtcpObserver : public RtcpBandwidthObserver {
@ -936,7 +952,7 @@ Channel::Channel(int32_t channelId,
statistics_proxy_.reset(new StatisticsProxy(_rtpRtcpModule->SSRC()));
rtp_receive_statistics_->RegisterRtcpStatisticsCallback(
statistics_proxy_.get());
configuration.rtcp_packet_type_counter_observer = statistics_proxy_.get();
Config audioproc_config;
audioproc_config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
audioproc_config.Set<ExtendedFilter>(
@ -3274,6 +3290,14 @@ Channel::GetRTPStatistics(CallStatistics& stats)
return 0;
}
int Channel::GetRTCPPacketTypeCounters(RtcpPacketTypeCounter& stats) {
if (_rtpRtcpModule->RTCP() == RtcpMode::kOff) {
return -1;
}
statistics_proxy_->GetPacketTypeCounter(stats);
return 0;
}
int Channel::SetREDStatus(bool enable, int redPayloadtype) {
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
"Channel::SetREDStatus()");

View File

@ -355,6 +355,7 @@ public:
unsigned int& maxJitterMs,
unsigned int& discardedPackets,
unsigned int& cumulativeLost);
int GetRTCPPacketTypeCounters(RtcpPacketTypeCounter& stats);
int GetRemoteRTCPReportBlocks(std::vector<ReportBlock>* report_blocks);
int GetRTPStatistics(CallStatistics& stats);
int SetREDStatus(bool enable, int redPayloadtype);

View File

@ -179,6 +179,10 @@ class WEBRTC_DLLEXPORT VoERTP_RTCP {
// Gets RTCP statistics for a specific |channel|.
virtual int GetRTCPStatistics(int channel, CallStatistics& stats) = 0;
// Gets RTCP packet type counters for a specific channel
virtual int GetRTCPPacketTypeCounters(int channel,
RtcpPacketTypeCounter& stats) = 0;
// Gets the report block parts of the last received RTCP Sender Report (SR),
// or RTCP Receiver Report (RR) on a specified |channel|. Each vector
// element also contains the SSRC of the sender in addition to a report

View File

@ -357,6 +357,22 @@ int VoERTP_RTCPImpl::GetRTCPStatistics(int channel, CallStatistics& stats) {
return channelPtr->GetRTPStatistics(stats);
}
int VoERTP_RTCPImpl::GetRTCPPacketTypeCounters(int channel,
RtcpPacketTypeCounter& stats) {
if (!_shared->statistics().Initialized()) {
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == NULL) {
_shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"GetRTCPPacketTypeCounters() failed to locate channel");
return -1;
}
return channelPtr->GetRTCPPacketTypeCounters(stats);
}
int VoERTP_RTCPImpl::GetRemoteRTCPReportBlocks(
int channel, std::vector<ReportBlock>* report_blocks) {
if (!_shared->statistics().Initialized()) {

View File

@ -70,6 +70,9 @@ class VoERTP_RTCPImpl : public VoERTP_RTCP {
int GetRTCPStatistics(int channel, CallStatistics& stats) override;
int GetRTCPPacketTypeCounters(int channel,
RtcpPacketTypeCounter& stats) override;
int GetRemoteRTCPReportBlocks(
int channel,
std::vector<ReportBlock>* report_blocks) override;