当我们完成整体的流转后,需要检测其中的每帧数据时,需要用到一些工具检测和API获取。以下我们来介绍2个方法。
内置性能测试工具webrtc-internals
针对云渲染产品进行性能测试,除过一些专业测试工具,企业自研测试工具外,我们还可以借助基于Google浏览器的调试工具Webrtc。
Google浏览器输入chrome://webrtc-internals/,
就会展示出有webrtc相关功能被启用的网页以及通信过程中产生的统计数据:
音频统计数据:
audioInputLevel
发送端采集的音频能量大小,数值越大,说明音频保真度越高。
bitsSentPerSecond
每秒发送出去的比特数,数值越大越好,发送字节的吞吐量。
packetsSentPerSecond
每秒发送出去的音频包数,数值越大越好。
googResidualEchoLikelihood
Chrome 56中新增的,主要用来标识是否存在回声,范围为0 (没有回声)- 1(有回声),当值大于0.5时表明存在回声。
视频统计数据:
bitsSentPerSecond
每秒发送出去的比特数,根据当前网络情况会进行动态调整,数值越大性能表现越好。
framesEncoded
累计编码出来的视频帧数,没有异常情况的话会一直增长。
packetsLost
发送端从接收端发送过来的RTCP Receiver Report中得到的累积丢包数量,可以和googNacksReceived数据进行对照。该数值越小越好,数值偏大说明网络存在瓶颈。
googRtt
Rtt全称为Round-trip time,是发送端从接受端发送过来的RTCPReceiver Report中得到的时间戳通过计算得到的往返时延。
packetsSentPerSecond
Chrome 56中新增的,每秒发送出去的视频包数量,数值越大性能越好。
qpSum
发送端编码出的带有QP值的帧的数量,QP全称为QuantizationParameter。帧值达到60以上一般都是良好的。
googAdaptationChanges
发送端因为CPU的负载变化导致的分辨变高或者变低的次数,需要设置。
googAvgEncodeMs
发送端平均编码时间,越小越好。
googEncodeUsagePercent
发送端(平均每帧编码时间)/(平均每帧采集时间),反应编码效率。
googFirsReceived
发送端收到的关键帧请求数量,FIR全称为Full Intra Request,一般来说在video conference模式下,有新的参与者进来会发出。
googPlisReceived
发送端收到的关键帧请求数量,PLI全称为Picture Loss Indication,一般来说在解码失败时会发出。
googNacksReceived
发送端收到的重传包请求数量,Nack全称为Negative ACKnowledgement可以和packetsLost数据进行对照。
googFrameHeightSent
发送端发送的分辨率高度,根据当前网络会进行动态调整。
googFrameWidthSent
发送端发送的分辨率宽度,根据当前网络会进行动态调整。
googFrameRateInput
发送端设置的初始帧率。
googFrameRateSent
发送端实际发送的帧率,根据当前网络会进行动态调整
通过API方式来检测
用PeerConnection->GetStats获取WebRTC状态信息,实现webrtc::RTCStatsCollectorCallback接口。然后调用PeerConnection→GetStats()函数即可。
可以设置轮询,也可以借用Pong消息,加入到MicroProfiler信息中
以下是具体的接口实现
#pragma once
#include <api/stats/rtc_stats_collector_callback.h>
#include <api/peer_connection_interface.h>
#include "api/stats/rtc_stats_report.h"
#include "QLSingleton.h"
#include "QLLogSystem.h"
#include "QLCommonDefine.h"
#include "QLJsonHelper.h"
namespace QL
{
class QLStatsCollection :virtual public webrtc::RTCStatsCollectorCallback, public QLSingleton<QLStatsCollection>
{
public:
friend class QLSingleton<QLStatsCollection>;
virtual void AddRef() const override
{
mRefCount.IncRef();
}
virtual rtc::RefCountReleaseStatus Release() const override
{
rtc::RefCountReleaseStatus status = mRefCount.DecRef();
return status;
}
/// <summary>
///
/// </summary>
/// <param name="report"></param>
virtual void OnStatsDelivered(const rtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override
{
if (!QLGlobalConfig::Get().IsEnablePongProfiler())
{
return;
}
for (const auto& stats : *report)
{
std::string elementName = stats.id();
std::string allElementData = stats.ToJson();
Json::Value myJsonValue = TO_JSON(allElementData);
std::string myType = JSON_STR(myJsonValue, "type");
std::string myID = JSON_STR(myJsonValue, "id");
std::string myKind = JSON_STR(myJsonValue, "kind");
if ((myType == "outbound-rtp") && (myKind == "video"))
{
std::string trackId = JSON_STR(myJsonValue, "trackId");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " trackId: " + trackId));
std::string codecId = JSON_STR(myJsonValue, "codecId");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " codecId: " + codecId));
int targetBitrate = JSON_INT(myJsonValue, "targetBitrate");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG+ " targetBitrate: " + std::to_string(targetBitrate)));
int framesEncoded = JSON_INT(myJsonValue, "framesEncoded");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " framesEncoded: " + std::to_string(framesEncoded)));
int framesSent = JSON_INT(myJsonValue, "framesSent");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " framesSent: " + std::to_string(framesSent)));
int keyFramesEncoded = JSON_INT(myJsonValue, "keyFramesEncoded");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " keyFramesEncoded: " + std::to_string(keyFramesEncoded)));
int totalEncodeTime = JSON_INT(myJsonValue, "totalEncodeTime");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " totalEncodeTime: " + std::to_string(totalEncodeTime) + "(ms)"));
float totalPacketSendDelay = JSON_FLOAT(myJsonValue, "totalPacketSendDelay"); //ms
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " totalPacketSendDelay: " + std::to_string(totalPacketSendDelay)+"(ms)"));
int framesPerSecond = JSON_INT(myJsonValue, "framesPerSecond");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " framesPerSecond: " + std::to_string(framesPerSecond)));
}
if ((myType == "remote-inbound-rtp") && (myKind == "video"))
{
float jitter = JSON_FLOAT(myJsonValue, "jitter");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " jitter: " + std::to_string(jitter)));
int packetLost = JSON_INT(myJsonValue, "packetLost");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " packetLost: " + std::to_string(packetLost)));
int fractionLost = JSON_INT(myJsonValue, "fractionLost");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " fractionLost: " + std::to_string(fractionLost)));
float roundTripTime = JSON_FLOAT(myJsonValue, "roundTripTime");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " roundTripTime: " + std::to_string(roundTripTime) + "(ms)"));
float totalRoundTripTime = JSON_FLOAT(myJsonValue, "totalRoundTripTime");
QL_LOG(std::string(OBSERVER_MARK_EACH_PONG + " totalRoundTripTime: " + std::to_string(totalRoundTripTime) + "(ms)"));
}
if (myType == "local-candidate")
{
//暂时不输出这些ICE信息
}
//不限定trackIdentifier,因为我们的类型很多啊。匹配麻烦
if ((myType == "track") && (myKind == "video"))
{
//前端接受到帧,所以只能前端计算丢帧率
}
}
}
QLStatsCollection() :mRefCount(0)
{
}
private:
mutable webrtc::webrtc_impl::RefCounter mRefCount;
};
}
最终Console中打印的信息
主要重要的数据字段:
“type”: “codec”, 可以确定当前编码 “mimeType”: “video/H264”,
outbound-rtp中重要的数据
【以下的Json数据我们只罗列出来部分】
获取到的Json可视化后的数据
[
{
“type”: “certificate”,
“id”: “CF93:8A:53:EE:1F:44:3A:B6:A7:C8:74:6F:CB:9E:23:15:38:A0:9F:11:33:12:B8:11:3D:78:07:F0:90:CF:5F:0F”,
“timestamp”: 1694055419635000,
“fingerprint”: “93:8A:53:EE:1F:44:3A:B6:A7:C8:74:6F:CB:9E:23:15:38:A0:9F:11:33:12:B8:11:3D:78:07:F0:90:CF:5F:0F”,
“fingerprintAlgorithm”: “sha-256”,
“base64Certificate”: “MIIBFTCBvKADAgECAggWxMkFdNwv7TAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZXZWJSVEMwHhcNMjMwOTA2MDI1NjQ2WhcNMjMxMDA3MDI1NjQ2WjARMQ8wDQYDVQQDDAZXZWJSVEMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASM16HqFnNsOJSNXP3QC7PUkStRtgEDqfhZDvBT/UMCTOZp2xFG2ZHH2MhTPkjC6kXPCyVejDpFVcZSNpnhAyEgMAoGCCqGSM49BAMCA0gAMEUCIQCuGjErcgGwukrWZ8E5HzGouvWYwQGsTzx2Oj+zkjEKDgIgSZxxF9E+6G2Ge6UjmZfHTtrnP/wopvZkBXNJqobCtgI=”
},
{
“type”: “certificate”,
“id”: “CFDD:03:FA:EA:CB:E2:F8:0E:25:FA:D9:01:4C:B4:74:F1:02:01:EA:7A:8F:78:4E:6F:57:2E:43:D6:5D:85:23:BE”,
“timestamp”: 1694055419635000,
“fingerprint”: “DD:03:FA:EA:CB:E2:F8:0E:25:FA:D9:01:4C:B4:74:F1:02:01:EA:7A:8F:78:4E:6F:57:2E:43:D6:5D:85:23:BE”,
“fingerprintAlgorithm”: “sha-256”,
“base64Certificate”: “MIIBFjCBvKADAgECAghN0S0ay/cXIDAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZXZWJSVEMwHhcNMjMwOTA2MDI1NjQ3WhcNMjMxMDA3MDI1NjQ3WjARMQ8wDQYDVQQDDAZXZWJSVEMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARaCm3N+d0WXuSnqlNagGO4LMP1VR2pVyv+wDxw4lZB6cIR2Z1hAqdDLSrPafd0NsvMmVrLkxt85zRtOjkJyOt+MAoGCCqGSM49BAMCA0kAMEYCIQDjA7ogoIslpIrWXoJoqyJ17a2pD5/JeBRj3h7CbMuH+gIhANi0r8i4DenVioKdI7BmPwJ5O22tsuimZjGHVNAfekzf”
},
{
“type”: “codec”,
“id”: “COT01_102_level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f”,
“timestamp”: 1694055419635000,
“transportId”: “T01”,
“payloadType”: 102,
“mimeType”: “video/H264”,
“clockRate”: 90000,
“sdpFmtpLine”: “level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f”
},
{
“type”: “codec”,
“id”: “COT01_111_minptime=10;sprop-maxcapturerate=48000;stereo=1;useinbandfec=1”,
“timestamp”: 1694055419635000,
“transportId”: “T01”,
“payloadType”: 111,
“mimeType”: “audio/opus”,
“clockRate”: 48000,
“channels”: 2,
“sdpFmtpLine”: “minptime=10;sprop-maxcapturerate=48000;stereo=1;useinbandfec=1”
},
{
“type”: “candidate-pair”,
“id”: “CP18ZFk+UQ_5F/MfElY”,
“timestamp”: 1694055419635000,
“transportId”: “T01”,
“localCandidateId”: “I18ZFk+UQ”,
“remoteCandidateId”: “I5F/MfElY”,
“state”: “succeeded”,
“priority”: 7926380424097976000,
“nominated”: false,
“writable”: true,
“packetsSent”: 0,
“packetsReceived”: 0,
“bytesSent”: 0,
“bytesReceived”: 0,
“totalRoundTripTime”: 0.003,
“currentRoundTripTime”: 0.001,
“requestsReceived”: 5,
“requestsSent”: 4,
“responsesReceived”: 4,
“responsesSent”: 5,
“consentRequestsSent”: 3,
“packetsDiscardedOnSend”: 0,
“bytesDiscardedOnSend”: 0
},
{
“type”: “candidate-pair”,
“id”: “CP18ZFk+UQ_l7760kOD”,
“timestamp”: 1694055419635000,
“transportId”: “T01”,
“localCandidateId”: “I18ZFk+UQ”,
“remoteCandidateId”: “Il7760kOD”,
“state”: “in-progress”,
“priority”: 9079301928704823000,
“nominated”: false,
“writable”: false,
“packetsSent”: 0,
“packetsReceived”: 0,
“bytesSent”: 0,
“bytesReceived”: 0,
“totalRoundTripTime”: 0,
“requestsReceived”: 0,
“requestsSent”: 7,
“responsesReceived”: 0,
“responsesSent”: 0,
“consentRequestsSent”: 0,
“packetsDiscardedOnSend”: 0,
“bytesDiscardedOnSend”: 0
},
RA/SD 衍生者AI训练营。发布者:chris,转载请注明出处:https://www.shxcj.com/archives/6594