当前位置: 首页 > 工具软件 > mediasoup > 使用案例 >

mediasoup client与server交互过程

严兴旺
2023-12-01

mediasoup 不提供任何信令协议来通信客户端和服务器。 由应用程序使用 WebSocket、HTTP 或任何一种通信方式进行通信,并在客户端和服务器之间交换 mediasoup 相关的参数、请求/响应和通知。 在大多数情况下,这种通信必须是双向的,因此通常需要全双工通道。 但是,应用程序可以将相同的通道重用于非 mediasoup 相关的消息交换(例如身份验证过程、聊天消息、文件传输以及应用程序希望实现的任何内容)。

项目:https://github.com/versatica/mediasoup-demo

这是 mediasoup 作者制作的“官方” mediasoup 演示。 mediasoup-demo 有一个客户端 Web 应用程序和一个服务器端 Node.js 应用程序:客户端是一个 React 应用程序,它使用 mediasoup-client 和 protoo-client 以及其他库。
服务器端是一个使用 mediasoup 和 protoo-server 的 Node.js 应用程序。
protoo 是一个用于客户端和服务器端的 JavaScript 库,它为客户端提供了一种通过 WebSocket 连接到共享房间的简单方法。 API 提供双向的请求/响应事务和通知。
作为替代(有很多)读者可能更习惯于 socket.io。Example is as follow:

Project: https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms

Example website for multi-party video/audio/screen conferencing using mediasoup. This project is intended to better understand how mediasoup works with a simple example

const io = require('socket.io')(httpsServer)

假设我们的 JavaScript 或 C++ 客户端应用程序实例化 mediasoup-client 设备或 libmediasoupclient 设备对象以连接到 mediasoup 路由器(已在服务器中创建)并通过 WebRTC 发送和接收媒体。

信令和对等体
应用程序可以使用 WebSocket 并将每个经过身份验证的 WebSocket 连接与“对等点”相关联。

设备加载
客户端应用程序通过为其提供服务器端 mediasoup 路由器的 RTP 功能来加载其 mediasoup 设备。

创建传输
mediasoup-client 和 libmediasoupclient 都需要单独的 WebRTC 传输来发送和接收。通常,客户端应用程序会提前创建这些传输,甚至在希望发送或接收媒体之前。

对于发送媒体:

必须首先在 mediasoup 路由器中创建 WebRTC 传输:router.createWebRtcTransport()。
然后在客户端应用程序中复制:device.createSendTransport()。
客户端应用程序必须订阅本地传输中的“连接”和“生产”事件。
对于接收媒体:

必须首先在 mediasoup 路由器中创建 WebRTC 传输:router.createWebRtcTransport()。
然后在客户端应用程序中复制:device.createRecvTransport()。
客户端应用程序必须订阅本地传输中的“连接”事件。
如果这些传输需要 SCTP(WebRTC 中的 AKA DataChannel),则必须在其中启用 enableSctp(使用适当的 numSctpStreams)和其他与 SCTP 相关的设置。

制作媒体
创建发送传输后,客户端应用程序可以在其上生成多个音频和视频轨道。

应用程序获取轨道(例如,通过使用 navigator.mediaDevices.getUserMedia() API)。
它在本地发送传输中调用 transport.produce()。
如果这是对 transport.produce() 的第一次调用,则传输将发出“connect”。
传输将发出“produce”,因此应用程序会将事件参数传输到服务器,并在服务器端创建一个 Producer 实例。
最后 transport.produce() 将在客户端使用 Producer 实例。

消费媒体
创建接收传输后,客户端应用程序可以在其上使用多个音频和视频轨道。然而顺序是相反的(这里必须首先在服务器中创建消费者)。

客户端应用程序向服务器发送它的 device.rtpCapabilities 信号(它可能已经提前完成了)。
服务器应用程序应检查远程设备是否可以消费特定的生产者(即,它是否支持生产者媒体编解码器)。它可以通过使用 router.canConsume() 方法来完成。
然后服务器应用程序调用 WebRTC 传输中的 transport.consume() 为客户端创建用于接收媒体,从而生成服务器端消费者。
正如 transport.consume() 文档中所解释的,强烈建议使用 paused: true 创建服务器端消费者,并在远程端点创建后恢复它。
服务器应用程序将消费者信息和参数传输到远程客户端应用程序,远程客户端应用程序在本地接收传输中调用 transport.consume()。
如果这是对 transport.consume() 的第一次调用,则传输将发出“connect”。
最后 transport.consume() 将在客户端使用 Consumer 实例。

生产数据(DataChannels)
创建发送传输后,客户端应用程序可以在其上生成多个 DataChannel。

应用程序在本地发送传输中调用 transport.produceData()。
如果这是对 transport.produceData() 的第一次调用,则传输将发出“connect”。
传输将发出“producedata”,因此应用程序会将事件参数传输到服务器,并在服务器端创建一个 DataProducer 实例。
最后 transport.produceData() 将在客户端使用 DataProducer 实例。

消费数据(DataChannels)
创建接收传输后,客户端应用程序可以在其上使用多个 DataChannel。 然而顺序是相反的(这里必须首先在服务器中创建消费者)。

服务器应用程序调用客户端创建用于接收的 WebRTC 传输中的 transport.consumeData(),从而生成服务器端 DataConsumer。
服务器应用程序将消费者信息和参数传输到客户端应用程序,客户端应用程序在本地接收传输中调用 transport.consumeData()。
如果这是对 transport.consumeData() 的第一次调用,则传输将发出“connect”。
最后 transport.consumeData() 将在客户端使用 DataConsumer 实例。

Communicating Actions and Events

当传输、生产者、消费者、数据生产者或数据消费者在客户端或服务器端关闭时(例如,通过在其上调用 close()),应用程序应向另一端发出其关闭信号,另一端也应在客户端或服务器端调用 close() 对应的实体。 此外,服务器端应用程序应该监听以下关闭事件并通知客户端:

  • Transport “routerclose”. The client should call close() on the corresponding local transport.
  • Producer “transportclose”. The client should call close() on the corresponding local producer.
  • Consumer “transportclose”. The client should call close() on the corresponding local consumer.
  • Consumer “producerclose”. The client should call close() on the corresponding local consumer.
  • DataProducer “transportclose”. The client should call close() on the corresponding local data producer.
  • DataConsumer “transportclose”. The client should call close() on the corresponding local data consumer.
  • DataConsumer “dataproducerclose”. The client should call close() on the corresponding local data consumer.

在客户端或服务器端暂停 RTP 生产者或消费者时也会发生同样的情况。 该动作必须向另一方发出信号。 此外,服务器端应用程序应该监听以下事件并通知客户端:

  • Consumer “producerpause”. The client should call pause() on the corresponding local transport.
  • Consumer “producerresume”. The client should call resume() on the corresponding local transport (unless the consumer itself was also paused on purpose).

当使用simulcast 或 SVC 时,应用程序可能对在客户端和服务器端消费者之间发送首选层和有效层感兴趣。服务器端应用程序通过 consumer.setPreferredLayers() 设置消费者首选层。
服务器端消费者订阅“layerschange”事件并通知客户端应用程序正在传输的有效层。

FFmpeg 和 GStreamer(以及任何其他类似软件)都可用于将媒体注入 mediasoup 路由器或从 mediasoup 路由器消费媒体(用于记录目的、转码、使用 HLS 流式传输等)。

这可以通过创建服务器端普通传输(通过 router.createPlainTransport())然后使用适当的参数调用它的生产()或消费()来完成。

从外部端点制作媒体 (RTP In)
如果您希望使用外部工具(例如 FFmpeg 或 GS​​treamer)在 mediasoup 路由器中生成媒体,或者让 mediasoup 接收其他 RTP 源生成的媒体:

检查您的 mediasoup router.rtpCapabilities,因为它们确定 mediasoup 可以接收的媒体类型(编解码器配置、RTCP 功能、RTP 标头扩展等)。
在 mediasoup 中创建(如果尚未创建)普通传输,并为 RTP 获取其本地 IP 和端口(如果您的外部端点不支持 RTCP-mux,则可以选择为 RTCP)。
确定外部端点的 RTP 设置(SSRC 值、编解码器负载类型、RTCP 功能等)并创建 RtpSendParameters 对象。这些 RTP 参数必须与您的端点将发送到 mediasoup 的内容相匹配。
使用这些 RTP 参数在普通传输之上创建一个生产者(通过 transport.produce())。
指示您的外部端点将媒体发送到 mediasoup 普通传输的本地 IP 和端口。

在外部端点中使用媒体(RTP 输出)
如果您希望将生产者的媒体路由到外部 RTP 设备或端点(例如 FFmpeg 或 GS​​treamer):

在 mediasoup 中创建(如果尚未创建)普通传输,并为 RTP 获取其本地 IP 和端口(如果您的媒体端点不支持 RTCP-mux,则可以选择为 RTCP)。
检查您的 mediasoup router.rtpCapabilities 并使用您的外部端点支持的 RTP 功能创建它们的子集。保持相同的编解码器 preferredPayloadType 值和 RTP 标头扩展 preferredId 值至关重要。
在普通传输之上创建一个消费者(通过 transport.consume()),并使用相应的 producerId 和生成的外部端点的 rtpCapabilities。
获取 consumer.rtpParameters 和传输本地 RTP IP 和端口,并指示您的外部端点根据这些参数使用 RTP。
如果您的端点需要 SDP,您可能需要基于这些传输和 RTP 参数构建“远程”SDP 产品。
或者您可能需要告诉您的外部端点媒体源参数(通过 FFmpeg 或 GS​​treamer 命令行参数)。

支持 SRTP 的端点指南
从 mediasoup 3.5.0 开始,管道和普通传输都支持 SRTP。 有些端点不支持 WebRTC,但支持 SRTP。 通过启用 SRTP 进行加密 RTP 传输,这些端点可以通过普通传输连接到 mediasoup。

Node.js 中的 DataChannel 终止指南
只需使用 router.createDirectTransport() 创建一个 DirectTransport,然后在其上创建使用 directTransport.consumeData() 创建一个 DataConsumer,它将接收 WebRTC 端点发送的数据消息。
新的 dataConsumer.on('message') 事件将触发这些接收到的消息,以便 Node.js 应用程序可以处理它们。
在另一个方向,使用 directTransport.produceData() 在 Node.js 中创建一个 DataProducer 并让 WebRTC 对等点像往常一样使用它。
然后使用 dataProducer.send() 方法向他们发送文本或二进制消息。

 类似资料: