Compare commits

...

1 Commits

Author SHA1 Message Date
Evan Hahn
65fbea6729 Update to RingRTC v2.8.2-RC.6 (macOS and Linux only) 2020-11-17 12:06:14 -06:00
12 changed files with 382 additions and 1994 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

2
dist/index.d.ts vendored
View File

@ -1,4 +1,4 @@
export { AudioDevice, BandwidthMode, Call, CallId, CallEndedReason, CallLogLevel, CallState, CallingMessage, CallSettings, ConnectionState, DeviceId, GroupCall, GroupCallEndReason, GroupCallObserver, GroupMemberInfo, HangupMessage, HangupType, HttpMethod, JoinState, LocalDeviceState, OfferType, OpaqueMessage, RemoteDeviceState, RenderedResolution, RingRTCType, UserId, VideoCapturer, VideoRenderer } from './ringrtc/Service';
export { AudioDevice, BandwidthMode, Call, CallId, CallEndedReason, CallLogLevel, CallState, CallingMessage, CallSettings, ConnectionState, DeviceId, GroupCall, GroupCallEndReason, GroupCallObserver, GroupMemberInfo, HangupMessage, HangupType, HttpMethod, JoinState, LocalDeviceState, OfferType, OpaqueMessage, RemoteDeviceState, RingRTCType, UserId, VideoCapturer, VideoRenderer, VideoRequest } from './ringrtc/Service';
export { CanvasVideoRenderer, GumVideoCapturer, VideoFrameSource, } from './ringrtc/VideoSupport';
import { RingRTCType } from './ringrtc/Service';
export declare const RingRTC: RingRTCType;

2
dist/index.js vendored
View File

@ -25,8 +25,8 @@ exports.LocalDeviceState = Service_1.LocalDeviceState;
exports.OfferType = Service_1.OfferType;
exports.OpaqueMessage = Service_1.OpaqueMessage;
exports.RemoteDeviceState = Service_1.RemoteDeviceState;
exports.RenderedResolution = Service_1.RenderedResolution;
exports.RingRTCType = Service_1.RingRTCType;
exports.VideoRequest = Service_1.VideoRequest;
var VideoSupport_1 = require("./ringrtc/VideoSupport");
exports.CanvasVideoRenderer = VideoSupport_1.CanvasVideoRenderer;
exports.GumVideoCapturer = VideoSupport_1.GumVideoCapturer;

View File

@ -1,7 +1,17 @@
declare type GroupCallUserId = ArrayBuffer;
export declare class PeekInfo {
joinedMembers: Array<GroupCallUserId>;
creator?: GroupCallUserId;
eraId?: string;
maxDevices?: number;
deviceCount: number;
constructor();
}
export declare class RingRTCType {
private readonly callManager;
private _call;
private _groupCallByClientId;
private _peekRequests;
handleOutgoingSignaling: ((remoteUserId: UserId, message: CallingMessage) => Promise<boolean>) | null;
handleIncomingCall: ((call: Call) => Promise<CallSettings | null>) | null;
handleAutoEndedIncomingCallRequest: ((remoteUserId: UserId, reason: CallEndedReason) => void) | null;
@ -29,13 +39,15 @@ export declare class RingRTCType {
private sendSignaling;
receivedHttpResponse(requestId: number, status: number, body: ArrayBuffer): void;
httpRequestFailed(requestId: number, debugInfo: string | undefined): void;
getGroupCall(groupId: ArrayBuffer, observer: GroupCallObserver): GroupCall | undefined;
getGroupCall(groupId: ArrayBuffer, sfuUrl: string, observer: GroupCallObserver): GroupCall | undefined;
peekGroupCall(sfu_url: string, membership_proof: ArrayBuffer, group_members: Array<GroupMemberInfo>): Promise<PeekInfo>;
requestMembershipProof(clientId: GroupCallClientId): void;
requestGroupMembers(clientId: GroupCallClientId): void;
handleConnectionStateChanged(clientId: GroupCallClientId, connectionState: ConnectionState): void;
handleJoinStateChanged(clientId: GroupCallClientId, joinState: JoinState): void;
handleRemoteDevicesChanged(clientId: GroupCallClientId, remoteDeviceStates: Array<RemoteDeviceState>): void;
handleJoinedMembersChanged(clientId: GroupCallClientId, members: Array<ArrayBuffer>): void;
handlePeekChanged(clientId: GroupCallClientId, info: PeekInfo): void;
handlePeekResponse(request_id: number, info: PeekInfo): void;
handleEnded(clientId: GroupCallClientId, reason: GroupCallEndReason): void;
onLogMessage(level: number, fileName: string, line: number, message: string): void;
handleCallingMessage(remoteUserId: UserId, remoteUuid: ArrayBuffer | null, remoteDeviceId: DeviceId, localDeviceId: DeviceId, messageAgeSec: number, message: CallingMessage, senderIdentityKey: ArrayBuffer, receiverIdentityKey: ArrayBuffer): void;
@ -159,7 +171,8 @@ export declare enum GroupCallEndReason {
export declare enum HttpMethod {
Get = 0,
Put = 1,
Post = 2
Post = 2,
Delete = 3
}
export declare class LocalDeviceState {
connectionState: ConnectionState;
@ -171,19 +184,20 @@ export declare class LocalDeviceState {
export declare class RemoteDeviceState {
demuxId: number;
userId: ArrayBuffer;
mediaKeysReceived: boolean;
audioMuted: boolean | undefined;
videoMuted: boolean | undefined;
speakerIndex: number | undefined;
videoAspectRatio: number | undefined;
audioLevel: number | undefined;
constructor(demuxId: number, userId: ArrayBuffer);
constructor(demuxId: number, userId: ArrayBuffer, mediaKeysReceived: boolean);
}
export declare class GroupMemberInfo {
userId: ArrayBuffer;
userIdCipherText: ArrayBuffer;
constructor(userId: ArrayBuffer, userIdCipherText: ArrayBuffer);
}
export declare class RenderedResolution {
export declare class VideoRequest {
demuxId: number;
width: number;
height: number;
@ -195,7 +209,7 @@ export interface GroupCallObserver {
requestGroupMembers(groupCall: GroupCall): void;
onLocalDeviceStateChanged(groupCall: GroupCall): void;
onRemoteDeviceStatesChanged(groupCall: GroupCall): void;
onJoinedMembersChanged(groupCall: GroupCall): void;
onPeekChanged(groupCall: GroupCall): void;
onEnded(groupCall: GroupCall, reason: GroupCallEndReason): void;
}
export declare class GroupCall {
@ -205,19 +219,20 @@ export declare class GroupCall {
get clientId(): GroupCallClientId;
private _localDeviceState;
private _remoteDeviceStates;
private _joinedGroupMembers;
constructor(callManager: CallManager, groupId: ArrayBuffer, observer: GroupCallObserver);
private _peekInfo;
constructor(callManager: CallManager, groupId: ArrayBuffer, sfuUrl: string, observer: GroupCallObserver);
connect(): void;
join(): void;
leave(): void;
disconnect(): void;
getLocalDeviceState(): LocalDeviceState;
getRemoteDeviceStates(): Array<RemoteDeviceState> | undefined;
getJoinedGroupMembers(): Array<ArrayBuffer> | undefined;
getPeekInfo(): PeekInfo | undefined;
setOutgoingAudioMuted(muted: boolean): void;
setOutgoingVideoMuted(muted: boolean): void;
resendMediaKeys(): void;
setBandwidthMode(bandwidthMode: BandwidthMode): void;
setRenderedResolutions(resolutions: Array<RenderedResolution>): void;
requestVideo(resolutions: Array<VideoRequest>): void;
setGroupMembers(members: Array<GroupMemberInfo>): void;
setMembershipProof(proof: ArrayBuffer): void;
requestMembershipProof(): void;
@ -225,16 +240,17 @@ export declare class GroupCall {
handleConnectionStateChanged(connectionState: ConnectionState): void;
handleJoinStateChanged(joinState: JoinState): void;
handleRemoteDevicesChanged(remoteDeviceStates: Array<RemoteDeviceState>): void;
handleJoinedMembersChanged(members: Array<ArrayBuffer>): void;
handlePeekChanged(info: PeekInfo): void;
handleEnded(reason: GroupCallEndReason): void;
sendVideoFrame(width: number, height: number, rgbaBuffer: ArrayBuffer): void;
getVideoSource(remoteDemuxId: number): GroupCallVideoFrameSource;
setRemoteAspectRatio(remoteDemuxId: number, aspectRatio: number): void;
}
declare class GroupCallVideoFrameSource {
private readonly _callManager;
private readonly _clientId;
private readonly _groupCall;
private readonly _remoteDemuxId;
constructor(callManager: CallManager, clientId: GroupCallClientId, remoteDemuxId: number);
constructor(callManager: CallManager, groupCall: GroupCall, remoteDemuxId: number);
receiveVideoFrame(buffer: ArrayBuffer): [number, number] | undefined;
}
declare type ProtobufArrayBuffer = ArrayBuffer | {
@ -315,7 +331,7 @@ export interface CallManager {
receivedCallMessage(remoteUserId: ArrayBuffer, remoteDeviceId: DeviceId, localDeviceId: DeviceId, data: ArrayBuffer, messageAgeSec: number): void;
receivedHttpResponse(requestId: number, status: number, body: ArrayBuffer): void;
httpRequestFailed(requestId: number, debugInfo: string | undefined): void;
createGroupCallClient(groupId: ArrayBuffer): GroupCallClientId;
createGroupCallClient(groupId: ArrayBuffer, sfuUrl: string): GroupCallClientId;
deleteGroupCallClient(clientId: GroupCallClientId): void;
connect(clientId: GroupCallClientId): void;
join(clientId: GroupCallClientId): void;
@ -323,11 +339,13 @@ export interface CallManager {
disconnect(clientId: GroupCallClientId): void;
setOutgoingAudioMuted(clientId: GroupCallClientId, muted: boolean): void;
setOutgoingVideoMuted(clientId: GroupCallClientId, muted: boolean): void;
resendMediaKeys(clientId: GroupCallClientId): void;
setBandwidthMode(clientId: GroupCallClientId, bandwidthMode: BandwidthMode): void;
setRenderedResolutions(clientId: GroupCallClientId, resolutions: Array<RenderedResolution>): void;
requestVideo(clientId: GroupCallClientId, resolutions: Array<VideoRequest>): void;
setGroupMembers(clientId: GroupCallClientId, members: Array<GroupMemberInfo>): void;
setMembershipProof(clientId: GroupCallClientId, proof: ArrayBuffer): void;
receiveGroupCallVideoFrame(clientId: GroupCallClientId, remoteDemuxId: number, buffer: ArrayBuffer): [number, number] | undefined;
peekGroupCall(requestId: number, sfu_url: string, membership_proof: ArrayBuffer, group_members: Array<GroupMemberInfo>): Promise<PeekInfo>;
getAudioInputs(): AudioDevice[];
setAudioInput(index: number): void;
getAudioOutputs(): AudioDevice[];
@ -355,7 +373,8 @@ export interface CallManagerCallbacks {
handleConnectionStateChanged(clientId: GroupCallClientId, connectionState: ConnectionState): void;
handleJoinStateChanged(clientId: GroupCallClientId, joinState: JoinState): void;
handleRemoteDevicesChanged(clientId: GroupCallClientId, remoteDeviceStates: Array<RemoteDeviceState>): void;
handleJoinedMembersChanged(clientId: GroupCallClientId, members: Array<ArrayBuffer>): void;
handlePeekChanged(clientId: GroupCallClientId, info: PeekInfo): void;
handlePeekResponse(request_id: number, info: PeekInfo): void;
handleEnded(clientId: GroupCallClientId, reason: GroupCallEndReason): void;
onLogMessage(level: number, fileName: string, line: number, message: string): void;
}

View File

@ -19,6 +19,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
const os = require('os');
// tslint:disable-next-line no-var-requires no-require-imports
const Native = require('../../build/' + os.platform() + '/libringrtc.node');
class PeekInfo {
constructor() {
this.joinedMembers = [];
this.deviceCount = 0;
}
}
exports.PeekInfo = PeekInfo;
class Requests {
constructor() {
this._resolveById = new Map();
this._nextId = 1;
}
add() {
const id = this._nextId++;
const promise = new Promise((resolve, _reject) => {
this._resolveById.set(id, resolve);
});
return [id, promise];
}
resolve(id, response) {
const resolve = this._resolveById.get(id);
if (!resolve) {
return false;
}
resolve(response);
this._resolveById.delete(id);
return true;
}
}
class RingRTCType {
constructor() {
// Set by UX
@ -31,6 +60,7 @@ class RingRTCType {
this.callManager = new Native.CallManager();
this._call = null;
this._groupCallByClientId = new Map();
this._peekRequests = new Requests();
this.pollEvery(50);
}
pollEvery(intervalMs) {
@ -90,13 +120,9 @@ class RingRTCType {
}))();
}
proceed(callId, settings) {
// tslint:disable no-floating-promises
(() => __awaiter(this, void 0, void 0, function* () {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
yield 0;
silly_deadlock_protection(() => {
this.callManager.proceed(callId, settings.iceServer.username || '', settings.iceServer.password || '', settings.iceServer.urls, settings.hideIp);
}))();
});
}
// Called by Rust
onCallState(remoteUserId, state) {
@ -232,22 +258,47 @@ class RingRTCType {
}))();
}
receivedHttpResponse(requestId, status, body) {
this.callManager.receivedHttpResponse(requestId, status, body);
silly_deadlock_protection(() => {
try {
this.callManager.receivedHttpResponse(requestId, status, body);
}
catch (_a) {
// We may not have an active connection any more.
// In which case it doesn't matter
}
});
}
httpRequestFailed(requestId, debugInfo) {
this.callManager.httpRequestFailed(requestId, debugInfo);
silly_deadlock_protection(() => {
try {
this.callManager.httpRequestFailed(requestId, debugInfo);
}
catch (_a) {
// We may not have an active connection any more.
// In which case it doesn't matter
}
});
}
// Group Calls
// Called by UX
getGroupCall(groupId, observer) {
const groupCall = new GroupCall(this.callManager, groupId, observer);
getGroupCall(groupId, sfuUrl, observer) {
const groupCall = new GroupCall(this.callManager, groupId, sfuUrl, observer);
this._groupCallByClientId.set(groupCall.clientId, groupCall);
return groupCall;
}
// Called by UX
// Returns a list of user IDs
peekGroupCall(sfu_url, membership_proof, group_members) {
let [requestId, promise] = this._peekRequests.add();
// Response comes back via handlePeekResponse
silly_deadlock_protection(() => {
this.callManager.peekGroupCall(requestId, sfu_url, membership_proof, group_members);
});
return promise;
}
// Called by Rust
requestMembershipProof(clientId) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -255,12 +306,11 @@ class RingRTCType {
return;
}
groupCall.requestMembershipProof();
}))();
});
}
// Called by Rust
requestGroupMembers(clientId) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -268,12 +318,11 @@ class RingRTCType {
return;
}
groupCall.requestGroupMembers();
}))();
});
}
// Called by Rust
handleConnectionStateChanged(clientId, connectionState) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -281,12 +330,11 @@ class RingRTCType {
return;
}
groupCall.handleConnectionStateChanged(connectionState);
}))();
});
}
// Called by Rust
handleJoinStateChanged(clientId, joinState) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -294,12 +342,11 @@ class RingRTCType {
return;
}
groupCall.handleJoinStateChanged(joinState);
}))();
});
}
// Called by Rust
handleRemoteDevicesChanged(clientId, remoteDeviceStates) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -307,25 +354,31 @@ class RingRTCType {
return;
}
groupCall.handleRemoteDevicesChanged(remoteDeviceStates);
}))();
});
}
// Called by Rust
handleJoinedMembersChanged(clientId, members) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
handlePeekChanged(clientId, info) {
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
this.onLogMessage(CallLogLevel.Error, 'Service.ts', 0, 'handleJoinedMembersChanged(): GroupCall not found in map!');
this.onLogMessage(CallLogLevel.Error, 'Service.ts', 0, 'handlePeekChanged(): GroupCall not found in map!');
return;
}
groupCall.handleJoinedMembersChanged(members);
}))();
groupCall.handlePeekChanged(info);
});
}
// Called by Rust
handlePeekResponse(request_id, info) {
silly_deadlock_protection(() => {
if (!this._peekRequests.resolve(request_id, info)) {
this.onLogMessage(CallLogLevel.Warn, 'Service.ts', 0, `Invalid request ID for handlePeekResponse: ${request_id}`);
}
});
}
// Called by Rust
handleEnded(clientId, reason) {
(() => __awaiter(this, void 0, void 0, function* () {
yield 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -334,7 +387,7 @@ class RingRTCType {
}
this._groupCallByClientId.delete(clientId);
groupCall.handleEnded(reason);
}))();
});
}
// Called by Rust
onLogMessage(level, fileName, line, message) {
@ -576,12 +629,9 @@ class Call {
this._videoRenderer.disable();
}
// This assumes we only have one active all.
(() => __awaiter(this, void 0, void 0, function* () {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
yield 0;
silly_deadlock_protection(() => {
this._callManager.hangup();
}))();
});
}
get outgoingAudioEnabled() {
return this._outgoingAudioEnabled;
@ -589,12 +639,9 @@ class Call {
set outgoingAudioEnabled(enabled) {
this._outgoingAudioEnabled = enabled;
// This assumes we only have one active all.
(() => __awaiter(this, void 0, void 0, function* () {
// This is a silly way of not causing a deadlock.
// tslint:disable-next-line await-promise
yield 0;
silly_deadlock_protection(() => {
this._callManager.setOutgoingAudioEnabled(enabled);
}))();
});
}
get outgoingVideoEnabled() {
return this._outgoingVideoEnabled;
@ -651,11 +698,7 @@ class Call {
}
}
setOutgoingVideoEnabled(enabled) {
// tslint:disable no-floating-promises
(() => __awaiter(this, void 0, void 0, function* () {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
yield 0;
silly_deadlock_protection(() => {
try {
this._callManager.setOutgoingVideoEnabled(enabled);
}
@ -663,14 +706,10 @@ class Call {
// We may not have an active connection any more.
// In which case it doesn't matter
}
}))();
});
}
setLowBandwidthMode(enabled) {
// tslint:disable no-floating-promises
(() => __awaiter(this, void 0, void 0, function* () {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
yield 0;
silly_deadlock_protection(() => {
try {
this._callManager.setLowBandwidthMode(enabled);
}
@ -678,7 +717,7 @@ class Call {
// We may not have an active connection any more.
// In which case it doesn't matter
}
}))();
});
}
enableOrDisableRenderer() {
if (!this._videoRenderer) {
@ -752,6 +791,7 @@ var HttpMethod;
HttpMethod[HttpMethod["Get"] = 0] = "Get";
HttpMethod[HttpMethod["Put"] = 1] = "Put";
HttpMethod[HttpMethod["Post"] = 2] = "Post";
HttpMethod[HttpMethod["Delete"] = 3] = "Delete";
})(HttpMethod = exports.HttpMethod || (exports.HttpMethod = {}));
// The local device state for a group call.
class LocalDeviceState {
@ -766,9 +806,10 @@ class LocalDeviceState {
exports.LocalDeviceState = LocalDeviceState;
// All remote devices in a group call and their associated state.
class RemoteDeviceState {
constructor(demuxId, userId) {
constructor(demuxId, userId, mediaKeysReceived) {
this.demuxId = demuxId;
this.userId = userId;
this.mediaKeysReceived = mediaKeysReceived;
}
}
exports.RemoteDeviceState = RemoteDeviceState;
@ -782,7 +823,7 @@ class GroupMemberInfo {
exports.GroupMemberInfo = GroupMemberInfo;
// Used for the application to communicate the actual resolutions of
// each device in a group call to RingRTC and the SFU.
class RenderedResolution {
class VideoRequest {
constructor(demuxId, width, height, framerate) {
this.demuxId = demuxId;
this.width = width;
@ -790,14 +831,14 @@ class RenderedResolution {
this.framerate = framerate;
}
}
exports.RenderedResolution = RenderedResolution;
exports.VideoRequest = VideoRequest;
class GroupCall {
// Called by UI via RingRTC object
constructor(callManager, groupId, observer) {
constructor(callManager, groupId, sfuUrl, observer) {
this._callManager = callManager;
this._observer = observer;
this._localDeviceState = new LocalDeviceState();
this._clientId = this._callManager.createGroupCallClient(groupId);
this._clientId = this._callManager.createGroupCallClient(groupId, sfuUrl);
}
get clientId() {
return this._clientId;
@ -827,26 +868,32 @@ class GroupCall {
return this._remoteDeviceStates;
}
// Called by UI
getJoinedGroupMembers() {
return this._joinedGroupMembers;
getPeekInfo() {
return this._peekInfo;
}
// Called by UI
setOutgoingAudioMuted(muted) {
this._localDeviceState.audioMuted = muted;
this._callManager.setOutgoingAudioMuted(this._clientId, muted);
this._observer.onLocalDeviceStateChanged(this);
}
// Called by UI
setOutgoingVideoMuted(muted) {
this._localDeviceState.videoMuted = muted;
this._callManager.setOutgoingVideoMuted(this._clientId, muted);
this._observer.onLocalDeviceStateChanged(this);
}
// Called by UI
resendMediaKeys() {
this._callManager.resendMediaKeys(this._clientId);
}
// Called by UI
setBandwidthMode(bandwidthMode) {
this._callManager.setBandwidthMode(this._clientId, bandwidthMode);
}
// Called by UI
setRenderedResolutions(resolutions) {
this._callManager.setRenderedResolutions(this._clientId, resolutions);
requestVideo(resolutions) {
this._callManager.requestVideo(this._clientId, resolutions);
}
// Called by UI
setGroupMembers(members) {
@ -876,13 +923,19 @@ class GroupCall {
}
// Called by Rust via RingRTC object
handleRemoteDevicesChanged(remoteDeviceStates) {
var _a, _b;
// We don't get aspect ratios from RingRTC, so make sure to copy them over.
for (const noo of remoteDeviceStates) {
const old = (_a = this._remoteDeviceStates) === null || _a === void 0 ? void 0 : _a.find((old) => old.demuxId == noo.demuxId);
noo.videoAspectRatio = (_b = old) === null || _b === void 0 ? void 0 : _b.videoAspectRatio;
}
this._remoteDeviceStates = remoteDeviceStates;
this._observer.onRemoteDeviceStatesChanged(this);
}
// Called by Rust via RingRTC object
handleJoinedMembersChanged(members) {
this._joinedGroupMembers = members;
this._observer.onJoinedMembersChanged(this);
handlePeekChanged(info) {
this._peekInfo = info;
this._observer.onPeekChanged(this);
}
// Called by Rust via RingRTC object
handleEnded(reason) {
@ -896,19 +949,34 @@ class GroupCall {
}
// With this, a GroupCall can provide a VideoFrameSource for each remote device.
getVideoSource(remoteDemuxId) {
return new GroupCallVideoFrameSource(this._callManager, this._clientId, remoteDemuxId);
return new GroupCallVideoFrameSource(this._callManager, this, remoteDemuxId);
}
// Called by the GroupCallVideoFrameSource when it receives a video frame.
setRemoteAspectRatio(remoteDemuxId, aspectRatio) {
var _a;
const remoteDevice = (_a = this._remoteDeviceStates) === null || _a === void 0 ? void 0 : _a.find((device) => device.demuxId == remoteDemuxId);
if (!!remoteDevice && remoteDevice.videoAspectRatio != aspectRatio) {
remoteDevice.videoAspectRatio = aspectRatio;
this._observer.onRemoteDeviceStatesChanged(this);
}
}
}
exports.GroupCall = GroupCall;
// Implements VideoSource for use in CanvasVideoRenderer
class GroupCallVideoFrameSource {
constructor(callManager, clientId, remoteDemuxId) {
constructor(callManager, groupCall, remoteDemuxId) {
this._callManager = callManager;
this._clientId = clientId;
this._groupCall = groupCall;
this._remoteDemuxId = remoteDemuxId;
}
receiveVideoFrame(buffer) {
return this._callManager.receiveGroupCallVideoFrame(this._clientId, this._remoteDemuxId, buffer);
// This assumes we only have one active all.
const frame = this._callManager.receiveGroupCallVideoFrame(this._groupCall.clientId, this._remoteDemuxId, buffer);
if (!!frame) {
const [width, height] = frame;
this._groupCall.setRemoteAspectRatio(this._remoteDemuxId, width / height);
}
return frame;
}
}
function to_array_buffer(pbab) {
@ -991,3 +1059,12 @@ var CallLogLevel;
CallLogLevel[CallLogLevel["Debug"] = 4] = "Debug";
CallLogLevel[CallLogLevel["Trace"] = 5] = "Trace";
})(CallLogLevel = exports.CallLogLevel || (exports.CallLogLevel = {}));
function silly_deadlock_protection(f) {
// tslint:disable no-floating-promises
(() => __awaiter(this, void 0, void 0, function* () {
// This is a silly way of preventing a deadlock.
// tslint:disable-next-line await-promise
yield 0;
f();
}))();
}

View File

@ -244,8 +244,9 @@ class CanvasVideoRenderer {
if (!!this.source) {
// If we're replacing an existing source, make sure we stop the
// current rAF loop before starting another one.
// And blanking the video is nice as well.
this.disable();
if (this.rafId) {
window.cancelAnimationFrame(this.rafId);
}
}
this.source = source;
this.requestAnimationFrameCallback();

View File

@ -29,11 +29,11 @@ export {
OfferType,
OpaqueMessage,
RemoteDeviceState,
RenderedResolution,
RingRTCType,
UserId,
VideoCapturer,
VideoRenderer
VideoRenderer,
VideoRequest
} from './ringrtc/Service';
export {

1812
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,10 +12,50 @@ const os = require('os');
// tslint:disable-next-line no-var-requires no-require-imports
const Native = require('../../build/' + os.platform() + '/libringrtc.node');
type GroupId = ArrayBuffer;
type GroupCallUserId = ArrayBuffer;
export class PeekInfo {
joinedMembers: Array<GroupCallUserId>;
creator?: GroupCallUserId;
eraId?: string;
maxDevices?: number;
deviceCount: number;
constructor() {
this.joinedMembers = [];
this.deviceCount = 0;
}
}
class Requests<T> {
private _resolveById: Map<number, (response: T) => void> = new Map();
private _nextId: number = 1;
add(): [number, Promise<T>] {
const id = this._nextId++;
const promise = new Promise<T>((resolve, _reject) => {
this._resolveById.set(id, resolve);
});
return [id, promise];
}
resolve(id: number, response: T): boolean {
const resolve = this._resolveById.get(id);
if (!resolve) {
return false;
}
resolve(response);
this._resolveById.delete(id);
return true;
}
}
export class RingRTCType {
private readonly callManager: CallManager;
private _call: Call | null;
private _groupCallByClientId: Map<GroupCallClientId, GroupCall>;
private _peekRequests: Requests<PeekInfo>;
// Set by UX
handleOutgoingSignaling: ((remoteUserId: UserId, message: CallingMessage) => Promise<boolean>) | null = null;
@ -29,6 +69,7 @@ export class RingRTCType {
this.callManager = new Native.CallManager() as CallManager;
this._call = null;
this._groupCallByClientId = new Map();
this._peekRequests = new Requests<PeekInfo>();
this.pollEvery(50);
}
@ -117,12 +158,7 @@ export class RingRTCType {
}
private proceed(callId: CallId, settings: CallSettings): void {
// tslint:disable no-floating-promises
(async () => {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
await 0;
silly_deadlock_protection(() => {
this.callManager.proceed(
callId,
settings.iceServer.username || '',
@ -130,7 +166,7 @@ export class RingRTCType {
settings.iceServer.urls,
settings.hideIp,
);
})();
});
}
// Called by Rust
@ -327,11 +363,25 @@ export class RingRTCType {
}
receivedHttpResponse(requestId: number, status: number, body: ArrayBuffer) : void {
this.callManager.receivedHttpResponse(requestId, status, body);
silly_deadlock_protection(() => {
try {
this.callManager.receivedHttpResponse(requestId, status, body);
} catch {
// We may not have an active connection any more.
// In which case it doesn't matter
}
});
}
httpRequestFailed(requestId: number, debugInfo: string | undefined) {
this.callManager.httpRequestFailed(requestId, debugInfo);
httpRequestFailed(requestId: number, debugInfo: string | undefined) : void {
silly_deadlock_protection(() => {
try {
this.callManager.httpRequestFailed(requestId, debugInfo);
} catch {
// We may not have an active connection any more.
// In which case it doesn't matter
}
});
}
// Group Calls
@ -339,22 +389,37 @@ export class RingRTCType {
// Called by UX
getGroupCall(
groupId: ArrayBuffer,
sfuUrl: string,
observer: GroupCallObserver
): GroupCall | undefined {
const groupCall = new GroupCall(this.callManager, groupId, observer);
const groupCall = new GroupCall(this.callManager, groupId, sfuUrl, observer);
this._groupCallByClientId.set(groupCall.clientId, groupCall);
return groupCall;
}
// Called by UX
// Returns a list of user IDs
peekGroupCall(
sfu_url: string,
membership_proof: ArrayBuffer,
group_members: Array<GroupMemberInfo>,
): Promise<PeekInfo> {
let [requestId, promise] = this._peekRequests.add();
// Response comes back via handlePeekResponse
silly_deadlock_protection(() => {
this.callManager.peekGroupCall(requestId, sfu_url, membership_proof, group_members);
});
return promise;
}
// Called by Rust
requestMembershipProof(
clientId: GroupCallClientId
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -363,16 +428,14 @@ export class RingRTCType {
}
groupCall.requestMembershipProof();
})();
});
}
// Called by Rust
requestGroupMembers(
clientId: GroupCallClientId
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -381,7 +444,7 @@ export class RingRTCType {
}
groupCall.requestGroupMembers();
})();
});
}
// Called by Rust
@ -389,9 +452,7 @@ export class RingRTCType {
clientId: GroupCallClientId,
connectionState: ConnectionState
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -400,7 +461,7 @@ export class RingRTCType {
}
groupCall.handleConnectionStateChanged(connectionState);
})();
});
}
// Called by Rust
@ -408,9 +469,7 @@ export class RingRTCType {
clientId: GroupCallClientId,
joinState: JoinState
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -419,7 +478,7 @@ export class RingRTCType {
}
groupCall.handleJoinStateChanged(joinState);
})();
});
}
// Called by Rust
@ -427,9 +486,7 @@ export class RingRTCType {
clientId: GroupCallClientId,
remoteDeviceStates: Array<RemoteDeviceState>
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -438,26 +495,36 @@ export class RingRTCType {
}
groupCall.handleRemoteDevicesChanged(remoteDeviceStates);
})();
});
}
// Called by Rust
handleJoinedMembersChanged(
handlePeekChanged(
clientId: GroupCallClientId,
members: Array<ArrayBuffer>
info: PeekInfo,
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
this.onLogMessage(CallLogLevel.Error, 'Service.ts', 0, 'handleJoinedMembersChanged(): GroupCall not found in map!');
this.onLogMessage(CallLogLevel.Error, 'Service.ts', 0, 'handlePeekChanged(): GroupCall not found in map!');
return;
}
groupCall.handleJoinedMembersChanged(members);
})();
groupCall.handlePeekChanged(info);
});
}
// Called by Rust
handlePeekResponse(
request_id: number,
info: PeekInfo,
): void {
silly_deadlock_protection(() => {
if (!this._peekRequests.resolve(request_id, info)) {
this.onLogMessage(CallLogLevel.Warn, 'Service.ts', 0, `Invalid request ID for handlePeekResponse: ${request_id}`);
}
});
}
// Called by Rust
@ -465,9 +532,7 @@ export class RingRTCType {
clientId: GroupCallClientId,
reason: GroupCallEndReason
): void {
(async () => {
await 0;
silly_deadlock_protection(() => {
let groupCall = this._groupCallByClientId.get(clientId);
if (!groupCall) {
let error = new Error();
@ -478,7 +543,7 @@ export class RingRTCType {
this._groupCallByClientId.delete(clientId);
groupCall.handleEnded(reason);
})();
});
}
// Called by Rust
@ -883,12 +948,9 @@ export class Call {
this._videoRenderer.disable();
}
// This assumes we only have one active all.
(async () => {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
await 0;
silly_deadlock_protection(() => {
this._callManager.hangup();
})();
});
}
get outgoingAudioEnabled(): boolean {
@ -898,12 +960,9 @@ export class Call {
set outgoingAudioEnabled(enabled: boolean) {
this._outgoingAudioEnabled = enabled;
// This assumes we only have one active all.
(async () => {
// This is a silly way of not causing a deadlock.
// tslint:disable-next-line await-promise
await 0;
silly_deadlock_protection(() => {
this._callManager.setOutgoingAudioEnabled(enabled);
})();
});
}
get outgoingVideoEnabled(): boolean {
@ -968,33 +1027,25 @@ export class Call {
}
private setOutgoingVideoEnabled(enabled: boolean) {
// tslint:disable no-floating-promises
(async () => {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
await 0;
silly_deadlock_protection(() => {
try {
this._callManager.setOutgoingVideoEnabled(enabled);
} catch {
// We may not have an active connection any more.
// In which case it doesn't matter
}
})();
});
}
setLowBandwidthMode(enabled: boolean) {
// tslint:disable no-floating-promises
(async () => {
// This is a silly way of causing a deadlock.
// tslint:disable-next-line await-promise
await 0;
silly_deadlock_protection(() => {
try {
this._callManager.setLowBandwidthMode(enabled);
} catch {
// We may not have an active connection any more.
// In which case it doesn't matter
}
})();
});
}
private enableOrDisableRenderer(): void {
@ -1073,6 +1124,7 @@ export enum HttpMethod {
Get = 0,
Put = 1,
Post = 2,
Delete = 3,
}
// The local device state for a group call.
@ -1095,6 +1147,7 @@ export class LocalDeviceState {
export class RemoteDeviceState {
demuxId: number; // UInt32
userId: ArrayBuffer;
mediaKeysReceived: boolean;
audioMuted: boolean | undefined;
videoMuted: boolean | undefined;
@ -1104,10 +1157,12 @@ export class RemoteDeviceState {
constructor(
demuxId: number,
userId: ArrayBuffer
userId: ArrayBuffer,
mediaKeysReceived: boolean
) {
this.demuxId = demuxId;
this.userId = userId;
this.mediaKeysReceived = mediaKeysReceived
}
}
@ -1127,7 +1182,7 @@ export class GroupMemberInfo {
// Used for the application to communicate the actual resolutions of
// each device in a group call to RingRTC and the SFU.
export class RenderedResolution {
export class VideoRequest {
demuxId: number; // UInt32
width: number; // UInt16
height: number; // UInt16
@ -1151,7 +1206,7 @@ export interface GroupCallObserver {
requestGroupMembers(groupCall: GroupCall): void;
onLocalDeviceStateChanged(groupCall: GroupCall): void;
onRemoteDeviceStatesChanged(groupCall: GroupCall): void;
onJoinedMembersChanged(groupCall: GroupCall): void;
onPeekChanged(groupCall: GroupCall): void;
onEnded(groupCall: GroupCall, reason: GroupCallEndReason): void;
}
@ -1167,12 +1222,13 @@ export class GroupCall {
private _localDeviceState: LocalDeviceState;
private _remoteDeviceStates: Array<RemoteDeviceState> | undefined;
private _joinedGroupMembers: Array<ArrayBuffer> | undefined; // uuid
private _peekInfo: PeekInfo | undefined; // uuid
// Called by UI via RingRTC object
constructor(
callManager: CallManager,
groupId: ArrayBuffer,
sfuUrl: string,
observer: GroupCallObserver
) {
this._callManager = callManager;
@ -1180,7 +1236,7 @@ export class GroupCall {
this._localDeviceState = new LocalDeviceState();
this._clientId = this._callManager.createGroupCallClient(groupId);
this._clientId = this._callManager.createGroupCallClient(groupId, sfuUrl);
}
// Called by UI
@ -1214,20 +1270,27 @@ export class GroupCall {
}
// Called by UI
getJoinedGroupMembers(): Array<ArrayBuffer> | undefined {
return this._joinedGroupMembers;
getPeekInfo(): PeekInfo | undefined {
return this._peekInfo;
}
// Called by UI
setOutgoingAudioMuted(muted: boolean): void {
this._localDeviceState.audioMuted = muted;
this._callManager.setOutgoingAudioMuted(this._clientId, muted);
this._observer.onLocalDeviceStateChanged(this);
}
// Called by UI
setOutgoingVideoMuted(muted: boolean): void {
this._localDeviceState.videoMuted = muted;
this._callManager.setOutgoingVideoMuted(this._clientId, muted);
this._observer.onLocalDeviceStateChanged(this);
}
// Called by UI
resendMediaKeys(): void {
this._callManager.resendMediaKeys(this._clientId);
}
// Called by UI
@ -1236,8 +1299,8 @@ export class GroupCall {
}
// Called by UI
setRenderedResolutions(resolutions: Array<RenderedResolution>): void {
this._callManager.setRenderedResolutions(this._clientId, resolutions);
requestVideo(resolutions: Array<VideoRequest>): void {
this._callManager.requestVideo(this._clientId, resolutions);
}
// Called by UI
@ -1276,16 +1339,22 @@ export class GroupCall {
// Called by Rust via RingRTC object
handleRemoteDevicesChanged(remoteDeviceStates: Array<RemoteDeviceState>): void {
// We don't get aspect ratios from RingRTC, so make sure to copy them over.
for (const noo of remoteDeviceStates) {
const old = this._remoteDeviceStates?.find((old) => old.demuxId == noo.demuxId);
noo.videoAspectRatio = old?.videoAspectRatio;
}
this._remoteDeviceStates = remoteDeviceStates;
this._observer.onRemoteDeviceStatesChanged(this);
}
// Called by Rust via RingRTC object
handleJoinedMembersChanged(members: Array<ArrayBuffer>): void {
this._joinedGroupMembers = members;
handlePeekChanged(info: PeekInfo): void {
this._peekInfo = info;
this._observer.onJoinedMembersChanged(this);
this._observer.onPeekChanged(this);
}
// Called by Rust via RingRTC object
@ -1303,28 +1372,43 @@ export class GroupCall {
// With this, a GroupCall can provide a VideoFrameSource for each remote device.
getVideoSource(remoteDemuxId: number): GroupCallVideoFrameSource {
return new GroupCallVideoFrameSource(this._callManager, this._clientId, remoteDemuxId);
return new GroupCallVideoFrameSource(this._callManager, this, remoteDemuxId);
}
// Called by the GroupCallVideoFrameSource when it receives a video frame.
setRemoteAspectRatio(remoteDemuxId: number, aspectRatio: number) {
const remoteDevice = this._remoteDeviceStates?.find((device) => device.demuxId == remoteDemuxId);
if (!!remoteDevice && remoteDevice.videoAspectRatio != aspectRatio) {
remoteDevice.videoAspectRatio = aspectRatio;
this._observer.onRemoteDeviceStatesChanged(this);
}
}
}
// Implements VideoSource for use in CanvasVideoRenderer
class GroupCallVideoFrameSource {
private readonly _callManager: CallManager;
private readonly _clientId: GroupCallClientId;
private readonly _groupCall: GroupCall;
private readonly _remoteDemuxId: number; // Uint32
constructor(
callManager: CallManager,
clientId: GroupCallClientId,
groupCall: GroupCall,
remoteDemuxId: number, // Uint32
) {
this._callManager = callManager;
this._clientId = clientId;
this._groupCall = groupCall;
this._remoteDemuxId = remoteDemuxId;
}
receiveVideoFrame(buffer: ArrayBuffer): [number, number] | undefined {
return this._callManager.receiveGroupCallVideoFrame(this._clientId, this._remoteDemuxId, buffer);
// This assumes we only have one active all.
const frame = this._callManager.receiveGroupCallVideoFrame(this._groupCall.clientId, this._remoteDemuxId, buffer);
if (!!frame) {
const [ width, height ] = frame;
this._groupCall.setRemoteAspectRatio(this._remoteDemuxId, width / height);
}
return frame;
}
}
@ -1485,7 +1569,7 @@ export interface CallManager {
// Group Calls
createGroupCallClient(groupId: ArrayBuffer) : GroupCallClientId;
createGroupCallClient(groupId: ArrayBuffer, sfuUrl: string) : GroupCallClientId;
deleteGroupCallClient(clientId: GroupCallClientId): void;
connect(clientId: GroupCallClientId): void;
join(clientId: GroupCallClientId): void;
@ -1493,12 +1577,15 @@ export interface CallManager {
disconnect(clientId: GroupCallClientId): void;
setOutgoingAudioMuted(clientId: GroupCallClientId, muted: boolean): void;
setOutgoingVideoMuted(clientId: GroupCallClientId, muted: boolean): void;
resendMediaKeys(clientId: GroupCallClientId): void;
setBandwidthMode(clientId: GroupCallClientId, bandwidthMode: BandwidthMode): void;
setRenderedResolutions(clientId: GroupCallClientId, resolutions: Array<RenderedResolution>): void;
requestVideo(clientId: GroupCallClientId, resolutions: Array<VideoRequest>): void;
setGroupMembers(clientId: GroupCallClientId, members: Array<GroupMemberInfo>): void;
setMembershipProof(clientId: GroupCallClientId, proof: ArrayBuffer): void;
// Same as receiveVideoFrame, but with a specific GroupCallClientId and remoteDemuxId.
receiveGroupCallVideoFrame(clientId: GroupCallClientId, remoteDemuxId: number, buffer: ArrayBuffer): [number, number] | undefined;
// Response comes back via handlePeekResponse
peekGroupCall(requestId: number, sfu_url: string, membership_proof: ArrayBuffer, group_members: Array<GroupMemberInfo>): Promise<PeekInfo>;
getAudioInputs() : AudioDevice[];
setAudioInput(index: number) : void;
@ -1591,9 +1678,13 @@ export interface CallManagerCallbacks {
clientId: GroupCallClientId,
remoteDeviceStates: Array<RemoteDeviceState>
): void;
handleJoinedMembersChanged(
handlePeekChanged(
clientId: GroupCallClientId,
members: Array<ArrayBuffer>
info: PeekInfo
): void;
handlePeekResponse(
request_id: number,
info: PeekInfo
): void;
handleEnded(
clientId: GroupCallClientId,
@ -1644,3 +1735,14 @@ export enum CallLogLevel {
Debug,
Trace,
}
function silly_deadlock_protection(f: () => void) {
// tslint:disable no-floating-promises
(async () => {
// This is a silly way of preventing a deadlock.
// tslint:disable-next-line await-promise
await 0;
f();
})();
}

View File

@ -303,8 +303,9 @@ export class CanvasVideoRenderer {
if (!!this.source) {
// If we're replacing an existing source, make sure we stop the
// current rAF loop before starting another one.
// And blanking the video is nice as well.
this.disable();
if (this.rafId) {
window.cancelAnimationFrame(this.rafId);
}
}
this.source = source;
this.requestAnimationFrameCallback();