@flow-player/software-decoder-ffmpeg 0.8.0 → 0.10.0
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.
|
@@ -40,6 +40,7 @@ var FfmpegSimdSoftwareDecoder = class {
|
|
|
40
40
|
#acquireFrameBuffers;
|
|
41
41
|
#viewFrames;
|
|
42
42
|
#pendingTimestamps = [];
|
|
43
|
+
/** 创建实例并立即构建原生解码句柄(尚未配置 codec,configure 前不可 decode)。 */
|
|
43
44
|
constructor(module, options) {
|
|
44
45
|
this.#module = module;
|
|
45
46
|
this.#onFrame = options.onFrame;
|
|
@@ -59,6 +60,18 @@ var FfmpegSimdSoftwareDecoder = class {
|
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* 配置(或重配)解码器:把 codec 私有数据(HEVC extradata/hvcC)交给 ffdecoder。
|
|
65
|
+
* 可在已配置状态下再次调用——重配先释放旧句柄并降回未配置,再全新重建,
|
|
66
|
+
* 保证与全新 configure 的状态一致;重配中重建失败时状态一致回落,原始错误
|
|
67
|
+
* 原样上抛。
|
|
68
|
+
*
|
|
69
|
+
* @param config.codec 目标编码,本 adapter 仅支持 "h265"(HEVC)
|
|
70
|
+
* @param config.description codec 私有数据;缺省按空 description 配置
|
|
71
|
+
* @throws 已 close 后调用("decoder closed")
|
|
72
|
+
* @throws codec 不是 "h265"(`FFmpeg SIMD adapter only supports h265, got ...`)
|
|
73
|
+
* @throws ffdecoder setCodec 失败(`FFmpeg setCodec failed: <非零码>`)
|
|
74
|
+
*/
|
|
62
75
|
configure(config) {
|
|
63
76
|
if (this.#closed) throw new Error("decoder closed");
|
|
64
77
|
if (config.codec !== "h265") {
|
|
@@ -77,6 +90,20 @@ var FfmpegSimdSoftwareDecoder = class {
|
|
|
77
90
|
if (result !== 0) throw new Error(`FFmpeg setCodec failed: ${result}`);
|
|
78
91
|
this.#configured = true;
|
|
79
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* 解码一个视频包;FFmpeg 解出帧时在本次调用内同步触发 onFrame(可能一次
|
|
95
|
+
* 吐出积压的有序帧,也可能本次无输出)。
|
|
96
|
+
*
|
|
97
|
+
* 输出帧时间戳取毫秒,回退链为 `timestampMs ?? ptsMs ?? timestamp / 1000`:
|
|
98
|
+
* 优先用包携带的 timestampMs;缺失时回退 ptsMs(同为毫秒);再缺失则以
|
|
99
|
+
* timestamp / 1000 兜底(timestamp 为微秒,与 WebCodecs EncodedVideoChunk
|
|
100
|
+
* 的 timestamp 同单位)。回退结果与 timestamp 原值一起进入 pending 队列,
|
|
101
|
+
* 输出帧按"最早 pending"取出,保证 B 帧重排下时间戳与画面对齐。
|
|
102
|
+
*
|
|
103
|
+
* @throws 已 close("decoder closed")或尚未 configure("decoder is not configured")
|
|
104
|
+
* @throws ffdecoder 输入暂存分配失败 / 解码失败(非零返回码);错误先经
|
|
105
|
+
* onError 回调上报,再原样抛给调用方
|
|
106
|
+
*/
|
|
80
107
|
decode(packet) {
|
|
81
108
|
if (this.#closed) throw new Error("decoder closed");
|
|
82
109
|
const decoder = this.#decoder;
|
|
@@ -109,8 +136,22 @@ var FfmpegSimdSoftwareDecoder = class {
|
|
|
109
136
|
}
|
|
110
137
|
return decoder.decode(payload, timestamp);
|
|
111
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* 有意的 no-op:协议里的 flush 面向"内部有异步输出队列"的解码器,而本 adapter
|
|
141
|
+
* 的帧在 decode() 调用内同步经 draw 回调吐出,flush 时没有可排空的在途输出;
|
|
142
|
+
* ffdecoder embind 句柄也没有 EOF/drain 原语(仅 setCodec/seek/clear/delete),
|
|
143
|
+
* 无法把 FFmpeg 内部帧缓冲显式排空。runtime 侧真正的解码器清理由 configure
|
|
144
|
+
* 全新重建句柄完成(seek 路径刻意不走 reset/flush,见 browser-runtime
|
|
145
|
+
* software-decode-controller 的 avcodec_flush_buffers 约束),故这里保持空实现。
|
|
146
|
+
*/
|
|
112
147
|
flush() {
|
|
113
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* 软复位:丢弃 pending 时间戳并调用 ffdecoder seek() 清空解码器内部状态
|
|
151
|
+
* (不丢 codec 配置,之后可直接继续 decode);已 close 时静默返回(幂等)。
|
|
152
|
+
*
|
|
153
|
+
* @throws ffdecoder seek 失败时抛 `FFmpeg seek failed: <非零码>`
|
|
154
|
+
*/
|
|
114
155
|
reset() {
|
|
115
156
|
if (this.#closed) return;
|
|
116
157
|
this.#pendingTimestamps = [];
|
|
@@ -119,6 +160,11 @@ var FfmpegSimdSoftwareDecoder = class {
|
|
|
119
160
|
const result = decoder.seek();
|
|
120
161
|
if (result !== 0) throw new Error(`FFmpeg seek failed: ${result}`);
|
|
121
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* 终止解码器:置 closed 标记(此后 configure/decode 抛错)并释放原生句柄。
|
|
165
|
+
* 幂等:重复调用无副作用;句柄已随 wasm abort/重建失败失效时 teardown 也
|
|
166
|
+
* 不会抛错(见 #releaseNativeDecoder)。
|
|
167
|
+
*/
|
|
122
168
|
close() {
|
|
123
169
|
if (this.#closed) return;
|
|
124
170
|
this.#closed = true;
|
|
@@ -196,8 +242,13 @@ var FfmpegSimdSoftwareDecoder = class {
|
|
|
196
242
|
}
|
|
197
243
|
return output;
|
|
198
244
|
}
|
|
199
|
-
|
|
200
|
-
|
|
245
|
+
/**
|
|
246
|
+
* 返回 ffdecoder WASM 线性内存当前字节数(HEAPU8.buffer.byteLength)。
|
|
247
|
+
* decode worker 每秒读取该值上报 decoderWasmMemoryPeakMb;ffdecoder WASM 堆
|
|
248
|
+
* 只涨不缩(ALLOW_MEMORY_GROWTH),缺失此方法的观测盲区会掩盖堆增长。
|
|
249
|
+
* 协议签名保留 null(对齐 SoftwareVideoDecoder.getWasmMemoryBytes),当前实现
|
|
250
|
+
* 恒返回数值。
|
|
251
|
+
*/
|
|
201
252
|
getWasmMemoryBytes() {
|
|
202
253
|
return this.#module.HEAPU8.buffer.byteLength;
|
|
203
254
|
}
|
|
@@ -236,17 +287,29 @@ function resolveSoftwareDecoderWasmUrl(options) {
|
|
|
236
287
|
return explicit;
|
|
237
288
|
}
|
|
238
289
|
async function instantiateFfmpegWasm(url, imports) {
|
|
290
|
+
let streamingError;
|
|
291
|
+
let streamingFailed = false;
|
|
239
292
|
if (typeof WebAssembly.instantiateStreaming === "function") {
|
|
240
293
|
try {
|
|
241
|
-
const { instance
|
|
242
|
-
return
|
|
243
|
-
} catch {
|
|
294
|
+
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), imports);
|
|
295
|
+
return instance;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
streamingError = error;
|
|
298
|
+
streamingFailed = true;
|
|
244
299
|
}
|
|
245
300
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
301
|
+
try {
|
|
302
|
+
const response = await fetch(url);
|
|
303
|
+
const bytes = await response.arrayBuffer();
|
|
304
|
+
const { instance } = await WebAssembly.instantiate(bytes, imports);
|
|
305
|
+
return instance;
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (!streamingFailed) throw error;
|
|
308
|
+
throw new Error(
|
|
309
|
+
`FFmpeg wasm arrayBuffer fallback failed: ${String(error)}; instantiateStreaming first failed: ${String(streamingError)}`,
|
|
310
|
+
{ cause: streamingError }
|
|
311
|
+
);
|
|
312
|
+
}
|
|
250
313
|
}
|
|
251
314
|
async function loadFfmpegSimdModule(options) {
|
|
252
315
|
const { glueText: glueText2 } = await loadFfmpegAssets();
|
package/dist/index.d.ts
CHANGED
|
@@ -24,16 +24,59 @@
|
|
|
24
24
|
* 传参,runtime R4 起使用);未提供时 adapter 报错提示传入
|
|
25
25
|
* `softwareDecoderWasmUrl`。
|
|
26
26
|
*/
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* 软解 adapter ESM 模块产物 URL(对应 FlowPlayerRuntimeOptions.softwareDecoderUrl)。
|
|
29
|
+
* runtime 仅在 decodeMode 非 "webcodecs" 时经该 URL 动态 import 模块(default 导出
|
|
30
|
+
* 为 `createSoftwareVideoDecoder` 工厂)。值为 `new URL(..., import.meta.url)`
|
|
31
|
+
* 字面量推导的绝对地址,由 consumer bundler 静态扫描改写为哈希资产。
|
|
32
|
+
*/
|
|
28
33
|
export declare const softwareDecoderUrl: string;
|
|
29
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 软解 decode worker 产物 URL(对应 FlowPlayerRuntimeOptions.softwareDecoderWorkerUrl)。
|
|
36
|
+
* runtime 以 module worker 拉起,worker 内部托管软解 adapter 与帧缓冲池,把解码
|
|
37
|
+
* 移出主线程。值为 `new URL(..., import.meta.url)` 字面量推导的绝对地址。
|
|
38
|
+
*/
|
|
30
39
|
export declare const softwareDecoderWorkerUrl: string;
|
|
31
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* 软解 wasm 产物 URL(对应 FlowPlayerRuntimeOptions.softwareDecoderWasmUrl)。
|
|
42
|
+
* SIMD 版 ffdecoder(FFmpeg→WASM)二进制,adapter 经该 URL fetch 并实例化
|
|
43
|
+
* WASM(instantiateStreaming,MIME 不支持时回退 arrayBuffer)。值为
|
|
44
|
+
* `new URL(..., import.meta.url)` 字面量推导的绝对地址。
|
|
45
|
+
*/
|
|
32
46
|
export declare const softwareDecoderWasmUrl: string;
|
|
47
|
+
/**
|
|
48
|
+
* 软解后端对接字段集合:展开进 `FlowPlayerRuntimeOptions`(即
|
|
49
|
+
* `FlowLivePlayerOptions` / `FlowVodPlayerOptions` 的公共软解可选字段)的同名字段。
|
|
50
|
+
*
|
|
51
|
+
* 三个值均为字符串形式的绝对 URL,由本入口模块内 `new URL(..., import.meta.url)`
|
|
52
|
+
* 字面量推导——入口被 consumer 静态 import,bundler 才能把三个相对路径识别为
|
|
53
|
+
* 静态资产并产出哈希产物地址;请勿在 adapter/worker 等动态加载模块内重新推导。
|
|
54
|
+
*
|
|
55
|
+
* 推荐不手工构造,直接使用 {@link ffmpegSoftwareDecoderOptions} 的返回值。
|
|
56
|
+
*/
|
|
33
57
|
export interface FfmpegSoftwareDecoderOptions {
|
|
58
|
+
/** 软解 adapter ESM 模块 URL(赋给 options.softwareDecoderUrl)。 */
|
|
34
59
|
softwareDecoderUrl: string;
|
|
60
|
+
/** 软解 decode worker URL(赋给 options.softwareDecoderWorkerUrl)。 */
|
|
35
61
|
softwareDecoderWorkerUrl: string;
|
|
62
|
+
/** 软解 wasm 资产 URL(赋给 options.softwareDecoderWasmUrl,最终传给 adapter 的 wasmUrl)。 */
|
|
36
63
|
softwareDecoderWasmUrl: string;
|
|
37
64
|
}
|
|
38
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* 汇总软解后端三个 URL 字段,返回可直接展开进播放器 options 的软解配置:
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* new FlowPlayerRuntime({
|
|
70
|
+
* decodeMode: "software",
|
|
71
|
+
* ...ffmpegSoftwareDecoderOptions(),
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* 同样适用于 `FlowLivePlayerOptions`(`flowVodPlayer` 之外的 Live 入口)与
|
|
76
|
+
* `FlowVodPlayerOptions`(`flowVodPlayer`):三个字段名与各 options 的软解
|
|
77
|
+
* 可选字段一一同名,展开即完成对接。
|
|
78
|
+
*
|
|
79
|
+
* @returns {@link FfmpegSoftwareDecoderOptions},三个值均为绝对 URL 字符串,
|
|
80
|
+
* 展开用法见包级 TSDoc 与上方示例。
|
|
81
|
+
*/
|
|
39
82
|
export declare function ffmpegSoftwareDecoderOptions(): FfmpegSoftwareDecoderOptions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flow-player/software-decoder-ffmpeg",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "FFmpeg (ffdecoder) WASM software decode backend for @flow-player/browser-runtime: prebuilt adapter + decode worker, drop-in HEVC software decoding.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"esm",
|
|
@@ -34,6 +34,6 @@
|
|
|
34
34
|
"tag": "latest"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@flow-player/browser-runtime": "^0.
|
|
37
|
+
"@flow-player/browser-runtime": "^0.10.0"
|
|
38
38
|
}
|
|
39
39
|
}
|