@remotion/webcodecs 4.0.226 → 4.0.228
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 +3 -3
- package/dist/audio-decoder.js +23 -29
- package/dist/audio-encoder.d.ts +3 -2
- package/dist/audio-encoder.js +17 -29
- package/dist/auto-select-writer.d.ts +3 -0
- package/dist/auto-select-writer.js +20 -0
- package/dist/calculate-progress.d.ts +4 -0
- package/dist/calculate-progress.js +10 -0
- package/dist/convert-media.d.ts +11 -4
- package/dist/convert-media.js +60 -9
- package/dist/esm/index.mjs +339 -150
- package/dist/event-emitter.d.ts +25 -0
- package/dist/event-emitter.js +23 -0
- package/dist/io-manager/event-emitter.d.ts +27 -0
- package/dist/io-manager/event-emitter.js +24 -0
- package/dist/io-manager/io-synchronizer.d.ts +12 -0
- package/dist/io-manager/io-synchronizer.js +95 -0
- package/dist/log.d.ts +10 -0
- package/dist/log.js +6 -0
- package/dist/on-audio-track.d.ts +3 -2
- package/dist/on-audio-track.js +3 -1
- package/dist/on-video-track.d.ts +3 -2
- package/dist/on-video-track.js +8 -3
- package/dist/video-decoder.d.ts +3 -3
- package/dist/video-decoder.js +23 -28
- package/dist/video-encoder.d.ts +3 -2
- package/dist/video-encoder.js +26 -29
- package/dist/wait-until-return.d.ts +4 -0
- package/dist/wait-until-return.js +14 -0
- package/dist/with-resolvers.d.ts +5 -0
- package/dist/with-resolvers.js +16 -1
- package/package.json +8 -6
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type Input = {
|
|
2
|
+
timestamp: number;
|
|
3
|
+
keyframe: boolean;
|
|
4
|
+
};
|
|
5
|
+
type Output = {
|
|
6
|
+
timestamp: number;
|
|
7
|
+
};
|
|
8
|
+
type IoEventMap = {
|
|
9
|
+
input: Input;
|
|
10
|
+
output: Output;
|
|
11
|
+
};
|
|
12
|
+
export type IoEventTypes = keyof IoEventMap;
|
|
13
|
+
export type CallbackListener<T extends IoEventTypes> = (data: {
|
|
14
|
+
detail: IoEventMap[T];
|
|
15
|
+
}) => void;
|
|
16
|
+
type IoListeners = {
|
|
17
|
+
[EventType in IoEventTypes]: CallbackListener<EventType>[];
|
|
18
|
+
};
|
|
19
|
+
export declare class IoEventEmitter {
|
|
20
|
+
listeners: IoListeners;
|
|
21
|
+
addEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
|
|
22
|
+
removeEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
|
|
23
|
+
dispatchEvent<T extends IoEventTypes>(dispatchName: T, context: IoEventMap[T]): void;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IoEventEmitter = void 0;
|
|
4
|
+
class IoEventEmitter {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.listeners = {
|
|
7
|
+
input: [],
|
|
8
|
+
output: [],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
addEventListener(name, callback) {
|
|
12
|
+
this.listeners[name].push(callback);
|
|
13
|
+
}
|
|
14
|
+
removeEventListener(name, callback) {
|
|
15
|
+
this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
|
|
16
|
+
}
|
|
17
|
+
dispatchEvent(dispatchName, context) {
|
|
18
|
+
this.listeners[dispatchName].forEach((callback) => {
|
|
19
|
+
callback({ detail: context });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.IoEventEmitter = IoEventEmitter;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type Input = {
|
|
2
|
+
timestamp: number;
|
|
3
|
+
keyFrame: boolean;
|
|
4
|
+
};
|
|
5
|
+
type Output = {
|
|
6
|
+
timestamp: number;
|
|
7
|
+
};
|
|
8
|
+
type Processed = {};
|
|
9
|
+
type IoEventMap = {
|
|
10
|
+
input: Input;
|
|
11
|
+
output: Output;
|
|
12
|
+
processed: Processed;
|
|
13
|
+
};
|
|
14
|
+
export type IoEventTypes = keyof IoEventMap;
|
|
15
|
+
export type CallbackListener<T extends IoEventTypes> = (data: {
|
|
16
|
+
detail: IoEventMap[T];
|
|
17
|
+
}) => void;
|
|
18
|
+
type IoListeners = {
|
|
19
|
+
[EventType in IoEventTypes]: CallbackListener<EventType>[];
|
|
20
|
+
};
|
|
21
|
+
export declare class IoEventEmitter {
|
|
22
|
+
listeners: IoListeners;
|
|
23
|
+
addEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
|
|
24
|
+
removeEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
|
|
25
|
+
dispatchEvent<T extends IoEventTypes>(dispatchName: T, context: IoEventMap[T]): void;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IoEventEmitter = void 0;
|
|
4
|
+
class IoEventEmitter {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.listeners = {
|
|
7
|
+
input: [],
|
|
8
|
+
output: [],
|
|
9
|
+
processed: [],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
addEventListener(name, callback) {
|
|
13
|
+
this.listeners[name].push(callback);
|
|
14
|
+
}
|
|
15
|
+
removeEventListener(name, callback) {
|
|
16
|
+
this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
|
|
17
|
+
}
|
|
18
|
+
dispatchEvent(dispatchName, context) {
|
|
19
|
+
this.listeners[dispatchName].forEach((callback) => {
|
|
20
|
+
callback({ detail: context });
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.IoEventEmitter = IoEventEmitter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LogLevel } from '../log';
|
|
2
|
+
export declare const makeIoSynchronizer: (logLevel: LogLevel, label: string) => {
|
|
3
|
+
inputItem: (timestamp: number, keyFrame: boolean) => void;
|
|
4
|
+
onOutput: (timestamp: number) => void;
|
|
5
|
+
waitFor: ({ _unprocessed, unemitted, }: {
|
|
6
|
+
unemitted: number;
|
|
7
|
+
_unprocessed: number;
|
|
8
|
+
}) => Promise<void>;
|
|
9
|
+
waitForFinish: () => Promise<void>;
|
|
10
|
+
onProcessed: () => void;
|
|
11
|
+
getUnprocessed: () => number;
|
|
12
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeIoSynchronizer = void 0;
|
|
4
|
+
const log_1 = require("../log");
|
|
5
|
+
const with_resolvers_1 = require("../with-resolvers");
|
|
6
|
+
const event_emitter_1 = require("./event-emitter");
|
|
7
|
+
const makeIoSynchronizer = (logLevel, label) => {
|
|
8
|
+
const eventEmitter = new event_emitter_1.IoEventEmitter();
|
|
9
|
+
let lastInput = 0;
|
|
10
|
+
let lastInputKeyframe = 0;
|
|
11
|
+
let lastOutput = 0;
|
|
12
|
+
let inputsSinceLastOutput = 0;
|
|
13
|
+
let inputs = [];
|
|
14
|
+
let keyframes = [];
|
|
15
|
+
// Once WebCodecs emits items, the user has to handle them
|
|
16
|
+
// Let's keep count of how many items are unprocessed
|
|
17
|
+
let unprocessed = 0;
|
|
18
|
+
const getUnprocessed = () => unprocessed;
|
|
19
|
+
const getUnemittedItems = () => {
|
|
20
|
+
inputs = inputs.filter((input) => input > lastOutput);
|
|
21
|
+
return inputs.length;
|
|
22
|
+
};
|
|
23
|
+
const getUnemittedKeyframes = () => {
|
|
24
|
+
keyframes = keyframes.filter((keyframe) => keyframe > lastOutput);
|
|
25
|
+
return keyframes.length;
|
|
26
|
+
};
|
|
27
|
+
const printState = (prefix) => {
|
|
28
|
+
log_1.Log.trace(logLevel, `[${label}] ${prefix}, state: Last input = ${lastInput} Last input keyframe = ${lastInputKeyframe} Last output = ${lastOutput} Inputs since last output = ${inputsSinceLastOutput}, Queue = ${getUnemittedItems()} (${getUnemittedKeyframes()} keyframes), Unprocessed = ${getUnprocessed()}`);
|
|
29
|
+
};
|
|
30
|
+
const inputItem = (timestamp, keyFrame) => {
|
|
31
|
+
lastInput = timestamp;
|
|
32
|
+
if (keyFrame) {
|
|
33
|
+
lastInputKeyframe = timestamp;
|
|
34
|
+
keyframes.push(timestamp);
|
|
35
|
+
}
|
|
36
|
+
inputsSinceLastOutput++;
|
|
37
|
+
inputs.push(timestamp);
|
|
38
|
+
eventEmitter.dispatchEvent('input', {
|
|
39
|
+
timestamp,
|
|
40
|
+
keyFrame,
|
|
41
|
+
});
|
|
42
|
+
printState('Input item');
|
|
43
|
+
};
|
|
44
|
+
const onOutput = (timestamp) => {
|
|
45
|
+
lastOutput = timestamp;
|
|
46
|
+
inputsSinceLastOutput = 0;
|
|
47
|
+
eventEmitter.dispatchEvent('output', {
|
|
48
|
+
timestamp,
|
|
49
|
+
});
|
|
50
|
+
unprocessed++;
|
|
51
|
+
printState('Got output');
|
|
52
|
+
};
|
|
53
|
+
const waitForOutput = () => {
|
|
54
|
+
const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
|
|
55
|
+
const on = () => {
|
|
56
|
+
eventEmitter.removeEventListener('output', on);
|
|
57
|
+
resolve();
|
|
58
|
+
};
|
|
59
|
+
eventEmitter.addEventListener('output', on);
|
|
60
|
+
return promise;
|
|
61
|
+
};
|
|
62
|
+
const waitForProcessed = () => {
|
|
63
|
+
const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
|
|
64
|
+
const on = () => {
|
|
65
|
+
eventEmitter.removeEventListener('processed', on);
|
|
66
|
+
resolve();
|
|
67
|
+
};
|
|
68
|
+
eventEmitter.addEventListener('processed', on);
|
|
69
|
+
return promise;
|
|
70
|
+
};
|
|
71
|
+
const waitFor = async ({ _unprocessed, unemitted, }) => {
|
|
72
|
+
while (getUnemittedItems() > unemitted) {
|
|
73
|
+
await waitForOutput();
|
|
74
|
+
}
|
|
75
|
+
while (getUnprocessed() > _unprocessed) {
|
|
76
|
+
await waitForProcessed();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const waitForFinish = async () => {
|
|
80
|
+
await waitFor({ _unprocessed: 0, unemitted: 0 });
|
|
81
|
+
};
|
|
82
|
+
const onProcessed = () => {
|
|
83
|
+
eventEmitter.dispatchEvent('processed', {});
|
|
84
|
+
unprocessed--;
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
inputItem,
|
|
88
|
+
onOutput,
|
|
89
|
+
waitFor,
|
|
90
|
+
waitForFinish,
|
|
91
|
+
onProcessed,
|
|
92
|
+
getUnprocessed,
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
exports.makeIoSynchronizer = makeIoSynchronizer;
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LogLevel } from '@remotion/media-parser';
|
|
2
|
+
declare const Log: {
|
|
3
|
+
trace: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
|
|
4
|
+
verbose: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
|
|
5
|
+
info: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
|
|
6
|
+
warn: (logLevel: LogLevel, ...args: Parameters<typeof console.log>) => void;
|
|
7
|
+
error: (...args: Parameters<typeof console.log>) => void;
|
|
8
|
+
};
|
|
9
|
+
export { Log };
|
|
10
|
+
export type { LogLevel };
|
package/dist/log.js
ADDED
package/dist/on-audio-track.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { MediaFn, OnAudioTrack } from '@remotion/media-parser';
|
|
1
|
+
import type { LogLevel, MediaFn, OnAudioTrack } from '@remotion/media-parser';
|
|
2
2
|
import type { ConvertMediaAudioCodec } from './codec-id';
|
|
3
3
|
import type { ConvertMediaState } from './convert-media';
|
|
4
4
|
import Error from './error-cause';
|
|
5
5
|
import type { ResolveAudioActionFn } from './resolve-audio-action';
|
|
6
|
-
export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, }: {
|
|
6
|
+
export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, logLevel, }: {
|
|
7
7
|
state: MediaFn;
|
|
8
8
|
audioCodec: ConvertMediaAudioCodec;
|
|
9
9
|
convertMediaState: ConvertMediaState;
|
|
@@ -12,4 +12,5 @@ export declare const makeAudioTrackHandler: ({ state, audioCodec, convertMediaSt
|
|
|
12
12
|
onMediaStateUpdate: null | ((state: ConvertMediaState) => void);
|
|
13
13
|
onAudioTrack: ResolveAudioActionFn;
|
|
14
14
|
bitrate: number;
|
|
15
|
+
logLevel: LogLevel;
|
|
15
16
|
}) => OnAudioTrack;
|
package/dist/on-audio-track.js
CHANGED
|
@@ -10,7 +10,7 @@ const audio_encoder_1 = require("./audio-encoder");
|
|
|
10
10
|
const audio_encoder_config_1 = require("./audio-encoder-config");
|
|
11
11
|
const error_cause_1 = __importDefault(require("./error-cause"));
|
|
12
12
|
const resolve_audio_action_1 = require("./resolve-audio-action");
|
|
13
|
-
const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, }) => async (track) => {
|
|
13
|
+
const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controller, abortConversion, onMediaStateUpdate, onAudioTrack, bitrate, logLevel, }) => async (track) => {
|
|
14
14
|
const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
|
|
15
15
|
codec: audioCodec,
|
|
16
16
|
numberOfChannels: track.numberOfChannels,
|
|
@@ -76,6 +76,7 @@ const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controlle
|
|
|
76
76
|
codec: audioCodec,
|
|
77
77
|
signal: controller.signal,
|
|
78
78
|
config: audioEncoderConfig,
|
|
79
|
+
logLevel,
|
|
79
80
|
});
|
|
80
81
|
const audioDecoder = (0, audio_decoder_1.createAudioDecoder)({
|
|
81
82
|
onFrame: async (frame) => {
|
|
@@ -91,6 +92,7 @@ const makeAudioTrackHandler = ({ state, audioCodec, convertMediaState, controlle
|
|
|
91
92
|
},
|
|
92
93
|
signal: controller.signal,
|
|
93
94
|
config: audioDecoderConfig,
|
|
95
|
+
logLevel,
|
|
94
96
|
});
|
|
95
97
|
state.addWaitForFinishPromise(async () => {
|
|
96
98
|
await audioDecoder.waitForFinish();
|
package/dist/on-video-track.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { MediaFn, OnVideoTrack, VideoTrack } from '@remotion/media-parser';
|
|
1
|
+
import type { LogLevel, MediaFn, OnVideoTrack, VideoTrack } from '@remotion/media-parser';
|
|
2
2
|
import type { ConvertMediaVideoCodec } from './codec-id';
|
|
3
3
|
import type { ConvertMediaState } from './convert-media';
|
|
4
4
|
import Error from './error-cause';
|
|
5
5
|
import type { ResolveVideoActionFn } from './resolve-video-action';
|
|
6
|
-
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, }: {
|
|
6
|
+
export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, logLevel, }: {
|
|
7
7
|
state: MediaFn;
|
|
8
8
|
onVideoFrame: null | ((frame: VideoFrame, track: VideoTrack) => Promise<void>);
|
|
9
9
|
onMediaStateUpdate: null | ((state: ConvertMediaState) => void);
|
|
@@ -12,4 +12,5 @@ export declare const makeVideoTrackHandler: ({ state, onVideoFrame, onMediaState
|
|
|
12
12
|
controller: AbortController;
|
|
13
13
|
videoCodec: ConvertMediaVideoCodec;
|
|
14
14
|
onVideoTrack: ResolveVideoActionFn;
|
|
15
|
+
logLevel: LogLevel;
|
|
15
16
|
}) => OnVideoTrack;
|
package/dist/on-video-track.js
CHANGED
|
@@ -10,7 +10,10 @@ const video_decoder_1 = require("./video-decoder");
|
|
|
10
10
|
const video_decoder_config_1 = require("./video-decoder-config");
|
|
11
11
|
const video_encoder_1 = require("./video-encoder");
|
|
12
12
|
const video_encoder_config_1 = require("./video-encoder-config");
|
|
13
|
-
const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, }) => async (track) => {
|
|
13
|
+
const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortConversion, convertMediaState, controller, videoCodec, onVideoTrack, logLevel, }) => async (track) => {
|
|
14
|
+
if (controller.signal.aborted) {
|
|
15
|
+
throw new error_cause_1.default('Aborted');
|
|
16
|
+
}
|
|
14
17
|
const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
|
|
15
18
|
codec: videoCodec === 'vp9' ? 'vp09.00.10.08' : videoCodec,
|
|
16
19
|
height: track.displayAspectHeight,
|
|
@@ -36,8 +39,8 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
36
39
|
codec: track.codecWithoutConfig,
|
|
37
40
|
codecPrivate: track.codecPrivate,
|
|
38
41
|
});
|
|
39
|
-
return (sample) => {
|
|
40
|
-
state.addSample(new EncodedVideoChunk(sample), videoTrack.trackNumber, true);
|
|
42
|
+
return async (sample) => {
|
|
43
|
+
await state.addSample(new EncodedVideoChunk(sample), videoTrack.trackNumber, true);
|
|
41
44
|
convertMediaState.decodedVideoFrames++;
|
|
42
45
|
onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate({ ...convertMediaState });
|
|
43
46
|
};
|
|
@@ -71,6 +74,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
71
74
|
},
|
|
72
75
|
signal: controller.signal,
|
|
73
76
|
config: videoEncoderConfig,
|
|
77
|
+
logLevel,
|
|
74
78
|
});
|
|
75
79
|
const videoDecoder = (0, video_decoder_1.createVideoDecoder)({
|
|
76
80
|
config: videoDecoderConfig,
|
|
@@ -87,6 +91,7 @@ const makeVideoTrackHandler = ({ state, onVideoFrame, onMediaStateUpdate, abortC
|
|
|
87
91
|
}));
|
|
88
92
|
},
|
|
89
93
|
signal: controller.signal,
|
|
94
|
+
logLevel,
|
|
90
95
|
});
|
|
91
96
|
state.addWaitForFinishPromise(async () => {
|
|
92
97
|
await videoDecoder.waitForFinish();
|
package/dist/video-decoder.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type { VideoSample } from '@remotion/media-parser';
|
|
1
|
+
import type { LogLevel, VideoSample } from '@remotion/media-parser';
|
|
2
2
|
export type WebCodecsVideoDecoder = {
|
|
3
3
|
processSample: (videoSample: VideoSample) => Promise<void>;
|
|
4
4
|
waitForFinish: () => Promise<void>;
|
|
5
5
|
close: () => void;
|
|
6
|
-
getQueueSize: () => number;
|
|
7
6
|
flush: () => Promise<void>;
|
|
8
7
|
};
|
|
9
|
-
export declare const createVideoDecoder: ({ onFrame, onError, signal, config, }: {
|
|
8
|
+
export declare const createVideoDecoder: ({ onFrame, onError, signal, config, logLevel, }: {
|
|
10
9
|
onFrame: (frame: VideoFrame) => Promise<void>;
|
|
11
10
|
onError: (error: DOMException) => void;
|
|
12
11
|
signal: AbortSignal;
|
|
13
12
|
config: VideoDecoderConfig;
|
|
13
|
+
logLevel: LogLevel;
|
|
14
14
|
}) => WebCodecsVideoDecoder;
|
package/dist/video-decoder.js
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createVideoDecoder = void 0;
|
|
4
|
-
const
|
|
4
|
+
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
|
5
|
+
const createVideoDecoder = ({ onFrame, onError, signal, config, logLevel, }) => {
|
|
6
|
+
const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)(logLevel, 'Video decoder');
|
|
5
7
|
let outputQueue = Promise.resolve();
|
|
6
|
-
let outputQueueSize = 0;
|
|
7
|
-
let dequeueResolver = () => { };
|
|
8
8
|
const videoDecoder = new VideoDecoder({
|
|
9
9
|
output(inputFrame) {
|
|
10
|
-
|
|
10
|
+
ioSynchronizer.onOutput(inputFrame.timestamp);
|
|
11
|
+
const abortHandler = () => {
|
|
12
|
+
inputFrame.close();
|
|
13
|
+
};
|
|
14
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
11
15
|
outputQueue = outputQueue
|
|
12
|
-
.then(() => onFrame(inputFrame))
|
|
13
16
|
.then(() => {
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
if (signal.aborted) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
return onFrame(inputFrame);
|
|
21
|
+
})
|
|
22
|
+
.then(() => {
|
|
23
|
+
ioSynchronizer.onProcessed();
|
|
24
|
+
signal.removeEventListener('abort', abortHandler);
|
|
16
25
|
return Promise.resolve();
|
|
26
|
+
})
|
|
27
|
+
.catch((err) => {
|
|
28
|
+
inputFrame.close();
|
|
29
|
+
onError(err);
|
|
17
30
|
});
|
|
18
31
|
},
|
|
19
32
|
error(error) {
|
|
@@ -32,38 +45,21 @@ const createVideoDecoder = ({ onFrame, onError, signal, config, }) => {
|
|
|
32
45
|
close();
|
|
33
46
|
};
|
|
34
47
|
signal.addEventListener('abort', onAbort);
|
|
35
|
-
const getQueueSize = () => {
|
|
36
|
-
return videoDecoder.decodeQueueSize + outputQueueSize;
|
|
37
|
-
};
|
|
38
48
|
videoDecoder.configure(config);
|
|
39
|
-
const waitForDequeue = async () => {
|
|
40
|
-
await new Promise((r) => {
|
|
41
|
-
dequeueResolver = r;
|
|
42
|
-
videoDecoder.addEventListener('dequeue', () => r(), {
|
|
43
|
-
once: true,
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
const waitForFinish = async () => {
|
|
48
|
-
while (getQueueSize() > 0) {
|
|
49
|
-
await waitForDequeue();
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
49
|
const processSample = async (sample) => {
|
|
53
50
|
if (videoDecoder.state === 'closed') {
|
|
54
51
|
return;
|
|
55
52
|
}
|
|
56
|
-
while (getQueueSize() > 10) {
|
|
57
|
-
await waitForDequeue();
|
|
58
|
-
}
|
|
59
53
|
// @ts-expect-error - can have changed in the meanwhile
|
|
60
54
|
if (videoDecoder.state === 'closed') {
|
|
61
55
|
return;
|
|
62
56
|
}
|
|
57
|
+
await ioSynchronizer.waitFor({ unemitted: 20, _unprocessed: 2 });
|
|
63
58
|
if (sample.type === 'key') {
|
|
64
59
|
await videoDecoder.flush();
|
|
65
60
|
}
|
|
66
61
|
videoDecoder.decode(new EncodedVideoChunk(sample));
|
|
62
|
+
ioSynchronizer.inputItem(sample.timestamp, sample.type === 'key');
|
|
67
63
|
};
|
|
68
64
|
let inputQueue = Promise.resolve();
|
|
69
65
|
return {
|
|
@@ -73,12 +69,11 @@ const createVideoDecoder = ({ onFrame, onError, signal, config, }) => {
|
|
|
73
69
|
},
|
|
74
70
|
waitForFinish: async () => {
|
|
75
71
|
await videoDecoder.flush();
|
|
76
|
-
await waitForFinish();
|
|
72
|
+
await ioSynchronizer.waitForFinish();
|
|
77
73
|
await outputQueue;
|
|
78
74
|
await inputQueue;
|
|
79
75
|
},
|
|
80
76
|
close,
|
|
81
|
-
getQueueSize,
|
|
82
77
|
flush: async () => {
|
|
83
78
|
await videoDecoder.flush();
|
|
84
79
|
},
|
package/dist/video-encoder.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import type { LogLevel } from '@remotion/media-parser';
|
|
1
2
|
export type WebCodecsVideoEncoder = {
|
|
2
3
|
encodeFrame: (videoFrame: VideoFrame) => Promise<void>;
|
|
3
4
|
waitForFinish: () => Promise<void>;
|
|
4
5
|
close: () => void;
|
|
5
|
-
getQueueSize: () => number;
|
|
6
6
|
flush: () => Promise<void>;
|
|
7
7
|
};
|
|
8
|
-
export declare const createVideoEncoder: ({ onChunk, onError, signal, config, }: {
|
|
8
|
+
export declare const createVideoEncoder: ({ onChunk, onError, signal, config, logLevel, }: {
|
|
9
9
|
onChunk: (chunk: EncodedVideoChunk) => Promise<void>;
|
|
10
10
|
onError: (error: DOMException) => void;
|
|
11
11
|
signal: AbortSignal;
|
|
12
12
|
config: VideoEncoderConfig;
|
|
13
|
+
logLevel: LogLevel;
|
|
13
14
|
}) => WebCodecsVideoEncoder;
|
package/dist/video-encoder.js
CHANGED
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createVideoEncoder = void 0;
|
|
4
|
-
const
|
|
4
|
+
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
|
5
|
+
const createVideoEncoder = ({ onChunk, onError, signal, config, logLevel, }) => {
|
|
5
6
|
if (signal.aborted) {
|
|
6
7
|
throw new Error('Not creating video encoder, already aborted');
|
|
7
8
|
}
|
|
9
|
+
const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)(logLevel, 'Video encoder');
|
|
8
10
|
let outputQueue = Promise.resolve();
|
|
9
|
-
let outputQueueSize = 0;
|
|
10
|
-
let dequeueResolver = () => { };
|
|
11
11
|
const encoder = new VideoEncoder({
|
|
12
12
|
error(error) {
|
|
13
13
|
onError(error);
|
|
14
14
|
},
|
|
15
15
|
output(chunk) {
|
|
16
|
-
|
|
16
|
+
if (chunk.duration === null) {
|
|
17
|
+
throw new Error('Duration is null');
|
|
18
|
+
}
|
|
19
|
+
const timestamp = chunk.timestamp + chunk.duration;
|
|
20
|
+
ioSynchronizer.onOutput(timestamp);
|
|
17
21
|
outputQueue = outputQueue
|
|
18
|
-
.then(() => onChunk(chunk))
|
|
19
22
|
.then(() => {
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
if (signal.aborted) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
return onChunk(chunk);
|
|
27
|
+
})
|
|
28
|
+
.then(() => {
|
|
29
|
+
ioSynchronizer.onProcessed();
|
|
22
30
|
return Promise.resolve();
|
|
31
|
+
})
|
|
32
|
+
.catch((err) => {
|
|
33
|
+
onError(err);
|
|
23
34
|
});
|
|
24
35
|
},
|
|
25
36
|
});
|
|
@@ -35,38 +46,25 @@ const createVideoEncoder = ({ onChunk, onError, signal, config, }) => {
|
|
|
35
46
|
close();
|
|
36
47
|
};
|
|
37
48
|
signal.addEventListener('abort', onAbort);
|
|
38
|
-
const getQueueSize = () => {
|
|
39
|
-
return encoder.encodeQueueSize + outputQueueSize;
|
|
40
|
-
};
|
|
41
49
|
encoder.configure(config);
|
|
42
50
|
let framesProcessed = 0;
|
|
43
|
-
const waitForDequeue = async () => {
|
|
44
|
-
await new Promise((r) => {
|
|
45
|
-
dequeueResolver = r;
|
|
46
|
-
encoder.addEventListener('dequeue', () => r(), {
|
|
47
|
-
once: true,
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
const waitForFinish = async () => {
|
|
52
|
-
while (getQueueSize() > 0) {
|
|
53
|
-
await waitForDequeue();
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
51
|
const encodeFrame = async (frame) => {
|
|
57
52
|
if (encoder.state === 'closed') {
|
|
58
53
|
return;
|
|
59
54
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
await ioSynchronizer.waitFor({
|
|
56
|
+
unemitted: 2,
|
|
57
|
+
_unprocessed: 2,
|
|
58
|
+
});
|
|
63
59
|
// @ts-expect-error - can have changed in the meanwhile
|
|
64
60
|
if (encoder.state === 'closed') {
|
|
65
61
|
return;
|
|
66
62
|
}
|
|
63
|
+
const keyFrame = framesProcessed % 40 === 0;
|
|
67
64
|
encoder.encode(frame, {
|
|
68
|
-
keyFrame
|
|
65
|
+
keyFrame,
|
|
69
66
|
});
|
|
67
|
+
ioSynchronizer.inputItem(frame.timestamp, keyFrame);
|
|
70
68
|
framesProcessed++;
|
|
71
69
|
};
|
|
72
70
|
let inputQueue = Promise.resolve();
|
|
@@ -78,10 +76,9 @@ const createVideoEncoder = ({ onChunk, onError, signal, config, }) => {
|
|
|
78
76
|
waitForFinish: async () => {
|
|
79
77
|
await encoder.flush();
|
|
80
78
|
await outputQueue;
|
|
81
|
-
await waitForFinish();
|
|
79
|
+
await ioSynchronizer.waitForFinish();
|
|
82
80
|
},
|
|
83
81
|
close,
|
|
84
|
-
getQueueSize,
|
|
85
82
|
flush: async () => {
|
|
86
83
|
await encoder.flush();
|
|
87
84
|
},
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitUntilReturn = void 0;
|
|
4
|
+
const with_resolvers_1 = require("./with-resolvers");
|
|
5
|
+
const waitUntilReturn = () => {
|
|
6
|
+
const { promise, resolve } = (0, with_resolvers_1.withResolvers)();
|
|
7
|
+
return {
|
|
8
|
+
waitForReturn: () => promise,
|
|
9
|
+
isReturning: () => {
|
|
10
|
+
resolve(undefined);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
exports.waitUntilReturn = waitUntilReturn;
|
package/dist/with-resolvers.d.ts
CHANGED
|
@@ -3,3 +3,8 @@ export declare const withResolvers: <T>() => {
|
|
|
3
3
|
resolve: (value: T | PromiseLike<T>) => void;
|
|
4
4
|
reject: (reason?: any) => void;
|
|
5
5
|
};
|
|
6
|
+
export declare const withResolversAndWaitForReturn: <T>() => {
|
|
7
|
+
getPromiseToImmediatelyReturn: () => Promise<T>;
|
|
8
|
+
reject: (reason: unknown) => void;
|
|
9
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
10
|
+
};
|
package/dist/with-resolvers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.withResolvers = void 0;
|
|
3
|
+
exports.withResolversAndWaitForReturn = exports.withResolvers = void 0;
|
|
4
4
|
const withResolvers = function () {
|
|
5
5
|
let resolve;
|
|
6
6
|
let reject;
|
|
@@ -11,3 +11,18 @@ const withResolvers = function () {
|
|
|
11
11
|
return { promise, resolve: resolve, reject: reject };
|
|
12
12
|
};
|
|
13
13
|
exports.withResolvers = withResolvers;
|
|
14
|
+
const withResolversAndWaitForReturn = () => {
|
|
15
|
+
const { promise, reject, resolve } = (0, exports.withResolvers)();
|
|
16
|
+
const { promise: returnPromise, resolve: resolveReturn } = (0, exports.withResolvers)();
|
|
17
|
+
return {
|
|
18
|
+
getPromiseToImmediatelyReturn: () => {
|
|
19
|
+
resolveReturn(undefined);
|
|
20
|
+
return promise;
|
|
21
|
+
},
|
|
22
|
+
reject: (reason) => {
|
|
23
|
+
returnPromise.then(() => reject(reason));
|
|
24
|
+
},
|
|
25
|
+
resolve,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
exports.withResolversAndWaitForReturn = withResolversAndWaitForReturn;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/webcodecs",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.228",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
"author": "Jonny Burger <jonny@remotion.dev>",
|
|
18
18
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@remotion/media-parser": "4.0.
|
|
20
|
+
"@remotion/media-parser": "4.0.228"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@types/dom-webcodecs": "0.1.11"
|
|
24
|
+
"@types/dom-webcodecs": "0.1.11",
|
|
25
|
+
"eslint": "9.14.0",
|
|
26
|
+
"@remotion/eslint-config-internal": "4.0.228"
|
|
25
27
|
},
|
|
26
28
|
"keywords": [],
|
|
27
29
|
"publishConfig": {
|
|
@@ -30,8 +32,8 @@
|
|
|
30
32
|
"description": "Media conversion in the browser",
|
|
31
33
|
"scripts": {
|
|
32
34
|
"formatting": "prettier src --check",
|
|
33
|
-
"lint": "eslint src
|
|
34
|
-
"
|
|
35
|
-
"
|
|
35
|
+
"lint": "eslint src",
|
|
36
|
+
"watch": "tsc -w",
|
|
37
|
+
"make": "tsc -d && bun --env-file=../.env.bundle bundle.ts"
|
|
36
38
|
}
|
|
37
39
|
}
|