架構說明
建立一個Component及Service,分別負責處理控制及服務。
cd src/appng g c screen-shareng g s share
App.component基本不動,只在App.component.html中將Component引入。
<app-screen-share></app-screen-share>
到這邊serve起來看到畫面出現app-screen-share work!就可以了
安裝SDK
npm install agora-rtc-sdk-ng
導入至剛剛建立的Service
import AgoraRTC, { IAgoraRTCClient, ILocalAudioTrack, ILocalVideoTrack, UID } from “agora-rtc-sdk-ng”
核心程式碼(share.service.ts)
這邊要負責主要的邏輯,讓Component能控制。
我們需要建立的Function()為:
- 初始化對象變數
- 初始化本地端
- 加入頻道
- 建立螢幕分享流
- 發布螢幕分享流
以及其他像是
- 取得本地端資料(上面步驟2建立完之後回傳的值)
- 取得螢幕流(上面步驟4建立完之後回傳的值)
知道要做什麼之後,一步一步來。
1.初始化對象變數(用來儲存建立後的對象)
private rtc: { client: IAgoraRTCClient | null, localScreenVideoTrack: ILocalVideoTrack | null, localScreenAudioTrack: ILocalAudioTrack | null, uid: UID | null } = { client: null, localScreenVideoTrack: null, localScreenAudioTrack: null, uid: null};
2.初始化本地端(建立本地對象)
initRTCClient(): IAgoraRTCClient { if (!this.rtc.client) { this.rtc.client = AgoraRTC.createClient({ mode: “rtc”, codec: “vp8” }); } return this.rtc.client;}
3.加入頻道(透過Agora控制台獲得該項目的AppID以及房間名稱與對應的token)
async joinRTCChannel() { if (!this.rtc.client) { return; } this.rtc.client.join(“APPID”,“CHANNEL_NAME”, “TOKEN”);}
4.建立螢幕分享流
透過createScreenVideoTrack()來建立螢幕分享對象,這個方法有兩個參數
- encoderConfig(影片的相關設定,這個參數也有五個參數,都是選填)
frameRate 影片楨率
height 影片的高
width 影片的寬
bitrateMax 傳輸中最大碼率
bitrateMin 傳輸中最小碼率
- withAudio(是否共享音頻,這個參數有三種選項)
enable: 分享音頻。
disable: (默認)不分享音頻。
auto: 根據瀏覽器是否支持決定是否分享音頻。
async createScreenTrack() { if (!this.rtc.client) { return null; } const screenTrack = await AgoraRTC.createScreenVideoTrack({ encoderConfig: { width: 1920, height: 1080, frameRate: 15 } }, ‘auto’);
if (Array.isArray(screenTrack)) {
[this.rtc.localScreenVideoTrack, this.rtc.localScreenAudioTrack] = screenTrack;return [this.rtc.localScreenVideoTrack, this.rtc.localScreenAudioTrack]; } else { this.rtc.localScreenVideoTrack = screenTrack; this.rtc.localScreenAudioTrack = null; return this.rtc.localScreenVideoTrack; }}
這邊要判斷AgoraRTC.createScreenVideoTrack回傳的值是不是陣列
因為如果沒有音訊則只會回傳視訊,若有則是回傳含有視訊與音頻的陣列
5.發布螢幕分享流
async publishScreenTrack() { if (!this.rtc.client) { return; } if (this.rtc.localScreenVideoTrack && this.rtc.localScreenAudioTrack)
{ await this.rtc.client.publish(
[this.rtc.localScreenVideoTrack, this.rtc.localScreenAudioTrack]
); } else if (this.rtc.localScreenVideoTrack && !this.rtc.localScreenAudioTrack)
{ await this.rtc.client.publish(this.rtc.localScreenVideoTrack); }}
有音訊流就連同視訊流一起發布,若無則只發布視訊流
其他
取得本地端資料(上面步驟2建立完之後回傳的值)
getRTCClient(): IAgoraRTCClient | null { return this.rtc.client;}
取得螢幕流(上面步驟4建立完之後回傳的值)
getScreenVideoTrack() { return this.rtc.localScreenVideoTrack!;}
控制部分(screen-share.component.ts)
上面Service已經都把邏輯寫好了,這邊只需要負責呼叫就行,不過這邊要額外再寫一個負責訂閱事件的功能。
這邊主要的流程為(斜體表示邏輯已在Service完成,這邊只需呼叫):
- 加入撥放影片用的<div>對象
- 呼叫 初始化服務
- 啟動訂閱功能
- 呼叫 加入頻道功能
- 呼叫 建立螢幕流功能
- 呼叫 發布螢幕流功能
- 將 螢幕流 輸出到Div上
知道要做什麼之後,一步一步來。
1.加入撥放影片用的<div>對象
在screen-share.component.html中建立兩個div,分別負責顯示自己的螢幕流以及顯示別人的螢幕流。
<div
id=”play-div-local”
style=” width: 50vh; height: 50vh; background-color:darkgray;”>
</div><div
id=”play-div-custom”
style=” width: 50vh; height: 50vh; background-color:gray;”>
</div>
只有這段是HTML,再來往下都是screen-share.component.ts的範圍
2.呼叫 初始化服務
this.shareService.initRTCClient();
3.啟動訂閱功能
這邊我們要寫訂閱功能,這樣當有其他人發布影音流時,我們才能取得資料,進而將它顯示在我們的畫面上。
subscribeRTCEvent(): void { this.shareService.getRTCClient()!.on(“user-published”, async (user, mediaType) => { await this.shareService.getRTCClient()!.subscribe(user, mediaType); if (mediaType === “video”) { const playerContainer = document.getElementById(`play-div-custom`); playerContainer && user.videoTrack?.play(playerContainer); } });}
寫好後呼叫
this.subscribeRTCEvent();
事件有很多,其他還有像是有使用者加入;離開、或是某人停止發布影音流時...等等很多的事件可以看文檔有更詳細的說明。這邊單純舉例所以只使用user-published,避免程式碼過於冗長。
4.呼叫 加入頻道功能
await this.shareService.joinRTCChannel();
5.呼叫 建立螢幕流功能
await this.shareService.createScreenTrack();
6.呼叫 發布螢幕流功能
this.shareService.publishScreenTrack();
7.將 螢幕流輸出到Div上
this.shareService.getScreenVideoTrack()?.play(‘play-div-local’);
完整的程式碼(screen-share.component.ts)
ngOnInit(): void { this.init();}async init(){ this.shareService.initRTCClient(); this.subscribeRTCEvent(); await this.shareService.joinRTCChannel(); this.shareService.createScreenTrack().then(() => { this.shareService.publishScreenTrack();
this.shareService.getScreenVideoTrack()?.play(‘play-div-local’); })}
subscribeRTCEvent(): void {this.shareService.getRTCClient()!.on("user-published", async (user, mediaType) => { await this.shareService.getRTCClient()!.subscribe(user, mediaType); if (mediaType === "video") { const playerContainer = document.getElementById(`play-div-custom`); playerContainer && user.videoTrack?.play(playerContainer); } });}
成果
補充
- 若將這個功能與RTC搭配做的話,且原本已經有輸出本地鏡頭的Track,則要先將local的音訊及視訊畫面先unpublish,否則分享畫面會報錯