@remotion/webcodecs 4.0.210
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/LICENSE.md +49 -0
- package/README.md +18 -0
- package/dist/audio-decoder-config.d.ts +2 -0
- package/dist/audio-decoder-config.js +13 -0
- package/dist/audio-decoder.d.ts +15 -0
- package/dist/audio-decoder.js +85 -0
- package/dist/audio-encoder-config.d.ts +2 -0
- package/dist/audio-encoder-config.js +13 -0
- package/dist/audio-encoder.d.ts +16 -0
- package/dist/audio-encoder.js +90 -0
- package/dist/codec-id.d.ts +2 -0
- package/dist/codec-id.js +2 -0
- package/dist/convert-media.d.ts +26 -0
- package/dist/convert-media.js +86 -0
- package/dist/create-audio-decoder.d.ts +7 -0
- package/dist/create-audio-decoder.js +18 -0
- package/dist/create-decoder.d.ts +6 -0
- package/dist/create-decoder.js +32 -0
- package/dist/create-encoder.d.ts +9 -0
- package/dist/create-encoder.js +46 -0
- package/dist/create-video-decoder.d.ts +6 -0
- package/dist/create-video-decoder.js +18 -0
- package/dist/decoder.d.ts +7 -0
- package/dist/decoder.js +44 -0
- package/dist/encoder.d.ts +7 -0
- package/dist/encoder.js +43 -0
- package/dist/error-cause.d.ts +8 -0
- package/dist/error-cause.js +3 -0
- package/dist/esm/index.mjs +793 -0
- package/dist/get-config.d.ts +1 -0
- package/dist/get-config.js +21 -0
- package/dist/get-description.d.ts +6 -0
- package/dist/get-description.js +20 -0
- package/dist/get-samples.d.ts +6 -0
- package/dist/get-samples.js +24 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +13 -0
- package/dist/load-mp4-file.d.ts +8 -0
- package/dist/load-mp4-file.js +37 -0
- package/dist/on-audio-track.d.ts +15 -0
- package/dist/on-audio-track.js +105 -0
- package/dist/on-video-track.d.ts +15 -0
- package/dist/on-video-track.js +101 -0
- package/dist/reencode-video.d.ts +1 -0
- package/dist/reencode-video.js +68 -0
- package/dist/resolve-audio-action.d.ts +16 -0
- package/dist/resolve-audio-action.js +30 -0
- package/dist/resolve-video-action.d.ts +15 -0
- package/dist/resolve-video-action.js +30 -0
- package/dist/video-decoder-config.d.ts +1 -0
- package/dist/video-decoder-config.js +24 -0
- package/dist/video-decoder.d.ts +14 -0
- package/dist/video-decoder.js +87 -0
- package/dist/video-encoder-config.d.ts +1 -0
- package/dist/video-encoder-config.js +24 -0
- package/dist/video-encoder.d.ts +13 -0
- package/dist/video-encoder.js +90 -0
- package/dist/video-parser.d.ts +1 -0
- package/dist/video-parser.js +51 -0
- package/dist/wait-for-dequeue.d.ts +5 -0
- package/dist/wait-for-dequeue.js +51 -0
- package/dist/with-resolvers.d.ts +5 -0
- package/dist/with-resolvers.js +13 -0
- package/package.json +37 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getVideoDecoderConfigWithHardwareAcceleration: (config: VideoDecoderConfig) => Promise<VideoDecoderConfig | null>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVideoDecoderConfigWithHardwareAcceleration = void 0;
|
|
4
|
+
const getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
|
|
5
|
+
const hardware = {
|
|
6
|
+
...config,
|
|
7
|
+
hardwareAcceleration: 'prefer-hardware',
|
|
8
|
+
};
|
|
9
|
+
if ((await VideoDecoder.isConfigSupported(hardware)).supported) {
|
|
10
|
+
return hardware;
|
|
11
|
+
}
|
|
12
|
+
const software = {
|
|
13
|
+
...config,
|
|
14
|
+
hardwareAcceleration: 'prefer-software',
|
|
15
|
+
};
|
|
16
|
+
if ((await VideoDecoder.isConfigSupported(software)).supported) {
|
|
17
|
+
return software;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
exports.getVideoDecoderConfigWithHardwareAcceleration = getVideoDecoderConfigWithHardwareAcceleration;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDescription = void 0;
|
|
4
|
+
const mp4box_1 = require("mp4box");
|
|
5
|
+
const getDescription = ({ mp4File, track, }) => {
|
|
6
|
+
const trak = mp4File.getTrackById(track.id);
|
|
7
|
+
for (const entry of trak.mdia.minf.stbl.stsd.entries) {
|
|
8
|
+
if (entry.avcC || entry.hvcC) {
|
|
9
|
+
const stream = new mp4box_1.DataStream(undefined, 0, mp4box_1.DataStream.BIG_ENDIAN);
|
|
10
|
+
if (entry.avcC) {
|
|
11
|
+
entry.avcC.write(stream);
|
|
12
|
+
}
|
|
13
|
+
else if (entry.hvcC) {
|
|
14
|
+
entry.hvcC.write(stream);
|
|
15
|
+
}
|
|
16
|
+
return new Uint8Array(stream.buffer, 8); // Remove the box header.
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
exports.getDescription = getDescription;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSamples = void 0;
|
|
4
|
+
const getSamples = ({ mp4File, track, }) => {
|
|
5
|
+
mp4File.setExtractionOptions(track.id, null, {
|
|
6
|
+
nbSamples: Infinity,
|
|
7
|
+
});
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
mp4File.onSamples = (track_id, _ref, samples) => {
|
|
10
|
+
if (track_id === track.id) {
|
|
11
|
+
resolve(samples);
|
|
12
|
+
mp4File.onSamples = undefined;
|
|
13
|
+
mp4File.onError = undefined;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
mp4File.onError = (e) => {
|
|
17
|
+
reject(e);
|
|
18
|
+
mp4File.onSamples = undefined;
|
|
19
|
+
mp4File.onError = undefined;
|
|
20
|
+
};
|
|
21
|
+
mp4File.start();
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
exports.getSamples = getSamples;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createAudioDecoder } from './audio-decoder';
|
|
2
|
+
export { createAudioEncoder } from './audio-encoder';
|
|
3
|
+
export { ConvertMediaState, ConvertMediaTo, convertMedia } from './convert-media';
|
|
4
|
+
export { createVideoDecoder } from './video-decoder';
|
|
5
|
+
export { createVideoEncoder } from './video-encoder';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createVideoEncoder = exports.createVideoDecoder = exports.convertMedia = exports.createAudioEncoder = exports.createAudioDecoder = void 0;
|
|
4
|
+
var audio_decoder_1 = require("./audio-decoder");
|
|
5
|
+
Object.defineProperty(exports, "createAudioDecoder", { enumerable: true, get: function () { return audio_decoder_1.createAudioDecoder; } });
|
|
6
|
+
var audio_encoder_1 = require("./audio-encoder");
|
|
7
|
+
Object.defineProperty(exports, "createAudioEncoder", { enumerable: true, get: function () { return audio_encoder_1.createAudioEncoder; } });
|
|
8
|
+
var convert_media_1 = require("./convert-media");
|
|
9
|
+
Object.defineProperty(exports, "convertMedia", { enumerable: true, get: function () { return convert_media_1.convertMedia; } });
|
|
10
|
+
var video_decoder_1 = require("./video-decoder");
|
|
11
|
+
Object.defineProperty(exports, "createVideoDecoder", { enumerable: true, get: function () { return video_decoder_1.createVideoDecoder; } });
|
|
12
|
+
var video_encoder_1 = require("./video-encoder");
|
|
13
|
+
Object.defineProperty(exports, "createVideoEncoder", { enumerable: true, get: function () { return video_encoder_1.createVideoEncoder; } });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadMp4File = void 0;
|
|
4
|
+
const mp4box_1 = require("mp4box");
|
|
5
|
+
const readFile = (file) => {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const reader = new FileReader();
|
|
8
|
+
reader.onload = () => {
|
|
9
|
+
reader.result.fileStart = 0;
|
|
10
|
+
resolve(reader.result);
|
|
11
|
+
reader.onerror = null;
|
|
12
|
+
reader.onload = null;
|
|
13
|
+
};
|
|
14
|
+
reader.onerror = (error) => {
|
|
15
|
+
reject(error);
|
|
16
|
+
reader.onerror = null;
|
|
17
|
+
reader.onload = null;
|
|
18
|
+
};
|
|
19
|
+
reader.readAsArrayBuffer(file);
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const loadMp4File = async (file) => {
|
|
23
|
+
const mp4File = (0, mp4box_1.createFile)();
|
|
24
|
+
const arrayBuffer = await readFile(file);
|
|
25
|
+
const prom = new Promise((resolve, reject) => {
|
|
26
|
+
mp4File.onError = (error) => {
|
|
27
|
+
reject(error);
|
|
28
|
+
};
|
|
29
|
+
mp4File.onReady = (info) => {
|
|
30
|
+
resolve({ mp4File, info });
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
mp4File.appendBuffer(arrayBuffer);
|
|
34
|
+
mp4File.flush();
|
|
35
|
+
return prom;
|
|
36
|
+
};
|
|
37
|
+
exports.loadMp4File = loadMp4File;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MediaFn, OnAudioTrack } from '@remotion/media-parser';
|
|
2
|
+
import type { ConvertMediaAudioCodec } from './codec-id';
|
|
3
|
+
import type { ConvertMediaState } from './convert-media';
|
|
4
|
+
import Error from './error-cause';
|
|
5
|
+
import type { ResolveAudioActionFn } from './resolve-audio-action';
|
|
6
|
+
export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, }: {
|
|
7
|
+
state: MediaFn;
|
|
8
|
+
audioCodec: ConvertMediaAudioCodec;
|
|
9
|
+
convertMediaState: ConvertMediaState;
|
|
10
|
+
controller: AbortController;
|
|
11
|
+
abortConversion: (errCause: Error) => void;
|
|
12
|
+
onMediaStateUpdate: ((state: ConvertMediaState) => void) | null;
|
|
13
|
+
onAudioTrack: ResolveAudioActionFn;
|
|
14
|
+
bitrate: number;
|
|
15
|
+
}) => OnAudioTrack;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.makeAudioTrackHandler = void 0;
|
|
7
|
+
const audio_decoder_1 = require("./audio-decoder");
|
|
8
|
+
const audio_decoder_config_1 = require("./audio-decoder-config");
|
|
9
|
+
const audio_encoder_1 = require("./audio-encoder");
|
|
10
|
+
const audio_encoder_config_1 = require("./audio-encoder-config");
|
|
11
|
+
const error_cause_1 = __importDefault(require("./error-cause"));
|
|
12
|
+
const resolve_audio_action_1 = require("./resolve-audio-action");
|
|
13
|
+
const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, }) => async (track) => {
|
|
14
|
+
const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
|
|
15
|
+
codec: audioCodec,
|
|
16
|
+
numberOfChannels: track.numberOfChannels,
|
|
17
|
+
sampleRate: track.sampleRate,
|
|
18
|
+
bitrate,
|
|
19
|
+
});
|
|
20
|
+
const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)({
|
|
21
|
+
codec: track.codec,
|
|
22
|
+
numberOfChannels: track.numberOfChannels,
|
|
23
|
+
sampleRate: track.sampleRate,
|
|
24
|
+
description: track.description,
|
|
25
|
+
});
|
|
26
|
+
const audioOperation = await (0, resolve_audio_action_1.resolveAudioAction)({
|
|
27
|
+
audioDecoderConfig,
|
|
28
|
+
audioEncoderConfig,
|
|
29
|
+
audioCodec,
|
|
30
|
+
track,
|
|
31
|
+
resolverFunction: onAudioTrack,
|
|
32
|
+
});
|
|
33
|
+
if (audioOperation === 'drop') {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (audioOperation === 'copy') {
|
|
37
|
+
const addedTrack = await state.addTrack({
|
|
38
|
+
type: 'audio',
|
|
39
|
+
codec: audioCodec,
|
|
40
|
+
numberOfChannels: track.numberOfChannels,
|
|
41
|
+
sampleRate: track.sampleRate,
|
|
42
|
+
codecPrivate: track.codecPrivate,
|
|
43
|
+
});
|
|
44
|
+
return async (audioSample) => {
|
|
45
|
+
await state.addSample(new EncodedAudioChunk(audioSample), addedTrack.trackNumber);
|
|
46
|
+
convertMediaState.encodedAudioFrames++;
|
|
47
|
+
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (!audioEncoderConfig) {
|
|
51
|
+
abortConversion(new error_cause_1.default(`Could not configure audio encoder of track ${track.trackId}`));
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (!audioDecoderConfig) {
|
|
55
|
+
abortConversion(new error_cause_1.default(`Could not configure audio decoder of track ${track.trackId}`));
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const { trackNumber } = await state.addTrack({
|
|
59
|
+
type: 'audio',
|
|
60
|
+
codec: audioCodec,
|
|
61
|
+
numberOfChannels: track.numberOfChannels,
|
|
62
|
+
sampleRate: track.sampleRate,
|
|
63
|
+
codecPrivate: null,
|
|
64
|
+
});
|
|
65
|
+
const audioEncoder = (0, audio_encoder_1.createAudioEncoder)({
|
|
66
|
+
onChunk: async (chunk) => {
|
|
67
|
+
await state.addSample(chunk, trackNumber);
|
|
68
|
+
convertMediaState.encodedAudioFrames++;
|
|
69
|
+
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
|
|
70
|
+
},
|
|
71
|
+
onError: (err) => {
|
|
72
|
+
abortConversion(new error_cause_1.default(`Audio encoder of ${track.trackId} failed (see .cause of this error)`, {
|
|
73
|
+
cause: err,
|
|
74
|
+
}));
|
|
75
|
+
},
|
|
76
|
+
codec: audioCodec,
|
|
77
|
+
signal: controller.signal,
|
|
78
|
+
config: audioEncoderConfig,
|
|
79
|
+
});
|
|
80
|
+
const audioDecoder = (0, audio_decoder_1.createAudioDecoder)({
|
|
81
|
+
onFrame: async (frame) => {
|
|
82
|
+
await audioEncoder.encodeFrame(frame);
|
|
83
|
+
convertMediaState.decodedAudioFrames++;
|
|
84
|
+
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate(convertMediaState);
|
|
85
|
+
frame.close();
|
|
86
|
+
},
|
|
87
|
+
onError(error) {
|
|
88
|
+
abortConversion(new error_cause_1.default(`Audio decoder of track ${track.trackId} failed (see .cause of this error)`, {
|
|
89
|
+
cause: error,
|
|
90
|
+
}));
|
|
91
|
+
},
|
|
92
|
+
signal: controller.signal,
|
|
93
|
+
config: audioDecoderConfig,
|
|
94
|
+
});
|
|
95
|
+
state.addWaitForFinishPromise(async () => {
|
|
96
|
+
await audioDecoder.waitForFinish();
|
|
97
|
+
await audioEncoder.waitForFinish();
|
|
98
|
+
audioDecoder.close();
|
|
99
|
+
audioEncoder.close();
|
|
100
|
+
});
|
|
101
|
+
return async (audioSample) => {
|
|
102
|
+
await audioDecoder.processSample(audioSample);
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
exports.makeAudioTrackHandler = makeAudioTrackHandler;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MediaFn, OnVideoTrack, VideoTrack } from '@remotion/media-parser';
|
|
2
|
+
import type { ConvertMediaVideoCodec } from './codec-id';
|
|
3
|
+
import type { ConvertMediaState } from './convert-media';
|
|
4
|
+
import Error from './error-cause';
|
|
5
|
+
import type { ResolveVideoActionFn } from './resolve-video-action';
|
|
6
|
+
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, }: {
|
|
7
|
+
state: MediaFn;
|
|
8
|
+
onVideoFrame: ((frame: VideoFrame, track: VideoTrack) => Promise<void>) | null;
|
|
9
|
+
onMediaStateUpdate: ((state: ConvertMediaState) => void) | null;
|
|
10
|
+
abortConversion: (errCause: Error) => void;
|
|
11
|
+
convertMediaState: ConvertMediaState;
|
|
12
|
+
controller: AbortController;
|
|
13
|
+
videoCodec: ConvertMediaVideoCodec;
|
|
14
|
+
onVideoTrack: ResolveVideoActionFn;
|
|
15
|
+
}) => OnVideoTrack;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.makeVideoTrackHandler = void 0;
|
|
7
|
+
const error_cause_1 = __importDefault(require("./error-cause"));
|
|
8
|
+
const resolve_video_action_1 = require("./resolve-video-action");
|
|
9
|
+
const video_decoder_1 = require("./video-decoder");
|
|
10
|
+
const video_decoder_config_1 = require("./video-decoder-config");
|
|
11
|
+
const video_encoder_1 = require("./video-encoder");
|
|
12
|
+
const video_encoder_config_1 = require("./video-encoder-config");
|
|
13
|
+
const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, }) => async (track) => {
|
|
14
|
+
const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
|
|
15
|
+
codec: videoCodec,
|
|
16
|
+
height: track.displayAspectHeight,
|
|
17
|
+
width: track.displayAspectWidth,
|
|
18
|
+
});
|
|
19
|
+
const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
|
|
20
|
+
const videoOperation = await (0, resolve_video_action_1.resolveVideoAction)({
|
|
21
|
+
videoDecoderConfig,
|
|
22
|
+
videoEncoderConfig,
|
|
23
|
+
track,
|
|
24
|
+
videoCodec,
|
|
25
|
+
resolverFunction: onVideoTrack,
|
|
26
|
+
});
|
|
27
|
+
if (videoOperation === 'drop') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (videoOperation === 'copy') {
|
|
31
|
+
const videoTrack = await state.addTrack({
|
|
32
|
+
type: 'video',
|
|
33
|
+
color: track.color,
|
|
34
|
+
width: track.codedWidth,
|
|
35
|
+
height: track.codedHeight,
|
|
36
|
+
codec: track.codecWithoutConfig,
|
|
37
|
+
codecPrivate: track.codecPrivate,
|
|
38
|
+
});
|
|
39
|
+
return (sample) => {
|
|
40
|
+
state.addSample(new EncodedVideoChunk(sample), videoTrack.trackNumber);
|
|
41
|
+
convertMediaState.decodedVideoFrames++;
|
|
42
|
+
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (videoEncoderConfig === null) {
|
|
46
|
+
abortConversion(new error_cause_1.default(`Could not configure video encoder of track ${track.trackId}`));
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (videoDecoderConfig === null) {
|
|
50
|
+
abortConversion(new error_cause_1.default(`Could not configure video decoder of track ${track.trackId}`));
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const { trackNumber } = await state.addTrack({
|
|
54
|
+
type: 'video',
|
|
55
|
+
color: track.color,
|
|
56
|
+
width: track.codedWidth,
|
|
57
|
+
height: track.codedHeight,
|
|
58
|
+
codec: videoCodec,
|
|
59
|
+
codecPrivate: null,
|
|
60
|
+
});
|
|
61
|
+
const videoEncoder = (0, video_encoder_1.createVideoEncoder)({
|
|
62
|
+
onChunk: async (chunk) => {
|
|
63
|
+
await state.addSample(chunk, trackNumber);
|
|
64
|
+
convertMediaState.encodedVideoFrames++;
|
|
65
|
+
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
|
|
66
|
+
},
|
|
67
|
+
onError: (err) => {
|
|
68
|
+
abortConversion(new error_cause_1.default(`Video encoder of track ${track.trackId} failed (see .cause of this error)`, {
|
|
69
|
+
cause: err,
|
|
70
|
+
}));
|
|
71
|
+
},
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
config: videoEncoderConfig,
|
|
74
|
+
});
|
|
75
|
+
const videoDecoder = (0, video_decoder_1.createVideoDecoder)({
|
|
76
|
+
config: videoDecoderConfig,
|
|
77
|
+
onFrame: async (frame) => {
|
|
78
|
+
await (onVideoFrame === null || onVideoFrame === void 0 ? void 0 : onVideoFrame(frame, track));
|
|
79
|
+
await videoEncoder.encodeFrame(frame);
|
|
80
|
+
convertMediaState.decodedVideoFrames++;
|
|
81
|
+
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
|
|
82
|
+
frame.close();
|
|
83
|
+
},
|
|
84
|
+
onError: (err) => {
|
|
85
|
+
abortConversion(new error_cause_1.default(`Video decoder of track ${track.trackId} failed (see .cause of this error)`, {
|
|
86
|
+
cause: err,
|
|
87
|
+
}));
|
|
88
|
+
},
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
});
|
|
91
|
+
state.addWaitForFinishPromise(async () => {
|
|
92
|
+
await videoDecoder.waitForFinish();
|
|
93
|
+
await videoEncoder.waitForFinish();
|
|
94
|
+
videoDecoder.close();
|
|
95
|
+
videoEncoder.close();
|
|
96
|
+
});
|
|
97
|
+
return async (chunk) => {
|
|
98
|
+
await videoDecoder.processSample(chunk);
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
exports.makeVideoTrackHandler = makeVideoTrackHandler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const reencodeVideo: (file: File) => Promise<void>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reencodeVideo = void 0;
|
|
4
|
+
const create_encoder_1 = require("./create-encoder");
|
|
5
|
+
const create_video_decoder_1 = require("./create-video-decoder");
|
|
6
|
+
const get_description_1 = require("./get-description");
|
|
7
|
+
const get_samples_1 = require("./get-samples");
|
|
8
|
+
const load_mp4_file_1 = require("./load-mp4-file");
|
|
9
|
+
const reencodeVideo = async (file) => {
|
|
10
|
+
const { info, mp4File } = await (0, load_mp4_file_1.loadMp4File)(file);
|
|
11
|
+
const track = info.videoTracks[0];
|
|
12
|
+
if (!track) {
|
|
13
|
+
throw new Error('No video track found in the file');
|
|
14
|
+
}
|
|
15
|
+
const { encoder, outputMp4 } = (0, create_encoder_1.createEncoder)({
|
|
16
|
+
width: track.track_width,
|
|
17
|
+
height: track.track_height,
|
|
18
|
+
onProgress: (encoded) => {
|
|
19
|
+
const encodingProgress = Math.round((100 * encoded) / track.nb_samples);
|
|
20
|
+
console.log(`Encoding frame ${encoded} (${encodingProgress}%)`);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
let nextKeyFrameTimestamp = 0;
|
|
24
|
+
let keyFrame = false;
|
|
25
|
+
const keyFrameEveryHowManySeconds = 2;
|
|
26
|
+
const { decoder } = (0, create_video_decoder_1.createVideoDecoder)({
|
|
27
|
+
onFrame: (frame) => {
|
|
28
|
+
if (frame.timestamp >= nextKeyFrameTimestamp) {
|
|
29
|
+
keyFrame = true;
|
|
30
|
+
nextKeyFrameTimestamp =
|
|
31
|
+
frame.timestamp + keyFrameEveryHowManySeconds * 1e6;
|
|
32
|
+
}
|
|
33
|
+
encoder.encode(frame, { keyFrame });
|
|
34
|
+
frame.close();
|
|
35
|
+
},
|
|
36
|
+
onProgress: (decoded) => {
|
|
37
|
+
const decodingProgress = Math.round((100 * decoded) / track.nb_samples);
|
|
38
|
+
console.log(`Decoding frame ${decoded} (${decodingProgress}%)`);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
decoder.configure({
|
|
42
|
+
codec: track.codec,
|
|
43
|
+
codedWidth: track.track_width,
|
|
44
|
+
codedHeight: track.track_height,
|
|
45
|
+
// TODO: Check first if hardware acceleration is supported
|
|
46
|
+
hardwareAcceleration: 'prefer-hardware',
|
|
47
|
+
description: (0, get_description_1.getDescription)({ mp4File, track }),
|
|
48
|
+
});
|
|
49
|
+
const samples = await (0, get_samples_1.getSamples)({ mp4File, track });
|
|
50
|
+
console.log(samples);
|
|
51
|
+
for (const sample of samples) {
|
|
52
|
+
const duration = (sample.duration * 1000000) / sample.timescale;
|
|
53
|
+
const timestamp = (sample.cts * 1000000) / sample.timescale;
|
|
54
|
+
const chunk = new EncodedVideoChunk({
|
|
55
|
+
type: sample.is_sync ? 'key' : 'delta',
|
|
56
|
+
timestamp,
|
|
57
|
+
duration,
|
|
58
|
+
data: sample.data,
|
|
59
|
+
});
|
|
60
|
+
decoder.decode(chunk);
|
|
61
|
+
}
|
|
62
|
+
await decoder.flush();
|
|
63
|
+
await encoder.flush();
|
|
64
|
+
encoder.close();
|
|
65
|
+
decoder.close();
|
|
66
|
+
outputMp4.save('mp4box.mp4');
|
|
67
|
+
};
|
|
68
|
+
exports.reencodeVideo = reencodeVideo;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="dom-webcodecs" />
|
|
2
|
+
import type { AudioTrack } from '@remotion/media-parser';
|
|
3
|
+
import type { ConvertMediaAudioCodec } from './codec-id';
|
|
4
|
+
export type AudioOperation = 'reencode' | 'copy' | 'drop';
|
|
5
|
+
export type ResolveAudioActionFn = (options: {
|
|
6
|
+
canReencode: boolean;
|
|
7
|
+
canCopy: boolean;
|
|
8
|
+
}) => AudioOperation | Promise<AudioOperation>;
|
|
9
|
+
export declare const defaultResolveAudioAction: ResolveAudioActionFn;
|
|
10
|
+
export declare const resolveAudioAction: ({ audioDecoderConfig, audioEncoderConfig, track, audioCodec, resolverFunction, }: {
|
|
11
|
+
audioDecoderConfig: AudioDecoderConfig | null;
|
|
12
|
+
audioEncoderConfig: AudioEncoderConfig | null;
|
|
13
|
+
track: AudioTrack;
|
|
14
|
+
audioCodec: ConvertMediaAudioCodec;
|
|
15
|
+
resolverFunction: ResolveAudioActionFn;
|
|
16
|
+
}) => Promise<AudioOperation>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAudioAction = exports.defaultResolveAudioAction = void 0;
|
|
4
|
+
const canCopyAudioTrack = (inputCodec, outputCodec) => {
|
|
5
|
+
if (outputCodec === 'opus') {
|
|
6
|
+
return inputCodec === 'opus';
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`Unhandled codec: ${outputCodec}`);
|
|
9
|
+
};
|
|
10
|
+
const defaultResolveAudioAction = ({ canReencode, canCopy, }) => {
|
|
11
|
+
if (canCopy) {
|
|
12
|
+
return 'copy';
|
|
13
|
+
}
|
|
14
|
+
if (canReencode) {
|
|
15
|
+
return 'reencode';
|
|
16
|
+
}
|
|
17
|
+
// TODO: Make a fail option?
|
|
18
|
+
return 'drop';
|
|
19
|
+
};
|
|
20
|
+
exports.defaultResolveAudioAction = defaultResolveAudioAction;
|
|
21
|
+
const resolveAudioAction = async ({ audioDecoderConfig, audioEncoderConfig, track, audioCodec, resolverFunction, }) => {
|
|
22
|
+
const canReencode = Boolean(audioDecoderConfig && audioEncoderConfig);
|
|
23
|
+
const canCopy = canCopyAudioTrack(track.codecWithoutConfig, audioCodec);
|
|
24
|
+
const resolved = await resolverFunction({
|
|
25
|
+
canReencode,
|
|
26
|
+
canCopy,
|
|
27
|
+
});
|
|
28
|
+
return resolved;
|
|
29
|
+
};
|
|
30
|
+
exports.resolveAudioAction = resolveAudioAction;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VideoTrack } from '@remotion/media-parser';
|
|
2
|
+
import type { ConvertMediaVideoCodec } from './codec-id';
|
|
3
|
+
export type VideoOperation = 'reencode' | 'copy' | 'drop';
|
|
4
|
+
export type ResolveVideoActionFn = (options: {
|
|
5
|
+
canReencode: boolean;
|
|
6
|
+
canCopy: boolean;
|
|
7
|
+
}) => VideoOperation | Promise<VideoOperation>;
|
|
8
|
+
export declare const defaultResolveVideoAction: ResolveVideoActionFn;
|
|
9
|
+
export declare const resolveVideoAction: ({ videoDecoderConfig, videoEncoderConfig, track, videoCodec, resolverFunction, }: {
|
|
10
|
+
videoDecoderConfig: VideoDecoderConfig | null;
|
|
11
|
+
videoEncoderConfig: VideoEncoderConfig | null;
|
|
12
|
+
videoCodec: ConvertMediaVideoCodec;
|
|
13
|
+
track: VideoTrack;
|
|
14
|
+
resolverFunction: ResolveVideoActionFn;
|
|
15
|
+
}) => Promise<VideoOperation>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveVideoAction = exports.defaultResolveVideoAction = void 0;
|
|
4
|
+
const canCopyVideoTrack = (inputCodec, outputCodec) => {
|
|
5
|
+
if (outputCodec === 'vp8') {
|
|
6
|
+
return inputCodec === 'vp8';
|
|
7
|
+
}
|
|
8
|
+
throw new Error(`Unhandled codec: ${outputCodec}`);
|
|
9
|
+
};
|
|
10
|
+
const defaultResolveVideoAction = ({ canReencode, canCopy, }) => {
|
|
11
|
+
if (canCopy) {
|
|
12
|
+
return 'copy';
|
|
13
|
+
}
|
|
14
|
+
if (canReencode) {
|
|
15
|
+
return 'reencode';
|
|
16
|
+
}
|
|
17
|
+
// TODO: Make a fail option?
|
|
18
|
+
return 'drop';
|
|
19
|
+
};
|
|
20
|
+
exports.defaultResolveVideoAction = defaultResolveVideoAction;
|
|
21
|
+
const resolveVideoAction = async ({ videoDecoderConfig, videoEncoderConfig, track, videoCodec, resolverFunction, }) => {
|
|
22
|
+
const canReencode = Boolean(videoDecoderConfig && videoEncoderConfig);
|
|
23
|
+
const canCopy = canCopyVideoTrack(track.codecWithoutConfig, videoCodec);
|
|
24
|
+
const resolved = await resolverFunction({
|
|
25
|
+
canReencode,
|
|
26
|
+
canCopy,
|
|
27
|
+
});
|
|
28
|
+
return resolved;
|
|
29
|
+
};
|
|
30
|
+
exports.resolveVideoAction = resolveVideoAction;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getVideoDecoderConfigWithHardwareAcceleration: (config: VideoDecoderConfig) => Promise<VideoDecoderConfig | null>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVideoDecoderConfigWithHardwareAcceleration = void 0;
|
|
4
|
+
const getVideoDecoderConfigWithHardwareAcceleration = async (config) => {
|
|
5
|
+
if (typeof VideoDecoder === 'undefined') {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const hardware = {
|
|
9
|
+
...config,
|
|
10
|
+
hardwareAcceleration: 'prefer-hardware',
|
|
11
|
+
};
|
|
12
|
+
if ((await VideoDecoder.isConfigSupported(hardware)).supported) {
|
|
13
|
+
return hardware;
|
|
14
|
+
}
|
|
15
|
+
const software = {
|
|
16
|
+
...config,
|
|
17
|
+
hardwareAcceleration: 'prefer-software',
|
|
18
|
+
};
|
|
19
|
+
if ((await VideoDecoder.isConfigSupported(software)).supported) {
|
|
20
|
+
return software;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
exports.getVideoDecoderConfigWithHardwareAcceleration = getVideoDecoderConfigWithHardwareAcceleration;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { VideoSample } from '@remotion/media-parser';
|
|
2
|
+
export type WebCodecsVideoDecoder = {
|
|
3
|
+
processSample: (videoSample: VideoSample) => Promise<void>;
|
|
4
|
+
waitForFinish: () => Promise<void>;
|
|
5
|
+
close: () => void;
|
|
6
|
+
getQueueSize: () => number;
|
|
7
|
+
flush: () => Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare const createVideoDecoder: ({ onFrame, onError, signal, config, }: {
|
|
10
|
+
onFrame: (frame: VideoFrame) => Promise<void>;
|
|
11
|
+
onError: (error: DOMException) => void;
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
config: VideoDecoderConfig;
|
|
14
|
+
}) => WebCodecsVideoDecoder;
|