WebRTC 视频流接纳核算陈述
在每次视频推流或拉流结束后,WebRTC都会输出本次视频推拉流的核算陈述。其中包含了关于评价本次推拉流质量相关的若干参数。本文的首要意图是介绍视频拉流相关的核算目标意义。
关于推流相关的核算目标,别的的文章进行独自描绘。
本文源码基于 WebRTC M94 编写。后续 WebRTC 版本或许有所变化(如 receive_statistics_proxy 已被移除,改为 receive_statistics_proxy2),细节或许不同,但基本原理应该适用。
相关源码
- video\receive_statistics_proxy.cc
- call\video_receive_stream.h
核算目标
ReceiveStreamLifetimeInSeconds
意义如字面意思。表明从视频拉流对象创立,到结束的总时长,单位是秒。
核算的开始时刻是 ReceiveStatisticsProxy
类结构的时分,在 ReceiveStatisticsProxy::UpdateHistograms()
中,当时时刻减去开始时刻,得到耗时。
VideoReceiveStream
的结构函数,一起结构了ReceiveStatisticsProxy
。
VideoReceiveStream::Stop()
的时分,会调用ReceiveStatisticsProxy::UpdateHistograms()
完结各种衡量目标核算。
Frames decoded
望文生义,表明解码总帧数。对应 VideoReceiveStream::Stats
的成员变量 frames_decoded
。 视频帧解码告诉时序:
@startuml
Actor Decoder
Decoder -> VCMDecodedFrameCallback : Decoded
VCMDecodedFrameCallback -> VideoStreamDecoder : FrameToRender
VideoStreamDecoder -> ReceiveStatisticsProxy : OnDecodedFrame
@enduml
解码器解码一帧后,会告诉到 ReceiveStatisticsProxy::OnDecodedFrame()
中对解码帧数添加计数。
DroppedFrames.Receiver
当 VideoReceiveStream::Stop()
执行的时分,会调用 RtpVideoStreamReceiver::GetUniqueFramesSeen()
获取一个视频帧计数器 frame_counter_
,它的值是在 RtpVideoStreamReceiver::OnReceivedPayloadData()
中依据收包时刻戳来添加,详细见源码 video\rtp_video_stream_receiver.cc
。
frame_counter_.Add(packet->timestamp);
这个视频帧计数器能够了解为从收到的视频帧总数,然后用这个总数减去解码帧数Frames decoded
,就得到了丢掉的总帧数:
int num_dropped_frames = *num_unique_frames_ - stats_.frames_decoded;
ReceivedPacketsLostInPercent
望文生义,丢包率(百分比)。当 ReceiveStreamLifetimeInSeconds
大于10秒时才或许输出这个数值。这个数值实际的核算位置来自 StreamStatisticianImpl::GetFractionLostInPercent()
。丢包率的分子和分母不在本文介绍规模内,感兴趣的读者自行阅览源码。
DecodedFramesPerSecond
均匀解码帧率。以当时时刻减去解码第一帧开始时刻得到的差值做为分母,解码总帧数做为分子,核算得出的整型数值。
RenderFramesPerSecond
均匀烘托帧率。数值来自于 ReceiveStatisticsProxy
的成员变量 render_fps_tracker_
,变量类型是rtc::RateTracker
。关于rtc::RateTracker
的完成原理,能够参考这篇文章。
在ReceiveStatisticsProxy::OnRenderedFrame
中会调用 AddSamples
添加一个采样计数。终究调用 RateTracker::ComputeTotalRate
返回值并对其四舍五入获得烘托帧率。例如烘托帧数总共是 545,总时长是 60000 毫秒,得到的帧率便是 545 x 1000 60000 = 9.08 ≈ 9 帧/秒。
代码中对采样计数有最少200个的束缚,即少于200帧是不会输出烘托帧率的。
烘托帧告诉时序大概如下:
@startuml
Actor Decoder
Decoder -> VideoStreamDecoder : FrameToRender
VideoStreamDecoder -> IncomingVideoStream : OnFrame
note right
如果 VideoReceiveStream::Config 的
enable_prerenderer_smoothing
是 false,则会直接送给 VideoReceiveStream
end note
IncomingVideoStream -> VideoReceiveStream : OnFrame
note right
onFrame 是完成
rtc::VideoSinkInterface 的虚函数
end note
VideoReceiveStream -> ReceiveStatisticsProxy : OnRenderedFrame
@enduml
KeyFramesReceivedInPermille
permille
是“千分率”的意思,因此,这个数值是表明接纳要害帧的千分率。核算公式在代码中如下:
(要害帧总数 x 1000 + 总帧数 / 2) / 总帧数 (1)
其中总帧数是要害帧和非要害帧的总和。留意核算的结果会被强转成整型。
其实我看到这个公式是有点不太了解的。如果只是核算千分率,为何不直接就使用
(要害帧总数 总帧数) x 1000 (2)
就行了?如果依照上面的公式(1)核算,意味着核算出来的结果始终是会比公式(2)要大一些的(约等于0.5)。不知道作者是出于什么考虑。
DecodeTimeInMs
均匀解码耗时。数值来自于 ReceiveStatisticsProxy
的成员变量 decode_time_counter_
,变量类型是rtc::SampleCounter
(源码:rtc_base\numerics\sample_counter.cc
)。rtc::SampleCounter
的源码能够阅览一下,比较简略,ReceiveStatisticsProxy
有许多变量类型都是它。
在ReceiveStatisticsProxy::OnDecodedFrame
被调用的时分,会将当时帧的解码耗时decode_time_ms
追加到decode_time_counter_
中记载下来,并添加一个采样计数。终究核算的均匀解码耗时,便是调用rtc::SampleCounter
的Avg
方法核算得到的,其原理比较简略:悉数帧的解码耗时总时长采样计数。与 RenderFramesPerSecond
一样,对采样计数现在有最小200的要求,即小于200个采样计数不输出此项数据。
JitterBufferDelayInMs, TargetDelayInMs, CurrentDelayInMs
这三个数值具有强相关性,因此放到一起描绘。它们别离对应 ReceiveStatisticsProxy
的成员变量 jitter_buffer_delay_counter_
,target_delay_counter_
和current_delay_counter_
,变量类型是rtc::SampleCounter
。
在ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated
被调用的时分,会传入jitter_buffer_ms
,target_delay_ms
以及current_delay_ms
,累加到对应的采样计数器变量中。终究用于核算均匀值:分子是每次传入的数值总和,分母是次数。
ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated
由 FrameBuffer
调用,源码:modules\video_coding\frame_buffer2.cc
:
@startuml
Actor jitter_buffer
jitter_buffer -> FrameBuffer : IntersetFrame/NextFrame
FrameBuffer -> FrameBuffer : StartWaitForNextFrameOnQueue
FrameBuffer -> FrameBuffer : GetNextFrame
FrameBuffer -> FrameBuffer : UpdateJitterDelay
FrameBuffer -> ReceiveStatisticsProxy : OnFrameBufferTimingsUpdated
@enduml
FrameBuffer 和 JitterBuffer 的完成原理不在本文介绍规模之内,感兴趣的读者自行研读源码。
EndToEndDelayInMs
端到端均匀延时。对应 VideoReceiveStream::Stats
的成员变量 e2e_delay_counter
。 它的类型是rtc::SampleCounter
。在ReceiveStatisticsProxy::OnRenderedFrame
中会核算单帧的延时,累加到e2e_delay_counter
。单次的延时数值是当时 ntp_time(webrtc::Clock::CurrentNtpInMilliseconds()
)减去webrtc::VideoFrame
的ntp_time_ms_
得到的。
EndToEndDelayMaxInMs
端到端最大延时。它是 VideoReceiveStream::Stats
的成员变量 e2e_delay_counter
记载的一切单帧延时的最大值。
InterframeDelayInMs
解码帧距离均匀值。对应 VideoReceiveStream::Stats
的成员变量 interframe_delay_counter
。 它的类型是rtc::SampleCounter
。在ReceiveStatisticsProxy::OnDecodedFrame
中会核算单帧的解码时刻距离(即ReceiveStatisticsProxy::OnDecodedFrame
被调用的两次距离),累加到interframe_delay_counter
。
InterframeDelayMaxInMs
最大解码帧距离。它是 VideoReceiveStream::Stats
的成员变量 interframe_delay_counter
记载的一切单帧解码帧距离数值的最大者。
InterframeDelay95PercentileInMs
这个数值代表:超越95%帧距离数值里的最小值,或者了解为 95%的帧距离都小于多少。对应 VideoReceiveStream::Stats
的成员变量 interframe_delay_percentiles
。它的类型是rtc::HistogramPercentileCounter
。这个数值乍一看不太简略了解,这里简略介绍一下。
要了解这个数值,需求先了解rtc::HistogramPercentileCounter
。它与 rtc::SampleCounter
有类似之处,但本质上仍是有许多不同。它存在几个要害的成员变量:
std::vector<size_t> histogram_low_;
std::map<uint32_t, size_t> histogram_high_;
const uint32_t long_tail_boundary_;
size_t total_elements_;
size_t total_elements_low_;
以
long_tail_boundary_
作为分界点,小于long_tail_boundary_
的值,记载到histogram_low_
,不然记载到histogram_high_
。现在就InterframeDelay95PercentileInMs
来说,这个分界点是500
(kMaxCommonInterframeDelayMs
)。
histogram_low_
的每个元素索引序号代表详细的一个帧距离数值,值表明此帧距离的次数。分界点是500的话,histogram_low_
所代表的帧距离数值规模便是0~499。
histogram_high_
与histogram_low_
的效果类似,但它的类型是 std::map,key表明帧距离数值,value表明次数。
InterframeDelay95PercentileInMs
调用的是rtc::HistogramPercentileCounter
的GetPercentile()
方法,传入参数0.95f
。它的意图是跳过悉数计数total_elements_
的 95% 减 1 今后,寻找剩下部分里最小的帧距离数值。留意这里的 95% 是帧距离的发生总次数,不是帧距离数值。
下面举个比如:
测验帧距离数值:{ 10, 20, 10, 30, 10, 50, 30, 70, 225, 10, 110, 120, 530, 145, 15, 560, 127, 138, 15, 200 };
悉数添加到 rtc::HistogramPercentileCounter 后的摆放:
---------- histogram_low_:
[10] = 4
[15] = 2
[20] = 1
[30] = 2
[50] = 1
[70] = 1
[110] = 1
[120] = 1
[127] = 1
[138] = 1
[145] = 1
[200] = 1
[225] = 1
---------- long_tail_boundary_:500
---------- histogram_high_:
<530, 1>
<560, 1>
依照上面测验序列,(20 x 0.95) - 1 = 18,跳过18个今后,首个帧距离是 530,
所以 InterframeDelay95PercentileInMs 的结果便是 530。表明 95% 的帧距离都小于 530。
ReceivedWidthInPixels
拉取的视频流分辨率的均匀宽度。由于长途视频流推流分辨率或许发生变化,这里输出的是均匀值,而非其中的某一次。它的类型是rtc::SampleCounter
。
ReceivedHeightInPixels
拉取的视频流分辨率的均匀高度。其他与ReceivedWidthInPixels
相同,不再赘述。
MediaBitrateReceivedInKbps
均匀接纳码率。接纳的字节数来自 VideoReceiveStream::Stats
的成员变量 total_media_bytes
。在ReceiveStatisticsProxy::OnCompleteFrame
会对接纳字节数进行累加,终究作为核算均匀接纳码率的分子。