DmtpPixStream像素流传输
定义
TouchSocketPro.Dmtp.PixStreamdotnet add package TouchSocketPro.Dmtp.PixStream一、说明
DmtpPixStream 是基于 DMTP 协议的像素流图像传输扩展,适用于需要将画面(摄像头、屏幕截图、工业相机帧等)从一个终端实时传输到另一个终端的场景。
三种典型拓扑:
| 拓扑 | 说明 |
|---|---|
| 服务器采集 → 客户端显示 | 服务器持有 Provider,客户端直连并拉取帧 |
| 客户端采集 → 服务器显示 | 客户端持有 Provider,服务器主动向该客户端发起会话并拉取帧 |
| 客户端采集 → 客户端显示(路由) | 服务器作为纯路由节点,采集端与接收端均为 TcpDmtpClient,接收端通过采集端 ID 路由寻址 |
第三种路由拓扑与 DmtpRpc 路由调用完全一致。前两种是直连模式,无需路由配置。
核心传输机制:
- 首次请求:采集端返回完整帧(Full Frame),包含全部像素数据。
- 后续请求:若启用差分传输,采集端仅返回相对上一帧发生变化的像素块(Delta Frame),接收端将变化块叠加到本地缓存画面,大幅降低网络带宽消耗。
二、功能特性
| 特性 | 说明 |
|---|---|
| 完整帧传输 | 首次请求或关闭差分传输时,一次传输完整画面 |
| 增量差分传输 | 仅传输变化像素块,带宽利用率高 |
| JPEG 压缩 | 可配置 JPEG 质量,进一步减少传输数据量 |
| 多会话隔离 | 每个接收端会话独立维护差分帧状态 |
| 三种拓扑 | 直连(服务器采集)、直连(客户端采集)、路由(客户端间转发)均支持 |
| 会话审批 | 可通过插件拦截并审批会话创建请求 |
三、场景一:服务器采集,客户端直连显示
这是最简单的直连场景:服务器持有 IPixStreamProvider,客户端直接连接后调用 CreatePixStreamSessionAsync(request) 拉取帧,无需 targetId。
3.1 服务器配置
服务器注册 UseDmtpPixStream 并设置 Provider:
3.2 推送帧数据
服务器采集到新帧后调用 UpdateFrame 推入缓冲区:
UpdateFrame 接受 Image<Bgr24> 类型(来自 ImageSharp)。示例使用随机测试帧,实际项目中替换为相机 SDK 回调、屏幕截图等任意来源即可。
3.3 客户端直连配置与创建会话
客户端注册 UseDmtpPixStream(不设 Provider)后,直接调用无 targetId 的重载创建会话:
四、场景二:客户端采集,服务器主动拉取
此场景中客户端持有 IPixStreamProvider,服务器在客户端连接后,通过 TcpDmtpSessionClient.GetDmtpPixStreamActor() 主动向该客户端发起会话来拉取画面。
4.1 服务器配置(接收端)
服务器不设 Provider,在 IDmtpConnectedPlugin 生命周期事件中主动向刚连接的客户端发起会话:
4.2 客户端配置(采集端)
客户端注册 UseDmtpPixStream 并设置 Provider,连接服务器即可等待服务器主动拉取:
五、场景三:两个客户端间路由传输
此场景下路由服务器作为纯中间节点,采集端 和 接收端 都是普通的 TcpDmtpClient,接收端通过采集端的 DMTP ID 路由寻址。拓扑与 DmtpRpc 路由完全一致。
5.1 路由服务器配置
服务器注册 UseDmtpPixStream(不设 Provider)、启用 AddDmtpRouteService 并允许转发:
服务器 UseDmtpPixStream() 不设 Provider,仅用于识别和转发像素流协议报文,不参与任何帧数据的生产。
5.2 采集端(TcpDmtpClient)配置
采集端是普通客户端,连接路由服务器后设置 Provider 并持续推送帧。其 DMTP ID 即为接收端路由寻址的目标:
5.3 接收端(TcpDmtpClient)配置与路由会话
接收端也是普通客户端,连接路由服务器后使用 带 targetId 的重载 创建会话:
六、接收和处理帧数据
无论哪种拓扑,接收到的 PixFrame 结构相同:
| 属性 | 类型 | 说明 |
|---|---|---|
FrameType | PixFrameType | Full(完整帧)或 Delta(增量帧) |
Width / Height | int | 图像尺寸(像素) |
Blocks | IReadOnlyList<PixBlock> | 本次传输的像素块集合 |
TransferredBytes | long | 本次实际传输的字节数 |
每个 PixBlock 表示图像中的一个矩形区域:
| 属性 | 类型 | 说明 |
|---|---|---|
X / Y | int | 块左上角坐标 |
Width / Height | int | 块尺寸 |
PixelFormat | PixelFormat | 编码格式:Bgr24 或 Jpeg |
RawData | ReadOnlyMemory<byte> | 原始像素数据 |
Bgr24Data | ReadOnlyMemory<byte> | 解码后的 BGR24 数据(JPEG 时惰性解码) |
以下示例展示如何在 WPF 中将帧块写入 WriteableBitmap 渲染:
PixFrame 实现了 IDisposable,内部像素块使用内存池分配。框架在 OnReceived 回调返回后自动释放;若需异步持有,应在回调内将所需数据解码拷贝后再返回。
七、差分传输
差分传输(Delta Transfer)是 DmtpPixStream 的核心优化机制。启用后,采集端对相邻两帧进行逐块比较(按 BlockSize 分块,默认 64 像素),仅将发生变化的块打包传输。
- 对于静态画面,增量帧可能只包含少量块,带宽消耗极低。
- 对于高动态场景,增量帧大小可能接近完整帧,差分传输优势减弱。
可通过 IPixStreamSession.EnableDeltaTransfer 在运行时动态切换。DmtpPixStreamOption.BlockSize 可调整块大小,块越小差异检测越精细,但计算开销越大,默认 64 像素在大多数场景下均有良好表现。
八、会话审批
任意场景下,采集端所在节点(服务器或客户端)均可实现 IDmtpPixStreamCreatingPlugin 插件来审批或拒绝会话创建请求。事件参数 DmtpPixStreamCreatingEventArgs 提供了源端 ID、目标端 ID、会话 ID 及请求参数。
将 e.IsPermitOperation 设为 false(默认值)即可拒绝,并可通过 e.Message 设置拒绝原因。