1、问题描述
在进行推理延迟定位的时候,发现了视频解码延迟问题,使用相同的拉流SDK,最后的解码视频显示不能保持一致。流程如下图所示:
2、解决尝试
查阅资料,Nvidia提供了如下的减少延迟的参数:
由于SDK接口封装的很好,能操作的没啥东西。
// 减少显示缓存数量
videoParserParameters.ulMaxDisplayDelay = bLowLatency ? 0 : 2;
// 添加一帧标识符
packet.flags = CUVID_PKT_TIMESTAMP | CUVID_PKT_ENDOFPICTURE;
// 手动取出,原谅我这里什么都取不出来,timestamp号也没有
if (m_bForce_zero_latency && ((!pPicParams->field_pic_flag) || (pPicParams->second_field)))
{
CUVIDPARSERDISPINFO dispInfo;
memset(&dispInfo, 0, sizeof(dispInfo));
dispInfo.picture_index = pPicParams->CurrPicIdx;
dispInfo.progressive_frame = !pPicParams->field_pic_flag;
dispInfo.top_field_first = pPicParams->bottom_field_flag ^ 1;
LOG_INFO("come into HandlePictureDisplay. timestamp={0}?", dispInfo.timestamp);
HandlePictureDisplay(&dispInfo);
}
也查了一些官网的帖子,也没有任何发现什么解决方案。
查阅官方文档,发现了这么一句话:
大致意思是,Nvidia解码是数据驱动的,输入会将数据输入到一个队列里面,输出会将数据进行解码,队列的大小是4,只要不满,就一直向里面塞数据。
此时,我已经高度怀疑是这个队列导致的200ms的延迟了。可惜的是,这个队列没有任何接口可以控制,唯一可以刷新的是发送EOS流,而发送EOS流意味着解码器不再工作。
3、运行测试
运行日志中显示,第5帧的时候才触发了第一帧的输出,即输入队列塞满不再工作后,触发了解码操作。
目前,定位到了问题所在,但是没有解决方案!!!
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
更新 2024-05-28
参考连接(大佬):
https://forums.developer.nvidia.com/t/video-sdk-decoder-or-encoder-have-always-5-frames-buffer-dpb-buffer-or-some-other-frame-buffer/65848/4
之前一直怀疑是nvidia解码中内部缓存机制造成的解码延迟,今天再次对问题进行解决尝试,才发现问题在nvcuvid的显示回调pfnDisplayPicture的触发上。
在配置pfnDecodePicture 回调函数后,ulMaxDisplayDelay 即使设置为0,也会导致解码有3-4帧的延迟。
测试环境, CPU:i7 9700KF,GPU:3080TI
修改代码部分如下
关闭解码显示回调函数:
if (m_bForce_zero_latency)
{
bLowLatency = true;
}
videoParserParameters.ulMaxNumDecodeSurfaces = 1;
videoParserParameters.ulClockRate = 10000;
videoParserParameters.ulMaxDisplayDelay = bLowLatency ? 0 : 2; // 这里如果设置为2,则会由1到2帧的延迟
videoParserParameters.pUserData = this;
videoParserParameters.pfnSequenceCallback = HandleVideoSequenceProc;
videoParserParameters.pfnDecodePicture = HandlePictureDecodeProc;
// 如果是实时RTSP,没有B帧,可以直接关闭pfnDisplayPicture,设置为NULL,如果这里设置了回调函数,至少有3-4帧的延迟
videoParserParameters.pfnDisplayPicture = m_bForce_zero_latency ? NULL : HandlePictureDisplayProc;
对裸码流添加如下符号:
CUVIDSOURCEDATAPACKET packet = { 0 };
packet.flags = CUVID_PKT_TIMESTAMP | CUVID_PKT_ENDOFPICTURE; // 不设置CUVID_PKT_ENDOFPICTURE,会有一帧延迟
在HandlePictureDecode回调最后添加:
if (m_bForce_zero_latency && ((!pPicParams->field_pic_flag))) // || (pPicParams->second_field)))
{
CUVIDPARSERDISPINFO dispInfo;
memset(&dispInfo, 0, sizeof(dispInfo));
dispInfo.picture_index = pPicParams->CurrPicIdx;
dispInfo.progressive_frame = !pPicParams->field_pic_flag;
dispInfo.top_field_first = pPicParams->bottom_field_flag ^ 1;
std::unique_lock<std::shared_mutex> lk(thread_mutex);
// TODO 这里直接使用最新的时间戳,正常情况下这个时间戳是正确的,异常情况下,时间戳对不对不重要了
dispInfo.timestamp = m_push_date_node;
lk.unlock();
HandlePictureDisplay(&dispInfo); // 取数据函数,对应pfnDisplayPicture函数,这里自己触发
}
配置上述三处地方,就可以解决解码延迟问题,需要注意的是,这里只适合实时的RTSP流,每一帧码流数据都能解出一帧图像,不包含B帧。
至此,通过避开设置pfnDecodePicture回调方式,nvidia解码延迟问题解决。