处理 chrome 的 rtp 流需要哪些 gstreamer 管道设置?

Posted

技术标签:

【中文标题】处理 chrome 的 rtp 流需要哪些 gstreamer 管道设置?【英文标题】:which gstreamer pipeline settings are needed to process chrome's rtp stream? 【发布时间】:2014-09-26 17:04:42 【问题描述】:

我正在使用 webrtc 将网络摄像头从浏览器流式传输到以下设置工作的服务器:

    使用 firefox 和修改后的 echo-test html from janus gateway 我将网络摄像头流发送到 janus 服务器 janus 服务器使用修改后的 echotest 插件运行,该插件只是将 janus_videorecv_incoming_rtp() 中的给定 char *buf udp 流式传输到端口 5060,仅用于测试目的 (pretty much like this) 以下 gstreamer 命令行实际上会打开一个显示流视频的窗口

GST_DEBUG=p*:5 gst-launch-1.0 -vvv udpsrc caps="application/x-rtp,media=video,clock-rate=90000,payload=96" port=5060 ! rtpvp8depay ! vp8dec ! autovideosink

在修改后的 echo 测试 javascript 中,我从 sdp 答案中删除了几行,浏览器将收到如下内容:

//jsep.sdp = jsep.sdp.replace(/a=rtcp-mux[^\s]*\s*/g, '');
jsep.sdp = jsep.sdp.replace(/a=rtpmap[^\s]*\s*red[^\s]*\s*/g, '');
jsep.sdp = jsep.sdp.replace(/a=rtpmap[^\s]*\s*ulpfec[^\s]*\s*/g, '');
jsep.sdp = jsep.sdp.replace(/a=fmtp[^\r\n]*\r*\n*/g, '');
jsep.sdp = jsep.sdp.replace(/a=rtcp-fb[^\s]*\s*goog-remb[^\s]*\s*/g, '');

下面,可以找到适用于上述 gstreamer 命令的修改后的 firefox sdp 答案 但是,以同样的方式,修改后的 sdp 答案在 chrome 的情况下不起作用 我考虑过调整 gstreamer 上限中的有效载荷,但 32,33,96,100,120 不起作用

所以问题是:在 chrome 的情况下需要什么才能让它工作?

我还尝试添加 fir/pli 请求,例如来自 janus 的 videoroom.c 中的 suggested here

如果是 chrome,gstreamer 的输出是,命令只是在最后一行等待:

0:00:00.025791492 22279      0x1954b90 DEBUG               pipeline gstpipeline.c:219:gst_pipeline_init:<GstPipeline@0x1962180> set bus <bus2> on pipeline
Setting pipeline to PAUSED ...
0:00:00.029798090 22279      0x1954b90 DEBUG               pipeline gstpipeline.c:282:reset_start_time:<pipeline0> reset start_time to 0
Pipeline is live and does not need PREROLL ...
/GstPipeline:pipeline0/GstUDPSrc:udpsrc0.GstPad:src: caps = application/x-rtp, media=(string)video, clock-rate=(int)90000, payload=(int)96, encoding-name=(string)VP8-DRAFT-IETF-01
Setting pipeline to PLAYING ...
0:00:00.030045034 22279      0x1954b90 DEBUG               pipeline gstpipeline.c:377:gst_pipeline_change_state:<pipeline0> selecting clock and base_time
0:00:00.030053523 22279      0x1954b90 DEBUG               pipeline gstpipeline.c:398:gst_pipeline_change_state:<pipeline0> Need to update start_time
0:00:00.030058181 22279      0x1954b90 DEBUG               pipeline gstpipeline.c:403:gst_pipeline_change_state:<pipeline0> Need to update clock.
/GstPipeline:pipeline0/GstRtpVP8Depay:rtpvp8depay0.GstPad:src: caps = video/x-vp8, framerate=(fraction)0/1
/GstPipeline:pipeline0/GstVP8Dec:vp8dec0.GstPad:sink: caps = video/x-vp8, framerate=(fraction)0/1
0:00:00.030111345 22279      0x1954b90 DEBUG               pipeline gstpipeline.c:443:gst_pipeline_change_state:<pipeline0> start_time=0:00:00.000000000, now=33:52:04.529345754, base_time 33:52:04.529345754
/GstPipeline:pipeline0/GstRtpVP8Depay:rtpvp8depay0.GstPad:sink: caps = application/x-rtp, media=(string)video, clock-rate=(int)90000, payload=(int)96, encoding-name=(string)VP8-DRAFT-IETF-01
New clock: GstSystemClock

铬答案:

v=0
o=- 8913399741269897639 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS janus
m=audio 1 RTP/SAVPF 111 103 104 0 8 106 105 13 126
a=mid:audio
c=IN IP4 192.168.0.1
a=sendrecv
a=rtcp-mux
a=ice-ufrag:l0n9
a=ice-pwd:r1elT1Ew8lP3TNlzwAHUsC
a=ice-options:trickle
a=fingerprint:sha-256 C5:5F:DA:7D:84:47:B1:BF:6B:55:16:62:48:31:3E:D3:F1:7B:25:89:92:4A:4B:4D:4D:D9:D5:AF:EA:D8:15:44
a=setup:active
a=connection:new
a=rtpmap:111 opus/48000/2
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:126 telephone-event/8000
a=maxptime:60
a=s-s-rc:600390024 cname:janusaudio
a=s-s-rc:600390024 msid:janus janusa0
a=s-s-rc:600390024 mslabel:janus
a=s-s-rc:600390024 label:janusa0
a=candidate:1 1 udp 2013266431 192.168.0.1 45728 typ host
m=video 1 RTP/SAVPF 100 116 117 96
a=mid:video
c=IN IP4 192.168.0.1
a=sendrecv
a=rtcp-mux
a=ice-ufrag:l0n9
a=ice-pwd:r1elT1Ew8lP3TNlzwAHUsC
a=ice-options:trickle
a=fingerprint:sha-256 C5:5F:DA:7D:84:47:B1:BF:6B:55:16:62:48:31:3E:D3:F1:7B:25:89:92:4A:4B:4D:4D:D9:D5:AF:EA:D8:15:44
a=setup:active
a=connection:new
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtpmap:96 rtx/90000
a=s-s-rc-group:FID 3188003624 3419969288
a=s-s-rc:677441062 cname:janusvideo
a=s-s-rc:677441062 msid:janus janusv0
a=s-s-rc:677441062 mslabel:janus
a=s-s-rc:677441062 label:janusv0
a=candidate:1 1 udp 2013266431 192.168.0.1 45728 typ host
m=application 0 DTLS/SCTP 0
c=IN IP4 192.168.0.1

火狐回答:

v=0
o=Mozilla-SIPUA-32.0.3 11426 0 IN IP4 127.0.0.1
s=SIP Call
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS janus
m=audio 1 RTP/SAVPF 109 0 8 101
a=mid:audio
c=IN IP4 192.168.0.1
a=sendrecv
a=rtcp-mux
a=ice-ufrag:BRBU
a=ice-pwd:2W4fGNr//HejhiC4UIabW6
a=ice-options:trickle
a=fingerprint:sha-256 C5:5F:DA:7D:84:47:B1:BF:6B:55:16:62:48:31:3E:D3:F1:7B:25:89:92:4A:4B:4D:4D:D9:D5:AF:EA:D8:15:44
a=setup:active
a=connection:new
a=rtpmap:109 opus/48000/2
a=ptime:20
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=s-s-rc:3725983979 cname:janusaudio
a=s-s-rc:3725983979 msid:janus janusa0
a=s-s-rc:3725983979 mslabel:janus
a=s-s-rc:3725983979 label:janusa0
a=candidate:1 1 udp 2013266431 192.168.0.1 56574 typ host
m=video 1 RTP/SAVPF 120
a=mid:video
c=IN IP4 192.168.0.1
a=sendrecv
a=rtcp-mux
a=ice-ufrag:jZ5b
a=ice-pwd:dQQej9UIpPl5zuXBQNg3Nz
a=ice-options:trickle
a=fingerprint:sha-256 C5:5F:DA:7D:84:47:B1:BF:6B:55:16:62:48:31:3E:D3:F1:7B:25:89:92:4A:4B:4D:4D:D9:D5:AF:EA:D8:15:44
a=setup:active
a=connection:new
a=rtpmap:120 VP8/90000
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtcp-fb:120 ccm fir
a=s-s-rc:1425382999 cname:janusvideo
a=s-s-rc:1425382999 msid:janus janusv0
a=s-s-rc:1425382999 mslabel:janus
a=s-s-rc:1425382999 label:janusv0
a=candidate:2 1 udp 2013266431 192.168.0.1 39063 typ host
m=application 0 DTLS/SCTP 0
c=IN IP4 192.168.0.1

更新: 我修改了 sdp-answer,因此 firefox 和 chrome 几乎相同 除了我刚刚从 sdp-offer 复制的“o=”和“s=”行 v=0 o=- 7589782217972865757 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE audio video a=msid-semantic: WMS janus m=audio 1 RTP/SAVPF 111 a=mid:audio c=IN IP4 192.168.0.1 a=sendrecv a=rtcp-mux a=ice-ufrag:g0kZ a=ice-pwd:d5oEody1jqIzDYUdf1fg6t a=ice-options:trickle a=fingerprint:sha-256 C5:5F:DA:7D:84:47:B1:BF:6B:55:16:62:48:31:3E:D3:F1:7B:25:89:92:4A:4B:4D:4D:D9:D5:AF:EA:D8:15:44 a=setup:active a=connection:new a=rtpmap:111 opus/48000/2 a=s-s-rc:1038736511 cname:janusaudio a=s-s-rc:1038736511 msid:janus janusa0 a=s-s-rc:1038736511 mslabel:janus a=s-s-rc:1038736511 label:janusa0 a=candidate:1 1 udp 2013266431 192.168.0.1 51232 typ host m=video 1 RTP/SAVPF 100 a=mid:video c=IN IP4 192.168.0.1 a=sendrecv a=rtcp-mux a=ice-ufrag:g0kZ a=ice-pwd:d5oEody1jqIzDYUdf1fg6t a=ice-options:trickle a=fingerprint:sha-256 C5:5F:DA:7D:84:47:B1:BF:6B:55:16:62:48:31:3E:D3:F1:7B:25:89:92:4A:4B:4D:4D:D9:D5:AF:EA:D8:15:44 a=setup:active a=connection:new a=rtpmap:100 VP8/90000 a=rtcp-fb:100 ccm fir a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=rtcp-fb:100 goog-remb a=s-s-rc:2455978689 cname:janusvideo a=s-s-rc:2455978689 msid:janus janusv0 a=s-s-rc:2455978689 mslabel:janus a=s-s-rc:2455978689 label:janusv0 a=candidate:1 1 udp 2013266431 192.168.0.1 51232 typ host m=application 0 DTLS/SCTP 0 c=IN IP4 192.168.0.1

【问题讨论】:

在 chrome 中,并非所有映射都被删除。你为什么要删除a=rtcp-fb:100 goog-remb?更正视频媒体行上显示的 rtp 有效负载并删除不需要的rtx rtp 映射。那应该会让你走得更远。您确定 chrome 甚至会向您发送任何媒体吗?我猜它在答案上吓坏了,没有发送任何东西。如果它确实发送媒体,您可以分解 rtp 标头并查看有效负载以确保它是 VP8 我实际上删除了 goog-remb,因为我试图逐行删除以可能使某些东西起作用。实际上chrome正在发送数据,我的janus插件也正确转发,正如我通过wireshark看到的那样。关于“视频媒体行中的正确 rtp 有效负载”,您的意思是我应该像在 firefox 中那样设置 m=video 1 RTP/SAVPF 120 吗?分解 rtp 标头到底是什么意思?你会怎么做?感谢您的宝贵时间 该行应为m=video 1 RTP/SAVPF 100 以指示仅一个映射(对于 VP8)。那么只有 VP8 映射应该存在。 Some webrtc sdp pointers。您可以将缓冲区转换为 rtp 数据包,然后访问该类型。 rtp_header* packetheader = (rtp_header*) buf; packetheader-&gt;type; //payload m 行现已修复,我还从 janus_videoroom.c 插件复制了 sdp_template 并进行了一些调整。现在我对 Firefox 和 chrome 有一个相同的 sdp 答案。 firefox 正在使用上面的 gstreamer,chrome 不是。 packetheader->type 的值为 100 或 111 - 正如我所理解的视频或音频所预期的那样。 rtx 映射也被删除 好吧,如果您正在接收一个视频负载为 100 的视频源,它是 VP8 好的。我的猜测是 chrome 的关键帧问题正在发生。另外,尝试将接收 Gstreamer 管道更改为 udpsrc port=5060 caps="application/x-rtp, media=video, clock-rate=90000, encoding-name=VP8-DRAFT-IETF-01, payload=100" ! rtpvp8depay ! vp8dec ! ffmpegcolorspace ! autovideosink 【参考方案1】:

WebRTC 使用 DTLS-SRTP 强制加密(Chrome 仍支持非标准和显式 MUST-NOT-IMPLMENT SDES 密钥)。

您不能只向 webrtc 提供 RTP 流;它必须是带有初始 DTLS 连接的 DTLS-SRTP 流。

人们已经将 node.js 连接到 webrtc 浏览器,所以我想你需要的所有机器都在那里。

【讨论】:

我不确定我是否理解你的意思。您的意思是“不能只将 rtp 流提供给 webrtc”,即我将 rtp 从服务器发送到浏览器的情况?如果是,那不是我想要做的。我正在将 rtp 从 webrtc(浏览器)发送到服务器,并在服务器上尝试使用 gstreamer 向我显示流,主要用于调试/测试目的。关于 dtls,虽然我不知道所有细节,但我读到 janus(服务器)处理这个 数据包在到达 gstreamer 管道时被解密。 Janus 处理每个数据包的解密和解复用。【参考方案2】:

我已经更新了包含 the bidirectional streaming plugin 的 fork 以向您展示一个可行的示例(我在 debian jessie 上进行了测试)。

这是我对插件更改的建议

    在您从 chrome 请求关键帧之前,请确保您的 gstreamer 管道已设置为接收 当 webrtc 媒体准备好时请求你的关键帧(详见janus_bidirectional_streaming_setup_media函数) 不要使用rtpbin gstreamer 元素来处理传入的流。由于某种原因,它设置上限的方式并没有真正起作用,并且管道会崩溃。如果您确实获得了 rtp 数据包并且能够将它们发送到端口,那么以下管道可以正常工作:gst-launch-1.0 udpsrc port=&lt;your listener&gt; caps="application/x-rtp, clock-rate=90000, payload=100" ! rtpvp8depay ! vp8dec ! autovideosink sync=false async=false

理论上,直接将缓冲区推送到插件中的appsrc 应该也可以。

【讨论】:

【参考方案3】:

Kurento 媒体服务器 (KMS) 是完全基于 GStreamer 编写的 WebRTC 媒体服务器。 KMS 提供了一个 WebRtcEndpoint,实现了向/从 Web 浏览器发送/接收 WebRTC 流所需的所有协议和算法。 KMS 公开基于媒体元素和媒体管道的 API,这些 API 转换为 GStreamer 媒体管道。一般来说,您在 GStreamer 上拥有的所有功能也可以在 KMS 中使用。您可以在http://www.kurento.org查看KMS。

免责声明:我是 Kurento 开发团队的一员。

【讨论】:

什么管道特别适用于来自 Chrome 的 WebRTC 流的解密、解复用的 RTP 流?用户已经在使用 janus,因此仅链接另一种技术并不能真正回答问题。

以上是关于处理 chrome 的 rtp 流需要哪些 gstreamer 管道设置?的主要内容,如果未能解决你的问题,请参考以下文章

使用 gstreamer 播放传入的 RTP 视频流

udpsink 似乎没有流任何东西,但 fileink 工作

Gstreamer Gstreamer中通过UDP(RTP)远程播放MP3

C# - 捕获 RTP 流并发送到语音识别

视音频数据处理入门:UDP-RTP协议解析

视音频数据处理入门:UDP-RTP协议解析