@livekit/rtc-node 0.13.11 → 0.13.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio_filter.cjs.map +1 -1
- package/dist/audio_filter.d.ts.map +1 -1
- package/dist/audio_filter.js +1 -4
- package/dist/audio_filter.js.map +1 -1
- package/dist/audio_source.cjs +5 -1
- package/dist/audio_source.cjs.map +1 -1
- package/dist/audio_source.d.ts.map +1 -1
- package/dist/audio_source.js +5 -1
- package/dist/audio_source.js.map +1 -1
- package/dist/audio_stream.cjs +15 -36
- package/dist/audio_stream.cjs.map +1 -1
- package/dist/audio_stream.d.cts +4 -22
- package/dist/audio_stream.d.ts +4 -22
- package/dist/audio_stream.d.ts.map +1 -1
- package/dist/audio_stream.js +15 -36
- package/dist/audio_stream.js.map +1 -1
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/room.cjs +1 -0
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +1 -0
- package/dist/room.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/video_stream.cjs +20 -48
- package/dist/video_stream.cjs.map +1 -1
- package/dist/video_stream.d.cts +4 -19
- package/dist/video_stream.d.ts +4 -19
- package/dist/video_stream.d.ts.map +1 -1
- package/dist/video_stream.js +20 -48
- package/dist/video_stream.js.map +1 -1
- package/package.json +6 -6
- package/src/audio_filter.ts +2 -4
- package/src/audio_source.ts +7 -3
- package/src/audio_stream.ts +30 -51
- package/src/room.ts +2 -0
- package/src/version.ts +1 -1
- package/src/video_stream.ts +24 -60
package/dist/video_stream.d.cts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { VideoRotation, VideoStreamInfo } from './proto/video_frame_pb.cjs';
|
|
1
|
+
import { ReadableStream } from 'node:stream/web';
|
|
2
|
+
import { VideoRotation } from './proto/video_frame_pb.cjs';
|
|
4
3
|
import { Track } from './track.cjs';
|
|
5
4
|
import { VideoFrame } from './video_frame.cjs';
|
|
6
5
|
import '@bufbuild/protobuf';
|
|
@@ -11,6 +10,7 @@ import './proto/handle_pb.cjs';
|
|
|
11
10
|
import './audio_source.cjs';
|
|
12
11
|
import './audio_frame.cjs';
|
|
13
12
|
import './proto/audio_frame_pb.cjs';
|
|
13
|
+
import './napi/native.d.cjs';
|
|
14
14
|
import './video_source.cjs';
|
|
15
15
|
|
|
16
16
|
type VideoFrameEvent = {
|
|
@@ -18,23 +18,8 @@ type VideoFrameEvent = {
|
|
|
18
18
|
timestampUs: bigint;
|
|
19
19
|
rotation: VideoRotation;
|
|
20
20
|
};
|
|
21
|
-
declare class VideoStream
|
|
22
|
-
/** @internal */
|
|
23
|
-
info?: VideoStreamInfo;
|
|
24
|
-
/** @internal */
|
|
25
|
-
ffiHandle: FfiHandle;
|
|
26
|
-
/** @internal */
|
|
27
|
-
eventQueue: (VideoFrameEvent | null)[];
|
|
28
|
-
/** @internal */
|
|
29
|
-
queueResolve: ((value: IteratorResult<VideoFrameEvent>) => void) | null;
|
|
30
|
-
/** @internal */
|
|
31
|
-
mutex: Mutex;
|
|
32
|
-
track: Track;
|
|
21
|
+
declare class VideoStream extends ReadableStream<VideoFrameEvent> {
|
|
33
22
|
constructor(track: Track);
|
|
34
|
-
private onEvent;
|
|
35
|
-
next(): Promise<IteratorResult<VideoFrameEvent>>;
|
|
36
|
-
close(): void;
|
|
37
|
-
[Symbol.asyncIterator](): VideoStream;
|
|
38
23
|
}
|
|
39
24
|
|
|
40
25
|
export { type VideoFrameEvent, VideoStream };
|
package/dist/video_stream.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { VideoRotation, VideoStreamInfo } from './proto/video_frame_pb.js';
|
|
1
|
+
import { ReadableStream } from 'node:stream/web';
|
|
2
|
+
import { VideoRotation } from './proto/video_frame_pb.js';
|
|
4
3
|
import { Track } from './track.js';
|
|
5
4
|
import { VideoFrame } from './video_frame.js';
|
|
6
5
|
import '@bufbuild/protobuf';
|
|
@@ -11,6 +10,7 @@ import './proto/handle_pb.js';
|
|
|
11
10
|
import './audio_source.js';
|
|
12
11
|
import './audio_frame.js';
|
|
13
12
|
import './proto/audio_frame_pb.js';
|
|
13
|
+
import './napi/native.d.js';
|
|
14
14
|
import './video_source.js';
|
|
15
15
|
|
|
16
16
|
type VideoFrameEvent = {
|
|
@@ -18,23 +18,8 @@ type VideoFrameEvent = {
|
|
|
18
18
|
timestampUs: bigint;
|
|
19
19
|
rotation: VideoRotation;
|
|
20
20
|
};
|
|
21
|
-
declare class VideoStream
|
|
22
|
-
/** @internal */
|
|
23
|
-
info?: VideoStreamInfo;
|
|
24
|
-
/** @internal */
|
|
25
|
-
ffiHandle: FfiHandle;
|
|
26
|
-
/** @internal */
|
|
27
|
-
eventQueue: (VideoFrameEvent | null)[];
|
|
28
|
-
/** @internal */
|
|
29
|
-
queueResolve: ((value: IteratorResult<VideoFrameEvent>) => void) | null;
|
|
30
|
-
/** @internal */
|
|
31
|
-
mutex: Mutex;
|
|
32
|
-
track: Track;
|
|
21
|
+
declare class VideoStream extends ReadableStream<VideoFrameEvent> {
|
|
33
22
|
constructor(track: Track);
|
|
34
|
-
private onEvent;
|
|
35
|
-
next(): Promise<IteratorResult<VideoFrameEvent>>;
|
|
36
|
-
close(): void;
|
|
37
|
-
[Symbol.asyncIterator](): VideoStream;
|
|
38
23
|
}
|
|
39
24
|
|
|
40
25
|
export { type VideoFrameEvent, VideoStream };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video_stream.d.ts","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"video_stream.d.ts","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAyB,MAAM,iBAAiB,CAAC;AAGxE,OAAO,KAAK,EAA0B,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAEvF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AAkEF,qBAAa,WAAY,SAAQ,cAAc,CAAC,eAAe,CAAC;gBAClD,KAAK,EAAE,KAAK;CAGzB"}
|
package/dist/video_stream.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReadableStream } from "node:stream/web";
|
|
2
2
|
import { FfiClient, FfiClientEvent, FfiHandle } from "./ffi_client.js";
|
|
3
3
|
import { NewVideoStreamRequest, VideoStreamType } from "./proto/video_frame_pb.js";
|
|
4
4
|
import { VideoFrame } from "./video_frame.js";
|
|
5
|
-
class
|
|
5
|
+
class VideoStreamSource {
|
|
6
6
|
constructor(track) {
|
|
7
|
-
/** @internal */
|
|
8
|
-
this.eventQueue = [];
|
|
9
|
-
/** @internal */
|
|
10
|
-
this.queueResolve = null;
|
|
11
|
-
/** @internal */
|
|
12
|
-
this.mutex = new Mutex();
|
|
13
7
|
this.onEvent = (ev) => {
|
|
8
|
+
if (!this.controller) {
|
|
9
|
+
throw new Error("Stream controller not initialized");
|
|
10
|
+
}
|
|
14
11
|
if (ev.message.case != "videoStreamEvent" || ev.message.value.streamHandle != this.ffiHandle.handle) {
|
|
15
12
|
return;
|
|
16
13
|
}
|
|
@@ -21,31 +18,19 @@ class VideoStream {
|
|
|
21
18
|
const timestampUs = streamEvent.value.timestampUs;
|
|
22
19
|
const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer);
|
|
23
20
|
const value = { rotation, timestampUs, frame };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
rotation: value.rotation
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
this.queueResolve = null;
|
|
34
|
-
} else {
|
|
35
|
-
this.eventQueue.push({
|
|
36
|
-
frame: value.frame,
|
|
37
|
-
timestampUs: value.timestampUs,
|
|
38
|
-
rotation: value.rotation
|
|
39
|
-
});
|
|
40
|
-
}
|
|
21
|
+
const videoFrameEvent = {
|
|
22
|
+
frame: value.frame,
|
|
23
|
+
timestampUs: value.timestampUs,
|
|
24
|
+
rotation: value.rotation
|
|
25
|
+
};
|
|
26
|
+
this.controller.enqueue(videoFrameEvent);
|
|
41
27
|
break;
|
|
42
28
|
case "eos":
|
|
43
29
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
30
|
+
this.controller.close();
|
|
44
31
|
break;
|
|
45
32
|
}
|
|
46
33
|
};
|
|
47
|
-
var _a;
|
|
48
|
-
this.track = track;
|
|
49
34
|
const req = new NewVideoStreamRequest({
|
|
50
35
|
type: VideoStreamType.VIDEO_STREAM_NATIVE,
|
|
51
36
|
trackHandle: track.ffi_handle.handle
|
|
@@ -56,33 +41,20 @@ class VideoStream {
|
|
|
56
41
|
value: req
|
|
57
42
|
}
|
|
58
43
|
});
|
|
59
|
-
this.info = (_a = res.stream) == null ? void 0 : _a.info;
|
|
60
44
|
this.ffiHandle = new FfiHandle(res.stream.handle.id);
|
|
61
45
|
FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);
|
|
62
46
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (this.eventQueue.length > 0) {
|
|
66
|
-
unlock();
|
|
67
|
-
const value = this.eventQueue.shift();
|
|
68
|
-
if (value) {
|
|
69
|
-
return { done: false, value };
|
|
70
|
-
} else {
|
|
71
|
-
return { done: true, value: void 0 };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const promise = new Promise(
|
|
75
|
-
(resolve) => this.queueResolve = resolve
|
|
76
|
-
);
|
|
77
|
-
unlock();
|
|
78
|
-
return promise;
|
|
47
|
+
start(controller) {
|
|
48
|
+
this.controller = controller;
|
|
79
49
|
}
|
|
80
|
-
|
|
81
|
-
|
|
50
|
+
cancel() {
|
|
51
|
+
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
82
52
|
this.ffiHandle.dispose();
|
|
83
53
|
}
|
|
84
|
-
|
|
85
|
-
|
|
54
|
+
}
|
|
55
|
+
class VideoStream extends ReadableStream {
|
|
56
|
+
constructor(track) {
|
|
57
|
+
super(new VideoStreamSource(track));
|
|
86
58
|
}
|
|
87
59
|
}
|
|
88
60
|
export {
|
package/dist/video_stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/video_stream.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { ReadableStream, type UnderlyingSource } from 'node:stream/web';\nimport type { FfiEvent } from './ffi_client.js';\nimport { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';\nimport type { NewVideoStreamResponse, VideoRotation } from './proto/video_frame_pb.js';\nimport { NewVideoStreamRequest, VideoStreamType } from './proto/video_frame_pb.js';\nimport type { Track } from './track.js';\nimport { VideoFrame } from './video_frame.js';\n\nexport type VideoFrameEvent = {\n frame: VideoFrame;\n timestampUs: bigint;\n rotation: VideoRotation;\n};\n\nclass VideoStreamSource implements UnderlyingSource<VideoFrameEvent> {\n private controller?: ReadableStreamDefaultController<VideoFrameEvent>;\n private ffiHandle: FfiHandle;\n\n constructor(track: Track) {\n const req = new NewVideoStreamRequest({\n type: VideoStreamType.VIDEO_STREAM_NATIVE,\n trackHandle: track.ffi_handle.handle,\n });\n\n const res = FfiClient.instance.request<NewVideoStreamResponse>({\n message: {\n case: 'newVideoStream',\n value: req,\n },\n });\n\n this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);\n FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);\n }\n\n private onEvent = (ev: FfiEvent) => {\n if (!this.controller) {\n throw new Error('Stream controller not initialized');\n }\n\n if (\n ev.message.case != 'videoStreamEvent' ||\n ev.message.value.streamHandle != this.ffiHandle.handle\n ) {\n return;\n }\n\n const streamEvent = ev.message.value.message;\n switch (streamEvent.case) {\n case 'frameReceived':\n const rotation = streamEvent.value.rotation;\n const timestampUs = streamEvent.value.timestampUs;\n const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer!);\n const value = { rotation, timestampUs, frame };\n const videoFrameEvent = {\n frame: value.frame,\n timestampUs: value.timestampUs!,\n rotation: value.rotation!,\n };\n this.controller.enqueue(videoFrameEvent);\n break;\n case 'eos':\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n this.controller.close();\n break;\n }\n };\n\n start(controller: ReadableStreamDefaultController<VideoFrameEvent>) {\n this.controller = controller;\n }\n\n cancel() {\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);\n this.ffiHandle.dispose();\n }\n}\n\nexport class VideoStream extends ReadableStream<VideoFrameEvent> {\n constructor(track: Track) {\n super(new VideoStreamSource(track));\n }\n}\n"],"mappings":"AAGA,SAAS,sBAA6C;AAEtD,SAAS,WAAW,gBAAgB,iBAAiB;AAErD,SAAS,uBAAuB,uBAAuB;AAEvD,SAAS,kBAAkB;AAQ3B,MAAM,kBAA+D;AAAA,EAInE,YAAY,OAAc;AAiB1B,SAAQ,UAAU,CAAC,OAAiB;AAClC,UAAI,CAAC,KAAK,YAAY;AACpB,cAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAEA,UACE,GAAG,QAAQ,QAAQ,sBACnB,GAAG,QAAQ,MAAM,gBAAgB,KAAK,UAAU,QAChD;AACA;AAAA,MACF;AAEA,YAAM,cAAc,GAAG,QAAQ,MAAM;AACrC,cAAQ,YAAY,MAAM;AAAA,QACxB,KAAK;AACH,gBAAM,WAAW,YAAY,MAAM;AACnC,gBAAM,cAAc,YAAY,MAAM;AACtC,gBAAM,QAAQ,WAAW,cAAc,YAAY,MAAM,MAAO;AAChE,gBAAM,QAAQ,EAAE,UAAU,aAAa,MAAM;AAC7C,gBAAM,kBAAkB;AAAA,YACtB,OAAO,MAAM;AAAA,YACb,aAAa,MAAM;AAAA,YACnB,UAAU,MAAM;AAAA,UAClB;AACA,eAAK,WAAW,QAAQ,eAAe;AACvC;AAAA,QACF,KAAK;AACH,oBAAU,SAAS,IAAI,eAAe,UAAU,KAAK,OAAO;AAC5D,eAAK,WAAW,MAAM;AACtB;AAAA,MACJ;AAAA,IACF;AA/CE,UAAM,MAAM,IAAI,sBAAsB;AAAA,MACpC,MAAM,gBAAgB;AAAA,MACtB,aAAa,MAAM,WAAW;AAAA,IAChC,CAAC;AAED,UAAM,MAAM,UAAU,SAAS,QAAgC;AAAA,MAC7D,SAAS;AAAA,QACP,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,YAAY,IAAI,UAAU,IAAI,OAAQ,OAAQ,EAAG;AACtD,cAAU,SAAS,GAAG,eAAe,UAAU,KAAK,OAAO;AAAA,EAC7D;AAAA,EAmCA,MAAM,YAA8D;AAClE,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,SAAS;AACP,cAAU,SAAS,IAAI,eAAe,UAAU,KAAK,OAAO;AAC5D,SAAK,UAAU,QAAQ;AAAA,EACzB;AACF;AAEO,MAAM,oBAAoB,eAAgC;AAAA,EAC/D,YAAY,OAAc;AACxB,UAAM,IAAI,kBAAkB,KAAK,CAAC;AAAA,EACpC;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "LiveKit RTC Node",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "LiveKit",
|
|
6
|
-
"version": "0.13.
|
|
6
|
+
"version": "0.13.13",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"require": "dist/index.cjs",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"@bufbuild/protoc-gen-es": "^1.10.0"
|
|
62
62
|
},
|
|
63
63
|
"optionalDependencies": {
|
|
64
|
-
"@livekit/rtc-node-darwin-arm64": "0.13.
|
|
65
|
-
"@livekit/rtc-node-darwin-x64": "0.13.
|
|
66
|
-
"@livekit/rtc-node-linux-arm64-gnu": "0.13.
|
|
67
|
-
"@livekit/rtc-node-linux-x64-gnu": "0.13.
|
|
68
|
-
"@livekit/rtc-node-win32-x64-msvc": "0.13.
|
|
64
|
+
"@livekit/rtc-node-darwin-arm64": "0.13.13",
|
|
65
|
+
"@livekit/rtc-node-darwin-x64": "0.13.13",
|
|
66
|
+
"@livekit/rtc-node-linux-arm64-gnu": "0.13.13",
|
|
67
|
+
"@livekit/rtc-node-linux-x64-gnu": "0.13.13",
|
|
68
|
+
"@livekit/rtc-node-win32-x64-msvc": "0.13.13"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">= 18"
|
package/src/audio_filter.ts
CHANGED
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import { FfiClient } from './ffi_client.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
LoadAudioFilterPluginResponse,
|
|
8
|
-
} from './proto/audio_frame_pb.js';
|
|
5
|
+
import type { LoadAudioFilterPluginResponse } from './proto/audio_frame_pb.js';
|
|
6
|
+
import { LoadAudioFilterPluginRequest } from './proto/audio_frame_pb.js';
|
|
9
7
|
|
|
10
8
|
export class AudioFilter {
|
|
11
9
|
constructor(moduleId: string, path: string, dependencies: string[] = []) {
|
package/src/audio_source.ts
CHANGED
|
@@ -99,6 +99,7 @@ export class AudioSource {
|
|
|
99
99
|
this.lastCapture = 0;
|
|
100
100
|
this.currentQueueSize = 0;
|
|
101
101
|
this.promise = this.newPromise();
|
|
102
|
+
this.timeout = undefined;
|
|
102
103
|
});
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -106,6 +107,11 @@ export class AudioSource {
|
|
|
106
107
|
if (this.closed) {
|
|
107
108
|
throw new Error('AudioSource is closed');
|
|
108
109
|
}
|
|
110
|
+
|
|
111
|
+
if (frame.samplesPerChannel === 0) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
109
115
|
const now = Number(process.hrtime.bigint() / BigInt(1000000));
|
|
110
116
|
const elapsed = this.lastCapture === 0 ? 0 : now - this.lastCapture;
|
|
111
117
|
const frameDurationMs = (frame.samplesPerChannel / frame.sampleRate) * 1000;
|
|
@@ -117,9 +123,7 @@ export class AudioSource {
|
|
|
117
123
|
clearTimeout(this.timeout);
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
|
|
121
|
-
// (e.g. using wait_for_playout for very small chunks)
|
|
122
|
-
this.timeout = setTimeout(this.release, this.currentQueueSize - 50);
|
|
126
|
+
this.timeout = setTimeout(this.release, this.currentQueueSize);
|
|
123
127
|
|
|
124
128
|
const req = new CaptureAudioFrameRequest({
|
|
125
129
|
sourceHandle: this.ffiHandle.handle,
|
package/src/audio_stream.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import {
|
|
4
|
+
import { ReadableStream, type UnderlyingSource } from 'node:stream/web';
|
|
5
5
|
import { AudioFrame } from './audio_frame.js';
|
|
6
6
|
import type { FfiEvent } from './ffi_client.js';
|
|
7
7
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
8
|
-
import type {
|
|
8
|
+
import type { NewAudioStreamResponse } from './proto/audio_frame_pb.js';
|
|
9
9
|
import { AudioStreamType, NewAudioStreamRequest } from './proto/audio_frame_pb.js';
|
|
10
10
|
import type { Track } from './track.js';
|
|
11
11
|
|
|
@@ -20,34 +20,18 @@ export interface NoiseCancellationOptions {
|
|
|
20
20
|
options: Record<string, any>;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
eventQueue: (AudioFrame | null)[] = [];
|
|
30
|
-
/** @internal */
|
|
31
|
-
queueResolve: ((value: IteratorResult<AudioFrame>) => void) | null = null;
|
|
32
|
-
/** @internal */
|
|
33
|
-
mutex = new Mutex();
|
|
34
|
-
|
|
35
|
-
track: Track;
|
|
36
|
-
sampleRate: number;
|
|
37
|
-
numChannels: number;
|
|
38
|
-
ncOptions?: NoiseCancellationOptions;
|
|
39
|
-
|
|
40
|
-
constructor(track: Track);
|
|
41
|
-
constructor(track: Track, sampleRate: number);
|
|
42
|
-
constructor(track: Track, sampleRate: number, numChannels: number);
|
|
43
|
-
constructor(track: Track, options: AudioStreamOptions);
|
|
23
|
+
class AudioStreamSource implements UnderlyingSource<AudioFrame> {
|
|
24
|
+
private controller?: ReadableStreamDefaultController<AudioFrame>;
|
|
25
|
+
private ffiHandle: FfiHandle;
|
|
26
|
+
private sampleRate: number;
|
|
27
|
+
private numChannels: number;
|
|
28
|
+
private ncOptions?: NoiseCancellationOptions;
|
|
44
29
|
|
|
45
30
|
constructor(
|
|
46
31
|
track: Track,
|
|
47
32
|
sampleRateOrOptions?: number | AudioStreamOptions,
|
|
48
33
|
numChannels?: number,
|
|
49
34
|
) {
|
|
50
|
-
this.track = track;
|
|
51
35
|
if (sampleRateOrOptions !== undefined && typeof sampleRateOrOptions !== 'number') {
|
|
52
36
|
this.sampleRate = sampleRateOrOptions.sampleRate ?? 48000;
|
|
53
37
|
this.numChannels = sampleRateOrOptions.numChannels ?? 1;
|
|
@@ -77,13 +61,16 @@ export class AudioStream implements AsyncIterableIterator<AudioFrame> {
|
|
|
77
61
|
},
|
|
78
62
|
});
|
|
79
63
|
|
|
80
|
-
this.info = res.stream!.info!;
|
|
81
64
|
this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);
|
|
82
65
|
|
|
83
66
|
FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);
|
|
84
67
|
}
|
|
85
68
|
|
|
86
69
|
private onEvent = (ev: FfiEvent) => {
|
|
70
|
+
if (!this.controller) {
|
|
71
|
+
throw new Error('Stream controller not initialized');
|
|
72
|
+
}
|
|
73
|
+
|
|
87
74
|
if (
|
|
88
75
|
ev.message.case != 'audioStreamEvent' ||
|
|
89
76
|
ev.message.value.streamHandle != this.ffiHandle.handle
|
|
@@ -95,43 +82,35 @@ export class AudioStream implements AsyncIterableIterator<AudioFrame> {
|
|
|
95
82
|
switch (streamEvent.case) {
|
|
96
83
|
case 'frameReceived':
|
|
97
84
|
const frame = AudioFrame.fromOwnedInfo(streamEvent.value.frame!);
|
|
98
|
-
|
|
99
|
-
this.queueResolve({ done: false, value: frame });
|
|
100
|
-
this.queueResolve = null;
|
|
101
|
-
} else {
|
|
102
|
-
this.eventQueue.push(frame);
|
|
103
|
-
}
|
|
85
|
+
this.controller.enqueue(frame);
|
|
104
86
|
break;
|
|
105
87
|
case 'eos':
|
|
106
88
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
89
|
+
this.controller.close();
|
|
107
90
|
break;
|
|
108
91
|
}
|
|
109
92
|
};
|
|
110
93
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (this.eventQueue.length > 0) {
|
|
114
|
-
unlock();
|
|
115
|
-
const value = this.eventQueue.shift();
|
|
116
|
-
if (value) {
|
|
117
|
-
return { done: false, value };
|
|
118
|
-
} else {
|
|
119
|
-
return { done: true, value: undefined };
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const promise = new Promise<IteratorResult<AudioFrame>>(
|
|
123
|
-
(resolve) => (this.queueResolve = resolve),
|
|
124
|
-
);
|
|
125
|
-
unlock();
|
|
126
|
-
return promise;
|
|
94
|
+
start(controller: ReadableStreamDefaultController<AudioFrame>) {
|
|
95
|
+
this.controller = controller;
|
|
127
96
|
}
|
|
128
97
|
|
|
129
|
-
|
|
130
|
-
|
|
98
|
+
cancel() {
|
|
99
|
+
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
131
100
|
this.ffiHandle.dispose();
|
|
132
101
|
}
|
|
102
|
+
}
|
|
133
103
|
|
|
134
|
-
|
|
135
|
-
|
|
104
|
+
export class AudioStream extends ReadableStream<AudioFrame> {
|
|
105
|
+
constructor(track: Track);
|
|
106
|
+
constructor(track: Track, sampleRate: number);
|
|
107
|
+
constructor(track: Track, sampleRate: number, numChannels: number);
|
|
108
|
+
constructor(track: Track, options: AudioStreamOptions);
|
|
109
|
+
constructor(
|
|
110
|
+
track: Track,
|
|
111
|
+
sampleRateOrOptions?: number | AudioStreamOptions,
|
|
112
|
+
numChannels?: number,
|
|
113
|
+
) {
|
|
114
|
+
super(new AudioStreamSource(track, sampleRateOrOptions, numChannels));
|
|
136
115
|
}
|
|
137
116
|
}
|
package/src/room.ts
CHANGED
|
@@ -148,6 +148,8 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
148
148
|
return ev.message.case == 'connect' && ev.message.value.asyncId == res.asyncId;
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
log.debug('Connect callback received');
|
|
152
|
+
|
|
151
153
|
switch (cb.message.case) {
|
|
152
154
|
case 'result':
|
|
153
155
|
this.ffiHandle = new FfiHandle(cb.message.value.room!.handle!.id!);
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.13.
|
|
1
|
+
export const SDK_VERSION = "0.13.13";
|
package/src/video_stream.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import {
|
|
4
|
+
import { ReadableStream, type UnderlyingSource } from 'node:stream/web';
|
|
5
5
|
import type { FfiEvent } from './ffi_client.js';
|
|
6
6
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
7
|
-
import type {
|
|
8
|
-
NewVideoStreamResponse,
|
|
9
|
-
VideoRotation,
|
|
10
|
-
VideoStreamInfo,
|
|
11
|
-
} from './proto/video_frame_pb.js';
|
|
7
|
+
import type { NewVideoStreamResponse, VideoRotation } from './proto/video_frame_pb.js';
|
|
12
8
|
import { NewVideoStreamRequest, VideoStreamType } from './proto/video_frame_pb.js';
|
|
13
9
|
import type { Track } from './track.js';
|
|
14
10
|
import { VideoFrame } from './video_frame.js';
|
|
@@ -19,23 +15,11 @@ export type VideoFrameEvent = {
|
|
|
19
15
|
rotation: VideoRotation;
|
|
20
16
|
};
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/** @internal */
|
|
26
|
-
ffiHandle: FfiHandle;
|
|
27
|
-
/** @internal */
|
|
28
|
-
eventQueue: (VideoFrameEvent | null)[] = [];
|
|
29
|
-
/** @internal */
|
|
30
|
-
queueResolve: ((value: IteratorResult<VideoFrameEvent>) => void) | null = null;
|
|
31
|
-
/** @internal */
|
|
32
|
-
mutex = new Mutex();
|
|
33
|
-
|
|
34
|
-
track: Track;
|
|
18
|
+
class VideoStreamSource implements UnderlyingSource<VideoFrameEvent> {
|
|
19
|
+
private controller?: ReadableStreamDefaultController<VideoFrameEvent>;
|
|
20
|
+
private ffiHandle: FfiHandle;
|
|
35
21
|
|
|
36
22
|
constructor(track: Track) {
|
|
37
|
-
this.track = track;
|
|
38
|
-
|
|
39
23
|
const req = new NewVideoStreamRequest({
|
|
40
24
|
type: VideoStreamType.VIDEO_STREAM_NATIVE,
|
|
41
25
|
trackHandle: track.ffi_handle.handle,
|
|
@@ -48,13 +32,15 @@ export class VideoStream implements AsyncIterableIterator<VideoFrameEvent> {
|
|
|
48
32
|
},
|
|
49
33
|
});
|
|
50
34
|
|
|
51
|
-
this.info = res.stream?.info;
|
|
52
35
|
this.ffiHandle = new FfiHandle(res.stream!.handle!.id!);
|
|
53
|
-
|
|
54
36
|
FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);
|
|
55
37
|
}
|
|
56
38
|
|
|
57
39
|
private onEvent = (ev: FfiEvent) => {
|
|
40
|
+
if (!this.controller) {
|
|
41
|
+
throw new Error('Stream controller not initialized');
|
|
42
|
+
}
|
|
43
|
+
|
|
58
44
|
if (
|
|
59
45
|
ev.message.case != 'videoStreamEvent' ||
|
|
60
46
|
ev.message.value.streamHandle != this.ffiHandle.handle
|
|
@@ -69,54 +55,32 @@ export class VideoStream implements AsyncIterableIterator<VideoFrameEvent> {
|
|
|
69
55
|
const timestampUs = streamEvent.value.timestampUs;
|
|
70
56
|
const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer!);
|
|
71
57
|
const value = { rotation, timestampUs, frame };
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
rotation: value.rotation!,
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
this.queueResolve = null;
|
|
82
|
-
} else {
|
|
83
|
-
this.eventQueue.push({
|
|
84
|
-
frame: value.frame,
|
|
85
|
-
timestampUs: value.timestampUs!,
|
|
86
|
-
rotation: value.rotation!,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
58
|
+
const videoFrameEvent = {
|
|
59
|
+
frame: value.frame,
|
|
60
|
+
timestampUs: value.timestampUs!,
|
|
61
|
+
rotation: value.rotation!,
|
|
62
|
+
};
|
|
63
|
+
this.controller.enqueue(videoFrameEvent);
|
|
89
64
|
break;
|
|
90
65
|
case 'eos':
|
|
91
66
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
67
|
+
this.controller.close();
|
|
92
68
|
break;
|
|
93
69
|
}
|
|
94
70
|
};
|
|
95
71
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (this.eventQueue.length > 0) {
|
|
99
|
-
unlock();
|
|
100
|
-
const value = this.eventQueue.shift();
|
|
101
|
-
if (value) {
|
|
102
|
-
return { done: false, value };
|
|
103
|
-
} else {
|
|
104
|
-
return { done: true, value: undefined };
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
const promise = new Promise<IteratorResult<VideoFrameEvent>>(
|
|
108
|
-
(resolve) => (this.queueResolve = resolve),
|
|
109
|
-
);
|
|
110
|
-
unlock();
|
|
111
|
-
return promise;
|
|
72
|
+
start(controller: ReadableStreamDefaultController<VideoFrameEvent>) {
|
|
73
|
+
this.controller = controller;
|
|
112
74
|
}
|
|
113
75
|
|
|
114
|
-
|
|
115
|
-
|
|
76
|
+
cancel() {
|
|
77
|
+
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
116
78
|
this.ffiHandle.dispose();
|
|
117
79
|
}
|
|
80
|
+
}
|
|
118
81
|
|
|
119
|
-
|
|
120
|
-
|
|
82
|
+
export class VideoStream extends ReadableStream<VideoFrameEvent> {
|
|
83
|
+
constructor(track: Track) {
|
|
84
|
+
super(new VideoStreamSource(track));
|
|
121
85
|
}
|
|
122
86
|
}
|