/* * https://github.com/morethanwords/tweb * Copyright (C) 2019-2021 Eduard Kuzmenko * https://github.com/morethanwords/tweb/blob/master/LICENSE * * Originally from: * https://github.com/evgeny-nadymov/telegram-react * Copyright (C) 2018 Evgeny Nadymov * https://github.com/evgeny-nadymov/telegram-react/blob/master/LICENSE */ import indexOfAndSplice from '../../helpers/array/indexOfAndSplice'; import safeAssign from '../../helpers/object/safeAssign'; import { GroupCallParticipantVideoSourceGroup } from '../../layer'; import { fixMediaLineType, SDPBuilder, WebRTCLineType, WEBRTC_MEDIA_PORT } from './sdpBuilder'; import { AudioCodec, GroupCallConnectionTransport, Ssrc, UpdateGroupCallConnectionData, VideoCodec } from './types'; export class ConferenceEntry { public source: number; public sourceGroups: GroupCallParticipantVideoSourceGroup[]; public transceiver: RTCRtpTransceiver; public originalDirection: RTCRtpTransceiverDirection; public direction: RTCRtpTransceiverDirection; public port: string; public endpoint: string; public peerId: PeerId; public sendEntry: ConferenceEntry; public recvEntry: ConferenceEntry; constructor(public mid: string, public type: WebRTCLineType) { this.port = WEBRTC_MEDIA_PORT; } public setDirection(direction: RTCRtpTransceiverDirection) { if(!this.originalDirection) { this.originalDirection = direction; } return this.direction = direction; } public setPort(port: string) { return this.port = port; } public setEndpoint(endpoint: string) { return this.endpoint = endpoint; } public setPeerId(peerId: PeerId) { return this.peerId = peerId; } public createTransceiver(connection: RTCPeerConnection, init?: RTCRtpTransceiverInit) { if(init?.direction) { this.setDirection(init.direction); } return this.transceiver = connection.addTransceiver(fixMediaLineType(this.type), init); } public setSource(source: number | GroupCallParticipantVideoSourceGroup[]) { let sourceGroups: GroupCallParticipantVideoSourceGroup[]; if(Array.isArray(source)) { if(!source[0]) return; sourceGroups = source; source = sourceGroups[0].sources[0]; } this.sourceGroups = sourceGroups; return this.source = source; } public shouldBeSkipped(isAnswer?: boolean) { return isAnswer && this.direction === 'inactive'; } } export function generateSsrc(type: WebRTCLineType, source: number | GroupCallParticipantVideoSourceGroup[], endpoint?: string): Ssrc { let sourceGroups: GroupCallParticipantVideoSourceGroup[]; if(Array.isArray(source)) { if(!source[0]) return; sourceGroups = source; source = sourceGroups[0].sources[0]; } return { endpoint, type, source, sourceGroups, }; } export default class LocalConferenceDescription implements UpdateGroupCallConnectionData { public readonly sessionId: string; // public ssrcs: Ssrc[]; public readonly transport: GroupCallConnectionTransport; public readonly audio?: AudioCodec; public readonly video: VideoCodec; public readonly screencast?: VideoCodec; private maxSeenId: number; public readonly entries: ConferenceEntry[]; private entriesByMid: Map; private entriesBySource: Map; private entriesByPeerId: Map>; constructor(public connection: RTCPeerConnection) { this.sessionId = '' + Date.now(); // this.ssrcs = []; this.maxSeenId = -1; this.entries = []; this.entriesByMid = new Map(); this.entriesBySource = new Map(); this.entriesByPeerId = new Map(); } public setData(data: UpdateGroupCallConnectionData) { return safeAssign(this, data); } public createEntry(type: WebRTCLineType) { const mid = '' + ++this.maxSeenId; const entry = new ConferenceEntry(mid, type); this.entries.push(entry); this.entriesByMid.set(mid, entry); return entry; } public deleteEntry(entry: ConferenceEntry) { indexOfAndSplice(this.entries, entry); this.entriesByMid.delete(entry.mid); this.entriesBySource.delete(entry.source); const set = this.entriesByPeerId.get(entry.peerId); if(set) { set.delete(entry); if(!set.size) { this.entriesByPeerId.delete(entry.peerId); } } } public setEntrySource(entry: ConferenceEntry, source: Parameters[0]) { entry.setSource(source); this.entriesBySource.set(entry.source, entry); } public setEntryPeerId(entry: ConferenceEntry, peerId: ConferenceEntry['peerId']) { entry.setPeerId(peerId); let set = this.entriesByPeerId.get(peerId); if(!set) { this.entriesByPeerId.set(peerId, set = new Set()); } set.add(entry); } public findEntry(verify: Parameters[0]) { return this.entries.find(verify); } public findFreeSendRecvEntry(type: WebRTCLineType, isSending: boolean) { let entry = this.entries.find(entry => { return entry.direction === 'sendrecv' && entry.type === type && !(isSending ? entry.sendEntry : entry.recvEntry); }); if(!entry) { entry = this.createEntry(type); entry.setDirection('sendrecv'); } return entry; } public getEntryByMid(mid: ConferenceEntry['mid']) { return this.entriesByMid.get(mid); } public getEntryBySource(source: ConferenceEntry['source']) { return this.entriesBySource.get(source); } public getEntriesByPeerId(peerId: ConferenceEntry['peerId']) { return this.entriesByPeerId.get(peerId); } public generateSdp(options: Omit[0], 'conference'>) { return SDPBuilder.fromConference({ conference: this, ...options }); } }