@spatialwalk/avatarkit-rtc 1.0.0-beta.6 → 1.0.0-beta.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarPlayer.d.ts","sourceRoot":"","sources":["../../src/core/AvatarPlayer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpD,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,wBAAwB,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,YAAY;IA4BvB;;;;;OAKG;gBAED,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,mBAAmB;IA2B/B;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAczD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBjC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB1D;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IASrC;;;;;;;;;;;;;;OAcG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBtC;;;;;OAKG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrC;;;OAGG;IACH,kBAAkB,IAAI,MAAM;IAI5B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,IAAI,OAAO;IAI1B;;;;OAIG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAa1C;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAyB3C;;;;;;;;;;;;;OAaG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAmIjC"}
1
+ {"version":3,"file":"AvatarPlayer.d.ts","sourceRoot":"","sources":["../../src/core/AvatarPlayer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpD,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,wBAAwB,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,YAAY;IAkCvB;;;;;OAKG;gBAED,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,mBAAmB;IA6B/B;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAczD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBjC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB1D;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IASrC;;;;;;;;;;;;;;OAcG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBtC;;;;;OAKG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrC;;;OAGG;IACH,kBAAkB,IAAI,MAAM;IAI5B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,IAAI,OAAO;IAI1B;;;;OAIG;IACH,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAa1C;;;;OAIG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAyB3C;;;;;;;;;;;;;OAaG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CA+JjC"}
package/dist/index10.js CHANGED
@@ -1,75 +1,108 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { createAnimationReceiverTransform } from "./index14.js";
4
5
  import { logger } from "./index7.js";
5
- class BaseProvider {
6
+ import WorkerWrapper from "./index15.js";
7
+ const DEFAULT_TRANSITION_START_FRAMES = 8;
8
+ const DEFAULT_TRANSITION_END_FRAMES = 12;
9
+ class VP8Extractor {
6
10
  constructor() {
7
11
  /** @internal */
8
- __publicField(this, "connectionState", "disconnected");
12
+ __publicField(this, "callbacks", null);
9
13
  /** @internal */
10
- __publicField(this, "eventHandlers", /* @__PURE__ */ new Map());
14
+ __publicField(this, "receiver", null);
15
+ /** @internal */
16
+ __publicField(this, "transform", null);
11
17
  }
12
18
  /**
13
- * Add event listener.
14
- * @param event - Event name
15
- * @param handler - Event handler
19
+ * Initialize the extractor with a receiver and callbacks.
20
+ * @param receiver - RTCRtpReceiver to extract data from
21
+ * @param callbacks - Callbacks to receive extracted data
22
+ * @internal
16
23
  */
17
- on(event, handler) {
18
- if (!this.eventHandlers.has(event)) {
19
- this.eventHandlers.set(event, /* @__PURE__ */ new Set());
24
+ async initialize(receiver, callbacks) {
25
+ if (this.receiver || this.transform) {
26
+ throw new Error("VP8Extractor already initialized");
20
27
  }
21
- this.eventHandlers.get(event).add(handler);
28
+ this.receiver = receiver;
29
+ this.callbacks = callbacks;
30
+ if (receiver.transform) {
31
+ return;
32
+ }
33
+ const worker = new WorkerWrapper();
34
+ const onEvent = (evt) => {
35
+ this.handleTransformEvent(evt);
36
+ };
37
+ this.transform = createAnimationReceiverTransform(worker, onEvent);
38
+ receiver.transform = this.transform;
22
39
  }
23
40
  /**
24
- * Remove event listener.
25
- * @param event - Event name
26
- * @param handler - Event handler
41
+ * Handle events from the animation transform.
42
+ * @internal
27
43
  */
28
- off(event, handler) {
29
- const handlers = this.eventHandlers.get(event);
30
- if (handlers) {
31
- handlers.delete(handler);
44
+ handleTransformEvent(evt) {
45
+ if (!this.callbacks) return;
46
+ if (evt.type === "metadata") {
47
+ this.callbacks.onStreamStats({
48
+ framesPerSec: evt.framesPerSec,
49
+ totalFrames: evt.totalFrames ?? 0,
50
+ framesSent: evt.framesSent ?? 0,
51
+ framesLost: evt.framesLost ?? 0,
52
+ framesRecovered: evt.framesRecovered ?? 0,
53
+ framesDropped: evt.framesDropped ?? 0,
54
+ framesOutOfOrder: evt.framesOutOfOrder ?? 0,
55
+ framesDuplicate: evt.framesDuplicate ?? 0,
56
+ lastRenderedSeq: evt.lastRenderedSeq ?? -1
57
+ });
58
+ } else if (evt.type === "transition") {
59
+ this.callbacks.onTransition(evt.protobufData, DEFAULT_TRANSITION_START_FRAMES);
60
+ } else if (evt.type === "transitionEnd") {
61
+ this.callbacks.onTransitionEnd(evt.protobufData, DEFAULT_TRANSITION_END_FRAMES);
62
+ } else if (evt.type === "animation") {
63
+ if (!evt.isIdle) {
64
+ this.callbacks.onAnimationData(evt.protobufData, {
65
+ frameSeq: evt.frameSeq,
66
+ isStart: evt.isStart,
67
+ isEnd: evt.isEnd,
68
+ isIdle: evt.isIdle,
69
+ isRecovered: evt.isRecovered
70
+ });
71
+ }
72
+ } else if (evt.type === "idleStart") {
73
+ this.callbacks.onIdleStart();
74
+ } else if (evt.type === "error") {
75
+ logger.error("VP8Extractor", "Error:", evt.error);
32
76
  }
33
77
  }
34
78
  /**
35
- * Emit event to all listeners.
36
- * @param event - Event name
37
- * @param args - Event arguments
79
+ * Check if this extractor is connected to the given receiver.
80
+ * @param receiver - RTCRtpReceiver to check
81
+ * @returns true if connected to this receiver
38
82
  * @internal
39
83
  */
40
- emit(event, ...args) {
41
- const handlers = this.eventHandlers.get(event);
42
- if (handlers) {
43
- handlers.forEach((handler) => {
44
- try {
45
- handler(...args);
46
- } catch (error) {
47
- logger.error(this.name, `Error in event handler for ${event}:`, error);
48
- }
49
- });
50
- }
84
+ isConnectedTo(receiver) {
85
+ return this.receiver === receiver;
51
86
  }
52
87
  /**
53
- * Update connection state and emit event.
54
- * @param state - New connection state
88
+ * Get the receiver this extractor is connected to.
89
+ * @returns The connected RTCRtpReceiver or null
55
90
  * @internal
56
91
  */
57
- setConnectionState(state) {
58
- if (this.connectionState !== state) {
59
- const prevState = this.connectionState;
60
- this.connectionState = state;
61
- if (state === "disconnected" || state === "failed") {
62
- logger.error(this.name, `Connection state: ${prevState} -> ${state}`);
63
- } else if (state === "reconnecting") {
64
- logger.warn(this.name, `Connection state: ${prevState} -> ${state}`);
65
- } else {
66
- logger.info(this.name, `Connection state: ${prevState} -> ${state}`);
67
- }
68
- this.emit("connection-state-changed", state);
69
- }
92
+ getReceiver() {
93
+ return this.receiver;
94
+ }
95
+ /**
96
+ * Dispose the extractor and release resources.
97
+ * @internal
98
+ */
99
+ dispose() {
100
+ this.receiver = null;
101
+ this.transform = null;
102
+ this.callbacks = null;
70
103
  }
71
104
  }
72
105
  export {
73
- BaseProvider
106
+ VP8Extractor
74
107
  };
75
108
  //# sourceMappingURL=index10.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index10.js","sources":["../src/providers/base/BaseProvider.ts"],"sourcesContent":["/**\n * Base Provider - Common implementation for RTC providers.\n *\n * This class provides common functionality that can be shared\n * across different provider implementations.\n *\n * @internal\n * @packageDocumentation\n */\n\nimport type { RTCProviderEvents, AnimationTrackCallbacks, AudioTrackCallbacks } from '../../core/types';\nimport { logger } from '../../utils';\n\n/**\n * Base class for RTC providers.\n * Provides common event handling and state management.\n */\nexport abstract class BaseProvider {\n /** Provider name identifier */\n abstract readonly name: string;\n\n /** @internal */\n protected connectionState: string = 'disconnected';\n /** @internal */\n protected eventHandlers: Map<string, Set<Function>> = new Map();\n\n /**\n * Connect to RTC server.\n * @param config - Connection configuration\n */\n abstract connect(config: import('../../types').RTCConnectionConfig): Promise<void>;\n\n /**\n * Disconnect from RTC server.\n */\n abstract disconnect(): Promise<void>;\n\n /**\n * Get current connection state.\n */\n abstract getConnectionState(): string;\n\n /**\n * Subscribe to animation track.\n * @param callbacks - Animation track callbacks\n * @internal\n */\n abstract subscribeAnimationTrack(callbacks: AnimationTrackCallbacks): Promise<void>;\n\n /**\n * Unsubscribe from animation track.\n * @internal\n */\n abstract unsubscribeAnimationTrack(): Promise<void>;\n\n /**\n * Subscribe to audio track.\n * @param callbacks - Audio track callbacks\n * @internal\n */\n abstract subscribeAudioTrack(callbacks: AudioTrackCallbacks): Promise<void>;\n\n /**\n * Unsubscribe from audio track.\n * @internal\n */\n abstract unsubscribeAudioTrack(): Promise<void>;\n\n /**\n * Publish local audio track.\n * @param track - MediaStreamTrack to publish\n */\n abstract publishAudioTrack(track: MediaStreamTrack): Promise<void>;\n\n /**\n * Unpublish audio track.\n */\n abstract unpublishAudioTrack(): Promise<void>;\n\n /**\n * Get the native RTC client object.\n * @returns The native client, or null if not connected\n */\n abstract getNativeClient(): unknown;\n\n /**\n * Add event listener.\n * @param event - Event name\n * @param handler - Event handler\n */\n on(event: string, handler: Function): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n }\n this.eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * Remove event listener.\n * @param event - Event name\n * @param handler - Event handler\n */\n off(event: string, handler: Function): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * Emit event to all listeners.\n * @param event - Event name\n * @param args - Event arguments\n * @internal\n */\n protected emit<K extends keyof RTCProviderEvents>(\n event: K,\n ...args: Parameters<RTCProviderEvents[K]>\n ): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (handler as any)(...args);\n } catch (error) {\n logger.error(this.name, `Error in event handler for ${event}:`, error);\n }\n });\n }\n }\n\n /**\n * Update connection state and emit event.\n * @param state - New connection state\n * @internal\n */\n protected setConnectionState(state: string): void {\n if (this.connectionState !== state) {\n const prevState = this.connectionState;\n this.connectionState = state;\n \n // Log connection state changes\n if (state === 'disconnected' || state === 'failed') {\n logger.error(this.name, `Connection state: ${prevState} -> ${state}`);\n } else if (state === 'reconnecting') {\n logger.warn(this.name, `Connection state: ${prevState} -> ${state}`);\n } else {\n logger.info(this.name, `Connection state: ${prevState} -> ${state}`);\n }\n \n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.emit('connection-state-changed', state as any);\n }\n }\n}\n"],"names":[],"mappings":";;;;AAiBO,MAAe,aAAa;AAAA,EAA5B;AAKK;AAAA,2CAA0B;AAE1B;AAAA,6DAAgD,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkE1D,GAAG,OAAe,SAAyB;AACzC,QAAI,CAAC,KAAK,cAAc,IAAI,KAAK,GAAG;AAClC,WAAK,cAAc,IAAI,OAAO,oBAAI,KAAK;AAAA,IACzC;AACA,SAAK,cAAc,IAAI,KAAK,EAAG,IAAI,OAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,OAAe,SAAyB;AAC1C,UAAM,WAAW,KAAK,cAAc,IAAI,KAAK;AAC7C,QAAI,UAAU;AACZ,eAAS,OAAO,OAAO;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,KACR,UACG,MACG;AACN,UAAM,WAAW,KAAK,cAAc,IAAI,KAAK;AAC7C,QAAI,UAAU;AACZ,eAAS,QAAQ,CAAC,YAAY;AAC5B,YAAI;AAED,kBAAgB,GAAG,IAAI;AAAA,QAC1B,SAAS,OAAO;AACd,iBAAO,MAAM,KAAK,MAAM,8BAA8B,KAAK,KAAK,KAAK;AAAA,QACvE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,mBAAmB,OAAqB;AAChD,QAAI,KAAK,oBAAoB,OAAO;AAClC,YAAM,YAAY,KAAK;AACvB,WAAK,kBAAkB;AAGvB,UAAI,UAAU,kBAAkB,UAAU,UAAU;AAClD,eAAO,MAAM,KAAK,MAAM,qBAAqB,SAAS,OAAO,KAAK,EAAE;AAAA,MACtE,WAAW,UAAU,gBAAgB;AACnC,eAAO,KAAK,KAAK,MAAM,qBAAqB,SAAS,OAAO,KAAK,EAAE;AAAA,MACrE,OAAO;AACL,eAAO,KAAK,KAAK,MAAM,qBAAqB,SAAS,OAAO,KAAK,EAAE;AAAA,MACrE;AAGA,WAAK,KAAK,4BAA4B,KAAY;AAAA,IACpD;AAAA,EACF;AACF;"}
1
+ {"version":3,"file":"index10.js","sources":["../src/providers/livekit/VP8Extractor.ts"],"sourcesContent":["/**\n * VP8 Data Extractor for LiveKit.\n *\n * Extracts animation data from VP8 video tracks using RTCRtpScriptTransform.\n *\n * @internal\n * @packageDocumentation\n */\n\nimport type { AnimationTrackCallbacks } from '../../core/types';\nimport type { AnimationTransformEvent } from './types';\nimport { createAnimationReceiverTransform } from './animation-transform';\nimport { logger } from '../../utils';\n\n// Import worker with inline - this bundles the worker code as a blob URL\nimport AnimationWorker from './animation-worker.ts?worker&inline';\n\n// Default transition frame counts (used when protocol doesn't specify)\nconst DEFAULT_TRANSITION_START_FRAMES = 8;\nconst DEFAULT_TRANSITION_END_FRAMES = 12;\n\n/**\n * VP8Extractor - Extracts animation data from VP8 video tracks.\n *\n * Uses RTCRtpScriptTransform and a Web Worker to parse VP8 frames\n * and extract embedded animation data.\n *\n * @internal\n */\nexport class VP8Extractor {\n /** @internal */\n private callbacks: AnimationTrackCallbacks | null = null;\n /** @internal */\n private receiver: RTCRtpReceiver | null = null;\n /** @internal */\n private transform: RTCRtpScriptTransform | null = null;\n\n /**\n * Initialize the extractor with a receiver and callbacks.\n * @param receiver - RTCRtpReceiver to extract data from\n * @param callbacks - Callbacks to receive extracted data\n * @internal\n */\n async initialize(\n receiver: RTCRtpReceiver,\n callbacks: AnimationTrackCallbacks\n ): Promise<void> {\n if (this.receiver || this.transform) {\n throw new Error('VP8Extractor already initialized');\n }\n\n this.receiver = receiver;\n this.callbacks = callbacks;\n\n // Check if transform is already set\n if (receiver.transform) {\n return;\n }\n\n // Create worker and transform\n const worker = new AnimationWorker();\n const onEvent = (evt: AnimationTransformEvent) => {\n this.handleTransformEvent(evt);\n };\n\n this.transform = createAnimationReceiverTransform(worker, onEvent);\n receiver.transform = this.transform;\n }\n\n /**\n * Handle events from the animation transform.\n * @internal\n */\n private handleTransformEvent(evt: AnimationTransformEvent): void {\n if (!this.callbacks) return;\n\n if (evt.type === 'metadata') {\n this.callbacks.onStreamStats({\n framesPerSec: evt.framesPerSec,\n totalFrames: evt.totalFrames ?? 0,\n framesSent: evt.framesSent ?? 0,\n framesLost: evt.framesLost ?? 0,\n framesRecovered: evt.framesRecovered ?? 0,\n framesDropped: evt.framesDropped ?? 0,\n framesOutOfOrder: evt.framesOutOfOrder ?? 0,\n framesDuplicate: evt.framesDuplicate ?? 0,\n lastRenderedSeq: evt.lastRenderedSeq ?? -1,\n });\n } else if (evt.type === 'transition') {\n this.callbacks.onTransition(evt.protobufData, DEFAULT_TRANSITION_START_FRAMES);\n } else if (evt.type === 'transitionEnd') {\n this.callbacks.onTransitionEnd(evt.protobufData, DEFAULT_TRANSITION_END_FRAMES);\n } else if (evt.type === 'animation') {\n if (!evt.isIdle) {\n this.callbacks.onAnimationData(evt.protobufData, {\n frameSeq: evt.frameSeq,\n isStart: evt.isStart,\n isEnd: evt.isEnd,\n isIdle: evt.isIdle,\n isRecovered: evt.isRecovered,\n });\n }\n } else if (evt.type === 'idleStart') {\n this.callbacks.onIdleStart();\n } else if (evt.type === 'error') {\n logger.error('VP8Extractor', 'Error:', evt.error);\n }\n }\n\n /**\n * Check if this extractor is connected to the given receiver.\n * @param receiver - RTCRtpReceiver to check\n * @returns true if connected to this receiver\n * @internal\n */\n isConnectedTo(receiver: RTCRtpReceiver): boolean {\n return this.receiver === receiver;\n }\n\n /**\n * Get the receiver this extractor is connected to.\n * @returns The connected RTCRtpReceiver or null\n * @internal\n */\n getReceiver(): RTCRtpReceiver | null {\n return this.receiver;\n }\n\n /**\n * Dispose the extractor and release resources.\n * @internal\n */\n dispose(): void {\n this.receiver = null;\n this.transform = null;\n this.callbacks = null;\n }\n}\n"],"names":["AnimationWorker"],"mappings":";;;;;;AAkBA,MAAM,kCAAkC;AACxC,MAAM,gCAAgC;AAU/B,MAAM,aAAa;AAAA,EAAnB;AAEG;AAAA,qCAA4C;AAE5C;AAAA,oCAAkC;AAElC;AAAA,qCAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQlD,MAAM,WACJ,UACA,WACe;AACf,QAAI,KAAK,YAAY,KAAK,WAAW;AACnC,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAEA,SAAK,WAAW;AAChB,SAAK,YAAY;AAGjB,QAAI,SAAS,WAAW;AACtB;AAAA,IACF;AAGA,UAAM,SAAS,IAAIA,cAAA;AACnB,UAAM,UAAU,CAAC,QAAiC;AAChD,WAAK,qBAAqB,GAAG;AAAA,IAC/B;AAEA,SAAK,YAAY,iCAAiC,QAAQ,OAAO;AACjE,aAAS,YAAY,KAAK;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqB,KAAoC;AAC/D,QAAI,CAAC,KAAK,UAAW;AAErB,QAAI,IAAI,SAAS,YAAY;AAC3B,WAAK,UAAU,cAAc;AAAA,QAC3B,cAAc,IAAI;AAAA,QAClB,aAAa,IAAI,eAAe;AAAA,QAChC,YAAY,IAAI,cAAc;AAAA,QAC9B,YAAY,IAAI,cAAc;AAAA,QAC9B,iBAAiB,IAAI,mBAAmB;AAAA,QACxC,eAAe,IAAI,iBAAiB;AAAA,QACpC,kBAAkB,IAAI,oBAAoB;AAAA,QAC1C,iBAAiB,IAAI,mBAAmB;AAAA,QACxC,iBAAiB,IAAI,mBAAmB;AAAA,MAAA,CACzC;AAAA,IACH,WAAW,IAAI,SAAS,cAAc;AACpC,WAAK,UAAU,aAAa,IAAI,cAAc,+BAA+B;AAAA,IAC/E,WAAW,IAAI,SAAS,iBAAiB;AACvC,WAAK,UAAU,gBAAgB,IAAI,cAAc,6BAA6B;AAAA,IAChF,WAAW,IAAI,SAAS,aAAa;AACnC,UAAI,CAAC,IAAI,QAAQ;AACf,aAAK,UAAU,gBAAgB,IAAI,cAAc;AAAA,UAC/C,UAAU,IAAI;AAAA,UACd,SAAS,IAAI;AAAA,UACb,OAAO,IAAI;AAAA,UACX,QAAQ,IAAI;AAAA,UACZ,aAAa,IAAI;AAAA,QAAA,CAClB;AAAA,MACH;AAAA,IACF,WAAW,IAAI,SAAS,aAAa;AACnC,WAAK,UAAU,YAAA;AAAA,IACjB,WAAW,IAAI,SAAS,SAAS;AAC/B,aAAO,MAAM,gBAAgB,UAAU,IAAI,KAAK;AAAA,IAClD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,cAAc,UAAmC;AAC/C,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAqC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAgB;AACd,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,YAAY;AAAA,EACnB;AACF;"}
package/dist/index11.js CHANGED
@@ -1,390 +1,18 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { logger } from "./index7.js";
5
- const PacketFlags = {
6
- /** Idle packet - no payload */
7
- Idle: 1,
8
- /** First frame of animation session */
9
- Start: 2,
10
- /** Last frame of animation session */
11
- End: 4,
12
- /** Payload is zlib compressed */
13
- Gzipped: 8,
14
- /** Transition from idle to animation */
15
- Transition: 16,
16
- /** Transition from animation back to idle */
17
- TransitionEnd: 32
18
- };
19
- const SEI_HEADER_SIZE = 5;
20
- const FRAME_SEQ_SIZE = 4;
21
- const DEFAULT_TRANSITION_START_FRAMES = 8;
22
- const DEFAULT_TRANSITION_END_FRAMES = 12;
23
- class SEIExtractor {
24
- constructor() {
25
- /** @internal */
26
- __publicField(this, "callbacks", null);
27
- // Session state tracking
28
- /** @internal */
29
- __publicField(this, "lastWasIdle", true);
30
- // Transition state tracking (to handle consecutive packets)
31
- /** @internal */
32
- __publicField(this, "isInStartTransition", false);
33
- /** @internal */
34
- __publicField(this, "isInEndTransition", false);
35
- // Transition frame counts
36
- /** @internal */
37
- __publicField(this, "transitionStartFrameCount");
38
- /** @internal */
39
- __publicField(this, "transitionEndFrameCount");
40
- // Statistics tracking
41
- /** @internal */
42
- __publicField(this, "totalFrameCount", 0);
43
- /** @internal */
44
- __publicField(this, "intervalFrameCount", 0);
45
- /** @internal */
46
- __publicField(this, "lastStatsTime", 0);
47
- /** @internal */
48
- __publicField(this, "statsInterval", null);
49
- // Debug logging
50
- /** @internal */
51
- __publicField(this, "debugLogging", false);
52
- this.transitionStartFrameCount = DEFAULT_TRANSITION_START_FRAMES;
53
- this.transitionEndFrameCount = DEFAULT_TRANSITION_END_FRAMES;
54
- }
55
- /**
56
- * Initialize the extractor with callbacks.
57
- * @param callbacks - Callbacks to receive extracted data
58
- * @internal
59
- */
60
- initialize(callbacks) {
61
- this.callbacks = callbacks;
62
- this.startStatsInterval();
63
- }
64
- /**
65
- * Enable or disable debug logging.
66
- * @param enabled - Whether to enable debug logging
67
- * @internal
68
- */
69
- setDebugLogging(enabled) {
70
- this.debugLogging = enabled;
71
- }
72
- /**
73
- * Handle SEI data from Agora.
74
- * Wraps async processing to not block the event handler.
75
- * @param seiData - Raw SEI payload
76
- * @internal
77
- */
78
- handleSEIData(seiData) {
79
- this.processSEIData(seiData).catch((error) => {
80
- logger.error("SEIExtractor", "Failed to process SEI data:", error);
81
- this.logDebugInfo(seiData);
82
- });
83
- }
84
- /**
85
- * Convert EBSP (Encapsulated Byte Sequence Payload) to RBSP (Raw Byte Sequence Payload).
86
- * This removes H.264 emulation prevention bytes (0x03) inserted after 00 00 sequences.
87
- *
88
- * H.264 spec: When encoding, 0x03 is inserted after 00 00 to prevent start code emulation:
89
- * - 00 00 00 → 00 00 03 00
90
- * - 00 00 01 → 00 00 03 01
91
- * - 00 00 02 → 00 00 03 02
92
- * - 00 00 03 → 00 00 03 03
93
- *
94
- * @param data - EBSP data
95
- * @returns RBSP data
96
- * @internal
97
- */
98
- ebspToRbsp(data) {
99
- if (data.length === 0) {
100
- return data;
101
- }
102
- const result = [];
103
- let zeroCount = 0;
104
- for (let i = 0; i < data.length; i++) {
105
- const byte = data[i];
106
- if (zeroCount >= 2 && byte === 3) {
107
- if (i + 1 < data.length) {
108
- const nextByte = data[i + 1];
109
- if (nextByte <= 3) {
110
- zeroCount = 0;
111
- continue;
112
- }
113
- }
114
- }
115
- if (byte === 0) {
116
- zeroCount++;
117
- } else {
118
- zeroCount = 0;
119
- }
120
- result.push(byte);
121
- }
122
- return new Uint8Array(result);
123
- }
124
- /**
125
- * Unescape zero bytes that were escaped by the server.
126
- * Escape scheme: 0x00 0xFF -> 0x00, 0xFF 0xFF -> 0xFF
127
- * This reverses the escaping done to avoid H.264 emulation prevention issues.
128
- *
129
- * @param data - Escaped data
130
- * @returns Unescaped data
131
- * @internal
132
- */
133
- unescapeZeroBytes(data) {
134
- const result = [];
135
- let i = 0;
136
- while (i < data.length) {
137
- if (i + 1 < data.length && data[i] === 0 && data[i + 1] === 255) {
138
- result.push(0);
139
- i += 2;
140
- } else if (i + 1 < data.length && data[i] === 255 && data[i + 1] === 255) {
141
- result.push(255);
142
- i += 2;
143
- } else {
144
- result.push(data[i]);
145
- i++;
146
- }
147
- }
148
- return new Uint8Array(result);
149
- }
150
- /**
151
- * Decompress zlib data using DecompressionStream API.
152
- * Uses 'deflate' format (not gzip) to avoid H.264 emulation prevention issues
153
- * with gzip's mtime field containing 00 00 00 00.
154
- *
155
- * @param data - Zlib compressed data
156
- * @returns Decompressed data
157
- * @internal
158
- */
159
- async decompressZlib(data) {
160
- const ds = new DecompressionStream("deflate");
161
- const writer = ds.writable.getWriter();
162
- const copy = new Uint8Array(data);
163
- writer.write(copy);
164
- writer.close();
165
- const reader = ds.readable.getReader();
166
- const chunks = [];
167
- let totalLength = 0;
168
- while (true) {
169
- const { done, value } = await reader.read();
170
- if (done) break;
171
- chunks.push(value);
172
- totalLength += value.length;
173
- }
174
- const result = new Uint8Array(totalLength);
175
- let offset = 0;
176
- for (const chunk of chunks) {
177
- result.set(chunk, offset);
178
- offset += chunk.length;
179
- }
180
- return result;
181
- }
182
- /**
183
- * Process SEI data asynchronously.
184
- * @param seiData - Raw SEI payload
185
- * @internal
186
- */
187
- async processSEIData(seiData) {
188
- var _a, _b, _c, _d;
189
- if (!this.callbacks) {
190
- return;
191
- }
192
- this.totalFrameCount++;
193
- this.intervalFrameCount++;
194
- const cleanedData = this.ebspToRbsp(seiData);
195
- if (cleanedData.length < SEI_HEADER_SIZE) {
196
- logger.warn("SEIExtractor", `SEI data too short: ${cleanedData.length}`);
197
- return;
198
- }
199
- const flags = cleanedData[0];
200
- const msgLen = new DataView(
201
- cleanedData.buffer,
202
- cleanedData.byteOffset + 1,
203
- 4
204
- ).getUint32(0, true);
205
- if (this.debugLogging && (this.totalFrameCount <= 5 || this.totalFrameCount % 50 === 0)) {
206
- logger.info(
207
- "SEIExtractor",
208
- `SEI packet #${this.totalFrameCount}: flags=0x${flags.toString(16)}, msgLen=${msgLen}, cleanedLen=${cleanedData.length}`
209
- );
210
- }
211
- const isIdle = (flags & PacketFlags.Idle) !== 0;
212
- if (isIdle || msgLen === 0) {
213
- if (!this.lastWasIdle) {
214
- this.lastWasIdle = true;
215
- this.isInStartTransition = false;
216
- this.isInEndTransition = false;
217
- this.callbacks.onIdleStart();
218
- }
219
- return;
220
- }
221
- const rawPayload = cleanedData.slice(SEI_HEADER_SIZE);
222
- const compressedPayload = this.unescapeZeroBytes(rawPayload);
223
- let payload;
224
- if ((flags & PacketFlags.Gzipped) !== 0) {
225
- payload = await this.decompressZlib(compressedPayload);
226
- } else {
227
- payload = compressedPayload;
228
- }
229
- const isTransition = (flags & PacketFlags.Transition) !== 0;
230
- const isTransitionEnd = (flags & PacketFlags.TransitionEnd) !== 0;
231
- const isStart = (flags & PacketFlags.Start) !== 0;
232
- const isEnd = (flags & PacketFlags.End) !== 0;
233
- let protobufData;
234
- let frameSeq;
235
- if (isTransition || isTransitionEnd) {
236
- protobufData = new Uint8Array(payload).buffer;
237
- } else {
238
- if (payload.length < FRAME_SEQ_SIZE) {
239
- logger.warn("SEIExtractor", "Payload too short for frame sequence");
240
- return;
241
- }
242
- frameSeq = new DataView(payload.buffer, payload.byteOffset, 4).getUint32(
243
- 0,
244
- true
245
- );
246
- if (this.debugLogging && (this.totalFrameCount <= 5 || this.totalFrameCount % 50 === 0)) {
247
- logger.info("SEIExtractor", `Animation frame seq=${frameSeq}`);
248
- }
249
- protobufData = new Uint8Array(
250
- payload.subarray(FRAME_SEQ_SIZE)
251
- ).buffer;
252
- }
253
- if (isTransition) {
254
- if (!this.isInStartTransition) {
255
- this.isInStartTransition = true;
256
- this.isInEndTransition = false;
257
- if (this.debugLogging) {
258
- logger.info("SEIExtractor", "Transition START (first packet)");
259
- }
260
- this.callbacks.onTransition(protobufData, this.transitionStartFrameCount);
261
- }
262
- return;
263
- }
264
- if (isTransitionEnd) {
265
- if (!this.isInEndTransition) {
266
- this.isInEndTransition = true;
267
- this.isInStartTransition = false;
268
- if (this.debugLogging) {
269
- logger.info("SEIExtractor", "Transition END (first packet)");
270
- }
271
- this.callbacks.onTransitionEnd(protobufData, this.transitionEndFrameCount);
272
- }
273
- return;
274
- }
275
- this.isInStartTransition = false;
276
- this.isInEndTransition = false;
277
- const isFirstFrame = this.lastWasIdle || isStart;
278
- this.lastWasIdle = false;
279
- if (isFirstFrame) {
280
- if (this.debugLogging) {
281
- logger.info("SEIExtractor", "Session start");
282
- }
283
- (_b = (_a = this.callbacks).onSessionStart) == null ? void 0 : _b.call(_a);
284
- }
285
- this.callbacks.onAnimationData(protobufData, {
286
- frameSeq,
287
- isStart: isFirstFrame,
288
- isEnd,
289
- isIdle: false,
290
- isRecovered: false
291
- // Agora handles reliability internally
292
- });
293
- if (isEnd) {
294
- if (this.debugLogging) {
295
- logger.info("SEIExtractor", "Session end");
296
- }
297
- (_d = (_c = this.callbacks).onSessionEnd) == null ? void 0 : _d.call(_c);
298
- }
299
- }
300
- /**
301
- * Log debug information for failed SEI processing.
302
- * @internal
303
- */
304
- logDebugInfo(seiData) {
305
- const totalLen = seiData.length;
306
- logger.error("SEIExtractor", `Total SEI length: ${totalLen}`);
307
- if (seiData.length >= 20) {
308
- const hexBytes = Array.from(seiData.slice(0, 20)).map((b) => b.toString(16).padStart(2, "0")).join(" ");
309
- logger.error("SEIExtractor", "First 20 bytes of SEI:", hexBytes);
310
- }
311
- if (seiData.length >= SEI_HEADER_SIZE) {
312
- const flags = seiData[0];
313
- const msgLen = new DataView(
314
- seiData.buffer,
315
- seiData.byteOffset + 1,
316
- 4
317
- ).getUint32(0, true);
318
- logger.error(
319
- "SEIExtractor",
320
- `SEI header: flags=0x${flags.toString(16)}, msgLen=${msgLen}`
321
- );
322
- }
323
- }
324
- /**
325
- * Start the statistics reporting interval.
326
- * @internal
327
- */
328
- startStatsInterval() {
329
- this.lastStatsTime = Date.now();
330
- this.statsInterval = setInterval(() => {
331
- const now = Date.now();
332
- const elapsedSeconds = (now - this.lastStatsTime) / 1e3;
333
- if (elapsedSeconds > 0 && this.callbacks) {
334
- const fps = this.intervalFrameCount / elapsedSeconds;
335
- const stats = {
336
- framesPerSec: Math.round(fps * 10) / 10,
337
- totalFrames: this.totalFrameCount,
338
- framesSent: this.totalFrameCount,
339
- framesLost: 0,
340
- framesRecovered: 0,
341
- framesDropped: 0,
342
- framesOutOfOrder: 0,
343
- framesDuplicate: 0,
344
- lastRenderedSeq: -1
345
- };
346
- this.callbacks.onStreamStats(stats);
347
- }
348
- this.intervalFrameCount = 0;
349
- this.lastStatsTime = now;
350
- }, 1e3);
351
- }
352
- /**
353
- * Stop the statistics reporting interval.
354
- * @internal
355
- */
356
- stopStatsInterval() {
357
- if (this.statsInterval) {
358
- clearInterval(this.statsInterval);
359
- this.statsInterval = null;
360
- }
361
- }
362
- /**
363
- * Reset session state.
364
- * Call this when reconnecting or starting a new session.
365
- * @internal
366
- */
367
- reset() {
368
- this.lastWasIdle = true;
369
- this.isInStartTransition = false;
370
- this.isInEndTransition = false;
371
- this.totalFrameCount = 0;
372
- this.intervalFrameCount = 0;
373
- this.lastStatsTime = Date.now();
374
- }
375
- /**
376
- * Dispose the extractor and release resources.
377
- * @internal
378
- */
379
- dispose() {
380
- this.stopStatsInterval();
381
- this.callbacks = null;
382
- this.lastWasIdle = true;
383
- this.isInStartTransition = false;
384
- this.isInEndTransition = false;
385
- }
1
+ function supportsScriptTransform() {
2
+ return typeof RTCRtpScriptTransform !== "undefined";
3
+ }
4
+ function supportsEncodedStreams() {
5
+ var _a;
6
+ return typeof ((_a = RTCRtpSender == null ? void 0 : RTCRtpSender.prototype) == null ? void 0 : _a.createEncodedStreams) === "function";
7
+ }
8
+ function getInsertableStreamsMethod() {
9
+ if (supportsScriptTransform()) return "scriptTransform";
10
+ if (supportsEncodedStreams()) return "encodedStreams";
11
+ return null;
386
12
  }
387
13
  export {
388
- SEIExtractor
14
+ getInsertableStreamsMethod,
15
+ supportsEncodedStreams,
16
+ supportsScriptTransform
389
17
  };
390
18
  //# sourceMappingURL=index11.js.map