-
Notifications
You must be signed in to change notification settings - Fork 825
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(flat-web): add rtc & rtm (#717)
- Loading branch information
Showing
15 changed files
with
354 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Get rid of "process.env.NODE_ENV" replace error in agora-rtm-sdk. | ||
* | ||
* @TODO: Remove this file when agora-rtm-sdk fix the code. | ||
*/ | ||
|
||
/// <reference types="node" /> | ||
|
||
// https://vitejs.dev/guide/env-and-mode.html#production-replacement | ||
|
||
import fs from "fs"; | ||
// NOTE: `import.meta.resolve` is still experimental | ||
const file = require.resolve("agora-rtm-sdk"); | ||
const code = fs.readFileSync(file, "utf-8"); | ||
const modified = code.replace("process.env.NODE_ENV", "process\u200b.env.NODE_ENV"); | ||
fs.writeFileSync(file, modified); | ||
console.log("agora-rtm-sdk: done!"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import "./white-web-sdk"; | ||
import "./agora-rtm-sdk"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import type { | ||
IAgoraRTCClient, | ||
IAgoraRTCRemoteUser, | ||
ICameraVideoTrack, | ||
IMicrophoneAudioTrack, | ||
IRemoteAudioTrack, | ||
IRemoteVideoTrack, | ||
ITrack, | ||
} from "agora-rtc-sdk-ng"; | ||
import AgoraRTC from "agora-rtc-sdk-ng"; | ||
import type { User } from "../../stores/UserStore"; | ||
import type { RtcRoom } from "./room"; | ||
|
||
export interface RtcAvatarParams { | ||
rtc: RtcRoom; | ||
userUUID: string; | ||
avatarUser: User; | ||
} | ||
|
||
/** | ||
* @example | ||
* const avatar = new RtcAvatar({ rtc, userUUID, avatarUser }) | ||
* avatar.element = el | ||
* avatar.setCamera(true) | ||
*/ | ||
export class RtcAvatar { | ||
private readonly rtc: RtcRoom; | ||
|
||
public readonly userUUID: string; | ||
public readonly avatarUser: User; | ||
public element?: HTMLElement; | ||
public audioTrack?: ITrack; | ||
public videoTrack?: ITrack; | ||
|
||
private readonly isLocal: boolean; | ||
private readonly remoteAudioTrack: Promise<IRemoteAudioTrack>; | ||
private readonly remoteVideoTrack: Promise<IRemoteVideoTrack>; | ||
|
||
private resolveRemoteAudioTrack?: (value: IRemoteAudioTrack) => void; | ||
private resolveRemoteVideoTrack?: (value: IRemoteVideoTrack) => void; | ||
|
||
constructor({ rtc, userUUID, avatarUser }: RtcAvatarParams) { | ||
this.rtc = rtc; | ||
this.userUUID = userUUID; | ||
this.avatarUser = avatarUser; | ||
this.isLocal = userUUID === avatarUser.userUUID; | ||
this.remoteAudioTrack = new Promise<IRemoteAudioTrack>(resolve => { | ||
this.resolveRemoteAudioTrack = resolve; | ||
}); | ||
this.remoteVideoTrack = new Promise<IRemoteVideoTrack>(resolve => { | ||
this.resolveRemoteVideoTrack = resolve; | ||
}); | ||
if (!this.isLocal) { | ||
this.setupExistingTracks(); | ||
this.client.on("user-published", this.onUserPublished); | ||
} | ||
} | ||
|
||
private get client(): IAgoraRTCClient { | ||
return this.rtc.client!; | ||
} | ||
|
||
private async setupExistingTracks(): Promise<void> { | ||
const exist = this.client.remoteUsers.find(e => e.uid === this.avatarUser.rtcUID); | ||
if (exist) { | ||
if (exist.hasAudio) { | ||
const audioTrack = await this.client.subscribe(exist, "audio"); | ||
this.resolveRemoteAudioTrack?.(audioTrack); | ||
this.resolveRemoteAudioTrack = undefined; | ||
} | ||
if (exist.hasVideo) { | ||
const videoTrack = await this.client.subscribe(exist, "video"); | ||
this.resolveRemoteVideoTrack?.(videoTrack); | ||
this.resolveRemoteVideoTrack = undefined; | ||
} | ||
} | ||
} | ||
|
||
public destroy(): void { | ||
if (!this.isLocal && this.client) { | ||
this.client.off("user-published", this.onUserPublished); | ||
} | ||
this.resolveRemoteAudioTrack = undefined; | ||
this.resolveRemoteVideoTrack = undefined; | ||
} | ||
|
||
private onUserPublished = async ( | ||
user: IAgoraRTCRemoteUser, | ||
mediaType: "video" | "audio", | ||
): Promise<void> => { | ||
if (user.uid === this.avatarUser.rtcUID) { | ||
const track = await this.client.subscribe(user, mediaType); | ||
if (mediaType === "audio") { | ||
this.resolveRemoteAudioTrack?.(track as IRemoteAudioTrack); | ||
this.resolveRemoteAudioTrack = undefined; | ||
} else { | ||
this.resolveRemoteVideoTrack?.(track as IRemoteVideoTrack); | ||
this.resolveRemoteVideoTrack = undefined; | ||
} | ||
} | ||
}; | ||
|
||
public async setCamera(enable: boolean): Promise<void> { | ||
try { | ||
if (this.isLocal) { | ||
const videoTrack = this.videoTrack as ICameraVideoTrack | undefined; | ||
if (videoTrack) { | ||
videoTrack.setEnabled(enable); | ||
} else if (enable) { | ||
const videoTrack = await AgoraRTC.createCameraVideoTrack({ | ||
encoderConfig: { width: 288, height: 216 }, | ||
}); | ||
this.videoTrack = videoTrack; | ||
this.element && videoTrack.play(this.element); | ||
await this.client.publish([videoTrack]); | ||
} | ||
} else { | ||
if (!this.videoTrack && enable) { | ||
const videoTrack = await this.remoteVideoTrack; | ||
this.videoTrack = videoTrack; | ||
this.element && videoTrack.play(this.element); | ||
} | ||
} | ||
} catch (error) { | ||
console.info("setCamera failed", error); | ||
} | ||
} | ||
|
||
public async setMic(enable: boolean): Promise<void> { | ||
try { | ||
if (this.isLocal) { | ||
const audioTrack = this.audioTrack as IMicrophoneAudioTrack | undefined; | ||
if (audioTrack) { | ||
audioTrack.setEnabled(enable); | ||
} else if (enable) { | ||
const audioTrack = await AgoraRTC.createMicrophoneAudioTrack(); | ||
this.audioTrack = audioTrack; | ||
audioTrack.play(); | ||
await this.client.publish(audioTrack); | ||
} | ||
} else { | ||
if (!this.audioTrack && enable) { | ||
const audioTrack = await this.remoteAudioTrack; | ||
this.audioTrack = audioTrack; | ||
audioTrack.play(); | ||
} | ||
} | ||
} catch (error) { | ||
console.info("setMic failed", error); | ||
} | ||
} | ||
} | ||
|
||
(window as any).RtcAvatar = RtcAvatar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import AgoraRTC, { IAgoraRTCClient } from "agora-rtc-sdk-ng"; | ||
import { AGORA } from "../../constants/Process"; | ||
import { globalStore } from "../../stores/GlobalStore"; | ||
import { generateRTCToken } from "../flatServer/agora"; | ||
|
||
AgoraRTC.setLogLevel(/* WARNING */ 2); | ||
|
||
export enum RtcChannelType { | ||
Communication = 0, | ||
Broadcast = 1, | ||
} | ||
|
||
/** | ||
* Flow: | ||
* ``` | ||
* join() -> now it has `client` | ||
* getLatency() -> number | ||
* destroy() | ||
* ``` | ||
*/ | ||
export class RtcRoom { | ||
public client?: IAgoraRTCClient; | ||
|
||
private roomUUID?: string; | ||
|
||
public async join({ | ||
roomUUID, | ||
isCreator, | ||
rtcUID, | ||
channelType, | ||
}: { | ||
roomUUID: string; | ||
isCreator: boolean; | ||
rtcUID: number; | ||
channelType: RtcChannelType; | ||
}): Promise<void> { | ||
if (this.client) { | ||
await this.destroy(); | ||
} | ||
|
||
const mode = channelType === RtcChannelType.Communication ? "rtc" : "live"; | ||
this.client = AgoraRTC.createClient({ mode, codec: "vp8" }); | ||
|
||
this.client.on("token-privilege-will-expire", this.renewToken); | ||
|
||
await this.client.setClientRole( | ||
channelType === RtcChannelType.Broadcast && !isCreator ? "audience" : "host", | ||
); | ||
const token = globalStore.rtcToken || (await generateRTCToken(roomUUID)); | ||
await this.client.join(AGORA.APP_ID, roomUUID, token, rtcUID); | ||
|
||
this.roomUUID = roomUUID; | ||
} | ||
|
||
public getLatency(): number { | ||
return this.client?.getRTCStats().RTT ?? NaN; | ||
} | ||
|
||
public async destroy(): Promise<void> { | ||
if (this.client) { | ||
this.client.off("token-privilege-will-expire", this.renewToken); | ||
await this.client.leave(); | ||
this.client = undefined; | ||
} | ||
} | ||
|
||
private renewToken = async (): Promise<void> => { | ||
if (this.client && this.roomUUID) { | ||
const token = await generateRTCToken(this.roomUUID); | ||
await this.client.renewToken(token); | ||
} | ||
}; | ||
} | ||
|
||
(window as any).RtcRoom = RtcRoom; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.