livekit-client 2.1.1 → 2.1.3
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +215 -22
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +2 -1
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts +1 -0
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +5 -2
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +20 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +2 -1
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrack.d.ts +1 -0
- package/dist/src/room/track/RemoteTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +7 -0
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +3 -1
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +8 -0
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/ts4.2/src/index.d.ts +1 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +2 -1
- package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +1 -0
- package/dist/ts4.2/src/room/Room.d.ts +5 -2
- package/dist/ts4.2/src/room/events.d.ts +20 -1
- package/dist/ts4.2/src/room/participant/Participant.d.ts +2 -1
- package/dist/ts4.2/src/room/track/RemoteTrack.d.ts +1 -0
- package/dist/ts4.2/src/room/track/Track.d.ts +7 -0
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +3 -1
- package/dist/ts4.2/src/room/types.d.ts +8 -0
- package/dist/ts4.2/src/room/utils.d.ts +3 -1
- package/dist/ts4.2/src/version.d.ts +1 -1
- package/package.json +8 -8
- package/src/api/SignalClient.ts +3 -1
- package/src/index.ts +1 -1
- package/src/room/RTCEngine.ts +51 -10
- package/src/room/RegionUrlProvider.ts +5 -0
- package/src/room/Room.ts +29 -2
- package/src/room/events.ts +23 -0
- package/src/room/participant/Participant.ts +5 -1
- package/src/room/track/RemoteTrack.ts +16 -0
- package/src/room/track/Track.ts +9 -0
- package/src/room/track/TrackPublication.ts +3 -1
- package/src/room/types.ts +9 -0
- package/src/room/utils.ts +17 -2
- package/src/version.ts +1 -1
@@ -1,7 +1,7 @@
|
|
1
1
|
import { Encryption_Type } from '@livekit/protocol';
|
2
2
|
import type { SubscriptionError, TrackInfo, UpdateSubscription, UpdateTrackSettings } from '@livekit/protocol';
|
3
3
|
import type TypedEventEmitter from 'typed-emitter';
|
4
|
-
import type { LoggerOptions } from '../types';
|
4
|
+
import type { LoggerOptions, TranscriptionSegment } from '../types';
|
5
5
|
import LocalAudioTrack from './LocalAudioTrack';
|
6
6
|
import LocalVideoTrack from './LocalVideoTrack';
|
7
7
|
import RemoteAudioTrack from './RemoteAudioTrack';
|
@@ -72,6 +72,8 @@ export type PublicationEventCallbacks = {
|
|
72
72
|
unsubscribed: (track: RemoteTrack) => void;
|
73
73
|
subscriptionStatusChanged: (status: TrackPublication.SubscriptionStatus, prevStatus: TrackPublication.SubscriptionStatus) => void;
|
74
74
|
subscriptionFailed: (error: SubscriptionError) => void;
|
75
|
+
transcriptionReceived: (transcription: TranscriptionSegment[]) => void;
|
76
|
+
timeSyncUpdate: (timestamp: number) => void;
|
75
77
|
};
|
76
78
|
export {};
|
77
79
|
//# sourceMappingURL=TrackPublication.d.ts.map
|
@@ -35,4 +35,12 @@ export type LoggerOptions = {
|
|
35
35
|
loggerName?: string;
|
36
36
|
loggerContextCb?: () => Record<string, unknown>;
|
37
37
|
};
|
38
|
+
export interface TranscriptionSegment {
|
39
|
+
id: string;
|
40
|
+
text: string;
|
41
|
+
language: string;
|
42
|
+
startTime: number;
|
43
|
+
endTime: number;
|
44
|
+
final: boolean;
|
45
|
+
}
|
38
46
|
//# sourceMappingURL=types.d.ts.map
|
@@ -1,7 +1,8 @@
|
|
1
|
-
import { ClientInfo } from '@livekit/protocol';
|
1
|
+
import { ClientInfo, Transcription as TranscriptionModel } from '@livekit/protocol';
|
2
2
|
import type LocalAudioTrack from './track/LocalAudioTrack';
|
3
3
|
import type RemoteAudioTrack from './track/RemoteAudioTrack';
|
4
4
|
import { VideoCodec } from './track/options';
|
5
|
+
import type { TranscriptionSegment } from './types';
|
5
6
|
export declare const ddExtensionURI = "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension";
|
6
7
|
export declare function unpackStreamId(packed: string): string[];
|
7
8
|
export declare function sleep(duration: number): Promise<void>;
|
@@ -93,4 +94,5 @@ export declare function isVideoCodec(maybeCodec: string): maybeCodec is VideoCod
|
|
93
94
|
export declare function unwrapConstraint(constraint: ConstrainDOMString): string;
|
94
95
|
export declare function toWebsocketUrl(url: string): string;
|
95
96
|
export declare function toHttpUrl(url: string): string;
|
97
|
+
export declare function extractTranscriptionSegments(transcription: TranscriptionModel): TranscriptionSegment[];
|
96
98
|
//# sourceMappingURL=utils.d.ts.map
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "livekit-client",
|
3
|
-
"version": "2.1.
|
3
|
+
"version": "2.1.3",
|
4
4
|
"description": "JavaScript/TypeScript client SDK for LiveKit",
|
5
5
|
"main": "./dist/livekit-client.umd.js",
|
6
6
|
"unpkg": "./dist/livekit-client.umd.js",
|
@@ -36,7 +36,7 @@
|
|
36
36
|
"author": "David Zhao <david@davidzhao.com>",
|
37
37
|
"license": "Apache-2.0",
|
38
38
|
"dependencies": {
|
39
|
-
"@livekit/protocol": "1.
|
39
|
+
"@livekit/protocol": "1.15.0",
|
40
40
|
"events": "^3.3.0",
|
41
41
|
"loglevel": "^1.8.0",
|
42
42
|
"sdp-transform": "^2.14.1",
|
@@ -46,8 +46,8 @@
|
|
46
46
|
"webrtc-adapter": "^8.1.1"
|
47
47
|
},
|
48
48
|
"devDependencies": {
|
49
|
-
"@babel/core": "7.24.
|
50
|
-
"@babel/preset-env": "7.24.
|
49
|
+
"@babel/core": "7.24.5",
|
50
|
+
"@babel/preset-env": "7.24.5",
|
51
51
|
"@bufbuild/protoc-gen-es": "^1.3.0",
|
52
52
|
"@changesets/cli": "2.27.1",
|
53
53
|
"@livekit/changesets-changelog-github": "^0.0.4",
|
@@ -73,15 +73,15 @@
|
|
73
73
|
"gh-pages": "6.1.1",
|
74
74
|
"jsdom": "^24.0.0",
|
75
75
|
"prettier": "^3.0.0",
|
76
|
-
"rollup": "4.
|
76
|
+
"rollup": "4.17.2",
|
77
77
|
"rollup-plugin-delete": "^2.0.0",
|
78
78
|
"rollup-plugin-re": "1.0.7",
|
79
79
|
"rollup-plugin-typescript2": "0.36.0",
|
80
80
|
"size-limit": "^8.2.4",
|
81
|
-
"typedoc": "0.25.
|
81
|
+
"typedoc": "0.25.13",
|
82
82
|
"typedoc-plugin-no-inherit": "1.4.0",
|
83
|
-
"typescript": "5.4.
|
84
|
-
"vite": "5.
|
83
|
+
"typescript": "5.4.5",
|
84
|
+
"vite": "5.2.10",
|
85
85
|
"vitest": "^1.0.0"
|
86
86
|
},
|
87
87
|
"scripts": {
|
package/src/api/SignalClient.ts
CHANGED
@@ -6,6 +6,7 @@ import {
|
|
6
6
|
DisconnectReason,
|
7
7
|
JoinResponse,
|
8
8
|
LeaveRequest,
|
9
|
+
LeaveRequest_Action,
|
9
10
|
MuteTrackRequest,
|
10
11
|
ParticipantInfo,
|
11
12
|
Ping,
|
@@ -596,8 +597,9 @@ export class SignalClient {
|
|
596
597
|
return this.sendRequest({
|
597
598
|
case: 'leave',
|
598
599
|
value: new LeaveRequest({
|
599
|
-
canReconnect: false,
|
600
600
|
reason: DisconnectReason.CLIENT_INITIATED,
|
601
|
+
// server doesn't process this field, keeping it here to indicate the intent of a full disconnect
|
602
|
+
action: LeaveRequest_Action.DISCONNECT,
|
601
603
|
}),
|
602
604
|
});
|
603
605
|
}
|
package/src/index.ts
CHANGED
@@ -44,7 +44,7 @@ export { facingModeFromDeviceLabel, facingModeFromLocalTrack } from './room/trac
|
|
44
44
|
export * from './room/track/options';
|
45
45
|
export * from './room/track/processor/types';
|
46
46
|
export * from './room/track/types';
|
47
|
-
export type { DataPublishOptions, SimulationScenario } from './room/types';
|
47
|
+
export type { DataPublishOptions, SimulationScenario, TranscriptionSegment } from './room/types';
|
48
48
|
export * from './version';
|
49
49
|
export {
|
50
50
|
ConnectionQuality,
|
package/src/room/RTCEngine.ts
CHANGED
@@ -9,6 +9,7 @@ import {
|
|
9
9
|
DisconnectReason,
|
10
10
|
type JoinResponse,
|
11
11
|
type LeaveRequest,
|
12
|
+
LeaveRequest_Action,
|
12
13
|
ParticipantInfo,
|
13
14
|
ReconnectReason,
|
14
15
|
type ReconnectResponse,
|
@@ -23,6 +24,7 @@ import {
|
|
23
24
|
TrackInfo,
|
24
25
|
type TrackPublishedResponse,
|
25
26
|
TrackUnpublishedResponse,
|
27
|
+
Transcription,
|
26
28
|
UpdateSubscription,
|
27
29
|
UserPacket,
|
28
30
|
} from '@livekit/protocol';
|
@@ -434,7 +436,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
434
436
|
this.emit(EngineEvent.MediaTrackAdded, ev.track, ev.streams[0], ev.receiver);
|
435
437
|
};
|
436
438
|
|
437
|
-
|
439
|
+
if (!supportOptionalDatachannel(joinResponse.serverInfo?.protocol)) {
|
440
|
+
this.createDataChannels();
|
441
|
+
}
|
438
442
|
}
|
439
443
|
|
440
444
|
private setupSignalClientCallbacks() {
|
@@ -503,16 +507,28 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
503
507
|
this.handleDisconnect('signal', ReconnectReason.RR_SIGNAL_DISCONNECTED);
|
504
508
|
};
|
505
509
|
|
506
|
-
this.client.onLeave = (leave
|
507
|
-
if (leave?.canReconnect) {
|
508
|
-
this.fullReconnectOnNext = true;
|
509
|
-
// reconnect immediately instead of waiting for next attempt
|
510
|
-
this.handleDisconnect(leaveReconnect);
|
511
|
-
} else {
|
512
|
-
this.emit(EngineEvent.Disconnected, leave?.reason);
|
513
|
-
this.close();
|
514
|
-
}
|
510
|
+
this.client.onLeave = (leave: LeaveRequest) => {
|
515
511
|
this.log.debug('client leave request', { ...this.logContext, reason: leave?.reason });
|
512
|
+
if (leave.regions && this.regionUrlProvider) {
|
513
|
+
this.log.debug('updating regions', this.logContext);
|
514
|
+
this.regionUrlProvider.setServerReportedRegions(leave.regions);
|
515
|
+
}
|
516
|
+
switch (leave.action) {
|
517
|
+
case LeaveRequest_Action.DISCONNECT:
|
518
|
+
this.emit(EngineEvent.Disconnected, leave?.reason);
|
519
|
+
this.close();
|
520
|
+
break;
|
521
|
+
case LeaveRequest_Action.RECONNECT:
|
522
|
+
this.fullReconnectOnNext = true;
|
523
|
+
// reconnect immediately instead of waiting for next attempt
|
524
|
+
this.handleDisconnect(leaveReconnect);
|
525
|
+
break;
|
526
|
+
case LeaveRequest_Action.RESUME:
|
527
|
+
// reconnect immediately instead of waiting for next attempt
|
528
|
+
this.handleDisconnect(leaveReconnect);
|
529
|
+
default:
|
530
|
+
break;
|
531
|
+
}
|
516
532
|
};
|
517
533
|
}
|
518
534
|
|
@@ -634,6 +650,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
634
650
|
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
|
635
651
|
} else if (dp.value?.case === 'user') {
|
636
652
|
this.emit(EngineEvent.DataPacketReceived, dp.value.value, dp.kind);
|
653
|
+
} else if (dp.value?.case === 'transcription') {
|
654
|
+
this.emit(EngineEvent.TranscriptionReceived, dp.value.value);
|
637
655
|
}
|
638
656
|
} finally {
|
639
657
|
unlock();
|
@@ -1100,11 +1118,21 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1100
1118
|
throw new ConnectionError(`${transportName} connection not set`);
|
1101
1119
|
}
|
1102
1120
|
|
1121
|
+
let needNegotiation = false;
|
1122
|
+
if (!subscriber && !this.dataChannelForKind(kind, subscriber)) {
|
1123
|
+
this.createDataChannels();
|
1124
|
+
needNegotiation = true;
|
1125
|
+
}
|
1126
|
+
|
1103
1127
|
if (
|
1128
|
+
!needNegotiation &&
|
1104
1129
|
!subscriber &&
|
1105
1130
|
!this.pcManager.publisher.isICEConnected &&
|
1106
1131
|
this.pcManager.publisher.getICEConnectionState() !== 'checking'
|
1107
1132
|
) {
|
1133
|
+
needNegotiation = true;
|
1134
|
+
}
|
1135
|
+
if (needNegotiation) {
|
1108
1136
|
// start negotiation
|
1109
1137
|
this.negotiate();
|
1110
1138
|
}
|
@@ -1165,6 +1193,14 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1165
1193
|
}
|
1166
1194
|
|
1167
1195
|
this.pcManager.requirePublisher();
|
1196
|
+
// don't negotiate without any transceivers or data channel, it will generate sdp without ice frag then negotiate failed
|
1197
|
+
if (
|
1198
|
+
this.pcManager.publisher.getTransceivers().length == 0 &&
|
1199
|
+
!this.lossyDC &&
|
1200
|
+
!this.reliableDC
|
1201
|
+
) {
|
1202
|
+
this.createDataChannels();
|
1203
|
+
}
|
1168
1204
|
|
1169
1205
|
const abortController = new AbortController();
|
1170
1206
|
|
@@ -1357,6 +1393,7 @@ export type EngineEventCallbacks = {
|
|
1357
1393
|
) => void;
|
1358
1394
|
activeSpeakersUpdate: (speakers: Array<SpeakerInfo>) => void;
|
1359
1395
|
dataPacketReceived: (userPacket: UserPacket, kind: DataPacket_Kind) => void;
|
1396
|
+
transcriptionReceived: (transcription: Transcription) => void;
|
1360
1397
|
transportsCreated: (publisher: PCTransport, subscriber: PCTransport) => void;
|
1361
1398
|
/** @internal */
|
1362
1399
|
trackSenderAdded: (track: Track, sender: RTCRtpSender) => void;
|
@@ -1374,3 +1411,7 @@ export type EngineEventCallbacks = {
|
|
1374
1411
|
remoteMute: (trackSid: string, muted: boolean) => void;
|
1375
1412
|
offline: () => void;
|
1376
1413
|
};
|
1414
|
+
|
1415
|
+
function supportOptionalDatachannel(protocol: number | undefined): boolean {
|
1416
|
+
return protocol !== undefined && protocol > 13;
|
1417
|
+
}
|
package/src/room/Room.ts
CHANGED
@@ -4,6 +4,7 @@ import {
|
|
4
4
|
DisconnectReason,
|
5
5
|
JoinResponse,
|
6
6
|
LeaveRequest,
|
7
|
+
LeaveRequest_Action,
|
7
8
|
ParticipantInfo,
|
8
9
|
ParticipantInfo_State,
|
9
10
|
ParticipantPermission,
|
@@ -18,6 +19,8 @@ import {
|
|
18
19
|
TrackInfo,
|
19
20
|
TrackSource,
|
20
21
|
TrackType,
|
22
|
+
Transcription as TranscriptionModel,
|
23
|
+
TranscriptionSegment as TranscriptionSegmentModel,
|
21
24
|
UserPacket,
|
22
25
|
protoInt64,
|
23
26
|
} from '@livekit/protocol';
|
@@ -61,11 +64,12 @@ import type { TrackPublication } from './track/TrackPublication';
|
|
61
64
|
import type { TrackProcessor } from './track/processor/types';
|
62
65
|
import type { AdaptiveStreamSettings } from './track/types';
|
63
66
|
import { getNewAudioContext, sourceToKind } from './track/utils';
|
64
|
-
import type { SimulationOptions, SimulationScenario } from './types';
|
67
|
+
import type { SimulationOptions, SimulationScenario, TranscriptionSegment } from './types';
|
65
68
|
import {
|
66
69
|
Future,
|
67
70
|
Mutex,
|
68
71
|
createDummyVideoStreamTrack,
|
72
|
+
extractTranscriptionSegments,
|
69
73
|
getEmptyAudioStreamTrack,
|
70
74
|
isBrowserSupported,
|
71
75
|
isCloud,
|
@@ -330,6 +334,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
330
334
|
})
|
331
335
|
.on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
|
332
336
|
.on(EngineEvent.DataPacketReceived, this.handleDataPacket)
|
337
|
+
.on(EngineEvent.TranscriptionReceived, this.handleTranscription)
|
333
338
|
.on(EngineEvent.Resuming, () => {
|
334
339
|
this.clearConnectionReconcile();
|
335
340
|
this.isResuming = true;
|
@@ -855,7 +860,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
855
860
|
onLeave(
|
856
861
|
new LeaveRequest({
|
857
862
|
reason: DisconnectReason.CLIENT_INITIATED,
|
858
|
-
|
863
|
+
action: LeaveRequest_Action.RECONNECT,
|
859
864
|
}),
|
860
865
|
);
|
861
866
|
}
|
@@ -1471,6 +1476,23 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1471
1476
|
participant?.emit(ParticipantEvent.DataReceived, userPacket.payload, kind);
|
1472
1477
|
};
|
1473
1478
|
|
1479
|
+
bufferedSegments: Map<string, TranscriptionSegmentModel> = new Map();
|
1480
|
+
|
1481
|
+
private handleTranscription = (transcription: TranscriptionModel) => {
|
1482
|
+
// find the participant
|
1483
|
+
const participant =
|
1484
|
+
transcription.participantIdentity === this.localParticipant.identity
|
1485
|
+
? this.localParticipant
|
1486
|
+
: this.remoteParticipants.get(transcription.participantIdentity);
|
1487
|
+
const publication = participant?.trackPublications.get(transcription.trackId);
|
1488
|
+
|
1489
|
+
const segments = extractTranscriptionSegments(transcription);
|
1490
|
+
|
1491
|
+
publication?.emit(TrackEvent.TranscriptionReceived, segments);
|
1492
|
+
participant?.emit(ParticipantEvent.TranscriptionReceived, segments, publication);
|
1493
|
+
this.emit(RoomEvent.TranscriptionReceived, segments, participant, publication);
|
1494
|
+
};
|
1495
|
+
|
1474
1496
|
private handleAudioPlaybackStarted = () => {
|
1475
1497
|
if (this.canPlaybackAudio) {
|
1476
1498
|
return;
|
@@ -2071,6 +2093,11 @@ export type RoomEventCallbacks = {
|
|
2071
2093
|
kind?: DataPacket_Kind,
|
2072
2094
|
topic?: string,
|
2073
2095
|
) => void;
|
2096
|
+
transcriptionReceived: (
|
2097
|
+
transcription: TranscriptionSegment[],
|
2098
|
+
participant?: Participant,
|
2099
|
+
publication?: TrackPublication,
|
2100
|
+
) => void;
|
2074
2101
|
connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void;
|
2075
2102
|
mediaDevicesError: (error: Error) => void;
|
2076
2103
|
trackStreamStateChanged: (
|
package/src/room/events.ts
CHANGED
@@ -197,6 +197,12 @@ export enum RoomEvent {
|
|
197
197
|
*/
|
198
198
|
DataReceived = 'dataReceived',
|
199
199
|
|
200
|
+
/**
|
201
|
+
* Transcription received from a participant's track.
|
202
|
+
* @beta
|
203
|
+
*/
|
204
|
+
TranscriptionReceived = 'transcriptionReceived',
|
205
|
+
|
200
206
|
/**
|
201
207
|
* Connection quality was changed for a Participant. It'll receive updates
|
202
208
|
* from the local participant, as well as any [[RemoteParticipant]]s that we are
|
@@ -402,6 +408,12 @@ export enum ParticipantEvent {
|
|
402
408
|
*/
|
403
409
|
DataReceived = 'dataReceived',
|
404
410
|
|
411
|
+
/**
|
412
|
+
* Transcription received from this participant as data source.
|
413
|
+
* @beta
|
414
|
+
*/
|
415
|
+
TranscriptionReceived = 'transcriptionReceived',
|
416
|
+
|
405
417
|
/**
|
406
418
|
* Has speaking status changed for the current participant
|
407
419
|
*
|
@@ -479,6 +491,7 @@ export enum EngineEvent {
|
|
479
491
|
MediaTrackAdded = 'mediaTrackAdded',
|
480
492
|
ActiveSpeakersUpdate = 'activeSpeakersUpdate',
|
481
493
|
DataPacketReceived = 'dataPacketReceived',
|
494
|
+
TranscriptionReceived = 'transcriptionReceived',
|
482
495
|
RTPVideoMapUpdate = 'rtpVideoMapUpdate',
|
483
496
|
DCBufferStatusChanged = 'dcBufferStatusChanged',
|
484
497
|
ParticipantUpdate = 'participantUpdate',
|
@@ -562,4 +575,14 @@ export enum TrackEvent {
|
|
562
575
|
* @internal
|
563
576
|
*/
|
564
577
|
AudioTrackFeatureUpdate = 'audioTrackFeatureUpdate',
|
578
|
+
|
579
|
+
/**
|
580
|
+
* @beta
|
581
|
+
*/
|
582
|
+
TranscriptionReceived = 'transcriptionReceived',
|
583
|
+
|
584
|
+
/**
|
585
|
+
* @experimental
|
586
|
+
*/
|
587
|
+
TimeSyncUpdate = 'timeSyncUpdate',
|
565
588
|
}
|
@@ -16,7 +16,7 @@ import type RemoteTrack from '../track/RemoteTrack';
|
|
16
16
|
import type RemoteTrackPublication from '../track/RemoteTrackPublication';
|
17
17
|
import { Track } from '../track/Track';
|
18
18
|
import type { TrackPublication } from '../track/TrackPublication';
|
19
|
-
import type { LoggerOptions } from '../types';
|
19
|
+
import type { LoggerOptions, TranscriptionSegment } from '../types';
|
20
20
|
|
21
21
|
export enum ConnectionQuality {
|
22
22
|
Excellent = 'excellent',
|
@@ -329,6 +329,10 @@ export type ParticipantEventCallbacks = {
|
|
329
329
|
participantMetadataChanged: (prevMetadata: string | undefined, participant?: any) => void;
|
330
330
|
participantNameChanged: (name: string) => void;
|
331
331
|
dataReceived: (payload: Uint8Array, kind: DataPacket_Kind) => void;
|
332
|
+
transcriptionReceived: (
|
333
|
+
transcription: TranscriptionSegment[],
|
334
|
+
publication?: TrackPublication,
|
335
|
+
) => void;
|
332
336
|
isSpeakingChanged: (speaking: boolean) => void;
|
333
337
|
connectionQualityChanged: (connectionQuality: ConnectionQuality) => void;
|
334
338
|
trackStreamStateChanged: (
|
@@ -77,7 +77,23 @@ export default abstract class RemoteTrack<
|
|
77
77
|
if (!this.monitorInterval) {
|
78
78
|
this.monitorInterval = setInterval(() => this.monitorReceiver(), monitorFrequency);
|
79
79
|
}
|
80
|
+
this.registerTimeSyncUpdate();
|
80
81
|
}
|
81
82
|
|
82
83
|
protected abstract monitorReceiver(): void;
|
84
|
+
|
85
|
+
registerTimeSyncUpdate() {
|
86
|
+
const loop = () => {
|
87
|
+
this.timeSyncHandle = requestAnimationFrame(() => loop());
|
88
|
+
const sources = this.receiver?.getSynchronizationSources()[0];
|
89
|
+
if (sources) {
|
90
|
+
const { timestamp, rtpTimestamp } = sources;
|
91
|
+
if (rtpTimestamp && this.rtpTimestamp !== rtpTimestamp) {
|
92
|
+
this.emit(TrackEvent.TimeSyncUpdate, { timestamp, rtpTimestamp });
|
93
|
+
this.rtpTimestamp = rtpTimestamp;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
};
|
97
|
+
loop();
|
98
|
+
}
|
83
99
|
}
|
package/src/room/track/Track.ts
CHANGED
@@ -53,6 +53,9 @@ export abstract class Track<
|
|
53
53
|
*/
|
54
54
|
streamState: Track.StreamState = Track.StreamState.Active;
|
55
55
|
|
56
|
+
/** @internal */
|
57
|
+
rtpTimestamp: number | undefined;
|
58
|
+
|
56
59
|
protected _mediaStreamTrack: MediaStreamTrack;
|
57
60
|
|
58
61
|
protected _mediaStreamID: string;
|
@@ -63,6 +66,8 @@ export abstract class Track<
|
|
63
66
|
|
64
67
|
private loggerContextCb: LoggerOptions['loggerContextCb'];
|
65
68
|
|
69
|
+
protected timeSyncHandle: number | undefined;
|
70
|
+
|
66
71
|
protected _currentBitrate: number = 0;
|
67
72
|
|
68
73
|
protected monitorInterval?: ReturnType<typeof setInterval>;
|
@@ -255,6 +260,9 @@ export abstract class Track<
|
|
255
260
|
if (this.monitorInterval) {
|
256
261
|
clearInterval(this.monitorInterval);
|
257
262
|
}
|
263
|
+
if (this.timeSyncHandle) {
|
264
|
+
cancelAnimationFrame(this.timeSyncHandle);
|
265
|
+
}
|
258
266
|
}
|
259
267
|
|
260
268
|
/** @internal */
|
@@ -517,4 +525,5 @@ export type TrackEventCallbacks = {
|
|
517
525
|
upstreamResumed: (track: any) => void;
|
518
526
|
trackProcessorUpdate: (processor?: TrackProcessor<Track.Kind, any>) => void;
|
519
527
|
audioTrackFeatureUpdate: (track: any, feature: AudioTrackFeature, enabled: boolean) => void;
|
528
|
+
timeSyncUpdate: (update: { timestamp: number; rtpTimestamp: number }) => void;
|
520
529
|
};
|
@@ -9,7 +9,7 @@ import { EventEmitter } from 'events';
|
|
9
9
|
import type TypedEventEmitter from 'typed-emitter';
|
10
10
|
import log, { LoggerNames, getLogger } from '../../logger';
|
11
11
|
import { TrackEvent } from '../events';
|
12
|
-
import type { LoggerOptions } from '../types';
|
12
|
+
import type { LoggerOptions, TranscriptionSegment } from '../types';
|
13
13
|
import LocalAudioTrack from './LocalAudioTrack';
|
14
14
|
import LocalVideoTrack from './LocalVideoTrack';
|
15
15
|
import RemoteAudioTrack from './RemoteAudioTrack';
|
@@ -174,4 +174,6 @@ export type PublicationEventCallbacks = {
|
|
174
174
|
prevStatus: TrackPublication.SubscriptionStatus,
|
175
175
|
) => void;
|
176
176
|
subscriptionFailed: (error: SubscriptionError) => void;
|
177
|
+
transcriptionReceived: (transcription: TranscriptionSegment[]) => void;
|
178
|
+
timeSyncUpdate: (timestamp: number) => void;
|
177
179
|
};
|
package/src/room/types.ts
CHANGED
@@ -55,3 +55,12 @@ export type LoggerOptions = {
|
|
55
55
|
loggerName?: string;
|
56
56
|
loggerContextCb?: () => Record<string, unknown>;
|
57
57
|
};
|
58
|
+
|
59
|
+
export interface TranscriptionSegment {
|
60
|
+
id: string;
|
61
|
+
text: string;
|
62
|
+
language: string;
|
63
|
+
startTime: number;
|
64
|
+
endTime: number;
|
65
|
+
final: boolean;
|
66
|
+
}
|
package/src/room/utils.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ClientInfo, ClientInfo_SDK } from '@livekit/protocol';
|
1
|
+
import { ClientInfo, ClientInfo_SDK, Transcription as TranscriptionModel } from '@livekit/protocol';
|
2
2
|
import { getBrowser } from '../utils/browserParser';
|
3
3
|
import { protocolVersion, version } from '../version';
|
4
4
|
import CriticalTimers from './timers';
|
@@ -6,7 +6,7 @@ import type LocalAudioTrack from './track/LocalAudioTrack';
|
|
6
6
|
import type RemoteAudioTrack from './track/RemoteAudioTrack';
|
7
7
|
import { VideoCodec, videoCodecs } from './track/options';
|
8
8
|
import { getNewAudioContext } from './track/utils';
|
9
|
-
import type { LiveKitReactNativeInfo } from './types';
|
9
|
+
import type { LiveKitReactNativeInfo, TranscriptionSegment } from './types';
|
10
10
|
|
11
11
|
const separator = '|';
|
12
12
|
export const ddExtensionURI =
|
@@ -527,3 +527,18 @@ export function toHttpUrl(url: string): string {
|
|
527
527
|
}
|
528
528
|
return url;
|
529
529
|
}
|
530
|
+
|
531
|
+
export function extractTranscriptionSegments(
|
532
|
+
transcription: TranscriptionModel,
|
533
|
+
): TranscriptionSegment[] {
|
534
|
+
return transcription.segments.map(({ id, text, language, startTime, endTime, final }) => {
|
535
|
+
return {
|
536
|
+
id,
|
537
|
+
text,
|
538
|
+
startTime: Number.parseInt(startTime.toString()),
|
539
|
+
endTime: Number.parseInt(endTime.toString()),
|
540
|
+
final,
|
541
|
+
language,
|
542
|
+
};
|
543
|
+
});
|
544
|
+
}
|
package/src/version.ts
CHANGED