@remotion/webcodecs 4.0.267 → 4.0.268

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.
@@ -19,7 +19,7 @@ export type EbmlParsedOrUint8Array<T extends Ebml> = {
19
19
  value: EbmlValueOrUint8Array<T>;
20
20
  minVintWidth: number | null;
21
21
  };
22
- export declare const measureEBMLVarInt: (value: number) => 2 | 1 | 4 | 3 | 5 | 6;
22
+ export declare const measureEBMLVarInt: (value: number) => 2 | 1 | 5 | 4 | 3 | 6;
23
23
  export declare const getVariableInt: (value: number, minWidth: number | null) => Uint8Array;
24
24
  export declare const makeMatroskaBytes: (fields: PossibleEbmlOrUint8Array) => BytesAndOffset;
25
25
  export type PossibleEbmlOrUint8Array = Prettify<{
@@ -1954,6 +1954,59 @@ var onFrame = async ({
1954
1954
  }
1955
1955
  };
1956
1956
 
1957
+ // src/sort-video-frames.ts
1958
+ var MAX_QUEUE_SIZE = 5;
1959
+ var videoFrameSorter = ({
1960
+ onRelease,
1961
+ controller
1962
+ }) => {
1963
+ const frames = [];
1964
+ const releaseFrame = async () => {
1965
+ await controller._internals.checkForAbortAndPause();
1966
+ const frame = frames.shift();
1967
+ if (frame) {
1968
+ await onRelease(frame);
1969
+ }
1970
+ };
1971
+ const sortFrames = () => {
1972
+ frames.sort((a, b) => a.timestamp - b.timestamp);
1973
+ };
1974
+ const releaseIfQueueFull = async () => {
1975
+ if (frames.length >= MAX_QUEUE_SIZE) {
1976
+ sortFrames();
1977
+ await releaseFrame();
1978
+ }
1979
+ };
1980
+ const addFrame = (frame) => {
1981
+ frames.push(frame);
1982
+ };
1983
+ const inputFrame = async (frame) => {
1984
+ addFrame(frame);
1985
+ await releaseIfQueueFull();
1986
+ };
1987
+ const onAbort = () => {
1988
+ while (frames.length > 0) {
1989
+ const frame = frames.shift();
1990
+ if (frame) {
1991
+ frame.close();
1992
+ }
1993
+ }
1994
+ frames.length = 0;
1995
+ };
1996
+ const flush = async () => {
1997
+ sortFrames();
1998
+ while (frames.length > 0) {
1999
+ await releaseFrame();
2000
+ }
2001
+ controller._internals.signal.removeEventListener("abort", onAbort);
2002
+ };
2003
+ controller._internals.signal.addEventListener("abort", onAbort);
2004
+ return {
2005
+ inputFrame,
2006
+ flush
2007
+ };
2008
+ };
2009
+
1957
2010
  // src/video-decoder.ts
1958
2011
  var createVideoDecoder = ({
1959
2012
  onFrame: onFrame2,
@@ -1969,28 +2022,38 @@ var createVideoDecoder = ({
1969
2022
  progress
1970
2023
  });
1971
2024
  let outputQueue = Promise.resolve();
2025
+ const addToQueue = (frame) => {
2026
+ const cleanup = () => {
2027
+ frame.close();
2028
+ };
2029
+ controller._internals.signal.addEventListener("abort", cleanup, {
2030
+ once: true
2031
+ });
2032
+ outputQueue = outputQueue.then(() => {
2033
+ if (controller._internals.signal.aborted) {
2034
+ return;
2035
+ }
2036
+ return onFrame2(frame);
2037
+ }).then(() => {
2038
+ ioSynchronizer.onProcessed();
2039
+ }).catch((err) => {
2040
+ onError(err);
2041
+ }).finally(() => {
2042
+ controller._internals.signal.removeEventListener("abort", cleanup);
2043
+ cleanup();
2044
+ });
2045
+ return outputQueue;
2046
+ };
2047
+ const frameSorter = videoFrameSorter({
2048
+ controller,
2049
+ onRelease: async (frame) => {
2050
+ await addToQueue(frame);
2051
+ }
2052
+ });
1972
2053
  const videoDecoder = new VideoDecoder({
1973
- output(inputFrame) {
1974
- ioSynchronizer.onOutput(inputFrame.timestamp);
1975
- const abortHandler = () => {
1976
- inputFrame.close();
1977
- };
1978
- controller._internals.signal.addEventListener("abort", abortHandler, {
1979
- once: true
1980
- });
1981
- outputQueue = outputQueue.then(() => {
1982
- if (controller._internals.signal.aborted) {
1983
- return;
1984
- }
1985
- return onFrame2(inputFrame);
1986
- }).then(() => {
1987
- ioSynchronizer.onProcessed();
1988
- controller._internals.signal.removeEventListener("abort", abortHandler);
1989
- return Promise.resolve();
1990
- }).catch((err) => {
1991
- inputFrame.close();
1992
- onError(err);
1993
- });
2054
+ output(frame) {
2055
+ ioSynchronizer.onOutput(frame.timestamp);
2056
+ frameSorter.inputFrame(frame);
1994
2057
  },
1995
2058
  error(error) {
1996
2059
  onError(error);
@@ -2018,13 +2081,10 @@ var createVideoDecoder = ({
2018
2081
  progress.setPossibleLowestTimestamp(Math.min(sample.timestamp, sample.dts ?? Infinity, sample.cts ?? Infinity));
2019
2082
  await ioSynchronizer.waitFor({
2020
2083
  unemitted: 20,
2021
- unprocessed: 2,
2084
+ unprocessed: 10,
2022
2085
  minimumProgress: sample.timestamp - 1e7,
2023
2086
  controller
2024
2087
  });
2025
- if (sample.type === "key") {
2026
- await videoDecoder.flush();
2027
- }
2028
2088
  videoDecoder.decode(new EncodedVideoChunk(sample));
2029
2089
  ioSynchronizer.inputItem(sample.timestamp, sample.type === "key");
2030
2090
  };
@@ -2037,6 +2097,8 @@ var createVideoDecoder = ({
2037
2097
  waitForFinish: async () => {
2038
2098
  await videoDecoder.flush();
2039
2099
  Log.verbose(logLevel, "Flushed video decoder");
2100
+ await frameSorter.flush();
2101
+ Log.verbose(logLevel, "Frame sorter flushed");
2040
2102
  await ioSynchronizer.waitForFinish(controller);
2041
2103
  Log.verbose(logLevel, "IO synchro finished");
2042
2104
  await outputQueue;
@@ -0,0 +1,8 @@
1
+ import type { WebCodecsController } from './webcodecs-controller';
2
+ export declare const videoFrameSorter: ({ onRelease, controller, }: {
3
+ onRelease: (frame: VideoFrame) => Promise<void>;
4
+ controller: WebCodecsController;
5
+ }) => {
6
+ inputFrame: (frame: VideoFrame) => Promise<void>;
7
+ flush: () => Promise<void>;
8
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.videoFrameSorter = void 0;
4
+ const MAX_QUEUE_SIZE = 5;
5
+ const videoFrameSorter = ({ onRelease, controller, }) => {
6
+ const frames = [];
7
+ const releaseFrame = async () => {
8
+ await controller._internals.checkForAbortAndPause();
9
+ const frame = frames.shift();
10
+ if (frame) {
11
+ await onRelease(frame);
12
+ }
13
+ };
14
+ const sortFrames = () => {
15
+ frames.sort((a, b) => a.timestamp - b.timestamp);
16
+ };
17
+ const releaseIfQueueFull = async () => {
18
+ if (frames.length >= MAX_QUEUE_SIZE) {
19
+ sortFrames();
20
+ await releaseFrame();
21
+ }
22
+ };
23
+ const addFrame = (frame) => {
24
+ frames.push(frame);
25
+ };
26
+ const inputFrame = async (frame) => {
27
+ addFrame(frame);
28
+ await releaseIfQueueFull();
29
+ };
30
+ const onAbort = () => {
31
+ while (frames.length > 0) {
32
+ const frame = frames.shift();
33
+ if (frame) {
34
+ frame.close();
35
+ }
36
+ }
37
+ frames.length = 0;
38
+ };
39
+ const flush = async () => {
40
+ sortFrames();
41
+ while (frames.length > 0) {
42
+ await releaseFrame();
43
+ }
44
+ controller._internals.signal.removeEventListener('abort', onAbort);
45
+ };
46
+ controller._internals.signal.addEventListener('abort', onAbort);
47
+ return {
48
+ inputFrame,
49
+ flush,
50
+ };
51
+ };
52
+ exports.videoFrameSorter = videoFrameSorter;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createVideoDecoder = void 0;
4
4
  const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
5
  const log_1 = require("./log");
6
+ const sort_video_frames_1 = require("./sort-video-frames");
6
7
  const createVideoDecoder = ({ onFrame, onError, controller, config, logLevel, progress, }) => {
7
8
  const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
8
9
  logLevel,
@@ -10,31 +11,42 @@ const createVideoDecoder = ({ onFrame, onError, controller, config, logLevel, pr
10
11
  progress,
11
12
  });
12
13
  let outputQueue = Promise.resolve();
14
+ const addToQueue = (frame) => {
15
+ const cleanup = () => {
16
+ frame.close();
17
+ };
18
+ controller._internals.signal.addEventListener('abort', cleanup, {
19
+ once: true,
20
+ });
21
+ outputQueue = outputQueue
22
+ .then(() => {
23
+ if (controller._internals.signal.aborted) {
24
+ return;
25
+ }
26
+ return onFrame(frame);
27
+ })
28
+ .then(() => {
29
+ ioSynchronizer.onProcessed();
30
+ })
31
+ .catch((err) => {
32
+ onError(err);
33
+ })
34
+ .finally(() => {
35
+ controller._internals.signal.removeEventListener('abort', cleanup);
36
+ cleanup();
37
+ });
38
+ return outputQueue;
39
+ };
40
+ const frameSorter = (0, sort_video_frames_1.videoFrameSorter)({
41
+ controller,
42
+ onRelease: async (frame) => {
43
+ await addToQueue(frame);
44
+ },
45
+ });
13
46
  const videoDecoder = new VideoDecoder({
14
- output(inputFrame) {
15
- ioSynchronizer.onOutput(inputFrame.timestamp);
16
- const abortHandler = () => {
17
- inputFrame.close();
18
- };
19
- controller._internals.signal.addEventListener('abort', abortHandler, {
20
- once: true,
21
- });
22
- outputQueue = outputQueue
23
- .then(() => {
24
- if (controller._internals.signal.aborted) {
25
- return;
26
- }
27
- return onFrame(inputFrame);
28
- })
29
- .then(() => {
30
- ioSynchronizer.onProcessed();
31
- controller._internals.signal.removeEventListener('abort', abortHandler);
32
- return Promise.resolve();
33
- })
34
- .catch((err) => {
35
- inputFrame.close();
36
- onError(err);
37
- });
47
+ output(frame) {
48
+ ioSynchronizer.onOutput(frame.timestamp);
49
+ frameSorter.inputFrame(frame);
38
50
  },
39
51
  error(error) {
40
52
  onError(error);
@@ -65,13 +77,14 @@ const createVideoDecoder = ({ onFrame, onError, controller, config, logLevel, pr
65
77
  progress.setPossibleLowestTimestamp(Math.min(sample.timestamp, (_a = sample.dts) !== null && _a !== void 0 ? _a : Infinity, (_b = sample.cts) !== null && _b !== void 0 ? _b : Infinity));
66
78
  await ioSynchronizer.waitFor({
67
79
  unemitted: 20,
68
- unprocessed: 2,
80
+ unprocessed: 10,
69
81
  minimumProgress: sample.timestamp - 10000000,
70
82
  controller,
71
83
  });
72
- if (sample.type === 'key') {
73
- await videoDecoder.flush();
74
- }
84
+ // Don't flush here.
85
+ // We manually keep track of the memory with the IO synchornizer.
86
+ // Example of flushing breaking things:
87
+ // IMG_2310.MOV has B-frames, and if we flush on a keyframe, we discard some frames that are yet to come.
75
88
  videoDecoder.decode(new EncodedVideoChunk(sample));
76
89
  ioSynchronizer.inputItem(sample.timestamp, sample.type === 'key');
77
90
  };
@@ -84,6 +97,8 @@ const createVideoDecoder = ({ onFrame, onError, controller, config, logLevel, pr
84
97
  waitForFinish: async () => {
85
98
  await videoDecoder.flush();
86
99
  log_1.Log.verbose(logLevel, 'Flushed video decoder');
100
+ await frameSorter.flush();
101
+ log_1.Log.verbose(logLevel, 'Frame sorter flushed');
87
102
  await ioSynchronizer.waitForFinish(controller);
88
103
  log_1.Log.verbose(logLevel, 'IO synchro finished');
89
104
  await outputQueue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/webcodecs",
3
- "version": "4.0.267",
3
+ "version": "4.0.268",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -17,15 +17,15 @@
17
17
  "author": "Jonny Burger <jonny@remotion.dev>",
18
18
  "license": "Remotion License (See https://remotion.dev/docs/webcodecs#license)",
19
19
  "dependencies": {
20
- "@remotion/media-parser": "4.0.267",
21
- "@remotion/licensing": "4.0.267"
20
+ "@remotion/media-parser": "4.0.268",
21
+ "@remotion/licensing": "4.0.268"
22
22
  },
23
23
  "peerDependencies": {},
24
24
  "devDependencies": {
25
25
  "@types/dom-webcodecs": "0.1.11",
26
26
  "eslint": "9.19.0",
27
- "@remotion/example-videos": "4.0.267",
28
- "@remotion/eslint-config-internal": "4.0.267"
27
+ "@remotion/eslint-config-internal": "4.0.268",
28
+ "@remotion/example-videos": "4.0.268"
29
29
  },
30
30
  "keywords": [],
31
31
  "publishConfig": {