通过 Janus 从 ffmpeg 流式传输的 H264 WebRTC 视频在播放时非常不稳定
H264 WebRTC video streamed from ffmpeg through Janus is very choppy on playback
尝试通过以下链流式传输视频:h264/mp4 本地实例存储上的文件 (AWS)->ffmpeg->rtp->同一实例上的 Janus->WebRTC 播放 (Chrome/mac)。即使 none 的资源似乎过载(CPU/memory/network 所涉及的任何系统上的带宽),生成的视频也是断断续续的。我也用 Coturn TURN 服务器,它也完全没有负载,带宽充足。
尝试切换编解码器,除了 vp8 之外没有任何帮助,它虽然工作(有点 - 不稳定仍然存在,但非常罕见且可以接受),导致如此高的 CPU 消耗,实际上这是不可接受的.
ffmpeg -re -stream_loop -1 -i ./short.mp4 -s 426x240 -c:v libx264 -profile:v baseline -b:v 1M -r 24 -g 60 -an -f rtp rtp://127.0.0.1:5004
生成的 SDP 是:
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 58.20.100
m=video 5004 RTP/AVP 96
b=AS:1000
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1
stream 是用 Janus API as
设置的
"janus" : "message",
"transaction" : 'Transaction',
"body": {
"request" : "create",
"type" : "rtp",
"id" : newId,
"name": streamId+newId,
"audio": false,
"video": true,
"description" : streamId+newId,
"videoport" : 5000+newId*4,
"videopt" : 96,
"videortpmap": "H264/90000",
"secret" : "adminpwd"
}
}
尝试了 bw 的各种选项,完全没有帮助。将 -g(GOP 大小)更改为较低的值可以使断断续续的持续时间更短但更频繁。在 -g 3 或 4 时,这是可以接受的,但可以预见的是,可容忍质量的比特率会变得疯狂。
预期结果:视频播放流畅。
我的理论是它可能是以下之一:
要么 ffmpeg 以缓冲区太小的方式提供数据,因此有时 Janus 需要在数据包尚未准备就绪时发送下一个数据包,从而导致缓冲区不足并导致中断 - 所以也许有一种方法可以使 ffmpeg 编码成某种短的(半秒左右?缓冲区来调节流量)。怎么样?
或者 H264 在 UDP 上的效果太差了,我无能为力。然后我必须切换到 TCP,但到目前为止,尝试这样做一直没有成功。
ffmpeg 针对分块输出帧进行了优化,而不是针对输出单个编码帧进行了优化。多路复用器,在你的例子中是 rtp 多路复用器,通常在刷新输出之前缓冲数据。因此 ffmpeg 并未针对需要或多或少 frame-by-frame 输出的 real-time 流进行优化。然而,WebRTC 确实需要到达 real-time 的帧,因此如果成束发送帧,它可能会丢弃 "late" 帧,因此会出现断断续续的情况。
但是,ffmpeg 中有一个选项,可以将 muxer 的缓冲区大小设置为 0,效果很好。它是:
-max_delay 0
此外,对于 WebRTC,您希望禁用 b-frames 并将 SPS-PPS 附加到每个关键帧:
-bf 0 +global_header -bsf:v "dump_extra=freq=keyframe"
这个解决方案被证明是显而易见的。 ffmpeg 将流作为 RTP 发送给 Janus,Janus 将其进一步发送给观众,显然,作为 SRTP,因为这是 WebRTC,并且始终是加密的。它向每个数据包添加了一堆字节作为加密开销。在某些情况下,这意味着数据包通过 MTU 并被丢弃 - 每次发生这种情况时,视频中都会出现明显的抖动。
简单添加 ?pkt_size=1300 到 ffmpeg 的输出 RTP URL 解决了问题。
感谢 Meetecho 的 Lorenzo Miniero 解决了这个问题。
尝试通过以下链流式传输视频:h264/mp4 本地实例存储上的文件 (AWS)->ffmpeg->rtp->同一实例上的 Janus->WebRTC 播放 (Chrome/mac)。即使 none 的资源似乎过载(CPU/memory/network 所涉及的任何系统上的带宽),生成的视频也是断断续续的。我也用 Coturn TURN 服务器,它也完全没有负载,带宽充足。
尝试切换编解码器,除了 vp8 之外没有任何帮助,它虽然工作(有点 - 不稳定仍然存在,但非常罕见且可以接受),导致如此高的 CPU 消耗,实际上这是不可接受的.
ffmpeg -re -stream_loop -1 -i ./short.mp4 -s 426x240 -c:v libx264 -profile:v baseline -b:v 1M -r 24 -g 60 -an -f rtp rtp://127.0.0.1:5004
生成的 SDP 是:
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 58.20.100
m=video 5004 RTP/AVP 96
b=AS:1000
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1
stream 是用 Janus API as
设置的 "janus" : "message",
"transaction" : 'Transaction',
"body": {
"request" : "create",
"type" : "rtp",
"id" : newId,
"name": streamId+newId,
"audio": false,
"video": true,
"description" : streamId+newId,
"videoport" : 5000+newId*4,
"videopt" : 96,
"videortpmap": "H264/90000",
"secret" : "adminpwd"
}
}
尝试了 bw 的各种选项,完全没有帮助。将 -g(GOP 大小)更改为较低的值可以使断断续续的持续时间更短但更频繁。在 -g 3 或 4 时,这是可以接受的,但可以预见的是,可容忍质量的比特率会变得疯狂。
预期结果:视频播放流畅。
我的理论是它可能是以下之一:
要么 ffmpeg 以缓冲区太小的方式提供数据,因此有时 Janus 需要在数据包尚未准备就绪时发送下一个数据包,从而导致缓冲区不足并导致中断 - 所以也许有一种方法可以使 ffmpeg 编码成某种短的(半秒左右?缓冲区来调节流量)。怎么样?
或者 H264 在 UDP 上的效果太差了,我无能为力。然后我必须切换到 TCP,但到目前为止,尝试这样做一直没有成功。
ffmpeg 针对分块输出帧进行了优化,而不是针对输出单个编码帧进行了优化。多路复用器,在你的例子中是 rtp 多路复用器,通常在刷新输出之前缓冲数据。因此 ffmpeg 并未针对需要或多或少 frame-by-frame 输出的 real-time 流进行优化。然而,WebRTC 确实需要到达 real-time 的帧,因此如果成束发送帧,它可能会丢弃 "late" 帧,因此会出现断断续续的情况。
但是,ffmpeg 中有一个选项,可以将 muxer 的缓冲区大小设置为 0,效果很好。它是:
-max_delay 0
此外,对于 WebRTC,您希望禁用 b-frames 并将 SPS-PPS 附加到每个关键帧:
-bf 0 +global_header -bsf:v "dump_extra=freq=keyframe"
这个解决方案被证明是显而易见的。 ffmpeg 将流作为 RTP 发送给 Janus,Janus 将其进一步发送给观众,显然,作为 SRTP,因为这是 WebRTC,并且始终是加密的。它向每个数据包添加了一堆字节作为加密开销。在某些情况下,这意味着数据包通过 MTU 并被丢弃 - 每次发生这种情况时,视频中都会出现明显的抖动。
简单添加 ?pkt_size=1300 到 ffmpeg 的输出 RTP URL 解决了问题。
感谢 Meetecho 的 Lorenzo Miniero 解决了这个问题。