【Web 流媒体三部曲之二】从 getUserMedia 到 publish:Web 直播数据链路图解

系列: Web 端流媒体入门(共 3 篇)
本篇: 一. 概念科普 二. 数据链路图解 三. 声网 Web SDK 上手
建议: 先读一再读本篇
范围: 仅 Web(getUserMedia + 声网 Web SDK agora-rtc-sdk-ng


摘要

「本地预览有画面,观众却黑屏」多半是没搞清数据流到了哪一站。本文用一条 8 步流水线(含 join 入会)+ 时序图,讲清 Web 直播从摄像头到观众端的完整路径,并总结 4 个最容易踩的坑。

关键词: getUserMedia、publish、subscribe、WebRTC、SFU、声网、数据链路


一、先建立全局:8 步流水线(含 join)

把 Web 直播想成一条工厂流水线。下面 8 步覆盖从硬件到观众画面的主路径(①② 多在浏览器/OS 内完成,业务代码从 ③ 开始介入):

[摄像头/麦克风]
        │
        ▼
① 操作系统驱动
        │
        ▼
② 浏览器 getUserMedia          ← 原始 MediaStreamTrack(YUV / PCM)
        │
        ▼
③ RTC SDK 采集 + 编码          ← LocalTrack(H.264 / AAC 等,见第①篇 §5)
        │
        ▼
④ client.join()                ← 带 Token 进入频道(主播/观众都要,在 publish 之前)
        │
        ├─ track.play(容器)     ← 仅本地预览(主播侧,≠ 推流)
        │
        └─ ⑤ client.publish()   ← 仅 host 角色:上行开始
                │
                ▼
        ⑥ 媒体服务器 SFU 转发    ← 按 channelName 分发给已入会用户
                │
                ▼
        ⑦ subscribe()            ← 观众在 user-published 里拉远端轨
                │
                ▼
        ⑧ remoteTrack.play()    ← SDK 在容器内挂载 <video> 并解码播放

一句话记忆: 采集 → 编码 → join 入会 →(主播)publish → SFU →(观众)subscribeplay

主播 vs 观众:路径分叉

角色关键步骤
主播(host)③ 创建轨 → ④ join → 预览 play → ⑤ publish
观众(audience)join(通常无需本地摄像头)→ 监听 user-published → ⑦ subscribe → ⑧ play

观众不会不必调用 publish(除非切换为 host 连麦)。


二、每一步干什么?(附生活比喻)

技术动作在做什么生活比喻
getUserMedia向浏览器申请设备,得到原始轨打开话筒
createMicrophoneAndCameraTracks包装为 LocalTrack + 编码把画面压成可传输的小包
client.join(appId, channel, token, uid)鉴权进入同一频道刷卡进房间
预览track.play(容器)仅本地 DOM 渲染,不上传对着镜子练声
client.publish(host)编码流经 WebRTC 上行电台开始广播
SFU按频道转发多路流,服务器不混画面快递分拣中心
subscribe + user-published观众拉取主播远端轨调频收听
remoteTrack.play(容器)解码并显示收音机出声

和「打电话」对照

步骤打电话Web 直播
输入你说话摄像头 / 麦克风
数字化话筒 → 电信号getUserMedia
压缩语音/画面编码H.264 + AAC/Opus
传输基站、运营商WebRTC + SFU
输出对方听到观众看到画面

三、第 ② 站详解:getUserMedia

这是 Web 标准 API,所有 RTC SDK 最终都要靠它拿设备(除非你用屏幕共享等特殊源)。

// 最小示例:constraints 为「期望」而非保证值,各浏览器支持不一
const stream = await navigator.mediaDevices.getUserMedia({
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 },
    frameRate: { ideal: 30 },
  },
  audio: true,
});

// 实际分辨率以 track.getSettings() 为准;直播码率/帧率建议在 SDK encoderConfig 里配

注意:

  • 须在安全上下文(HTTPS / localhost);iframe 嵌入时还受 Permissions Policy 限制。
  • 用户拒绝会抛 NotAllowedError,需单独 catch 并提示去浏览器设置里开启权限。
  • 仅做设备检测时,测完对所有轨执行 stream.getTracks().forEach((t) => t.stop()),避免占用摄像头。

四、第 ③~⑤ 步:编码、入会、推流(顺序不能乱)

以声网 Web SDK 为例,主播侧推荐顺序

createClient → setClientRole('host') → createTracks → join → play(预览) → publish

createMicrophoneAndCameraTracks() 内部会:

  1. 调用 getUserMedia
  2. 创建 LocalAudioTrack / LocalVideoTrack
  3. encoderConfig 编码(详见第①篇 §5)。
const client = AgoraRTC.createClient({ mode: 'live', codec: 'h264' });
await client.setClientRole('host');

const [microphoneTrack, cameraTrack] =
  await AgoraRTC.createMicrophoneAndCameraTracks();

const token = await fetchTokenFromBackend(channel, uid);
await client.join(appId, channel, token, uid); // ④ 必须先 join

cameraTrack.play(document.getElementById('preview')); // 预览,仍不等于推流

await client.publish([microphoneTrack, cameraTrack]); // ⑤ 此刻才开始上行

为什么要编码?再记一个数

720p 未压缩约 83 MB/s,家庭上行无法承载。编码后视频常见 约 1–2 Mbps 量级,才能稳定直播(见第①篇 H.264 码率表)。


五、第 ⑥ 步:SFU 是什么?

SFU(Selective Forwarding Unit) = 选择性转发:服务器收到主播的多路编码流后,按观众需要分别转发,不在服务器上混成一张大画面(那是 MCU 干的事)。

声网在全球的 SD-RTN™ 网络里部署的就是这类媒体节点。你只需要知道:

  • 同一 channelName 的用户会在逻辑上进入「同一房间」;
  • 主播 publish 后,SFU 把该用户的音视频轨转发给同一频道内已 join 且完成 subscribe 的观众

本篇不展开 MCU、旁路推流 CDN,Web 入门知道 SFU 即可。

参考:WebRTC for the Curious


六、完整时序图(主播 + 观众 + Token)

观众页 声网SFU 业务后端 主播页 观众页 声网SFU 业务后端 主播页 getUserMedia / createTracks track.play() 本地预览 请求 Token(channel, uid) token join(appId, channel, token, uid) publish(音视频轨) on('user-published') 注册监听 请求 Token token join(同一 channel) user-published(主播, video/audio) subscribe(主播, mediaType) remoteTrack.play(容器)

Token 为什么必须经过后端?
它绑定了 appId、频道、用户权限和过期时间。前端只负责「拿着卡进门」,不能自己印卡。


七、四个最易踩的坑

坑 1:track.play() ≠ 已经推流

很多 bug 是:主播预览正常,观众黑屏。

调用效果
cameraTrack.play(dom)仅本地预览
client.publish([tracks])才真正上行

排查时在 Chrome / Edge 打开 chrome://webrtc-internals,看主播页是否有 outbound-rtp(上行)、观众页是否有 inbound-rtp(下行)。Safari 需用 Web Inspector 的 WebRTC 面板。

坑 2:未 join 就 publish,或频道不一致

publish / subscribe 都要求在 join 成功之后,且主播与观众必须使用相同的 channelName(以及有效 Token)。否则会失败或观众永远收不到 user-published

坑 3:Token 写死在前端

测试阶段可以临时用控制台生成的 Token,上线必须后端签发,且 channeluid 要与 join 参数一致,否则会 join 失败或中途被踢。

坑 4:测试流忘记 stop

const test = await navigator.mediaDevices.getUserMedia({ video: true });
test.getTracks().forEach((t) => t.stop()); // 必须释放

否则正式 createMicrophoneAndCameraTracks 时可能遇到设备被占用。


八、直播 RTC vs 点播(再对齐一次)

维度直播 RTC(本系列)点播 HLS
内容从哪来实时采集已存储文件
关键 APIgetUserMedia + join + publish<video> + HLS 播放器(如 hls.js)
延迟约 1–3 秒通常更长

两套链路 不要混读代码:点播页面里不会出现 publish


九、本篇小结

  1. 8 步流水线:采集 → 编码 → join →(host)publish → SFU → subscribeplay
  2. 预览和推流是两件独立的事。
  3. 观众依赖 user-published,不是只 join 就能出画。
  4. Token 走后端;测试采集后要对所有轨 stop()

下一篇预告

③ 声网 Web SDK 快速上手:8 个 API 搞定推流拉流 — 文档入口、API 对照表、主播/观众五步流程与最小代码骨架。


参考文献

资料链接
第①篇 H.264 / AAC 说明https://blog.csdn.net/qq_41860203/article/details/161461471?spm=1011.2415.3001.5331
MDN 安全上下文https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
MDN getUserMediahttps://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
MDN WebRTC 协议简介https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Protocols
WebRTC for the Curioushttps://webrtcforthecurious.com/
声网 Web 快速开始https://doc.shengwang.cn/doc/rtc/javascript/get-started/quick-start
流媒体技术科普https://zhuanlan.zhihu.com/p/1947215285758203669
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小前端--可笑可笑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值