webcodecs-node 0.7.2 → 0.7.5
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/README.md +94 -12
- package/dist/config/ffmpeg-quality.d.ts +10 -1
- package/dist/config/ffmpeg-quality.d.ts.map +1 -1
- package/dist/config/ffmpeg-quality.js +12 -38
- package/dist/config/ffmpeg-quality.js.map +1 -1
- package/dist/config/webcodecs-config.d.ts +58 -0
- package/dist/config/webcodecs-config.d.ts.map +1 -0
- package/dist/config/webcodecs-config.js +187 -0
- package/dist/config/webcodecs-config.js.map +1 -0
- package/dist/containers/Demuxer.d.ts +3 -1
- package/dist/containers/Demuxer.d.ts.map +1 -1
- package/dist/containers/Demuxer.js +26 -19
- package/dist/containers/Demuxer.js.map +1 -1
- package/dist/containers/FFmpegMuxer.d.ts +42 -0
- package/dist/containers/FFmpegMuxer.d.ts.map +1 -0
- package/dist/containers/FFmpegMuxer.js +311 -0
- package/dist/containers/FFmpegMuxer.js.map +1 -0
- package/dist/containers/FFmpegSpawnMuxer.d.ts +42 -0
- package/dist/containers/FFmpegSpawnMuxer.d.ts.map +1 -0
- package/dist/containers/FFmpegSpawnMuxer.js +311 -0
- package/dist/containers/FFmpegSpawnMuxer.js.map +1 -0
- package/dist/containers/FallbackMuxer.d.ts +42 -0
- package/dist/containers/FallbackMuxer.d.ts.map +1 -0
- package/dist/containers/FallbackMuxer.js +311 -0
- package/dist/containers/FallbackMuxer.js.map +1 -0
- package/dist/containers/Muxer.d.ts +75 -107
- package/dist/containers/Muxer.d.ts.map +1 -1
- package/dist/containers/Muxer.js +184 -243
- package/dist/containers/Muxer.js.map +1 -1
- package/dist/containers/MuxerWithFallback.d.ts +110 -0
- package/dist/containers/MuxerWithFallback.d.ts.map +1 -0
- package/dist/containers/MuxerWithFallback.js +239 -0
- package/dist/containers/MuxerWithFallback.js.map +1 -0
- package/dist/containers/NodeAvMuxer.d.ts +118 -0
- package/dist/containers/NodeAvMuxer.d.ts.map +1 -0
- package/dist/containers/NodeAvMuxer.js +338 -0
- package/dist/containers/NodeAvMuxer.js.map +1 -0
- package/dist/containers/extract.d.ts.map +1 -1
- package/dist/containers/extract.js +3 -1
- package/dist/containers/extract.js.map +1 -1
- package/dist/containers/index.d.ts +20 -14
- package/dist/containers/index.d.ts.map +1 -1
- package/dist/containers/index.js +21 -14
- package/dist/containers/index.js.map +1 -1
- package/dist/containers/muxer-types.d.ts +117 -0
- package/dist/containers/muxer-types.d.ts.map +1 -0
- package/dist/containers/muxer-types.js +45 -0
- package/dist/containers/muxer-types.js.map +1 -0
- package/dist/containers/transcode.d.ts.map +1 -1
- package/dist/containers/transcode.js +171 -150
- package/dist/containers/transcode.js.map +1 -1
- package/dist/core/VideoFrame.d.ts +19 -0
- package/dist/core/VideoFrame.d.ts.map +1 -1
- package/dist/core/VideoFrame.js +11 -0
- package/dist/core/VideoFrame.js.map +1 -1
- package/dist/decoders/VideoDecoder.d.ts +1 -0
- package/dist/decoders/VideoDecoder.d.ts.map +1 -1
- package/dist/decoders/VideoDecoder.js +6 -4
- package/dist/decoders/VideoDecoder.js.map +1 -1
- package/dist/demos/demo-audio-visualizer-mediabunny.d.ts +10 -0
- package/dist/demos/demo-audio-visualizer-mediabunny.d.ts.map +1 -0
- package/dist/demos/demo-audio-visualizer-mediabunny.js +357 -0
- package/dist/demos/demo-audio-visualizer-mediabunny.js.map +1 -0
- package/dist/demos/demo-audio-visualizer-nodeav.d.ts +10 -0
- package/dist/demos/demo-audio-visualizer-nodeav.d.ts.map +1 -0
- package/dist/demos/demo-audio-visualizer-nodeav.js +318 -0
- package/dist/demos/demo-audio-visualizer-nodeav.js.map +1 -0
- package/dist/demos/demo-muxer-fallback.d.ts +8 -0
- package/dist/demos/demo-muxer-fallback.d.ts.map +1 -0
- package/dist/demos/demo-muxer-fallback.js +165 -0
- package/dist/demos/demo-muxer-fallback.js.map +1 -0
- package/dist/encoders/AudioEncoder.d.ts +2 -0
- package/dist/encoders/AudioEncoder.d.ts.map +1 -1
- package/dist/encoders/AudioEncoder.js +7 -4
- package/dist/encoders/AudioEncoder.js.map +1 -1
- package/dist/hardware/decoder-args.d.ts.map +1 -1
- package/dist/hardware/decoder-args.js +35 -14
- package/dist/hardware/decoder-args.js.map +1 -1
- package/dist/hardware/detection.d.ts.map +1 -1
- package/dist/hardware/detection.js +39 -0
- package/dist/hardware/detection.js.map +1 -1
- package/dist/hardware/encoder-args.d.ts.map +1 -1
- package/dist/hardware/encoder-args.js +43 -5
- package/dist/hardware/encoder-args.js.map +1 -1
- package/dist/hardware/types.d.ts.map +1 -1
- package/dist/hardware/types.js +30 -28
- package/dist/hardware/types.js.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.d.ts +5 -0
- package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.js +76 -23
- package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
- package/dist/utils/avc.d.ts +2 -0
- package/dist/utils/avc.d.ts.map +1 -1
- package/dist/utils/avc.js +36 -8
- package/dist/utils/avc.js.map +1 -1
- package/dist/utils/codec-validation.d.ts.map +1 -1
- package/dist/utils/codec-validation.js +18 -8
- package/dist/utils/codec-validation.js.map +1 -1
- package/dist/utils/hevc.d.ts +2 -0
- package/dist/utils/hevc.d.ts.map +1 -1
- package/dist/utils/hevc.js +42 -8
- package/dist/utils/hevc.js.map +1 -1
- package/docs/api.md +20 -2
- package/docs/configuration.md +10 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -367,26 +367,79 @@ encoder.configure({
|
|
|
367
367
|
|
|
368
368
|
### Container Utilities
|
|
369
369
|
|
|
370
|
-
Import container demuxing/muxing utilities for working with MP4 and
|
|
370
|
+
Import container demuxing/muxing utilities for working with MP4, WebM, and MKV files:
|
|
371
371
|
|
|
372
372
|
```typescript
|
|
373
|
-
import {
|
|
373
|
+
import { Demuxer, Muxer, muxChunks, extractVideoFrames } from 'webcodecs-node/containers';
|
|
374
374
|
|
|
375
|
-
// Demux
|
|
376
|
-
const demuxer = new
|
|
377
|
-
await demuxer.
|
|
375
|
+
// Demux a video file
|
|
376
|
+
const demuxer = new Demuxer({ path: 'input.mp4' });
|
|
377
|
+
await demuxer.open();
|
|
378
378
|
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
console.log('Video:', demuxer.videoConfig);
|
|
380
|
+
console.log('Audio:', demuxer.audioConfig);
|
|
381
|
+
|
|
382
|
+
for await (const chunk of demuxer.videoChunks()) {
|
|
383
|
+
// chunk is EncodedVideoChunk ready for VideoDecoder
|
|
381
384
|
}
|
|
385
|
+
await demuxer.close();
|
|
386
|
+
|
|
387
|
+
// Mux encoded chunks to a file
|
|
388
|
+
const muxer = new Muxer({ path: 'output.mp4' });
|
|
389
|
+
await muxer.open();
|
|
390
|
+
await muxer.addVideoTrack({
|
|
391
|
+
codec: 'avc1.42001E',
|
|
392
|
+
codedWidth: 1920,
|
|
393
|
+
codedHeight: 1080,
|
|
394
|
+
description: spsNaluBuffer, // Optional: H.264 SPS/PPS
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
for (const chunk of encodedChunks) {
|
|
398
|
+
await muxer.writeVideoChunk(chunk);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const result = await muxer.closeWithResult();
|
|
402
|
+
console.log(`Muxed with ${result.backend} in ${result.durationMs}ms`);
|
|
382
403
|
|
|
383
|
-
//
|
|
384
|
-
const
|
|
385
|
-
|
|
404
|
+
// Or use the convenience function
|
|
405
|
+
const result = await muxChunks({
|
|
406
|
+
path: 'output.mp4',
|
|
407
|
+
video: { config: videoTrackConfig, chunks: videoChunks },
|
|
408
|
+
audio: { config: audioTrackConfig, chunks: audioChunks },
|
|
386
409
|
});
|
|
387
410
|
|
|
388
|
-
|
|
389
|
-
const
|
|
411
|
+
// Extract decoded frames directly
|
|
412
|
+
for await (const frame of extractVideoFrames('input.mp4')) {
|
|
413
|
+
console.log(`Frame: ${frame.timestamp}us`);
|
|
414
|
+
frame.close();
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Muxer Fallback Architecture:**
|
|
419
|
+
|
|
420
|
+
The `Muxer` class uses a two-tier approach for reliability:
|
|
421
|
+
|
|
422
|
+
1. **Primary: node-av** (~5ms) - Fast native muxing using node-av's FormatContext API
|
|
423
|
+
2. **Fallback: FFmpeg subprocess** (~130ms) - Spawns FFmpeg process if node-av fails
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
const muxer = new Muxer({
|
|
427
|
+
path: 'output.mp4',
|
|
428
|
+
onFallback: (err) => console.warn('Using FFmpeg fallback:', err.message),
|
|
429
|
+
forceBackend: 'node-av', // Optional: 'node-av' or 'ffmpeg-spawn'
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
You can also use the backend-specific classes directly:
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
import { NodeAvMuxer, FFmpegMuxer } from 'webcodecs-node/containers';
|
|
437
|
+
|
|
438
|
+
// Fast path only
|
|
439
|
+
const fastMuxer = new NodeAvMuxer({ path: 'output.mp4' });
|
|
440
|
+
|
|
441
|
+
// FFmpeg subprocess only
|
|
442
|
+
const ffmpegMuxer = new FFmpegMuxer({ path: 'output.mp4' });
|
|
390
443
|
```
|
|
391
444
|
|
|
392
445
|
### Streaming & Latency Modes
|
|
@@ -652,6 +705,35 @@ const png = await canvas.toBuffer('png');
|
|
|
652
705
|
| Batch processing | Hardware for throughput |
|
|
653
706
|
| Low-end systems | Software (more compatible) |
|
|
654
707
|
|
|
708
|
+
## Debugging
|
|
709
|
+
|
|
710
|
+
Enable debug logging to troubleshoot encoding/decoding issues:
|
|
711
|
+
|
|
712
|
+
```bash
|
|
713
|
+
# Enable debug output
|
|
714
|
+
WEBCODECS_DEBUG=1 node your-script.js
|
|
715
|
+
|
|
716
|
+
# Or set programmatically
|
|
717
|
+
import { setDebugMode } from 'webcodecs-node';
|
|
718
|
+
setDebugMode(true);
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
Debug mode outputs detailed information about:
|
|
722
|
+
- Hardware acceleration detection and selection
|
|
723
|
+
- Encoder/decoder initialization
|
|
724
|
+
- Muxer backend selection and fallback events
|
|
725
|
+
- Filter chain configuration
|
|
726
|
+
- Error details with context
|
|
727
|
+
|
|
728
|
+
Example debug output:
|
|
729
|
+
```
|
|
730
|
+
[webcodecs:Transcode] Using hardware acceleration: vaapi
|
|
731
|
+
[webcodecs:Transcode] Using hardware decoder for h264
|
|
732
|
+
[webcodecs:Transcode] Using hardware encoder for h264
|
|
733
|
+
[webcodecs:Transcode] Using filter chain: scale_vaapi=format=nv12
|
|
734
|
+
[webcodecs:NodeAvMuxer] writeTrailer returned error code -22
|
|
735
|
+
```
|
|
736
|
+
|
|
655
737
|
## Demos
|
|
656
738
|
|
|
657
739
|
Run the included demos to test functionality:
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FFmpeg quality overrides
|
|
3
|
+
*
|
|
4
|
+
* This module provides backwards compatibility with the old ffmpeg-quality.js config.
|
|
5
|
+
* New code should use webcodecs-config.js via getQualityConfig() instead.
|
|
6
|
+
*/
|
|
1
7
|
export type FfmpegQualityOverrides = {
|
|
2
8
|
crf?: number;
|
|
3
9
|
preset?: string;
|
|
@@ -6,7 +12,10 @@ export type FfmpegQualityOverrides = {
|
|
|
6
12
|
preset?: string;
|
|
7
13
|
}>;
|
|
8
14
|
};
|
|
9
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Get quality overrides for a codec
|
|
17
|
+
* Uses unified webcodecs-config.js
|
|
18
|
+
*/
|
|
10
19
|
export declare function getFfmpegQualityOverrides(codecName: string): {
|
|
11
20
|
crf?: number;
|
|
12
21
|
preset?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffmpeg-quality.d.ts","sourceRoot":"","sources":["../../src/config/ffmpeg-quality.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ffmpeg-quality.d.ts","sourceRoot":"","sources":["../../src/config/ffmpeg-quality.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAE9F"}
|
|
@@ -1,41 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
crf: typeof src.crf === 'number' ? src.crf : undefined,
|
|
13
|
-
preset: typeof src.preset === 'string' ? src.preset : undefined,
|
|
14
|
-
perCodec,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
async function loadOverrides() {
|
|
18
|
-
const configPath = process.env.WEB_CODECS_FFMPEG_QUALITY
|
|
19
|
-
?? path.join(process.cwd(), 'ffmpeg-quality.js');
|
|
20
|
-
if (!fs.existsSync(configPath)) {
|
|
21
|
-
return DEFAULT_OVERRIDES;
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
const mod = await import(pathToFileURL(configPath).href);
|
|
25
|
-
const raw = mod?.default ?? mod?.ffmpegQuality ?? mod;
|
|
26
|
-
return sanitizeOverrides(raw);
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return DEFAULT_OVERRIDES;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export const ffmpegQualityOverrides = await loadOverrides();
|
|
1
|
+
/**
|
|
2
|
+
* FFmpeg quality overrides
|
|
3
|
+
*
|
|
4
|
+
* This module provides backwards compatibility with the old ffmpeg-quality.js config.
|
|
5
|
+
* New code should use webcodecs-config.js via getQualityConfig() instead.
|
|
6
|
+
*/
|
|
7
|
+
import { getQualityConfig } from './webcodecs-config.js';
|
|
8
|
+
/**
|
|
9
|
+
* Get quality overrides for a codec
|
|
10
|
+
* Uses unified webcodecs-config.js
|
|
11
|
+
*/
|
|
33
12
|
export function getFfmpegQualityOverrides(codecName) {
|
|
34
|
-
|
|
35
|
-
const perCodec = ffmpegQualityOverrides.perCodec?.[key];
|
|
36
|
-
return {
|
|
37
|
-
crf: typeof perCodec?.crf === 'number' ? perCodec.crf : ffmpegQualityOverrides.crf,
|
|
38
|
-
preset: typeof perCodec?.preset === 'string' ? perCodec.preset : ffmpegQualityOverrides.preset,
|
|
39
|
-
};
|
|
13
|
+
return getQualityConfig(codecName);
|
|
40
14
|
}
|
|
41
15
|
//# sourceMappingURL=ffmpeg-quality.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffmpeg-quality.js","sourceRoot":"","sources":["../../src/config/ffmpeg-quality.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ffmpeg-quality.js","sourceRoot":"","sources":["../../src/config/ffmpeg-quality.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAQzD;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,SAAiB;IACzD,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified WebCodecs configuration
|
|
3
|
+
*
|
|
4
|
+
* Loads configuration from webcodecs-config.js (or WEBCODECS_CONFIG env var).
|
|
5
|
+
* All settings are optional - omit or comment out to use defaults.
|
|
6
|
+
*/
|
|
7
|
+
import type { HardwareAccelerationMethod } from '../hardware/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Per-codec configuration overrides
|
|
10
|
+
*/
|
|
11
|
+
export interface CodecConfig {
|
|
12
|
+
/** CRF value for quality-based encoding */
|
|
13
|
+
crf?: number;
|
|
14
|
+
/** Encoder preset (e.g., 'fast', 'medium', 'slow') */
|
|
15
|
+
preset?: string;
|
|
16
|
+
/** Hardware acceleration priority order for this codec */
|
|
17
|
+
hwaccel?: HardwareAccelerationMethod[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* WebCodecs configuration options
|
|
21
|
+
*/
|
|
22
|
+
export interface WebCodecsConfig {
|
|
23
|
+
/** Global CRF value (overridden by perCodec) */
|
|
24
|
+
crf?: number;
|
|
25
|
+
/** Global encoder preset (overridden by perCodec) */
|
|
26
|
+
preset?: string;
|
|
27
|
+
/** Global hardware acceleration priority order (overridden by perCodec) */
|
|
28
|
+
hwaccel?: HardwareAccelerationMethod[];
|
|
29
|
+
/** Per-codec overrides */
|
|
30
|
+
perCodec?: Record<string, CodecConfig>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the loaded configuration (cached)
|
|
34
|
+
*/
|
|
35
|
+
export declare function getConfig(): Promise<WebCodecsConfig>;
|
|
36
|
+
/**
|
|
37
|
+
* Get configuration synchronously (loads config if not cached)
|
|
38
|
+
*/
|
|
39
|
+
export declare function getConfigSync(): WebCodecsConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Clear cached config (for testing or reloading)
|
|
42
|
+
*/
|
|
43
|
+
export declare function clearConfigCache(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get quality settings for a specific codec
|
|
46
|
+
*/
|
|
47
|
+
export declare function getQualityConfig(codecName: string): {
|
|
48
|
+
crf?: number;
|
|
49
|
+
preset?: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Get hardware acceleration priority order for a specific codec
|
|
53
|
+
* Returns undefined if no override is configured (use default priorities)
|
|
54
|
+
* Returns empty array if explicitly set to [] (force software encoding)
|
|
55
|
+
*/
|
|
56
|
+
export declare function getHwaccelConfig(codecName: string): HardwareAccelerationMethod[] | undefined;
|
|
57
|
+
export declare const webCodecsConfig: WebCodecsConfig;
|
|
58
|
+
//# sourceMappingURL=webcodecs-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs-config.d.ts","sourceRoot":"","sources":["../../src/config/webcodecs-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAKvE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,0BAA0B,EAAE,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACvC,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACxC;AAgGD;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC,CAK1D;AAuCD;;GAEG;AACH,wBAAgB,aAAa,IAAI,eAAe,CAK/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CASrF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,0BAA0B,EAAE,GAAG,SAAS,CAiB5F;AAGD,eAAO,MAAM,eAAe,iBAAqB,CAAC"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified WebCodecs configuration
|
|
3
|
+
*
|
|
4
|
+
* Loads configuration from webcodecs-config.js (or WEBCODECS_CONFIG env var).
|
|
5
|
+
* All settings are optional - omit or comment out to use defaults.
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { pathToFileURL } from 'url';
|
|
10
|
+
import { createRequire } from 'module';
|
|
11
|
+
// Create require function for synchronous config loading
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const DEFAULT_CONFIG = {};
|
|
14
|
+
let cachedConfig = null;
|
|
15
|
+
/**
|
|
16
|
+
* Validate and sanitize raw config object
|
|
17
|
+
*/
|
|
18
|
+
function sanitizeConfig(raw) {
|
|
19
|
+
if (!raw || typeof raw !== 'object') {
|
|
20
|
+
return DEFAULT_CONFIG;
|
|
21
|
+
}
|
|
22
|
+
const src = raw;
|
|
23
|
+
const config = {};
|
|
24
|
+
// Global quality settings
|
|
25
|
+
if (typeof src.crf === 'number') {
|
|
26
|
+
config.crf = src.crf;
|
|
27
|
+
}
|
|
28
|
+
if (typeof src.preset === 'string') {
|
|
29
|
+
config.preset = src.preset;
|
|
30
|
+
}
|
|
31
|
+
// Global hwaccel order
|
|
32
|
+
if (Array.isArray(src.hwaccel)) {
|
|
33
|
+
config.hwaccel = src.hwaccel.filter((m) => typeof m === 'string');
|
|
34
|
+
}
|
|
35
|
+
// Per-codec overrides
|
|
36
|
+
if (src.perCodec && typeof src.perCodec === 'object') {
|
|
37
|
+
config.perCodec = {};
|
|
38
|
+
for (const [codec, codecConfig] of Object.entries(src.perCodec)) {
|
|
39
|
+
if (codecConfig && typeof codecConfig === 'object') {
|
|
40
|
+
const cc = codecConfig;
|
|
41
|
+
const parsed = {};
|
|
42
|
+
if (typeof cc.crf === 'number') {
|
|
43
|
+
parsed.crf = cc.crf;
|
|
44
|
+
}
|
|
45
|
+
if (typeof cc.preset === 'string') {
|
|
46
|
+
parsed.preset = cc.preset;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(cc.hwaccel)) {
|
|
49
|
+
parsed.hwaccel = cc.hwaccel.filter((m) => typeof m === 'string');
|
|
50
|
+
}
|
|
51
|
+
if (Object.keys(parsed).length > 0) {
|
|
52
|
+
config.perCodec[codec.toLowerCase()] = parsed;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return config;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Load configuration from file
|
|
61
|
+
*/
|
|
62
|
+
async function loadConfig() {
|
|
63
|
+
// Check for config file path in env
|
|
64
|
+
if (process.env.WEBCODECS_CONFIG) {
|
|
65
|
+
const configPath = process.env.WEBCODECS_CONFIG;
|
|
66
|
+
if (fs.existsSync(configPath)) {
|
|
67
|
+
try {
|
|
68
|
+
const mod = await import(pathToFileURL(configPath).href);
|
|
69
|
+
const raw = mod?.default ?? mod?.webCodecsConfig ?? mod;
|
|
70
|
+
return sanitizeConfig(raw);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Fall through to defaults
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return DEFAULT_CONFIG;
|
|
77
|
+
}
|
|
78
|
+
// Try config file in current working directory
|
|
79
|
+
const configPath = path.join(process.cwd(), 'webcodecs-config.js');
|
|
80
|
+
if (fs.existsSync(configPath)) {
|
|
81
|
+
try {
|
|
82
|
+
const mod = await import(pathToFileURL(configPath).href);
|
|
83
|
+
const raw = mod?.default ?? mod?.webCodecsConfig ?? mod;
|
|
84
|
+
return sanitizeConfig(raw);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Fall through to defaults
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return DEFAULT_CONFIG;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get the loaded configuration (cached)
|
|
94
|
+
*/
|
|
95
|
+
export async function getConfig() {
|
|
96
|
+
if (cachedConfig === null) {
|
|
97
|
+
cachedConfig = await loadConfig();
|
|
98
|
+
}
|
|
99
|
+
return cachedConfig;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Load configuration synchronously using require
|
|
103
|
+
*/
|
|
104
|
+
function loadConfigSync() {
|
|
105
|
+
// Check for config file path in env
|
|
106
|
+
if (process.env.WEBCODECS_CONFIG) {
|
|
107
|
+
const configPath = process.env.WEBCODECS_CONFIG;
|
|
108
|
+
if (fs.existsSync(configPath)) {
|
|
109
|
+
try {
|
|
110
|
+
// Clear require cache to allow reloading
|
|
111
|
+
delete require.cache[require.resolve(configPath)];
|
|
112
|
+
const mod = require(configPath);
|
|
113
|
+
const raw = mod?.default ?? mod?.webCodecsConfig ?? mod;
|
|
114
|
+
return sanitizeConfig(raw);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Fall through to defaults
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return DEFAULT_CONFIG;
|
|
121
|
+
}
|
|
122
|
+
// Try config file in current working directory
|
|
123
|
+
const configPath = path.join(process.cwd(), 'webcodecs-config.js');
|
|
124
|
+
if (fs.existsSync(configPath)) {
|
|
125
|
+
try {
|
|
126
|
+
delete require.cache[require.resolve(configPath)];
|
|
127
|
+
const mod = require(configPath);
|
|
128
|
+
const raw = mod?.default ?? mod?.webCodecsConfig ?? mod;
|
|
129
|
+
return sanitizeConfig(raw);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Fall through to defaults
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return DEFAULT_CONFIG;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get configuration synchronously (loads config if not cached)
|
|
139
|
+
*/
|
|
140
|
+
export function getConfigSync() {
|
|
141
|
+
if (cachedConfig === null) {
|
|
142
|
+
cachedConfig = loadConfigSync();
|
|
143
|
+
}
|
|
144
|
+
return cachedConfig;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Clear cached config (for testing or reloading)
|
|
148
|
+
*/
|
|
149
|
+
export function clearConfigCache() {
|
|
150
|
+
cachedConfig = null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get quality settings for a specific codec
|
|
154
|
+
*/
|
|
155
|
+
export function getQualityConfig(codecName) {
|
|
156
|
+
const config = getConfigSync();
|
|
157
|
+
const key = codecName.toLowerCase();
|
|
158
|
+
const perCodec = config.perCodec?.[key];
|
|
159
|
+
return {
|
|
160
|
+
crf: perCodec?.crf ?? config.crf,
|
|
161
|
+
preset: perCodec?.preset ?? config.preset,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get hardware acceleration priority order for a specific codec
|
|
166
|
+
* Returns undefined if no override is configured (use default priorities)
|
|
167
|
+
* Returns empty array if explicitly set to [] (force software encoding)
|
|
168
|
+
*/
|
|
169
|
+
export function getHwaccelConfig(codecName) {
|
|
170
|
+
const config = getConfigSync();
|
|
171
|
+
const key = codecName.toLowerCase();
|
|
172
|
+
// Per-codec hwaccel takes precedence (including empty array)
|
|
173
|
+
const perCodec = config.perCodec?.[key]?.hwaccel;
|
|
174
|
+
if (perCodec !== undefined) {
|
|
175
|
+
return perCodec;
|
|
176
|
+
}
|
|
177
|
+
// Fall back to global hwaccel (including empty array)
|
|
178
|
+
if (config.hwaccel !== undefined) {
|
|
179
|
+
return config.hwaccel;
|
|
180
|
+
}
|
|
181
|
+
// No override - use default priorities
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
// Load config on module init
|
|
185
|
+
export const webCodecsConfig = await loadConfig();
|
|
186
|
+
cachedConfig = webCodecsConfig;
|
|
187
|
+
//# sourceMappingURL=webcodecs-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webcodecs-config.js","sourceRoot":"","sources":["../../src/config/webcodecs-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,6 +49,8 @@ export declare class Demuxer {
|
|
|
49
49
|
private _audioStreamIndex;
|
|
50
50
|
private _videoTimeBase;
|
|
51
51
|
private _audioTimeBase;
|
|
52
|
+
private _videoTimeMultiplier;
|
|
53
|
+
private _audioTimeMultiplier;
|
|
52
54
|
constructor(config: DemuxerConfig);
|
|
53
55
|
/**
|
|
54
56
|
* Open the container file and parse stream information
|
|
@@ -71,7 +73,7 @@ export declare class Demuxer {
|
|
|
71
73
|
*/
|
|
72
74
|
get duration(): number | undefined;
|
|
73
75
|
/**
|
|
74
|
-
* Convert timestamp from stream time base to microseconds
|
|
76
|
+
* Convert timestamp from stream time base to microseconds using pre-calculated multiplier
|
|
75
77
|
*/
|
|
76
78
|
private toMicroseconds;
|
|
77
79
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Demuxer.d.ts","sourceRoot":"","sources":["../../src/containers/Demuxer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAA8B,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAA8B,MAAM,8BAA8B,CAAC;AAG7F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAC/F,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AA+C/F;;GAEG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,cAAc,CAA6C;
|
|
1
|
+
{"version":3,"file":"Demuxer.d.ts","sourceRoot":"","sources":["../../src/containers/Demuxer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAA8B,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAA8B,MAAM,8BAA8B,CAAC;AAG7F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAC/F,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AA+C/F;;GAEG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,cAAc,CAA6C;IAEnE,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,oBAAoB,CAAa;gBAE7B,MAAM,EAAE,aAAa;IAIjC;;OAEG;IACG,IAAI,CAAC,OAAO,GAAE,MAA8B,GAAG,OAAO,CAAC,IAAI,CAAC;IAgDlE;;OAEG;IACH,IAAI,WAAW,IAAI,iBAAiB,GAAG,IAAI,CAE1C;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,iBAAiB,GAAG,IAAI,CAE1C;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAEjC;IAED;;OAEG;IACH,OAAO,CAAC,cAAc;IAKtB;;OAEG;IACI,WAAW,IAAI,cAAc,CAAC,iBAAiB,CAAC;IAyBvD;;OAEG;IACI,WAAW,IAAI,cAAc,CAAC,iBAAiB,CAAC;IAyBvD;;;OAGG;IACI,MAAM,IAAI,cAAc,CAC3B;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,GACtE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CACzE;IAuCD;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE;QACnB,YAAY,CAAC,EAAE,kBAAkB,CAAC;QAClC,YAAY,CAAC,EAAE,kBAAkB,CAAC;KACnC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;OAEG;IACH,IAAI,MAAM,IAAI,aAAa,GAAG,IAAI,CAEjC;CACF"}
|
|
@@ -63,6 +63,9 @@ export class Demuxer {
|
|
|
63
63
|
_audioStreamIndex = -1;
|
|
64
64
|
_videoTimeBase = null;
|
|
65
65
|
_audioTimeBase = null;
|
|
66
|
+
// Pre-calculated multipliers for timestamp conversion (avoids division per packet)
|
|
67
|
+
_videoTimeMultiplier = 0;
|
|
68
|
+
_audioTimeMultiplier = 0;
|
|
66
69
|
constructor(config) {
|
|
67
70
|
this.path = config.path;
|
|
68
71
|
}
|
|
@@ -79,6 +82,8 @@ export class Demuxer {
|
|
|
79
82
|
num: videoStream.timeBase.num,
|
|
80
83
|
den: videoStream.timeBase.den,
|
|
81
84
|
};
|
|
85
|
+
// Pre-calculate multiplier: (num * 1_000_000) / den
|
|
86
|
+
this._videoTimeMultiplier = (videoStream.timeBase.num * 1_000_000) / videoStream.timeBase.den;
|
|
82
87
|
const cp = videoStream.codecpar;
|
|
83
88
|
this._videoConfig = {
|
|
84
89
|
codec: mapVideoCodecId(cp.codecId, cp.extradata),
|
|
@@ -95,6 +100,8 @@ export class Demuxer {
|
|
|
95
100
|
num: audioStream.timeBase.num,
|
|
96
101
|
den: audioStream.timeBase.den,
|
|
97
102
|
};
|
|
103
|
+
// Pre-calculate multiplier: (num * 1_000_000) / den
|
|
104
|
+
this._audioTimeMultiplier = (audioStream.timeBase.num * 1_000_000) / audioStream.timeBase.den;
|
|
98
105
|
const cp = audioStream.codecpar;
|
|
99
106
|
this._audioConfig = {
|
|
100
107
|
codec: mapAudioCodecId(cp.codecId),
|
|
@@ -129,32 +136,32 @@ export class Demuxer {
|
|
|
129
136
|
return this.demuxer?.duration;
|
|
130
137
|
}
|
|
131
138
|
/**
|
|
132
|
-
* Convert timestamp from stream time base to microseconds
|
|
139
|
+
* Convert timestamp from stream time base to microseconds using pre-calculated multiplier
|
|
133
140
|
*/
|
|
134
|
-
toMicroseconds(pts,
|
|
141
|
+
toMicroseconds(pts, multiplier) {
|
|
135
142
|
const ptsNum = typeof pts === 'bigint' ? Number(pts) : pts;
|
|
136
|
-
return Math.round(
|
|
143
|
+
return Math.round(ptsNum * multiplier);
|
|
137
144
|
}
|
|
138
145
|
/**
|
|
139
146
|
* Iterate over video chunks
|
|
140
147
|
*/
|
|
141
148
|
async *videoChunks() {
|
|
142
|
-
if (!this.demuxer || this._videoStreamIndex < 0 || !this._videoConfig || !this.
|
|
149
|
+
if (!this.demuxer || this._videoStreamIndex < 0 || !this._videoConfig || !this._videoTimeMultiplier) {
|
|
143
150
|
return;
|
|
144
151
|
}
|
|
145
152
|
for await (const packet of this.demuxer.packets()) {
|
|
146
153
|
if (!packet || !packet.data)
|
|
147
154
|
continue;
|
|
148
155
|
if (packet.streamIndex === this._videoStreamIndex) {
|
|
149
|
-
const timestamp = this.toMicroseconds(packet.pts, this.
|
|
156
|
+
const timestamp = this.toMicroseconds(packet.pts, this._videoTimeMultiplier);
|
|
150
157
|
const duration = packet.duration
|
|
151
|
-
? this.toMicroseconds(packet.duration, this.
|
|
158
|
+
? this.toMicroseconds(packet.duration, this._videoTimeMultiplier)
|
|
152
159
|
: undefined;
|
|
153
160
|
const chunk = new EncodedVideoChunk({
|
|
154
161
|
type: packet.isKeyframe ? 'key' : 'delta',
|
|
155
162
|
timestamp,
|
|
156
163
|
duration,
|
|
157
|
-
data:
|
|
164
|
+
data: packet.data.slice(),
|
|
158
165
|
});
|
|
159
166
|
yield chunk;
|
|
160
167
|
}
|
|
@@ -164,22 +171,22 @@ export class Demuxer {
|
|
|
164
171
|
* Iterate over audio chunks
|
|
165
172
|
*/
|
|
166
173
|
async *audioChunks() {
|
|
167
|
-
if (!this.demuxer || this._audioStreamIndex < 0 || !this._audioConfig || !this.
|
|
174
|
+
if (!this.demuxer || this._audioStreamIndex < 0 || !this._audioConfig || !this._audioTimeMultiplier) {
|
|
168
175
|
return;
|
|
169
176
|
}
|
|
170
177
|
for await (const packet of this.demuxer.packets()) {
|
|
171
178
|
if (!packet || !packet.data)
|
|
172
179
|
continue;
|
|
173
180
|
if (packet.streamIndex === this._audioStreamIndex) {
|
|
174
|
-
const timestamp = this.toMicroseconds(packet.pts, this.
|
|
181
|
+
const timestamp = this.toMicroseconds(packet.pts, this._audioTimeMultiplier);
|
|
175
182
|
const duration = packet.duration
|
|
176
|
-
? this.toMicroseconds(packet.duration, this.
|
|
183
|
+
? this.toMicroseconds(packet.duration, this._audioTimeMultiplier)
|
|
177
184
|
: undefined;
|
|
178
185
|
const chunk = new EncodedAudioChunk({
|
|
179
186
|
type: packet.isKeyframe ? 'key' : 'delta',
|
|
180
187
|
timestamp,
|
|
181
188
|
duration,
|
|
182
|
-
data:
|
|
189
|
+
data: packet.data.slice(),
|
|
183
190
|
});
|
|
184
191
|
yield chunk;
|
|
185
192
|
}
|
|
@@ -196,29 +203,29 @@ export class Demuxer {
|
|
|
196
203
|
for await (const packet of this.demuxer.packets()) {
|
|
197
204
|
if (!packet || !packet.data)
|
|
198
205
|
continue;
|
|
199
|
-
if (packet.streamIndex === this._videoStreamIndex && this._videoConfig && this.
|
|
200
|
-
const timestamp = this.toMicroseconds(packet.pts, this.
|
|
206
|
+
if (packet.streamIndex === this._videoStreamIndex && this._videoConfig && this._videoTimeMultiplier) {
|
|
207
|
+
const timestamp = this.toMicroseconds(packet.pts, this._videoTimeMultiplier);
|
|
201
208
|
const duration = packet.duration
|
|
202
|
-
? this.toMicroseconds(packet.duration, this.
|
|
209
|
+
? this.toMicroseconds(packet.duration, this._videoTimeMultiplier)
|
|
203
210
|
: undefined;
|
|
204
211
|
const chunk = new EncodedVideoChunk({
|
|
205
212
|
type: packet.isKeyframe ? 'key' : 'delta',
|
|
206
213
|
timestamp,
|
|
207
214
|
duration,
|
|
208
|
-
data:
|
|
215
|
+
data: packet.data.slice(),
|
|
209
216
|
});
|
|
210
217
|
yield { type: 'video', chunk, config: this._videoConfig };
|
|
211
218
|
}
|
|
212
|
-
else if (packet.streamIndex === this._audioStreamIndex && this._audioConfig && this.
|
|
213
|
-
const timestamp = this.toMicroseconds(packet.pts, this.
|
|
219
|
+
else if (packet.streamIndex === this._audioStreamIndex && this._audioConfig && this._audioTimeMultiplier) {
|
|
220
|
+
const timestamp = this.toMicroseconds(packet.pts, this._audioTimeMultiplier);
|
|
214
221
|
const duration = packet.duration
|
|
215
|
-
? this.toMicroseconds(packet.duration, this.
|
|
222
|
+
? this.toMicroseconds(packet.duration, this._audioTimeMultiplier)
|
|
216
223
|
: undefined;
|
|
217
224
|
const chunk = new EncodedAudioChunk({
|
|
218
225
|
type: packet.isKeyframe ? 'key' : 'delta',
|
|
219
226
|
timestamp,
|
|
220
227
|
duration,
|
|
221
|
-
data:
|
|
228
|
+
data: packet.data.slice(),
|
|
222
229
|
});
|
|
223
230
|
yield { type: 'audio', chunk, config: this._audioConfig };
|
|
224
231
|
}
|