第二十一章 实战青龙流式系统问题总结

我们在实际的开发过程会遇到很多的问题,这里总结和归纳,可以帮助各位

流式协议带来的限制

媒体流属性的随机化处理

  • RTC 协议要求接收方在接收到媒体流后复写 media track 上的 id, label, contentHint 等属性以保证流属性不会泄漏发送者的媒体设备信息,并使流在 P2P 网络中唯一.
  • 这导致接收者无法区分流中的同类型 track. 例如发送者将相机,屏幕共享两条视频轨道合在同一条流中发给接受者,发送者不知道这两条流到接受者处时 Id 分别是什么,接受者也无法区分哪个是屏幕共享,哪个是视频流
  • 解决方法:在传输多个 track 需要分离的时建立多条 P2P 连接 (虽然看起来很蠢)

FireFox 禁止两个相同节点之间无法建立多条连接

  • FirFox 禁止两个相同节点做多次媒体协,也就是说 A 节点最多 call B 节点一次.
  • 解决方案:如果需要建立两条流 (例如相机和屏幕共享流) 就让 A, B 互相 call 一次

一旦连接建立,不能添加或者删除 Track

  • 建立 RTC 连接的时候虽然我们传入的是一个流,但是协议会获取流中的所有 Track, 然后传输这些 Track, 这就意味着协议并不会引用流对象,我们为流加入或者删除 Track 无法对 RTC 产生影响.
  • 例如用户目前正在使用前置摄像头,当用户切换到后置摄像头时,如果我们删除流中的前置媒体流,添加后置媒体流,RTC 连接并不会切换轨道,反而会认为无法获取轨道信息
  • 解决方案:在建立连接时创建最大可能使用数量的占位流,在增减设备时使用 RTC API 中的 replaceTrack 方法替换占位流。例如:在传输摄像头和麦克风数据时,我们可能需要采集麦克风,摄像头,背景虚化遮罩的 Track, 那么我们就需要使用一个占位 AudioTrack, 两个占位 VideoTrack. 当启动摄像头时候,将占位 Video Track 切换为实际 Track, 在禁用摄像头时再切换回去

RTC 连接状态统计

  • RTC 并不支持直接汇总全部 RTC 连接状态,只支持统计某一个连接的连接状态。开发者可以通过 RTCPeerConnection.getStats 获取对于一个连接的不同主题的报告。常见的有:
    • 统计比特率:
      • 获取当前连接发出或者接收到的总字节数
      • 求和所有连接的收发的总字节数
      • 比特率 = 8 * 两次检测时的字节数差 / 两次检测间隔
    • 获取收发字节数
      • 获取下行字节数: report.type === 'inbound-rtp' 的报告的 report.bytesReceived
      • 获取上行字节数: report.type === 'outbound-rtp' 的报告的 report.bytesReceived
    • 区分媒体类型
      • 视频: report.mediaType === 'video'
      • 音频: report.mediaType === 'audio'
    • 统计下行丢包率:
    • 一个连接的累计丢包: report.type === 'inbound-rtp' 报告的 report.packetsLost
    • 一个连接的从收包: report.type === 'inbound-rtp' 报告的 report.packageReceived
    • 总下行丢包率: 所有连接的丢包量求和 / (所有连接的丢包量求和 + 所有连接的收包量和)
    • 网络延迟:
    • 一个连接的延迟: report.currentRoundTripTime (单位: s)
    • 总延迟: 所有延迟求和 / 连接数量
    • 视频帧率:
    • 上行连接帧率: report.type === 'outbound-rtp' 报告的 report.framesPerSecond
    • 下行连接帧率: report.type === 'inbound-rtp' 报告的 report.framesPerSecond
    • 总帧率: 连接帧率求和 / 连接数
    • 视频分辨率:
    • 上行连接分辨率: report.type === 'outbound-rtp' 报告的 report.frameWidth, report.frameHeight
    • 下行连接分辨率: report.type === 'inbound-rtp' 报告的 report.frameWidth, report.frameHeight
  • 实现可以参考 https://github.com/KairuiLiu/conflux-client/blob/master/src/utils/report.ts
  • 用来调试的 RTC 连接状态报告: chrome://webrtc-internals/

媒体流的处理与兼容性问题

媒体权限的获取与浏览器兼容性问题

  • 在首次 navigator.mediaDevices.getUserMedia() 浏览器会提示用户授权。但是这会导致设备列表只有在获取用户设备时才刷新。不方便 UI 展示。建议采用如下方式:
    • 判断是否授权:
      • 现代化的方法:使用 navigator.permissions
      • 但是该 API 存在兼容性问题,只有 Chrome 与 Safari 支持 microphonecamera 这两个 permissionName, FireFox 会报错
      • 兜底方法:检查 navigator.mediaDevices.enumerateDevices() 中某类 type 是否没有或者 Label 为空。如果是则证明要么用户没有这类设备,要么没有授权。该函数不会让浏览器弹出授权
      • 总结:
    • 申请授权:
    • 监控授权状态变化
    • 监控设备变化

指定音频输出设备

  • 注意:仅桌面 Chrome 与 FireFox 浏览器支持支持,TS 也没有这个方法
// @ts-ignore
audioElement?.setSinkId?.(speaker.deviceId);

指定音视频采集设备

const constraints: MediaStreamConstraints = {[type]: { deviceId: { exact: deviceId } },};
const stream = await navigator.mediaDevices.getUserMedia(constraints);

音频轨道音量统计

  • 注意:音量取值是 [0,1], 当麦克风离得比较远时采集的音量一直很小,进度条展示出来一直处于低点。可以采用 softmax 对结果做变化,让用户可以明显看到音量变化
  • 注意: AudioContext 并不能指定音频播放设备,因此不能用它来顺便播放音频.
  • 原理:
    • 创建 AnalyserNode 用于实时获取音频信号的时间域和频率域数据 (默认使用 FFT 采样)
    • 使用 getByteTimeDomainData 获取最近的时域数据,值代表振幅值 (取值 0-256, 128 为静音)
    • 每个样本值减去 128, 使得波形数据中心化到 0 (静音为 0)
    • 将每个去中心化后的值求平方,得到每个样本的能量,计算所有样本能量的平均值
    • 计算平均能量的平方根,得到均方根值,(当音量最大时,均方根为 255)

如何解决ontrack事件不触发的问题?

1、确保正确绑定了事件,检查代码中的语法错误,并确保使用正确的轨道类型和流对象。

const localStream = await navigator.mediaDevices.getUserMedia({ video: true });
localStream.addEventListener('ontrack', (event) => {
  console.log('ontrack event triggered');
});

2、如果仍然无法触发事件,尝试使用不同的轨道类型或流对象进行测试。

Web 通话过程中出现回声、杂音、噪声、声音小?

通话双方的设备相距太近的时候,属于正常现象,测试时请相互距离远一点。当其他端听到 Web 端的声音存在回声、噪声、杂音等情况时,说明 Web 端的 3A 处理没有生效。

若使用了浏览器原生 getUserMedia API 进行自定义采集,则需要手动设置 3A 参数:

  • echoCancellation:回声消除开关
  • noiseSuppression:噪声抑制开关
  • autoGainControl:自动增益开关? 详细设置参考 媒体追踪约束 。

RA/SD 衍生者AI训练营。发布者:chris,转载请注明出处:https://www.shxcj.com/archives/6559

(0)
上一篇 4天前
下一篇 4天前

相关推荐

发表回复

登录后才能评论
本文授权以下站点有原版访问授权 https://www.shxcj.com https://www.2img.ai https://www.2video.cn