webcodecs-node 0.3.0 → 0.5.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.
- package/dist/__tests__/AudioData.test.js +2 -1
- package/dist/__tests__/AudioData.test.js.map +1 -1
- package/dist/__tests__/AudioDecoder.test.js +5 -5
- package/dist/__tests__/AudioDecoder.test.js.map +1 -1
- package/dist/__tests__/AudioEncoder.test.js +3 -3
- package/dist/__tests__/AudioEncoder.test.js.map +1 -1
- package/dist/__tests__/EncodedChunks.test.js +3 -2
- package/dist/__tests__/EncodedChunks.test.js.map +1 -1
- package/dist/__tests__/HardwareAcceleration.test.js +2 -1
- package/dist/__tests__/HardwareAcceleration.test.js.map +1 -1
- package/dist/__tests__/ImageDecoder.test.js +16 -45
- package/dist/__tests__/ImageDecoder.test.js.map +1 -1
- package/dist/__tests__/NodeAvDecoder.test.js +12 -5
- package/dist/__tests__/NodeAvDecoder.test.js.map +1 -1
- package/dist/__tests__/VideoDecoder.test.js +5 -5
- package/dist/__tests__/VideoDecoder.test.js.map +1 -1
- package/dist/__tests__/VideoEncoder.test.js +3 -3
- package/dist/__tests__/VideoEncoder.test.js.map +1 -1
- package/dist/__tests__/VideoFrame.test.js +4 -1
- package/dist/__tests__/VideoFrame.test.js.map +1 -1
- package/dist/backends/types.d.ts +6 -3
- package/dist/backends/types.d.ts.map +1 -1
- package/dist/backends/types.js +2 -2
- package/dist/backends/types.js.map +1 -1
- package/dist/codec-utils/audio-codecs.d.ts +60 -0
- package/dist/codec-utils/audio-codecs.d.ts.map +1 -0
- package/dist/codec-utils/audio-codecs.js +117 -0
- package/dist/codec-utils/audio-codecs.js.map +1 -0
- package/dist/codec-utils/formats.d.ts +42 -0
- package/dist/codec-utils/formats.d.ts.map +1 -0
- package/dist/codec-utils/formats.js +147 -0
- package/dist/codec-utils/formats.js.map +1 -0
- package/dist/codec-utils/index.d.ts +9 -0
- package/dist/codec-utils/index.d.ts.map +1 -0
- package/dist/codec-utils/index.js +12 -0
- package/dist/codec-utils/index.js.map +1 -0
- package/dist/codec-utils/types.d.ts +87 -0
- package/dist/codec-utils/types.d.ts.map +1 -0
- package/dist/codec-utils/types.js +10 -0
- package/dist/codec-utils/types.js.map +1 -0
- package/dist/containers/Demuxer.d.ts +114 -0
- package/dist/containers/Demuxer.d.ts.map +1 -0
- package/dist/containers/Demuxer.js +256 -0
- package/dist/containers/Demuxer.js.map +1 -0
- package/dist/containers/Muxer.d.ts +142 -0
- package/dist/containers/Muxer.d.ts.map +1 -0
- package/dist/containers/Muxer.js +295 -0
- package/dist/containers/Muxer.js.map +1 -0
- package/dist/containers/extract.d.ts +25 -0
- package/dist/containers/extract.d.ts.map +1 -0
- package/dist/containers/extract.js +64 -0
- package/dist/containers/extract.js.map +1 -0
- package/dist/containers/index.d.ts +40 -0
- package/dist/containers/index.d.ts.map +1 -0
- package/dist/containers/index.js +41 -0
- package/dist/containers/index.js.map +1 -0
- package/dist/containers/transcode.d.ts +138 -0
- package/dist/containers/transcode.d.ts.map +1 -0
- package/dist/containers/transcode.js +536 -0
- package/dist/containers/transcode.js.map +1 -0
- package/dist/core/AudioData.d.ts +4 -0
- package/dist/core/AudioData.d.ts.map +1 -1
- package/dist/core/AudioData.js +56 -4
- package/dist/core/AudioData.js.map +1 -1
- package/dist/core/EncodedAudioChunk.d.ts +1 -1
- package/dist/core/EncodedAudioChunk.d.ts.map +1 -1
- package/dist/core/EncodedAudioChunk.js +1 -1
- package/dist/core/EncodedAudioChunk.js.map +1 -1
- package/dist/core/EncodedVideoChunk.d.ts.map +1 -1
- package/dist/core/EncodedVideoChunk.js +3 -19
- package/dist/core/EncodedVideoChunk.js.map +1 -1
- package/dist/core/VideoFrame.d.ts +6 -1
- package/dist/core/VideoFrame.d.ts.map +1 -1
- package/dist/core/VideoFrame.js +130 -17
- package/dist/core/VideoFrame.js.map +1 -1
- package/dist/decoders/AudioDecoder.d.ts +8 -0
- package/dist/decoders/AudioDecoder.d.ts.map +1 -1
- package/dist/decoders/AudioDecoder.js +62 -32
- package/dist/decoders/AudioDecoder.js.map +1 -1
- package/dist/decoders/ImageDecoder.d.ts +0 -3
- package/dist/decoders/ImageDecoder.d.ts.map +1 -1
- package/dist/decoders/ImageDecoder.js +13 -225
- package/dist/decoders/ImageDecoder.js.map +1 -1
- package/dist/decoders/VideoDecoder.d.ts +8 -0
- package/dist/decoders/VideoDecoder.d.ts.map +1 -1
- package/dist/decoders/VideoDecoder.js +76 -46
- package/dist/decoders/VideoDecoder.js.map +1 -1
- package/dist/demos/demo-1080p-transcode.d.ts +8 -0
- package/dist/demos/demo-1080p-transcode.d.ts.map +1 -0
- package/dist/demos/demo-1080p-transcode.js +188 -0
- package/dist/demos/demo-1080p-transcode.js.map +1 -0
- package/dist/demos/demo-containers.d.ts +7 -0
- package/dist/demos/demo-containers.d.ts.map +1 -0
- package/dist/demos/demo-containers.js +140 -0
- package/dist/demos/demo-containers.js.map +1 -0
- package/dist/demos/demo-four-corners.js +4 -4
- package/dist/demos/demo-four-corners.js.map +1 -1
- package/dist/demos/demo-hwaccel-conversion.js +1 -1
- package/dist/demos/demo-hwaccel-conversion.js.map +1 -1
- package/dist/demos/demo-hwaccel.js +1 -1
- package/dist/demos/demo-hwaccel.js.map +1 -1
- package/dist/demos/demo-samples.js +4 -4
- package/dist/demos/demo-samples.js.map +1 -1
- package/dist/demos/demo-streaming.js +6 -6
- package/dist/demos/demo-streaming.js.map +1 -1
- package/dist/encoders/AudioEncoder.d.ts +8 -0
- package/dist/encoders/AudioEncoder.d.ts.map +1 -1
- package/dist/encoders/AudioEncoder.js +68 -26
- package/dist/encoders/AudioEncoder.js.map +1 -1
- package/dist/encoders/VideoEncoder.d.ts +8 -0
- package/dist/encoders/VideoEncoder.d.ts.map +1 -1
- package/dist/encoders/VideoEncoder.js +64 -13
- package/dist/encoders/VideoEncoder.js.map +1 -1
- package/dist/ffmpeg/index.d.ts +3 -2
- package/dist/ffmpeg/index.d.ts.map +1 -1
- package/dist/ffmpeg/index.js +3 -3
- package/dist/ffmpeg/index.js.map +1 -1
- package/dist/formats/conversions/batch-converter.d.ts +61 -0
- package/dist/formats/conversions/batch-converter.d.ts.map +1 -0
- package/dist/formats/conversions/batch-converter.js +274 -0
- package/dist/formats/conversions/batch-converter.js.map +1 -0
- package/dist/formats/conversions/frame-converter.d.ts.map +1 -1
- package/dist/formats/conversions/frame-converter.js +17 -10
- package/dist/formats/conversions/frame-converter.js.map +1 -1
- package/dist/formats/conversions/index.d.ts +1 -0
- package/dist/formats/conversions/index.d.ts.map +1 -1
- package/dist/formats/conversions/index.js +2 -0
- package/dist/formats/conversions/index.js.map +1 -1
- package/dist/hardware/decoder-args.d.ts +2 -7
- package/dist/hardware/decoder-args.d.ts.map +1 -1
- package/dist/hardware/decoder-args.js +2 -32
- package/dist/hardware/decoder-args.js.map +1 -1
- package/dist/hardware/encoder-args.d.ts +2 -11
- package/dist/hardware/encoder-args.d.ts.map +1 -1
- package/dist/hardware/encoder-args.js +2 -71
- package/dist/hardware/encoder-args.js.map +1 -1
- package/dist/hardware/index.d.ts +2 -2
- package/dist/hardware/index.d.ts.map +1 -1
- package/dist/hardware/index.js +2 -2
- package/dist/hardware/index.js.map +1 -1
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/mediabunny/FFmpegAudioDecoder.d.ts +3 -2
- package/dist/mediabunny/FFmpegAudioDecoder.d.ts.map +1 -1
- package/dist/mediabunny/FFmpegAudioDecoder.js +5 -8
- package/dist/mediabunny/FFmpegAudioDecoder.js.map +1 -1
- package/dist/mediabunny/FFmpegAudioEncoder.d.ts +3 -2
- package/dist/mediabunny/FFmpegAudioEncoder.d.ts.map +1 -1
- package/dist/mediabunny/FFmpegAudioEncoder.js +3 -2
- package/dist/mediabunny/FFmpegAudioEncoder.js.map +1 -1
- package/dist/mediabunny/FFmpegVideoDecoder.d.ts +4 -3
- package/dist/mediabunny/FFmpegVideoDecoder.d.ts.map +1 -1
- package/dist/mediabunny/FFmpegVideoDecoder.js +6 -9
- package/dist/mediabunny/FFmpegVideoDecoder.js.map +1 -1
- package/dist/mediabunny/FFmpegVideoEncoder.d.ts +4 -3
- package/dist/mediabunny/FFmpegVideoEncoder.d.ts.map +1 -1
- package/dist/mediabunny/FFmpegVideoEncoder.js +4 -3
- package/dist/mediabunny/FFmpegVideoEncoder.js.map +1 -1
- package/dist/node-av/NodeAvAudioDecoder.d.ts +2 -2
- package/dist/node-av/NodeAvAudioDecoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvAudioDecoder.js +63 -56
- package/dist/node-av/NodeAvAudioDecoder.js.map +1 -1
- package/dist/node-av/NodeAvAudioEncoder.d.ts +6 -0
- package/dist/node-av/NodeAvAudioEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvAudioEncoder.js +166 -45
- package/dist/node-av/NodeAvAudioEncoder.js.map +1 -1
- package/dist/node-av/NodeAvImageDecoder.d.ts +28 -9
- package/dist/node-av/NodeAvImageDecoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvImageDecoder.js +156 -37
- package/dist/node-av/NodeAvImageDecoder.js.map +1 -1
- package/dist/node-av/NodeAvVideoDecoder.d.ts +1 -1
- package/dist/node-av/NodeAvVideoDecoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvVideoDecoder.js +20 -16
- package/dist/node-av/NodeAvVideoDecoder.js.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.d.ts +12 -0
- package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.js +175 -63
- package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
- package/dist/polyfill.d.ts.map +1 -1
- package/dist/polyfill.js +11 -9
- package/dist/polyfill.js.map +1 -1
- package/dist/utils/aac.d.ts.map +1 -1
- package/dist/utils/aac.js +6 -0
- package/dist/utils/aac.js.map +1 -1
- package/dist/utils/buffer-pool.d.ts +109 -0
- package/dist/utils/buffer-pool.d.ts.map +1 -0
- package/dist/utils/buffer-pool.js +278 -0
- package/dist/utils/buffer-pool.js.map +1 -0
- package/dist/utils/codec-cache.d.ts +65 -0
- package/dist/utils/codec-cache.d.ts.map +1 -0
- package/dist/utils/codec-cache.js +116 -0
- package/dist/utils/codec-cache.js.map +1 -0
- package/dist/utils/errors.d.ts +62 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +84 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/hardware-pool.d.ts +88 -0
- package/dist/utils/hardware-pool.d.ts.map +1 -0
- package/dist/utils/hardware-pool.js +266 -0
- package/dist/utils/hardware-pool.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/timeout.d.ts +44 -0
- package/dist/utils/timeout.d.ts.map +1 -0
- package/dist/utils/timeout.js +73 -0
- package/dist/utils/timeout.js.map +1 -0
- package/dist/utils/type-guards.d.ts +11 -1
- package/dist/utils/type-guards.d.ts.map +1 -1
- package/dist/utils/type-guards.js.map +1 -1
- package/package.json +13 -35
|
@@ -6,6 +6,7 @@ import { WebCodecsEventTarget } from '../utils/event-target.js';
|
|
|
6
6
|
import { AudioData } from '../core/AudioData.js';
|
|
7
7
|
import type { AudioSampleFormat } from '../core/AudioData.js';
|
|
8
8
|
import { EncodedAudioChunk } from '../core/EncodedAudioChunk.js';
|
|
9
|
+
type EventHandler = ((event: Event) => void) | null;
|
|
9
10
|
export type CodecState = 'unconfigured' | 'configured' | 'closed';
|
|
10
11
|
export interface AudioDecoderConfig {
|
|
11
12
|
codec: string;
|
|
@@ -32,10 +33,16 @@ export declare class AudioDecoder extends WebCodecsEventTarget {
|
|
|
32
33
|
private _frameIndex;
|
|
33
34
|
private _outputFormat;
|
|
34
35
|
private _aacConfig;
|
|
36
|
+
private _ondequeue;
|
|
35
37
|
constructor(init: AudioDecoderInit);
|
|
36
38
|
get state(): CodecState;
|
|
37
39
|
get decodeQueueSize(): number;
|
|
40
|
+
/** Event handler called when decodeQueueSize decreases */
|
|
41
|
+
get ondequeue(): EventHandler;
|
|
42
|
+
set ondequeue(handler: EventHandler);
|
|
38
43
|
private _safeErrorCallback;
|
|
44
|
+
/** Fire the dequeue event (both EventTarget and ondequeue handler) */
|
|
45
|
+
private _fireDequeueEvent;
|
|
39
46
|
private _safeOutputCallback;
|
|
40
47
|
static isConfigSupported(config: AudioDecoderConfig): Promise<AudioDecoderSupport>;
|
|
41
48
|
configure(config: AudioDecoderConfig): void;
|
|
@@ -48,4 +55,5 @@ export declare class AudioDecoder extends WebCodecsEventTarget {
|
|
|
48
55
|
private _handleDecodedFrame;
|
|
49
56
|
private _parseAacDescription;
|
|
50
57
|
}
|
|
58
|
+
export {};
|
|
51
59
|
//# sourceMappingURL=AudioDecoder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioDecoder.d.ts","sourceRoot":"","sources":["../../src/decoders/AudioDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"AudioDecoder.d.ts","sourceRoot":"","sources":["../../src/decoders/AudioDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGhE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,KAAK,YAAY,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;AAYpD,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,YAAY,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,GAAG,eAAe,CAAC;IAC5C,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAKD,qBAAa,YAAa,SAAQ,oBAAoB;IACpD,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,UAAU,CAA6B;gBAEnC,IAAI,EAAE,gBAAgB;IAclC,IAAI,KAAK,IAAI,UAAU,CAAwB;IAC/C,IAAI,eAAe,IAAI,MAAM,CAAkC;IAE/D,0DAA0D;IAC1D,IAAI,SAAS,IAAI,YAAY,CAA4B;IACzD,IAAI,SAAS,CAAC,OAAO,EAAE,YAAY,EAAgC;IAEnE,OAAO,CAAC,kBAAkB;IAQ1B,sEAAsE;IACtE,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,mBAAmB;WAQd,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWxF,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAiC3C,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAkChC,KAAK,CAAC,OAAO,GAAE,MAA8B,GAAG,OAAO,CAAC,IAAI,CAAC;IAmDnE,KAAK,IAAI,IAAI;IAab,KAAK,IAAI,IAAI;IAUb,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,mBAAmB;IAsC3B,OAAO,CAAC,oBAAoB;CAgB7B"}
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
* https://developer.mozilla.org/en-US/docs/Web/API/AudioDecoder
|
|
4
4
|
*/
|
|
5
5
|
import { WebCodecsEventTarget } from '../utils/event-target.js';
|
|
6
|
+
import { toUint8Array } from '../utils/buffer.js';
|
|
7
|
+
import { validateNonEmptyString, validatePositiveInteger, validateRequired } from '../utils/validation.js';
|
|
6
8
|
import { AudioData } from '../core/AudioData.js';
|
|
7
9
|
import { EncodedAudioChunk } from '../core/EncodedAudioChunk.js';
|
|
8
10
|
import { DOMException } from '../types/index.js';
|
|
9
|
-
import { AUDIO_DECODER_CODEC_MAP, AUDIO_OUTPUT_FORMAT_MAP, } from '../
|
|
11
|
+
import { AUDIO_DECODER_CODEC_MAP, AUDIO_OUTPUT_FORMAT_MAP, } from '../codec-utils/audio-codecs.js';
|
|
10
12
|
import { parseAudioSpecificConfig } from '../utils/aac.js';
|
|
11
13
|
import { NodeAvAudioDecoder } from '../node-av/NodeAvAudioDecoder.js';
|
|
14
|
+
import { getCodecBase } from '../utils/codec-cache.js';
|
|
15
|
+
import { encodingError, wrapAsWebCodecsError } from '../utils/errors.js';
|
|
12
16
|
const DEFAULT_FLUSH_TIMEOUT = 30000;
|
|
17
|
+
const MAX_QUEUE_SIZE = 100; // Prevent unbounded memory growth
|
|
13
18
|
export class AudioDecoder extends WebCodecsEventTarget {
|
|
14
19
|
_state = 'unconfigured';
|
|
15
20
|
_decodeQueueSize = 0;
|
|
@@ -20,6 +25,7 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
20
25
|
_frameIndex = 0;
|
|
21
26
|
_outputFormat = 'f32';
|
|
22
27
|
_aacConfig = null;
|
|
28
|
+
_ondequeue = null;
|
|
23
29
|
constructor(init) {
|
|
24
30
|
super();
|
|
25
31
|
if (!init || typeof init.output !== 'function') {
|
|
@@ -33,6 +39,9 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
33
39
|
}
|
|
34
40
|
get state() { return this._state; }
|
|
35
41
|
get decodeQueueSize() { return this._decodeQueueSize; }
|
|
42
|
+
/** Event handler called when decodeQueueSize decreases */
|
|
43
|
+
get ondequeue() { return this._ondequeue; }
|
|
44
|
+
set ondequeue(handler) { this._ondequeue = handler; }
|
|
36
45
|
_safeErrorCallback(error) {
|
|
37
46
|
try {
|
|
38
47
|
this._errorCallback(error);
|
|
@@ -41,19 +50,31 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
41
50
|
this.emit('callbackError', error);
|
|
42
51
|
}
|
|
43
52
|
}
|
|
53
|
+
/** Fire the dequeue event (both EventTarget and ondequeue handler) */
|
|
54
|
+
_fireDequeueEvent() {
|
|
55
|
+
this.emit('dequeue');
|
|
56
|
+
if (this._ondequeue) {
|
|
57
|
+
try {
|
|
58
|
+
this._ondequeue(new Event('dequeue'));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Ignore errors in user handler per spec
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
44
65
|
_safeOutputCallback(data) {
|
|
45
66
|
try {
|
|
46
67
|
this._outputCallback(data);
|
|
47
68
|
}
|
|
48
69
|
catch (err) {
|
|
49
|
-
this._safeErrorCallback(err
|
|
70
|
+
this._safeErrorCallback(wrapAsWebCodecsError(err, 'EncodingError'));
|
|
50
71
|
}
|
|
51
72
|
}
|
|
52
73
|
static async isConfigSupported(config) {
|
|
53
74
|
if (!config.codec || !config.sampleRate || !config.numberOfChannels) {
|
|
54
75
|
return { supported: false, config };
|
|
55
76
|
}
|
|
56
|
-
const codecBase = config.codec
|
|
77
|
+
const codecBase = getCodecBase(config.codec);
|
|
57
78
|
const supported = codecBase in AUDIO_DECODER_CODEC_MAP || config.codec in AUDIO_DECODER_CODEC_MAP;
|
|
58
79
|
return { supported, config };
|
|
59
80
|
}
|
|
@@ -61,19 +82,14 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
61
82
|
if (this._state === 'closed') {
|
|
62
83
|
throw new DOMException('Decoder is closed', 'InvalidStateError');
|
|
63
84
|
}
|
|
64
|
-
|
|
85
|
+
validateRequired(config, 'config');
|
|
86
|
+
if (typeof config !== 'object') {
|
|
65
87
|
throw new TypeError('config must be an object');
|
|
66
88
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
throw new TypeError('sampleRate must be a positive integer');
|
|
72
|
-
}
|
|
73
|
-
if (typeof config.numberOfChannels !== 'number' || config.numberOfChannels <= 0 || !Number.isInteger(config.numberOfChannels)) {
|
|
74
|
-
throw new TypeError('numberOfChannels must be a positive integer');
|
|
75
|
-
}
|
|
76
|
-
const codecBase = config.codec.split('.')[0].toLowerCase();
|
|
89
|
+
validateNonEmptyString(config.codec, 'codec');
|
|
90
|
+
validatePositiveInteger(config.sampleRate, 'sampleRate');
|
|
91
|
+
validatePositiveInteger(config.numberOfChannels, 'numberOfChannels');
|
|
92
|
+
const codecBase = getCodecBase(config.codec);
|
|
77
93
|
if (!(codecBase in AUDIO_DECODER_CODEC_MAP) && !(config.codec in AUDIO_DECODER_CODEC_MAP)) {
|
|
78
94
|
throw new DOMException(`Codec '${config.codec}' is not supported`, 'NotSupportedError');
|
|
79
95
|
}
|
|
@@ -96,7 +112,12 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
96
112
|
throw new TypeError('chunk must be an EncodedAudioChunk');
|
|
97
113
|
}
|
|
98
114
|
if (!this._decoder?.isHealthy) {
|
|
99
|
-
this._safeErrorCallback(
|
|
115
|
+
this._safeErrorCallback(encodingError('Decoder is not healthy'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// Check queue saturation to prevent unbounded memory growth
|
|
119
|
+
if (this._decodeQueueSize >= MAX_QUEUE_SIZE) {
|
|
120
|
+
this._safeErrorCallback(new DOMException(`Decoder queue saturated (${MAX_QUEUE_SIZE} chunks pending). Wait for dequeue events before decoding more chunks.`, 'QuotaExceededError'));
|
|
100
121
|
return;
|
|
101
122
|
}
|
|
102
123
|
this._decodeQueueSize++;
|
|
@@ -106,7 +127,7 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
106
127
|
}
|
|
107
128
|
catch {
|
|
108
129
|
this._decodeQueueSize = Math.max(0, this._decodeQueueSize - 1);
|
|
109
|
-
this._safeErrorCallback(
|
|
130
|
+
this._safeErrorCallback(encodingError('Failed to write chunk data to decoder'));
|
|
110
131
|
}
|
|
111
132
|
}
|
|
112
133
|
async flush(timeout = DEFAULT_FLUSH_TIMEOUT) {
|
|
@@ -202,37 +223,46 @@ export class AudioDecoder extends WebCodecsEventTarget {
|
|
|
202
223
|
if (!this._config)
|
|
203
224
|
return;
|
|
204
225
|
const timestamp = (this._frameIndex * 1_000_000) / this._config.sampleRate;
|
|
205
|
-
const
|
|
226
|
+
const init = {
|
|
206
227
|
format: this._outputFormat,
|
|
207
228
|
sampleRate: this._config.sampleRate,
|
|
208
229
|
numberOfChannels: this._config.numberOfChannels,
|
|
209
230
|
numberOfFrames: frame.numberOfFrames,
|
|
210
231
|
timestamp,
|
|
211
|
-
|
|
212
|
-
|
|
232
|
+
};
|
|
233
|
+
if (frame.nativeFrame) {
|
|
234
|
+
init._nativeFrame = frame.nativeFrame;
|
|
235
|
+
init._nativeCleanup = () => {
|
|
236
|
+
try {
|
|
237
|
+
frame.nativeFrame.unref?.();
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
// ignore cleanup errors
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
init.data = new Uint8Array(0);
|
|
244
|
+
}
|
|
245
|
+
else if (frame.data) {
|
|
246
|
+
init.data = new Uint8Array(frame.data);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
init.data = new Uint8Array(0);
|
|
250
|
+
}
|
|
251
|
+
const audioData = new AudioData(init);
|
|
213
252
|
this._frameIndex += frame.numberOfFrames;
|
|
214
253
|
this._decodeQueueSize = Math.max(0, this._decodeQueueSize - 1);
|
|
215
|
-
this.
|
|
254
|
+
this._fireDequeueEvent();
|
|
216
255
|
this._safeOutputCallback(audioData);
|
|
217
256
|
}
|
|
218
257
|
_parseAacDescription(config) {
|
|
219
|
-
const codecBase = config.codec
|
|
258
|
+
const codecBase = getCodecBase(config.codec);
|
|
220
259
|
const isAac = codecBase === 'mp4a' || codecBase === 'aac';
|
|
221
260
|
if (!isAac || !config.description) {
|
|
222
261
|
return null;
|
|
223
262
|
}
|
|
224
|
-
let bytes;
|
|
225
|
-
if (config.description instanceof ArrayBuffer) {
|
|
226
|
-
bytes = new Uint8Array(config.description);
|
|
227
|
-
}
|
|
228
|
-
else if (ArrayBuffer.isView(config.description)) {
|
|
229
|
-
bytes = new Uint8Array(config.description.buffer, config.description.byteOffset, config.description.byteLength);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
const copy = new Uint8Array(bytes);
|
|
235
263
|
try {
|
|
264
|
+
const bytes = toUint8Array(config.description);
|
|
265
|
+
const copy = new Uint8Array(bytes);
|
|
236
266
|
return parseAudioSpecificConfig(copy);
|
|
237
267
|
}
|
|
238
268
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioDecoder.js","sourceRoot":"","sources":["../../src/decoders/AudioDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"AudioDecoder.js","sourceRoot":"","sources":["../../src/decoders/AudioDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAsBzE,MAAM,qBAAqB,GAAG,KAAK,CAAC;AACpC,MAAM,cAAc,GAAG,GAAG,CAAC,CAAC,kCAAkC;AAE9D,MAAM,OAAO,YAAa,SAAQ,oBAAoB;IAC5C,MAAM,GAAe,cAAc,CAAC;IACpC,gBAAgB,GAAG,CAAC,CAAC;IACrB,OAAO,GAA8B,IAAI,CAAC;IAC1C,eAAe,CAA4B;IAC3C,cAAc,CAAyB;IACvC,QAAQ,GAA8B,IAAI,CAAC;IAC3C,WAAW,GAAG,CAAC,CAAC;IAChB,aAAa,GAAsB,KAAK,CAAC;IACzC,UAAU,GAAqB,IAAI,CAAC;IACpC,UAAU,GAAwB,IAAI,CAAC;IAE/C,YAAY,IAAsB;QAChC,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/C,MAAM,IAAI,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACrC,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC;QACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC;IACnC,CAAC;IAED,IAAI,KAAK,KAAiB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,IAAI,eAAe,KAAa,OAAO,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAE/D,0DAA0D;IAC1D,IAAI,SAAS,KAAmB,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,IAAI,SAAS,CAAC,OAAqB,IAAI,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAE3D,kBAAkB,CAAC,KAAY;QACrC,IAAI,CAAC;YACH,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,sEAAsE;IAC9D,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,yCAAyC;YAC3C,CAAC;QACH,CAAC;IACH,CAAC;IAEO,mBAAmB,CAAC,IAAe;QACzC,IAAI,CAAC;YACH,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAA0B;QACvD,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YACpE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QACtC,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,SAAS,IAAI,uBAAuB,IAAI,MAAM,CAAC,KAAK,IAAI,uBAAuB,CAAC;QAElG,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAC/B,CAAC;IAED,SAAS,CAAC,MAA0B;QAClC,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,YAAY,CAAC,mBAAmB,EAAE,mBAAmB,CAAC,CAAC;QACnE,CAAC;QAED,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAC;QAClD,CAAC;QACD,sBAAsB,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC9C,uBAAuB,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QACzD,uBAAuB,CAAC,MAAM,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;QAErE,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,CAAC,CAAC,SAAS,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,uBAAuB,CAAC,EAAE,CAAC;YAC1F,MAAM,IAAI,YAAY,CAAC,UAAU,MAAM,CAAC,KAAK,oBAAoB,EAAE,mBAAmB,CAAC,CAAC;QAC1F,CAAC;QAED,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,YAAY,IAAI,uBAAuB,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,SAAS,CAAC,yBAAyB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAEpB,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,YAAY,IAAI,KAAK,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAEpD,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,KAAwB;QAC7B,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACjC,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,mBAAmB,CAAC,CAAC;QAC3E,CAAC;QAED,IAAI,CAAC,CAAC,KAAK,YAAY,iBAAiB,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,wBAAwB,CAAC,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QAED,4DAA4D;QAC5D,IAAI,IAAI,CAAC,gBAAgB,IAAI,cAAc,EAAE,CAAC;YAC5C,IAAI,CAAC,kBAAkB,CAAC,IAAI,YAAY,CACtC,4BAA4B,cAAc,wEAAwE,EAClH,oBAAoB,CACrB,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC;YAC/D,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,uCAAuC,CAAC,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,UAAkB,qBAAqB;QACjD,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACjC,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,mBAAmB,CAAC,CAAC;QAC3E,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,IAAI,SAAS,GAA0B,IAAI,CAAC;YAC5C,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,IAAI,SAAS,EAAE,CAAC;oBACd,YAAY,CAAC,SAAS,CAAC,CAAC;oBACxB,SAAS,GAAG,IAAI,CAAC;gBACnB,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,SAAS,GAAG,GAAG,EAAE;gBACrB,IAAI,QAAQ;oBAAE,OAAO;gBACrB,QAAQ,GAAG,IAAI,CAAC;gBAChB,OAAO,EAAE,CAAC;gBACV,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;gBAC1B,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;gBACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACrB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YAEF,MAAM,QAAQ,GAAG,CAAC,GAAU,EAAE,EAAE;gBAC9B,IAAI,QAAQ;oBAAE,OAAO;gBACrB,QAAQ,GAAG,IAAI,CAAC;gBAChB,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC;YAEF,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC1B,QAAQ,CAAC,IAAI,YAAY,CAAC,2BAA2B,EAAE,cAAc,CAAC,CAAC,CAAC;YAC1E,CAAC,EAAE,OAAO,CAAC,CAAC;YAEZ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACvC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,YAAY,CAAC,mBAAmB,EAAE,mBAAmB,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO;QAErC,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QACvB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,IAAI,CAAC,QAAQ,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;YACzB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;YACzB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;YACnC,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC,gBAAgB;YAC/C,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,YAAY,EAAE,IAAI,CAAC,aAAa;SACjC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAsF,EAAE,EAAE;YACnH,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YACvC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,YAAY;QAClB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;IAEO,mBAAmB,CAAC,KAAsF;QAChH,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QAE3E,MAAM,IAAI,GAAQ;YAChB,MAAM,EAAE,IAAI,CAAC,aAAa;YAC1B,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;YACnC,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC,gBAAgB;YAC/C,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,SAAS;SACV,CAAC;QAEF,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;YACtC,IAAI,CAAC,cAAc,GAAG,GAAG,EAAE;gBACzB,IAAI,CAAC;oBACH,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC9B,CAAC;gBAAC,MAAM,CAAC;oBACP,wBAAwB;gBAC1B,CAAC;YACH,CAAC,CAAC;YACF,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;QAEtC,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,cAAc,CAAC;QACzC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAEO,oBAAoB,CAAC,MAA0B;QACrD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,KAAK,GAAG,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK,CAAC;QAE1D,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;YACnC,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
|
|
@@ -84,12 +84,10 @@ export declare class ImageDecoder {
|
|
|
84
84
|
private _frames;
|
|
85
85
|
private _framesParsed;
|
|
86
86
|
private _repetitionCount;
|
|
87
|
-
private _frameDurations;
|
|
88
87
|
constructor(init: ImageDecoderInit);
|
|
89
88
|
private _readStream;
|
|
90
89
|
private _initializeTracks;
|
|
91
90
|
private _parseImage;
|
|
92
|
-
private _probeAnimationMetadata;
|
|
93
91
|
private _frameTypeSupportsAlpha;
|
|
94
92
|
private _evaluateOrientation;
|
|
95
93
|
private _shouldPremultiplyAlpha;
|
|
@@ -99,7 +97,6 @@ export declare class ImageDecoder {
|
|
|
99
97
|
private _applyOrientation;
|
|
100
98
|
private _decodeWithNodeAv;
|
|
101
99
|
private _decodeAllFramesDirect;
|
|
102
|
-
private _probeDimensions;
|
|
103
100
|
get complete(): boolean;
|
|
104
101
|
get completed(): Promise<void>;
|
|
105
102
|
get tracks(): ImageTrackList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageDecoder.d.ts","sourceRoot":"","sources":["../../src/decoders/ImageDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"ImageDecoder.d.ts","sourceRoot":"","sources":["../../src/decoders/ImageDecoder.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,12 +2,10 @@
|
|
|
2
2
|
* ImageDecoder - Decodes encoded image data to VideoFrames
|
|
3
3
|
* https://developer.mozilla.org/en-US/docs/Web/API/ImageDecoder
|
|
4
4
|
*/
|
|
5
|
-
import { spawn } from 'child_process';
|
|
6
5
|
import { VideoFrame } from '../core/VideoFrame.js';
|
|
7
6
|
import { DOMException } from '../types/index.js';
|
|
8
7
|
import { createLogger } from '../utils/index.js';
|
|
9
|
-
import {
|
|
10
|
-
import { NodeAvImageDecoder, probeImageDimensions } from '../node-av/NodeAvImageDecoder.js';
|
|
8
|
+
import { NodeAvImageDecoder } from '../node-av/NodeAvImageDecoder.js';
|
|
11
9
|
const logger = createLogger('ImageDecoder');
|
|
12
10
|
/**
|
|
13
11
|
* ImageTrack - Represents an individual image track
|
|
@@ -66,19 +64,8 @@ export class ImageTrackList {
|
|
|
66
64
|
return this._tracks[Symbol.iterator]();
|
|
67
65
|
}
|
|
68
66
|
}
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
'image/png': { format: 'png_pipe' },
|
|
72
|
-
'image/apng': { format: 'apng' },
|
|
73
|
-
'image/jpeg': { format: 'jpeg_pipe' },
|
|
74
|
-
'image/jpg': { format: 'jpeg_pipe' },
|
|
75
|
-
'image/webp': { format: 'webp_pipe', autoDetect: true },
|
|
76
|
-
'image/gif': { format: 'gif' },
|
|
77
|
-
'image/bmp': { format: 'bmp_pipe' },
|
|
78
|
-
'image/avif': { format: 'avif', autoDetect: true },
|
|
79
|
-
'image/tiff': { format: 'tiff_pipe' },
|
|
80
|
-
};
|
|
81
|
-
const SUPPORTED_TYPES = new Set(Object.keys(MIME_TO_CODEC));
|
|
67
|
+
// Supported image types (delegated to node-av)
|
|
68
|
+
const ANIMATED_FORMATS = ['image/gif', 'image/apng', 'image/webp'];
|
|
82
69
|
function isReadableStream(obj) {
|
|
83
70
|
return typeof obj === 'object' && obj !== null && typeof obj.getReader === 'function';
|
|
84
71
|
}
|
|
@@ -105,7 +92,6 @@ export class ImageDecoder {
|
|
|
105
92
|
_frames = [];
|
|
106
93
|
_framesParsed = false;
|
|
107
94
|
_repetitionCount = 0;
|
|
108
|
-
_frameDurations = [];
|
|
109
95
|
constructor(init) {
|
|
110
96
|
if (!init || typeof init !== 'object') {
|
|
111
97
|
throw new TypeError('init must be an object');
|
|
@@ -200,11 +186,12 @@ export class ImageDecoder {
|
|
|
200
186
|
async _parseImage() {
|
|
201
187
|
if (!this._data || this._framesParsed)
|
|
202
188
|
return;
|
|
203
|
-
|
|
204
|
-
if (!codecInfo) {
|
|
189
|
+
if (!NodeAvImageDecoder.isTypeSupported(this._type)) {
|
|
205
190
|
throw new DOMException(`Unsupported image type: ${this._type}`, 'NotSupportedError');
|
|
206
191
|
}
|
|
207
|
-
|
|
192
|
+
// Set default repetition count (GIF loops forever by default)
|
|
193
|
+
const type = this._type.toLowerCase();
|
|
194
|
+
this._repetitionCount = type === 'image/gif' ? Infinity : 1;
|
|
208
195
|
await this._decodeAllFramesDirect();
|
|
209
196
|
this._framesParsed = true;
|
|
210
197
|
this._updateVisibleTrackInfo();
|
|
@@ -216,58 +203,6 @@ export class ImageDecoder {
|
|
|
216
203
|
});
|
|
217
204
|
this._tracks._addTrack(track);
|
|
218
205
|
}
|
|
219
|
-
async _probeAnimationMetadata() {
|
|
220
|
-
if (!this._data)
|
|
221
|
-
return;
|
|
222
|
-
const codecInfo = MIME_TO_CODEC[this._type.toLowerCase()];
|
|
223
|
-
const isAnimatedFormat = ['image/gif', 'image/apng', 'image/webp'].includes(this._type.toLowerCase());
|
|
224
|
-
if (!isAnimatedFormat)
|
|
225
|
-
return;
|
|
226
|
-
return new Promise((resolve) => {
|
|
227
|
-
const args = [
|
|
228
|
-
'-hide_banner', '-v', 'error',
|
|
229
|
-
'-select_streams', 'v:0',
|
|
230
|
-
'-show_entries', 'frame=pkt_duration_time,pkt_pts_time',
|
|
231
|
-
'-show_entries', 'format_tags=loop',
|
|
232
|
-
'-show_entries', 'stream_tags=loop',
|
|
233
|
-
'-of', 'json',
|
|
234
|
-
];
|
|
235
|
-
if (!codecInfo.autoDetect) {
|
|
236
|
-
args.push('-f', codecInfo.format);
|
|
237
|
-
}
|
|
238
|
-
args.push('pipe:0');
|
|
239
|
-
const process = spawn('ffprobe', args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
240
|
-
let stdout = '';
|
|
241
|
-
process.stdout?.on('data', (data) => { stdout += data.toString(); });
|
|
242
|
-
process.stderr?.on('data', () => { });
|
|
243
|
-
process.on('close', () => {
|
|
244
|
-
try {
|
|
245
|
-
const info = JSON.parse(stdout);
|
|
246
|
-
const loopValue = info.format?.tags?.loop ?? info.streams?.[0]?.tags?.loop;
|
|
247
|
-
if (loopValue !== undefined) {
|
|
248
|
-
const loopNum = parseInt(loopValue, 10);
|
|
249
|
-
this._repetitionCount = loopNum === 0 ? Infinity : loopNum;
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
this._repetitionCount = this._type.toLowerCase() === 'image/gif' ? Infinity : 1;
|
|
253
|
-
}
|
|
254
|
-
if (info.frames && Array.isArray(info.frames)) {
|
|
255
|
-
for (const frame of info.frames) {
|
|
256
|
-
const durationSec = parseFloat(frame.pkt_duration_time || '0.1');
|
|
257
|
-
this._frameDurations.push(Math.round(durationSec * 1_000_000));
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
catch {
|
|
262
|
-
this._repetitionCount = this._type.toLowerCase() === 'image/gif' ? Infinity : 1;
|
|
263
|
-
}
|
|
264
|
-
resolve();
|
|
265
|
-
});
|
|
266
|
-
process.stdin?.on('error', () => { });
|
|
267
|
-
process.stdin?.write(Buffer.from(this._data.buffer, this._data.byteOffset, this._data.byteLength));
|
|
268
|
-
process.stdin?.end();
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
206
|
_frameTypeSupportsAlpha() {
|
|
272
207
|
const type = this._type.toLowerCase();
|
|
273
208
|
return ['image/png', 'image/apng', 'image/webp', 'image/gif', 'image/avif'].includes(type);
|
|
@@ -520,165 +455,19 @@ export class ImageDecoder {
|
|
|
520
455
|
async _decodeAllFramesDirect() {
|
|
521
456
|
if (!this._data)
|
|
522
457
|
return;
|
|
523
|
-
//
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (this._frames.length > 0) {
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
catch (err) {
|
|
533
|
-
logger.warn('node-av decode failed, falling back to FFmpeg', { error: err });
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
const codecInfo = MIME_TO_CODEC[this._type.toLowerCase()];
|
|
537
|
-
const dimensions = await this._probeDimensions();
|
|
538
|
-
if (dimensions.width === 0 || dimensions.height === 0) {
|
|
539
|
-
throw new Error(`Could not determine image dimensions for ${this._type}`);
|
|
540
|
-
}
|
|
541
|
-
const args = ['-hide_banner', '-loglevel', 'error', '-noautorotate'];
|
|
542
|
-
if (!codecInfo.autoDetect) {
|
|
543
|
-
args.push('-f', codecInfo.format);
|
|
544
|
-
}
|
|
545
|
-
args.push('-i', 'pipe:0');
|
|
546
|
-
if (this._desiredWidth || this._desiredHeight) {
|
|
547
|
-
args.push('-vf', `scale=${this._desiredWidth || -1}:${this._desiredHeight || -1}`);
|
|
548
|
-
}
|
|
549
|
-
args.push('-f', 'rawvideo', '-pix_fmt', 'rgba', 'pipe:1');
|
|
550
|
-
const width = this._desiredWidth || dimensions.width;
|
|
551
|
-
const height = this._desiredHeight || dimensions.height;
|
|
552
|
-
const frameSize = width * height * 4;
|
|
553
|
-
if (frameSize === 0) {
|
|
554
|
-
throw new Error('Failed to determine frame size');
|
|
555
|
-
}
|
|
556
|
-
const defaultDuration = 100000;
|
|
557
|
-
const hasFrameDurations = this._frameDurations.length > 0;
|
|
558
|
-
return new Promise((resolve, reject) => {
|
|
559
|
-
const process = spawn('ffmpeg', args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
560
|
-
let buffer = Buffer.alloc(0);
|
|
561
|
-
let timestamp = 0;
|
|
562
|
-
let frameIndex = 0;
|
|
563
|
-
const emitFrameFromBuffer = (frameBuffer) => {
|
|
564
|
-
const treatAsAnimated = hasFrameDurations || frameIndex > 0;
|
|
565
|
-
const duration = treatAsAnimated
|
|
566
|
-
? (this._frameDurations[frameIndex] ?? defaultDuration)
|
|
567
|
-
: 0;
|
|
568
|
-
const frameTimestamp = timestamp;
|
|
569
|
-
timestamp += duration;
|
|
570
|
-
const frameBytes = new Uint8Array(frameBuffer);
|
|
571
|
-
const processed = this._processFrameData(frameBytes);
|
|
572
|
-
const oriented = this._applyOrientation(processed, width, height);
|
|
573
|
-
this._frames.push({
|
|
574
|
-
data: oriented.data,
|
|
575
|
-
width: oriented.width,
|
|
576
|
-
height: oriented.height,
|
|
577
|
-
timestamp: frameTimestamp,
|
|
578
|
-
duration,
|
|
579
|
-
complete: true,
|
|
580
|
-
colorSpace: this._preferredColorSpace,
|
|
581
|
-
});
|
|
582
|
-
frameIndex++;
|
|
583
|
-
};
|
|
584
|
-
process.stdout?.on('data', (data) => {
|
|
585
|
-
if (!data || data.length === 0) {
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
buffer = buffer.length === 0 ? Buffer.from(data) : Buffer.concat([buffer, data]);
|
|
589
|
-
while (buffer.length >= frameSize) {
|
|
590
|
-
const frameChunk = buffer.subarray(0, frameSize);
|
|
591
|
-
emitFrameFromBuffer(frameChunk);
|
|
592
|
-
buffer = buffer.subarray(frameSize);
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
process.stderr?.on('data', (data) => {
|
|
596
|
-
const msg = data.toString();
|
|
597
|
-
if (msg.toLowerCase().includes('error') && !msg.includes('Invalid data')) {
|
|
598
|
-
logger.warn('FFmpeg stderr', { message: msg });
|
|
599
|
-
}
|
|
600
|
-
});
|
|
601
|
-
process.on('close', (code) => {
|
|
602
|
-
buffer = Buffer.alloc(0);
|
|
603
|
-
if (code !== 0 && this._frames.length === 0) {
|
|
604
|
-
reject(new Error(`FFmpeg failed with code ${code}`));
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
if (this._frames.length === 0) {
|
|
608
|
-
reject(new Error('No frames decoded'));
|
|
609
|
-
}
|
|
610
|
-
else {
|
|
611
|
-
resolve();
|
|
612
|
-
}
|
|
613
|
-
});
|
|
614
|
-
process.stdin?.on('error', () => { });
|
|
615
|
-
process.stdin?.write(this._data);
|
|
616
|
-
process.stdin?.end();
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
async _probeDimensions() {
|
|
620
|
-
if (!this._data)
|
|
621
|
-
return { width: 0, height: 0 };
|
|
622
|
-
// For WebP, try native header parsing first
|
|
623
|
-
if (this._type.toLowerCase() === 'image/webp') {
|
|
624
|
-
const webpInfo = parseWebPHeader(this._data);
|
|
625
|
-
if (webpInfo && webpInfo.width > 0 && webpInfo.height > 0) {
|
|
626
|
-
return { width: webpInfo.width, height: webpInfo.height };
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// Try node-av probing first
|
|
630
|
-
if (NodeAvImageDecoder.isTypeSupported(this._type)) {
|
|
631
|
-
try {
|
|
632
|
-
const dims = await probeImageDimensions(this._data, this._type);
|
|
633
|
-
if (dims.width > 0 && dims.height > 0) {
|
|
634
|
-
return dims;
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
catch {
|
|
638
|
-
// Fall through to FFmpeg probing
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
const codecInfo = MIME_TO_CODEC[this._type.toLowerCase()];
|
|
642
|
-
const tryAutoDetect = codecInfo.autoDetect;
|
|
643
|
-
const attemptProbe = (useFormat) => {
|
|
644
|
-
return new Promise((resolve) => {
|
|
645
|
-
const args = ['-hide_banner'];
|
|
646
|
-
if (useFormat && !tryAutoDetect) {
|
|
647
|
-
args.push('-f', codecInfo.format);
|
|
648
|
-
}
|
|
649
|
-
args.push('-i', 'pipe:0', '-frames:v', '1', '-f', 'null', '-');
|
|
650
|
-
const process = spawn('ffmpeg', args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
651
|
-
let stderr = '';
|
|
652
|
-
process.stderr?.on('data', (data) => { stderr += data.toString(); });
|
|
653
|
-
process.on('close', () => {
|
|
654
|
-
const match = stderr.match(/\b(\d{2,5})x(\d{2,5})\b/);
|
|
655
|
-
if (match) {
|
|
656
|
-
resolve({ width: parseInt(match[1], 10), height: parseInt(match[2], 10) });
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
resolve({ width: 0, height: 0 });
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
process.stdin?.on('error', () => { });
|
|
663
|
-
process.stdin?.write(Buffer.from(this._data.buffer, this._data.byteOffset, this._data.byteLength));
|
|
664
|
-
process.stdin?.end();
|
|
665
|
-
});
|
|
666
|
-
};
|
|
667
|
-
let result = await attemptProbe(!tryAutoDetect);
|
|
668
|
-
if ((result.width === 0 || result.height === 0) && tryAutoDetect) {
|
|
669
|
-
result = await attemptProbe(true);
|
|
670
|
-
}
|
|
671
|
-
if ((result.width === 0 || result.height === 0) && !tryAutoDetect) {
|
|
672
|
-
result = await attemptProbe(false);
|
|
458
|
+
// Decode using node-av
|
|
459
|
+
// Note: Animated WebP has limited support (FFmpeg webp demuxer skips ANIM/ANMF chunks)
|
|
460
|
+
await this._decodeWithNodeAv();
|
|
461
|
+
if (this._frames.length === 0) {
|
|
462
|
+
throw new Error('No frames decoded');
|
|
673
463
|
}
|
|
674
|
-
return result;
|
|
675
464
|
}
|
|
676
465
|
get complete() { return this._complete; }
|
|
677
466
|
get completed() { return this._completed; }
|
|
678
467
|
get tracks() { return this._tracks; }
|
|
679
468
|
get type() { return this._type; }
|
|
680
469
|
static async isTypeSupported(type) {
|
|
681
|
-
return
|
|
470
|
+
return NodeAvImageDecoder.isTypeSupported(type);
|
|
682
471
|
}
|
|
683
472
|
async decode(options) {
|
|
684
473
|
if (this._closed) {
|
|
@@ -719,7 +508,6 @@ export class ImageDecoder {
|
|
|
719
508
|
this._frames = [];
|
|
720
509
|
this._framesParsed = false;
|
|
721
510
|
this._repetitionCount = 0;
|
|
722
|
-
this._frameDurations = [];
|
|
723
511
|
this._visibleFrameCount = 0;
|
|
724
512
|
this._visibleAnimated = false;
|
|
725
513
|
this._visibleRepetitionCount = 1;
|