@give-tech/ec-player 0.0.1-beta.32 → 0.0.1-beta.33

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.
@@ -6,9 +6,14 @@ import { WASMLoader } from './WASMLoader';
6
6
  export declare class H264Decoder {
7
7
  private wasmModule;
8
8
  private decoderContext;
9
+ private destroyed;
9
10
  constructor(wasmLoader: WASMLoader);
10
11
  init(): Promise<void>;
11
12
  decode(nalUnit: NALUnit): VideoFrame | null;
13
+ /**
14
+ * 销毁解码器,释放 WASM 资源
15
+ */
16
+ destroy(): void;
12
17
  private yuv420pToRgba;
13
18
  }
14
19
  //# sourceMappingURL=H264Decoder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"H264Decoder.d.ts","sourceRoot":"","sources":["../../src/decoder/H264Decoder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAA8B,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA8B;gBAExC,UAAU,EAAE,UAAU;IAI5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI;IA4C3C,OAAO,CAAC,aAAa;CAuCtB"}
1
+ {"version":3,"file":"H264Decoder.d.ts","sourceRoot":"","sources":["../../src/decoder/H264Decoder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAA8B,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,SAAS,CAAQ;gBAEb,UAAU,EAAE,UAAU;IAI5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI;IA4C3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAaf,OAAO,CAAC,aAAa;CAuCtB"}
@@ -13,9 +13,14 @@ export declare const HEVC_NAL_TYPE: {
13
13
  export declare class HEVCDecoder {
14
14
  private wasmModule;
15
15
  private decoderContext;
16
+ private destroyed;
16
17
  constructor(wasmLoader: WASMLoader);
17
18
  init(): Promise<void>;
18
19
  decode(nalUnit: NALUnit): VideoFrame | null;
20
+ /**
21
+ * 销毁解码器,释放 WASM 资源
22
+ */
23
+ destroy(): void;
19
24
  private yuv420pToRgba;
20
25
  }
21
26
  //# sourceMappingURL=HEVCDecoder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HEVCDecoder.d.ts","sourceRoot":"","sources":["../../src/decoder/HEVCDecoder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAA8B,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAGzC,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAA;AAEV,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA8B;gBAExC,UAAU,EAAE,UAAU;IAI5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAe3B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI;IA4C3C,OAAO,CAAC,aAAa;CAuCtB"}
1
+ {"version":3,"file":"HEVCDecoder.d.ts","sourceRoot":"","sources":["../../src/decoder/HEVCDecoder.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAA8B,UAAU,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAGzC,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAA;AAEV,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,SAAS,CAAQ;gBAEb,UAAU,EAAE,UAAU;IAI5B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAe3B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI;IA4C3C;;OAEG;IACH,OAAO,IAAI,IAAI;IAaf,OAAO,CAAC,aAAa;CAuCtB"}
@@ -1,10 +1,39 @@
1
1
  /**
2
- * WASM 加载器
2
+ * WASM 加载器(多模块缓存)
3
+ *
4
+ * 支持同时缓存多个 WASM 模块(如 SIMD 和非 SIMD 版本)
5
+ * 使用懒加载,按需加载所需模块
6
+ * 解决快速切换源时的竞态条件问题
3
7
  */
4
8
  import type { WASMModule } from '../types';
5
9
  export declare class WASMLoader {
6
- private module;
10
+ private localModule;
11
+ private localPath;
12
+ /**
13
+ * 加载 WASM 模块
14
+ */
7
15
  load(wasmPath: string): Promise<WASMModule>;
16
+ private doLoad;
8
17
  getModule(): WASMModule | null;
18
+ /**
19
+ * 预加载 WASM 模块(后台加载,不阻塞)
20
+ */
21
+ static preload(wasmPath: string): void;
22
+ /**
23
+ * 批量预加载
24
+ */
25
+ static preloadAll(wasmPaths: string[]): void;
26
+ /**
27
+ * 检查是否已加载
28
+ */
29
+ static isLoaded(wasmPath?: string): boolean;
30
+ /**
31
+ * 获取已缓存的路径
32
+ */
33
+ static getLoadedPaths(): string[];
34
+ /**
35
+ * 重置(仅用于测试)
36
+ */
37
+ static reset(): void;
9
38
  }
10
39
  //# sourceMappingURL=WASMLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WASMLoader.d.ts","sourceRoot":"","sources":["../../src/decoder/WASMLoader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA0B;IAElC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAmCjD,SAAS,IAAI,UAAU,GAAG,IAAI;CAG/B"}
1
+ {"version":3,"file":"WASMLoader.d.ts","sourceRoot":"","sources":["../../src/decoder/WASMLoader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAQ1C,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,SAAS,CAAsB;IAEvC;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAsCnC,MAAM;IAuCpB,SAAS,IAAI,UAAU,GAAG,IAAI;IAI9B;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWtC;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAI5C;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAO3C;;OAEG;IACH,MAAM,CAAC,cAAc,IAAI,MAAM,EAAE;IAIjC;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;CAIrB"}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  export { EcPlayerCore, type EcPlayerCoreConfig } from './player';
8
8
  export { EnvDetector, type EnvInfo, type EnvCapabilities } from './env';
9
+ export { WASMLoader } from './decoder/WASMLoader';
9
10
  export type { PlayerState as EcPlayerState, PlayerCallbacks as EcPlayerCallbacks } from './types';
10
11
  export { LogLevel, setLogLevel } from './utils';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAGhE,OAAO,EACL,WAAW,EACX,KAAK,OAAO,EACZ,KAAK,eAAe,EACrB,MAAM,OAAO,CAAA;AAGd,YAAY,EACV,WAAW,IAAI,aAAa,EAC5B,eAAe,IAAI,iBAAiB,EACrC,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAGhE,OAAO,EACL,WAAW,EACX,KAAK,OAAO,EACZ,KAAK,eAAe,EACrB,MAAM,OAAO,CAAA;AAGd,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAGjD,YAAY,EACV,WAAW,IAAI,aAAa,EAC5B,eAAe,IAAI,iBAAiB,EACrC,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -1,8 +1,43 @@
1
+ const moduleCache = /* @__PURE__ */ new Map();
2
+ const loadingPromises = /* @__PURE__ */ new Map();
1
3
  class WASMLoader {
2
4
  constructor() {
3
- this.module = null;
5
+ this.localModule = null;
6
+ this.localPath = null;
4
7
  }
8
+ /**
9
+ * 加载 WASM 模块
10
+ */
5
11
  async load(wasmPath) {
12
+ if (this.localModule && this.localPath === wasmPath) {
13
+ return this.localModule;
14
+ }
15
+ const cachedModule = moduleCache.get(wasmPath);
16
+ if (cachedModule) {
17
+ this.localModule = cachedModule;
18
+ this.localPath = wasmPath;
19
+ return cachedModule;
20
+ }
21
+ const loadingPromise = loadingPromises.get(wasmPath);
22
+ if (loadingPromise) {
23
+ const module = await loadingPromise;
24
+ this.localModule = module;
25
+ this.localPath = wasmPath;
26
+ return module;
27
+ }
28
+ const loadPromise = this.doLoad(wasmPath);
29
+ loadingPromises.set(wasmPath, loadPromise);
30
+ try {
31
+ const module = await loadPromise;
32
+ this.localModule = module;
33
+ this.localPath = wasmPath;
34
+ return module;
35
+ } catch (error) {
36
+ loadingPromises.delete(wasmPath);
37
+ throw error;
38
+ }
39
+ }
40
+ async doLoad(wasmPath) {
6
41
  return new Promise((resolve, reject) => {
7
42
  const script = document.createElement("script");
8
43
  script.src = wasmPath;
@@ -14,30 +49,75 @@ class WASMLoader {
14
49
  reject(new Error("createFFmpegDecoder not found"));
15
50
  return;
16
51
  }
17
- this.module = await createFFmpegDecoder();
18
- if (typeof this.module?._create_decoder !== "function") {
52
+ const module = await createFFmpegDecoder();
53
+ if (typeof module?._create_decoder !== "function") {
19
54
  reject(new Error("Module initialization failed"));
20
55
  return;
21
56
  }
22
- resolve(this.module);
57
+ moduleCache.set(wasmPath, module);
58
+ loadingPromises.delete(wasmPath);
59
+ resolve(module);
23
60
  } catch (e) {
61
+ loadingPromises.delete(wasmPath);
24
62
  reject(new Error(`Failed to initialize decoder: ${e.message}`));
25
63
  }
26
64
  };
27
65
  script.onerror = () => {
66
+ loadingPromises.delete(wasmPath);
28
67
  reject(new Error(`Failed to load script: ${wasmPath}`));
29
68
  };
30
69
  document.head.appendChild(script);
31
70
  });
32
71
  }
33
72
  getModule() {
34
- return this.module;
73
+ return this.localModule;
74
+ }
75
+ /**
76
+ * 预加载 WASM 模块(后台加载,不阻塞)
77
+ */
78
+ static preload(wasmPath) {
79
+ if (moduleCache.has(wasmPath) || loadingPromises.has(wasmPath)) {
80
+ return;
81
+ }
82
+ const loader = new WASMLoader();
83
+ loader.load(wasmPath).catch((err) => {
84
+ console.warn(`[WASMLoader] Preload failed for ${wasmPath}:`, err.message);
85
+ });
86
+ }
87
+ /**
88
+ * 批量预加载
89
+ */
90
+ static preloadAll(wasmPaths) {
91
+ wasmPaths.forEach((path) => this.preload(path));
92
+ }
93
+ /**
94
+ * 检查是否已加载
95
+ */
96
+ static isLoaded(wasmPath) {
97
+ if (wasmPath) {
98
+ return moduleCache.has(wasmPath);
99
+ }
100
+ return moduleCache.size > 0;
101
+ }
102
+ /**
103
+ * 获取已缓存的路径
104
+ */
105
+ static getLoadedPaths() {
106
+ return Array.from(moduleCache.keys());
107
+ }
108
+ /**
109
+ * 重置(仅用于测试)
110
+ */
111
+ static reset() {
112
+ moduleCache.clear();
113
+ loadingPromises.clear();
35
114
  }
36
115
  }
37
116
  class H264Decoder {
38
117
  constructor(wasmLoader) {
39
118
  this.wasmModule = null;
40
119
  this.decoderContext = null;
120
+ this.destroyed = false;
41
121
  this.wasmModule = wasmLoader.getModule();
42
122
  }
43
123
  async init() {
@@ -50,7 +130,7 @@ class H264Decoder {
50
130
  }
51
131
  }
52
132
  decode(nalUnit) {
53
- if (this.decoderContext === null || !this.wasmModule) {
133
+ if (this.destroyed || this.decoderContext === null || !this.wasmModule) {
54
134
  return null;
55
135
  }
56
136
  const dataPtr = this.wasmModule._malloc(nalUnit.size);
@@ -87,6 +167,18 @@ class H264Decoder {
87
167
  }
88
168
  return null;
89
169
  }
170
+ /**
171
+ * 销毁解码器,释放 WASM 资源
172
+ */
173
+ destroy() {
174
+ if (this.destroyed) return;
175
+ this.destroyed = true;
176
+ if (this.decoderContext !== null && this.wasmModule?._destroy_decoder) {
177
+ this.wasmModule._destroy_decoder(this.decoderContext);
178
+ }
179
+ this.decoderContext = null;
180
+ this.wasmModule = null;
181
+ }
90
182
  yuv420pToRgba(yPtr, uPtr, vPtr, width, height, yLineSize, uLineSize, vLineSize) {
91
183
  const rgba = new Uint8Array(width * height * 4);
92
184
  for (let y = 0; y < height; y++) {
@@ -907,6 +999,7 @@ class HEVCDecoder {
907
999
  constructor(wasmLoader) {
908
1000
  this.wasmModule = null;
909
1001
  this.decoderContext = null;
1002
+ this.destroyed = false;
910
1003
  this.wasmModule = wasmLoader.getModule();
911
1004
  }
912
1005
  async init() {
@@ -920,7 +1013,7 @@ class HEVCDecoder {
920
1013
  console.log("[HEVCDecoder] Initialized with codec_id=173");
921
1014
  }
922
1015
  decode(nalUnit) {
923
- if (this.decoderContext === null || !this.wasmModule) {
1016
+ if (this.destroyed || this.decoderContext === null || !this.wasmModule) {
924
1017
  return null;
925
1018
  }
926
1019
  const dataPtr = this.wasmModule._malloc(nalUnit.size);
@@ -957,6 +1050,18 @@ class HEVCDecoder {
957
1050
  }
958
1051
  return null;
959
1052
  }
1053
+ /**
1054
+ * 销毁解码器,释放 WASM 资源
1055
+ */
1056
+ destroy() {
1057
+ if (this.destroyed) return;
1058
+ this.destroyed = true;
1059
+ if (this.decoderContext !== null && this.wasmModule?._destroy_decoder) {
1060
+ this.wasmModule._destroy_decoder(this.decoderContext);
1061
+ }
1062
+ this.decoderContext = null;
1063
+ this.wasmModule = null;
1064
+ }
960
1065
  yuv420pToRgba(yPtr, uPtr, vPtr, width, height, yLineSize, uLineSize, vLineSize) {
961
1066
  const rgba = new Uint8Array(width * height * 4);
962
1067
  for (let y = 0; y < height; y++) {
@@ -3161,6 +3266,25 @@ class FLVPlayer extends BasePlayer {
3161
3266
  console.log("[FLVPlayer] Pausing live stream, keeping download running");
3162
3267
  }
3163
3268
  }
3269
+ /**
3270
+ * 销毁播放器,释放所有资源
3271
+ */
3272
+ destroy() {
3273
+ this.stopLiveDownload();
3274
+ if (this.h264Decoder) {
3275
+ this.h264Decoder.destroy();
3276
+ this.h264Decoder = null;
3277
+ }
3278
+ if (this.hevcDecoder) {
3279
+ this.hevcDecoder.destroy();
3280
+ this.hevcDecoder = null;
3281
+ }
3282
+ if (this.prefetcher) {
3283
+ this.prefetcher.reset();
3284
+ this.prefetcher = null;
3285
+ }
3286
+ super.destroy();
3287
+ }
3164
3288
  /**
3165
3289
  * 处理预取缓冲区数据(供预取器调用)
3166
3290
  */
@@ -4254,6 +4378,7 @@ export {
4254
4378
  EcPlayerCore,
4255
4379
  EnvDetector,
4256
4380
  LogLevel,
4381
+ WASMLoader,
4257
4382
  setLogLevel
4258
4383
  };
4259
4384
  //# sourceMappingURL=index.js.map