@memberjunction/ai-realtime-client 6.2.0-edge.3 → 6.2.0-edge.5
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/README.md +156 -29
- package/dist/audio/audioMeter.d.ts +71 -0
- package/dist/audio/audioMeter.d.ts.map +1 -1
- package/dist/audio/audioMeter.js +83 -0
- package/dist/audio/audioMeter.js.map +1 -1
- package/dist/audio/mediaElementAudioRouter.d.ts +41 -0
- package/dist/audio/mediaElementAudioRouter.d.ts.map +1 -0
- package/dist/audio/mediaElementAudioRouter.js +82 -0
- package/dist/audio/mediaElementAudioRouter.js.map +1 -0
- package/dist/audio/micCapture.d.ts +12 -3
- package/dist/audio/micCapture.d.ts.map +1 -1
- package/dist/audio/micCapture.js +15 -1
- package/dist/audio/micCapture.js.map +1 -1
- package/dist/audio/pcmPlayback.d.ts +72 -4
- package/dist/audio/pcmPlayback.d.ts.map +1 -1
- package/dist/audio/pcmPlayback.js +101 -1
- package/dist/audio/pcmPlayback.js.map +1 -1
- package/dist/drivers/assemblyAIRealtimeClient.d.ts +2 -0
- package/dist/drivers/assemblyAIRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/assemblyAIRealtimeClient.js +14 -0
- package/dist/drivers/assemblyAIRealtimeClient.js.map +1 -1
- package/dist/drivers/elevenLabsRealtimeClient.d.ts +2 -0
- package/dist/drivers/elevenLabsRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/elevenLabsRealtimeClient.js +14 -0
- package/dist/drivers/elevenLabsRealtimeClient.js.map +1 -1
- package/dist/drivers/geminiAvatarOutput.d.ts +132 -0
- package/dist/drivers/geminiAvatarOutput.d.ts.map +1 -0
- package/dist/drivers/geminiAvatarOutput.js +310 -0
- package/dist/drivers/geminiAvatarOutput.js.map +1 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts +38 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts.map +1 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.js +99 -0
- package/dist/drivers/geminiEnterpriseRealtimeClient.js.map +1 -0
- package/dist/drivers/geminiRealtimeClient.d.ts +325 -50
- package/dist/drivers/geminiRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/geminiRealtimeClient.js +782 -184
- package/dist/drivers/geminiRealtimeClient.js.map +1 -1
- package/dist/drivers/openAILiveClient.d.ts +5 -8
- package/dist/drivers/openAILiveClient.d.ts.map +1 -1
- package/dist/drivers/openAILiveClient.js +19 -32
- package/dist/drivers/openAILiveClient.js.map +1 -1
- package/dist/drivers/openAIRealtimeClient.d.ts +13 -26
- package/dist/drivers/openAIRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/openAIRealtimeClient.js +28 -55
- package/dist/drivers/openAIRealtimeClient.js.map +1 -1
- package/dist/drivers/xaiRealtimeClient.d.ts.map +1 -1
- package/dist/drivers/xaiRealtimeClient.js +19 -14
- package/dist/drivers/xaiRealtimeClient.js.map +1 -1
- package/dist/generic/baseRealtimeClient.d.ts +183 -29
- package/dist/generic/baseRealtimeClient.d.ts.map +1 -1
- package/dist/generic/baseRealtimeClient.js +246 -14
- package/dist/generic/baseRealtimeClient.js.map +1 -1
- package/dist/generic/openAIProtocolClient.d.ts +2 -0
- package/dist/generic/openAIProtocolClient.d.ts.map +1 -1
- package/dist/generic/openAIProtocolClient.js +17 -0
- package/dist/generic/openAIProtocolClient.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/media/attachVideoSource.d.ts +23 -0
- package/dist/media/attachVideoSource.d.ts.map +1 -0
- package/dist/media/attachVideoSource.js +49 -0
- package/dist/media/attachVideoSource.js.map +1 -0
- package/dist/media/channelVideoSource.d.ts +110 -14
- package/dist/media/channelVideoSource.d.ts.map +1 -1
- package/dist/media/channelVideoSource.js +183 -18
- package/dist/media/channelVideoSource.js.map +1 -1
- package/dist/media/decoders/frameScheduler.d.ts +119 -0
- package/dist/media/decoders/frameScheduler.d.ts.map +1 -0
- package/dist/media/decoders/frameScheduler.js +238 -0
- package/dist/media/decoders/frameScheduler.js.map +1 -0
- package/dist/media/decoders/imageFrameDecoder.d.ts +58 -0
- package/dist/media/decoders/imageFrameDecoder.d.ts.map +1 -0
- package/dist/media/decoders/imageFrameDecoder.js +110 -0
- package/dist/media/decoders/imageFrameDecoder.js.map +1 -0
- package/dist/media/decoders/mseFmp4Decoder.d.ts +130 -0
- package/dist/media/decoders/mseFmp4Decoder.d.ts.map +1 -0
- package/dist/media/decoders/mseFmp4Decoder.js +402 -0
- package/dist/media/decoders/mseFmp4Decoder.js.map +1 -0
- package/dist/media/decoders/streamCanvas.d.ts +46 -0
- package/dist/media/decoders/streamCanvas.d.ts.map +1 -0
- package/dist/media/decoders/streamCanvas.js +163 -0
- package/dist/media/decoders/streamCanvas.js.map +1 -0
- package/dist/media/decoders/videoMimeTypes.d.ts +29 -0
- package/dist/media/decoders/videoMimeTypes.d.ts.map +1 -0
- package/dist/media/decoders/videoMimeTypes.js +50 -0
- package/dist/media/decoders/videoMimeTypes.js.map +1 -0
- package/dist/media/decoders/webCodecsChunkDecoder.d.ts +99 -0
- package/dist/media/decoders/webCodecsChunkDecoder.d.ts.map +1 -0
- package/dist/media/decoders/webCodecsChunkDecoder.js +265 -0
- package/dist/media/decoders/webCodecsChunkDecoder.js.map +1 -0
- package/dist/media/displayCapture.d.ts +128 -0
- package/dist/media/displayCapture.d.ts.map +1 -0
- package/dist/media/displayCapture.js +245 -0
- package/dist/media/displayCapture.js.map +1 -0
- package/dist/media/frameCapture.d.ts +25 -8
- package/dist/media/frameCapture.d.ts.map +1 -1
- package/dist/media/frameCapture.js +42 -72
- package/dist/media/frameCapture.js.map +1 -1
- package/dist/media/frameSampler.d.ts +94 -0
- package/dist/media/frameSampler.d.ts.map +1 -0
- package/dist/media/frameSampler.js +134 -0
- package/dist/media/frameSampler.js.map +1 -0
- package/dist/media/index.d.ts +32 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +32 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/localMediaController.d.ts +116 -0
- package/dist/media/localMediaController.d.ts.map +1 -0
- package/dist/media/localMediaController.js +281 -0
- package/dist/media/localMediaController.js.map +1 -0
- package/dist/media/mediaLayout.d.ts +93 -0
- package/dist/media/mediaLayout.d.ts.map +1 -0
- package/dist/media/mediaLayout.js +187 -0
- package/dist/media/mediaLayout.js.map +1 -0
- package/dist/media/mediaPreview.d.ts +94 -0
- package/dist/media/mediaPreview.d.ts.map +1 -0
- package/dist/media/mediaPreview.js +154 -0
- package/dist/media/mediaPreview.js.map +1 -0
- package/dist/media/mediaStage.d.ts +99 -0
- package/dist/media/mediaStage.d.ts.map +1 -0
- package/dist/media/mediaStage.js +171 -0
- package/dist/media/mediaStage.js.map +1 -0
- package/dist/media/model.d.ts +154 -0
- package/dist/media/model.d.ts.map +1 -0
- package/dist/media/model.js +9 -0
- package/dist/media/model.js.map +1 -0
- package/dist/media/playbackClock.d.ts +20 -0
- package/dist/media/playbackClock.d.ts.map +1 -0
- package/dist/media/playbackClock.js +13 -0
- package/dist/media/playbackClock.js.map +1 -0
- package/dist/media/videoFrameDecoder.d.ts +86 -0
- package/dist/media/videoFrameDecoder.d.ts.map +1 -0
- package/dist/media/videoFrameDecoder.js +2 -0
- package/dist/media/videoFrameDecoder.js.map +1 -0
- package/dist/media/videoFrameDecoderRegistry.d.ts +40 -0
- package/dist/media/videoFrameDecoderRegistry.d.ts.map +1 -0
- package/dist/media/videoFrameDecoderRegistry.js +69 -0
- package/dist/media/videoFrameDecoderRegistry.js.map +1 -0
- package/dist/media/videoPacing.d.ts +32 -0
- package/dist/media/videoPacing.d.ts.map +1 -0
- package/dist/media/videoPacing.js +37 -0
- package/dist/media/videoPacing.js.map +1 -0
- package/dist/media/videoPlayout.d.ts +168 -0
- package/dist/media/videoPlayout.d.ts.map +1 -0
- package/dist/media/videoPlayout.js +217 -0
- package/dist/media/videoPlayout.js.map +1 -0
- package/dist/media/videoSourceArbiter.d.ts +250 -0
- package/dist/media/videoSourceArbiter.d.ts.map +1 -0
- package/dist/media/videoSourceArbiter.js +423 -0
- package/dist/media/videoSourceArbiter.js.map +1 -0
- package/dist/testing/conformanceAssertions.d.ts +25 -0
- package/dist/testing/conformanceAssertions.d.ts.map +1 -0
- package/dist/testing/conformanceAssertions.js +64 -0
- package/dist/testing/conformanceAssertions.js.map +1 -0
- package/dist/testing/conformanceFixtures.d.ts +76 -0
- package/dist/testing/conformanceFixtures.d.ts.map +1 -0
- package/dist/testing/conformanceFixtures.js +169 -0
- package/dist/testing/conformanceFixtures.js.map +1 -0
- package/dist/testing/conformanceGates.d.ts +32 -0
- package/dist/testing/conformanceGates.d.ts.map +1 -0
- package/dist/testing/conformanceGates.js +35 -0
- package/dist/testing/conformanceGates.js.map +1 -0
- package/dist/testing/conformanceMicrophone.d.ts +10 -0
- package/dist/testing/conformanceMicrophone.d.ts.map +1 -0
- package/dist/testing/conformanceMicrophone.js +71 -0
- package/dist/testing/conformanceMicrophone.js.map +1 -0
- package/dist/testing/conformanceTimeline.d.ts +128 -0
- package/dist/testing/conformanceTimeline.d.ts.map +1 -0
- package/dist/testing/conformanceTimeline.js +32 -0
- package/dist/testing/conformanceTimeline.js.map +1 -0
- package/dist/testing/conformanceVideoStream.d.ts +29 -0
- package/dist/testing/conformanceVideoStream.d.ts.map +1 -0
- package/dist/testing/conformanceVideoStream.js +39 -0
- package/dist/testing/conformanceVideoStream.js.map +1 -0
- package/dist/testing/index.d.ts +29 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +28 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/realtimeVideoConformance.d.ts +23 -0
- package/dist/testing/realtimeVideoConformance.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformance.js +139 -0
- package/dist/testing/realtimeVideoConformance.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceChecks.d.ts +4 -0
- package/dist/testing/realtimeVideoConformanceChecks.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceChecks.js +164 -0
- package/dist/testing/realtimeVideoConformanceChecks.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts +4 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.js +63 -0
- package/dist/testing/realtimeVideoConformanceFrameChecks.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceSession.d.ts +108 -0
- package/dist/testing/realtimeVideoConformanceSession.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceSession.js +270 -0
- package/dist/testing/realtimeVideoConformanceSession.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts +4 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.js +248 -0
- package/dist/testing/realtimeVideoConformanceTurnChecks.js.map +1 -0
- package/dist/testing/realtimeVideoConformanceTypes.d.ts +155 -0
- package/dist/testing/realtimeVideoConformanceTypes.d.ts.map +1 -0
- package/dist/testing/realtimeVideoConformanceTypes.js +2 -0
- package/dist/testing/realtimeVideoConformanceTypes.js.map +1 -0
- package/dist/testing/recordingPlayout.d.ts +84 -0
- package/dist/testing/recordingPlayout.d.ts.map +1 -0
- package/dist/testing/recordingPlayout.js +120 -0
- package/dist/testing/recordingPlayout.js.map +1 -0
- package/package.json +29 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoSourceArbiter.d.ts","sourceRoot":"","sources":["../../src/media/videoSourceArbiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The VIDEO SOURCE ARBITER: the single writer of a session's inbound video.
|
|
3
|
+
*
|
|
4
|
+
* A realtime session can have many things worth showing the model at once: a shared screen, the camera,
|
|
5
|
+
* a whiteboard, a remote browser. Today's video models accept ONE inbound stream. Before this existed,
|
|
6
|
+
* every source wrote to that one stream directly: whichever happened to send last "won", and the model
|
|
7
|
+
* was never told what it was looking at. The arbiter owns the decision:
|
|
8
|
+
*
|
|
9
|
+
* - **Sources register** (`RegisterSource`) with an id, a label and a kind, and push frames to the
|
|
10
|
+
* arbiter (`PushFrame`) rather than to the client.
|
|
11
|
+
* - **The arbiter maps N live sources onto the model's `MaxInboundVideoStreams`.** With enough streams
|
|
12
|
+
* for every enabled source (a future multi-stream model) frames pass through untouched, each tagged
|
|
13
|
+
* with its source id. Otherwise it ranks the sources by a POLICY (data, overridable) and forwards only
|
|
14
|
+
* the top N.
|
|
15
|
+
* - **The model is told when the choice changes** (`[You can now see: Camera]`), because a model
|
|
16
|
+
* that silently starts seeing something else will describe the old thing.
|
|
17
|
+
* - **The user can switch a source off.** A disabled source is not a candidate, sends nothing, and the
|
|
18
|
+
* model is told it was turned off (`[You can no longer see: Camera (the user turned it off)]`).
|
|
19
|
+
* - **The rate comes only from the negotiated track.** Pacing is read from the sink
|
|
20
|
+
* (`InboundVideoRate`), never a constant here.
|
|
21
|
+
*
|
|
22
|
+
* The notes' wording comes from `@memberjunction/ai` ({@link VideoSourceSeenNote}, {@link VideoSourceTurnedOffNote},
|
|
23
|
+
* {@link VideoSourceTurnedOnNote}), where a meeting's bridge engine gets its notes too, so the model reads one style of
|
|
24
|
+
* note in a call and in a meeting.
|
|
25
|
+
*
|
|
26
|
+
* It deliberately depends only on the narrow {@link IVideoFrameSink} interface (which
|
|
27
|
+
* `BaseRealtimeClient` satisfies) and imports nothing from `generic/` or the drivers, so the media code
|
|
28
|
+
* can be split into its own subpath without untangling it.
|
|
29
|
+
*
|
|
30
|
+
* The default policy and the rationale for its ordering are on {@link DEFAULT_VIDEO_SOURCE_POLICY}.
|
|
31
|
+
*
|
|
32
|
+
* @module @memberjunction/ai-realtime-client
|
|
33
|
+
*/
|
|
34
|
+
import { VideoSourceSeenNote, VideoSourceTurnedOffNote, VideoSourceTurnedOnNote } from '@memberjunction/ai';
|
|
35
|
+
import { MinVideoFrameSpacingMs } from './videoPacing.js';
|
|
36
|
+
/** Source kinds the default ranking treats as captures: things a person started sharing on purpose. */
|
|
37
|
+
const CAPTURE_KINDS = new Set(['camera', 'screen']);
|
|
38
|
+
/** Whether a source kind is a capture (as opposed to a channel surface). */
|
|
39
|
+
export function IsVideoCaptureKind(kind) {
|
|
40
|
+
return kind !== undefined && CAPTURE_KINDS.has(kind);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The default selection policy, as DATA so an agent or host can reorder or trim it.
|
|
44
|
+
*
|
|
45
|
+
* Order: an explicit user pick beats everything (the user is the authority on what the agent sees);
|
|
46
|
+
* then the most recently started CAPTURE, because starting a camera or share is a deliberate "look at
|
|
47
|
+
* this" and a surface is always there to come back to; then the focused surface; then simply the newest
|
|
48
|
+
* source, so a lone unfocused surface is never invisible.
|
|
49
|
+
*
|
|
50
|
+
* "Most recently started" is applied to captures rather than to every source. Applied to everything it
|
|
51
|
+
* would always produce a winner and the focused-surface rule could never be reached.
|
|
52
|
+
*/
|
|
53
|
+
export const DEFAULT_VIDEO_SOURCE_POLICY = Object.freeze([
|
|
54
|
+
'user-pick',
|
|
55
|
+
'recent-capture',
|
|
56
|
+
'focused',
|
|
57
|
+
'recent',
|
|
58
|
+
]);
|
|
59
|
+
/**
|
|
60
|
+
* How many streams a sink can take right now. A sink that predates `MaxInboundVideoStreams` (a custom
|
|
61
|
+
* client, a test double) is read the way every model was until the field existed: one stream while an
|
|
62
|
+
* inbound video track is live, none otherwise.
|
|
63
|
+
*/
|
|
64
|
+
function streamCapacity(sink) {
|
|
65
|
+
if (!sink) {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
if (typeof sink.MaxInboundVideoStreams === 'number') {
|
|
69
|
+
return sink.MaxInboundVideoStreams;
|
|
70
|
+
}
|
|
71
|
+
return sink.IsTrackEstablished('video', 'inbound') ? 1 : 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The per-session arbiter. Obtain the one for a client with {@link VideoSourceArbiter.ForSink} so every
|
|
75
|
+
* writer shares it; constructing a second one for the same client would make two writers again.
|
|
76
|
+
*/
|
|
77
|
+
export class VideoSourceArbiter {
|
|
78
|
+
static { this.bySink = new WeakMap(); }
|
|
79
|
+
/**
|
|
80
|
+
* The arbiter for a sink, created on first use. One arbiter per sink is the whole point (a single
|
|
81
|
+
* writer), so every bridge and channel that gets its arbiter this way shares it.
|
|
82
|
+
*
|
|
83
|
+
* @param sink The client (anything satisfying {@link IVideoFrameSink}).
|
|
84
|
+
* @param options Options applied only when the arbiter is created.
|
|
85
|
+
*/
|
|
86
|
+
static ForSink(sink, options) {
|
|
87
|
+
let arbiter = VideoSourceArbiter.bySink.get(sink);
|
|
88
|
+
if (!arbiter) {
|
|
89
|
+
arbiter = new VideoSourceArbiter(sink, options);
|
|
90
|
+
VideoSourceArbiter.bySink.set(sink, arbiter);
|
|
91
|
+
}
|
|
92
|
+
return arbiter;
|
|
93
|
+
}
|
|
94
|
+
constructor(sinkSource, options = {}) {
|
|
95
|
+
this.sinkSource = sinkSource;
|
|
96
|
+
this.options = options;
|
|
97
|
+
this.sources = new Map();
|
|
98
|
+
this.handlers = new Set();
|
|
99
|
+
this.nextSeq = 1;
|
|
100
|
+
this.userPick = null;
|
|
101
|
+
this.focused = null;
|
|
102
|
+
this.focusedChannel = null;
|
|
103
|
+
/** The ids forwarded as of the last reconcile — the baseline a change is detected against. */
|
|
104
|
+
this.lastActive = [];
|
|
105
|
+
/** Whether the previous reconcile had more enabled sources than streams (arbitration in effect). */
|
|
106
|
+
this.lastArbitrating = false;
|
|
107
|
+
this.policy = options.Policy ?? DEFAULT_VIDEO_SOURCE_POLICY;
|
|
108
|
+
}
|
|
109
|
+
get sink() {
|
|
110
|
+
const resolved = typeof this.sinkSource === 'function' ? this.sinkSource() : this.sinkSource;
|
|
111
|
+
return resolved ?? null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Registers a source, or updates its label/kind if the id is already registered (its recency, enabled
|
|
115
|
+
* state and counters are kept). A newly registered source counts as the most recently STARTED.
|
|
116
|
+
*/
|
|
117
|
+
RegisterSource(descriptor) {
|
|
118
|
+
if (!descriptor.SourceID) {
|
|
119
|
+
throw new Error('VideoSourceArbiter.RegisterSource requires a SourceID.');
|
|
120
|
+
}
|
|
121
|
+
const existing = this.sources.get(descriptor.SourceID);
|
|
122
|
+
if (existing) {
|
|
123
|
+
existing.Label = descriptor.Label;
|
|
124
|
+
existing.Kind = descriptor.Kind ?? existing.Kind;
|
|
125
|
+
existing.ChannelKey = descriptor.ChannelKey ?? existing.ChannelKey;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.sources.set(descriptor.SourceID, {
|
|
129
|
+
...descriptor,
|
|
130
|
+
Kind: descriptor.Kind ?? 'surface',
|
|
131
|
+
Enabled: descriptor.Enabled ?? true,
|
|
132
|
+
Seq: this.nextSeq++,
|
|
133
|
+
FramesSent: 0,
|
|
134
|
+
LastSentAt: 0,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
this.reconcile();
|
|
138
|
+
this.emitChange();
|
|
139
|
+
}
|
|
140
|
+
/** Removes a source. Its explicit pick or focus, if it held one, is cleared. */
|
|
141
|
+
UnregisterSource(sourceId) {
|
|
142
|
+
if (!this.sources.delete(sourceId)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (this.userPick === sourceId) {
|
|
146
|
+
this.userPick = null;
|
|
147
|
+
}
|
|
148
|
+
if (this.focused === sourceId) {
|
|
149
|
+
this.focused = null;
|
|
150
|
+
}
|
|
151
|
+
this.reconcile();
|
|
152
|
+
this.emitChange();
|
|
153
|
+
}
|
|
154
|
+
/** Whether a source id is registered. */
|
|
155
|
+
HasSource(sourceId) {
|
|
156
|
+
return this.sources.has(sourceId);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The user explicitly picks the source the agent sees (`null` clears the pick). A pick beats every
|
|
160
|
+
* other rule. Returns `false` for an unknown or disabled source (nothing changes).
|
|
161
|
+
*/
|
|
162
|
+
SelectSource(sourceId) {
|
|
163
|
+
if (sourceId !== null) {
|
|
164
|
+
const source = this.sources.get(sourceId);
|
|
165
|
+
if (!source || !source.Enabled) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
this.userPick = sourceId;
|
|
170
|
+
this.reconcile();
|
|
171
|
+
this.emitChange();
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Marks the CHANNEL whose surface the user is looking at (`null` clears it): every source registered for that
|
|
176
|
+
* channel counts as focused, including one that registers after this call (a surface the user is already on
|
|
177
|
+
* before its first frame). Used by the `'focused'` rule alongside {@link SetFocusedSource}.
|
|
178
|
+
*/
|
|
179
|
+
SetFocusedChannel(channelKey) {
|
|
180
|
+
if (this.focusedChannel === channelKey) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
this.focusedChannel = channelKey;
|
|
184
|
+
this.reconcile();
|
|
185
|
+
this.emitChange();
|
|
186
|
+
}
|
|
187
|
+
/** Marks the source whose surface the user is looking at (`null` clears it). Used by the `'focused'` rule. */
|
|
188
|
+
SetFocusedSource(sourceId) {
|
|
189
|
+
this.focused = sourceId !== null && this.sources.has(sourceId) ? sourceId : null;
|
|
190
|
+
this.reconcile();
|
|
191
|
+
this.emitChange();
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Turns a source on or off. A disabled source is not a candidate and forwards nothing.
|
|
195
|
+
*
|
|
196
|
+
* @param sourceId The source.
|
|
197
|
+
* @param enabled The new state.
|
|
198
|
+
* @param notify Whether to tell the model. Default `true`. A caller that sends its own note about the
|
|
199
|
+
* same change (a channel lowering its exposure) passes `false` so the model is told once.
|
|
200
|
+
* @returns `false` for an unknown source; `true` otherwise (including "already in that state").
|
|
201
|
+
*/
|
|
202
|
+
SetSourceEnabled(sourceId, enabled, notify = true) {
|
|
203
|
+
const source = this.sources.get(sourceId);
|
|
204
|
+
if (!source) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
if (source.Enabled === enabled) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
source.Enabled = enabled;
|
|
211
|
+
if (!enabled && this.userPick === sourceId) {
|
|
212
|
+
this.userPick = null;
|
|
213
|
+
}
|
|
214
|
+
// Reconcile FIRST and quietly: the toggle has its own note, so a switch note on top of it
|
|
215
|
+
// would tell the model the same thing twice.
|
|
216
|
+
this.reconcile(true);
|
|
217
|
+
if (notify) {
|
|
218
|
+
this.sendNote(enabled ? this.formatEnabledNote(source.Label) : this.formatDisabledNote(source.Label));
|
|
219
|
+
}
|
|
220
|
+
this.emitChange();
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
/** A snapshot of every registered source and whether it is being forwarded, in registration order. */
|
|
224
|
+
GetSources() {
|
|
225
|
+
const active = new Set(this.computeActive());
|
|
226
|
+
return [...this.sources.values()]
|
|
227
|
+
.sort((a, b) => a.Seq - b.Seq)
|
|
228
|
+
.map((s) => ({
|
|
229
|
+
SourceID: s.SourceID,
|
|
230
|
+
Label: s.Label,
|
|
231
|
+
Kind: s.Kind,
|
|
232
|
+
ChannelKey: s.ChannelKey,
|
|
233
|
+
Enabled: s.Enabled,
|
|
234
|
+
Active: active.has(s.SourceID),
|
|
235
|
+
FramesSent: s.FramesSent,
|
|
236
|
+
LastFrameAt: s.LastFrameAt,
|
|
237
|
+
...(this.userPick === s.SourceID ? { Picked: true } : {}),
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
/** The ids the arbiter would forward right now, best first. */
|
|
241
|
+
GetActiveSourceIDs() {
|
|
242
|
+
return this.computeActive();
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Subscribes to structural changes (a source registered, removed, enabled, disabled, picked, focused,
|
|
246
|
+
* or the forwarded set changing). Not called per frame.
|
|
247
|
+
*
|
|
248
|
+
* @returns An unsubscribe function.
|
|
249
|
+
*/
|
|
250
|
+
OnChange(handler) {
|
|
251
|
+
this.handlers.add(handler);
|
|
252
|
+
return () => {
|
|
253
|
+
this.handlers.delete(handler);
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A source offers a frame. It is forwarded to the model only if the source is enabled, among the
|
|
258
|
+
* sources the model should see, and not inside the negotiated pacing interval.
|
|
259
|
+
*
|
|
260
|
+
* @param sourceId The producing source (it must be registered).
|
|
261
|
+
* @param base64Image The frame, base64-encoded.
|
|
262
|
+
* @param mimeType Image MIME type. Defaults to `'image/jpeg'`.
|
|
263
|
+
* @returns `true` if the frame was sent to the model.
|
|
264
|
+
*/
|
|
265
|
+
PushFrame(sourceId, base64Image, mimeType = 'image/jpeg') {
|
|
266
|
+
const source = this.sources.get(sourceId);
|
|
267
|
+
const sink = this.sink;
|
|
268
|
+
if (!source || !source.Enabled || !sink || typeof sink.SendVideoFrame !== 'function') {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
if (!sink.IsTrackEstablished('video', 'inbound')) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
// The stream count can change once the track is negotiated, so the active set is re-derived
|
|
275
|
+
// here rather than trusted from the last registration.
|
|
276
|
+
this.reconcile();
|
|
277
|
+
if (!this.computeActive().includes(sourceId)) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
const now = this.now();
|
|
281
|
+
if (source.LastSentAt > 0 && now - source.LastSentAt < MinVideoFrameSpacingMs(sink.InboundVideoRate)) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
const sent = this.send(sink, base64Image, mimeType, sourceId);
|
|
285
|
+
if (sent) {
|
|
286
|
+
source.LastSentAt = now;
|
|
287
|
+
source.LastFrameAt = now;
|
|
288
|
+
source.FramesSent++;
|
|
289
|
+
}
|
|
290
|
+
return sent;
|
|
291
|
+
}
|
|
292
|
+
/** Drops every source and subscription. The arbiter stays usable (it simply has no sources). */
|
|
293
|
+
Dispose() {
|
|
294
|
+
this.sources.clear();
|
|
295
|
+
this.userPick = null;
|
|
296
|
+
this.focused = null;
|
|
297
|
+
this.focusedChannel = null;
|
|
298
|
+
this.lastActive = [];
|
|
299
|
+
this.lastArbitrating = false;
|
|
300
|
+
this.handlers.clear();
|
|
301
|
+
}
|
|
302
|
+
// ── selection ───────────────────────────────────────────────────────────
|
|
303
|
+
/** The enabled sources, ranked by policy, best first. */
|
|
304
|
+
rank() {
|
|
305
|
+
const enabled = [...this.sources.values()].filter((s) => s.Enabled);
|
|
306
|
+
const newestFirst = (list) => [...list].sort((a, b) => b.Seq - a.Seq);
|
|
307
|
+
const ranked = [];
|
|
308
|
+
const add = (list) => {
|
|
309
|
+
for (const s of list) {
|
|
310
|
+
if (!ranked.includes(s)) {
|
|
311
|
+
ranked.push(s);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
for (const rule of this.policy) {
|
|
316
|
+
switch (rule) {
|
|
317
|
+
case 'user-pick':
|
|
318
|
+
add(enabled.filter((s) => s.SourceID === this.userPick));
|
|
319
|
+
break;
|
|
320
|
+
case 'recent-capture':
|
|
321
|
+
add(newestFirst(enabled.filter((s) => IsVideoCaptureKind(s.Kind))));
|
|
322
|
+
break;
|
|
323
|
+
case 'focused':
|
|
324
|
+
add(enabled.filter((s) => s.SourceID === this.focused || (this.focusedChannel !== null && s.ChannelKey === this.focusedChannel)));
|
|
325
|
+
break;
|
|
326
|
+
case 'recent':
|
|
327
|
+
add(newestFirst(enabled));
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// A policy without the catch-all must still be total over the enabled sources' ORDER, but it is
|
|
332
|
+
// allowed to leave some out: that is what trimming the policy means.
|
|
333
|
+
return ranked;
|
|
334
|
+
}
|
|
335
|
+
/** The ids to forward: the top N of the ranking, N being the model's inbound video stream count. */
|
|
336
|
+
computeActive() {
|
|
337
|
+
const streams = streamCapacity(this.sink);
|
|
338
|
+
if (streams <= 0) {
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
return this.rank().slice(0, streams).map((s) => s.SourceID);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Re-derives the forwarded set and, when it changed while sources are being arbitrated, tells the model.
|
|
345
|
+
*
|
|
346
|
+
* @param quiet Update the baseline without sending a switch note (the caller sends its own).
|
|
347
|
+
*/
|
|
348
|
+
reconcile(quiet = false) {
|
|
349
|
+
const sink = this.sink;
|
|
350
|
+
const streams = streamCapacity(sink);
|
|
351
|
+
const enabledCount = [...this.sources.values()].filter((s) => s.Enabled).length;
|
|
352
|
+
const arbitrating = streams > 0 && enabledCount > streams;
|
|
353
|
+
const active = this.computeActive();
|
|
354
|
+
const changed = active.length !== this.lastActive.length || active.some((id, i) => id !== this.lastActive[i]);
|
|
355
|
+
const wasArbitrating = this.lastArbitrating;
|
|
356
|
+
this.lastActive = active;
|
|
357
|
+
this.lastArbitrating = arbitrating;
|
|
358
|
+
if (!changed || quiet || !sink) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
// Notes only matter when the model could be confused about what it sees: more sources than
|
|
362
|
+
// streams now, or just before. A lone source appearing or leaving is not a "switch".
|
|
363
|
+
if (!arbitrating && !wasArbitrating) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (active.length === 0) {
|
|
367
|
+
// Nothing left to switch TO. A source the user turned off has its own note, and a session whose
|
|
368
|
+
// last source stopped has nothing the model could be confused about.
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const labels = active.map((id) => this.sources.get(id)?.Label ?? id);
|
|
372
|
+
this.sendNote(this.options.FormatSwitchNote ? this.options.FormatSwitchNote(labels) : VideoSourceSeenNote(labels));
|
|
373
|
+
}
|
|
374
|
+
// ── plumbing ────────────────────────────────────────────────────────────
|
|
375
|
+
formatDisabledNote(label) {
|
|
376
|
+
return this.options.FormatDisabledNote ? this.options.FormatDisabledNote(label) : VideoSourceTurnedOffNote(label);
|
|
377
|
+
}
|
|
378
|
+
formatEnabledNote(label) {
|
|
379
|
+
return this.options.FormatEnabledNote ? this.options.FormatEnabledNote(label) : VideoSourceTurnedOnNote(label);
|
|
380
|
+
}
|
|
381
|
+
now() {
|
|
382
|
+
return this.options.Now ? this.options.Now() : Date.now();
|
|
383
|
+
}
|
|
384
|
+
send(sink, base64Image, mimeType, sourceId) {
|
|
385
|
+
try {
|
|
386
|
+
return Boolean(sink.SendVideoFrame?.(base64Image, mimeType, sourceId));
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
this.reportError('sending a video frame', error);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
sendNote(note) {
|
|
394
|
+
const sink = this.sink;
|
|
395
|
+
if (!note || !sink) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
sink.SendContextNote(note);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
this.reportError('sending a source note', error);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
emitChange() {
|
|
406
|
+
for (const handler of [...this.handlers]) {
|
|
407
|
+
try {
|
|
408
|
+
handler();
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
this.reportError('notifying a change handler', error);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
reportError(context, error) {
|
|
416
|
+
if (this.options.OnError) {
|
|
417
|
+
this.options.OnError(context, error);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
console.error(`[VideoSourceArbiter] Error ${context}:`, error);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=videoSourceArbiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"videoSourceArbiter.js","sourceRoot":"","sources":["../../src/media/videoSourceArbiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The kit's plain assertions: no test framework. Each failure names the rule the driver broke and what
|
|
3
|
+
* was expected and seen; the runner prefixes the check's id.
|
|
4
|
+
*
|
|
5
|
+
* @module @memberjunction/ai-realtime-client/testing
|
|
6
|
+
*/
|
|
7
|
+
/** Thrown when a driver breaks a rule of the video conformance kit; the message names the check and the rule. */
|
|
8
|
+
export declare class RealtimeVideoConformanceError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/** Fails the check with `<rule>: <detail>`. */
|
|
12
|
+
export declare function Fail(rule: string, detail: string): never;
|
|
13
|
+
/** Fails unless `condition` holds. `seen` says what was seen instead. */
|
|
14
|
+
export declare function AssertTrue(condition: boolean, rule: string, seen: string): void;
|
|
15
|
+
/** Fails unless a count is what the rule says. */
|
|
16
|
+
export declare function AssertCount(actual: number, expected: number, rule: string): void;
|
|
17
|
+
/** Fails unless a value (a state, a track state, a kind) is what the rule says. */
|
|
18
|
+
export declare function AssertValue(actual: string | boolean | null | undefined, expected: string | boolean | null, rule: string): void;
|
|
19
|
+
/** Fails unless the pieces seen are the pieces sent, in order, byte for byte. */
|
|
20
|
+
export declare function AssertSameBytes(actual: readonly ArrayBuffer[], expected: readonly ArrayBuffer[], rule: string): void;
|
|
21
|
+
/** Fails unless two amounts of seconds agree to the nanosecond: sums of 1/24 s are inexact in floating point. */
|
|
22
|
+
export declare function AssertSeconds(actual: number, expected: number, rule: string): void;
|
|
23
|
+
/** Seconds rounded to the nanosecond. */
|
|
24
|
+
export declare function ToNanoseconds(seconds: number): number;
|
|
25
|
+
//# sourceMappingURL=conformanceAssertions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conformanceAssertions.d.ts","sourceRoot":"","sources":["../../src/testing/conformanceAssertions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,iHAAiH;AACjH,qBAAa,6BAA8B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI9B;AAED,+CAA+C;AAC/C,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAExD;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI/E;AAED,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAIhF;AAED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI9H;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQpH;AAED,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAIlF;AAED,yCAAyC;AACzC,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The kit's plain assertions: no test framework. Each failure names the rule the driver broke and what
|
|
3
|
+
* was expected and seen; the runner prefixes the check's id.
|
|
4
|
+
*
|
|
5
|
+
* @module @memberjunction/ai-realtime-client/testing
|
|
6
|
+
*/
|
|
7
|
+
/** Thrown when a driver breaks a rule of the video conformance kit; the message names the check and the rule. */
|
|
8
|
+
export class RealtimeVideoConformanceError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'RealtimeVideoConformanceError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Fails the check with `<rule>: <detail>`. */
|
|
15
|
+
export function Fail(rule, detail) {
|
|
16
|
+
throw new RealtimeVideoConformanceError(`${rule}: ${detail}`);
|
|
17
|
+
}
|
|
18
|
+
/** Fails unless `condition` holds. `seen` says what was seen instead. */
|
|
19
|
+
export function AssertTrue(condition, rule, seen) {
|
|
20
|
+
if (!condition) {
|
|
21
|
+
Fail(rule, seen);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Fails unless a count is what the rule says. */
|
|
25
|
+
export function AssertCount(actual, expected, rule) {
|
|
26
|
+
if (actual !== expected) {
|
|
27
|
+
Fail(rule, `expected ${expected}, got ${actual}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Fails unless a value (a state, a track state, a kind) is what the rule says. */
|
|
31
|
+
export function AssertValue(actual, expected, rule) {
|
|
32
|
+
if (actual !== expected) {
|
|
33
|
+
Fail(rule, `expected ${String(expected)}, got ${String(actual)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Fails unless the pieces seen are the pieces sent, in order, byte for byte. */
|
|
37
|
+
export function AssertSameBytes(actual, expected, rule) {
|
|
38
|
+
if (actual.length !== expected.length) {
|
|
39
|
+
Fail(rule, `expected ${expected.length} pieces, got ${actual.length}`);
|
|
40
|
+
}
|
|
41
|
+
const different = expected.findIndex((piece, index) => !sameBytes(actual[index], piece));
|
|
42
|
+
if (different >= 0) {
|
|
43
|
+
Fail(rule, `piece ${different + 1} of ${expected.length} differs from what the model sent (or came out of order)`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Fails unless two amounts of seconds agree to the nanosecond: sums of 1/24 s are inexact in floating point. */
|
|
47
|
+
export function AssertSeconds(actual, expected, rule) {
|
|
48
|
+
if (ToNanoseconds(actual) !== ToNanoseconds(expected)) {
|
|
49
|
+
Fail(rule, `expected ${ToNanoseconds(expected)} s, got ${ToNanoseconds(actual)} s`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Seconds rounded to the nanosecond. */
|
|
53
|
+
export function ToNanoseconds(seconds) {
|
|
54
|
+
return Math.round(seconds * 1e9) / 1e9;
|
|
55
|
+
}
|
|
56
|
+
function sameBytes(a, b) {
|
|
57
|
+
if (!a || a.byteLength !== b.byteLength) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const left = new Uint8Array(a);
|
|
61
|
+
const right = new Uint8Array(b);
|
|
62
|
+
return left.every((byte, index) => byte === right[index]);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=conformanceAssertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conformanceAssertions.js","sourceRoot":"","sources":["../../src/testing/conformanceAssertions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The media the video conformance kit sends as the model: tiny, synthetic, deterministic video in each
|
|
3
|
+
* form a model's video comes in (Core's `RealtimeVideoFrame` kinds), and PCM chunks. Nothing here decodes; the frames
|
|
4
|
+
* only need to be read (by `@memberjunction/ai`'s fMP4 reader) and routed.
|
|
5
|
+
*
|
|
6
|
+
* - **Fragmented MP4**, as a realtime avatar sends it: an init segment (`ftyp` + `moov`) with an H.264 `avc1.42c01f` video
|
|
7
|
+
* track at a 90 kHz timescale and an AAC-LC `mp4a.40.2` audio track at 24 kHz unless the video comes without the
|
|
8
|
+
* voice; video fragments (`moof` + `mdat`) of one sample per 24 fps frame, so each frame is
|
|
9
|
+
* {@link CONFORMANCE_FMP4_FRAME_SECONDS}; audio-only fragments of a muxed avatar, which carry no video seconds.
|
|
10
|
+
* - **Encoded chunks** ({@link ConformanceChunkFrames}): opaque bytes labelled H.264, a key frame every 24, presentation
|
|
11
|
+
* times 24 to the second.
|
|
12
|
+
* - **Images** ({@link ConformanceImageFrame}): JPEG start and end markers around a few bytes, 24 to the second.
|
|
13
|
+
* - **PCM** chunks of 4 bytes, too short to ever be read as an MP4 box.
|
|
14
|
+
*
|
|
15
|
+
* Every kind is spaced 1/24 s apart, so a frame is {@link CONFORMANCE_FMP4_FRAME_SECONDS} whatever its kind. The box
|
|
16
|
+
* builders are exported for this package's own tests; the `/testing` entry exports only the fixtures.
|
|
17
|
+
*
|
|
18
|
+
* @module @memberjunction/ai-realtime-client/testing
|
|
19
|
+
*/
|
|
20
|
+
import { type RealtimeChunkVideoFrame, type RealtimeFmp4VideoFrame, type RealtimeImageVideoFrame } from '@memberjunction/ai';
|
|
21
|
+
/** The seconds of video one frame carries, of any kind: 1/24. A {@link ConformanceFmp4VideoFragment} frame lasts this long. */
|
|
22
|
+
export declare const CONFORMANCE_FMP4_FRAME_SECONDS: number;
|
|
23
|
+
/** Joins byte arrays end to end. */
|
|
24
|
+
export declare function ConcatBytes(...parts: Uint8Array[]): Uint8Array;
|
|
25
|
+
/** A big-endian 32-bit unsigned integer. */
|
|
26
|
+
export declare function Uint32Bytes(value: number): Uint8Array;
|
|
27
|
+
/** The bytes of an ASCII string, such as a box type. */
|
|
28
|
+
export declare function AsciiBytes(text: string): Uint8Array;
|
|
29
|
+
/** An MP4 box: its size, its four-character type, then its payload. */
|
|
30
|
+
export declare function Mp4Box(type: string, ...payload: Uint8Array[]): Uint8Array;
|
|
31
|
+
/** A full box: version 0, no flags, then the payload. */
|
|
32
|
+
export declare function Mp4FullBox(type: string, ...payload: Uint8Array[]): Uint8Array;
|
|
33
|
+
/**
|
|
34
|
+
* An init segment (`ftyp` + `moov`): an H.264 video track at `videoTimescale` units a second, and an AAC-LC audio track
|
|
35
|
+
* (the avatar's voice) unless `withAudio` is false.
|
|
36
|
+
*
|
|
37
|
+
* @param withAudio Whether the video carries the voice: an audio track beside the video. Default `true`.
|
|
38
|
+
* @param videoTimescale The video track's units per second. Default 90 000, which times each frame at 1/24 s.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ConformanceFmp4InitSegment(withAudio?: boolean, videoTimescale?: number): ArrayBuffer;
|
|
41
|
+
/**
|
|
42
|
+
* A video fragment (`moof` + `mdat`) of `frames` 24 fps frames, {@link CONFORMANCE_FMP4_FRAME_SECONDS} each under the
|
|
43
|
+
* default init segment. `sequence` makes each fragment's bytes distinct.
|
|
44
|
+
*/
|
|
45
|
+
export declare function ConformanceFmp4VideoFragment(frames?: number, sequence?: number): ArrayBuffer;
|
|
46
|
+
/** An audio-only fragment of a muxed avatar: one AAC frame and no video, so it adds no video seconds. */
|
|
47
|
+
export declare function ConformanceFmp4AudioFragment(sequence?: number): ArrayBuffer;
|
|
48
|
+
/**
|
|
49
|
+
* A 4-byte PCM16 chunk of the agent's voice. Too short to be read as an MP4 box, so a driver that sniffs bytes never
|
|
50
|
+
* takes it for video. `seed` makes each chunk's bytes distinct.
|
|
51
|
+
*/
|
|
52
|
+
export declare function ConformancePcm(seed?: number): ArrayBuffer;
|
|
53
|
+
/** How to number and label {@link ConformanceChunkFrames}. */
|
|
54
|
+
export interface ConformanceChunkOptions {
|
|
55
|
+
/** The first chunk's number in the stream, from 0: it sets its time, its bytes and whether it is a key frame. Default 0. */
|
|
56
|
+
FirstIndex?: number;
|
|
57
|
+
/** The chunks' type. Default `video/h264; codecs="avc1.42e01f"`. */
|
|
58
|
+
MimeType?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* `count` encoded chunks as a raw-frame provider sends them: each with distinct opaque bytes, its presentation time on a
|
|
62
|
+
* 24 fps timeline (chunk `n` at `n` × 1000/24 ms), and a key frame every 24 (chunk 0, 24, 48 …). Nothing decodes them.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ConformanceChunkFrames(count: number, options?: ConformanceChunkOptions): RealtimeChunkVideoFrame[];
|
|
65
|
+
/** Image `index` of a 24 fps stream: JPEG start and end markers around distinct bytes, timed at `index` × 1000/24 ms. */
|
|
66
|
+
export declare function ConformanceImageFrame(index?: number): RealtimeImageVideoFrame;
|
|
67
|
+
/**
|
|
68
|
+
* A piece of fragmented MP4 as the frame a provider's driver builds from it (Core's `Fmp4PieceToVideoFrame`), labelled
|
|
69
|
+
* `video/mp4`. For this package's own use; a harness gets the frames the kit sends.
|
|
70
|
+
*
|
|
71
|
+
* @throws When the piece is not fragmented MP4.
|
|
72
|
+
*/
|
|
73
|
+
export declare function Fmp4Frame(piece: ArrayBuffer): RealtimeFmp4VideoFrame;
|
|
74
|
+
/** Frame `index`'s presentation time on the kit's 24 fps timeline, in ms. */
|
|
75
|
+
export declare function FrameTimeMs(index: number): number;
|
|
76
|
+
//# sourceMappingURL=conformanceFixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conformanceFixtures.d.ts","sourceRoot":"","sources":["../../src/testing/conformanceFixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAEH,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC/B,MAAM,oBAAoB,CAAC;AAW5B,+HAA+H;AAC/H,eAAO,MAAM,8BAA8B,QAAsC,CAAC;AAWlF,oCAAoC;AACpC,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,CAQ9D;AAED,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAIrD;AAED,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEnD;AAED,uEAAuE;AACvE,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,CAGzE;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,CAE7E;AAqCD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,GAAE,OAAc,EAAE,cAAc,GAAE,MAAwB,GAAG,WAAW,CAI3H;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,GAAE,MAAU,EAAE,QAAQ,GAAE,MAAU,GAAG,WAAW,CAGlG;AAED,yGAAyG;AACzG,wBAAgB,4BAA4B,CAAC,QAAQ,GAAE,MAAU,GAAG,WAAW,CAG9E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAU,GAAG,WAAW,CAE5D;AAED,8DAA8D;AAC9D,MAAM,WAAW,uBAAuB;IACpC,4HAA4H;IAC5H,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,uBAAuB,EAAE,CAYtH;AAED,yHAAyH;AACzH,wBAAgB,qBAAqB,CAAC,KAAK,GAAE,MAAU,GAAG,uBAAuB,CAQhF;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,sBAAsB,CAMpE;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|