Skip to content
Open
3 changes: 0 additions & 3 deletions .github/workflows/size-limit.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: 'size'
on:
pull_request:
branches:
- main
workflow_dispatch:
jobs:
package-size:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
test:
Expand Down
3 changes: 1 addition & 2 deletions src/api/SignalClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,6 @@ export class SignalClient {
withTimeout(firstMessageOrClose, 5_000),
abortSignal,
).orTee((error) => {
console.warn('signal connection aborted');
if (error.reason === ConnectionErrorReason.Cancelled) {
self
.sendLeave()
Expand Down Expand Up @@ -947,13 +946,13 @@ export class SignalClient {
);
} else if (!isReconnect) {
// non-reconnect case, should receive join response first

return err(
ConnectionError.internal(
`did not receive join response, got ${firstSignalResponse.message?.case} instead`,
),
);
}

return err(ConnectionError.internal('Unexpected first message'));
}

Expand Down
14 changes: 14 additions & 0 deletions src/api/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Mutex } from '@livekit/mutex';
import { SignalResponse } from '@livekit/protocol';
import { Result, ResultAsync, errAsync } from 'neverthrow';
import type TypedEventEmitter from 'typed-emitter';
import type { EventMap } from 'typed-emitter';
import { ConnectionError } from '../room/errors';
import { toHttpUrl, toWebsocketUrl } from '../room/utils';

Expand Down Expand Up @@ -177,3 +179,15 @@ export function raceResults<T extends readonly ResultAsyncLike<any, any>[]>(
}

export type ResultAsyncLike<T, E> = ResultAsync<T, E> | Promise<Result<T, E>>;

export function resultFromEvent<C extends EventMap, K extends keyof C>(
emitter: TypedEventEmitter<C>,
event: K,
): ResultAsync<Parameters<C[K]>, never> {
const resultPromise = new Promise<Parameters<C[K]>>((resolve) => {
emitter.once(event, ((...args: Parameters<C[K]>) => {
resolve(args);
}) as C[K]);
});
return ResultAsync.fromSafePromise(resultPromise);
}
7 changes: 6 additions & 1 deletion src/connectionHelper/checks/cloudRegion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ export class CloudRegionCheck extends Checker {
const regionStats: RegionStats[] = [];
const seenUrls: Set<string> = new Set();
for (let i = 0; i < 3; i++) {
const regionUrl = await regionProvider.getNextBestRegionUrl();
const regionUrlResult = await regionProvider.getNextBestRegionUrl();
if (regionUrlResult.isErr()) {
console.error(regionUrlResult.error);
return;
}
const regionUrl = regionUrlResult.value;
if (!regionUrl) {
break;
}
Expand Down
7 changes: 5 additions & 2 deletions src/connectionHelper/checks/turn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ export class TURNCheck extends Checker {
singlePeerConnection: false,
});

// TODO fix unsafe usage
const joinRes = joinResult._unsafeUnwrap();
if (joinResult.isErr()) {
throw joinResult.error;
}

const joinRes = joinResult.value;

let hasTLS = false;
let hasTURN = false;
Expand Down
27 changes: 22 additions & 5 deletions src/room/PCTransport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Mutex } from '@livekit/mutex';
import { EventEmitter } from 'events';
import { ResultAsync, errAsync, okAsync } from 'neverthrow';
import type { MediaDescription, SessionDescription } from 'sdp-transform';
import { parse, write } from 'sdp-transform';
import { debounce } from 'ts-debounce';
Expand Down Expand Up @@ -375,19 +376,35 @@ export default class PCTransport extends EventEmitter {
return this.pc.createDataChannel(label, dataChannelDict);
}

addTransceiver(mediaStreamTrack: MediaStreamTrack, transceiverInit: RTCRtpTransceiverInit) {
return this.pc.addTransceiver(mediaStreamTrack, transceiverInit);
addTransceiver(
mediaStreamTrack: MediaStreamTrack,
transceiverInit: RTCRtpTransceiverInit,
): ResultAsync<RTCRtpTransceiver, TypeError | RangeError | DOMException> {
return ResultAsync.fromPromise(
// wrapping this awkwardly as an async IIFE is required as `addTransceiver` is async in react native
(async () => {
const res = await this.pc.addTransceiver(mediaStreamTrack, transceiverInit);
return res;
})(),
(e) => e as TypeError | RangeError | DOMException,
);
}

addTransceiverOfKind(kind: 'audio' | 'video', transceiverInit: RTCRtpTransceiverInit) {
return this.pc.addTransceiver(kind, transceiverInit);
}

addTrack(track: MediaStreamTrack) {
addTrack(
track: MediaStreamTrack,
): ResultAsync<RTCRtpSender, UnexpectedConnectionState | DOMException> {
if (!this._pc) {
throw new UnexpectedConnectionState('PC closed, cannot add track');
return errAsync(new UnexpectedConnectionState('PC closed, cannot add track'));
}
try {
return okAsync(this._pc.addTrack(track));
} catch (e: unknown) {
return errAsync(e as DOMException);
}
return this._pc.addTrack(track);
}

setTrackCodecBitrate(info: TrackBitrateInfo) {
Expand Down
Loading