Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/eleven-timers-kick.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@livekit/rtc-node": minor
"@livekit/rtc-node": patch
---

Add typeguards for frame processors in order to avoid dual package hazard
4 changes: 2 additions & 2 deletions packages/livekit-rtc/src/audio_stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { UnderlyingSource } from 'node:stream/web';
import { AudioFrame } from './audio_frame.js';
import type { FfiEvent } from './ffi_client.js';
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
import { type FrameProcessor, isAudioFrameProcessor } from './frame_processor.js';
import { type FrameProcessor, isFrameProcessor } from './frame_processor.js';
import { log } from './log.js';
import type { NewAudioStreamResponse } from './proto/audio_frame_pb.js';
import { AudioStreamType, NewAudioStreamRequest } from './proto/audio_frame_pb.js';
Expand Down Expand Up @@ -41,7 +41,7 @@ class AudioStreamSource implements UnderlyingSource<AudioFrame> {
if (sampleRateOrOptions !== undefined && typeof sampleRateOrOptions !== 'number') {
this.sampleRate = sampleRateOrOptions.sampleRate ?? 48000;
this.numChannels = sampleRateOrOptions.numChannels ?? 1;
if (isAudioFrameProcessor(sampleRateOrOptions.noiseCancellation)) {
if (isFrameProcessor(sampleRateOrOptions.noiseCancellation)) {
this.frameProcessor = sampleRateOrOptions.noiseCancellation;
} else {
this.legacyNcOptions = sampleRateOrOptions.noiseCancellation;
Expand Down
34 changes: 10 additions & 24 deletions packages/livekit-rtc/src/frame_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,26 @@ export type FrameProcessorCredentials = {

export const FrameProcessorSymbol = Symbol.for('lk.frame-processor');

export function isFrameProcessor<Type extends 'audio' | 'video'>(
export function isFrameProcessor<
T extends FrameProcessor<AudioFrame> | FrameProcessor<VideoFrame> | unknown,
>(
maybeProcessor: unknown,
type?: Type,
): maybeProcessor is FrameProcessor<
Type extends 'audio' ? AudioFrame : Type extends 'video' ? VideoFrame : AudioFrame | VideoFrame
> {
): maybeProcessor is T extends FrameProcessor<AudioFrame>
? FrameProcessor<AudioFrame>
: T extends FrameProcessor<VideoFrame>
? FrameProcessor<VideoFrame>
: FrameProcessor<AudioFrame> | FrameProcessor<VideoFrame> {
return (
maybeProcessor !== null &&
typeof maybeProcessor === 'object' &&
'symbol' in maybeProcessor &&
maybeProcessor.symbol === FrameProcessorSymbol &&
(!type || ('type' in maybeProcessor && maybeProcessor.type === type))
maybeProcessor.symbol === FrameProcessorSymbol
);
}

export function isAudioFrameProcessor(
maybeProcessor: unknown,
): maybeProcessor is FrameProcessor<AudioFrame> {
return isFrameProcessor(maybeProcessor, 'audio');
}

export function isVideoFrameProcessor(
maybeProcessor: unknown,
): maybeProcessor is FrameProcessor<VideoFrame> {
return isFrameProcessor(maybeProcessor, 'video');
}

export abstract class FrameProcessor<Frame extends VideoFrame | AudioFrame> {
readonly symbol = FrameProcessorSymbol;
abstract readonly type: Frame extends VideoFrame
? 'video'
: Frame extends AudioFrame
? 'audio'
: never;

abstract isEnabled(): boolean;
abstract setEnabled(enabled: boolean): void;

Expand Down
1 change: 1 addition & 0 deletions packages/livekit-rtc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ export {
FrameProcessor,
type FrameProcessorStreamInfo,
type FrameProcessorCredentials,
isFrameProcessor,
} from './frame_processor.js';
Loading