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; opponentDeviceId?: string; opponentSessionId?: string; groupCallId?: string; } interface TurnServer { urls: Array; 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; } /** * 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 { 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; /** * Place a video call to this room. * @throws If you have not specified a listener for 'error' events. */ placeVideoCall(): Promise; /** * 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; /** * Returns an array of all local CallFeeds * @returns local CallFeeds */ getLocalFeeds(): Array; /** * Returns an array of all remote CallFeeds * @returns remote CallFeeds */ getRemoteFeeds(): Array; 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; private collectCallStats; /** * Configure this call from an invite event. Used by MatrixClient. * @param event - The m.call.invite event */ initWithInvite(event: MatrixEvent): Promise; /** * 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; 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; /** * 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; /** * 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; /** * Check if local video is muted. * * If there are multiple video tracks, all 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; /** * Check if the microphone is muted. * * If there are multiple audio tracks, all 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; private gotCallFeedsForInvite; private sendAnswer; private queueGotCallFeedsForAnswer; private mungeSdp; private createOffer; private createAnswer; private gotCallFeedsForAnswer; /** * Internal */ private gotLocalIceCandidate; private onIceGatheringStateChange; onRemoteIceCandidatesReceived(ev: MatrixEvent): Promise; /** * Used by MatrixClient. */ onAnswerReceived(event: MatrixEvent): Promise; onSelectAnswerReceived(event: MatrixEvent): Promise; onNegotiateReceived(event: MatrixEvent): Promise; private updateRemoteSDPStreamMetadata; onSDPStreamMetadataChangedReceived(event: MatrixEvent): void; onAssertedIdentityReceived(event: MatrixEvent): Promise; 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; transferToCall(transferTargetCall: MatrixCall): Promise; 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; /** * 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; 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, 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): MatrixCall | null; export {}; //# sourceMappingURL=call.d.ts.map