You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用SignalR广播媒体流?.NET Core屏幕共享实现求助

.NET Core + SignalR 实现屏幕共享:MediaStream传输与广播方案

问题根源

你直接尝试通过SignalR传递MediaStream对象是行不通的——MediaStream是浏览器本地的媒体对象,无法被序列化为可传输的格式,SignalR无法直接处理这类对象的跨网络传输。

解决方案思路

需要将屏幕流拆解为可传输的媒体片段,通过SignalR分段发送,接收端再将这些片段拼接还原成可播放的流:

  • 前端用MediaRecorder捕获屏幕流的媒体片段(Blob格式)
  • 将Blob分段发送到SignalR Hub
  • 后端Hub直接转发这些片段给所有客户端
  • 接收端通过MediaSource和SourceBuffer拼接片段,实现流式播放

修改后的代码实现

1. 前端HTML(无需修改)

<div class="container">
    <div class="row p-1">
        <h1>WebRTC Screen Sharing Demo</h1>
        <hr>
        <button onclick="captureScreen()">Capture</button>
        <video id="local-video" muted autoplay></video>
    </div>
</div>
<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/stream.js"></script>

2. 前端Stream.js

"use strict";
var connection = new signalR.HubConnectionBuilder().withUrl("/streamHub").build();
let mediaRecorder = null;
let mediaSource = new MediaSource();
const videoElement = document.getElementById("local-video");
videoElement.srcObject = mediaSource;
let sourceBuffer = null;

// 初始化MediaSource的SourceBuffer
mediaSource.addEventListener('sourceopen', () => {
    sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
    sourceBuffer.mode = 'sequence';
});

// 接收服务器转发的媒体片段并播放
connection.on("ClientReceiveStream", function (blobData) {
    const blob = new Blob([blobData], { type: 'video/webm' });
    if (!sourceBuffer.updating) {
        blob.arrayBuffer().then(buffer => {
            sourceBuffer.appendBuffer(buffer);
        });
    }
});

// 启动SignalR连接
connection.start().then(function () {
    console.log("Connection started");
}).catch(function (err) {
    return console.error(err.toString());
});

async function captureScreen() {
    let mediaStream = null;
    try {
        mediaStream = await navigator.mediaDevices.getDisplayMedia({
            video: { cursor: "always" },
            audio: false
        });

        // 创建MediaRecorder,指定WebM格式
        mediaRecorder = new MediaRecorder(mediaStream, { mimeType: 'video/webm; codecs="vp8"' });
        
        // 每500ms捕获一个媒体片段并发送
        mediaRecorder.ondataavailable = (event) => {
            if (event.data.size > 0) {
                connection.invoke("ServerSendStream", event.data).catch(function (err) {
                    return console.error(err.toString());
                });
            }
        };
        
        // 开始录制,设置片段间隔
        mediaRecorder.start(500);

        // 停止屏幕共享时清理资源
        mediaStream.getTracks().forEach(track => {
            track.addEventListener('ended', () => {
                mediaRecorder.stop();
            });
        });

    } catch (ex) {
        console.log("Error occurred", ex);
    }
}

3. 后端StreamHub

using Microsoft.AspNetCore.SignalR;

public class StreamHub : Hub
{
    // 接收前端发送的媒体片段(SignalR会自动将Blob转换为byte[])
    public async Task ServerSendStream(byte[] mediaSegment)
    {
        // 将片段广播给所有客户端
        await Clients.All.SendAsync("ClientReceiveStream", mediaSegment);
    }
}

关键说明

  • 选择video/webm格式是因为它兼容性好且体积适中,你也可以根据需求更换为其他支持的格式(如MP4)
  • 录制间隔(500ms)可根据网络状况调整,间隔越小延迟越低,但带宽占用越高
  • 如果需要支持多房间或指定客户端转发,可以修改Clients.All为Clients.Others或根据分组转发
  • 生产环境中建议添加错误处理、断线重连逻辑,以及带宽控制

内容的提问来源于stack exchange,提问作者Dr. MAF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:55:31