gstreamer webrtc h264 播放在浏览器中几帧后停止
gstreamer webrtc h264 playback stops after few frames in browser
我需要帮助来调试概率问题。我构建了一个 gstreamer 管道以将 NVENC 编码的 h264 比特流(仅限视频)流式传输到浏览器。浏览器很少能正常播放。在大多数情况下,只渲染几帧,然后图片就会冻结。
NVENC 设置遵循“https://cloud.google.com/solutions/gpu-accelerated-streaming-using-webrtc”,它们是 h264 高配置和低延迟高质量 & NVENC_INFINITE_GOPLENGTH(已经尝试了一些设置,例如rateControlMode/enableVFR/sliceMode/repeatSPSPPS/outputAUD 但没有帮助)。在 运行 时间,NVENC 将实时渲染的 opengl fbo 纹理编码为 h264 比特流,并通过 appsrc 将它们推送到 gstreamer。当前纹理大小为 512x512 并以 10/20/30 fps 的速度进给。
我使用 gstreamer 1.18.2,管道定义为“appsrc name=nvenc_src do-timestamp=1 !video/x-h264, stream-format=byte-stream, alignment=au ! rtph264pay aggregate-mode=zero-latency !queue !application/x-rtp,media=video,encoding-name=H264,payload=123 !webrtcbin bundle-policy=max-compat name=backend_webrtc".
gstreamer 部分代码遵循 sendrecv 示例(用 websocketpp 替换 libsoup 并删除 recv 逻辑)。
应用程序构建为 MSVC 2019 32 位。浏览器解码器是 NVDEC。同一台PC(windwos 10,gtx1060,驱动程序版本460.89)上的exe应用程序和js代码运行。我试过 Chrome(87.0.4280.88) 和 edge(87.0.664.66)。我还尝试了 运行ning android(chrome) 和 ios(safari) 中的 js 代码,得到了相同的结果。
可以得出结论,NVENC 生成 'correct' h264 比特流。我将原始 h264 比特流转储到文件中。该文件在 VLC 中正常播放。我还尝试将转储的 h264 比特流推送到 gstreamer 中。冻结的问题仍然存在。
画面冻结后,播放永远无法恢复。浏览器的 'webrtc-internals' 显示 bytes/headerBytes/packests_Received 继续增长,而 frameReceived/framesDecoded/framesDropped 保持不变。
由于按位相同的 h264 帧在不同 运行 时表现不同,我猜 rtp 时间戳可能会导致问题。我已经尝试将 appsrc 的 do-timestamp 设置为 0 并手动设置 gstbuffer 的 PTS 但它没有帮助。
以下是您需要注意的几点:
- 无限 GOP 将不起作用 - 您必须将 NVENC 配置为每 30 - 60 帧发送一个关键帧。
- 当然,SPS-PPS NAL 必须出现在每个关键帧之前。
- 禁止 B 帧:WebRTC 不支持它们,因为它们会增加延迟。
- NAL 之间的启动代码必须是 3 字节启动代码:WebRTC 不遵守 2 字节启动代码。我们之前遇到过这个问题,不得不手动更正启动代码。
感谢user1390208的好心提醒,我使用了h264分析器工具来检查dumped bitstreams并找出漏洞。
浏览器支持无限 GOP。但它需要关键帧和 SPS-PPS 来从错误中恢复。在启动过程中很可能需要重新发送。所以一个快速的解决方案是在js检测到fps为0后重新发送关键帧和SPS-PPS,并通过webrtc数据通道向gstreamer发送重新发送请求。
我找不到答案的原因有两个:
我在调用nvEncEncodePicture之前没有设置encodePicFlags。 NVENC 始终生成无限 GOP,无论 gopLength 和 frameIntervalP 设置为所有 I 还是 I&P。有许多与 GOP 相关的参数,现在看起来让我感到困惑。在我当前的代码中,获得所需 GOP 控制的唯一方法是在调用 nvEncEncodePicture 之前设置 NV_ENC_PIC_PARAMS::encodePicFlags。请注意,我使用 NV_ENC_PRESET_LOW_LATENCY_HQ_GUID & NV_ENC_H264_PROFILE_HIGH_GUID 这可能会导致始终生成无限 GOP(未设置 encodePicFlags 时)? NVENC 在设置 gopLength & frameIntervalP & repeatSPSPPS 时报告没有错误,所以我认为当 GOP 全部是 I 帧并且 SPS-PPS 没有帮助时也会出现这个问题。
Infinity GOP 在启动过程中并不总是在浏览器中引起上述问题。
所以在我用分析工具检查h264比特流之前,在我看来即使是全关键帧+SPS-PPS比特流也有这个概率问题。
顺便说一句,NVENC 生成 4 字节的起始码。
我需要帮助来调试概率问题。我构建了一个 gstreamer 管道以将 NVENC 编码的 h264 比特流(仅限视频)流式传输到浏览器。浏览器很少能正常播放。在大多数情况下,只渲染几帧,然后图片就会冻结。
NVENC 设置遵循“https://cloud.google.com/solutions/gpu-accelerated-streaming-using-webrtc”,它们是 h264 高配置和低延迟高质量 & NVENC_INFINITE_GOPLENGTH(已经尝试了一些设置,例如rateControlMode/enableVFR/sliceMode/repeatSPSPPS/outputAUD 但没有帮助)。在 运行 时间,NVENC 将实时渲染的 opengl fbo 纹理编码为 h264 比特流,并通过 appsrc 将它们推送到 gstreamer。当前纹理大小为 512x512 并以 10/20/30 fps 的速度进给。
我使用 gstreamer 1.18.2,管道定义为“appsrc name=nvenc_src do-timestamp=1 !video/x-h264, stream-format=byte-stream, alignment=au ! rtph264pay aggregate-mode=zero-latency !queue !application/x-rtp,media=video,encoding-name=H264,payload=123 !webrtcbin bundle-policy=max-compat name=backend_webrtc".
gstreamer 部分代码遵循 sendrecv 示例(用 websocketpp 替换 libsoup 并删除 recv 逻辑)。
应用程序构建为 MSVC 2019 32 位。浏览器解码器是 NVDEC。同一台PC(windwos 10,gtx1060,驱动程序版本460.89)上的exe应用程序和js代码运行。我试过 Chrome(87.0.4280.88) 和 edge(87.0.664.66)。我还尝试了 运行ning android(chrome) 和 ios(safari) 中的 js 代码,得到了相同的结果。
可以得出结论,NVENC 生成 'correct' h264 比特流。我将原始 h264 比特流转储到文件中。该文件在 VLC 中正常播放。我还尝试将转储的 h264 比特流推送到 gstreamer 中。冻结的问题仍然存在。
画面冻结后,播放永远无法恢复。浏览器的 'webrtc-internals' 显示 bytes/headerBytes/packests_Received 继续增长,而 frameReceived/framesDecoded/framesDropped 保持不变。
由于按位相同的 h264 帧在不同 运行 时表现不同,我猜 rtp 时间戳可能会导致问题。我已经尝试将 appsrc 的 do-timestamp 设置为 0 并手动设置 gstbuffer 的 PTS 但它没有帮助。
以下是您需要注意的几点:
- 无限 GOP 将不起作用 - 您必须将 NVENC 配置为每 30 - 60 帧发送一个关键帧。
- 当然,SPS-PPS NAL 必须出现在每个关键帧之前。
- 禁止 B 帧:WebRTC 不支持它们,因为它们会增加延迟。
- NAL 之间的启动代码必须是 3 字节启动代码:WebRTC 不遵守 2 字节启动代码。我们之前遇到过这个问题,不得不手动更正启动代码。
感谢user1390208的好心提醒,我使用了h264分析器工具来检查dumped bitstreams并找出漏洞。
浏览器支持无限 GOP。但它需要关键帧和 SPS-PPS 来从错误中恢复。在启动过程中很可能需要重新发送。所以一个快速的解决方案是在js检测到fps为0后重新发送关键帧和SPS-PPS,并通过webrtc数据通道向gstreamer发送重新发送请求。
我找不到答案的原因有两个:
我在调用nvEncEncodePicture之前没有设置encodePicFlags。 NVENC 始终生成无限 GOP,无论 gopLength 和 frameIntervalP 设置为所有 I 还是 I&P。有许多与 GOP 相关的参数,现在看起来让我感到困惑。在我当前的代码中,获得所需 GOP 控制的唯一方法是在调用 nvEncEncodePicture 之前设置 NV_ENC_PIC_PARAMS::encodePicFlags。请注意,我使用 NV_ENC_PRESET_LOW_LATENCY_HQ_GUID & NV_ENC_H264_PROFILE_HIGH_GUID 这可能会导致始终生成无限 GOP(未设置 encodePicFlags 时)? NVENC 在设置 gopLength & frameIntervalP & repeatSPSPPS 时报告没有错误,所以我认为当 GOP 全部是 I 帧并且 SPS-PPS 没有帮助时也会出现这个问题。
Infinity GOP 在启动过程中并不总是在浏览器中引起上述问题。
所以在我用分析工具检查h264比特流之前,在我看来即使是全关键帧+SPS-PPS比特流也有这个概率问题。
顺便说一句,NVENC 生成 4 字节的起始码。