@livekit/rtc-node 0.8.1 → 0.9.0
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_source.d.ts +16 -1
- package/dist/audio_source.d.ts.map +1 -1
- package/dist/audio_source.js +49 -3
- package/dist/audio_source.js.map +1 -1
- package/dist/audio_stream.d.ts +10 -13
- package/dist/audio_stream.d.ts.map +1 -1
- package/dist/audio_stream.js +34 -8
- package/dist/audio_stream.js.map +1 -1
- package/dist/ffi_client.d.ts +1 -1
- package/dist/ffi_client.d.ts.map +1 -1
- package/dist/ffi_client.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/napi/native.d.ts +5 -8
- package/dist/participant.d.ts +2 -1
- package/dist/participant.d.ts.map +1 -1
- package/dist/participant.js +3 -0
- package/dist/participant.js.map +1 -1
- package/dist/proto/audio_frame_pb.d.ts +32 -2
- package/dist/proto/audio_frame_pb.d.ts.map +1 -1
- package/dist/proto/audio_frame_pb.js +59 -1
- package/dist/proto/audio_frame_pb.js.map +1 -1
- package/dist/proto/ffi_pb.d.ts +21 -9
- package/dist/proto/ffi_pb.d.ts.map +1 -1
- package/dist/proto/ffi_pb.js +11 -9
- package/dist/proto/ffi_pb.js.map +1 -1
- package/dist/room.d.ts +1 -1
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +0 -3
- package/dist/room.js.map +1 -1
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +41 -0
- package/dist/utils.js.map +1 -0
- package/dist/video_stream.d.ts +10 -10
- package/dist/video_stream.d.ts.map +1 -1
- package/dist/video_stream.js +35 -11
- package/dist/video_stream.js.map +1 -1
- package/package.json +7 -7
- package/src/audio_source.ts +69 -2
- package/src/audio_stream.ts +36 -17
- package/src/ffi_client.ts +1 -1
- package/src/index.ts +3 -2
- package/src/napi/native.d.ts +5 -8
- package/src/participant.ts +5 -1
- package/src/proto/audio_frame_pb.ts +71 -3
- package/src/proto/ffi_pb.ts +31 -17
- package/src/room.ts +1 -1
- package/src/utils.ts +36 -0
- package/src/video_stream.ts +37 -13
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,qBAAa,KAAK;;gBAKJ,KAAK,SAAI;IAMrB,QAAQ,IAAI,OAAO;IAIb,IAAI,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC;CAiBlC"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _Mutex_locking, _Mutex_locks, _Mutex_limit;
|
|
13
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
16
|
+
export class Mutex {
|
|
17
|
+
constructor(limit = 1) {
|
|
18
|
+
_Mutex_locking.set(this, void 0);
|
|
19
|
+
_Mutex_locks.set(this, void 0);
|
|
20
|
+
_Mutex_limit.set(this, void 0);
|
|
21
|
+
__classPrivateFieldSet(this, _Mutex_locking, Promise.resolve(), "f");
|
|
22
|
+
__classPrivateFieldSet(this, _Mutex_locks, 0, "f");
|
|
23
|
+
__classPrivateFieldSet(this, _Mutex_limit, limit, "f");
|
|
24
|
+
}
|
|
25
|
+
isLocked() {
|
|
26
|
+
return __classPrivateFieldGet(this, _Mutex_locks, "f") >= __classPrivateFieldGet(this, _Mutex_limit, "f");
|
|
27
|
+
}
|
|
28
|
+
async lock() {
|
|
29
|
+
__classPrivateFieldSet(this, _Mutex_locks, __classPrivateFieldGet(this, _Mutex_locks, "f") + 1, "f");
|
|
30
|
+
let unlockNext;
|
|
31
|
+
const willLock = new Promise((resolve) => (unlockNext = () => {
|
|
32
|
+
__classPrivateFieldSet(this, _Mutex_locks, __classPrivateFieldGet(this, _Mutex_locks, "f") - 1, "f");
|
|
33
|
+
resolve();
|
|
34
|
+
}));
|
|
35
|
+
const willUnlock = __classPrivateFieldGet(this, _Mutex_locking, "f").then(() => unlockNext);
|
|
36
|
+
__classPrivateFieldSet(this, _Mutex_locking, __classPrivateFieldGet(this, _Mutex_locking, "f").then(() => willLock), "f");
|
|
37
|
+
return willUnlock;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
_Mutex_locking = new WeakMap(), _Mutex_locks = new WeakMap(), _Mutex_limit = new WeakMap();
|
|
41
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,MAAM,OAAO,KAAK;IAKhB,YAAY,KAAK,GAAG,CAAC;QAJrB,iCAAwB;QACxB,+BAAe;QACf,+BAAe;QAGb,uBAAA,IAAI,kBAAY,OAAO,CAAC,OAAO,EAAE,MAAA,CAAC;QAClC,uBAAA,IAAI,gBAAU,CAAC,MAAA,CAAC;QAChB,uBAAA,IAAI,gBAAU,KAAK,MAAA,CAAC;IACtB,CAAC;IAED,QAAQ;QACN,OAAO,uBAAA,IAAI,oBAAO,IAAI,uBAAA,IAAI,oBAAO,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,6FAAe,CAAC,MAAA,CAAC;QAEjB,IAAI,UAAsB,CAAC;QAE3B,MAAM,QAAQ,GAAG,IAAI,OAAO,CAC1B,CAAC,OAAO,EAAE,EAAE,CACV,CAAC,UAAU,GAAG,GAAG,EAAE;YACjB,6FAAe,CAAC,MAAA,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CACL,CAAC;QAEF,MAAM,UAAU,GAAG,uBAAA,IAAI,sBAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC;QACxD,uBAAA,IAAI,kBAAY,uBAAA,IAAI,sBAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAA,CAAC;QACnD,OAAO,UAAU,CAAC;IACpB,CAAC;CACF"}
|
package/dist/video_stream.d.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import type TypedEmitter from 'typed-emitter';
|
|
2
1
|
import { FfiHandle } from './ffi_client.js';
|
|
3
2
|
import type { VideoRotation, VideoStreamInfo } from './proto/video_frame_pb.js';
|
|
4
3
|
import type { Track } from './track.js';
|
|
4
|
+
import { Mutex } from './utils.js';
|
|
5
5
|
import { VideoFrame } from './video_frame.js';
|
|
6
6
|
export type VideoFrameEvent = {
|
|
7
7
|
frame: VideoFrame;
|
|
8
8
|
timestampUs: bigint;
|
|
9
9
|
rotation: VideoRotation;
|
|
10
10
|
};
|
|
11
|
-
export
|
|
12
|
-
frameReceived: (evt: VideoFrameEvent) => void;
|
|
13
|
-
};
|
|
14
|
-
export declare enum VideoStreamEvent {
|
|
15
|
-
FrameReceived = "frameReceived"
|
|
16
|
-
}
|
|
17
|
-
declare const VideoStream_base: new () => TypedEmitter<VideoStreamCallbacks>;
|
|
18
|
-
export declare class VideoStream extends VideoStream_base {
|
|
11
|
+
export declare class VideoStream implements AsyncIterableIterator<VideoFrameEvent> {
|
|
19
12
|
/** @internal */
|
|
20
13
|
info: VideoStreamInfo;
|
|
21
14
|
/** @internal */
|
|
22
15
|
ffiHandle: FfiHandle;
|
|
16
|
+
/** @internal */
|
|
17
|
+
eventQueue: (VideoFrameEvent | null)[];
|
|
18
|
+
/** @internal */
|
|
19
|
+
queueResolve: ((value: IteratorResult<VideoFrameEvent>) => void) | null;
|
|
20
|
+
/** @internal */
|
|
21
|
+
mutex: Mutex;
|
|
23
22
|
track: Track;
|
|
24
23
|
constructor(track: Track);
|
|
25
24
|
private onEvent;
|
|
25
|
+
next(): Promise<IteratorResult<VideoFrameEvent>>;
|
|
26
26
|
close(): void;
|
|
27
|
+
[Symbol.asyncIterator](): VideoStream;
|
|
27
28
|
}
|
|
28
|
-
export {};
|
|
29
29
|
//# sourceMappingURL=video_stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video_stream.d.ts","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"video_stream.d.ts","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"AAIA,OAAO,EAA6B,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAEV,aAAa,EACb,eAAe,EAChB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,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;AAEF,qBAAa,WAAY,YAAW,qBAAqB,CAAC,eAAe,CAAC;IACxE,gBAAgB;IAChB,IAAI,EAAE,eAAe,CAAC;IACtB,gBAAgB;IAChB,SAAS,EAAE,SAAS,CAAC;IACrB,gBAAgB;IAChB,UAAU,EAAE,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,CAAM;IAC5C,gBAAgB;IAChB,YAAY,EAAE,CAAC,CAAC,KAAK,EAAE,cAAc,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAC/E,gBAAgB;IAChB,KAAK,QAAe;IAEpB,KAAK,EAAE,KAAK,CAAC;gBAED,KAAK,EAAE,KAAK;IAqBxB,OAAO,CAAC,OAAO,CAyBb;IAEI,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;IAkBtD,KAAK;IAKL,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,WAAW;CAGtC"}
|
package/dist/video_stream.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
-
//
|
|
3
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import EventEmitter from 'events';
|
|
5
1
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
6
2
|
import { NewVideoStreamRequest, VideoStreamType } from './proto/video_frame_pb.js';
|
|
3
|
+
import { Mutex } from './utils.js';
|
|
7
4
|
import { VideoFrame } from './video_frame.js';
|
|
8
|
-
export
|
|
9
|
-
(function (VideoStreamEvent) {
|
|
10
|
-
VideoStreamEvent["FrameReceived"] = "frameReceived";
|
|
11
|
-
})(VideoStreamEvent || (VideoStreamEvent = {}));
|
|
12
|
-
export class VideoStream extends EventEmitter {
|
|
5
|
+
export class VideoStream {
|
|
13
6
|
constructor(track) {
|
|
14
|
-
|
|
7
|
+
/** @internal */
|
|
8
|
+
this.eventQueue = [];
|
|
9
|
+
/** @internal */
|
|
10
|
+
this.queueResolve = null;
|
|
11
|
+
/** @internal */
|
|
12
|
+
this.mutex = new Mutex();
|
|
15
13
|
this.onEvent = (ev) => {
|
|
16
14
|
if (ev.message.case != 'videoStreamEvent' ||
|
|
17
15
|
ev.message.value.streamHandle != this.ffiHandle.handle) {
|
|
@@ -23,7 +21,13 @@ export class VideoStream extends EventEmitter {
|
|
|
23
21
|
const rotation = streamEvent.value.rotation;
|
|
24
22
|
const timestampUs = streamEvent.value.timestampUs;
|
|
25
23
|
const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer);
|
|
26
|
-
|
|
24
|
+
const value = { rotation, timestampUs, frame };
|
|
25
|
+
if (this.queueResolve) {
|
|
26
|
+
this.queueResolve({ done: false, value });
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.eventQueue.push(value);
|
|
30
|
+
}
|
|
27
31
|
break;
|
|
28
32
|
case 'eos':
|
|
29
33
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
@@ -45,8 +49,28 @@ export class VideoStream extends EventEmitter {
|
|
|
45
49
|
this.ffiHandle = new FfiHandle(res.stream.handle.id);
|
|
46
50
|
FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onEvent);
|
|
47
51
|
}
|
|
52
|
+
async next() {
|
|
53
|
+
const unlock = await this.mutex.lock();
|
|
54
|
+
if (this.eventQueue.length > 0) {
|
|
55
|
+
unlock();
|
|
56
|
+
const value = this.eventQueue.shift();
|
|
57
|
+
if (value) {
|
|
58
|
+
return { done: false, value };
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return { done: true, value: undefined };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const promise = new Promise((resolve) => (this.queueResolve = resolve));
|
|
65
|
+
unlock();
|
|
66
|
+
return promise;
|
|
67
|
+
}
|
|
48
68
|
close() {
|
|
69
|
+
this.eventQueue.push(null);
|
|
49
70
|
this.ffiHandle.dispose();
|
|
50
71
|
}
|
|
72
|
+
[Symbol.asyncIterator]() {
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
51
75
|
}
|
|
52
76
|
//# sourceMappingURL=video_stream.js.map
|
package/dist/video_stream.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video_stream.js","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"video_stream.js","sourceRoot":"","sources":["../src/video_stream.ts"],"names":[],"mappings":"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"}
|
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.
|
|
6
|
+
"version": "0.9.0",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@bufbuild/protobuf": "^1.4.2",
|
|
37
|
-
"typed-emitter": "^
|
|
37
|
+
"@livekit/typed-emitter": "^3.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@napi-rs/cli": "^2.18.0",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"prettier": "^3.0.3"
|
|
44
44
|
},
|
|
45
45
|
"optionalDependencies": {
|
|
46
|
-
"@livekit/rtc-node-darwin-arm64": "0.
|
|
47
|
-
"@livekit/rtc-node-darwin-x64": "0.
|
|
48
|
-
"@livekit/rtc-node-linux-arm64-gnu": "0.
|
|
49
|
-
"@livekit/rtc-node-linux-x64-gnu": "0.
|
|
50
|
-
"@livekit/rtc-node-win32-x64-msvc": "0.
|
|
46
|
+
"@livekit/rtc-node-darwin-arm64": "0.9.0",
|
|
47
|
+
"@livekit/rtc-node-darwin-x64": "0.9.0",
|
|
48
|
+
"@livekit/rtc-node-linux-arm64-gnu": "0.9.0",
|
|
49
|
+
"@livekit/rtc-node-linux-x64-gnu": "0.9.0",
|
|
50
|
+
"@livekit/rtc-node-win32-x64-msvc": "0.9.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">= 18"
|
package/src/audio_source.ts
CHANGED
|
@@ -8,11 +8,13 @@ import type {
|
|
|
8
8
|
AudioSourceInfo,
|
|
9
9
|
CaptureAudioFrameCallback,
|
|
10
10
|
CaptureAudioFrameResponse,
|
|
11
|
+
ClearAudioBufferResponse,
|
|
11
12
|
NewAudioSourceResponse,
|
|
12
13
|
} from './proto/audio_frame_pb.js';
|
|
13
14
|
import {
|
|
14
15
|
AudioSourceType,
|
|
15
16
|
CaptureAudioFrameRequest,
|
|
17
|
+
ClearAudioBufferRequest,
|
|
16
18
|
NewAudioSourceRequest,
|
|
17
19
|
} from './proto/audio_frame_pb.js';
|
|
18
20
|
|
|
@@ -21,19 +23,33 @@ export class AudioSource {
|
|
|
21
23
|
info: AudioSourceInfo;
|
|
22
24
|
/** @internal */
|
|
23
25
|
ffiHandle: FfiHandle;
|
|
26
|
+
/** @internal */
|
|
27
|
+
lastCapture: number;
|
|
28
|
+
/** @internal */
|
|
29
|
+
currentQueueSize: number;
|
|
30
|
+
/** @internal */
|
|
31
|
+
release = () => {};
|
|
32
|
+
promise = this.newPromise();
|
|
33
|
+
/** @internal */
|
|
34
|
+
timeout?: ReturnType<typeof setTimeout> = undefined;
|
|
24
35
|
|
|
25
36
|
sampleRate: number;
|
|
26
37
|
numChannels: number;
|
|
38
|
+
queueSize: number;
|
|
27
39
|
|
|
28
|
-
constructor(sampleRate: number, numChannels: number,
|
|
40
|
+
constructor(sampleRate: number, numChannels: number, queueSize = 1000) {
|
|
29
41
|
this.sampleRate = sampleRate;
|
|
30
42
|
this.numChannels = numChannels;
|
|
43
|
+
this.queueSize = queueSize;
|
|
44
|
+
|
|
45
|
+
this.lastCapture = 0;
|
|
46
|
+
this.currentQueueSize = 0;
|
|
31
47
|
|
|
32
48
|
const req = new NewAudioSourceRequest({
|
|
33
49
|
type: AudioSourceType.AUDIO_SOURCE_NATIVE,
|
|
34
50
|
sampleRate: sampleRate,
|
|
35
51
|
numChannels: numChannels,
|
|
36
|
-
|
|
52
|
+
queueSizeMs: queueSize,
|
|
37
53
|
});
|
|
38
54
|
|
|
39
55
|
const res = FfiClient.instance.request<NewAudioSourceResponse>({
|
|
@@ -47,7 +63,58 @@ export class AudioSource {
|
|
|
47
63
|
this.ffiHandle = new FfiHandle(res.source.handle.id);
|
|
48
64
|
}
|
|
49
65
|
|
|
66
|
+
get queuedDuration(): number {
|
|
67
|
+
return Math.max(
|
|
68
|
+
this.currentQueueSize - Number(process.hrtime.bigint() / 1000000n) + this.lastCapture,
|
|
69
|
+
0,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
clearQueue() {
|
|
74
|
+
const req = new ClearAudioBufferRequest({
|
|
75
|
+
sourceHandle: this.ffiHandle.handle,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
FfiClient.instance.request<ClearAudioBufferResponse>({
|
|
79
|
+
message: {
|
|
80
|
+
case: 'clearAudioBuffer',
|
|
81
|
+
value: req,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
this.release();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** @internal */
|
|
89
|
+
async newPromise() {
|
|
90
|
+
return new Promise<void>((resolve) => {
|
|
91
|
+
this.release = resolve;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async waitForPlayout() {
|
|
96
|
+
return this.promise.then(() => {
|
|
97
|
+
this.lastCapture = 0;
|
|
98
|
+
this.currentQueueSize = 0;
|
|
99
|
+
this.promise = this.newPromise();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
50
103
|
async captureFrame(frame: AudioFrame) {
|
|
104
|
+
const now = Number(process.hrtime.bigint() / 1000000n);
|
|
105
|
+
const elapsed = this.lastCapture === 0 ? 0 : now - this.lastCapture;
|
|
106
|
+
this.currentQueueSize += (frame.samplesPerChannel / frame.sampleRate - elapsed) * 1000;
|
|
107
|
+
|
|
108
|
+
this.lastCapture = now;
|
|
109
|
+
|
|
110
|
+
if (this.timeout) {
|
|
111
|
+
clearTimeout(this.timeout);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// remove 50ms to account for processing time
|
|
115
|
+
// (e.g. using wait_for_playout for very small chunks)
|
|
116
|
+
this.timeout = setTimeout(this.release, this.currentQueueSize - 50);
|
|
117
|
+
|
|
51
118
|
const req = new CaptureAudioFrameRequest({
|
|
52
119
|
sourceHandle: this.ffiHandle.handle,
|
|
53
120
|
buffer: frame.protoInfo(),
|
package/src/audio_stream.ts
CHANGED
|
@@ -1,39 +1,31 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
import EventEmitter from 'events';
|
|
5
|
-
import type TypedEmitter from 'typed-emitter';
|
|
6
4
|
import { AudioFrame } from './audio_frame.js';
|
|
7
5
|
import type { FfiEvent } from './ffi_client.js';
|
|
8
6
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
9
7
|
import type { AudioStreamInfo, NewAudioStreamResponse } from './proto/audio_frame_pb.js';
|
|
10
8
|
import { AudioStreamType, NewAudioStreamRequest } from './proto/audio_frame_pb.js';
|
|
11
9
|
import type { Track } from './track.js';
|
|
10
|
+
import { Mutex } from './utils.js';
|
|
12
11
|
|
|
13
|
-
export
|
|
14
|
-
frame: AudioFrame;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type AudioStreamCallbacks = {
|
|
18
|
-
frameReceived: (frame: AudioFrameEvent) => void;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export enum AudioStreamEvent {
|
|
22
|
-
FrameReceived = 'frameReceived',
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class AudioStream extends (EventEmitter as new () => TypedEmitter<AudioStreamCallbacks>) {
|
|
12
|
+
export class AudioStream implements AsyncIterableIterator<AudioFrame> {
|
|
26
13
|
/** @internal */
|
|
27
14
|
info: AudioStreamInfo;
|
|
28
15
|
/** @internal */
|
|
29
16
|
ffiHandle: FfiHandle;
|
|
17
|
+
/** @internal */
|
|
18
|
+
eventQueue: (AudioFrame | null)[] = [];
|
|
19
|
+
/** @internal */
|
|
20
|
+
queueResolve: ((value: IteratorResult<AudioFrame>) => void) | null = null;
|
|
21
|
+
/** @internal */
|
|
22
|
+
mutex = new Mutex();
|
|
30
23
|
|
|
31
24
|
track: Track;
|
|
32
25
|
sampleRate: number;
|
|
33
26
|
numChannels: number;
|
|
34
27
|
|
|
35
28
|
constructor(track: Track, sampleRate: number = 48000, numChannels: number = 1) {
|
|
36
|
-
super();
|
|
37
29
|
this.track = track;
|
|
38
30
|
this.sampleRate = sampleRate;
|
|
39
31
|
this.numChannels = numChannels;
|
|
@@ -70,7 +62,11 @@ export class AudioStream extends (EventEmitter as new () => TypedEmitter<AudioSt
|
|
|
70
62
|
switch (streamEvent.case) {
|
|
71
63
|
case 'frameReceived':
|
|
72
64
|
const frame = AudioFrame.fromOwnedInfo(streamEvent.value.frame);
|
|
73
|
-
this.
|
|
65
|
+
if (this.queueResolve) {
|
|
66
|
+
this.queueResolve({ done: false, value: frame });
|
|
67
|
+
} else {
|
|
68
|
+
this.eventQueue.push(frame);
|
|
69
|
+
}
|
|
74
70
|
break;
|
|
75
71
|
case 'eos':
|
|
76
72
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
@@ -78,7 +74,30 @@ export class AudioStream extends (EventEmitter as new () => TypedEmitter<AudioSt
|
|
|
78
74
|
}
|
|
79
75
|
};
|
|
80
76
|
|
|
77
|
+
async next(): Promise<IteratorResult<AudioFrame>> {
|
|
78
|
+
const unlock = await this.mutex.lock();
|
|
79
|
+
if (this.eventQueue.length > 0) {
|
|
80
|
+
unlock();
|
|
81
|
+
const value = this.eventQueue.shift();
|
|
82
|
+
if (value) {
|
|
83
|
+
return { done: false, value };
|
|
84
|
+
} else {
|
|
85
|
+
return { done: true, value: undefined };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const promise = new Promise<IteratorResult<AudioFrame>>(
|
|
89
|
+
(resolve) => (this.queueResolve = resolve),
|
|
90
|
+
);
|
|
91
|
+
unlock();
|
|
92
|
+
return promise;
|
|
93
|
+
}
|
|
94
|
+
|
|
81
95
|
close() {
|
|
96
|
+
this.eventQueue.push(null);
|
|
82
97
|
this.ffiHandle.dispose();
|
|
83
98
|
}
|
|
99
|
+
|
|
100
|
+
[Symbol.asyncIterator](): AudioStream {
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
84
103
|
}
|
package/src/ffi_client.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import type { PartialMessage } from '@bufbuild/protobuf';
|
|
5
|
+
import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
|
|
5
6
|
import EventEmitter from 'events';
|
|
6
|
-
import type TypedEmitter from 'typed-emitter';
|
|
7
7
|
import {
|
|
8
8
|
FfiHandle,
|
|
9
9
|
livekitCopyBuffer,
|
package/src/index.ts
CHANGED
|
@@ -17,8 +17,8 @@ export {
|
|
|
17
17
|
} from './track.js';
|
|
18
18
|
export { VideoFrame } from './video_frame.js';
|
|
19
19
|
export { AudioFrame } from './audio_frame.js';
|
|
20
|
-
export { AudioStream
|
|
21
|
-
export { VideoStream,
|
|
20
|
+
export { AudioStream } from './audio_stream.js';
|
|
21
|
+
export { VideoStream, VideoFrameEvent } from './video_stream.js';
|
|
22
22
|
export { AudioSource } from './audio_source.js';
|
|
23
23
|
export { VideoSource } from './video_source.js';
|
|
24
24
|
export {
|
|
@@ -40,4 +40,5 @@ export {
|
|
|
40
40
|
export { EncryptionType, EncryptionState } from './proto/e2ee_pb.js';
|
|
41
41
|
export { StreamState, TrackKind, TrackSource } from './proto/track_pb.js';
|
|
42
42
|
export { VideoBufferType, VideoRotation } from './proto/video_frame_pb.js';
|
|
43
|
+
export { ParticipantKind } from './proto/participant_pb.js';
|
|
43
44
|
export { dispose } from './ffi_client.js';
|
package/src/napi/native.d.ts
CHANGED
|
@@ -3,14 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
):
|
|
10
|
-
export
|
|
11
|
-
export declare function livekitRetrievePtr(handle: Uint8Array): bigint;
|
|
12
|
-
export declare function livekitCopyBuffer(ptr: bigint, len: number): Uint8Array;
|
|
13
|
-
export declare function livekitDispose(): Promise<void>;
|
|
6
|
+
export function livekitInitialize(callback: (data: Uint8Array) => void, captureLogs: boolean): void;
|
|
7
|
+
export function livekitFfiRequest(data: Uint8Array): Uint8Array;
|
|
8
|
+
export function livekitRetrievePtr(handle: Uint8Array): bigint;
|
|
9
|
+
export function livekitCopyBuffer(ptr: bigint, len: number): Uint8Array;
|
|
10
|
+
export function livekitDispose(): Promise<void>;
|
|
14
11
|
export declare class FfiHandle {
|
|
15
12
|
constructor(handle: bigint);
|
|
16
13
|
dispose(): void;
|
package/src/participant.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
import { FfiClient, FfiHandle } from './ffi_client.js';
|
|
5
|
-
import type { OwnedParticipant, ParticipantInfo } from './proto/participant_pb.js';
|
|
5
|
+
import type { OwnedParticipant, ParticipantInfo, ParticipantKind } from './proto/participant_pb.js';
|
|
6
6
|
import type {
|
|
7
7
|
PublishDataCallback,
|
|
8
8
|
PublishDataResponse,
|
|
@@ -71,6 +71,10 @@ export abstract class Participant {
|
|
|
71
71
|
get attributes(): Record<string, string> {
|
|
72
72
|
return this.info.attributes;
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
get kind(): ParticipantKind {
|
|
76
|
+
return this.info.kind;
|
|
77
|
+
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
export type DataPublishOptions = {
|
|
@@ -276,9 +276,9 @@ export class NewAudioSourceRequest extends Message<NewAudioSourceRequest> {
|
|
|
276
276
|
numChannels = 0;
|
|
277
277
|
|
|
278
278
|
/**
|
|
279
|
-
* @generated from field:
|
|
279
|
+
* @generated from field: uint32 queue_size_ms = 5;
|
|
280
280
|
*/
|
|
281
|
-
|
|
281
|
+
queueSizeMs = 0;
|
|
282
282
|
|
|
283
283
|
constructor(data?: PartialMessage<NewAudioSourceRequest>) {
|
|
284
284
|
super();
|
|
@@ -292,7 +292,7 @@ export class NewAudioSourceRequest extends Message<NewAudioSourceRequest> {
|
|
|
292
292
|
{ no: 2, name: "options", kind: "message", T: AudioSourceOptions, opt: true },
|
|
293
293
|
{ no: 3, name: "sample_rate", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
294
294
|
{ no: 4, name: "num_channels", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
295
|
-
{ no: 5, name: "
|
|
295
|
+
{ no: 5, name: "queue_size_ms", kind: "scalar", T: 13 /* ScalarType.UINT32 */ },
|
|
296
296
|
]);
|
|
297
297
|
|
|
298
298
|
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): NewAudioSourceRequest {
|
|
@@ -475,6 +475,74 @@ export class CaptureAudioFrameCallback extends Message<CaptureAudioFrameCallback
|
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
+
/**
|
|
479
|
+
* @generated from message livekit.proto.ClearAudioBufferRequest
|
|
480
|
+
*/
|
|
481
|
+
export class ClearAudioBufferRequest extends Message<ClearAudioBufferRequest> {
|
|
482
|
+
/**
|
|
483
|
+
* @generated from field: uint64 source_handle = 1;
|
|
484
|
+
*/
|
|
485
|
+
sourceHandle = protoInt64.zero;
|
|
486
|
+
|
|
487
|
+
constructor(data?: PartialMessage<ClearAudioBufferRequest>) {
|
|
488
|
+
super();
|
|
489
|
+
proto3.util.initPartial(data, this);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
static readonly runtime: typeof proto3 = proto3;
|
|
493
|
+
static readonly typeName = "livekit.proto.ClearAudioBufferRequest";
|
|
494
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
495
|
+
{ no: 1, name: "source_handle", kind: "scalar", T: 4 /* ScalarType.UINT64 */ },
|
|
496
|
+
]);
|
|
497
|
+
|
|
498
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ClearAudioBufferRequest {
|
|
499
|
+
return new ClearAudioBufferRequest().fromBinary(bytes, options);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ClearAudioBufferRequest {
|
|
503
|
+
return new ClearAudioBufferRequest().fromJson(jsonValue, options);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ClearAudioBufferRequest {
|
|
507
|
+
return new ClearAudioBufferRequest().fromJsonString(jsonString, options);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
static equals(a: ClearAudioBufferRequest | PlainMessage<ClearAudioBufferRequest> | undefined, b: ClearAudioBufferRequest | PlainMessage<ClearAudioBufferRequest> | undefined): boolean {
|
|
511
|
+
return proto3.util.equals(ClearAudioBufferRequest, a, b);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* @generated from message livekit.proto.ClearAudioBufferResponse
|
|
517
|
+
*/
|
|
518
|
+
export class ClearAudioBufferResponse extends Message<ClearAudioBufferResponse> {
|
|
519
|
+
constructor(data?: PartialMessage<ClearAudioBufferResponse>) {
|
|
520
|
+
super();
|
|
521
|
+
proto3.util.initPartial(data, this);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
static readonly runtime: typeof proto3 = proto3;
|
|
525
|
+
static readonly typeName = "livekit.proto.ClearAudioBufferResponse";
|
|
526
|
+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
|
527
|
+
]);
|
|
528
|
+
|
|
529
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ClearAudioBufferResponse {
|
|
530
|
+
return new ClearAudioBufferResponse().fromBinary(bytes, options);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ClearAudioBufferResponse {
|
|
534
|
+
return new ClearAudioBufferResponse().fromJson(jsonValue, options);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ClearAudioBufferResponse {
|
|
538
|
+
return new ClearAudioBufferResponse().fromJsonString(jsonString, options);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
static equals(a: ClearAudioBufferResponse | PlainMessage<ClearAudioBufferResponse> | undefined, b: ClearAudioBufferResponse | PlainMessage<ClearAudioBufferResponse> | undefined): boolean {
|
|
542
|
+
return proto3.util.equals(ClearAudioBufferResponse, a, b);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
478
546
|
/**
|
|
479
547
|
* Create a new AudioResampler
|
|
480
548
|
*
|