引言
隨著社會的發(fā)展,人們對實時音視頻的需求越來越多。在線會議,電商直播,在線教育等相關(guān)產(chǎn)品不斷涌現(xiàn)。但是對于個人開發(fā)者或者小團隊來說,自己實現(xiàn)一個實時音視頻服務(wù)并且要保障服務(wù)穩(wěn)定,滿足低延時等要求,難度非常大。為此我們需要尋找一個合適的解決方案。功能強大且可靠的聲網(wǎng)Agora就成為了極佳的選擇。
今年聲網(wǎng)推出了下一代 Agora Web SDK (Agora Web SDK NG),基于 TypeScript 開發(fā),使用 Promise 來管理異步操作,靈活易用。今天我就來分享一下如何快速接入該SDK并實現(xiàn)一些簡單的實時音視頻通話。
前期準(zhǔn)備
首先我們需要注冊一個聲網(wǎng)賬號(注冊地址),注冊成功后會進入控制臺,完成實名認(rèn)證,在左側(cè)進入項目管理頁面,開始創(chuàng)建項目:

輸入項目名稱,選擇鑒權(quán)機制。為了項目安全性考慮,這里推薦使用安全模式。提交后進入項目信息頁面,記錄一下AppID和證書我們之后在代碼中會用到。由于我們選擇了安全模式,在使用SDK時我們需要生成token,在本地開發(fā)調(diào)試時可以在項目信息頁面下方點擊生成臨時token,然后拷貝到代碼中使用。


在項目發(fā)布時,可以參考文檔和官方倉庫編寫對應(yīng)的token生成代碼,部署到自己的服務(wù)器上通過調(diào)用接口的形式來獲取token。
新建項目并集成SDK
前期準(zhǔn)備工作已經(jīng)完成,大家根據(jù)自己的實際情況來新建一個web項目,完成后,我們開始通過npm安裝SDK。
npm install agora-rtc-sdk-ng --save
你也可以通過script標(biāo)簽的形式引入SDK。
<script src="https://download.agora.io/sdk/web/AgoraRTC_N-4.1.0.js"></script>
<!-- or -->
<script src="./AgoraRTC_N-4.1.0.js"></script>
之后我們只需要在項目代碼中引入agora-rtc-sdk-ng就可以直接使用了。
import AgoraRTC from "agora-rtc-sdk-ng";
實現(xiàn)基礎(chǔ)的1對1視頻通話
現(xiàn)在我們開始來按步驟實現(xiàn)基礎(chǔ)的1對1視頻通話。
1. 創(chuàng)建本地客戶端對象
首先我們需要創(chuàng)建一個本地客戶端對象,由于我們不是直播,mode我們選擇rtc,編碼有H.264和VP8兩種,我這里沒有兼容性要求,所以選用推薦的VP8,參考官方文檔。
const rtcClient = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
2. 加入頻道
加入頻道這里需要傳入4個參數(shù):聲網(wǎng)的項目appid,頻道名稱,如果鑒權(quán)開啟了安全模式需要傳入token,最后傳入uid,uid為null時SDK會自動生成一個uid返回給你。
如果你是用app證書通過自己的代碼生成token,需要保證頻道名稱和uid與生成token時保持一致,否則加入頻道會失敗。
const uid = await rtcClient.join(<appid>, <channel name>, <token>, <uid>);
3. 創(chuàng)建本地音視頻軌道
加入完頻道我們開始創(chuàng)建本地音視頻軌道,調(diào)用createMicrophoneAudioTrack()通過本地麥克風(fēng)采集的音頻創(chuàng)建音頻軌道對象,調(diào)用
createCameraVideoTrack()通過本地攝像頭采集的視頻創(chuàng)建視頻軌道對象。你也可以在調(diào)用時傳入?yún)?shù)來調(diào)整編碼、前置\后置攝像頭等配置。同時這里還需要注意瀏覽器提示或相關(guān)的設(shè)置,允許瀏覽器訪問攝像頭、麥克風(fēng)等設(shè)備,必要時對用戶進行引導(dǎo)和提示。
const localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
const localVideoTrack = await AgoraRTC.createCameraVideoTrack();
4. 播放本地音視頻軌道
創(chuàng)建完本地音視頻軌道對象后,我們可以調(diào)用play()進行播放,預(yù)覽一下音頻和視頻。播放音頻軌道時不需要傳入任何參數(shù),播放視頻軌道時需要指定一個DOM元素,你可以傳入一個元素對象,也可以傳入元素的id值。之后SDK會自動在該元素內(nèi)部生成一個video元素播放視頻軌道。
<div id="video-container"></div>
// 播放視頻軌道
localVideoTrack.play(document.getElementById('video-container'));
// or
localVideoTrack.play('video-container');
// 播放音頻軌道
localAudioTrack.play();
效果如圖:

5. 發(fā)布本地音視頻軌道
現(xiàn)在我們需要調(diào)用publish()將我們本地的音視頻軌道發(fā)布到頻道中,一個本地客戶端對象可以發(fā)布多個音頻軌道,比如:背景音樂、麥克風(fēng)聲音等,SDK會自動將其合并為一個音頻軌道發(fā)布到頻道中去,但是一個本地客戶端對象只能發(fā)布一個視頻軌道,如果有發(fā)布多個視頻軌道的需求,你可以創(chuàng)建多個本地客戶端對象加入同一個頻道,然后發(fā)布不同的視頻軌道,但是需要注意區(qū)分2個client使用的uid,并在本地訂閱時過濾掉,防止重復(fù)訂閱,參考文檔。
await rtcClient.publish([
localAudioTrack,
localVideoTrack
]);
6. 訂閱遠端用戶并播放遠端音視頻
現(xiàn)在我們需要監(jiān)聽user-published事件,當(dāng)同一頻道的其他用戶調(diào)用publish()發(fā)布音視頻軌道時,SDK會觸發(fā)該事件,我們需要監(jiān)聽這個事件并在回調(diào)中訂閱其他用戶發(fā)布的音視頻軌道,并調(diào)用play()進行播放
<div id="remote-user"></div>
rtcClient.on("user-published", async (user, mediaType) => {
await rtcClient.subscribe(user, mediaType);
if (mediaType === "video") {
console.log("subscribe video success", user);
user.videoTrack.play(document.getElementById('remote-user'));
}
if (mediaType === "audio") {
console.log("subscribe audio success");
user.audioTrack.play();
}
});
效果如下圖,上方是我們本地的攝像頭畫面,下方為遠端的攝像頭畫面,到這里為止一個基礎(chǔ)的1對1視頻通話已經(jīng)完成了!

通話質(zhì)量監(jiān)測
對于實時音視頻來說,保障通話質(zhì)量是很重要的一環(huán),在SDK中提供了相關(guān)的api供我們查詢當(dāng)前的通話質(zhì)量,文檔,通過使用這些api,客戶端可以及時采取措施,給予用戶及時的提示和反饋。避免影響用戶的使用體驗。
聲網(wǎng)控制臺還有水晶球面板,里面提供了豐富的通話質(zhì)量數(shù)據(jù)查詢功能,開發(fā)者可以使用這個工具更全面的掌握通話質(zhì)量狀況。

小拓展
多個音頻軌道
上文中有提到,一個客戶端對象可以發(fā)布多個音頻軌道,沒有先后順序,可以多次通過調(diào)用publish()進行發(fā)布,SDK會自動合并為一個音頻軌道發(fā)布到頻道中。下面我們就通過自定義音頻采集以及MediaStreamTrack API往通話中播放一個本地音頻文件作為背景音樂。
1. 創(chuàng)建HTMLAudioElement
我們在前面基礎(chǔ)音視頻通話代碼的基礎(chǔ)上進行修改,首先創(chuàng)建一個button確保用戶,讓用戶點擊后再播放音頻文件,確保用戶與頁面有一個交互行為。并監(jiān)聽對應(yīng)的點擊事件。
<button id="addMusic">添加音樂</button>
document.getElementById("addMusic").addEventListener("click", () => {
const audio = new Audio("./music.ogg");
audio.play();
});
2. 獲取音頻軌道并發(fā)布
調(diào)用captureStream()獲取MediaStream對象,監(jiān)聽addtrack事件,觸發(fā)時調(diào)用getAudioTracks()獲取MediaStreamTrack對象的集合,由于我的音頻文件只有一個音軌,所以這里直接取第1個元素。使用createCustomAudioTrack()將獲取到的MediaStreamTrack轉(zhuǎn)換為一個可以用于SDK的音頻軌道,最后使用客戶端對象的publish()發(fā)布。結(jié)合之前的音視頻通話,此時在遠端已經(jīng)能正常收聽到2個音軌的聲音了(麥克風(fēng)和音樂文件)。
document.getElementById("addMusic").addEventListener("click", () => {
const audio = new Audio("./music.ogg");
audio.play();
const musicStream = audio.captureStream();
const musicStream.onaddtrack = async () => {
const musicMediaStreamTrack = musicStream.getAudioTracks()[0];
const musicCustomAudioTrack = AgoraRTC.createCustomAudioTrack({
mediaStreamTrack: musicMediaStreamTrack
});
await rtClient.publish([musicCustomAudioTrack])
};
});
自定義視頻采集
和音頻一樣,SDK也支持你使用自定義的視頻軌道來實現(xiàn)如屏幕錄制,播放本地視頻文件等功能。屏幕錄制在官方文檔中有介紹,這里我分享一下如何獲取并發(fā)布一個本地視頻文件的視頻軌道。
一個客戶端對象只支持發(fā)布一個視頻軌道,同時發(fā)布多個(比如錄屏+攝像頭畫面)需要創(chuàng)建2個客戶端對象分別進行發(fā)布。
1. 創(chuàng)建video元素
首先我們創(chuàng)建一個video元素用于播放視頻文件,并獲取到HTMLVideoElement對象。
<video id="videoFile" src="./video.mp4" control>
const video = document.getElementById('video');
2. 獲取并發(fā)布視頻文件的媒體流
這一步和獲取音頻文件媒體流類似,我們分別發(fā)布了視頻和音頻軌道后的效果如圖。畫面上方的A端播放的視頻文件,畫面下方是B端的攝像頭畫面
視頻文件包含視頻軌道和音頻軌道,我們需要分別獲取對應(yīng)的軌道進行發(fā)布,如果只發(fā)布了視頻軌道會導(dǎo)致遠端沒有視頻聲音
const stream = video.captureStream();
stream.onaddtrack = async () => {
const videoMediaStreamTrack = stream.getVideoTracks()[0];
const audioMediaStreamTrack = stream.getAudioTracks()[0];
const videoTrack = AgoraRTC.createCustomVideoTrack({
mediaStreamTrack: videoMediaStreamTrack
});
const audioTrack = AgoraRTC.createCustomAudioTrack({
mediaStreamTrack: audioMediaStreamTrack
});
await this.rtc.client!.publish([videoTrack,audioTrack]);
};

結(jié)尾
聲網(wǎng)的Agora Web SDK NG 版接入是十分便捷的,簡單易上手,功能強大,文檔清晰。SDK也在github上開源。每個月還有10000分鐘的免費額度,他們也會不定期舉辦一些套餐包優(yōu)惠活動。通話質(zhì)量也有可靠的保障。對于個人或小團隊來說實現(xiàn)音視頻通話不再是很困難的一件事了??偨Y(jié)一下就是:真香。如果你和你的團隊有這方面的需求,不妨也來試試使用聲網(wǎng)。
「本文為個人原創(chuàng),首發(fā)于聲網(wǎng)開發(fā)者社區(qū)」