@memberjunction/ai-realtime-client 6.2.0-edge.2 → 6.2.0-edge.4

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.
Files changed (206) hide show
  1. package/README.md +156 -29
  2. package/dist/audio/audioMeter.d.ts +71 -0
  3. package/dist/audio/audioMeter.d.ts.map +1 -1
  4. package/dist/audio/audioMeter.js +83 -0
  5. package/dist/audio/audioMeter.js.map +1 -1
  6. package/dist/audio/mediaElementAudioRouter.d.ts +41 -0
  7. package/dist/audio/mediaElementAudioRouter.d.ts.map +1 -0
  8. package/dist/audio/mediaElementAudioRouter.js +82 -0
  9. package/dist/audio/mediaElementAudioRouter.js.map +1 -0
  10. package/dist/audio/micCapture.d.ts +12 -3
  11. package/dist/audio/micCapture.d.ts.map +1 -1
  12. package/dist/audio/micCapture.js +15 -1
  13. package/dist/audio/micCapture.js.map +1 -1
  14. package/dist/audio/pcmPlayback.d.ts +72 -4
  15. package/dist/audio/pcmPlayback.d.ts.map +1 -1
  16. package/dist/audio/pcmPlayback.js +101 -1
  17. package/dist/audio/pcmPlayback.js.map +1 -1
  18. package/dist/drivers/assemblyAIRealtimeClient.d.ts +2 -0
  19. package/dist/drivers/assemblyAIRealtimeClient.d.ts.map +1 -1
  20. package/dist/drivers/assemblyAIRealtimeClient.js +14 -0
  21. package/dist/drivers/assemblyAIRealtimeClient.js.map +1 -1
  22. package/dist/drivers/elevenLabsRealtimeClient.d.ts +2 -0
  23. package/dist/drivers/elevenLabsRealtimeClient.d.ts.map +1 -1
  24. package/dist/drivers/elevenLabsRealtimeClient.js +14 -0
  25. package/dist/drivers/elevenLabsRealtimeClient.js.map +1 -1
  26. package/dist/drivers/geminiAvatarOutput.d.ts +132 -0
  27. package/dist/drivers/geminiAvatarOutput.d.ts.map +1 -0
  28. package/dist/drivers/geminiAvatarOutput.js +310 -0
  29. package/dist/drivers/geminiAvatarOutput.js.map +1 -0
  30. package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts +38 -0
  31. package/dist/drivers/geminiEnterpriseRealtimeClient.d.ts.map +1 -0
  32. package/dist/drivers/geminiEnterpriseRealtimeClient.js +107 -0
  33. package/dist/drivers/geminiEnterpriseRealtimeClient.js.map +1 -0
  34. package/dist/drivers/geminiRealtimeClient.d.ts +314 -48
  35. package/dist/drivers/geminiRealtimeClient.d.ts.map +1 -1
  36. package/dist/drivers/geminiRealtimeClient.js +759 -176
  37. package/dist/drivers/geminiRealtimeClient.js.map +1 -1
  38. package/dist/drivers/openAILiveClient.d.ts +5 -8
  39. package/dist/drivers/openAILiveClient.d.ts.map +1 -1
  40. package/dist/drivers/openAILiveClient.js +19 -32
  41. package/dist/drivers/openAILiveClient.js.map +1 -1
  42. package/dist/drivers/openAIRealtimeClient.d.ts +23 -26
  43. package/dist/drivers/openAIRealtimeClient.d.ts.map +1 -1
  44. package/dist/drivers/openAIRealtimeClient.js +42 -57
  45. package/dist/drivers/openAIRealtimeClient.js.map +1 -1
  46. package/dist/generic/baseRealtimeClient.d.ts +188 -30
  47. package/dist/generic/baseRealtimeClient.d.ts.map +1 -1
  48. package/dist/generic/baseRealtimeClient.js +250 -14
  49. package/dist/generic/baseRealtimeClient.js.map +1 -1
  50. package/dist/generic/openAIProtocolClient.d.ts +57 -1
  51. package/dist/generic/openAIProtocolClient.d.ts.map +1 -1
  52. package/dist/generic/openAIProtocolClient.js +89 -4
  53. package/dist/generic/openAIProtocolClient.js.map +1 -1
  54. package/dist/index.d.ts +2 -3
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +3 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/media/attachVideoSource.d.ts +23 -0
  59. package/dist/media/attachVideoSource.d.ts.map +1 -0
  60. package/dist/media/attachVideoSource.js +49 -0
  61. package/dist/media/attachVideoSource.js.map +1 -0
  62. package/dist/media/channelVideoSource.d.ts +110 -14
  63. package/dist/media/channelVideoSource.d.ts.map +1 -1
  64. package/dist/media/channelVideoSource.js +183 -18
  65. package/dist/media/channelVideoSource.js.map +1 -1
  66. package/dist/media/decoders/frameScheduler.d.ts +119 -0
  67. package/dist/media/decoders/frameScheduler.d.ts.map +1 -0
  68. package/dist/media/decoders/frameScheduler.js +238 -0
  69. package/dist/media/decoders/frameScheduler.js.map +1 -0
  70. package/dist/media/decoders/imageFrameDecoder.d.ts +58 -0
  71. package/dist/media/decoders/imageFrameDecoder.d.ts.map +1 -0
  72. package/dist/media/decoders/imageFrameDecoder.js +110 -0
  73. package/dist/media/decoders/imageFrameDecoder.js.map +1 -0
  74. package/dist/media/decoders/mseFmp4Decoder.d.ts +130 -0
  75. package/dist/media/decoders/mseFmp4Decoder.d.ts.map +1 -0
  76. package/dist/media/decoders/mseFmp4Decoder.js +402 -0
  77. package/dist/media/decoders/mseFmp4Decoder.js.map +1 -0
  78. package/dist/media/decoders/streamCanvas.d.ts +46 -0
  79. package/dist/media/decoders/streamCanvas.d.ts.map +1 -0
  80. package/dist/media/decoders/streamCanvas.js +163 -0
  81. package/dist/media/decoders/streamCanvas.js.map +1 -0
  82. package/dist/media/decoders/videoMimeTypes.d.ts +29 -0
  83. package/dist/media/decoders/videoMimeTypes.d.ts.map +1 -0
  84. package/dist/media/decoders/videoMimeTypes.js +50 -0
  85. package/dist/media/decoders/videoMimeTypes.js.map +1 -0
  86. package/dist/media/decoders/webCodecsChunkDecoder.d.ts +99 -0
  87. package/dist/media/decoders/webCodecsChunkDecoder.d.ts.map +1 -0
  88. package/dist/media/decoders/webCodecsChunkDecoder.js +265 -0
  89. package/dist/media/decoders/webCodecsChunkDecoder.js.map +1 -0
  90. package/dist/media/displayCapture.d.ts +128 -0
  91. package/dist/media/displayCapture.d.ts.map +1 -0
  92. package/dist/media/displayCapture.js +245 -0
  93. package/dist/media/displayCapture.js.map +1 -0
  94. package/dist/media/frameCapture.d.ts +25 -8
  95. package/dist/media/frameCapture.d.ts.map +1 -1
  96. package/dist/media/frameCapture.js +42 -72
  97. package/dist/media/frameCapture.js.map +1 -1
  98. package/dist/media/frameSampler.d.ts +94 -0
  99. package/dist/media/frameSampler.d.ts.map +1 -0
  100. package/dist/media/frameSampler.js +134 -0
  101. package/dist/media/frameSampler.js.map +1 -0
  102. package/dist/media/index.d.ts +32 -0
  103. package/dist/media/index.d.ts.map +1 -0
  104. package/dist/media/index.js +32 -0
  105. package/dist/media/index.js.map +1 -0
  106. package/dist/media/localMediaController.d.ts +116 -0
  107. package/dist/media/localMediaController.d.ts.map +1 -0
  108. package/dist/media/localMediaController.js +281 -0
  109. package/dist/media/localMediaController.js.map +1 -0
  110. package/dist/media/mediaLayout.d.ts +93 -0
  111. package/dist/media/mediaLayout.d.ts.map +1 -0
  112. package/dist/media/mediaLayout.js +187 -0
  113. package/dist/media/mediaLayout.js.map +1 -0
  114. package/dist/media/mediaPreview.d.ts +94 -0
  115. package/dist/media/mediaPreview.d.ts.map +1 -0
  116. package/dist/media/mediaPreview.js +154 -0
  117. package/dist/media/mediaPreview.js.map +1 -0
  118. package/dist/media/mediaStage.d.ts +99 -0
  119. package/dist/media/mediaStage.d.ts.map +1 -0
  120. package/dist/media/mediaStage.js +171 -0
  121. package/dist/media/mediaStage.js.map +1 -0
  122. package/dist/media/model.d.ts +154 -0
  123. package/dist/media/model.d.ts.map +1 -0
  124. package/dist/media/model.js +9 -0
  125. package/dist/media/model.js.map +1 -0
  126. package/dist/media/playbackClock.d.ts +20 -0
  127. package/dist/media/playbackClock.d.ts.map +1 -0
  128. package/dist/media/playbackClock.js +13 -0
  129. package/dist/media/playbackClock.js.map +1 -0
  130. package/dist/media/videoFrameDecoder.d.ts +86 -0
  131. package/dist/media/videoFrameDecoder.d.ts.map +1 -0
  132. package/dist/media/videoFrameDecoder.js +2 -0
  133. package/dist/media/videoFrameDecoder.js.map +1 -0
  134. package/dist/media/videoFrameDecoderRegistry.d.ts +40 -0
  135. package/dist/media/videoFrameDecoderRegistry.d.ts.map +1 -0
  136. package/dist/media/videoFrameDecoderRegistry.js +69 -0
  137. package/dist/media/videoFrameDecoderRegistry.js.map +1 -0
  138. package/dist/media/videoPacing.d.ts +32 -0
  139. package/dist/media/videoPacing.d.ts.map +1 -0
  140. package/dist/media/videoPacing.js +37 -0
  141. package/dist/media/videoPacing.js.map +1 -0
  142. package/dist/media/videoPlayout.d.ts +168 -0
  143. package/dist/media/videoPlayout.d.ts.map +1 -0
  144. package/dist/media/videoPlayout.js +217 -0
  145. package/dist/media/videoPlayout.js.map +1 -0
  146. package/dist/media/videoSourceArbiter.d.ts +250 -0
  147. package/dist/media/videoSourceArbiter.d.ts.map +1 -0
  148. package/dist/media/videoSourceArbiter.js +423 -0
  149. package/dist/media/videoSourceArbiter.js.map +1 -0
  150. package/dist/testing/conformanceAssertions.d.ts +25 -0
  151. package/dist/testing/conformanceAssertions.d.ts.map +1 -0
  152. package/dist/testing/conformanceAssertions.js +64 -0
  153. package/dist/testing/conformanceAssertions.js.map +1 -0
  154. package/dist/testing/conformanceFixtures.d.ts +76 -0
  155. package/dist/testing/conformanceFixtures.d.ts.map +1 -0
  156. package/dist/testing/conformanceFixtures.js +169 -0
  157. package/dist/testing/conformanceFixtures.js.map +1 -0
  158. package/dist/testing/conformanceGates.d.ts +32 -0
  159. package/dist/testing/conformanceGates.d.ts.map +1 -0
  160. package/dist/testing/conformanceGates.js +35 -0
  161. package/dist/testing/conformanceGates.js.map +1 -0
  162. package/dist/testing/conformanceMicrophone.d.ts +10 -0
  163. package/dist/testing/conformanceMicrophone.d.ts.map +1 -0
  164. package/dist/testing/conformanceMicrophone.js +71 -0
  165. package/dist/testing/conformanceMicrophone.js.map +1 -0
  166. package/dist/testing/conformanceTimeline.d.ts +128 -0
  167. package/dist/testing/conformanceTimeline.d.ts.map +1 -0
  168. package/dist/testing/conformanceTimeline.js +32 -0
  169. package/dist/testing/conformanceTimeline.js.map +1 -0
  170. package/dist/testing/conformanceVideoStream.d.ts +29 -0
  171. package/dist/testing/conformanceVideoStream.d.ts.map +1 -0
  172. package/dist/testing/conformanceVideoStream.js +39 -0
  173. package/dist/testing/conformanceVideoStream.js.map +1 -0
  174. package/dist/testing/index.d.ts +29 -0
  175. package/dist/testing/index.d.ts.map +1 -0
  176. package/dist/testing/index.js +28 -0
  177. package/dist/testing/index.js.map +1 -0
  178. package/dist/testing/realtimeVideoConformance.d.ts +23 -0
  179. package/dist/testing/realtimeVideoConformance.d.ts.map +1 -0
  180. package/dist/testing/realtimeVideoConformance.js +139 -0
  181. package/dist/testing/realtimeVideoConformance.js.map +1 -0
  182. package/dist/testing/realtimeVideoConformanceChecks.d.ts +4 -0
  183. package/dist/testing/realtimeVideoConformanceChecks.d.ts.map +1 -0
  184. package/dist/testing/realtimeVideoConformanceChecks.js +164 -0
  185. package/dist/testing/realtimeVideoConformanceChecks.js.map +1 -0
  186. package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts +4 -0
  187. package/dist/testing/realtimeVideoConformanceFrameChecks.d.ts.map +1 -0
  188. package/dist/testing/realtimeVideoConformanceFrameChecks.js +63 -0
  189. package/dist/testing/realtimeVideoConformanceFrameChecks.js.map +1 -0
  190. package/dist/testing/realtimeVideoConformanceSession.d.ts +108 -0
  191. package/dist/testing/realtimeVideoConformanceSession.d.ts.map +1 -0
  192. package/dist/testing/realtimeVideoConformanceSession.js +270 -0
  193. package/dist/testing/realtimeVideoConformanceSession.js.map +1 -0
  194. package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts +4 -0
  195. package/dist/testing/realtimeVideoConformanceTurnChecks.d.ts.map +1 -0
  196. package/dist/testing/realtimeVideoConformanceTurnChecks.js +248 -0
  197. package/dist/testing/realtimeVideoConformanceTurnChecks.js.map +1 -0
  198. package/dist/testing/realtimeVideoConformanceTypes.d.ts +155 -0
  199. package/dist/testing/realtimeVideoConformanceTypes.d.ts.map +1 -0
  200. package/dist/testing/realtimeVideoConformanceTypes.js +2 -0
  201. package/dist/testing/realtimeVideoConformanceTypes.js.map +1 -0
  202. package/dist/testing/recordingPlayout.d.ts +84 -0
  203. package/dist/testing/recordingPlayout.d.ts.map +1 -0
  204. package/dist/testing/recordingPlayout.js +120 -0
  205. package/dist/testing/recordingPlayout.js.map +1 -0
  206. package/package.json +29 -4
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @fileoverview MEDIA LAYOUT: the user's layout of a media stage as a host keeps and saves it.
3
+ * - **Moves.** One move per surface, oldest first: the list `LayoutMediaStage` and `ResolveSurfacePlacements` read.
4
+ * {@link RecordPlacementMove} keeps it that way, and {@link WithoutStageMoves} hands the stage back to its default.
5
+ * - **Boxes.** Where the user put each picture-in-picture box, by surface key, as fractions of the stage.
6
+ * - **Self-view.** Whether the user hid their own tile, for a host that lets them (the meeting room).
7
+ *
8
+ * The realtime call keeps its channels' layout this way and the meeting room its participants' tiles, so both save the
9
+ * same form ({@link SerializePlacementMoves}, {@link SerializePipRects}, {@link SerializeSelfViewHidden}) and read it
10
+ * back tolerantly, through {@link MediaLayoutPrefs} under each host's own keys.
11
+ *
12
+ * @module @memberjunction/ai-realtime-client/media
13
+ */
14
+ import { type MediaPipRect, type MediaPlacementMove } from './model.js';
15
+ /** Adds a move at the end, dropping the surface's earlier move, so the list keeps one move per surface in order. */
16
+ export declare function RecordPlacementMove(moves: readonly MediaPlacementMove[], move: MediaPlacementMove): MediaPlacementMove[];
17
+ /**
18
+ * The moves without any move to the stage: whatever the user put there goes back to its default. The stage goes to the
19
+ * newest move there, so a host whose "unpin" should hand the stage back to its default, not to an earlier move, drops
20
+ * the stage moves before recording the next one.
21
+ */
22
+ export declare function WithoutStageMoves(moves: readonly MediaPlacementMove[]): MediaPlacementMove[];
23
+ /** Reads saved moves. Tolerant: a value that is not a list reads as no moves, and a malformed entry is skipped. */
24
+ export declare function ParsePlacementMoves(raw: string | null | undefined): MediaPlacementMove[];
25
+ /** The saved form of a list of moves. */
26
+ export declare function SerializePlacementMoves(moves: readonly MediaPlacementMove[]): string;
27
+ /** Adds a box at the end, dropping the surface's earlier box, so the boxes the user moved most recently come last. */
28
+ export declare function RecordPipRect(rects: ReadonlyMap<string, MediaPipRect>, key: string, rect: MediaPipRect): Map<string, MediaPipRect>;
29
+ /**
30
+ * Reads saved picture-in-picture boxes, by surface key. Tolerant: a value that is not an object reads as none, and a box
31
+ * that is not four fractions with a size is skipped.
32
+ */
33
+ export declare function ParsePipRects(raw: string | null | undefined): Map<string, MediaPipRect>;
34
+ /** The saved form of picture-in-picture boxes, by surface key. */
35
+ export declare function SerializePipRects(rects: ReadonlyMap<string, MediaPipRect>): string;
36
+ /**
37
+ * Reads whether the user hid their self-view. Tolerant: only a saved `true` hides it; nothing saved, `false` or anything
38
+ * else shows it.
39
+ */
40
+ export declare function ParseSelfViewHidden(raw: string | null | undefined): boolean;
41
+ /** The saved form of whether the user hid their self-view: JSON `true` or `false`. */
42
+ export declare function SerializeSelfViewHidden(hidden: boolean): string;
43
+ /** A per-user settings store: MJ's `UserInfoEngine` is one, or anything with the same two methods. */
44
+ export interface MediaLayoutSettings {
45
+ /** The text saved under a key, or `undefined` when there is none. */
46
+ GetSetting(key: string): string | undefined;
47
+ /** Saves text under a key; the store may wait for a burst of changes to end. */
48
+ SetSettingDebounced(key: string, value: string): void;
49
+ }
50
+ /** The keys a host saves its layout under, so two hosts never overwrite each other's. */
51
+ export interface MediaLayoutKeys {
52
+ /** Where the moves go. */
53
+ Moves: string;
54
+ /** Where the picture-in-picture boxes go. */
55
+ PipRects: string;
56
+ /** Where whether the user hid their self-view goes. A host with no Hide on the user's own tile leaves it out. */
57
+ SelfViewHidden?: string;
58
+ }
59
+ /** How many moves, and how many boxes, a saved layout keeps: the newest. The oldest go first. */
60
+ export declare const MEDIA_LAYOUT_SAVED_LIMIT = 50;
61
+ /**
62
+ * A host's saved layout: its moves, its picture-in-picture boxes and whether the user hid their self-view, in a per-user
63
+ * settings store under the host's own keys. Reading treats a store that is not ready (it throws) as nothing saved;
64
+ * writing skips one, or one that fails to save, so the layout then lasts for this session only. Writing keeps the newest
65
+ * {@link MEDIA_LAYOUT_SAVED_LIMIT} moves and boxes.
66
+ */
67
+ export declare class MediaLayoutPrefs {
68
+ private readonly store;
69
+ private readonly keys;
70
+ /**
71
+ * @param store The settings store, looked up each time it is used, since a host's store may be ready only later.
72
+ * @param keys Where this host's layout goes.
73
+ */
74
+ constructor(store: () => MediaLayoutSettings, keys: MediaLayoutKeys);
75
+ /** The saved moves, oldest first; none when nothing is saved or the store is not ready. */
76
+ LoadMoves(): MediaPlacementMove[];
77
+ /** The saved boxes, by surface key; none when nothing is saved or the store is not ready. */
78
+ LoadPipRects(): Map<string, MediaPipRect>;
79
+ /** Saves the moves, the newest {@link MEDIA_LAYOUT_SAVED_LIMIT} of them. */
80
+ SaveMoves(moves: readonly MediaPlacementMove[]): void;
81
+ /** Saves the boxes, the newest {@link MEDIA_LAYOUT_SAVED_LIMIT} of them (the last in the map's order). */
82
+ SavePipRects(rects: ReadonlyMap<string, MediaPipRect>): void;
83
+ /**
84
+ * Whether the user hid their self-view: shown when nothing usable is saved, the store is not ready, or the host keeps
85
+ * no self-view setting (it named no key for it).
86
+ */
87
+ LoadSelfViewHidden(): boolean;
88
+ /** Saves whether the user hid their self-view; nothing, for a host that keeps no self-view setting. */
89
+ SaveSelfViewHidden(hidden: boolean): void;
90
+ private read;
91
+ private write;
92
+ }
93
+ //# sourceMappingURL=mediaLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediaLayout.d.ts","sourceRoot":"","sources":["../../src/media/mediaLayout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAoB,KAAK,YAAY,EAAuB,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE5G,oHAAoH;AACpH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EAAE,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,EAAE,CAExH;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAE5F;AAED,mHAAmH;AACnH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,kBAAkB,EAAE,CAaxF;AAED,yCAAyC;AACzC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAEpF;AAED,sHAAsH;AACtH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAKlI;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAmBvF;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAElF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE3E;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAE/D;AAED,sGAAsG;AACtG,MAAM,WAAW,mBAAmB;IAChC,qEAAqE;IACrE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC5C,gFAAgF;IAChF,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACzD;AAED,yFAAyF;AACzF,MAAM,WAAW,eAAe;IAC5B,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,iHAAiH;IACjH,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,iGAAiG;AACjG,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;GAKG;AACH,qBAAa,gBAAgB;IAMrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANzB;;;OAGG;gBAEkB,KAAK,EAAE,MAAM,mBAAmB,EAChC,IAAI,EAAE,eAAe;IAG1C,2FAA2F;IACpF,SAAS,IAAI,kBAAkB,EAAE;IAIxC,6FAA6F;IACtF,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC;IAIhD,4EAA4E;IACrE,SAAS,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG,IAAI;IAI5D,0GAA0G;IACnG,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI;IAInE;;;OAGG;IACI,kBAAkB,IAAI,OAAO;IAKpC,uGAAuG;IAChG,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAOhD,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,KAAK;CAOhB"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * @fileoverview MEDIA LAYOUT: the user's layout of a media stage as a host keeps and saves it.
3
+ * - **Moves.** One move per surface, oldest first: the list `LayoutMediaStage` and `ResolveSurfacePlacements` read.
4
+ * {@link RecordPlacementMove} keeps it that way, and {@link WithoutStageMoves} hands the stage back to its default.
5
+ * - **Boxes.** Where the user put each picture-in-picture box, by surface key, as fractions of the stage.
6
+ * - **Self-view.** Whether the user hid their own tile, for a host that lets them (the meeting room).
7
+ *
8
+ * The realtime call keeps its channels' layout this way and the meeting room its participants' tiles, so both save the
9
+ * same form ({@link SerializePlacementMoves}, {@link SerializePipRects}, {@link SerializeSelfViewHidden}) and read it
10
+ * back tolerantly, through {@link MediaLayoutPrefs} under each host's own keys.
11
+ *
12
+ * @module @memberjunction/ai-realtime-client/media
13
+ */
14
+ import { MEDIA_PLACEMENTS } from './model.js';
15
+ /** Adds a move at the end, dropping the surface's earlier move, so the list keeps one move per surface in order. */
16
+ export function RecordPlacementMove(moves, move) {
17
+ return [...moves.filter((existing) => existing.SurfaceKey !== move.SurfaceKey), move];
18
+ }
19
+ /**
20
+ * The moves without any move to the stage: whatever the user put there goes back to its default. The stage goes to the
21
+ * newest move there, so a host whose "unpin" should hand the stage back to its default, not to an earlier move, drops
22
+ * the stage moves before recording the next one.
23
+ */
24
+ export function WithoutStageMoves(moves) {
25
+ return moves.filter((move) => move.Placement !== 'stage');
26
+ }
27
+ /** Reads saved moves. Tolerant: a value that is not a list reads as no moves, and a malformed entry is skipped. */
28
+ export function ParsePlacementMoves(raw) {
29
+ if (!raw) {
30
+ return [];
31
+ }
32
+ try {
33
+ const parsed = JSON.parse(raw);
34
+ if (!Array.isArray(parsed)) {
35
+ return [];
36
+ }
37
+ return parsed.filter(isMove).map((move) => ({ SurfaceKey: move.SurfaceKey, Placement: move.Placement }));
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ }
43
+ /** The saved form of a list of moves. */
44
+ export function SerializePlacementMoves(moves) {
45
+ return JSON.stringify(moves.map((move) => ({ SurfaceKey: move.SurfaceKey, Placement: move.Placement })));
46
+ }
47
+ /** Adds a box at the end, dropping the surface's earlier box, so the boxes the user moved most recently come last. */
48
+ export function RecordPipRect(rects, key, rect) {
49
+ const next = new Map(rects);
50
+ next.delete(key);
51
+ next.set(key, rect);
52
+ return next;
53
+ }
54
+ /**
55
+ * Reads saved picture-in-picture boxes, by surface key. Tolerant: a value that is not an object reads as none, and a box
56
+ * that is not four fractions with a size is skipped.
57
+ */
58
+ export function ParsePipRects(raw) {
59
+ const rects = new Map();
60
+ if (!raw) {
61
+ return rects;
62
+ }
63
+ try {
64
+ const parsed = JSON.parse(raw);
65
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
66
+ return rects;
67
+ }
68
+ for (const [key, value] of Object.entries(parsed)) {
69
+ if (isPipRect(value)) {
70
+ rects.set(key, { X: value.X, Y: value.Y, W: value.W, H: value.H });
71
+ }
72
+ }
73
+ return rects;
74
+ }
75
+ catch {
76
+ return rects;
77
+ }
78
+ }
79
+ /** The saved form of picture-in-picture boxes, by surface key. */
80
+ export function SerializePipRects(rects) {
81
+ return JSON.stringify(Object.fromEntries([...rects].map(([key, rect]) => [key, { X: rect.X, Y: rect.Y, W: rect.W, H: rect.H }])));
82
+ }
83
+ /**
84
+ * Reads whether the user hid their self-view. Tolerant: only a saved `true` hides it; nothing saved, `false` or anything
85
+ * else shows it.
86
+ */
87
+ export function ParseSelfViewHidden(raw) {
88
+ return raw === SerializeSelfViewHidden(true);
89
+ }
90
+ /** The saved form of whether the user hid their self-view: JSON `true` or `false`. */
91
+ export function SerializeSelfViewHidden(hidden) {
92
+ return JSON.stringify(hidden);
93
+ }
94
+ /** How many moves, and how many boxes, a saved layout keeps: the newest. The oldest go first. */
95
+ export const MEDIA_LAYOUT_SAVED_LIMIT = 50;
96
+ /**
97
+ * A host's saved layout: its moves, its picture-in-picture boxes and whether the user hid their self-view, in a per-user
98
+ * settings store under the host's own keys. Reading treats a store that is not ready (it throws) as nothing saved;
99
+ * writing skips one, or one that fails to save, so the layout then lasts for this session only. Writing keeps the newest
100
+ * {@link MEDIA_LAYOUT_SAVED_LIMIT} moves and boxes.
101
+ */
102
+ export class MediaLayoutPrefs {
103
+ /**
104
+ * @param store The settings store, looked up each time it is used, since a host's store may be ready only later.
105
+ * @param keys Where this host's layout goes.
106
+ */
107
+ constructor(store, keys) {
108
+ this.store = store;
109
+ this.keys = keys;
110
+ }
111
+ /** The saved moves, oldest first; none when nothing is saved or the store is not ready. */
112
+ LoadMoves() {
113
+ return ParsePlacementMoves(this.read(this.keys.Moves));
114
+ }
115
+ /** The saved boxes, by surface key; none when nothing is saved or the store is not ready. */
116
+ LoadPipRects() {
117
+ return ParsePipRects(this.read(this.keys.PipRects));
118
+ }
119
+ /** Saves the moves, the newest {@link MEDIA_LAYOUT_SAVED_LIMIT} of them. */
120
+ SaveMoves(moves) {
121
+ this.write(this.keys.Moves, SerializePlacementMoves(moves.slice(-MEDIA_LAYOUT_SAVED_LIMIT)));
122
+ }
123
+ /** Saves the boxes, the newest {@link MEDIA_LAYOUT_SAVED_LIMIT} of them (the last in the map's order). */
124
+ SavePipRects(rects) {
125
+ this.write(this.keys.PipRects, SerializePipRects(new Map([...rects].slice(-MEDIA_LAYOUT_SAVED_LIMIT))));
126
+ }
127
+ /**
128
+ * Whether the user hid their self-view: shown when nothing usable is saved, the store is not ready, or the host keeps
129
+ * no self-view setting (it named no key for it).
130
+ */
131
+ LoadSelfViewHidden() {
132
+ const key = this.keys.SelfViewHidden;
133
+ return key !== undefined && ParseSelfViewHidden(this.read(key));
134
+ }
135
+ /** Saves whether the user hid their self-view; nothing, for a host that keeps no self-view setting. */
136
+ SaveSelfViewHidden(hidden) {
137
+ const key = this.keys.SelfViewHidden;
138
+ if (key !== undefined) {
139
+ this.write(key, SerializeSelfViewHidden(hidden));
140
+ }
141
+ }
142
+ read(key) {
143
+ try {
144
+ return this.store().GetSetting(key);
145
+ }
146
+ catch {
147
+ return undefined;
148
+ }
149
+ }
150
+ write(key, value) {
151
+ try {
152
+ this.store().SetSettingDebounced(key, value);
153
+ }
154
+ catch {
155
+ // The store is not ready, or failed to save: the layout lasts for this session only.
156
+ }
157
+ }
158
+ }
159
+ function isMove(value) {
160
+ return (typeof value === 'object' &&
161
+ value !== null &&
162
+ 'SurfaceKey' in value &&
163
+ typeof value.SurfaceKey === 'string' &&
164
+ 'Placement' in value &&
165
+ isPlacement(value.Placement));
166
+ }
167
+ function isPlacement(value) {
168
+ return typeof value === 'string' && MEDIA_PLACEMENTS.includes(value);
169
+ }
170
+ function isPipRect(value) {
171
+ return (typeof value === 'object' &&
172
+ value !== null &&
173
+ 'X' in value &&
174
+ 'Y' in value &&
175
+ 'W' in value &&
176
+ 'H' in value &&
177
+ isFraction(value.X) &&
178
+ isFraction(value.Y) &&
179
+ isFraction(value.W) &&
180
+ isFraction(value.H) &&
181
+ value.W > 0 &&
182
+ value.H > 0);
183
+ }
184
+ function isFraction(value) {
185
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1;
186
+ }
187
+ //# sourceMappingURL=mediaLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediaLayout.js","sourceRoot":"","sources":["../../src/media/mediaLayout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview MEDIA PREVIEW: the user's camera and microphone before they join a call or a meeting. It gives a
3
+ * camera to see themselves in, a microphone level, the devices to choose from, and what they chose, for a lobby such
4
+ * as `mj-camera-check` to show. It drives an {@link ILocalMediaController} (the browser's `LocalMediaController`, or a
5
+ * fake in tests) and a microphone meter, owns both, and releases them when disposed, so the call or the meeting opens
6
+ * the chosen devices itself.
7
+ *
8
+ * - **What starts.** The microphone starts on and the camera off, unless the options say otherwise.
9
+ * - **Devices.** Picking a device moves a live kind to it at once; a kind that is off starts on it later. Until the
10
+ * user picks, the choice is the device in use, else the first one listed.
11
+ * - **Failures.** A kind that fails to start is turned off in the choices, with the reason in its state.
12
+ *
13
+ * The realtime call checks the camera inside the call instead (`RealtimeCaptures`), since its stream carries on into
14
+ * the call; this preview is for a lobby whose devices are released before joining.
15
+ *
16
+ * @module @memberjunction/ai-realtime-client/media
17
+ */
18
+ import { type Observable } from 'rxjs';
19
+ import { type IRealtimeAudioMeter } from '../audio/audioMeter.js';
20
+ import type { ILocalMediaController } from './localMediaController.js';
21
+ import type { LocalTrackState, MediaDevice, MediaDeviceSelection, MediaVideoSource } from './model.js';
22
+ /** What the user chose in the preview, for the call or the meeting to open. */
23
+ export interface MediaPreviewChoices {
24
+ MicrophoneOn: boolean;
25
+ CameraOn: boolean;
26
+ /** The microphone to use, or `null` when none is listed. */
27
+ MicrophoneDeviceID: string | null;
28
+ /** The camera to use, or `null` when none is listed. */
29
+ CameraDeviceID: string | null;
30
+ }
31
+ /** How a preview starts. */
32
+ export interface MediaPreviewOptions {
33
+ /** Whether the microphone starts on. Defaults to `true`. */
34
+ MicrophoneOn?: boolean;
35
+ /** Whether the camera starts on. Defaults to `false`. */
36
+ CameraOn?: boolean;
37
+ /** Meters the microphone's stream. Defaults to `RealtimeAudioMeter.ForMicStream`; a test passes its own. */
38
+ MeterFor?: (stream: MediaStream) => IRealtimeAudioMeter | null;
39
+ }
40
+ /** The preview now: what a lobby shows. */
41
+ export interface MediaPreviewState {
42
+ /** The camera to show, while it is on. The same object across device switches. */
43
+ CameraSource: MediaVideoSource | null;
44
+ /** The microphones and cameras to choose from. */
45
+ Devices: readonly MediaDevice[];
46
+ /** What the user chose. */
47
+ Choices: MediaPreviewChoices;
48
+ /** The camera's capture, for a failure to show. */
49
+ Camera: LocalTrackState;
50
+ /** The microphone's capture, for a failure to show. */
51
+ Microphone: LocalTrackState;
52
+ }
53
+ /** The user's camera and microphone in a lobby: see the file header. */
54
+ export declare class MediaPreview {
55
+ private readonly controller;
56
+ private readonly state;
57
+ private readonly watch;
58
+ private readonly meterFor;
59
+ private meter;
60
+ private cameraSource;
61
+ private choices;
62
+ /**
63
+ * @param controller The camera and microphone. The preview owns it from here, and disposes it with itself.
64
+ * @param options What starts, and how the microphone is metered.
65
+ */
66
+ constructor(controller: ILocalMediaController, options?: MediaPreviewOptions);
67
+ /** The preview now. */
68
+ get State(): MediaPreviewState;
69
+ /** The preview, now and on every change. Completes on {@link Dispose}. */
70
+ get State$(): Observable<MediaPreviewState>;
71
+ /** The microphone's level now, 0 to 1; 0 while it is off or cannot be metered. Pass it to a meter as its reader. */
72
+ readonly ReadMicrophoneLevel: () => number;
73
+ /** Starts the kinds the user starts with, then lists the devices (their names appear once access is allowed). */
74
+ Start(): Promise<void>;
75
+ /** Turns the camera on or off. */
76
+ SetCameraOn(on: boolean): Promise<void>;
77
+ /** Turns the microphone on or off. */
78
+ SetMicrophoneOn(on: boolean): Promise<void>;
79
+ /** Picks a device. A live kind moves to it at once; a kind that is off starts on it later. Speakers are ignored. */
80
+ SelectDevice(selection: MediaDeviceSelection): Promise<void>;
81
+ /** Stops the camera and the microphone, closes the meter, disposes the controller, and completes {@link State$}. */
82
+ Dispose(): void;
83
+ private setOn;
84
+ /** Starts a kind on the chosen device; a failure turns it off in the choices. */
85
+ private startKind;
86
+ private stopKind;
87
+ private remeter;
88
+ private closeMeter;
89
+ /** Publishes the state; after {@link Dispose} the completed subject ignores it. */
90
+ private publish;
91
+ /** The state from the controller's: the choices fall back to the device in use, else the first one listed. */
92
+ private snapshot;
93
+ }
94
+ //# sourceMappingURL=mediaPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediaPreview.d.ts","sourceRoot":"","sources":["../../src/media/mediaPreview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAmB,KAAK,UAAU,EAAqB,MAAM,MAAM,CAAC;AAC3E,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAmC,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAErI,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wDAAwD;IACxD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,4BAA4B;AAC5B,MAAM,WAAW,mBAAmB;IAChC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,mBAAmB,GAAG,IAAI,CAAC;CAClE;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAC9B,kFAAkF;IAClF,YAAY,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtC,kDAAkD;IAClD,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,2BAA2B;IAC3B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,mDAAmD;IACnD,MAAM,EAAE,eAAe,CAAC;IACxB,uDAAuD;IACvD,UAAU,EAAE,eAAe,CAAC;CAC/B;AAED,wEAAwE;AACxE,qBAAa,YAAY;IAajB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAZ/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsD;IAC/E,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,OAAO,CAAsB;IAErC;;;OAGG;gBAEkB,UAAU,EAAE,qBAAqB,EAClD,OAAO,GAAE,mBAAwB;IAQrC,uBAAuB;IACvB,IAAW,KAAK,IAAI,iBAAiB,CAEpC;IAED,0EAA0E;IAC1E,IAAW,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAEjD;IAED,oHAAoH;IACpH,SAAgB,mBAAmB,QAAO,MAAM,CAA6B;IAE7E,iHAAiH;IACpG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWnC,kCAAkC;IACrB,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,sCAAsC;IACzB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,oHAAoH;IACvG,YAAY,CAAC,SAAS,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBzE,oHAAoH;IAC7G,OAAO,IAAI,IAAI;YAQR,KAAK;IAUnB,iFAAiF;YACnE,SAAS;IAcvB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,UAAU;IAKlB,mFAAmF;IACnF,OAAO,CAAC,OAAO;IAIf,8GAA8G;IAC9G,OAAO,CAAC,QAAQ;CAenB"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * @fileoverview MEDIA PREVIEW: the user's camera and microphone before they join a call or a meeting. It gives a
3
+ * camera to see themselves in, a microphone level, the devices to choose from, and what they chose, for a lobby such
4
+ * as `mj-camera-check` to show. It drives an {@link ILocalMediaController} (the browser's `LocalMediaController`, or a
5
+ * fake in tests) and a microphone meter, owns both, and releases them when disposed, so the call or the meeting opens
6
+ * the chosen devices itself.
7
+ *
8
+ * - **What starts.** The microphone starts on and the camera off, unless the options say otherwise.
9
+ * - **Devices.** Picking a device moves a live kind to it at once; a kind that is off starts on it later. Until the
10
+ * user picks, the choice is the device in use, else the first one listed.
11
+ * - **Failures.** A kind that fails to start is turned off in the choices, with the reason in its state.
12
+ *
13
+ * The realtime call checks the camera inside the call instead (`RealtimeCaptures`), since its stream carries on into
14
+ * the call; this preview is for a lobby whose devices are released before joining.
15
+ *
16
+ * @module @memberjunction/ai-realtime-client/media
17
+ */
18
+ import { BehaviorSubject } from 'rxjs';
19
+ import { RealtimeAudioMeter } from '../audio/audioMeter.js';
20
+ /** The user's camera and microphone in a lobby: see the file header. */
21
+ export class MediaPreview {
22
+ /**
23
+ * @param controller The camera and microphone. The preview owns it from here, and disposes it with itself.
24
+ * @param options What starts, and how the microphone is metered.
25
+ */
26
+ constructor(controller, options = {}) {
27
+ this.controller = controller;
28
+ this.meter = null;
29
+ this.cameraSource = null;
30
+ /** The microphone's level now, 0 to 1; 0 while it is off or cannot be metered. Pass it to a meter as its reader. */
31
+ this.ReadMicrophoneLevel = () => this.meter?.Level() ?? 0;
32
+ this.meterFor = options.MeterFor ?? ((stream) => RealtimeAudioMeter.ForMicStream(stream));
33
+ this.choices = { MicrophoneOn: options.MicrophoneOn ?? true, CameraOn: options.CameraOn ?? false, MicrophoneDeviceID: null, CameraDeviceID: null };
34
+ this.state = new BehaviorSubject(this.snapshot(controller.State));
35
+ this.watch = controller.State$.subscribe((media) => this.publish(media));
36
+ }
37
+ /** The preview now. */
38
+ get State() {
39
+ return this.state.value;
40
+ }
41
+ /** The preview, now and on every change. Completes on {@link Dispose}. */
42
+ get State$() {
43
+ return this.state.asObservable();
44
+ }
45
+ /** Starts the kinds the user starts with, then lists the devices (their names appear once access is allowed). */
46
+ async Start() {
47
+ if (this.choices.MicrophoneOn) {
48
+ await this.startKind('microphone');
49
+ }
50
+ if (this.choices.CameraOn) {
51
+ await this.startKind('camera');
52
+ }
53
+ await this.controller.RefreshDevices();
54
+ this.publish(this.controller.State);
55
+ }
56
+ /** Turns the camera on or off. */
57
+ async SetCameraOn(on) {
58
+ await this.setOn('camera', on);
59
+ }
60
+ /** Turns the microphone on or off. */
61
+ async SetMicrophoneOn(on) {
62
+ await this.setOn('microphone', on);
63
+ }
64
+ /** Picks a device. A live kind moves to it at once; a kind that is off starts on it later. Speakers are ignored. */
65
+ async SelectDevice(selection) {
66
+ if (selection.Kind === 'speaker') {
67
+ return;
68
+ }
69
+ const kind = selection.Kind;
70
+ this.choices = kind === 'camera' ? { ...this.choices, CameraDeviceID: selection.DeviceID } : { ...this.choices, MicrophoneDeviceID: selection.DeviceID };
71
+ if (trackOf(this.controller.State, kind).Status === 'on') {
72
+ const result = await this.controller.SwitchDevice(kind, selection.DeviceID);
73
+ // The meter listens to a copy of the old track, so it follows the switch only when rebuilt.
74
+ if (kind === 'microphone' && result.Status === 'started') {
75
+ this.remeter(result.Stream);
76
+ }
77
+ }
78
+ this.publish(this.controller.State);
79
+ }
80
+ /** Stops the camera and the microphone, closes the meter, disposes the controller, and completes {@link State$}. */
81
+ Dispose() {
82
+ this.watch.unsubscribe();
83
+ this.closeMeter();
84
+ this.cameraSource = null;
85
+ this.controller.Dispose();
86
+ this.state.complete();
87
+ }
88
+ async setOn(kind, on) {
89
+ this.choices = kind === 'camera' ? { ...this.choices, CameraOn: on } : { ...this.choices, MicrophoneOn: on };
90
+ if (on) {
91
+ await this.startKind(kind);
92
+ }
93
+ else {
94
+ this.stopKind(kind);
95
+ }
96
+ this.publish(this.controller.State);
97
+ }
98
+ /** Starts a kind on the chosen device; a failure turns it off in the choices. */
99
+ async startKind(kind) {
100
+ const deviceId = kind === 'camera' ? this.choices.CameraDeviceID : this.choices.MicrophoneDeviceID;
101
+ const result = await this.controller.Start(kind, deviceId ?? undefined);
102
+ if (result.Status !== 'started') {
103
+ this.choices = kind === 'camera' ? { ...this.choices, CameraOn: false } : { ...this.choices, MicrophoneOn: false };
104
+ return;
105
+ }
106
+ if (kind === 'camera') {
107
+ this.cameraSource = { Kind: 'stream', Stream: result.Stream };
108
+ }
109
+ else {
110
+ this.remeter(result.Stream);
111
+ }
112
+ }
113
+ stopKind(kind) {
114
+ this.controller.Stop(kind);
115
+ if (kind === 'camera') {
116
+ this.cameraSource = null;
117
+ }
118
+ else {
119
+ this.closeMeter();
120
+ }
121
+ }
122
+ remeter(stream) {
123
+ this.closeMeter();
124
+ this.meter = this.meterFor(stream);
125
+ }
126
+ closeMeter() {
127
+ this.meter?.Close();
128
+ this.meter = null;
129
+ }
130
+ /** Publishes the state; after {@link Dispose} the completed subject ignores it. */
131
+ publish(media) {
132
+ this.state.next(this.snapshot(media));
133
+ }
134
+ /** The state from the controller's: the choices fall back to the device in use, else the first one listed. */
135
+ snapshot(media) {
136
+ const devices = media.Devices;
137
+ const firstOf = (kind) => devices.find((d) => d.Kind === kind)?.DeviceID ?? null;
138
+ return {
139
+ CameraSource: this.cameraSource,
140
+ Devices: devices,
141
+ Choices: {
142
+ ...this.choices,
143
+ MicrophoneDeviceID: this.choices.MicrophoneDeviceID ?? media.Microphone.DeviceID ?? firstOf('microphone'),
144
+ CameraDeviceID: this.choices.CameraDeviceID ?? media.Camera.DeviceID ?? firstOf('camera'),
145
+ },
146
+ Camera: media.Camera,
147
+ Microphone: media.Microphone,
148
+ };
149
+ }
150
+ }
151
+ function trackOf(media, kind) {
152
+ return kind === 'camera' ? media.Camera : media.Microphone;
153
+ }
154
+ //# sourceMappingURL=mediaPreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediaPreview.js","sourceRoot":"","sources":["../../src/media/mediaPreview.ts"],"names":[],"mappings":"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"}