@livekit/rtc-node 0.8.0 → 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 +1 -3
- package/dist/room.js.map +1 -1
- package/dist/track_publication.d.ts +5 -0
- package/dist/track_publication.d.ts.map +1 -1
- package/dist/track_publication.js +14 -0
- package/dist/track_publication.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 +2 -1
- package/src/track_publication.ts +18 -0
- package/src/utils.ts +36 -0
- package/src/video_stream.ts +37 -13
|
@@ -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
|
*
|
package/src/proto/ffi_pb.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { Message, proto3, protoInt64 } from "@bufbuild/protobuf";
|
|
|
22
22
|
import { ConnectCallback, ConnectRequest, ConnectResponse, DisconnectCallback, DisconnectRequest, DisconnectResponse, GetSessionStatsCallback, GetSessionStatsRequest, GetSessionStatsResponse, PublishDataCallback, PublishDataRequest, PublishDataResponse, PublishSipDtmfCallback, PublishSipDtmfRequest, PublishSipDtmfResponse, PublishTrackCallback, PublishTrackRequest, PublishTrackResponse, PublishTranscriptionCallback, PublishTranscriptionRequest, PublishTranscriptionResponse, RoomEvent, SetLocalAttributesCallback, SetLocalAttributesRequest, SetLocalAttributesResponse, SetLocalMetadataCallback, SetLocalMetadataRequest, SetLocalMetadataResponse, SetLocalNameCallback, SetLocalNameRequest, SetLocalNameResponse, SetSubscribedRequest, SetSubscribedResponse, UnpublishTrackCallback, UnpublishTrackRequest, UnpublishTrackResponse } from "./room_pb.js";
|
|
23
23
|
import { CreateAudioTrackRequest, CreateAudioTrackResponse, CreateVideoTrackRequest, CreateVideoTrackResponse, EnableRemoteTrackRequest, EnableRemoteTrackResponse, GetStatsCallback, GetStatsRequest, GetStatsResponse, LocalTrackMuteRequest, LocalTrackMuteResponse, TrackEvent } from "./track_pb.js";
|
|
24
24
|
import { CaptureVideoFrameRequest, CaptureVideoFrameResponse, NewVideoSourceRequest, NewVideoSourceResponse, NewVideoStreamRequest, NewVideoStreamResponse, VideoConvertRequest, VideoConvertResponse, VideoStreamEvent, VideoStreamFromParticipantRequest, VideoStreamFromParticipantResponse } from "./video_frame_pb.js";
|
|
25
|
-
import { AudioStreamEvent, AudioStreamFromParticipantRequest, AudioStreamFromParticipantResponse, CaptureAudioFrameCallback, CaptureAudioFrameRequest, CaptureAudioFrameResponse, NewAudioResamplerRequest, NewAudioResamplerResponse, NewAudioSourceRequest, NewAudioSourceResponse, NewAudioStreamRequest, NewAudioStreamResponse, RemixAndResampleRequest, RemixAndResampleResponse } from "./audio_frame_pb.js";
|
|
25
|
+
import { AudioStreamEvent, AudioStreamFromParticipantRequest, AudioStreamFromParticipantResponse, CaptureAudioFrameCallback, CaptureAudioFrameRequest, CaptureAudioFrameResponse, ClearAudioBufferRequest, ClearAudioBufferResponse, NewAudioResamplerRequest, NewAudioResamplerResponse, NewAudioSourceRequest, NewAudioSourceResponse, NewAudioStreamRequest, NewAudioStreamResponse, RemixAndResampleRequest, RemixAndResampleResponse } from "./audio_frame_pb.js";
|
|
26
26
|
import { E2eeRequest, E2eeResponse } from "./e2ee_pb.js";
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -239,25 +239,31 @@ export class FfiRequest extends Message<FfiRequest> {
|
|
|
239
239
|
case: "captureAudioFrame";
|
|
240
240
|
} | {
|
|
241
241
|
/**
|
|
242
|
-
* @generated from field: livekit.proto.
|
|
242
|
+
* @generated from field: livekit.proto.ClearAudioBufferRequest clear_audio_buffer = 28;
|
|
243
|
+
*/
|
|
244
|
+
value: ClearAudioBufferRequest;
|
|
245
|
+
case: "clearAudioBuffer";
|
|
246
|
+
} | {
|
|
247
|
+
/**
|
|
248
|
+
* @generated from field: livekit.proto.NewAudioResamplerRequest new_audio_resampler = 29;
|
|
243
249
|
*/
|
|
244
250
|
value: NewAudioResamplerRequest;
|
|
245
251
|
case: "newAudioResampler";
|
|
246
252
|
} | {
|
|
247
253
|
/**
|
|
248
|
-
* @generated from field: livekit.proto.RemixAndResampleRequest remix_and_resample =
|
|
254
|
+
* @generated from field: livekit.proto.RemixAndResampleRequest remix_and_resample = 30;
|
|
249
255
|
*/
|
|
250
256
|
value: RemixAndResampleRequest;
|
|
251
257
|
case: "remixAndResample";
|
|
252
258
|
} | {
|
|
253
259
|
/**
|
|
254
|
-
* @generated from field: livekit.proto.E2eeRequest e2ee =
|
|
260
|
+
* @generated from field: livekit.proto.E2eeRequest e2ee = 31;
|
|
255
261
|
*/
|
|
256
262
|
value: E2eeRequest;
|
|
257
263
|
case: "e2ee";
|
|
258
264
|
} | {
|
|
259
265
|
/**
|
|
260
|
-
* @generated from field: livekit.proto.AudioStreamFromParticipantRequest audio_stream_from_participant =
|
|
266
|
+
* @generated from field: livekit.proto.AudioStreamFromParticipantRequest audio_stream_from_participant = 32;
|
|
261
267
|
*/
|
|
262
268
|
value: AudioStreamFromParticipantRequest;
|
|
263
269
|
case: "audioStreamFromParticipant";
|
|
@@ -297,10 +303,11 @@ export class FfiRequest extends Message<FfiRequest> {
|
|
|
297
303
|
{ no: 25, name: "new_audio_stream", kind: "message", T: NewAudioStreamRequest, oneof: "message" },
|
|
298
304
|
{ no: 26, name: "new_audio_source", kind: "message", T: NewAudioSourceRequest, oneof: "message" },
|
|
299
305
|
{ no: 27, name: "capture_audio_frame", kind: "message", T: CaptureAudioFrameRequest, oneof: "message" },
|
|
300
|
-
{ no: 28, name: "
|
|
301
|
-
{ no: 29, name: "
|
|
302
|
-
{ no: 30, name: "
|
|
303
|
-
{ no: 31, name: "
|
|
306
|
+
{ no: 28, name: "clear_audio_buffer", kind: "message", T: ClearAudioBufferRequest, oneof: "message" },
|
|
307
|
+
{ no: 29, name: "new_audio_resampler", kind: "message", T: NewAudioResamplerRequest, oneof: "message" },
|
|
308
|
+
{ no: 30, name: "remix_and_resample", kind: "message", T: RemixAndResampleRequest, oneof: "message" },
|
|
309
|
+
{ no: 31, name: "e2ee", kind: "message", T: E2eeRequest, oneof: "message" },
|
|
310
|
+
{ no: 32, name: "audio_stream_from_participant", kind: "message", T: AudioStreamFromParticipantRequest, oneof: "message" },
|
|
304
311
|
]);
|
|
305
312
|
|
|
306
313
|
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FfiRequest {
|
|
@@ -495,25 +502,31 @@ export class FfiResponse extends Message<FfiResponse> {
|
|
|
495
502
|
case: "captureAudioFrame";
|
|
496
503
|
} | {
|
|
497
504
|
/**
|
|
498
|
-
* @generated from field: livekit.proto.
|
|
505
|
+
* @generated from field: livekit.proto.ClearAudioBufferResponse clear_audio_buffer = 28;
|
|
506
|
+
*/
|
|
507
|
+
value: ClearAudioBufferResponse;
|
|
508
|
+
case: "clearAudioBuffer";
|
|
509
|
+
} | {
|
|
510
|
+
/**
|
|
511
|
+
* @generated from field: livekit.proto.NewAudioResamplerResponse new_audio_resampler = 29;
|
|
499
512
|
*/
|
|
500
513
|
value: NewAudioResamplerResponse;
|
|
501
514
|
case: "newAudioResampler";
|
|
502
515
|
} | {
|
|
503
516
|
/**
|
|
504
|
-
* @generated from field: livekit.proto.RemixAndResampleResponse remix_and_resample =
|
|
517
|
+
* @generated from field: livekit.proto.RemixAndResampleResponse remix_and_resample = 30;
|
|
505
518
|
*/
|
|
506
519
|
value: RemixAndResampleResponse;
|
|
507
520
|
case: "remixAndResample";
|
|
508
521
|
} | {
|
|
509
522
|
/**
|
|
510
|
-
* @generated from field: livekit.proto.AudioStreamFromParticipantResponse audio_stream_from_participant =
|
|
523
|
+
* @generated from field: livekit.proto.AudioStreamFromParticipantResponse audio_stream_from_participant = 31;
|
|
511
524
|
*/
|
|
512
525
|
value: AudioStreamFromParticipantResponse;
|
|
513
526
|
case: "audioStreamFromParticipant";
|
|
514
527
|
} | {
|
|
515
528
|
/**
|
|
516
|
-
* @generated from field: livekit.proto.E2eeResponse e2ee =
|
|
529
|
+
* @generated from field: livekit.proto.E2eeResponse e2ee = 32;
|
|
517
530
|
*/
|
|
518
531
|
value: E2eeResponse;
|
|
519
532
|
case: "e2ee";
|
|
@@ -553,10 +566,11 @@ export class FfiResponse extends Message<FfiResponse> {
|
|
|
553
566
|
{ no: 25, name: "new_audio_stream", kind: "message", T: NewAudioStreamResponse, oneof: "message" },
|
|
554
567
|
{ no: 26, name: "new_audio_source", kind: "message", T: NewAudioSourceResponse, oneof: "message" },
|
|
555
568
|
{ no: 27, name: "capture_audio_frame", kind: "message", T: CaptureAudioFrameResponse, oneof: "message" },
|
|
556
|
-
{ no: 28, name: "
|
|
557
|
-
{ no: 29, name: "
|
|
558
|
-
{ no: 30, name: "
|
|
559
|
-
{ no: 31, name: "
|
|
569
|
+
{ no: 28, name: "clear_audio_buffer", kind: "message", T: ClearAudioBufferResponse, oneof: "message" },
|
|
570
|
+
{ no: 29, name: "new_audio_resampler", kind: "message", T: NewAudioResamplerResponse, oneof: "message" },
|
|
571
|
+
{ no: 30, name: "remix_and_resample", kind: "message", T: RemixAndResampleResponse, oneof: "message" },
|
|
572
|
+
{ no: 31, name: "audio_stream_from_participant", kind: "message", T: AudioStreamFromParticipantResponse, oneof: "message" },
|
|
573
|
+
{ no: 32, name: "e2ee", kind: "message", T: E2eeResponse, oneof: "message" },
|
|
560
574
|
]);
|
|
561
575
|
|
|
562
576
|
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FfiResponse {
|
package/src/room.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
|
|
4
5
|
import EventEmitter from 'events';
|
|
5
|
-
import type TypedEmitter from 'typed-emitter';
|
|
6
6
|
import type { E2EEOptions } from './e2ee.js';
|
|
7
7
|
import { E2EEManager } from './e2ee.js';
|
|
8
8
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
@@ -188,6 +188,7 @@ export class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>
|
|
|
188
188
|
this.emit(RoomEvent.LocalTrackUnpublished, publication, this.localParticipant);
|
|
189
189
|
} else if (ev.case == 'localTrackSubscribed') {
|
|
190
190
|
const publication = this.localParticipant.trackPublications.get(ev.value.trackSid);
|
|
191
|
+
publication.resolveFirstSubscription();
|
|
191
192
|
this.emit(RoomEvent.LocalTrackSubscribed, publication.track);
|
|
192
193
|
} else if (ev.case == 'trackPublished') {
|
|
193
194
|
const participant = this.remoteParticipants.get(ev.value.participantIdentity);
|
package/src/track_publication.ts
CHANGED
|
@@ -69,8 +69,26 @@ export abstract class TrackPublication {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export class LocalTrackPublication extends TrackPublication {
|
|
72
|
+
private firstSubscription: Promise<void>;
|
|
73
|
+
private firstSubscriptionResolver: (() => void) | null = null;
|
|
74
|
+
|
|
72
75
|
constructor(ownedInfo: OwnedTrackPublication) {
|
|
73
76
|
super(ownedInfo);
|
|
77
|
+
this.firstSubscription = new Promise<void>((resolve) => {
|
|
78
|
+
this.firstSubscriptionResolver = resolve;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async waitForSubscription(): Promise<void> {
|
|
83
|
+
await this.firstSubscription;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @internal */
|
|
87
|
+
resolveFirstSubscription(): void {
|
|
88
|
+
if (this.firstSubscriptionResolver) {
|
|
89
|
+
this.firstSubscriptionResolver();
|
|
90
|
+
this.firstSubscriptionResolver = null;
|
|
91
|
+
}
|
|
74
92
|
}
|
|
75
93
|
}
|
|
76
94
|
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
export class Mutex {
|
|
5
|
+
#locking: Promise<void>;
|
|
6
|
+
#locks: number;
|
|
7
|
+
#limit: number;
|
|
8
|
+
|
|
9
|
+
constructor(limit = 1) {
|
|
10
|
+
this.#locking = Promise.resolve();
|
|
11
|
+
this.#locks = 0;
|
|
12
|
+
this.#limit = limit;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
isLocked(): boolean {
|
|
16
|
+
return this.#locks >= this.#limit;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async lock(): Promise<() => void> {
|
|
20
|
+
this.#locks += 1;
|
|
21
|
+
|
|
22
|
+
let unlockNext: () => void;
|
|
23
|
+
|
|
24
|
+
const willLock = new Promise<void>(
|
|
25
|
+
(resolve) =>
|
|
26
|
+
(unlockNext = () => {
|
|
27
|
+
this.#locks -= 1;
|
|
28
|
+
resolve();
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const willUnlock = this.#locking.then(() => unlockNext);
|
|
33
|
+
this.#locking = this.#locking.then(() => willLock);
|
|
34
|
+
return willUnlock;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/video_stream.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
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 type { FfiEvent } from './ffi_client.js';
|
|
7
5
|
import { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';
|
|
8
6
|
import type {
|
|
@@ -12,6 +10,7 @@ import type {
|
|
|
12
10
|
} from './proto/video_frame_pb.js';
|
|
13
11
|
import { NewVideoStreamRequest, VideoStreamType } from './proto/video_frame_pb.js';
|
|
14
12
|
import type { Track } from './track.js';
|
|
13
|
+
import { Mutex } from './utils.js';
|
|
15
14
|
import { VideoFrame } from './video_frame.js';
|
|
16
15
|
|
|
17
16
|
export type VideoFrameEvent = {
|
|
@@ -20,24 +19,21 @@ export type VideoFrameEvent = {
|
|
|
20
19
|
rotation: VideoRotation;
|
|
21
20
|
};
|
|
22
21
|
|
|
23
|
-
export
|
|
24
|
-
frameReceived: (evt: VideoFrameEvent) => void;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export enum VideoStreamEvent {
|
|
28
|
-
FrameReceived = 'frameReceived',
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class VideoStream extends (EventEmitter as new () => TypedEmitter<VideoStreamCallbacks>) {
|
|
22
|
+
export class VideoStream implements AsyncIterableIterator<VideoFrameEvent> {
|
|
32
23
|
/** @internal */
|
|
33
24
|
info: VideoStreamInfo;
|
|
34
25
|
/** @internal */
|
|
35
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();
|
|
36
33
|
|
|
37
34
|
track: Track;
|
|
38
35
|
|
|
39
36
|
constructor(track: Track) {
|
|
40
|
-
super();
|
|
41
37
|
this.track = track;
|
|
42
38
|
|
|
43
39
|
const req = new NewVideoStreamRequest({
|
|
@@ -72,7 +68,12 @@ export class VideoStream extends (EventEmitter as new () => TypedEmitter<VideoSt
|
|
|
72
68
|
const rotation = streamEvent.value.rotation;
|
|
73
69
|
const timestampUs = streamEvent.value.timestampUs;
|
|
74
70
|
const frame = VideoFrame.fromOwnedInfo(streamEvent.value.buffer);
|
|
75
|
-
|
|
71
|
+
const value = { rotation, timestampUs, frame };
|
|
72
|
+
if (this.queueResolve) {
|
|
73
|
+
this.queueResolve({ done: false, value });
|
|
74
|
+
} else {
|
|
75
|
+
this.eventQueue.push(value);
|
|
76
|
+
}
|
|
76
77
|
break;
|
|
77
78
|
case 'eos':
|
|
78
79
|
FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onEvent);
|
|
@@ -80,7 +81,30 @@ export class VideoStream extends (EventEmitter as new () => TypedEmitter<VideoSt
|
|
|
80
81
|
}
|
|
81
82
|
};
|
|
82
83
|
|
|
84
|
+
async next(): Promise<IteratorResult<VideoFrameEvent>> {
|
|
85
|
+
const unlock = await this.mutex.lock();
|
|
86
|
+
if (this.eventQueue.length > 0) {
|
|
87
|
+
unlock();
|
|
88
|
+
const value = this.eventQueue.shift();
|
|
89
|
+
if (value) {
|
|
90
|
+
return { done: false, value };
|
|
91
|
+
} else {
|
|
92
|
+
return { done: true, value: undefined };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const promise = new Promise<IteratorResult<VideoFrameEvent>>(
|
|
96
|
+
(resolve) => (this.queueResolve = resolve),
|
|
97
|
+
);
|
|
98
|
+
unlock();
|
|
99
|
+
return promise;
|
|
100
|
+
}
|
|
101
|
+
|
|
83
102
|
close() {
|
|
103
|
+
this.eventQueue.push(null);
|
|
84
104
|
this.ffiHandle.dispose();
|
|
85
105
|
}
|
|
106
|
+
|
|
107
|
+
[Symbol.asyncIterator](): VideoStream {
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
86
110
|
}
|