本文將介紹如何使用微信小程序原生推拉流組件
成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括鹿城網(wǎng)站建設(shè)、鹿城網(wǎng)站制作、鹿城網(wǎng)頁制作以及鹿城網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,鹿城網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到鹿城省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
由于微信小程序原生推拉流組件使用起來比較復(fù)雜,推薦開發(fā)者使用即構(gòu)封裝的音視頻SDK
在實(shí)現(xiàn)基本的實(shí)時(shí)音視頻功能之前,請(qǐng)確保:
用戶通過 ZEGO Express SDK 即構(gòu)音視頻SDK進(jìn)行視頻通話的基本流程為:
用戶 A、B 加入房間,用戶 B 預(yù)覽并將音視頻流推送到 ZEGO 云服務(wù)(推流),用戶 A 收到用戶 B 推送音視頻流的通知之后,在通知中播放用戶 B 的音視頻流(拉流)。
在初始化 音視頻SDK 前,需要在 微信公眾平臺(tái) 中進(jìn)行如下配置:
服務(wù)器域名配置:在“小程序后臺(tái) > 開發(fā)管理 > 開發(fā)設(shè)置 > 服務(wù)器域名”中,按照協(xié)議分類,將即構(gòu) Server 地址、LogUrl、以及用戶業(yè)務(wù)需要用到的地址填到指定的“request合法域名”或“socket合法域名”中。
相關(guān)功能開啟:在“小程序后臺(tái) > 開發(fā)管理 > 接口設(shè)置 > 接口權(quán)限”中,打開 實(shí)時(shí)播放音視頻流 和 實(shí)時(shí)錄制音視頻流 功能開關(guān)。
1. 創(chuàng)建音視頻通話界面
根據(jù)音視頻場(chǎng)景需要,為您的項(xiàng)目創(chuàng)建音視頻通話的用戶界面。我們推薦您在項(xiàng)目中添加如下元素:
小程序推流組件
參考界面代碼:
2. 創(chuàng)建音視頻SDK引擎
創(chuàng)建 ZegoExpressEngine
引擎實(shí)例,將申請(qǐng)到的 AppID 傳入?yún)?shù) “appID”,將獲取到的 Server 地址傳入?yún)?shù) “server”。
// 初始化實(shí)例
zg = new ZegoExpressEngine(appID, server);
如果需要注冊(cè)回調(diào),開發(fā)者可根據(jù)實(shí)際需要,實(shí)現(xiàn) ZegoEvent 中的某些方法,創(chuàng)建引擎后可通過調(diào)用 on
接口設(shè)置回調(diào)。
zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
if (state == 'DISCONNECTED') {
// 與房間斷開了連接
// ...
}
if (state == 'CONNECTING') {
// 與房間嘗試連接中
// ...
}
if (state == 'CONNECTED') {
// 與房間連接成功
// ...
}
})
1. 獲取房間登錄 Token
登錄房間需要用于驗(yàn)證身份的 Token,獲取方式請(qǐng)參考 用戶權(quán)限控制。如需快速調(diào)試,建議使用控制臺(tái)生成的臨時(shí) Token,生成臨時(shí) Token 的具體操作請(qǐng)參考 控制臺(tái) - 項(xiàng)目管理。
2. 登錄音視頻房間
您可以調(diào)用 SDK 的 loginRoom
接口,傳入房間 ID 參數(shù) “roomID”、“token” 和用戶參數(shù) “user”,登錄房間。您可通過監(jiān)聽 roomStateUpdate
回調(diào)實(shí)時(shí)監(jiān)控自己在本房間內(nèi)的連接狀態(tài),具體請(qǐng)參考 4.1 常見通知回調(diào) 中的“4.1.1 我在房間內(nèi)的連接狀態(tài)變化通知”。
roomID 和 user 的參數(shù)由您本地生成,但是需要滿足以下條件:
為避免錯(cuò)過任何通知,您需要在登錄房間前先設(shè)置所有的監(jiān)聽回調(diào)(如房間狀態(tài)、用戶狀態(tài)、流狀態(tài)、推拉流狀態(tài)等),具體請(qǐng)參考 4.1 常見通知回調(diào) 。
// 登錄房間,成功則返回 true
const result = await zg.loginRoom(roomID, token, {userID, userName});
調(diào)用 initContext
接口初始化小程序組件。
小程序組件中用于存儲(chǔ)推流屬性 pusher 和拉流屬性列表 playerList 兩個(gè)字段需要傳給 SDK,SDK 后續(xù)將通過傳入的兩個(gè)字段對(duì)相應(yīng)的推拉流作狀態(tài)及視圖更新處理。
zg.initContext({
wxContext: this,
pushAtr: "pusher", // 對(duì)象名,對(duì)象屬性與 live-pusher 中的屬性為映射關(guān)系
playAtr: "playerList" // 對(duì)象名,對(duì)象屬性與 live-player 中的屬性為映射關(guān)系
})
即構(gòu)音視頻SDK 在內(nèi)部會(huì)對(duì)推拉流實(shí)例進(jìn)行操作以及視圖更新,開發(fā)者無需保存推拉流實(shí)例和調(diào)用小程序 setData 接口更新視圖,避免與 SDK 發(fā)生沖突。后續(xù)可通過 getPusherInstance
和 getPlayerInstance
接口獲取推拉流實(shí)例。
根據(jù)您的業(yè)務(wù)場(chǎng)景需求,編寫 WXML 文件,創(chuàng)建推拉流組件
WXML 的具體含義與用法請(qǐng)參考微信官網(wǎng)文檔中的介紹 WXML。
WXML 中的 pusher 與 playerList,必須與初始化小程序組件 initContext
中定義的這兩個(gè)字段屬性名保持一致,后續(xù) SDK 調(diào)用推拉流接口之后才能正確地進(jìn)行狀態(tài)及視圖更新。
bindstatechange 表示播放狀態(tài)變化事件;bindaudiovolumenotify 表示播放音量大小通知;bindnetstatus 表示網(wǎng)絡(luò)狀態(tài)通知。
必須完成初始化小程序組件實(shí)例和創(chuàng)建業(yè)務(wù)場(chǎng)景的 WXML 之后,才能調(diào)用 SDK 接口創(chuàng)建推流和拉流實(shí)例。
用戶調(diào)用 SDK 的 createPusher
接口創(chuàng)建推流實(shí)例,并通過調(diào)用實(shí)例對(duì)象上的 start
接口,傳入流 ID 參數(shù) “streamID”。您可通過監(jiān)聽 publisherStateUpdate
回調(diào)知曉推流是否成功,具體請(qǐng)參考 4.1 常見通知回調(diào) 中的“4.1.4 用戶推送音視頻流的狀態(tài)通知”。
“streamID” 由您本地生成,但是需要保證:
// 推流方登錄房間成功后觸發(fā)推流
const pusher = zg.createPusher();
pusher.start("streamID_xxx");
進(jìn)行視頻通話時(shí),我們需要拉取到其他用戶的音視頻。
用戶先調(diào)用 getPlayerInstance
接口,根據(jù)傳入的流 ID 參數(shù) “streamID”,獲取 streamID 對(duì)應(yīng)的拉流實(shí)例,然后通過調(diào)用拉流實(shí)例對(duì)象的 play
接口開始拉流。您可通過監(jiān)聽 playerStateUpdate
回調(diào)知曉是否成功拉取音視頻,具體請(qǐng)參考 4.1 常見通知回調(diào) 中的“4.1.5 用戶拉取音視頻流的狀態(tài)通知”。
遠(yuǎn)端用戶推送的 “streamID” 可以從 roomStreamUpdate 回調(diào)中獲得,具體回調(diào)設(shè)置請(qǐng)參考 4.1 常見通知回調(diào) 中的“4.1.3 房間內(nèi)流狀態(tài)變更的通知”。
// 在 SDK 的回調(diào) roomStreamUpdate 中獲取拉流 streamID
// 當(dāng)用戶加入或離開房間時(shí),該事件被觸發(fā)
zg.on("roomStreamUpdate", (roomID, updateType, streamList) => {
console.log("roomStreamUpdate", roomID, updateType, streamList);
if (updateType === "ADD") {
streamList.forEach(i => {
zg.getPlayerInstance(i.streamID).play();
})
} else {
streamList.forEach(i => {
zg.getPlayerInstance(i.streamID).stop();
})
}
});
roomStateUpdate
:本地調(diào)用 loginRoom
加入房間時(shí),您可通過監(jiān)聽該回調(diào)實(shí)時(shí)監(jiān)控自己在本房間內(nèi)的連接狀態(tài)。
用戶可以在回調(diào)中根據(jù)不同狀態(tài)處理業(yè)務(wù)邏輯。
zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
if (state == 'DISCONNECTED') {
// 與房間斷開了連接
// ...
}
if (state == 'CONNECTING') {
// 與房間嘗試連接中
// ...
}
if (state == 'CONNECTED') {
// 與房間連接成功
// ...
}
})
roomUserUpdate
:同一房間內(nèi)的其他用戶進(jìn)出房間時(shí),您可通過此回調(diào)收到通知。登錄房間后,當(dāng)房間內(nèi)有用戶新增或刪除時(shí),SDK 會(huì)通過該回調(diào)通知。
只有調(diào)用 loginRoom
接口登錄房間時(shí)傳入 ZegoRoomConfig
配置,且 “userUpdate” 參數(shù)取值為 “true” 時(shí),用戶才能收到 roomUserUpdate
回調(diào)。
// 用戶狀態(tài)更新回調(diào)
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
console.warn(
`roomUserUpdate: room ${roomID}, user ${updateType === 'ADD' ? 'added' : 'left'} `,
JSON.stringify(userList),
);
});
roomStreamUpdate
:流狀態(tài)更新回調(diào)。登錄房間后,當(dāng)房間內(nèi)有用戶新推送或刪除音視頻流時(shí),SDK 會(huì)通過該回調(diào)通知。
// 流狀態(tài)更新回調(diào)
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
if (updateType == 'ADD') {
// 流新增,開始拉流
} else if (updateType == 'DELETE') {
// 流刪除,停止拉流
}
});
微信小程序會(huì)在
a. 在 bindstatechange 綁定的回調(diào)函數(shù)中,調(diào)用 SDK 的 updatePlayerState
接口將推流狀態(tài)事件透?jìng)鹘o SDK。
b. 在 SDK 的 publisherStateUpdate
回調(diào)中處理推流的開始、失敗狀態(tài)。
// live-pusher 綁定推流事件
onPushStateChange(e) {
// 透?jìng)魍屏魇录o SDK
zg.updatePlayerState(this.data.publishStreamID, e);
},
// 推流后,服務(wù)器主動(dòng)推過來的,流狀態(tài)更新
// NO_PUBLISH:未推流狀態(tài),PUBLISH_REQUESTING:正在請(qǐng)求推流狀態(tài),PUBLISHING:正在推流狀態(tài)
// state: "PUBLISHING" | "NO_PUBLISH" | "PUBLISH_REQUESTING";
zg.on("publisherStateUpdate", (result) => {
console.log("publishStateUpdate", result.state);
});
微信小程序會(huì)在 bindnetstatus
綁定的方法中通知出推流網(wǎng)絡(luò)事件,開發(fā)者需要在對(duì)應(yīng)的小程序回調(diào)中,調(diào)用 SDK 的 updatePlayerNetStatus
接口將推流網(wǎng)絡(luò)事件透?jìng)鹘o SDK。
// live-pusher 綁定網(wǎng)絡(luò)狀態(tài)事件
onPushNetStateChange(e) {
//透?jìng)骶W(wǎng)絡(luò)狀態(tài)事件給 SDK
zg.updatePlayerNetStatus(this.data.publishStreamID, e);
},
// SDK 推流網(wǎng)絡(luò)質(zhì)量回調(diào)
zg.on("publishQualityUpdate", (streamID, publishStats) => {
console.log("publishQualityUpdate", streamID, publishStats);
});
微信小程序會(huì)在 bindstatechange
綁定的方法中通知出拉流狀態(tài)事件,開發(fā)者需要:
a. 在 bindstatechange
綁定的回調(diào)函數(shù)中,調(diào)用 SDK 的 updatePlayerState
接口將拉流狀態(tài)事件透?jìng)鹘o SDK。
b. 在 SDK 提供的 playerStateUpdate
回調(diào)中處理拉流的開始或失敗狀態(tài)。
// live-player 綁定的拉流事件
onPlayStateChange(e) {
// 透?jìng)骼魇录o SDK
zg.updatePlayerState(e.currentTarget.id, e);
},
// 服務(wù)器主動(dòng)推過來的流的播放狀態(tài)
// 視頻播放狀態(tài)通知;state: "NO_PLAY" | "PLAY_REQUESTING" | "PLAYING";
zg.on("playerStateUpdate", (result) => {
console.log("playStateUpdate", result.state);
});
微信小程序會(huì)在 bindnetstatus
綁定的方法中通知出拉流網(wǎng)絡(luò)事件,開發(fā)者需要在對(duì)應(yīng)的小程序回調(diào)中,調(diào)用 SDK 的 updatePlayerNetStatus
接口將推流網(wǎng)絡(luò)事件透?jìng)鹘o SDK。
// live-player 綁定網(wǎng)絡(luò)狀態(tài)事件
onPlayNetStateChange(e) {
// 透?jìng)骶W(wǎng)絡(luò)狀態(tài)事件給 SDK
zg.updatePlayerNetStatus(playStreamID, e);
},
// SDK 拉流網(wǎng)絡(luò)質(zhì)量回調(diào)
zg.on("playQualityUpdate", (playStreamID, playStats) => {
console.log("playQualityUpdate", playStreamID, playStats);
});
1. 停止推流
調(diào)用 SDK 的 getPusherInstance
接口獲取推流實(shí)例,并調(diào)用推流實(shí)例的 stop
方法停止推流。
// 停止推流
zg.getPusherInstance().stop();
2. 停止拉流
調(diào)用 SDK 的 getPlayerInstance
接口獲取拉流實(shí)例,并調(diào)用推流實(shí)例的 stop
方法停止拉流。
// 停止拉流
zg.getPlayerInstance(streamID).stop();
調(diào)用 SDK 的 logoutRoom
接口退出房間。
zg.logoutRoom(roomID);
在真機(jī)中運(yùn)行項(xiàng)目,運(yùn)行成功后,可以看到本端視頻畫面。
為方便體驗(yàn),ZEGO 提供了一個(gè) Web 端調(diào)試示例 ,在該頁面下,輸入相同的 AppID、RoomID,輸入一個(gè)不同的 UserID,即可加入同一房間與真機(jī)設(shè)備互通。當(dāng)成功開始音視頻通話時(shí),可以聽到遠(yuǎn)端的音頻,看到遠(yuǎn)端的視頻畫面。
整個(gè)推拉流過程的 API 調(diào)用時(shí)序可參考下圖: