@remotion/webcodecs 4.0.305 → 4.0.306
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio-decoder.d.ts +15 -10
- package/dist/audio-decoder.js +49 -52
- package/dist/audio-encoder.d.ts +5 -5
- package/dist/audio-encoder.js +20 -42
- package/dist/convert-media.js +2 -2
- package/dist/copy-audio-track.d.ts +11 -0
- package/dist/copy-audio-track.js +31 -0
- package/dist/copy-video-track.d.ts +11 -0
- package/dist/copy-video-track.js +32 -0
- package/dist/create/event-emitter.d.ts +0 -1
- package/dist/create/progress-tracker.d.ts +0 -2
- package/dist/create/progress-tracker.js +3 -20
- package/dist/esm/index.mjs +560 -470
- package/dist/get-wave-audio-decoder.d.ts +6 -1
- package/dist/get-wave-audio-decoder.js +16 -11
- package/dist/io-manager/io-synchronizer.d.ts +6 -13
- package/dist/io-manager/io-synchronizer.js +31 -72
- package/dist/io-manager/make-timeout-promise.d.ts +1 -1
- package/dist/io-manager/make-timeout-promise.js +8 -4
- package/dist/on-audio-track.d.ts +2 -2
- package/dist/on-audio-track.js +15 -150
- package/dist/on-frame.d.ts +2 -4
- package/dist/on-frame.js +8 -9
- package/dist/on-video-track.d.ts +2 -2
- package/dist/on-video-track.js +18 -129
- package/dist/processing-queue.d.ts +19 -0
- package/dist/processing-queue.js +47 -0
- package/dist/reencode-audio-track.d.ts +18 -0
- package/dist/reencode-audio-track.js +164 -0
- package/dist/reencode-video-track.d.ts +19 -0
- package/dist/reencode-video-track.js +151 -0
- package/dist/sort-video-frames.d.ts +4 -3
- package/dist/sort-video-frames.js +7 -3
- package/dist/video-decoder.d.ts +14 -8
- package/dist/video-decoder.js +37 -72
- package/dist/video-encoder.d.ts +6 -5
- package/dist/video-encoder.js +16 -40
- package/dist/wav-audio-encoder.d.ts +4 -1
- package/dist/wav-audio-encoder.js +3 -2
- package/package.json +5 -5
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
import type { MediaParserLogLevel } from '@remotion/media-parser';
|
|
1
2
|
import type { CreateAudioDecoderInit, WebCodecsAudioDecoder } from './audio-decoder';
|
|
2
|
-
|
|
3
|
+
import type { IoSynchronizer } from './io-manager/io-synchronizer';
|
|
4
|
+
export declare const getWaveAudioDecoder: ({ onFrame, config, sampleFormat, ioSynchronizer, onError, }: Pick<CreateAudioDecoderInit, "onFrame" | "config"> & {
|
|
3
5
|
sampleFormat: AudioSampleFormat;
|
|
6
|
+
logLevel: MediaParserLogLevel;
|
|
7
|
+
ioSynchronizer: IoSynchronizer;
|
|
8
|
+
onError: (error: Error) => void;
|
|
4
9
|
}) => WebCodecsAudioDecoder;
|
|
@@ -28,30 +28,35 @@ const getBytesPerSample = (sampleFormat) => {
|
|
|
28
28
|
}
|
|
29
29
|
throw new Error(`Unsupported sample format: ${sampleFormat}`);
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
const getWaveAudioDecoder = ({ onFrame, track, sampleFormat, }) => {
|
|
33
|
-
let queue = Promise.resolve();
|
|
31
|
+
const getWaveAudioDecoder = ({ onFrame, config, sampleFormat, ioSynchronizer, onError, }) => {
|
|
34
32
|
const processSample = async (audioSample) => {
|
|
35
33
|
const bytesPerSample = getBytesPerSample(sampleFormat);
|
|
36
|
-
|
|
34
|
+
const audioData = new AudioData({
|
|
37
35
|
data: audioSample.data,
|
|
38
36
|
format: sampleFormat,
|
|
39
|
-
numberOfChannels:
|
|
40
|
-
numberOfFrames: audioSample.data.byteLength / bytesPerSample /
|
|
41
|
-
sampleRate:
|
|
37
|
+
numberOfChannels: config.numberOfChannels,
|
|
38
|
+
numberOfFrames: audioSample.data.byteLength / bytesPerSample / config.numberOfChannels,
|
|
39
|
+
sampleRate: config.sampleRate,
|
|
42
40
|
timestamp: audioSample.timestamp,
|
|
43
|
-
})
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
await onFrame(audioData);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
audioData.close();
|
|
47
|
+
onError(err);
|
|
48
|
+
}
|
|
44
49
|
};
|
|
45
50
|
return {
|
|
46
51
|
close() {
|
|
47
52
|
return Promise.resolve();
|
|
48
53
|
},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return queue;
|
|
54
|
+
decode(audioSample) {
|
|
55
|
+
processSample(audioSample);
|
|
52
56
|
},
|
|
53
57
|
flush: () => Promise.resolve(),
|
|
54
58
|
waitForFinish: () => Promise.resolve(),
|
|
59
|
+
waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
|
|
55
60
|
};
|
|
56
61
|
};
|
|
57
62
|
exports.getWaveAudioDecoder = getWaveAudioDecoder;
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import type { ProgressTracker } from '../create/progress-tracker';
|
|
2
1
|
import type { LogLevel } from '../log';
|
|
3
2
|
import type { WebCodecsController } from '../webcodecs-controller';
|
|
4
|
-
export declare const makeIoSynchronizer: ({ logLevel, label,
|
|
3
|
+
export declare const makeIoSynchronizer: ({ logLevel, label, controller, }: {
|
|
5
4
|
logLevel: LogLevel;
|
|
6
5
|
label: string;
|
|
7
|
-
|
|
6
|
+
controller: WebCodecsController | null;
|
|
8
7
|
}) => {
|
|
9
|
-
inputItem: (timestamp: number
|
|
8
|
+
inputItem: (timestamp: number) => void;
|
|
10
9
|
onOutput: (timestamp: number) => void;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
unprocessed: number;
|
|
14
|
-
minimumProgress: number | null;
|
|
15
|
-
controller: WebCodecsController;
|
|
16
|
-
}) => Promise<void>;
|
|
17
|
-
waitForFinish: (controller: WebCodecsController) => Promise<void>;
|
|
18
|
-
onProcessed: () => void;
|
|
19
|
-
getUnprocessed: () => number;
|
|
10
|
+
waitForFinish: () => Promise<void>;
|
|
11
|
+
waitForQueueSize: (queueSize: number) => Promise<void>;
|
|
20
12
|
};
|
|
13
|
+
export type IoSynchronizer = ReturnType<typeof makeIoSynchronizer>;
|
|
@@ -5,40 +5,25 @@ const event_emitter_1 = require("../create/event-emitter");
|
|
|
5
5
|
const with_resolvers_1 = require("../create/with-resolvers");
|
|
6
6
|
const log_1 = require("../log");
|
|
7
7
|
const make_timeout_promise_1 = require("./make-timeout-promise");
|
|
8
|
-
const makeIoSynchronizer = ({ logLevel, label,
|
|
8
|
+
const makeIoSynchronizer = ({ logLevel, label, controller, }) => {
|
|
9
9
|
const eventEmitter = new event_emitter_1.IoEventEmitter();
|
|
10
10
|
let lastInput = 0;
|
|
11
|
-
let lastInputKeyframe = 0;
|
|
12
11
|
let lastOutput = 0;
|
|
13
12
|
let inputsSinceLastOutput = 0;
|
|
14
13
|
let inputs = [];
|
|
15
|
-
|
|
16
|
-
// Once WebCodecs emits items, the user has to handle them
|
|
17
|
-
// Let's keep count of how many items are unprocessed
|
|
18
|
-
let _unprocessed = 0;
|
|
19
|
-
const getUnprocessed = () => _unprocessed;
|
|
20
|
-
const getUnemittedItems = () => {
|
|
14
|
+
const getQueuedItems = () => {
|
|
21
15
|
inputs = inputs.filter((input) => Math.floor(input) > Math.floor(lastOutput));
|
|
22
16
|
return inputs.length;
|
|
23
17
|
};
|
|
24
|
-
const getUnemittedKeyframes = () => {
|
|
25
|
-
keyframes = keyframes.filter((keyframe) => Math.floor(keyframe) > Math.floor(lastOutput));
|
|
26
|
-
return keyframes.length;
|
|
27
|
-
};
|
|
28
18
|
const printState = (prefix) => {
|
|
29
|
-
log_1.Log.trace(logLevel, `[${label}] ${prefix}, state: Last input = ${lastInput} Last
|
|
19
|
+
log_1.Log.trace(logLevel, `[${label}] ${prefix}, state: Last input = ${lastInput} Last output = ${lastOutput} Inputs since last output = ${inputsSinceLastOutput}, Queue = ${getQueuedItems()}`);
|
|
30
20
|
};
|
|
31
|
-
const inputItem = (timestamp
|
|
21
|
+
const inputItem = (timestamp) => {
|
|
32
22
|
lastInput = timestamp;
|
|
33
|
-
if (keyFrame) {
|
|
34
|
-
lastInputKeyframe = timestamp;
|
|
35
|
-
keyframes.push(timestamp);
|
|
36
|
-
}
|
|
37
23
|
inputsSinceLastOutput++;
|
|
38
24
|
inputs.push(timestamp);
|
|
39
25
|
eventEmitter.dispatchEvent('input', {
|
|
40
26
|
timestamp,
|
|
41
|
-
keyFrame,
|
|
42
27
|
});
|
|
43
28
|
printState('Input item');
|
|
44
29
|
};
|
|
@@ -48,7 +33,6 @@ const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
|
|
|
48
33
|
eventEmitter.dispatchEvent('output', {
|
|
49
34
|
timestamp,
|
|
50
35
|
});
|
|
51
|
-
_unprocessed++;
|
|
52
36
|
printState('Got output');
|
|
53
37
|
};
|
|
54
38
|
const waitForOutput = () => {
|
|
@@ -60,75 +44,50 @@ const makeIoSynchronizer = ({ logLevel, label, progress, }) => {
|
|
|
60
44
|
eventEmitter.addEventListener('output', on);
|
|
61
45
|
return promise;
|
|
62
46
|
};
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return promise;
|
|
47
|
+
const makeErrorBanner = () => {
|
|
48
|
+
return [
|
|
49
|
+
`Waited too long for ${label} to finish:`,
|
|
50
|
+
`${getQueuedItems()} queued items`,
|
|
51
|
+
`inputs: ${JSON.stringify(inputs)}`,
|
|
52
|
+
`last output: ${lastOutput}`,
|
|
53
|
+
];
|
|
71
54
|
};
|
|
72
|
-
const
|
|
73
|
-
|
|
55
|
+
const waitForQueueSize = async (queueSize) => {
|
|
56
|
+
if (getQueuedItems() <= queueSize) {
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
}
|
|
74
59
|
const { timeoutPromise, clear } = (0, make_timeout_promise_1.makeTimeoutPromise)({
|
|
75
60
|
label: () => [
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`${getUnprocessed()} unprocessed items: ${JSON.stringify(_unprocessed)}`,
|
|
79
|
-
`smallest progress: ${progress.getSmallestProgress()}`,
|
|
80
|
-
`inputs: ${JSON.stringify(inputs)}`,
|
|
81
|
-
`last output: ${lastOutput}`,
|
|
82
|
-
`wanted: ${unemitted} unemitted items, ${unprocessed} unprocessed items, minimum progress ${minimumProgress}`,
|
|
61
|
+
...makeErrorBanner(),
|
|
62
|
+
`wanted: <${queueSize} queued items`,
|
|
83
63
|
`Report this at https://remotion.dev/report`,
|
|
84
64
|
].join('\n'),
|
|
85
65
|
ms: 10000,
|
|
86
66
|
controller,
|
|
87
67
|
});
|
|
88
|
-
controller
|
|
68
|
+
if (controller) {
|
|
69
|
+
controller._internals._mediaParserController._internals.signal.addEventListener('abort', clear);
|
|
70
|
+
}
|
|
89
71
|
await Promise.race([
|
|
90
72
|
timeoutPromise,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
})(),
|
|
97
|
-
(async () => {
|
|
98
|
-
while (getUnprocessed() > unprocessed) {
|
|
99
|
-
await waitForProcessed();
|
|
100
|
-
}
|
|
101
|
-
})(),
|
|
102
|
-
minimumProgress === null || progress.getSmallestProgress() === null
|
|
103
|
-
? Promise.resolve()
|
|
104
|
-
: (async () => {
|
|
105
|
-
while (progress.getSmallestProgress() < minimumProgress) {
|
|
106
|
-
await progress.waitForProgress();
|
|
107
|
-
}
|
|
108
|
-
})(),
|
|
109
|
-
]),
|
|
73
|
+
(async () => {
|
|
74
|
+
while (getQueuedItems() > queueSize) {
|
|
75
|
+
await waitForOutput();
|
|
76
|
+
}
|
|
77
|
+
})(),
|
|
110
78
|
]).finally(() => clear());
|
|
111
|
-
controller
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await waitFor({
|
|
115
|
-
unprocessed: 0,
|
|
116
|
-
unemitted: 0,
|
|
117
|
-
minimumProgress: null,
|
|
118
|
-
controller,
|
|
119
|
-
});
|
|
79
|
+
if (controller) {
|
|
80
|
+
controller._internals._mediaParserController._internals.signal.removeEventListener('abort', clear);
|
|
81
|
+
}
|
|
120
82
|
};
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
_unprocessed--;
|
|
83
|
+
const waitForFinish = async () => {
|
|
84
|
+
await waitForQueueSize(0);
|
|
124
85
|
};
|
|
125
86
|
return {
|
|
126
87
|
inputItem,
|
|
127
88
|
onOutput,
|
|
128
|
-
waitFor,
|
|
129
89
|
waitForFinish,
|
|
130
|
-
|
|
131
|
-
getUnprocessed,
|
|
90
|
+
waitForQueueSize,
|
|
132
91
|
};
|
|
133
92
|
};
|
|
134
93
|
exports.makeIoSynchronizer = makeIoSynchronizer;
|
|
@@ -2,7 +2,7 @@ import type { WebCodecsController } from '../webcodecs-controller';
|
|
|
2
2
|
export declare const makeTimeoutPromise: ({ label, ms, controller, }: {
|
|
3
3
|
label: () => string;
|
|
4
4
|
ms: number;
|
|
5
|
-
controller: WebCodecsController;
|
|
5
|
+
controller: WebCodecsController | null;
|
|
6
6
|
}) => {
|
|
7
7
|
timeoutPromise: Promise<void>;
|
|
8
8
|
clear: () => void;
|
|
@@ -19,8 +19,10 @@ const makeTimeoutPromise = ({ label, ms, controller, }) => {
|
|
|
19
19
|
const onResume = () => {
|
|
20
20
|
set();
|
|
21
21
|
};
|
|
22
|
-
controller
|
|
23
|
-
|
|
22
|
+
if (controller) {
|
|
23
|
+
controller.addEventListener('pause', onPause);
|
|
24
|
+
controller.addEventListener('resume', onResume);
|
|
25
|
+
}
|
|
24
26
|
return {
|
|
25
27
|
timeoutPromise: promise,
|
|
26
28
|
clear: () => {
|
|
@@ -28,8 +30,10 @@ const makeTimeoutPromise = ({ label, ms, controller, }) => {
|
|
|
28
30
|
clearTimeout(timeout);
|
|
29
31
|
}
|
|
30
32
|
resolve();
|
|
31
|
-
controller
|
|
32
|
-
|
|
33
|
+
if (controller) {
|
|
34
|
+
controller.removeEventListener('pause', onPause);
|
|
35
|
+
controller.removeEventListener('resume', onResume);
|
|
36
|
+
}
|
|
33
37
|
},
|
|
34
38
|
};
|
|
35
39
|
};
|
package/dist/on-audio-track.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { ConvertMediaContainer } from './get-available-containers';
|
|
|
7
7
|
import type { ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
|
|
8
8
|
import type { ConvertMediaProgressFn } from './throttled-state-update';
|
|
9
9
|
import type { WebCodecsController } from './webcodecs-controller';
|
|
10
|
-
export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer,
|
|
10
|
+
export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer, onAudioData, progressTracker, }: {
|
|
11
11
|
state: MediaFn;
|
|
12
12
|
defaultAudioCodec: ConvertMediaAudioCodec | null;
|
|
13
13
|
controller: WebCodecsController;
|
|
@@ -16,6 +16,6 @@ export declare const makeAudioTrackHandler: ({ state, defaultAudioCodec: audioCo
|
|
|
16
16
|
onAudioTrack: ConvertMediaOnAudioTrackHandler | null;
|
|
17
17
|
logLevel: MediaParserLogLevel;
|
|
18
18
|
outputContainer: ConvertMediaContainer;
|
|
19
|
-
progressTracker: ProgressTracker;
|
|
20
19
|
onAudioData: ConvertMediaOnAudioData | null;
|
|
20
|
+
progressTracker: ProgressTracker;
|
|
21
21
|
}) => MediaParserOnAudioTrack;
|
package/dist/on-audio-track.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.makeAudioTrackHandler = void 0;
|
|
4
|
-
const media_parser_1 = require("@remotion/media-parser");
|
|
5
|
-
const audio_decoder_1 = require("./audio-decoder");
|
|
6
|
-
const audio_decoder_config_1 = require("./audio-decoder-config");
|
|
7
|
-
const audio_encoder_1 = require("./audio-encoder");
|
|
8
|
-
const audio_encoder_config_1 = require("./audio-encoder-config");
|
|
9
4
|
const can_copy_audio_track_1 = require("./can-copy-audio-track");
|
|
10
|
-
const
|
|
5
|
+
const copy_audio_track_1 = require("./copy-audio-track");
|
|
11
6
|
const default_on_audio_track_handler_1 = require("./default-on-audio-track-handler");
|
|
12
7
|
const get_default_audio_codec_1 = require("./get-default-audio-codec");
|
|
13
|
-
const
|
|
14
|
-
const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer,
|
|
8
|
+
const reencode_audio_track_1 = require("./reencode-audio-track");
|
|
9
|
+
const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controller, abortConversion, onMediaStateUpdate, onAudioTrack, logLevel, outputContainer, onAudioData, progressTracker, }) => async ({ track, container: inputContainer }) => {
|
|
15
10
|
const canCopyTrack = (0, can_copy_audio_track_1.canCopyAudioTrack)({
|
|
16
11
|
inputCodec: track.codecEnum,
|
|
17
12
|
outputContainer,
|
|
@@ -32,154 +27,24 @@ const makeAudioTrackHandler = ({ state, defaultAudioCodec: audioCodec, controlle
|
|
|
32
27
|
throw new Error(`Audio track with ID ${track.trackId} resolved with {"type": "fail"}. This could mean that this audio track could neither be copied to the output container or re-encoded. You have the option to drop the track instead of failing it: https://remotion.dev/docs/webcodecs/track-transformation`);
|
|
33
28
|
}
|
|
34
29
|
if (audioOperation.type === 'copy') {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
timescale: track.originalTimescale,
|
|
30
|
+
return (0, copy_audio_track_1.copyAudioTrack)({
|
|
31
|
+
logLevel,
|
|
32
|
+
onMediaStateUpdate,
|
|
33
|
+
state,
|
|
34
|
+
track,
|
|
35
|
+
progressTracker,
|
|
42
36
|
});
|
|
43
|
-
log_1.Log.verbose(logLevel, `Copying audio track ${track.trackId} as track ${addedTrack.trackNumber}. Timescale = ${track.originalTimescale}, codec = ${track.codecEnum} (${track.codec}) `);
|
|
44
|
-
return async (audioSample) => {
|
|
45
|
-
await state.addSample({
|
|
46
|
-
chunk: audioSample,
|
|
47
|
-
trackNumber: addedTrack.trackNumber,
|
|
48
|
-
isVideo: false,
|
|
49
|
-
codecPrivate: track.codecData?.data ?? null,
|
|
50
|
-
});
|
|
51
|
-
onMediaStateUpdate?.((prevState) => {
|
|
52
|
-
return {
|
|
53
|
-
...prevState,
|
|
54
|
-
encodedAudioFrames: prevState.encodedAudioFrames + 1,
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
37
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
sampleRate: audioOperation.sampleRate ?? track.sampleRate,
|
|
62
|
-
codec: audioOperation.audioCodec,
|
|
63
|
-
bitrate: audioOperation.bitrate,
|
|
64
|
-
});
|
|
65
|
-
const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)({
|
|
66
|
-
codec: track.codec,
|
|
67
|
-
numberOfChannels: track.numberOfChannels,
|
|
68
|
-
sampleRate: track.sampleRate,
|
|
69
|
-
description: track.description,
|
|
70
|
-
});
|
|
71
|
-
log_1.Log.verbose(logLevel, 'Audio encoder config', audioEncoderConfig);
|
|
72
|
-
log_1.Log.verbose(logLevel, 'Audio decoder config', audioDecoderConfig ?? track);
|
|
73
|
-
if (!audioEncoderConfig) {
|
|
74
|
-
abortConversion(new Error(`Could not configure audio encoder of track ${track.trackId}`));
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
if (!audioDecoderConfig) {
|
|
78
|
-
abortConversion(new Error(`Could not configure audio decoder of track ${track.trackId}`));
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
const codecPrivate = audioOperation.audioCodec === 'aac'
|
|
82
|
-
? media_parser_1.MediaParserInternals.createAacCodecPrivate({
|
|
83
|
-
audioObjectType: 2,
|
|
84
|
-
sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
|
|
85
|
-
channelConfiguration: audioEncoderConfig.numberOfChannels,
|
|
86
|
-
codecPrivate: null,
|
|
87
|
-
})
|
|
88
|
-
: null;
|
|
89
|
-
const { trackNumber } = await state.addTrack({
|
|
90
|
-
type: 'audio',
|
|
91
|
-
codec: audioOperation.audioCodec === 'wav'
|
|
92
|
-
? 'pcm-s16'
|
|
93
|
-
: audioOperation.audioCodec,
|
|
94
|
-
numberOfChannels: audioEncoderConfig.numberOfChannels,
|
|
95
|
-
sampleRate: audioOperation.sampleRate ?? audioEncoderConfig.sampleRate,
|
|
96
|
-
codecPrivate,
|
|
97
|
-
timescale: track.originalTimescale,
|
|
98
|
-
});
|
|
99
|
-
const audioEncoder = (0, audio_encoder_1.createAudioEncoder)({
|
|
100
|
-
// This is weird 😵💫
|
|
101
|
-
// Chrome completely ignores the sample rate and uses it's own
|
|
102
|
-
// We cannot determine it here because it depends on the system
|
|
103
|
-
// sample rate. Unhardcode then declare it later once we know.
|
|
104
|
-
onNewAudioSampleRate: (sampleRate) => {
|
|
105
|
-
state.updateTrackSampleRate({ sampleRate, trackNumber });
|
|
106
|
-
},
|
|
107
|
-
onChunk: async (chunk) => {
|
|
108
|
-
await state.addSample({
|
|
109
|
-
chunk: (0, convert_encoded_chunk_1.convertEncodedChunk)(chunk),
|
|
110
|
-
trackNumber,
|
|
111
|
-
isVideo: false,
|
|
112
|
-
codecPrivate,
|
|
113
|
-
});
|
|
114
|
-
onMediaStateUpdate?.((prevState) => {
|
|
115
|
-
return {
|
|
116
|
-
...prevState,
|
|
117
|
-
encodedAudioFrames: prevState.encodedAudioFrames + 1,
|
|
118
|
-
};
|
|
119
|
-
});
|
|
120
|
-
},
|
|
121
|
-
onError: (err) => {
|
|
122
|
-
abortConversion(new Error(`Audio encoder of track ${track.trackId} failed (see .cause of this error)`, {
|
|
123
|
-
cause: err,
|
|
124
|
-
}));
|
|
125
|
-
},
|
|
126
|
-
codec: audioOperation.audioCodec,
|
|
127
|
-
controller,
|
|
128
|
-
config: audioEncoderConfig,
|
|
129
|
-
logLevel,
|
|
130
|
-
progressTracker,
|
|
131
|
-
});
|
|
132
|
-
const audioDecoder = (0, audio_decoder_1.createAudioDecoder)({
|
|
133
|
-
onFrame: async (audioData) => {
|
|
134
|
-
const newAudioData = onAudioData
|
|
135
|
-
? await onAudioData?.({ audioData, track })
|
|
136
|
-
: audioData;
|
|
137
|
-
if (newAudioData !== audioData) {
|
|
138
|
-
if (newAudioData.duration !== audioData.duration) {
|
|
139
|
-
throw new Error(`onAudioData returned a different duration than the input audio data. Original duration: ${audioData.duration}, new duration: ${newAudioData.duration}`);
|
|
140
|
-
}
|
|
141
|
-
if (newAudioData.numberOfChannels !== audioData.numberOfChannels) {
|
|
142
|
-
throw new Error(`onAudioData returned a different number of channels than the input audio data. Original channels: ${audioData.numberOfChannels}, new channels: ${newAudioData.numberOfChannels}`);
|
|
143
|
-
}
|
|
144
|
-
if (newAudioData.sampleRate !== audioData.sampleRate) {
|
|
145
|
-
throw new Error(`onAudioData returned a different sample rate than the input audio data. Original sample rate: ${audioData.sampleRate}, new sample rate: ${newAudioData.sampleRate}`);
|
|
146
|
-
}
|
|
147
|
-
if (newAudioData.format !== audioData.format) {
|
|
148
|
-
throw new Error(`onAudioData returned a different format than the input audio data. Original format: ${audioData.format}, new format: ${newAudioData.format}`);
|
|
149
|
-
}
|
|
150
|
-
if (newAudioData.timestamp !== audioData.timestamp) {
|
|
151
|
-
throw new Error(`onAudioData returned a different timestamp than the input audio data. Original timestamp: ${audioData.timestamp}, new timestamp: ${newAudioData.timestamp}`);
|
|
152
|
-
}
|
|
153
|
-
audioData.close();
|
|
154
|
-
}
|
|
155
|
-
await audioEncoder.encodeFrame(newAudioData);
|
|
156
|
-
onMediaStateUpdate?.((prevState) => {
|
|
157
|
-
return {
|
|
158
|
-
...prevState,
|
|
159
|
-
decodedAudioFrames: prevState.decodedAudioFrames + 1,
|
|
160
|
-
};
|
|
161
|
-
});
|
|
162
|
-
newAudioData.close();
|
|
163
|
-
},
|
|
164
|
-
onError(error) {
|
|
165
|
-
abortConversion(new Error(`Audio decoder of track ${track.trackId} failed. Config: ${JSON.stringify(audioDecoderConfig)} (see .cause of this error)`, {
|
|
166
|
-
cause: error,
|
|
167
|
-
}));
|
|
168
|
-
},
|
|
38
|
+
return (0, reencode_audio_track_1.reencodeAudioTrack)({
|
|
39
|
+
abortConversion,
|
|
169
40
|
controller,
|
|
170
|
-
config: audioDecoderConfig,
|
|
171
41
|
logLevel,
|
|
42
|
+
onMediaStateUpdate,
|
|
43
|
+
audioOperation,
|
|
44
|
+
onAudioData,
|
|
45
|
+
state,
|
|
172
46
|
track,
|
|
173
47
|
progressTracker,
|
|
174
48
|
});
|
|
175
|
-
state.addWaitForFinishPromise(async () => {
|
|
176
|
-
await audioDecoder.waitForFinish();
|
|
177
|
-
await audioEncoder.waitForFinish();
|
|
178
|
-
audioDecoder.close();
|
|
179
|
-
audioEncoder.close();
|
|
180
|
-
});
|
|
181
|
-
return async (audioSample) => {
|
|
182
|
-
await audioDecoder.processSample(audioSample);
|
|
183
|
-
};
|
|
184
49
|
};
|
|
185
50
|
exports.makeAudioTrackHandler = makeAudioTrackHandler;
|
package/dist/on-frame.d.ts
CHANGED
|
@@ -2,13 +2,11 @@ import type { MediaParserVideoTrack } from '@remotion/media-parser';
|
|
|
2
2
|
import type { ConvertMediaOnVideoFrame } from './convert-media';
|
|
3
3
|
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
|
4
4
|
import type { ResizeOperation } from './resizing/mode';
|
|
5
|
-
|
|
6
|
-
export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, track, outputCodec, rotation, resizeOperation, }: {
|
|
5
|
+
export declare const onFrame: ({ frame: unrotatedFrame, onVideoFrame, track, outputCodec, rotation, resizeOperation, }: {
|
|
7
6
|
frame: VideoFrame;
|
|
8
7
|
onVideoFrame: ConvertMediaOnVideoFrame | null;
|
|
9
|
-
videoEncoder: WebCodecsVideoEncoder;
|
|
10
8
|
track: MediaParserVideoTrack;
|
|
11
9
|
outputCodec: ConvertMediaVideoCodec;
|
|
12
10
|
rotation: number;
|
|
13
11
|
resizeOperation: ResizeOperation | null;
|
|
14
|
-
}) => Promise<
|
|
12
|
+
}) => Promise<VideoFrame>;
|
package/dist/on-frame.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.onFrame = void 0;
|
|
|
4
4
|
const browser_quirks_1 = require("./browser-quirks");
|
|
5
5
|
const convert_to_correct_videoframe_1 = require("./convert-to-correct-videoframe");
|
|
6
6
|
const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
|
|
7
|
-
const onFrame = async ({ frame: unrotatedFrame, onVideoFrame,
|
|
7
|
+
const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, track, outputCodec, rotation, resizeOperation, }) => {
|
|
8
8
|
const rotated = (0, rotate_and_resize_video_frame_1.rotateAndResizeVideoFrame)({
|
|
9
9
|
rotation,
|
|
10
10
|
frame: unrotatedFrame,
|
|
@@ -18,10 +18,10 @@ const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, trac
|
|
|
18
18
|
? await onVideoFrame({ frame: rotated, track })
|
|
19
19
|
: rotated;
|
|
20
20
|
if (userProcessedFrame.displayWidth !== rotated.displayWidth) {
|
|
21
|
-
throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${userProcessedFrame.displayWidth}) than the input frame (${
|
|
21
|
+
throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayWidth (${userProcessedFrame.displayWidth}) than the input frame (${rotated.displayWidth})`);
|
|
22
22
|
}
|
|
23
23
|
if (userProcessedFrame.displayHeight !== rotated.displayHeight) {
|
|
24
|
-
throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${userProcessedFrame.displayHeight}) than the input frame (${
|
|
24
|
+
throw new Error(`Returned VideoFrame of track ${track.trackId} has different displayHeight (${userProcessedFrame.displayHeight}) than the input frame (${rotated.displayHeight})`);
|
|
25
25
|
}
|
|
26
26
|
// In Safari, calling new VideoFrame() might change the timestamp
|
|
27
27
|
// In flipVideo test from 803000 to 803299
|
|
@@ -31,17 +31,16 @@ const onFrame = async ({ frame: unrotatedFrame, onVideoFrame, videoEncoder, trac
|
|
|
31
31
|
if ((userProcessedFrame.duration ?? 0) !== (rotated.duration ?? 0)) {
|
|
32
32
|
throw new Error(`Returned VideoFrame of track ${track.trackId} has different duration (${userProcessedFrame.duration}) than the input frame (${rotated.duration}). When calling new VideoFrame(), pass {duration: frame.duration} as second argument`);
|
|
33
33
|
}
|
|
34
|
+
if (rotated !== userProcessedFrame) {
|
|
35
|
+
rotated.close();
|
|
36
|
+
}
|
|
34
37
|
const fixedFrame = (0, convert_to_correct_videoframe_1.convertToCorrectVideoFrame)({
|
|
35
38
|
videoFrame: userProcessedFrame,
|
|
36
39
|
outputCodec,
|
|
37
40
|
});
|
|
38
|
-
await videoEncoder.encodeFrame(fixedFrame, fixedFrame.timestamp);
|
|
39
|
-
fixedFrame.close();
|
|
40
|
-
if (rotated !== userProcessedFrame) {
|
|
41
|
-
rotated.close();
|
|
42
|
-
}
|
|
43
41
|
if (fixedFrame !== userProcessedFrame) {
|
|
44
|
-
|
|
42
|
+
userProcessedFrame.close();
|
|
45
43
|
}
|
|
44
|
+
return fixedFrame;
|
|
46
45
|
};
|
|
47
46
|
exports.onFrame = onFrame;
|
package/dist/on-video-track.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
|
|
|
8
8
|
import type { ResizeOperation } from './resizing/mode';
|
|
9
9
|
import type { ConvertMediaProgressFn } from './throttled-state-update';
|
|
10
10
|
import type { WebCodecsController } from './webcodecs-controller';
|
|
11
|
-
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate,
|
|
11
|
+
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, controller, defaultVideoCodec, onVideoTrack, logLevel, outputContainer, rotate, resizeOperation, progressTracker, }: {
|
|
12
12
|
state: MediaFn;
|
|
13
13
|
onVideoFrame: null | ConvertMediaOnVideoFrame;
|
|
14
14
|
onMediaStateUpdate: null | ConvertMediaProgressFn;
|
|
@@ -19,6 +19,6 @@ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaState
|
|
|
19
19
|
logLevel: MediaParserLogLevel;
|
|
20
20
|
outputContainer: ConvertMediaContainer;
|
|
21
21
|
rotate: number;
|
|
22
|
-
progress: ProgressTracker;
|
|
23
22
|
resizeOperation: ResizeOperation | null;
|
|
23
|
+
progressTracker: ProgressTracker;
|
|
24
24
|
}) => MediaParserOnVideoTrack;
|