【實作紀錄】Agora × Angular 螢幕分享

Ray
11 min readOct 21, 2021

--

本文前置:需要一Agora控制台已存在項目。

本文重點:在本地端(localhost)利用分頁檢查是否完成螢幕分享功能

架構說明

建立一個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()為:

  1. 初始化對象變數
  2. 初始化本地端
  3. 加入頻道
  4. 建立螢幕分享流
  5. 發布螢幕分享流

以及其他像是

  1. 取得本地端資料(上面步驟2建立完之後回傳的值)
  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完成,這邊只需呼叫):

  1. 加入撥放影片用的<div>對象
  2. 呼叫 初始化服務
  3. 啟動訂閱功能
  4. 呼叫 加入頻道功能
  5. 呼叫 建立螢幕流功能
  6. 呼叫 發布螢幕流功能
  7. 將 螢幕流 輸出到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); } });}

成果

補充

  1. 若將這個功能與RTC搭配做的話,且原本已經有輸出本地鏡頭的Track,則要先將local的音訊及視訊畫面先unpublish,否則分享畫面會報錯

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response