关于web:融云实践实时音频混音在-Web-端的探索与实践

51次阅读

共计 3479 个字符,预计需要花费 9 分钟才能阅读完成。

互联网扭转世界,以社交为地基。关注【融云寰球互联网通信云】理解更多

在社交中,分享欲是维持关系和感情的重要规范,无论是熟人社交、婚恋交友还是趣味社群。

分享欲体现在语聊房、连麦直播等具体社交场景中,不仅是维系关系的形式,还带有更多陪伴的情绪价值。比方,语聊房中一起听一首最爱的歌,敌人连麦一起看一场独特期待的电影。

为了给用户提供基于分享的优质互动体验,就须要利用实时音频混音技术了。本文将从技术原理、技术计划、接口设计等方面分享融云实时音视频 Web 端 SDK 在混音技术方面的摸索实际。


Web 端音频分享实现计划

对于反对公布本地或线上音视频资源的 Web 端 SDK 来说,最简略地实现在利用中分享音频的形式是:同时公布麦克风资源和须要共享的音频资源,对方通过订阅麦克风音频和共享音频便可听到两个声音。

这种形式的外部门路如下:

(双通道获取两路音频)

但“公布端公布两个音轨,订阅端订阅两个音轨”的实现计划显然不够优雅。

融云采纳的计划,是通过实时音频混音技术,把麦克风和共享音频混合成一个音轨进行公布。这样,订阅端仅需订阅一个音轨。流程如下:

(实时音频混音)

混音后公布,不仅能够节俭带宽,还能够缩小订阅端的操作,更少操作,更优体验。

技术原理

Web Audio API 中的所有操作都基于音频节点 AudioNode 进行。AudioNode 是解决音频模块的通用接口,音频节点类型蕴含:音源节点、音效节点、音频输入节点、音频剖析节点 等。

每一个音频节点都有输出和输入,上一个音频节点输入解决完的音频,connect 连贯到下一个音频节点的输出,实现音频数据在音频节点间的流转。

不同的音频节点连贯在一起形成一个音频图 (Audio Graph),音频图内的节点操作须要依赖音频上下文 AudioContext,每一个音频节点只有一个音频上下文。

理论利用中,须要应用 AudioContext 创立各类型音频节点,从一个或多个音源节点开始,通过一个或多个音频解决节点(如一个过滤器节点 BiquadFilterNode,一个音量控制节点 GainNode),最终连贯到一个目的地(AudioContext.destination)。

AudioContext.destination 返回一个 Audio

DestinationNode,是音频图中的最初一个节点,它负责把声音数据传输给扬声器或耳机。

如下是一个精简的音频图(Audio Graph):

(单音轨 Audio Graph)

因为能够把多个音频源节点输入到同一个 AudioDestinationNode,也就意味着能够把麦克风音频和背景音乐连贯到同一个输入口播放,音频图 (Audio Graph) 就变成了上面这个样子:

(双音轨 Audio Graph)

把两个不同的音频源连贯到同一个输入口,即可达到混音目标。


技术计划

创立音源

音频源能够来源于一段从本地或线上读取的音频数据 ArrayBuffer、一个 audio 标签或浏览器麦克风生成的 MediaStream。以利用 MediaStream 生成音频源为例,创立音源节点的办法如下:
获取麦克风资源生成音源

// 创立一个音频上下文实例
const audioContext = new AudioContext();

// 浏览器生成一个蕴含音轨的 mediaStream 对象
let mediaStream = null;
navigator.mediaDevices.getUserMedia({audio: true}).then((ms) => {mediaStream = ms;});

// 生成一个 mediaStream 音频源
const micSourceNode = audioContext.createMediaStreamSource(mediaStream);

本地播放背景音乐生成音源

// 创立一个 audio 标签播放本地音频
const audioEl = document.createElement('audio');
audioEl.src = '/media/bgmusic.mp3';
audioEl.loop = true;
audioEl.play();

// 从 audio 标签中抓取一个 mediaStream 对象,以在 chrome 浏览器中为例
let mediaStream = null
audioEl.onloadedmetadata = () => {mediaStream =  audioEl.captureStream();
}

// 生成本地音频对应的 mediaStream 音频源
const bgMusicSourceNode = audioContext.createMediaStreamSource(mediaStream);

创立播放进口

// 创立音源播放进口
const destination = audioContext.createMediaStreamDestination()

连贯节点

将两个音频源节点连贯至同一输入口。

// 麦克风音频源连贯输入进口
micSourceNode.connect(destination)

// 背景音乐音频源连贯输入进口
bgMusicSourceNode.connect(destination)

获取混音音轨

从输入节点处获得混合后的音轨 MediaStreamTrack。

// 获取混音 audioTrack                             
const mixAudioTrack = destination.stream.getAudioTracks()[0]

取到混音音轨 mixAudioTrack 后,咱们须要解决的问题是,如何在理论的音视频场景中,无需从新建设对等连贯,就能使对端听到麦克风和共享音乐的声音。

混音音轨在 WebRTC 中失效


混音音轨在 WebRTC 中失效

此时就能够把混音音轨 mixAudioTrack 利用起来了。从 RTCPeerConnection 上找到发送麦克风音频的 RTCRtpSender 对象,应用 mixAudioTrack 替换曾经公布的麦克风音轨,具体实现如下:

// 公布麦克风资源前创立的 RTCPeerConnection 实例对象
const rtcPeerConnection = new RTCPeerConnection([configuration])

// 获取发送音频的 RTCRtpSender 对象
const micSender = rtcPeerConnection.getSenders().find((sender) => {return sender.track.kind === 'audio'})

// 应用 audioContetx 中拿到的混音 mixAudioTrack 替换麦克风 audioTrack
micSender.replaceTrack(mixAudioTrack)

接口设计规划

融云实时音视频 SDK 提供了 createMicrophone

AudioTrack、createLocalFileTracks 办法,别离用于创立麦克风音轨、本地或在线音频音轨。

混音仍然建设在这两个办法创立的实例之上,在 RCRTCRoom 和 RCLivingRoom 上减少 start

MixAudio 和 stopMixAudio 办法用于混音、进行混音的接口反对。

以下展现业务层混音的执行流程,rtcClient、rtcRoom 为业务端获得的 RTC 客户端实例和房间实例。

创立、公布一个麦克风资源

// 创立一个麦克风音轨
const micAudioTrack = rtcClient.createMicrophoneAudioTrack()

// 公布麦克风音轨
rtcRoom.pushlish([micAudioTrack])

创立一个本地音频音轨

// 创立一个本地音频音轨,file 为 <input type='file'> 获取到的 File 实例
const localAudioTrack = rtcClient.createLocalFileTracks('bgmusic', file)

开始混音

将本地音频混入已公布的麦克风音频中,startMixAudio 的第一个参数为已公布的音轨,第二个为要混入的本地音频音轨。

// 开始混音
rtcRoom.startMixAudio(micAudioTrack, localAudioTrack)

进行混音

传入须要去掉混音的音轨。SDK 会从麦克风音频中去掉本地音频的声音。

// 进行混音
rtcRoom.stopMixAudio(micAudioTrack)

基于以上更加优雅简洁的计划,开发者能够更便捷、低成本地接入混音,升高集成阶段的学习门槛。将来,围绕让开发者的业务实现更加高效简略,融云还将继续精进技术,优化体验。

正文完
 0