@remotion/webcodecs 4.0.229 → 4.0.231
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 +12 -1
- package/dist/arraybuffer-to-uint8-array.d.ts +1 -0
- package/dist/arraybuffer-to-uint8-array.js +7 -0
- package/dist/audio-decoder.d.ts +2 -2
- package/dist/audio-encoder-config.js +15 -2
- package/dist/audio-encoder.d.ts +2 -1
- package/dist/audio-encoder.js +17 -5
- package/dist/browser-quirks.d.ts +2 -0
- package/dist/browser-quirks.js +11 -0
- package/dist/can-copy-audio-track.d.ts +2 -4
- package/dist/can-copy-audio-track.js +7 -4
- package/dist/can-copy-video-track.d.ts +2 -4
- package/dist/can-copy-video-track.js +6 -6
- package/dist/can-reencode-audio-track.js +1 -6
- package/dist/can-reencode-video-track.js +1 -0
- package/dist/choose-correct-avc1-profile.d.ts +5 -0
- package/dist/choose-correct-avc1-profile.js +54 -0
- package/dist/codec-id.d.ts +7 -4
- package/dist/codec-id.js +28 -5
- package/dist/convert-encoded-chunk.d.ts +2 -1
- package/dist/convert-encoded-chunk.js +25 -2
- package/dist/convert-media.d.ts +13 -12
- package/dist/convert-media.js +62 -46
- package/dist/convert-to-correct-videoframe.d.ts +9 -0
- package/dist/convert-to-correct-videoframe.js +28 -0
- package/dist/default-on-audio-track-handler.d.ts +2 -0
- package/dist/default-on-audio-track-handler.js +36 -0
- package/dist/default-on-video-track-handler.d.ts +2 -0
- package/dist/default-on-video-track-handler.js +29 -0
- package/dist/esm/index.mjs +493 -174
- package/dist/generate-output-filename.d.ts +2 -0
- package/dist/generate-output-filename.js +14 -0
- package/dist/get-default-audio-codec.d.ts +4 -0
- package/dist/get-default-audio-codec.js +13 -0
- package/dist/get-default-video-codec.d.ts +4 -0
- package/dist/get-default-video-codec.js +10 -0
- package/dist/index.d.ts +12 -8
- package/dist/index.js +12 -1
- package/dist/io-manager/io-synchronizer.js +2 -2
- package/dist/{resolve-audio-action.d.ts → on-audio-track-handler.d.ts} +5 -5
- package/dist/on-audio-track-handler.js +2 -0
- package/dist/on-audio-track.d.ts +7 -8
- package/dist/on-audio-track.js +55 -16
- package/dist/on-frame.d.ts +4 -4
- package/dist/on-frame.js +15 -9
- package/dist/{resolve-video-action.d.ts → on-video-track-handler.d.ts} +5 -5
- package/dist/on-video-track-handler.js +2 -0
- package/dist/on-video-track.d.ts +8 -8
- package/dist/on-video-track.js +49 -15
- package/dist/set-remotion-imported.d.ts +6 -0
- package/dist/set-remotion-imported.js +25 -0
- package/dist/throttled-state-update.d.ts +13 -0
- package/dist/throttled-state-update.js +49 -0
- package/dist/video-decoder.d.ts +2 -2
- package/dist/video-decoder.js +5 -0
- package/dist/video-encoder-config.d.ts +2 -1
- package/dist/video-encoder-config.js +9 -2
- package/dist/video-encoder.d.ts +4 -2
- package/dist/video-encoder.js +12 -10
- package/package.json +4 -3
- package/dist/can-reencode-audio.d.ts +0 -7
- package/dist/can-reencode-audio.js +0 -21
- package/dist/can-reencode-video.d.ts +0 -6
- package/dist/can-reencode-video.js +0 -15
- package/dist/event-emitter.d.ts +0 -25
- package/dist/event-emitter.js +0 -23
- package/dist/polyfill-encoded-audio-chunk.d.ts +0 -3
- package/dist/polyfill-encoded-audio-chunk.js +0 -5
- package/dist/resolve-audio-action.js +0 -32
- package/dist/resolve-video-action.js +0 -26
- package/dist/wait-until-return.d.ts +0 -4
- package/dist/wait-until-return.js +0 -14
package/README.md
CHANGED
|
@@ -15,4 +15,15 @@ Remove the `^` character from the version number to use the exact version.
|
|
|
15
15
|
|
|
16
16
|
## Usage
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
See the [documentation](https://remotion.dev/webcodecs) for more information.
|
|
19
|
+
|
|
20
|
+
## License
|
|
21
|
+
This package is licensed under the [/docs/license](Remotion License).
|
|
22
|
+
We consider a team of 4 or more people a "company".
|
|
23
|
+
|
|
24
|
+
**For "companies"**: A Remotion Company license needs to be obtained to use this package.
|
|
25
|
+
In a future version of `@remotion/webcodecs`, this package will also require the purchase of a newly created "WebCodecs Conversion Seat". [Get in touch](https://remotion.dev/contact) with us if you are planning to use this package.
|
|
26
|
+
|
|
27
|
+
**For individuals and teams up to 3**: You can use this package for free.
|
|
28
|
+
|
|
29
|
+
This is a short, non-binding explanation of our license. See the [https://remotion.dev/docs/license](License) itself for more details.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const arrayBufferToUint8Array: (buffer: ArrayBuffer | null) => Uint8Array | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.arrayBufferToUint8Array = void 0;
|
|
4
|
+
const arrayBufferToUint8Array = (buffer) => {
|
|
5
|
+
return buffer ? new Uint8Array(buffer) : null;
|
|
6
|
+
};
|
|
7
|
+
exports.arrayBufferToUint8Array = arrayBufferToUint8Array;
|
package/dist/audio-decoder.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AudioOrVideoSample, LogLevel } from '@remotion/media-parser';
|
|
2
2
|
export type WebCodecsAudioDecoder = {
|
|
3
|
-
processSample: (audioSample:
|
|
3
|
+
processSample: (audioSample: AudioOrVideoSample) => Promise<void>;
|
|
4
4
|
waitForFinish: () => Promise<void>;
|
|
5
5
|
close: () => void;
|
|
6
6
|
flush: () => Promise<void>;
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getAudioEncoderConfig = void 0;
|
|
4
|
+
const getCodecString = (audioCodec) => {
|
|
5
|
+
if (audioCodec === 'opus') {
|
|
6
|
+
return 'opus';
|
|
7
|
+
}
|
|
8
|
+
if (audioCodec === 'aac') {
|
|
9
|
+
return 'mp4a.40.02';
|
|
10
|
+
}
|
|
11
|
+
throw new Error(`Unsupported audio codec: ${audioCodec}`);
|
|
12
|
+
};
|
|
4
13
|
const getAudioEncoderConfig = async (config) => {
|
|
14
|
+
const actualConfig = {
|
|
15
|
+
...config,
|
|
16
|
+
codec: getCodecString(config.codec),
|
|
17
|
+
};
|
|
5
18
|
if (typeof AudioEncoder === 'undefined') {
|
|
6
19
|
return null;
|
|
7
20
|
}
|
|
8
|
-
if ((await AudioEncoder.isConfigSupported(
|
|
9
|
-
return
|
|
21
|
+
if ((await AudioEncoder.isConfigSupported(actualConfig)).supported) {
|
|
22
|
+
return actualConfig;
|
|
10
23
|
}
|
|
11
24
|
return null;
|
|
12
25
|
};
|
package/dist/audio-encoder.d.ts
CHANGED
|
@@ -6,11 +6,12 @@ export type WebCodecsAudioEncoder = {
|
|
|
6
6
|
close: () => void;
|
|
7
7
|
flush: () => Promise<void>;
|
|
8
8
|
};
|
|
9
|
-
export declare const createAudioEncoder: ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, }: {
|
|
9
|
+
export declare const createAudioEncoder: ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, onNewAudioSampleRate, }: {
|
|
10
10
|
onChunk: (chunk: EncodedAudioChunk) => Promise<void>;
|
|
11
11
|
onError: (error: DOMException) => void;
|
|
12
12
|
codec: ConvertMediaAudioCodec;
|
|
13
13
|
signal: AbortSignal;
|
|
14
14
|
config: AudioEncoderConfig;
|
|
15
15
|
logLevel: LogLevel;
|
|
16
|
+
onNewAudioSampleRate: (sampleRate: number) => void;
|
|
16
17
|
}) => WebCodecsAudioEncoder;
|
package/dist/audio-encoder.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createAudioEncoder = void 0;
|
|
4
4
|
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
|
5
|
-
const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, }) => {
|
|
5
|
+
const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, onNewAudioSampleRate, }) => {
|
|
6
6
|
if (signal.aborted) {
|
|
7
7
|
throw new Error('Not creating audio encoder, already aborted');
|
|
8
8
|
}
|
|
@@ -42,19 +42,31 @@ const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEnco
|
|
|
42
42
|
close();
|
|
43
43
|
};
|
|
44
44
|
signal.addEventListener('abort', onAbort);
|
|
45
|
-
if (codec !== 'opus') {
|
|
46
|
-
throw new Error('Only `codec: "opus"` is supported currently');
|
|
45
|
+
if (codec !== 'opus' && codec !== 'aac') {
|
|
46
|
+
throw new Error('Only `codec: "opus"` and `codec: "aac"` is supported currently');
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
const wantedSampleRate = audioEncoderConfig.sampleRate;
|
|
49
49
|
const encodeFrame = async (audioData) => {
|
|
50
50
|
if (encoder.state === 'closed') {
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
|
-
await ioSynchronizer.waitFor({ unemitted:
|
|
53
|
+
await ioSynchronizer.waitFor({ unemitted: 20, _unprocessed: 20 });
|
|
54
54
|
// @ts-expect-error - can have changed in the meanwhile
|
|
55
55
|
if (encoder.state === 'closed') {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
|
+
if (encoder.state === 'unconfigured') {
|
|
59
|
+
if (audioData.sampleRate === wantedSampleRate) {
|
|
60
|
+
encoder.configure(audioEncoderConfig);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
encoder.configure({
|
|
64
|
+
...audioEncoderConfig,
|
|
65
|
+
sampleRate: audioData.sampleRate,
|
|
66
|
+
});
|
|
67
|
+
onNewAudioSampleRate(audioData.sampleRate);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
58
70
|
encoder.encode(audioData);
|
|
59
71
|
ioSynchronizer.inputItem(audioData.timestamp, true);
|
|
60
72
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSafari = exports.isFirefox = void 0;
|
|
4
|
+
const isFirefox = () => {
|
|
5
|
+
return navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
|
6
|
+
};
|
|
7
|
+
exports.isFirefox = isFirefox;
|
|
8
|
+
const isSafari = () => {
|
|
9
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
10
|
+
};
|
|
11
|
+
exports.isSafari = isSafari;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { MediaParserAudioCodec } from '@remotion/media-parser';
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
export declare const canCopyAudioTrack: ({ inputCodec, outputCodec, container, }: {
|
|
2
|
+
import type { ConvertMediaContainer } from './codec-id';
|
|
3
|
+
export declare const canCopyAudioTrack: ({ inputCodec, container, }: {
|
|
5
4
|
inputCodec: MediaParserAudioCodec;
|
|
6
|
-
outputCodec: ConvertMediaAudioCodec;
|
|
7
5
|
container: ConvertMediaContainer;
|
|
8
6
|
}) => boolean;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.canCopyAudioTrack = void 0;
|
|
4
|
-
const canCopyAudioTrack = ({ inputCodec,
|
|
5
|
-
if (
|
|
6
|
-
return inputCodec === 'opus'
|
|
4
|
+
const canCopyAudioTrack = ({ inputCodec, container, }) => {
|
|
5
|
+
if (container === 'webm') {
|
|
6
|
+
return inputCodec === 'opus';
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
if (container === 'mp4') {
|
|
9
|
+
return inputCodec === 'aac';
|
|
10
|
+
}
|
|
11
|
+
throw new Error(`Unhandled codec: ${container}`);
|
|
9
12
|
};
|
|
10
13
|
exports.canCopyAudioTrack = canCopyAudioTrack;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { MediaParserVideoCodec } from '@remotion/media-parser';
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
export declare const canCopyVideoTrack: ({ inputCodec, outputCodec, container, }: {
|
|
2
|
+
import type { ConvertMediaContainer } from './codec-id';
|
|
3
|
+
export declare const canCopyVideoTrack: ({ inputCodec, container, }: {
|
|
5
4
|
inputCodec: MediaParserVideoCodec;
|
|
6
|
-
outputCodec: ConvertMediaVideoCodec;
|
|
7
5
|
container: ConvertMediaContainer;
|
|
8
6
|
}) => boolean;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.canCopyVideoTrack = void 0;
|
|
4
|
-
const canCopyVideoTrack = ({ inputCodec,
|
|
5
|
-
if (
|
|
6
|
-
return inputCodec === 'vp8'
|
|
4
|
+
const canCopyVideoTrack = ({ inputCodec, container, }) => {
|
|
5
|
+
if (container === 'webm') {
|
|
6
|
+
return inputCodec === 'vp8' || inputCodec === 'vp9';
|
|
7
7
|
}
|
|
8
|
-
if (
|
|
9
|
-
return inputCodec === '
|
|
8
|
+
if (container === 'mp4') {
|
|
9
|
+
return inputCodec === 'h264' || inputCodec === 'h265';
|
|
10
10
|
}
|
|
11
|
-
throw new Error(`Unhandled codec: ${
|
|
11
|
+
throw new Error(`Unhandled codec: ${container}`);
|
|
12
12
|
};
|
|
13
13
|
exports.canCopyVideoTrack = canCopyVideoTrack;
|
|
@@ -4,12 +4,7 @@ exports.canReencodeAudioTrack = void 0;
|
|
|
4
4
|
const audio_decoder_config_1 = require("./audio-decoder-config");
|
|
5
5
|
const audio_encoder_config_1 = require("./audio-encoder-config");
|
|
6
6
|
const canReencodeAudioTrack = async ({ track, audioCodec, bitrate, }) => {
|
|
7
|
-
const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)(
|
|
8
|
-
codec: track.codec,
|
|
9
|
-
numberOfChannels: track.numberOfChannels,
|
|
10
|
-
sampleRate: track.sampleRate,
|
|
11
|
-
description: track.description,
|
|
12
|
-
});
|
|
7
|
+
const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)(track);
|
|
13
8
|
const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
|
|
14
9
|
codec: audioCodec,
|
|
15
10
|
numberOfChannels: track.numberOfChannels,
|
|
@@ -8,6 +8,7 @@ const canReencodeVideoTrack = async ({ videoCodec, track, }) => {
|
|
|
8
8
|
codec: videoCodec,
|
|
9
9
|
height: track.displayAspectHeight,
|
|
10
10
|
width: track.displayAspectWidth,
|
|
11
|
+
fps: track.fps,
|
|
11
12
|
});
|
|
12
13
|
const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
|
|
13
14
|
return Boolean(videoDecoderConfig && videoEncoderConfig);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.chooseCorrectAvc1Profile = void 0;
|
|
4
|
+
const chooseCorrectAvc1Profile = ({ width, height, fps, }) => {
|
|
5
|
+
// 0x42 = Baseline profile
|
|
6
|
+
// 0x4D = Main profile
|
|
7
|
+
// 0x58 = Extended profile
|
|
8
|
+
// 0x64 = High profile
|
|
9
|
+
// According to Wikipedia, 0x64 is the most widely supported profile.
|
|
10
|
+
// So we always choose 0x64.
|
|
11
|
+
/**
|
|
12
|
+
Ignoring lower levels of <720p, let's only support Players that can play 720p and above.
|
|
13
|
+
• Level 3.1 = 1F (hex) -> 1,280×720@30.0 (5)
|
|
14
|
+
• Level 3.2 = 20 (hex) -> 1,280×1,024@42.2 (4)
|
|
15
|
+
• Level 4.0 = 28 (hex) -> 2,048×1,024@30.0 (4)
|
|
16
|
+
• Level 4.1 = 29 (hex) -> 2,048×1,024@30.0 (4)
|
|
17
|
+
• Level 4.2 = 2A (hex) -> 2,048×1,080@60.0 (4)
|
|
18
|
+
• Level 5.0 = 32 (hex) -> 3,672×1,536@26.7 (5)
|
|
19
|
+
• Level 5.1 = 33 (hex) -> 4,096×2,304@26.7 (5)
|
|
20
|
+
• Level 5.2 = 34 (hex) -> 4,096×2,304@56.3 (5)
|
|
21
|
+
• Level 6.0 = 3C (hex) -> 8,192×4,320@30.2 (5)
|
|
22
|
+
• Level 6.1 = 3D (hex) -> 8,192×4,320@60.4 (5)
|
|
23
|
+
• Level 6.2 = 3E (hex) -> 8,192×4,320@120.8 (5)
|
|
24
|
+
*/
|
|
25
|
+
const profiles = [
|
|
26
|
+
{ level: '3.1', hex: '1F', width: 1280, height: 720, fps: 30.0 },
|
|
27
|
+
{ level: '3.2', hex: '20', width: 1280, height: 1024, fps: 42.2 },
|
|
28
|
+
{ level: '4.0', hex: '28', width: 2048, height: 1024, fps: 30.0 },
|
|
29
|
+
{ level: '4.1', hex: '29', width: 2048, height: 1024, fps: 30.0 },
|
|
30
|
+
{ level: '4.2', hex: '2A', width: 2048, height: 1080, fps: 60.0 },
|
|
31
|
+
{ level: '5.0', hex: '32', width: 3672, height: 1536, fps: 26.7 },
|
|
32
|
+
{ level: '5.1', hex: '33', width: 4096, height: 2304, fps: 26.7 },
|
|
33
|
+
{ level: '5.2', hex: '34', width: 4096, height: 2304, fps: 56.3 },
|
|
34
|
+
{ level: '6.0', hex: '3C', width: 8192, height: 4320, fps: 30.2 },
|
|
35
|
+
{ level: '6.1', hex: '3D', width: 8192, height: 4320, fps: 60.4 },
|
|
36
|
+
{ level: '6.2', hex: '3E', width: 8192, height: 4320, fps: 120.8 },
|
|
37
|
+
];
|
|
38
|
+
const profile = profiles.find((p) => {
|
|
39
|
+
if (width > p.width) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (height > p.height) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// if has no fps, use 60 as a conservative fallback
|
|
46
|
+
const fallbackFps = fps !== null && fps !== void 0 ? fps : 60;
|
|
47
|
+
return fallbackFps <= p.fps;
|
|
48
|
+
});
|
|
49
|
+
if (!profile) {
|
|
50
|
+
throw new Error(`No suitable AVC1 profile found for ${width}x${height}@${fps}fps`);
|
|
51
|
+
}
|
|
52
|
+
return `avc1.6400${profile.hex}`;
|
|
53
|
+
};
|
|
54
|
+
exports.chooseCorrectAvc1Profile = chooseCorrectAvc1Profile;
|
package/dist/codec-id.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
declare const
|
|
2
|
-
export
|
|
1
|
+
declare const availableContainers: readonly ["webm", "mp4"];
|
|
2
|
+
export type ConvertMediaContainer = (typeof availableContainers)[number];
|
|
3
|
+
export declare const getAvailableContainers: () => readonly ConvertMediaContainer[];
|
|
4
|
+
declare const availableVideoCodecs: readonly ["vp8", "vp9", "h264"];
|
|
3
5
|
export type ConvertMediaVideoCodec = (typeof availableVideoCodecs)[number];
|
|
4
|
-
declare const
|
|
5
|
-
|
|
6
|
+
export declare const getAvailableVideoCodecs: (container: ConvertMediaContainer) => ConvertMediaVideoCodec[];
|
|
7
|
+
declare const availableAudioCodecs: readonly ["opus", "aac"];
|
|
8
|
+
export declare const getAvailableAudioCodecs: (container: ConvertMediaContainer) => ConvertMediaAudioCodec[];
|
|
6
9
|
export type ConvertMediaAudioCodec = (typeof availableAudioCodecs)[number];
|
|
7
10
|
export {};
|
package/dist/codec-id.js
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getAvailableAudioCodecs = exports.getAvailableVideoCodecs = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
exports.getAvailableAudioCodecs = exports.getAvailableVideoCodecs = exports.getAvailableContainers = void 0;
|
|
4
|
+
const availableContainers = ['webm', 'mp4'];
|
|
5
|
+
const getAvailableContainers = () => {
|
|
6
|
+
return availableContainers;
|
|
7
|
+
};
|
|
8
|
+
exports.getAvailableContainers = getAvailableContainers;
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
|
+
const availableVideoCodecs = ['vp8', 'vp9', 'h264'];
|
|
11
|
+
const getAvailableVideoCodecs = (container) => {
|
|
12
|
+
if (container === 'mp4') {
|
|
13
|
+
return ['h264'];
|
|
14
|
+
}
|
|
15
|
+
if (container === 'webm') {
|
|
16
|
+
return ['vp8', 'vp9'];
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`Unsupported container: ${container}`);
|
|
19
|
+
};
|
|
6
20
|
exports.getAvailableVideoCodecs = getAvailableVideoCodecs;
|
|
7
|
-
|
|
8
|
-
const
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
22
|
+
const availableAudioCodecs = ['opus', 'aac'];
|
|
23
|
+
const getAvailableAudioCodecs = (container) => {
|
|
24
|
+
if (container === 'mp4') {
|
|
25
|
+
return ['aac'];
|
|
26
|
+
}
|
|
27
|
+
if (container === 'webm') {
|
|
28
|
+
return ['opus'];
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`Unsupported container: ${container}`);
|
|
31
|
+
};
|
|
9
32
|
exports.getAvailableAudioCodecs = getAvailableAudioCodecs;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { AudioOrVideoSample } from '@remotion/media-parser';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const combineUint8Arrays: (arrays: Uint8Array[]) => Uint8Array;
|
|
3
|
+
export declare const convertEncodedChunk: (chunk: EncodedAudioChunk | EncodedVideoChunk, trackId: number) => AudioOrVideoSample;
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.convertEncodedChunk = void 0;
|
|
4
|
-
const
|
|
3
|
+
exports.convertEncodedChunk = exports.combineUint8Arrays = void 0;
|
|
4
|
+
const combineUint8Arrays = (arrays) => {
|
|
5
|
+
if (arrays.length === 0) {
|
|
6
|
+
return new Uint8Array([]);
|
|
7
|
+
}
|
|
8
|
+
if (arrays.length === 1) {
|
|
9
|
+
return arrays[0];
|
|
10
|
+
}
|
|
11
|
+
let totalLength = 0;
|
|
12
|
+
for (const array of arrays) {
|
|
13
|
+
totalLength += array.length;
|
|
14
|
+
}
|
|
15
|
+
const result = new Uint8Array(totalLength);
|
|
16
|
+
let offset = 0;
|
|
17
|
+
for (const array of arrays) {
|
|
18
|
+
result.set(array, offset);
|
|
19
|
+
offset += array.length;
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
};
|
|
23
|
+
exports.combineUint8Arrays = combineUint8Arrays;
|
|
24
|
+
const convertEncodedChunk = (chunk, trackId) => {
|
|
5
25
|
var _a;
|
|
6
26
|
const arr = new Uint8Array(chunk.byteLength);
|
|
7
27
|
chunk.copyTo(arr);
|
|
@@ -10,6 +30,9 @@ const convertEncodedChunk = (chunk) => {
|
|
|
10
30
|
duration: (_a = chunk.duration) !== null && _a !== void 0 ? _a : undefined,
|
|
11
31
|
timestamp: chunk.timestamp,
|
|
12
32
|
type: chunk.type,
|
|
33
|
+
cts: chunk.timestamp,
|
|
34
|
+
dts: chunk.timestamp,
|
|
35
|
+
trackId,
|
|
13
36
|
};
|
|
14
37
|
};
|
|
15
38
|
exports.convertEncodedChunk = convertEncodedChunk;
|
package/dist/convert-media.d.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* For licensing, see: https://remotion.dev/docs/webcodecs#license
|
|
4
4
|
*/
|
|
5
5
|
import type { LogLevel, Options, ParseMediaDynamicOptions, ParseMediaFields, ParseMediaOptions, VideoTrack, WriterInterface } from '@remotion/media-parser';
|
|
6
|
-
import type { ConvertMediaAudioCodec, ConvertMediaVideoCodec } from './codec-id';
|
|
7
|
-
import { type
|
|
8
|
-
import { type
|
|
9
|
-
export type
|
|
6
|
+
import type { ConvertMediaAudioCodec, ConvertMediaContainer, ConvertMediaVideoCodec } from './codec-id';
|
|
7
|
+
import { type ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
|
|
8
|
+
import { type ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
|
|
9
|
+
export type ConvertMediaProgress = {
|
|
10
10
|
decodedVideoFrames: number;
|
|
11
11
|
decodedAudioFrames: number;
|
|
12
12
|
encodedVideoFrames: number;
|
|
@@ -16,27 +16,28 @@ export type ConvertMediaState = {
|
|
|
16
16
|
expectedOutputDurationInMs: number | null;
|
|
17
17
|
overallProgress: number | null;
|
|
18
18
|
};
|
|
19
|
-
export type ConvertMediaContainer = 'webm';
|
|
20
19
|
export type ConvertMediaResult = {
|
|
21
20
|
save: () => Promise<Blob>;
|
|
22
21
|
remove: () => Promise<void>;
|
|
22
|
+
finalState: ConvertMediaProgress;
|
|
23
23
|
};
|
|
24
|
-
export type
|
|
24
|
+
export type ConvertMediaOnProgress = (state: ConvertMediaProgress) => void;
|
|
25
25
|
export type ConvertMediaOnVideoFrame = (options: {
|
|
26
26
|
frame: VideoFrame;
|
|
27
27
|
track: VideoTrack;
|
|
28
28
|
}) => Promise<VideoFrame> | VideoFrame;
|
|
29
|
-
export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame,
|
|
29
|
+
export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel, writer, progressIntervalInMs, ...more }: {
|
|
30
30
|
src: ParseMediaOptions<F>["src"];
|
|
31
31
|
container: ConvertMediaContainer;
|
|
32
32
|
onVideoFrame?: ConvertMediaOnVideoFrame;
|
|
33
|
-
|
|
34
|
-
videoCodec
|
|
35
|
-
audioCodec
|
|
33
|
+
onProgress?: ConvertMediaOnProgress;
|
|
34
|
+
videoCodec?: ConvertMediaVideoCodec;
|
|
35
|
+
audioCodec?: ConvertMediaAudioCodec;
|
|
36
36
|
signal?: AbortSignal;
|
|
37
|
-
onAudioTrack?:
|
|
38
|
-
onVideoTrack?:
|
|
37
|
+
onAudioTrack?: ConvertMediaOnAudioTrackHandler;
|
|
38
|
+
onVideoTrack?: ConvertMediaOnVideoTrackHandler;
|
|
39
39
|
reader?: ParseMediaOptions<F>["reader"];
|
|
40
40
|
logLevel?: LogLevel;
|
|
41
41
|
writer?: WriterInterface;
|
|
42
|
+
progressIntervalInMs?: number;
|
|
42
43
|
} & ParseMediaDynamicOptions<F>) => Promise<ConvertMediaResult>;
|
package/dist/convert-media.js
CHANGED
|
@@ -12,20 +12,20 @@ const media_parser_1 = require("@remotion/media-parser");
|
|
|
12
12
|
const auto_select_writer_1 = require("./auto-select-writer");
|
|
13
13
|
const calculate_progress_1 = require("./calculate-progress");
|
|
14
14
|
const error_cause_1 = __importDefault(require("./error-cause"));
|
|
15
|
+
const generate_output_filename_1 = require("./generate-output-filename");
|
|
15
16
|
const on_audio_track_1 = require("./on-audio-track");
|
|
16
17
|
const on_video_track_1 = require("./on-video-track");
|
|
18
|
+
const throttled_state_update_1 = require("./throttled-state-update");
|
|
17
19
|
const with_resolvers_1 = require("./with-resolvers");
|
|
18
|
-
const convertMedia = async function ({ src, onVideoFrame,
|
|
20
|
+
const convertMedia = async function ({ src, onVideoFrame, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel = 'info', writer, progressIntervalInMs, ...more }) {
|
|
21
|
+
var _a, _b;
|
|
19
22
|
if (userPassedAbortSignal === null || userPassedAbortSignal === void 0 ? void 0 : userPassedAbortSignal.aborted) {
|
|
20
23
|
return Promise.reject(new error_cause_1.default('Aborted'));
|
|
21
24
|
}
|
|
22
|
-
if (container !== 'webm') {
|
|
23
|
-
return Promise.reject(new TypeError('Only `to: "webm"` is supported currently'));
|
|
25
|
+
if (container !== 'webm' && container !== 'mp4') {
|
|
26
|
+
return Promise.reject(new TypeError('Only `to: "webm"` and `to: "mp4"` is supported currently'));
|
|
24
27
|
}
|
|
25
|
-
if (
|
|
26
|
-
return Promise.reject(new TypeError('Only `audioCodec: "opus"` is supported currently'));
|
|
27
|
-
}
|
|
28
|
-
if (videoCodec !== 'vp8' && videoCodec !== 'vp9') {
|
|
28
|
+
if (videoCodec && videoCodec !== 'vp8' && videoCodec !== 'vp9') {
|
|
29
29
|
return Promise.reject(new TypeError('Only `videoCodec: "vp8"` and `videoCodec: "vp9"` are supported currently'));
|
|
30
30
|
}
|
|
31
31
|
const { resolve, reject, getPromiseToImmediatelyReturn } = (0, with_resolvers_1.withResolversAndWaitForReturn)();
|
|
@@ -40,63 +40,67 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
|
|
|
40
40
|
abortConversion(new error_cause_1.default('Conversion aborted by user'));
|
|
41
41
|
};
|
|
42
42
|
userPassedAbortSignal === null || userPassedAbortSignal === void 0 ? void 0 : userPassedAbortSignal.addEventListener('abort', onUserAbort);
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const onMediaStateUpdate = (newState) => {
|
|
54
|
-
if (controller.signal.aborted) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
onMediaStateDoNoCallDirectly === null || onMediaStateDoNoCallDirectly === void 0 ? void 0 : onMediaStateDoNoCallDirectly(newState);
|
|
58
|
-
};
|
|
59
|
-
const state = await media_parser_1.MediaParserInternals.createMedia({
|
|
43
|
+
const creator = container === 'webm'
|
|
44
|
+
? media_parser_1.MediaParserInternals.createMatroskaMedia
|
|
45
|
+
: media_parser_1.MediaParserInternals.createIsoBaseMedia;
|
|
46
|
+
const throttledState = (0, throttled_state_update_1.throttledStateUpdate)({
|
|
47
|
+
updateFn: onProgressDoNotCallDirectly !== null && onProgressDoNotCallDirectly !== void 0 ? onProgressDoNotCallDirectly : null,
|
|
48
|
+
everyMilliseconds: progressIntervalInMs !== null && progressIntervalInMs !== void 0 ? progressIntervalInMs : 100,
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
const state = await creator({
|
|
52
|
+
filename: (0, generate_output_filename_1.generateOutputFilename)(src, container),
|
|
60
53
|
writer: await (0, auto_select_writer_1.autoSelectWriter)(writer, logLevel),
|
|
61
54
|
onBytesProgress: (bytesWritten) => {
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
var _a;
|
|
56
|
+
(_a = throttledState.update) === null || _a === void 0 ? void 0 : _a.call(throttledState, (prevState) => {
|
|
57
|
+
return {
|
|
58
|
+
...prevState,
|
|
59
|
+
bytesWritten,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
64
62
|
},
|
|
65
63
|
onMillisecondsProgress: (millisecondsWritten) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
var _a;
|
|
65
|
+
(_a = throttledState.update) === null || _a === void 0 ? void 0 : _a.call(throttledState, (prevState) => {
|
|
66
|
+
if (millisecondsWritten > prevState.millisecondsWritten) {
|
|
67
|
+
return {
|
|
68
|
+
...prevState,
|
|
69
|
+
millisecondsWritten,
|
|
70
|
+
overallProgress: (0, calculate_progress_1.calculateProgress)({
|
|
71
|
+
millisecondsWritten: prevState.millisecondsWritten,
|
|
72
|
+
expectedOutputDurationInMs: prevState.expectedOutputDurationInMs,
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return prevState;
|
|
77
|
+
});
|
|
74
78
|
},
|
|
79
|
+
logLevel,
|
|
75
80
|
});
|
|
76
81
|
const onVideoTrack = (0, on_video_track_1.makeVideoTrackHandler)({
|
|
77
82
|
state,
|
|
78
83
|
onVideoFrame: onVideoFrame !== null && onVideoFrame !== void 0 ? onVideoFrame : null,
|
|
79
|
-
onMediaStateUpdate:
|
|
84
|
+
onMediaStateUpdate: (_a = throttledState.update) !== null && _a !== void 0 ? _a : null,
|
|
80
85
|
abortConversion,
|
|
81
|
-
convertMediaState,
|
|
82
86
|
controller,
|
|
83
|
-
videoCodec,
|
|
87
|
+
defaultVideoCodec: videoCodec !== null && videoCodec !== void 0 ? videoCodec : null,
|
|
84
88
|
onVideoTrack: userVideoResolver !== null && userVideoResolver !== void 0 ? userVideoResolver : null,
|
|
85
89
|
logLevel,
|
|
86
90
|
container,
|
|
87
91
|
});
|
|
88
92
|
const onAudioTrack = (0, on_audio_track_1.makeAudioTrackHandler)({
|
|
89
93
|
abortConversion,
|
|
90
|
-
audioCodec,
|
|
94
|
+
defaultAudioCodec: audioCodec !== null && audioCodec !== void 0 ? audioCodec : null,
|
|
91
95
|
controller,
|
|
92
|
-
|
|
93
|
-
onMediaStateUpdate: onMediaStateUpdate !== null && onMediaStateUpdate !== void 0 ? onMediaStateUpdate : null,
|
|
96
|
+
onMediaStateUpdate: (_b = throttledState.update) !== null && _b !== void 0 ? _b : null,
|
|
94
97
|
state,
|
|
95
98
|
onAudioTrack: userAudioResolver !== null && userAudioResolver !== void 0 ? userAudioResolver : null,
|
|
96
99
|
logLevel,
|
|
97
100
|
container,
|
|
98
101
|
});
|
|
99
102
|
(0, media_parser_1.parseMedia)({
|
|
103
|
+
logLevel,
|
|
100
104
|
src,
|
|
101
105
|
onVideoTrack,
|
|
102
106
|
onAudioTrack,
|
|
@@ -108,6 +112,7 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
|
|
|
108
112
|
reader,
|
|
109
113
|
...more,
|
|
110
114
|
onDurationInSeconds: (durationInSeconds) => {
|
|
115
|
+
var _a;
|
|
111
116
|
if (durationInSeconds === null) {
|
|
112
117
|
return null;
|
|
113
118
|
}
|
|
@@ -116,22 +121,33 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
|
|
|
116
121
|
casted.onDurationInSeconds(durationInSeconds);
|
|
117
122
|
}
|
|
118
123
|
const expectedOutputDurationInMs = durationInSeconds * 1000;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
(_a = throttledState.update) === null || _a === void 0 ? void 0 : _a.call(throttledState, (prevState) => {
|
|
125
|
+
return {
|
|
126
|
+
...prevState,
|
|
127
|
+
expectedOutputDurationInMs,
|
|
128
|
+
overallProgress: (0, calculate_progress_1.calculateProgress)({
|
|
129
|
+
millisecondsWritten: prevState.millisecondsWritten,
|
|
130
|
+
expectedOutputDurationInMs,
|
|
131
|
+
}),
|
|
132
|
+
};
|
|
123
133
|
});
|
|
124
|
-
onMediaStateUpdate(convertMediaState);
|
|
125
134
|
},
|
|
126
135
|
})
|
|
127
136
|
.then(() => {
|
|
128
137
|
return state.waitForFinish();
|
|
129
138
|
})
|
|
130
139
|
.then(() => {
|
|
131
|
-
resolve({
|
|
140
|
+
resolve({
|
|
141
|
+
save: state.save,
|
|
142
|
+
remove: state.remove,
|
|
143
|
+
finalState: throttledState.get(),
|
|
144
|
+
});
|
|
132
145
|
})
|
|
133
146
|
.catch((err) => {
|
|
134
147
|
reject(err);
|
|
148
|
+
})
|
|
149
|
+
.finally(() => {
|
|
150
|
+
throttledState.stopAndGetLastProgress();
|
|
135
151
|
});
|
|
136
152
|
return getPromiseToImmediatelyReturn().finally(() => {
|
|
137
153
|
userPassedAbortSignal === null || userPassedAbortSignal === void 0 ? void 0 : userPassedAbortSignal.removeEventListener('abort', onUserAbort);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ConvertMediaVideoCodec } from './codec-id';
|
|
2
|
+
export declare const needsToCorrectVideoFrame: ({ videoFrame, outputCodec, }: {
|
|
3
|
+
videoFrame: VideoFrame;
|
|
4
|
+
outputCodec: ConvertMediaVideoCodec;
|
|
5
|
+
}) => boolean;
|
|
6
|
+
export declare const convertToCorrectVideoFrame: ({ videoFrame, outputCodec, }: {
|
|
7
|
+
videoFrame: VideoFrame;
|
|
8
|
+
outputCodec: ConvertMediaVideoCodec;
|
|
9
|
+
}) => VideoFrame;
|