livekit-client 2.1.1 → 2.1.3
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/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