最近在处理高帧率,高分辨率时遇到视频帧卡顿,抖动问题,记录处理过程,下面分析几个可能出现卡顿的流程
CaptureSinkFilter::ProcessCapturedFrame //循环执行采集 //
{
VideoCaptureImpl::IncomingFrame //分辨率格式安全检查 ,缩放
{
ConvertToI420 //格式转换
CropAndScaleFrom // 如果不是目标分辨率 缩放
VideoCaptureImpl::DeliverCapturedFrame//传递
{
_dataCallBack->OnIncomingCapturedFrame//传递到自定义的capture接收接口
{
OnIncomingCapturedFrame//自定义的采集接收接口
{
sink_->OnFrame//编码器接收接口
{
ViEEncoder::OnFrame //编码器接收到采集的视频帧,放入任务队列
{
incoming_frame.set_render_time_ms //设置采集时间
incoming_frame.set_ntp_time_ms //设置ntp ,rtcp sr用
incoming_frame.set_timestamp //设置时间戳,rtp用
encoder_queue_.PostTask //放入编码队列
}
}
}
}
}
}
}
这个过程除了格式错误,没有地方进行丢帧处理
前面采集的帧已经放入队列,后面编码线程从队列中取出数据,在取数据的时候有丢帧处理
TaskQueue::ProcessQueuedMessages //循环执行任务
{
ViEEncoder::EncodeTask::Run() //执行编码 本地显示
{
if (--vie_encoder_->posted_frames_waiting_for_encode_ == 0) //如果任务队列只有一帧,则执行编码,超过一帧的就丢弃
{
vie_encoder_->EncodeVideoFrame //执行编码
{
pre_encode_callback_->OnFrame //本地渲染
{
RTCD3dRenderer::OnFrame //执行渲染
}
EncoderPaused() //判断是否丢帧
}
}
else
{
++vie_encoder_->dropped_frame_count_ //丢弃,当cpu能力不足,造成队列堆积会丢帧处理
}
}
}
由此看来本地视频卡顿,可能有以下几个原因
1、cpu处理不及时,造成队列堆积,丢弃
2、渲染和编码一个线程,会不会存在因编码造成的渲染间隔不均匀?
3、显卡渲染有问题,渲染展现出的卡顿
4、摄像头采集到的就是不均匀的
配置:cpu: amd R5-2400G 集显,相当于gtx1030
摄像头:1080p60 usb3.0
现象:本地渲染出现卡顿
步骤1、怀疑是cpu处理不及时,丢帧,或者编码慢,导致渲染不均匀,先把编码逻辑去掉,只做渲染
void ViEEncoder::EncodeVideoFrame(const VideoFrame& video_frame,
int64_t time_when_posted_in_ms)
{
RTC_DCHECK_RUN_ON(&encoder_queue_);
if (pre_encode_callback_)
{
pre_encode_callback_->OnFrame(video_frame);
}
return; //在此直接返回,不做编码处理,只渲染
}
测试发现,依然卡顿,尤其是在背景是高亮的情况下
步骤2、确认不是cpu造成的,日志打印渲染,查看渲染时间
n:1559288807289 c:1226 du:12 ---------------------------
n:1559288807303 c:1240 du:14 ---------------------------
n:1559288807320 c:1644 du:17
n:1559288807337 c:1578 du:17
n:1559288807352 c:1413 du:15
n:1559288807371 c:1595 du:19 ---------------------------
n:1559288807386 c:1609 du:15
n:1559288807405 c:1906 du:19 ---------------------------
n:1559288807419 c:1233 du:14 ---------------------------
n:1559288807437 c:1720 du:18 ---------------------------
n:1559288807453 c:1348 du:16
n:1559288807471 c:1716 du:18 ---------------------------
n:1559288807489 c:1614 du:18 ---------------------------
n:1559288807503 c:1226 du:14 ---------------------------
n:1559288807521 c:1667 du:18 ---------------------------
n:1559288807537 c:1330 du:16
n:1559288807554 c:1582 du:17
n:1559288807572 c:1633 du:18 ---------------------------
n:1559288807586 c:1210 du:14 ---------------------------
n:1559288807603 c:1612 du:17
n:1559288807622 c:1795 du:19 ---------------------------
n:1559288807636 c:1518 du:14 ---------------------------
n:1559288807653 c:1749 du:17
n:1559288807671 c:1704 du:18 ---------------------------
可以看到帧率不稳定,间隔时间应该是16,17ms,但在明暗变化的时候,进一步确认是摄像头硬件问题 还是 逻辑问题
步骤3、在最接近摄像头采集出来的地方,日志打印
int32_t VideoCaptureImpl::IncomingFrame(
uint8_t* videoFrame,
size_t videoFrameLength,
const VideoCaptureCapability& frameInfo,
int64_t captureTime/*=0*/)
//在这个函数直接打印日志, 返回
1559289895617 capture time:2430 cost 17
1559289895634 capture time:2447 cost 17
1559289895650 capture time:2463 cost 16
1559289895667 capture time:2480 cost 17
1559289895684 capture time:2497 cost 17
1559289895700 capture time:2513 cost 16
1559289895717 capture time:2530 cost 17
1559289895817 capture time:2630 cost 100 error 2433--------------
1559289895834 capture time:2647 cost 17
1559289895851 capture time:2664 cost 17
1559289895867 capture time:2680 cost 16
1559289895884 capture time:2697 cost 17
1559289895901 capture time:2714 cost 17
1559289895917 capture time:2730 cost 16
1559289895934 capture time:2747 cost 17
1559289895951 capture time:2764 cost 17
1559289895967 capture time:2780 cost 16
1559289895984 capture time:2797 cost 17
1559289896001 capture time:2814 cost 17
1559289896017 capture time:2830 cost 16
1559289896034 capture time:2847 cost 17
1559289896051 capture time:2864 cost 17
1559289896067 capture time:2880 cost 16
总体来说比较均匀,偶尔一次比较大,可能是因为摄像头硬件问题,或者usb传输导致,暂且放过
怀疑是这个转换,有的摄像头采集出来的不是yuv420,需要做转换,这个转换需要时间
VideoCaptureImpl::IncomingFrame
{
ConvertToI420 //转换
VideoFrame captureFrame(buffer, 0, rtc::TimeMillis(), !apply_rotation ? _rotateFrame : kVideoRotation_0);//在此设置的capture 时间,是有问题的
}
正常情况下先转格式,再赋值时间没什么影响,但是在大分辨率先转换,再赋值,可能会有几ms的差异,会出现卡顿,稍后验证一下。我先写个渲染,加个平滑机制试试