1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
|
import { MatrixEvent } from "../models/event";
import { RoomMember } from "../models/room-member";
import { MCallAnswer, MCallHangupReject } from "./callEventTypes";
import { CallFeed } from "./callFeed";
import { MatrixClient } from "../client";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { IScreensharingOpts } from "./mediaHandler";
import { GroupCallStats } from "./stats/groupCallStats";
interface CallOpts {
roomId: string;
invitee?: string;
client: MatrixClient;
/**
* Whether relay through TURN should be forced.
* @deprecated use opts.forceTURN when creating the matrix client
* since it's only possible to set this option on outbound calls.
*/
forceTURN?: boolean;
turnServers?: Array<TurnServer>;
opponentDeviceId?: string;
opponentSessionId?: string;
groupCallId?: string;
}
interface TurnServer {
urls: Array<string>;
username?: string;
password?: string;
ttl?: number;
}
interface AssertedIdentity {
id: string;
displayName: string;
}
export declare enum CallState {
Fledgling = "fledgling",
InviteSent = "invite_sent",
WaitLocalMedia = "wait_local_media",
CreateOffer = "create_offer",
CreateAnswer = "create_answer",
Connecting = "connecting",
Connected = "connected",
Ringing = "ringing",
Ended = "ended"
}
export declare enum CallType {
Voice = "voice",
Video = "video"
}
export declare enum CallDirection {
Inbound = "inbound",
Outbound = "outbound"
}
export declare enum CallParty {
Local = "local",
Remote = "remote"
}
export declare enum CallEvent {
Hangup = "hangup",
State = "state",
Error = "error",
Replaced = "replaced",
LocalHoldUnhold = "local_hold_unhold",
RemoteHoldUnhold = "remote_hold_unhold",
HoldUnhold = "hold_unhold",
FeedsChanged = "feeds_changed",
AssertedIdentityChanged = "asserted_identity_changed",
LengthChanged = "length_changed",
DataChannel = "datachannel",
SendVoipEvent = "send_voip_event"
}
export declare enum CallErrorCode {
/** The user chose to end the call */
UserHangup = "user_hangup",
/** An error code when the local client failed to create an offer. */
LocalOfferFailed = "local_offer_failed",
/**
* An error code when there is no local mic/camera to use. This may be because
* the hardware isn't plugged in, or the user has explicitly denied access.
*/
NoUserMedia = "no_user_media",
/**
* Error code used when a call event failed to send
* because unknown devices were present in the room
*/
UnknownDevices = "unknown_devices",
/**
* Error code used when we fail to send the invite
* for some reason other than there being unknown devices
*/
SendInvite = "send_invite",
/**
* An answer could not be created
*/
CreateAnswer = "create_answer",
/**
* An offer could not be created
*/
CreateOffer = "create_offer",
/**
* Error code used when we fail to send the answer
* for some reason other than there being unknown devices
*/
SendAnswer = "send_answer",
/**
* The session description from the other side could not be set
*/
SetRemoteDescription = "set_remote_description",
/**
* The session description from this side could not be set
*/
SetLocalDescription = "set_local_description",
/**
* A different device answered the call
*/
AnsweredElsewhere = "answered_elsewhere",
/**
* No media connection could be established to the other party
*/
IceFailed = "ice_failed",
/**
* The invite timed out whilst waiting for an answer
*/
InviteTimeout = "invite_timeout",
/**
* The call was replaced by another call
*/
Replaced = "replaced",
/**
* Signalling for the call could not be sent (other than the initial invite)
*/
SignallingFailed = "signalling_timeout",
/**
* The remote party is busy
*/
UserBusy = "user_busy",
/**
* We transferred the call off to somewhere else
*/
Transferred = "transferred",
/**
* A call from the same user was found with a new session id
*/
NewSession = "new_session"
}
export declare class CallError extends Error {
readonly code: string;
constructor(code: CallErrorCode, msg: string, err: Error);
}
export declare function genCallID(): string;
export interface VoipEvent {
type: "toDevice" | "sendEvent";
eventType: string;
userId?: string;
opponentDeviceId?: string;
roomId?: string;
content: Record<string, unknown>;
}
/**
* These now all have the call object as an argument. Why? Well, to know which call a given event is
* about you have three options:
* 1. Use a closure as the callback that remembers what call it's listening to. This can be
* a pain because you need to pass the listener function again when you remove the listener,
* which might be somewhere else.
* 2. Use not-very-well-known fact that EventEmitter sets 'this' to the emitter object in the
* callback. This doesn't really play well with modern Typescript and eslint and doesn't work
* with our pattern of re-emitting events.
* 3. Pass the object in question as an argument to the callback.
*
* Now that we have group calls which have to deal with multiple call objects, this will
* become more important, and I think methods 1 and 2 are just going to cause issues.
*/
export type CallEventHandlerMap = {
[CallEvent.DataChannel]: (channel: RTCDataChannel, call: MatrixCall) => void;
[CallEvent.FeedsChanged]: (feeds: CallFeed[], call: MatrixCall) => void;
[CallEvent.Replaced]: (newCall: MatrixCall, oldCall: MatrixCall) => void;
[CallEvent.Error]: (error: CallError, call: MatrixCall) => void;
[CallEvent.RemoteHoldUnhold]: (onHold: boolean, call: MatrixCall) => void;
[CallEvent.LocalHoldUnhold]: (onHold: boolean, call: MatrixCall) => void;
[CallEvent.LengthChanged]: (length: number, call: MatrixCall) => void;
[CallEvent.State]: (state: CallState, oldState: CallState, call: MatrixCall) => void;
[CallEvent.Hangup]: (call: MatrixCall) => void;
[CallEvent.AssertedIdentityChanged]: (call: MatrixCall) => void;
[CallEvent.HoldUnhold]: (onHold: boolean) => void;
[CallEvent.SendVoipEvent]: (event: VoipEvent, call: MatrixCall) => void;
};
export declare class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap> {
roomId: string;
callId: string;
invitee?: string;
hangupParty?: CallParty;
hangupReason?: string;
direction?: CallDirection;
ourPartyId: string;
peerConn?: RTCPeerConnection;
toDeviceSeq: number;
isPtt: boolean;
private _state;
private readonly client;
private readonly forceTURN?;
private readonly turnServers;
private candidateSendQueue;
private candidateSendTries;
private candidatesEnded;
private feeds;
private transceivers;
private inviteOrAnswerSent;
private waitForLocalAVStream;
private successor?;
private opponentMember?;
private opponentVersion?;
private opponentPartyId;
private opponentCaps?;
private iceDisconnectedTimeout?;
private inviteTimeout?;
private readonly removeTrackListeners;
private remoteOnHold;
private callStatsAtEnd?;
private makingOffer;
private ignoreOffer;
private responsePromiseChain?;
private remoteCandidateBuffer;
private remoteAssertedIdentity?;
private remoteSDPStreamMetadata?;
private callLengthInterval?;
private callStartTime?;
private opponentDeviceId?;
private opponentDeviceInfo?;
private opponentSessionId?;
groupCallId?: string;
private stopVideoTrackTimer?;
private readonly isOnlyDataChannelAllowed;
private stats;
/**
* Construct a new Matrix Call.
* @param opts - Config options.
*/
constructor(opts: CallOpts);
/**
* Place a voice call to this room.
* @throws If you have not specified a listener for 'error' events.
*/
placeVoiceCall(): Promise<void>;
/**
* Place a video call to this room.
* @throws If you have not specified a listener for 'error' events.
*/
placeVideoCall(): Promise<void>;
/**
* Create a datachannel using this call's peer connection.
* @param label - A human readable label for this datachannel
* @param options - An object providing configuration options for the data channel.
*/
createDataChannel(label: string, options: RTCDataChannelInit | undefined): RTCDataChannel;
getOpponentMember(): RoomMember | undefined;
getOpponentDeviceId(): string | undefined;
getOpponentSessionId(): string | undefined;
opponentCanBeTransferred(): boolean;
opponentSupportsDTMF(): boolean;
getRemoteAssertedIdentity(): AssertedIdentity | undefined;
get state(): CallState;
private set state(value);
get type(): CallType;
get hasLocalUserMediaVideoTrack(): boolean;
get hasRemoteUserMediaVideoTrack(): boolean;
get hasLocalUserMediaAudioTrack(): boolean;
get hasRemoteUserMediaAudioTrack(): boolean;
private get hasUserMediaAudioSender();
private get hasUserMediaVideoSender();
get localUsermediaFeed(): CallFeed | undefined;
get localScreensharingFeed(): CallFeed | undefined;
get localUsermediaStream(): MediaStream | undefined;
get localScreensharingStream(): MediaStream | undefined;
get remoteUsermediaFeed(): CallFeed | undefined;
get remoteScreensharingFeed(): CallFeed | undefined;
get remoteUsermediaStream(): MediaStream | undefined;
get remoteScreensharingStream(): MediaStream | undefined;
private getFeedByStreamId;
/**
* Returns an array of all CallFeeds
* @returns CallFeeds
*/
getFeeds(): Array<CallFeed>;
/**
* Returns an array of all local CallFeeds
* @returns local CallFeeds
*/
getLocalFeeds(): Array<CallFeed>;
/**
* Returns an array of all remote CallFeeds
* @returns remote CallFeeds
*/
getRemoteFeeds(): Array<CallFeed>;
private initOpponentCrypto;
/**
* Generates and returns localSDPStreamMetadata
* @returns localSDPStreamMetadata
*/
private getLocalSDPStreamMetadata;
/**
* Returns true if there are no incoming feeds,
* otherwise returns false
* @returns no incoming feeds
*/
noIncomingFeeds(): boolean;
private pushRemoteFeed;
/**
* This method is used ONLY if the other client doesn't support sending SDPStreamMetadata
*/
private pushRemoteFeedWithoutMetadata;
private pushNewLocalFeed;
/**
* Pushes supplied feed to the call
* @param callFeed - to push
* @param addToPeerConnection - whether to add the tracks to the peer connection
*/
pushLocalFeed(callFeed: CallFeed, addToPeerConnection?: boolean): void;
/**
* Removes local call feed from the call and its tracks from the peer
* connection
* @param callFeed - to remove
*/
removeLocalFeed(callFeed: CallFeed): void;
private deleteAllFeeds;
private deleteFeedByStream;
private deleteFeed;
getCurrentCallStats(): Promise<any[] | undefined>;
private collectCallStats;
/**
* Configure this call from an invite event. Used by MatrixClient.
* @param event - The m.call.invite event
*/
initWithInvite(event: MatrixEvent): Promise<void>;
/**
* Configure this call from a hangup or reject event. Used by MatrixClient.
* @param event - The m.call.hangup event
*/
initWithHangup(event: MatrixEvent): void;
private shouldAnswerWithMediaType;
/**
* Answer a call.
*/
answer(audio?: boolean, video?: boolean): Promise<void>;
answerWithCallFeeds(callFeeds: CallFeed[]): void;
/**
* Replace this call with a new call, e.g. for glare resolution. Used by
* MatrixClient.
* @param newCall - The new call.
*/
replacedBy(newCall: MatrixCall): void;
/**
* Hangup a call.
* @param reason - The reason why the call is being hung up.
* @param suppressEvent - True to suppress emitting an event.
*/
hangup(reason: CallErrorCode, suppressEvent: boolean): void;
/**
* Reject a call
* This used to be done by calling hangup, but is a separate method and protocol
* event as of MSC2746.
*/
reject(): void;
/**
* Adds an audio and/or video track - upgrades the call
* @param audio - should add an audio track
* @param video - should add an video track
*/
private upgradeCall;
/**
* Returns true if this.remoteSDPStreamMetadata is defined, otherwise returns false
* @returns can screenshare
*/
opponentSupportsSDPStreamMetadata(): boolean;
/**
* If there is a screensharing stream returns true, otherwise returns false
* @returns is screensharing
*/
isScreensharing(): boolean;
/**
* Starts/stops screensharing
* @param enabled - the desired screensharing state
* @param desktopCapturerSourceId - optional id of the desktop capturer source to use
* @returns new screensharing state
*/
setScreensharingEnabled(enabled: boolean, opts?: IScreensharingOpts): Promise<boolean>;
/**
* Starts/stops screensharing
* Should be used ONLY if the opponent doesn't support SDPStreamMetadata
* @param enabled - the desired screensharing state
* @param desktopCapturerSourceId - optional id of the desktop capturer source to use
* @returns new screensharing state
*/
private setScreensharingEnabledWithoutMetadataSupport;
/**
* Replaces/adds the tracks from the passed stream to the localUsermediaStream
* @param stream - to use a replacement for the local usermedia stream
*/
updateLocalUsermediaStream(stream: MediaStream, forceAudio?: boolean, forceVideo?: boolean): Promise<void>;
/**
* Set whether our outbound video should be muted or not.
* @param muted - True to mute the outbound video.
* @returns the new mute state
*/
setLocalVideoMuted(muted: boolean): Promise<boolean>;
/**
* Check if local video is muted.
*
* If there are multiple video tracks, <i>all</i> of the tracks need to be muted
* for this to return true. This means if there are no video tracks, this will
* return true.
* @returns True if the local preview video is muted, else false
* (including if the call is not set up yet).
*/
isLocalVideoMuted(): boolean;
/**
* Set whether the microphone should be muted or not.
* @param muted - True to mute the mic.
* @returns the new mute state
*/
setMicrophoneMuted(muted: boolean): Promise<boolean>;
/**
* Check if the microphone is muted.
*
* If there are multiple audio tracks, <i>all</i> of the tracks need to be muted
* for this to return true. This means if there are no audio tracks, this will
* return true.
* @returns True if the mic is muted, else false (including if the call
* is not set up yet).
*/
isMicrophoneMuted(): boolean;
/**
* @returns true if we have put the party on the other side of the call on hold
* (that is, we are signalling to them that we are not listening)
*/
isRemoteOnHold(): boolean;
setRemoteOnHold(onHold: boolean): void;
/**
* Indicates whether we are 'on hold' to the remote party (ie. if true,
* they cannot hear us).
* @returns true if the other party has put us on hold
*/
isLocalOnHold(): boolean;
/**
* Sends a DTMF digit to the other party
* @param digit - The digit (nb. string - '#' and '*' are dtmf too)
*/
sendDtmfDigit(digit: string): void;
private updateMuteStatus;
sendMetadataUpdate(): Promise<void>;
private gotCallFeedsForInvite;
private sendAnswer;
private queueGotCallFeedsForAnswer;
private mungeSdp;
private createOffer;
private createAnswer;
private gotCallFeedsForAnswer;
/**
* Internal
*/
private gotLocalIceCandidate;
private onIceGatheringStateChange;
onRemoteIceCandidatesReceived(ev: MatrixEvent): Promise<void>;
/**
* Used by MatrixClient.
*/
onAnswerReceived(event: MatrixEvent): Promise<void>;
onSelectAnswerReceived(event: MatrixEvent): Promise<void>;
onNegotiateReceived(event: MatrixEvent): Promise<void>;
private updateRemoteSDPStreamMetadata;
onSDPStreamMetadataChangedReceived(event: MatrixEvent): void;
onAssertedIdentityReceived(event: MatrixEvent): Promise<void>;
callHasEnded(): boolean;
private queueGotLocalOffer;
private wrappedGotLocalOffer;
private gotLocalOffer;
private getLocalOfferFailed;
private getUserMediaFailed;
private onIceConnectionStateChanged;
private onSignallingStateChanged;
private onTrack;
private onDataChannel;
/**
* This method removes all video/rtx codecs from screensharing video
* transceivers. This is necessary since they can cause problems. Without
* this the following steps should produce an error:
* Chromium calls Firefox
* Firefox answers
* Firefox starts screen-sharing
* Chromium starts screen-sharing
* Call crashes for Chromium with:
* [96685:23:0518/162603.933321:ERROR:webrtc_video_engine.cc(3296)] RTX codec (PT=97) mapped to PT=96 which is not in the codec list.
* [96685:23:0518/162603.933377:ERROR:webrtc_video_engine.cc(1171)] GetChangedRecvParameters called without any video codecs.
* [96685:23:0518/162603.933430:ERROR:sdp_offer_answer.cc(4302)] Failed to set local video description recv parameters for m-section with mid='2'. (INVALID_PARAMETER)
*/
private getRidOfRTXCodecs;
private onNegotiationNeeded;
onHangupReceived: (msg: MCallHangupReject) => void;
onRejectReceived: (msg: MCallHangupReject) => void;
onAnsweredElsewhere: (msg: MCallAnswer) => void;
/**
* @internal
*/
private sendVoipEvent;
/**
* Queue a candidate to be sent
* @param content - The candidate to queue up, or null if candidates have finished being generated
* and end-of-candidates should be signalled
*/
private queueCandidate;
private discardDuplicateCandidates;
transfer(targetUserId: string): Promise<void>;
transferToCall(transferTargetCall: MatrixCall): Promise<void>;
private terminate;
private stopAllMedia;
private checkForErrorListener;
private sendCandidateQueue;
/**
* Place a call to this room.
* @throws if you have not specified a listener for 'error' events.
* @throws if have passed audio=false.
*/
placeCall(audio: boolean, video: boolean): Promise<void>;
/**
* Place a call to this room with call feed.
* @param callFeeds - to use
* @throws if you have not specified a listener for 'error' events.
* @throws if have passed audio=false.
*/
placeCallWithCallFeeds(callFeeds: CallFeed[], requestScreenshareFeed?: boolean): Promise<void>;
private createPeerConnection;
private partyIdMatches;
private chooseOpponent;
private addBufferedIceCandidates;
private addIceCandidates;
get hasPeerConnection(): boolean;
initStats(stats: GroupCallStats, peerId?: string): void;
}
export declare function setTracksEnabled(tracks: Array<MediaStreamTrack>, enabled: boolean): void;
export declare function supportsMatrixCall(): boolean;
/**
* DEPRECATED
* Use client.createCall()
*
* Create a new Matrix call for the browser.
* @param client - The client instance to use.
* @param roomId - The room the call is in.
* @param options - DEPRECATED optional options map.
* @returns the call or null if the browser doesn't support calling.
*/
export declare function createNewMatrixCall(client: MatrixClient, roomId: string, options?: Pick<CallOpts, "forceTURN" | "invitee" | "opponentDeviceId" | "opponentSessionId" | "groupCallId">): MatrixCall | null;
export {};
//# sourceMappingURL=call.d.ts.map
|