mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
[SDK] simplify public api (no lk processor, no encryption$ as obs)
This commit is contained in:
+30
-18
@@ -11,11 +11,6 @@ Please see LICENSE in the repository root for full details.
|
||||
* `api.ts`.
|
||||
*/
|
||||
|
||||
import {
|
||||
type Track,
|
||||
type TrackProcessor,
|
||||
type VideoCodec,
|
||||
} from "livekit-client";
|
||||
import { type Observable } from "rxjs";
|
||||
|
||||
import { type Behavior } from "./reactive/Behavior";
|
||||
@@ -26,6 +21,8 @@ export interface AudioCaptureSettings {
|
||||
autoGainControl?: boolean;
|
||||
}
|
||||
|
||||
export type VideoCodec = "vp8" | "h264" | "vp9" | "av1" | "h265";
|
||||
|
||||
export interface VideoCaptureSettings {
|
||||
resolution?: { width: number; height: number; frameRate?: number };
|
||||
maxBitrate?: number;
|
||||
@@ -33,6 +30,26 @@ export interface VideoCaptureSettings {
|
||||
codec?: VideoCodec;
|
||||
}
|
||||
|
||||
/** What a processor is given to work on: the captured track and, where a view is attached, its element. */
|
||||
export interface VideoProcessorInit {
|
||||
track: MediaStreamTrack;
|
||||
element?: HTMLMediaElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a camera track before it is published: background blur and the
|
||||
* like. `processedTrack` is what gets published once `init` has resolved;
|
||||
* `restart` is called with the new track where the camera changes. The shape
|
||||
* is that of a LiveKit track processor, so one of those can be passed as is.
|
||||
*/
|
||||
export interface VideoProcessor {
|
||||
name: string;
|
||||
processedTrack?: MediaStreamTrack;
|
||||
init(opts: VideoProcessorInit): Promise<void>;
|
||||
restart(opts: VideoProcessorInit): Promise<void>;
|
||||
destroy(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* One thing to publish: a source, where to capture it from and how to encode
|
||||
* it. Where the device and the settings are left out, the browser's and
|
||||
@@ -43,13 +60,8 @@ export type PublishRequest =
|
||||
| {
|
||||
source: "camera";
|
||||
deviceId?: string;
|
||||
/** Background blur and the like. */
|
||||
// REVIEW: this is ugly makes us need to import lk
|
||||
// We need an interface:
|
||||
// - super high level: blur + background img
|
||||
// - lower level: rtc stream insertable stream (copy lk TrackProcessor concept)
|
||||
// - florian: should be independent of webCodec or WebRtc
|
||||
processor?: TrackProcessor<Track.Kind.Video>;
|
||||
/** Background blur and the like, applied before the first frame is published. */
|
||||
processor?: VideoProcessor;
|
||||
capture?: VideoCaptureSettings;
|
||||
}
|
||||
| {
|
||||
@@ -79,9 +91,11 @@ export interface MediaTrack {
|
||||
/** Stable for the life of the track. */
|
||||
id: string;
|
||||
muted$: Behavior<boolean>;
|
||||
/** False when the SFU reports the track as unencrypted. */
|
||||
// REVIEW: does it need to be a behavior
|
||||
encrypted$: Behavior<boolean>;
|
||||
/**
|
||||
* False when the SFU reports the track as unencrypted. Fixed for the life
|
||||
* of the track: a publisher that changes its encryption publishes anew.
|
||||
*/
|
||||
encrypted: boolean;
|
||||
/** Polled while subscribed. */
|
||||
// REVIEW: we want the perf gain of not having it always run.
|
||||
//
|
||||
@@ -143,9 +157,7 @@ export interface LocalVideoMediaTrack extends VideoMediaTrack, LocalMediaTrack {
|
||||
*/
|
||||
switchFacingMode(): Promise<string | undefined>;
|
||||
/** Background blur and the like; undefined removes the processor. */
|
||||
setProcessor(
|
||||
processor: TrackProcessor<Track.Kind.Video> | undefined,
|
||||
): Promise<void>;
|
||||
setProcessor(processor: VideoProcessor | undefined): Promise<void>;
|
||||
}
|
||||
|
||||
export type EncryptionError = "MissingKey" | "InvalidKey";
|
||||
|
||||
@@ -33,21 +33,20 @@ describe("createLivekitMediaTrack", () => {
|
||||
afterEach(() => scope.end());
|
||||
|
||||
it("describes the publication", () => {
|
||||
const { participant, publication, room } = fakes();
|
||||
const { participant, publication } = fakes();
|
||||
const track = createLivekitMediaTrack(
|
||||
scope,
|
||||
participant,
|
||||
publication as unknown as TrackPublication,
|
||||
room,
|
||||
);
|
||||
expect(track.kind).toBe("video");
|
||||
expect(track.source).toBe("camera");
|
||||
expect(track.id).toBe("TR_1");
|
||||
expect(track.encrypted$.value).toBe(true);
|
||||
expect(track.encrypted).toBe(true);
|
||||
});
|
||||
|
||||
it("attaches elements to the track, even one that arrives later", () => {
|
||||
const { participant, publication, room, emitter } = fakes({
|
||||
const { participant, publication, emitter } = fakes({
|
||||
track: undefined,
|
||||
});
|
||||
const element = document.createElement("video");
|
||||
@@ -55,7 +54,6 @@ describe("createLivekitMediaTrack", () => {
|
||||
scope,
|
||||
participant,
|
||||
publication as unknown as TrackPublication,
|
||||
room,
|
||||
);
|
||||
track.attach(element);
|
||||
track.attach(element);
|
||||
@@ -71,14 +69,13 @@ describe("createLivekitMediaTrack", () => {
|
||||
});
|
||||
|
||||
it("detaches everything when its scope ends", () => {
|
||||
const { participant, publication, room } = fakes();
|
||||
const { participant, publication } = fakes();
|
||||
const element = document.createElement("video");
|
||||
const scope = new ObservableScope();
|
||||
const track = createLivekitMediaTrack(
|
||||
scope,
|
||||
participant,
|
||||
publication as unknown as TrackPublication,
|
||||
room,
|
||||
);
|
||||
track.attach(element);
|
||||
scope.end();
|
||||
@@ -86,12 +83,11 @@ describe("createLivekitMediaTrack", () => {
|
||||
});
|
||||
|
||||
it("follows the mute state", () => {
|
||||
const { participant, publication, room, emitter } = fakes();
|
||||
const { participant, publication, emitter } = fakes();
|
||||
const track = createLivekitMediaTrack(
|
||||
scope,
|
||||
participant,
|
||||
publication as unknown as TrackPublication,
|
||||
room,
|
||||
);
|
||||
expect(track.muted$.value).toBe(false);
|
||||
publication.isMuted = true;
|
||||
@@ -100,28 +96,26 @@ describe("createLivekitMediaTrack", () => {
|
||||
});
|
||||
|
||||
it("scales a remote member's volume on the participant", () => {
|
||||
const { participant, publication, room } = fakes({
|
||||
const { participant, publication } = fakes({
|
||||
kind: Track.Kind.Audio,
|
||||
});
|
||||
const track = createLivekitMediaTrack(
|
||||
scope,
|
||||
participant,
|
||||
publication as unknown as TrackPublication,
|
||||
room,
|
||||
) as AudioMediaTrack;
|
||||
track.setVolume(0.5);
|
||||
expect(publication.track!.setVolume).toHaveBeenCalledWith(0.5);
|
||||
});
|
||||
|
||||
it("is active while the member speaks and the track is not muted", () => {
|
||||
const { participant, publication, room, emitter } = fakes({
|
||||
const { participant, publication, emitter } = fakes({
|
||||
kind: Track.Kind.Audio,
|
||||
});
|
||||
const track = createLivekitMediaTrack(
|
||||
scope,
|
||||
participant,
|
||||
publication as unknown as TrackPublication,
|
||||
room,
|
||||
) as AudioMediaTrack;
|
||||
expect(track.isActive$.value).toBe(false);
|
||||
participant.isSpeaking = true;
|
||||
|
||||
@@ -8,7 +8,6 @@ Please see LICENSE in the repository root for full details.
|
||||
import {
|
||||
observeParticipantEvents,
|
||||
observeParticipantMedia,
|
||||
roomEventSelector,
|
||||
} from "@livekit/components-core";
|
||||
import {
|
||||
facingModeFromLocalTrack,
|
||||
@@ -20,7 +19,6 @@ import {
|
||||
RemoteAudioTrack,
|
||||
RemoteTrack,
|
||||
type Room as LivekitRoom,
|
||||
RoomEvent,
|
||||
Track,
|
||||
TrackEvent,
|
||||
type TrackPublication,
|
||||
@@ -81,7 +79,6 @@ export function createLivekitMediaTrack(
|
||||
scope: ObservableScope,
|
||||
participant: Participant,
|
||||
publication: TrackPublication,
|
||||
room: LivekitRoom,
|
||||
): AudioMediaTrack | VideoMediaTrack {
|
||||
const mediaChanged$ = observeParticipantMedia(participant);
|
||||
const track$ = publicationTrack$(scope, participant, publication);
|
||||
@@ -118,13 +115,7 @@ export function createLivekitMediaTrack(
|
||||
kind: publication.kind === Track.Kind.Audio ? "audio" : "video",
|
||||
id: publication.trackSid,
|
||||
muted$,
|
||||
encrypted$: scope.behavior(
|
||||
merge(
|
||||
mediaChanged$,
|
||||
roomEventSelector(room, RoomEvent.ParticipantEncryptionStatusChanged),
|
||||
).pipe(map(() => publication.isEncrypted)),
|
||||
publication.isEncrypted,
|
||||
),
|
||||
encrypted: publication.isEncrypted,
|
||||
stats$: new LazyBehavior<MediaStreamStats>(
|
||||
refreshStats$.pipe(
|
||||
switchMap(async () => rtpStreamStats(publication, participant.isLocal)),
|
||||
@@ -189,7 +180,7 @@ export function createLocalLivekitMediaTrack(
|
||||
room: LivekitRoom,
|
||||
setEnabled: (source: MediaSource, enabled: boolean) => Promise<boolean>,
|
||||
): LocalAudioMediaTrack | LocalVideoMediaTrack {
|
||||
const base = createLivekitMediaTrack(scope, participant, publication, room);
|
||||
const base = createLivekitMediaTrack(scope, participant, publication);
|
||||
const local = {
|
||||
setEnabled: async (enabled: boolean) => setEnabled(base.source, enabled),
|
||||
setDevice: async (deviceId: string): Promise<void> => {
|
||||
|
||||
@@ -54,8 +54,8 @@ export function createLivekitMemberMedia(
|
||||
tracks$: memberTracks$(
|
||||
scope,
|
||||
source$,
|
||||
(trackScope, participant, publication, room) =>
|
||||
createLivekitMediaTrack(trackScope, participant, publication, room),
|
||||
(trackScope, participant, publication) =>
|
||||
createLivekitMediaTrack(trackScope, participant, publication),
|
||||
),
|
||||
encryptionError$: encryptionErrors$(source$, encryptionSystem),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user