From 96305c05c399b32d2f34500f33f8e1227fa4253f Mon Sep 17 00:00:00 2001 From: David Zhao Date: Sun, 5 Sep 2021 17:55:21 -0700 Subject: [PATCH] handle explicit remote unmute, removed mute status match (#40) * handle explicit remote unmute, removed mute status match * missed yarn.lock --- example/sample.ts | 7 +- package.json | 2 +- src/api/SignalClient.ts | 19 +- src/index.ts | 3 +- src/proto/livekit_models.ts | 634 +++++++++-------------- src/proto/livekit_rtc.ts | 493 ++---------------- src/room/RTCEngine.ts | 34 +- src/room/Room.ts | 5 +- src/room/events.ts | 1 + src/room/participant/LocalParticipant.ts | 46 +- yarn.lock | 12 +- 11 files changed, 332 insertions(+), 924 deletions(-) diff --git a/example/sample.ts b/example/sample.ts index aec4adf155..8f9e5c57c2 100644 --- a/example/sample.ts +++ b/example/sample.ts @@ -1,6 +1,5 @@ import { - connect, createLocalScreenTracks, CreateVideoTrackOptions, - LocalAudioTrack, + connect, createLocalScreenTracks, CreateVideoTrackOptions, DataPacket_Kind, LocalAudioTrack, LocalTrack, LocalVideoTrack, LogLevel, @@ -10,10 +9,8 @@ import { RemoteTrack, Room, RoomEvent, - Track, - VideoPresets, + Track, VideoPresets, } from '../src/index'; -import { DataPacket_Kind } from '../src/proto/livekit_rtc'; const $ = (id: string) => document.getElementById(id); diff --git a/package.json b/package.json index 0bda420f71..7d4177417e 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "eslint-plugin-import": "^2.22.0", "gh-pages": "^3.2.3", "ts-loader": "^8.1.0", - "ts-proto": "1.79.1", + "ts-proto": "1.82.5", "typedoc": "^0.20.35", "typedoc-plugin-no-inherit": "^1.2.0", "typescript": "~4.2.3", diff --git a/src/api/SignalClient.ts b/src/api/SignalClient.ts index c270ba2d64..1bb936bbf4 100644 --- a/src/api/SignalClient.ts +++ b/src/api/SignalClient.ts @@ -1,6 +1,6 @@ import log from 'loglevel'; import 'webrtc-adapter'; -import { ParticipantInfo } from '../proto/livekit_models'; +import { ParticipantInfo, SpeakerInfo } from '../proto/livekit_models'; import { AddTrackRequest, JoinResponse, @@ -8,7 +8,6 @@ import { SignalRequest, SignalResponse, SignalTarget, - SpeakerInfo, TrackPublishedResponse, UpdateSubscription, UpdateTrackSettings, @@ -21,7 +20,6 @@ import { protocolVersion } from '../version'; // internal options interface ConnectOpts { autoSubscribe?: boolean; - usePlanB?: boolean; /** internal */ reconnect?: boolean; } @@ -36,7 +34,7 @@ export interface SignalOptions { * so that it */ export interface SignalClient { - join(url: string, token: string, usePlanB?: boolean, opts?: SignalOptions): Promise; + join(url: string, token: string, opts?: SignalOptions): Promise; reconnect(url: string, token: string): Promise; sendOffer(offer: RTCSessionDescriptionInit): void; sendAnswer(answer: RTCSessionDescriptionInit): void; @@ -65,6 +63,8 @@ export interface SignalClient { onLocalTrackPublished?: (res: TrackPublishedResponse) => void; // when active speakers changed onActiveSpeakersChanged?: (res: SpeakerInfo[]) => void; + // when track was muted/unmuted by the server + onRemoteMuteChanged?: (trackSid: string, muted: boolean) => void; onLeave?: () => void; } @@ -90,6 +90,8 @@ export class WSSignalClient { onActiveSpeakersChanged?: (res: SpeakerInfo[]) => void; + onRemoteMuteChanged?: (trackSid: string, muted: boolean) => void; + onLeave?: () => void; ws?: WebSocket; @@ -102,11 +104,9 @@ export class WSSignalClient { async join( url: string, token: string, - planB: boolean = false, opts?: SignalOptions, ): Promise { const res = await this.connect(url, token, { - usePlanB: planB, autoSubscribe: opts?.autoSubscribe, }); return res as JoinResponse; @@ -130,9 +130,6 @@ export class WSSignalClient { if (opts.reconnect) { params += '&reconnect=1'; } - if (opts.usePlanB) { - params += '&planb=1'; - } if (opts.autoSubscribe !== undefined) { params += `&auto_subscribe=${opts.autoSubscribe ? '1' : '0'}`; } @@ -322,6 +319,10 @@ export class WSSignalClient { if (this.onLeave) { this.onLeave(); } + } else if (msg.mute) { + if (this.onRemoteMuteChanged) { + this.onRemoteMuteChanged(msg.mute.sid, msg.mute.muted); + } } else { log.warn('unsupported message', msg); } diff --git a/src/index.ts b/src/index.ts index 012a7833f3..ad68560cd0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ -import { DataPacket_Kind, VideoQuality } from './proto/livekit_rtc'; +import { DataPacket_Kind } from './proto/livekit_models'; +import { VideoQuality } from './proto/livekit_rtc'; import LocalParticipant from './room/participant/LocalParticipant'; import Participant from './room/participant/Participant'; import RemoteParticipant from './room/participant/RemoteParticipant'; diff --git a/src/proto/livekit_models.ts b/src/proto/livekit_models.ts index b8f7d857d1..de73886fa7 100644 --- a/src/proto/livekit_models.ts +++ b/src/proto/livekit_models.ts @@ -63,7 +63,7 @@ export interface ParticipantInfo { state: ParticipantInfo_State; tracks: TrackInfo[]; metadata: string; - /** timestamp when participant joined room */ + /** timestamp when participant joined room, in seconds */ joinedAt: number; /** hidden participant (used for recording) */ hidden: boolean; @@ -137,40 +137,64 @@ export interface TrackInfo { simulcast: boolean; } -/** old DataTrack message */ -export interface DataMessage { - text: string | undefined; - binary: Uint8Array | undefined; +/** new DataPacket API */ +export interface DataPacket { + kind: DataPacket_Kind; + user?: UserPacket | undefined; + speaker?: ActiveSpeakerUpdate | undefined; } -export interface RecordingTemplate { - layout: string; - wsUrl: string; - /** either token or room name required */ - token: string; - roomName: string; +export enum DataPacket_Kind { + RELIABLE = 0, + LOSSY = 1, + UNRECOGNIZED = -1, } -export interface RecordingS3Output { - bucket: string; - key: string; - /** optional */ - accessKey: string; - secret: string; +export function dataPacket_KindFromJSON(object: any): DataPacket_Kind { + switch (object) { + case 0: + case "RELIABLE": + return DataPacket_Kind.RELIABLE; + case 1: + case "LOSSY": + return DataPacket_Kind.LOSSY; + case -1: + case "UNRECOGNIZED": + default: + return DataPacket_Kind.UNRECOGNIZED; + } } -export interface RecordingOptions { - /** 720p30, 720p60, 1080p30, or 1080p60 */ - preset: string; - inputWidth: number; - inputHeight: number; - outputWidth: number; - outputHeight: number; - depth: number; - framerate: number; - audioBitrate: number; - audioFrequency: number; - videoBitrate: number; +export function dataPacket_KindToJSON(object: DataPacket_Kind): string { + switch (object) { + case DataPacket_Kind.RELIABLE: + return "RELIABLE"; + case DataPacket_Kind.LOSSY: + return "LOSSY"; + default: + return "UNKNOWN"; + } +} + +export interface ActiveSpeakerUpdate { + speakers: SpeakerInfo[]; +} + +export interface SpeakerInfo { + sid: string; + /** audio level, 0-1.0, 1 is loudest */ + level: number; + /** true if speaker is currently active */ + active: boolean; +} + +export interface UserPacket { + /** participant ID of user that sent the message */ + participantSid: string; + /** user defined payload */ + payload: Uint8Array; + /** the ID of the participants who will receive the message (the message will be sent to all the people in the room if this variable is empty) */ + destinationSids: string[]; } const baseRoom: object = { @@ -773,34 +797,43 @@ export const TrackInfo = { }, }; -const baseDataMessage: object = {}; +const baseDataPacket: object = { kind: 0 }; -export const DataMessage = { +export const DataPacket = { encode( - message: DataMessage, + message: DataPacket, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.text !== undefined) { - writer.uint32(10).string(message.text); + if (message.kind !== 0) { + writer.uint32(8).int32(message.kind); } - if (message.binary !== undefined) { - writer.uint32(18).bytes(message.binary); + if (message.user !== undefined) { + UserPacket.encode(message.user, writer.uint32(18).fork()).ldelim(); + } + if (message.speaker !== undefined) { + ActiveSpeakerUpdate.encode( + message.speaker, + writer.uint32(26).fork() + ).ldelim(); } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): DataMessage { + decode(input: _m0.Reader | Uint8Array, length?: number): DataPacket { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseDataMessage } as DataMessage; + const message = { ...baseDataPacket } as DataPacket; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.text = reader.string(); + message.kind = reader.int32() as any; break; case 2: - message.binary = reader.bytes(); + message.user = UserPacket.decode(reader, reader.uint32()); + break; + case 3: + message.speaker = ActiveSpeakerUpdate.decode(reader, reader.uint32()); break; default: reader.skipType(tag & 7); @@ -810,91 +843,83 @@ export const DataMessage = { return message; }, - fromJSON(object: any): DataMessage { - const message = { ...baseDataMessage } as DataMessage; - if (object.text !== undefined && object.text !== null) { - message.text = String(object.text); + fromJSON(object: any): DataPacket { + const message = { ...baseDataPacket } as DataPacket; + if (object.kind !== undefined && object.kind !== null) { + message.kind = dataPacket_KindFromJSON(object.kind); + } else { + message.kind = 0; + } + if (object.user !== undefined && object.user !== null) { + message.user = UserPacket.fromJSON(object.user); } else { - message.text = undefined; + message.user = undefined; } - if (object.binary !== undefined && object.binary !== null) { - message.binary = bytesFromBase64(object.binary); + if (object.speaker !== undefined && object.speaker !== null) { + message.speaker = ActiveSpeakerUpdate.fromJSON(object.speaker); + } else { + message.speaker = undefined; } return message; }, - toJSON(message: DataMessage): unknown { + toJSON(message: DataPacket): unknown { const obj: any = {}; - message.text !== undefined && (obj.text = message.text); - message.binary !== undefined && - (obj.binary = - message.binary !== undefined - ? base64FromBytes(message.binary) - : undefined); + message.kind !== undefined && + (obj.kind = dataPacket_KindToJSON(message.kind)); + message.user !== undefined && + (obj.user = message.user ? UserPacket.toJSON(message.user) : undefined); + message.speaker !== undefined && + (obj.speaker = message.speaker + ? ActiveSpeakerUpdate.toJSON(message.speaker) + : undefined); return obj; }, - fromPartial(object: DeepPartial): DataMessage { - const message = { ...baseDataMessage } as DataMessage; - if (object.text !== undefined && object.text !== null) { - message.text = object.text; + fromPartial(object: DeepPartial): DataPacket { + const message = { ...baseDataPacket } as DataPacket; + if (object.kind !== undefined && object.kind !== null) { + message.kind = object.kind; } else { - message.text = undefined; + message.kind = 0; } - if (object.binary !== undefined && object.binary !== null) { - message.binary = object.binary; + if (object.user !== undefined && object.user !== null) { + message.user = UserPacket.fromPartial(object.user); } else { - message.binary = undefined; + message.user = undefined; + } + if (object.speaker !== undefined && object.speaker !== null) { + message.speaker = ActiveSpeakerUpdate.fromPartial(object.speaker); + } else { + message.speaker = undefined; } return message; }, }; -const baseRecordingTemplate: object = { - layout: "", - wsUrl: "", - token: "", - roomName: "", -}; +const baseActiveSpeakerUpdate: object = {}; -export const RecordingTemplate = { +export const ActiveSpeakerUpdate = { encode( - message: RecordingTemplate, + message: ActiveSpeakerUpdate, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.layout !== "") { - writer.uint32(10).string(message.layout); - } - if (message.wsUrl !== "") { - writer.uint32(18).string(message.wsUrl); - } - if (message.token !== "") { - writer.uint32(26).string(message.token); - } - if (message.roomName !== "") { - writer.uint32(34).string(message.roomName); + for (const v of message.speakers) { + SpeakerInfo.encode(v!, writer.uint32(10).fork()).ldelim(); } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): RecordingTemplate { + decode(input: _m0.Reader | Uint8Array, length?: number): ActiveSpeakerUpdate { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseRecordingTemplate } as RecordingTemplate; + const message = { ...baseActiveSpeakerUpdate } as ActiveSpeakerUpdate; + message.speakers = []; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.layout = reader.string(); - break; - case 2: - message.wsUrl = reader.string(); - break; - case 3: - message.token = reader.string(); - break; - case 4: - message.roomName = reader.string(); + message.speakers.push(SpeakerInfo.decode(reader, reader.uint32())); break; default: reader.skipType(tag & 7); @@ -904,111 +929,75 @@ export const RecordingTemplate = { return message; }, - fromJSON(object: any): RecordingTemplate { - const message = { ...baseRecordingTemplate } as RecordingTemplate; - if (object.layout !== undefined && object.layout !== null) { - message.layout = String(object.layout); - } else { - message.layout = ""; - } - if (object.wsUrl !== undefined && object.wsUrl !== null) { - message.wsUrl = String(object.wsUrl); - } else { - message.wsUrl = ""; - } - if (object.token !== undefined && object.token !== null) { - message.token = String(object.token); - } else { - message.token = ""; - } - if (object.roomName !== undefined && object.roomName !== null) { - message.roomName = String(object.roomName); - } else { - message.roomName = ""; + fromJSON(object: any): ActiveSpeakerUpdate { + const message = { ...baseActiveSpeakerUpdate } as ActiveSpeakerUpdate; + message.speakers = []; + if (object.speakers !== undefined && object.speakers !== null) { + for (const e of object.speakers) { + message.speakers.push(SpeakerInfo.fromJSON(e)); + } } return message; }, - toJSON(message: RecordingTemplate): unknown { + toJSON(message: ActiveSpeakerUpdate): unknown { const obj: any = {}; - message.layout !== undefined && (obj.layout = message.layout); - message.wsUrl !== undefined && (obj.wsUrl = message.wsUrl); - message.token !== undefined && (obj.token = message.token); - message.roomName !== undefined && (obj.roomName = message.roomName); + if (message.speakers) { + obj.speakers = message.speakers.map((e) => + e ? SpeakerInfo.toJSON(e) : undefined + ); + } else { + obj.speakers = []; + } return obj; }, - fromPartial(object: DeepPartial): RecordingTemplate { - const message = { ...baseRecordingTemplate } as RecordingTemplate; - if (object.layout !== undefined && object.layout !== null) { - message.layout = object.layout; - } else { - message.layout = ""; - } - if (object.wsUrl !== undefined && object.wsUrl !== null) { - message.wsUrl = object.wsUrl; - } else { - message.wsUrl = ""; - } - if (object.token !== undefined && object.token !== null) { - message.token = object.token; - } else { - message.token = ""; - } - if (object.roomName !== undefined && object.roomName !== null) { - message.roomName = object.roomName; - } else { - message.roomName = ""; + fromPartial(object: DeepPartial): ActiveSpeakerUpdate { + const message = { ...baseActiveSpeakerUpdate } as ActiveSpeakerUpdate; + message.speakers = []; + if (object.speakers !== undefined && object.speakers !== null) { + for (const e of object.speakers) { + message.speakers.push(SpeakerInfo.fromPartial(e)); + } } return message; }, }; -const baseRecordingS3Output: object = { - bucket: "", - key: "", - accessKey: "", - secret: "", -}; +const baseSpeakerInfo: object = { sid: "", level: 0, active: false }; -export const RecordingS3Output = { +export const SpeakerInfo = { encode( - message: RecordingS3Output, + message: SpeakerInfo, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.bucket !== "") { - writer.uint32(10).string(message.bucket); - } - if (message.key !== "") { - writer.uint32(18).string(message.key); + if (message.sid !== "") { + writer.uint32(10).string(message.sid); } - if (message.accessKey !== "") { - writer.uint32(26).string(message.accessKey); + if (message.level !== 0) { + writer.uint32(21).float(message.level); } - if (message.secret !== "") { - writer.uint32(34).string(message.secret); + if (message.active === true) { + writer.uint32(24).bool(message.active); } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): RecordingS3Output { + decode(input: _m0.Reader | Uint8Array, length?: number): SpeakerInfo { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseRecordingS3Output } as RecordingS3Output; + const message = { ...baseSpeakerInfo } as SpeakerInfo; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.bucket = reader.string(); + message.sid = reader.string(); break; case 2: - message.key = reader.string(); + message.level = reader.float(); break; case 3: - message.accessKey = reader.string(); - break; - case 4: - message.secret = reader.string(); + message.active = reader.bool(); break; default: reader.skipType(tag & 7); @@ -1018,153 +1007,91 @@ export const RecordingS3Output = { return message; }, - fromJSON(object: any): RecordingS3Output { - const message = { ...baseRecordingS3Output } as RecordingS3Output; - if (object.bucket !== undefined && object.bucket !== null) { - message.bucket = String(object.bucket); - } else { - message.bucket = ""; - } - if (object.key !== undefined && object.key !== null) { - message.key = String(object.key); + fromJSON(object: any): SpeakerInfo { + const message = { ...baseSpeakerInfo } as SpeakerInfo; + if (object.sid !== undefined && object.sid !== null) { + message.sid = String(object.sid); } else { - message.key = ""; + message.sid = ""; } - if (object.accessKey !== undefined && object.accessKey !== null) { - message.accessKey = String(object.accessKey); + if (object.level !== undefined && object.level !== null) { + message.level = Number(object.level); } else { - message.accessKey = ""; + message.level = 0; } - if (object.secret !== undefined && object.secret !== null) { - message.secret = String(object.secret); + if (object.active !== undefined && object.active !== null) { + message.active = Boolean(object.active); } else { - message.secret = ""; + message.active = false; } return message; }, - toJSON(message: RecordingS3Output): unknown { + toJSON(message: SpeakerInfo): unknown { const obj: any = {}; - message.bucket !== undefined && (obj.bucket = message.bucket); - message.key !== undefined && (obj.key = message.key); - message.accessKey !== undefined && (obj.accessKey = message.accessKey); - message.secret !== undefined && (obj.secret = message.secret); + message.sid !== undefined && (obj.sid = message.sid); + message.level !== undefined && (obj.level = message.level); + message.active !== undefined && (obj.active = message.active); return obj; }, - fromPartial(object: DeepPartial): RecordingS3Output { - const message = { ...baseRecordingS3Output } as RecordingS3Output; - if (object.bucket !== undefined && object.bucket !== null) { - message.bucket = object.bucket; - } else { - message.bucket = ""; - } - if (object.key !== undefined && object.key !== null) { - message.key = object.key; + fromPartial(object: DeepPartial): SpeakerInfo { + const message = { ...baseSpeakerInfo } as SpeakerInfo; + if (object.sid !== undefined && object.sid !== null) { + message.sid = object.sid; } else { - message.key = ""; + message.sid = ""; } - if (object.accessKey !== undefined && object.accessKey !== null) { - message.accessKey = object.accessKey; + if (object.level !== undefined && object.level !== null) { + message.level = object.level; } else { - message.accessKey = ""; + message.level = 0; } - if (object.secret !== undefined && object.secret !== null) { - message.secret = object.secret; + if (object.active !== undefined && object.active !== null) { + message.active = object.active; } else { - message.secret = ""; + message.active = false; } return message; }, }; -const baseRecordingOptions: object = { - preset: "", - inputWidth: 0, - inputHeight: 0, - outputWidth: 0, - outputHeight: 0, - depth: 0, - framerate: 0, - audioBitrate: 0, - audioFrequency: 0, - videoBitrate: 0, -}; +const baseUserPacket: object = { participantSid: "", destinationSids: "" }; -export const RecordingOptions = { +export const UserPacket = { encode( - message: RecordingOptions, + message: UserPacket, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.preset !== "") { - writer.uint32(10).string(message.preset); + if (message.participantSid !== "") { + writer.uint32(10).string(message.participantSid); } - if (message.inputWidth !== 0) { - writer.uint32(16).int32(message.inputWidth); + if (message.payload.length !== 0) { + writer.uint32(18).bytes(message.payload); } - if (message.inputHeight !== 0) { - writer.uint32(24).int32(message.inputHeight); - } - if (message.outputWidth !== 0) { - writer.uint32(32).int32(message.outputWidth); - } - if (message.outputHeight !== 0) { - writer.uint32(40).int32(message.outputHeight); - } - if (message.depth !== 0) { - writer.uint32(48).int32(message.depth); - } - if (message.framerate !== 0) { - writer.uint32(56).int32(message.framerate); - } - if (message.audioBitrate !== 0) { - writer.uint32(64).int32(message.audioBitrate); - } - if (message.audioFrequency !== 0) { - writer.uint32(72).int32(message.audioFrequency); - } - if (message.videoBitrate !== 0) { - writer.uint32(80).int32(message.videoBitrate); + for (const v of message.destinationSids) { + writer.uint32(26).string(v!); } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): RecordingOptions { + decode(input: _m0.Reader | Uint8Array, length?: number): UserPacket { const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseRecordingOptions } as RecordingOptions; + const message = { ...baseUserPacket } as UserPacket; + message.destinationSids = []; + message.payload = new Uint8Array(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.preset = reader.string(); + message.participantSid = reader.string(); break; case 2: - message.inputWidth = reader.int32(); + message.payload = reader.bytes(); break; case 3: - message.inputHeight = reader.int32(); - break; - case 4: - message.outputWidth = reader.int32(); - break; - case 5: - message.outputHeight = reader.int32(); - break; - case 6: - message.depth = reader.int32(); - break; - case 7: - message.framerate = reader.int32(); - break; - case 8: - message.audioBitrate = reader.int32(); - break; - case 9: - message.audioFrequency = reader.int32(); - break; - case 10: - message.videoBitrate = reader.int32(); + message.destinationSids.push(reader.string()); break; default: reader.skipType(tag & 7); @@ -1174,133 +1101,65 @@ export const RecordingOptions = { return message; }, - fromJSON(object: any): RecordingOptions { - const message = { ...baseRecordingOptions } as RecordingOptions; - if (object.preset !== undefined && object.preset !== null) { - message.preset = String(object.preset); - } else { - message.preset = ""; - } - if (object.inputWidth !== undefined && object.inputWidth !== null) { - message.inputWidth = Number(object.inputWidth); - } else { - message.inputWidth = 0; - } - if (object.inputHeight !== undefined && object.inputHeight !== null) { - message.inputHeight = Number(object.inputHeight); - } else { - message.inputHeight = 0; - } - if (object.outputWidth !== undefined && object.outputWidth !== null) { - message.outputWidth = Number(object.outputWidth); + fromJSON(object: any): UserPacket { + const message = { ...baseUserPacket } as UserPacket; + message.destinationSids = []; + message.payload = new Uint8Array(); + if (object.participantSid !== undefined && object.participantSid !== null) { + message.participantSid = String(object.participantSid); } else { - message.outputWidth = 0; + message.participantSid = ""; } - if (object.outputHeight !== undefined && object.outputHeight !== null) { - message.outputHeight = Number(object.outputHeight); - } else { - message.outputHeight = 0; - } - if (object.depth !== undefined && object.depth !== null) { - message.depth = Number(object.depth); - } else { - message.depth = 0; - } - if (object.framerate !== undefined && object.framerate !== null) { - message.framerate = Number(object.framerate); - } else { - message.framerate = 0; - } - if (object.audioBitrate !== undefined && object.audioBitrate !== null) { - message.audioBitrate = Number(object.audioBitrate); - } else { - message.audioBitrate = 0; - } - if (object.audioFrequency !== undefined && object.audioFrequency !== null) { - message.audioFrequency = Number(object.audioFrequency); - } else { - message.audioFrequency = 0; + if (object.payload !== undefined && object.payload !== null) { + message.payload = bytesFromBase64(object.payload); } - if (object.videoBitrate !== undefined && object.videoBitrate !== null) { - message.videoBitrate = Number(object.videoBitrate); - } else { - message.videoBitrate = 0; + if ( + object.destinationSids !== undefined && + object.destinationSids !== null + ) { + for (const e of object.destinationSids) { + message.destinationSids.push(String(e)); + } } return message; }, - toJSON(message: RecordingOptions): unknown { + toJSON(message: UserPacket): unknown { const obj: any = {}; - message.preset !== undefined && (obj.preset = message.preset); - message.inputWidth !== undefined && (obj.inputWidth = message.inputWidth); - message.inputHeight !== undefined && - (obj.inputHeight = message.inputHeight); - message.outputWidth !== undefined && - (obj.outputWidth = message.outputWidth); - message.outputHeight !== undefined && - (obj.outputHeight = message.outputHeight); - message.depth !== undefined && (obj.depth = message.depth); - message.framerate !== undefined && (obj.framerate = message.framerate); - message.audioBitrate !== undefined && - (obj.audioBitrate = message.audioBitrate); - message.audioFrequency !== undefined && - (obj.audioFrequency = message.audioFrequency); - message.videoBitrate !== undefined && - (obj.videoBitrate = message.videoBitrate); + message.participantSid !== undefined && + (obj.participantSid = message.participantSid); + message.payload !== undefined && + (obj.payload = base64FromBytes( + message.payload !== undefined ? message.payload : new Uint8Array() + )); + if (message.destinationSids) { + obj.destinationSids = message.destinationSids.map((e) => e); + } else { + obj.destinationSids = []; + } return obj; }, - fromPartial(object: DeepPartial): RecordingOptions { - const message = { ...baseRecordingOptions } as RecordingOptions; - if (object.preset !== undefined && object.preset !== null) { - message.preset = object.preset; - } else { - message.preset = ""; - } - if (object.inputWidth !== undefined && object.inputWidth !== null) { - message.inputWidth = object.inputWidth; + fromPartial(object: DeepPartial): UserPacket { + const message = { ...baseUserPacket } as UserPacket; + message.destinationSids = []; + if (object.participantSid !== undefined && object.participantSid !== null) { + message.participantSid = object.participantSid; } else { - message.inputWidth = 0; + message.participantSid = ""; } - if (object.inputHeight !== undefined && object.inputHeight !== null) { - message.inputHeight = object.inputHeight; + if (object.payload !== undefined && object.payload !== null) { + message.payload = object.payload; } else { - message.inputHeight = 0; + message.payload = new Uint8Array(); } - if (object.outputWidth !== undefined && object.outputWidth !== null) { - message.outputWidth = object.outputWidth; - } else { - message.outputWidth = 0; - } - if (object.outputHeight !== undefined && object.outputHeight !== null) { - message.outputHeight = object.outputHeight; - } else { - message.outputHeight = 0; - } - if (object.depth !== undefined && object.depth !== null) { - message.depth = object.depth; - } else { - message.depth = 0; - } - if (object.framerate !== undefined && object.framerate !== null) { - message.framerate = object.framerate; - } else { - message.framerate = 0; - } - if (object.audioBitrate !== undefined && object.audioBitrate !== null) { - message.audioBitrate = object.audioBitrate; - } else { - message.audioBitrate = 0; - } - if (object.audioFrequency !== undefined && object.audioFrequency !== null) { - message.audioFrequency = object.audioFrequency; - } else { - message.audioFrequency = 0; - } - if (object.videoBitrate !== undefined && object.videoBitrate !== null) { - message.videoBitrate = object.videoBitrate; - } else { - message.videoBitrate = 0; + if ( + object.destinationSids !== undefined && + object.destinationSids !== null + ) { + for (const e of object.destinationSids) { + message.destinationSids.push(e); + } } return message; }, @@ -1333,13 +1192,20 @@ const btoa: (bin: string) => string = ((bin) => globalThis.Buffer.from(bin, "binary").toString("base64")); function base64FromBytes(arr: Uint8Array): string { const bin: string[] = []; - for (let i = 0; i < arr.byteLength; ++i) { - bin.push(String.fromCharCode(arr[i])); + for (const byte of arr) { + bin.push(String.fromCharCode(byte)); } return btoa(bin.join("")); } -type Builtin = Date | Function | Uint8Array | string | number | undefined; +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; export type DeepPartial = T extends Builtin ? T : T extends Array diff --git a/src/proto/livekit_rtc.ts b/src/proto/livekit_rtc.ts index fdae948849..85b744f901 100644 --- a/src/proto/livekit_rtc.ts +++ b/src/proto/livekit_rtc.ts @@ -6,6 +6,7 @@ import { Room, ParticipantInfo, TrackInfo, + ActiveSpeakerUpdate, trackTypeFromJSON, trackTypeToJSON, } from "./livekit_models"; @@ -118,6 +119,8 @@ export interface SignalResponse { speaker?: ActiveSpeakerUpdate | undefined; /** Immediately terminate session */ leave?: LeaveRequest | undefined; + /** server initiated mute */ + mute?: MuteTrackRequest | undefined; } export interface AddTrackRequest { @@ -169,18 +172,6 @@ export interface ParticipantUpdate { participants: ParticipantInfo[]; } -export interface ActiveSpeakerUpdate { - speakers: SpeakerInfo[]; -} - -export interface SpeakerInfo { - sid: string; - /** audio level, 0-1.0, 1 is loudest */ - level: number; - /** true if speaker is currently active */ - active: boolean; -} - export interface UpdateSubscription { trackSids: string[]; subscribe: boolean; @@ -206,54 +197,6 @@ export interface ICEServer { credential: string; } -/** new DataPacket API */ -export interface DataPacket { - kind: DataPacket_Kind; - user?: UserPacket | undefined; - speaker?: ActiveSpeakerUpdate | undefined; -} - -export enum DataPacket_Kind { - RELIABLE = 0, - LOSSY = 1, - UNRECOGNIZED = -1, -} - -export function dataPacket_KindFromJSON(object: any): DataPacket_Kind { - switch (object) { - case 0: - case "RELIABLE": - return DataPacket_Kind.RELIABLE; - case 1: - case "LOSSY": - return DataPacket_Kind.LOSSY; - case -1: - case "UNRECOGNIZED": - default: - return DataPacket_Kind.UNRECOGNIZED; - } -} - -export function dataPacket_KindToJSON(object: DataPacket_Kind): string { - switch (object) { - case DataPacket_Kind.RELIABLE: - return "RELIABLE"; - case DataPacket_Kind.LOSSY: - return "LOSSY"; - default: - return "UNKNOWN"; - } -} - -export interface UserPacket { - /** participant ID of user that sent the message */ - participantSid: string; - /** user defined payload */ - payload: Uint8Array; - /** the ID of the participants who will receive the message (the message will be sent to all the people in the room if this variable is empty) */ - destinationSids: string[]; -} - const baseSignalRequest: object = {}; export const SignalRequest = { @@ -552,6 +495,9 @@ export const SignalResponse = { if (message.leave !== undefined) { LeaveRequest.encode(message.leave, writer.uint32(66).fork()).ldelim(); } + if (message.mute !== undefined) { + MuteTrackRequest.encode(message.mute, writer.uint32(74).fork()).ldelim(); + } return writer; }, @@ -589,6 +535,9 @@ export const SignalResponse = { case 8: message.leave = LeaveRequest.decode(reader, reader.uint32()); break; + case 9: + message.mute = MuteTrackRequest.decode(reader, reader.uint32()); + break; default: reader.skipType(tag & 7); break; @@ -641,6 +590,11 @@ export const SignalResponse = { } else { message.leave = undefined; } + if (object.mute !== undefined && object.mute !== null) { + message.mute = MuteTrackRequest.fromJSON(object.mute); + } else { + message.mute = undefined; + } return message; }, @@ -676,6 +630,10 @@ export const SignalResponse = { (obj.leave = message.leave ? LeaveRequest.toJSON(message.leave) : undefined); + message.mute !== undefined && + (obj.mute = message.mute + ? MuteTrackRequest.toJSON(message.mute) + : undefined); return obj; }, @@ -723,6 +681,11 @@ export const SignalResponse = { } else { message.leave = undefined; } + if (object.mute !== undefined && object.mute !== null) { + message.mute = MuteTrackRequest.fromPartial(object.mute); + } else { + message.mute = undefined; + } return message; }, }; @@ -1505,165 +1468,6 @@ export const ParticipantUpdate = { }, }; -const baseActiveSpeakerUpdate: object = {}; - -export const ActiveSpeakerUpdate = { - encode( - message: ActiveSpeakerUpdate, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - for (const v of message.speakers) { - SpeakerInfo.encode(v!, writer.uint32(10).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): ActiveSpeakerUpdate { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseActiveSpeakerUpdate } as ActiveSpeakerUpdate; - message.speakers = []; - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.speakers.push(SpeakerInfo.decode(reader, reader.uint32())); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }, - - fromJSON(object: any): ActiveSpeakerUpdate { - const message = { ...baseActiveSpeakerUpdate } as ActiveSpeakerUpdate; - message.speakers = []; - if (object.speakers !== undefined && object.speakers !== null) { - for (const e of object.speakers) { - message.speakers.push(SpeakerInfo.fromJSON(e)); - } - } - return message; - }, - - toJSON(message: ActiveSpeakerUpdate): unknown { - const obj: any = {}; - if (message.speakers) { - obj.speakers = message.speakers.map((e) => - e ? SpeakerInfo.toJSON(e) : undefined - ); - } else { - obj.speakers = []; - } - return obj; - }, - - fromPartial(object: DeepPartial): ActiveSpeakerUpdate { - const message = { ...baseActiveSpeakerUpdate } as ActiveSpeakerUpdate; - message.speakers = []; - if (object.speakers !== undefined && object.speakers !== null) { - for (const e of object.speakers) { - message.speakers.push(SpeakerInfo.fromPartial(e)); - } - } - return message; - }, -}; - -const baseSpeakerInfo: object = { sid: "", level: 0, active: false }; - -export const SpeakerInfo = { - encode( - message: SpeakerInfo, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - if (message.sid !== "") { - writer.uint32(10).string(message.sid); - } - if (message.level !== 0) { - writer.uint32(21).float(message.level); - } - if (message.active === true) { - writer.uint32(24).bool(message.active); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): SpeakerInfo { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseSpeakerInfo } as SpeakerInfo; - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.sid = reader.string(); - break; - case 2: - message.level = reader.float(); - break; - case 3: - message.active = reader.bool(); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }, - - fromJSON(object: any): SpeakerInfo { - const message = { ...baseSpeakerInfo } as SpeakerInfo; - if (object.sid !== undefined && object.sid !== null) { - message.sid = String(object.sid); - } else { - message.sid = ""; - } - if (object.level !== undefined && object.level !== null) { - message.level = Number(object.level); - } else { - message.level = 0; - } - if (object.active !== undefined && object.active !== null) { - message.active = Boolean(object.active); - } else { - message.active = false; - } - return message; - }, - - toJSON(message: SpeakerInfo): unknown { - const obj: any = {}; - message.sid !== undefined && (obj.sid = message.sid); - message.level !== undefined && (obj.level = message.level); - message.active !== undefined && (obj.active = message.active); - return obj; - }, - - fromPartial(object: DeepPartial): SpeakerInfo { - const message = { ...baseSpeakerInfo } as SpeakerInfo; - if (object.sid !== undefined && object.sid !== null) { - message.sid = object.sid; - } else { - message.sid = ""; - } - if (object.level !== undefined && object.level !== null) { - message.level = object.level; - } else { - message.level = 0; - } - if (object.active !== undefined && object.active !== null) { - message.active = object.active; - } else { - message.active = false; - } - return message; - }, -}; - const baseUpdateSubscription: object = { trackSids: "", subscribe: false }; export const UpdateSubscription = { @@ -2008,249 +1812,14 @@ export const ICEServer = { }, }; -const baseDataPacket: object = { kind: 0 }; - -export const DataPacket = { - encode( - message: DataPacket, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - if (message.kind !== 0) { - writer.uint32(8).int32(message.kind); - } - if (message.user !== undefined) { - UserPacket.encode(message.user, writer.uint32(18).fork()).ldelim(); - } - if (message.speaker !== undefined) { - ActiveSpeakerUpdate.encode( - message.speaker, - writer.uint32(26).fork() - ).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): DataPacket { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseDataPacket } as DataPacket; - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.kind = reader.int32() as any; - break; - case 2: - message.user = UserPacket.decode(reader, reader.uint32()); - break; - case 3: - message.speaker = ActiveSpeakerUpdate.decode(reader, reader.uint32()); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }, - - fromJSON(object: any): DataPacket { - const message = { ...baseDataPacket } as DataPacket; - if (object.kind !== undefined && object.kind !== null) { - message.kind = dataPacket_KindFromJSON(object.kind); - } else { - message.kind = 0; - } - if (object.user !== undefined && object.user !== null) { - message.user = UserPacket.fromJSON(object.user); - } else { - message.user = undefined; - } - if (object.speaker !== undefined && object.speaker !== null) { - message.speaker = ActiveSpeakerUpdate.fromJSON(object.speaker); - } else { - message.speaker = undefined; - } - return message; - }, - - toJSON(message: DataPacket): unknown { - const obj: any = {}; - message.kind !== undefined && - (obj.kind = dataPacket_KindToJSON(message.kind)); - message.user !== undefined && - (obj.user = message.user ? UserPacket.toJSON(message.user) : undefined); - message.speaker !== undefined && - (obj.speaker = message.speaker - ? ActiveSpeakerUpdate.toJSON(message.speaker) - : undefined); - return obj; - }, - - fromPartial(object: DeepPartial): DataPacket { - const message = { ...baseDataPacket } as DataPacket; - if (object.kind !== undefined && object.kind !== null) { - message.kind = object.kind; - } else { - message.kind = 0; - } - if (object.user !== undefined && object.user !== null) { - message.user = UserPacket.fromPartial(object.user); - } else { - message.user = undefined; - } - if (object.speaker !== undefined && object.speaker !== null) { - message.speaker = ActiveSpeakerUpdate.fromPartial(object.speaker); - } else { - message.speaker = undefined; - } - return message; - }, -}; - -const baseUserPacket: object = { participantSid: "", destinationSids: "" }; - -export const UserPacket = { - encode( - message: UserPacket, - writer: _m0.Writer = _m0.Writer.create() - ): _m0.Writer { - if (message.participantSid !== "") { - writer.uint32(10).string(message.participantSid); - } - if (message.payload.length !== 0) { - writer.uint32(18).bytes(message.payload); - } - for (const v of message.destinationSids) { - writer.uint32(26).string(v!); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): UserPacket { - const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseUserPacket } as UserPacket; - message.destinationSids = []; - message.payload = new Uint8Array(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - message.participantSid = reader.string(); - break; - case 2: - message.payload = reader.bytes(); - break; - case 3: - message.destinationSids.push(reader.string()); - break; - default: - reader.skipType(tag & 7); - break; - } - } - return message; - }, - - fromJSON(object: any): UserPacket { - const message = { ...baseUserPacket } as UserPacket; - message.destinationSids = []; - message.payload = new Uint8Array(); - if (object.participantSid !== undefined && object.participantSid !== null) { - message.participantSid = String(object.participantSid); - } else { - message.participantSid = ""; - } - if (object.payload !== undefined && object.payload !== null) { - message.payload = bytesFromBase64(object.payload); - } - if ( - object.destinationSids !== undefined && - object.destinationSids !== null - ) { - for (const e of object.destinationSids) { - message.destinationSids.push(String(e)); - } - } - return message; - }, - - toJSON(message: UserPacket): unknown { - const obj: any = {}; - message.participantSid !== undefined && - (obj.participantSid = message.participantSid); - message.payload !== undefined && - (obj.payload = base64FromBytes( - message.payload !== undefined ? message.payload : new Uint8Array() - )); - if (message.destinationSids) { - obj.destinationSids = message.destinationSids.map((e) => e); - } else { - obj.destinationSids = []; - } - return obj; - }, - - fromPartial(object: DeepPartial): UserPacket { - const message = { ...baseUserPacket } as UserPacket; - message.destinationSids = []; - if (object.participantSid !== undefined && object.participantSid !== null) { - message.participantSid = object.participantSid; - } else { - message.participantSid = ""; - } - if (object.payload !== undefined && object.payload !== null) { - message.payload = object.payload; - } else { - message.payload = new Uint8Array(); - } - if ( - object.destinationSids !== undefined && - object.destinationSids !== null - ) { - for (const e of object.destinationSids) { - message.destinationSids.push(e); - } - } - return message; - }, -}; - -declare var self: any | undefined; -declare var window: any | undefined; -var globalThis: any = (() => { - if (typeof globalThis !== "undefined") return globalThis; - if (typeof self !== "undefined") return self; - if (typeof window !== "undefined") return window; - if (typeof global !== "undefined") return global; - throw "Unable to locate global object"; -})(); - -const atob: (b64: string) => string = - globalThis.atob || - ((b64) => globalThis.Buffer.from(b64, "base64").toString("binary")); -function bytesFromBase64(b64: string): Uint8Array { - const bin = atob(b64); - const arr = new Uint8Array(bin.length); - for (let i = 0; i < bin.length; ++i) { - arr[i] = bin.charCodeAt(i); - } - return arr; -} - -const btoa: (bin: string) => string = - globalThis.btoa || - ((bin) => globalThis.Buffer.from(bin, "binary").toString("base64")); -function base64FromBytes(arr: Uint8Array): string { - const bin: string[] = []; - for (let i = 0; i < arr.byteLength; ++i) { - bin.push(String.fromCharCode(arr[i])); - } - return btoa(bin.join("")); -} - -type Builtin = Date | Function | Uint8Array | string | number | undefined; +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; export type DeepPartial = T extends Builtin ? T : T extends Array diff --git a/src/room/RTCEngine.ts b/src/room/RTCEngine.ts index 633f81e544..fbd24462d0 100644 --- a/src/room/RTCEngine.ts +++ b/src/room/RTCEngine.ts @@ -1,10 +1,9 @@ import { EventEmitter } from 'events'; import log from 'loglevel'; import { SignalClient, SignalOptions } from '../api/SignalClient'; -import { TrackInfo } from '../proto/livekit_models'; +import { DataPacket, TrackInfo } from '../proto/livekit_models'; import { AddTrackRequest, - DataPacket, JoinResponse, SignalTarget, TrackPublishedResponse, @@ -12,7 +11,7 @@ import { import { ConnectionError, TrackInvalidError, UnexpectedConnectionState } from './errors'; import { EngineEvent } from './events'; import PCTransport from './PCTransport'; -import { sleep, useLegacyAPI } from './utils'; +import { sleep } from './utils'; const lossyDataChannel = '_lossy'; const reliableDataChannel = '_reliable'; @@ -27,8 +26,6 @@ export default class RTCEngine extends EventEmitter { rtcConfig: RTCConfiguration; - useLegacy: boolean; - lossyDC?: RTCDataChannel; reliableDC?: RTCDataChannel; @@ -50,15 +47,13 @@ export default class RTCEngine extends EventEmitter { super(); this.client = client; this.rtcConfig = config || {}; - this.useLegacy = useLegacyAPI(); - log.trace('creating RTCEngine', 'useLegacy', this.useLegacy); } async join(url: string, token: string, opts?: SignalOptions): Promise { this.url = url; this.token = token; - const joinResponse = await this.client.join(url, token, this.useLegacy, opts); + const joinResponse = await this.client.join(url, token, opts); this.isClosed = false; if (joinResponse.iceServers && !this.rtcConfig.iceServers) { @@ -76,10 +71,6 @@ export default class RTCEngine extends EventEmitter { // update ICE servers before creating PeerConnection if (!this.publisher) { - const conf: any = this.rtcConfig; - if (this.useLegacy) { - conf.sdpSemantics = 'plan-b'; - } this.publisher = new PCTransport(this.rtcConfig); this.subscriber = new PCTransport(this.rtcConfig); this.configure(); @@ -169,18 +160,9 @@ export default class RTCEngine extends EventEmitter { } }; - if (this.useLegacy) { - this.subscriber.pc.addEventListener('addstream', (ev: any) => { - const { stream } = ev; - stream.getTracks().forEach((t: MediaStreamTrack) => { - this.emitTrackEvent(t, stream); - }); - }); - } else { - this.subscriber.pc.ontrack = (ev: RTCTrackEvent) => { - this.emitTrackEvent(ev.track, ev.streams[0], ev.receiver); - }; - } + this.subscriber.pc.ontrack = (ev: RTCTrackEvent) => { + this.emitTrackEvent(ev.track, ev.streams[0], ev.receiver); + }; // data channels this.lossyDC = this.publisher.pc.createDataChannel(lossyDataChannel, { @@ -265,6 +247,10 @@ export default class RTCEngine extends EventEmitter { this.close(); this.emit(EngineEvent.Disconnected); }; + + this.client.onRemoteMuteChanged = (trackSid, muted) => { + this.emit(EngineEvent.RemoteMuteChanged, trackSid, muted); + }; } private handleDataMessage = async (message: MessageEvent) => { diff --git a/src/room/Room.ts b/src/room/Room.ts index ec80be8a25..e4775825d6 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -2,10 +2,9 @@ import { EventEmitter } from 'events'; import log from 'loglevel'; import { SignalClient, SignalOptions } from '../api/SignalClient'; import { - ParticipantInfo, - ParticipantInfo_State, + DataPacket_Kind, ParticipantInfo, + ParticipantInfo_State, SpeakerInfo, UserPacket, } from '../proto/livekit_models'; -import { DataPacket_Kind, SpeakerInfo, UserPacket } from '../proto/livekit_rtc'; import { ConnectionError, UnsupportedServer } from './errors'; import { EngineEvent, ParticipantEvent, RoomEvent, TrackEvent, diff --git a/src/room/events.ts b/src/room/events.ts index 61edf93a02..0cd5d34f51 100644 --- a/src/room/events.ts +++ b/src/room/events.ts @@ -147,6 +147,7 @@ export enum EngineEvent { MediaTrackAdded = 'mediaTrackAdded', SpeakersUpdate = 'speakersUpdate', DataPacketReceived = 'dataPacketReceived', + RemoteMuteChanged = 'remoteMuteChanged', } export enum TrackEvent { diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index 31b2b4b952..0b7f73d029 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -1,12 +1,11 @@ import log from 'loglevel'; -import { ParticipantInfo } from '../../proto/livekit_models'; -import { AddTrackRequest, DataPacket, DataPacket_Kind } from '../../proto/livekit_rtc'; +import { DataPacket, DataPacket_Kind } from '../../proto/livekit_models'; +import { AddTrackRequest } from '../../proto/livekit_rtc'; import { - PublishDataError, TrackInvalidError, UnexpectedConnectionState, } from '../errors'; -import { ParticipantEvent, TrackEvent } from '../events'; +import { EngineEvent, ParticipantEvent, TrackEvent } from '../events'; import RTCEngine from '../RTCEngine'; import LocalAudioTrack from '../track/LocalAudioTrack'; import LocalTrack from '../track/LocalTrack'; @@ -39,6 +38,18 @@ export default class LocalParticipant extends Participant { this.videoTracks = new Map(); this.tracks = new Map(); this.engine = engine; + + this.engine.on(EngineEvent.RemoteMuteChanged, (trackSid: string, muted: boolean) => { + const pub = this.tracks.get(trackSid); + if (!pub || !pub.track) { + return; + } + if (muted) { + pub.mute(); + } else { + pub.unmute(); + } + }); } /** @@ -226,25 +237,6 @@ export default class LocalParticipant extends Participant { return null; } - /** @internal */ - updateInfo(info: ParticipantInfo) { - super.updateInfo(info); - - // match local track mute status to server - info.tracks.forEach((ti) => { - const pub = this.tracks.get(ti.sid); - if (!pub) { - return; - } - - if (ti.muted && !pub.isMuted) { - pub.mute(); - } else if (!ti.muted && pub.isMuted) { - pub.unmute(); - } - }); - } - /** * Publish a new data payload to the room. Data will be forwarded to each * participant in the room if the destination argument is empty @@ -258,10 +250,6 @@ export default class LocalParticipant extends Participant { */ publishData(data: Uint8Array, kind: DataPacket_Kind, destination?: RemoteParticipant[] | string[]) { - if (data.length > 14_000) { - throw new PublishDataError('data cannot be larger than 14k'); - } - const dest: string[] = []; if (destination !== undefined) { destination.forEach((val : any) => { @@ -291,14 +279,14 @@ export default class LocalParticipant extends Participant { } /** @internal */ - onTrackUnmuted = (track: LocalVideoTrack | LocalAudioTrack) => { + onTrackUnmuted = (track: LocalTrack) => { this.onTrackMuted(track, false); }; // when the local track changes in mute status, we'll notify server as such /** @internal */ onTrackMuted = ( - track: LocalVideoTrack | LocalAudioTrack, + track: LocalTrack, muted?: boolean, ) => { if (muted === undefined) { diff --git a/yarn.lock b/yarn.lock index f561d83106..0ce458bc8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4285,7 +4285,7 @@ ts-poet@^4.5.0: lodash "^4.17.15" prettier "^2.0.2" -ts-proto-descriptors@^1.2.0: +ts-proto-descriptors@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== @@ -4293,17 +4293,17 @@ ts-proto-descriptors@^1.2.0: long "^4.0.0" protobufjs "^6.8.8" -ts-proto@1.79.1: - version "1.79.1" - resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.79.1.tgz#825520e503b4e551a8b86fa63aa83d2a8504418a" - integrity sha512-kSekfynENO6MjFl0G+1hvG8Ew3g1JlHN5QWwSfw1Rk09wf5Jg9uZ5qjk9YmWpQ0KFxDsocajV67RjFM/432Flw== +ts-proto@1.82.5: + version "1.82.5" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.82.5.tgz#9dd11785e314e2ae5467c09acdb739de010487b7" + integrity sha512-RqlTxosROuYdeWRLa6Qu8Wz9dc3fpAh+R8PELUlhSSZlUiEkTos662SgKHOB8UoJd9CBMSyJPRC7z8k2WvWzhw== dependencies: "@types/object-hash" "^1.3.0" dataloader "^1.4.0" object-hash "^1.3.1" protobufjs "^6.8.8" ts-poet "^4.5.0" - ts-proto-descriptors "^1.2.0" + ts-proto-descriptors "^1.2.1" tsconfig-paths@^3.9.0: version "3.10.1"