@remotion/webcodecs 4.0.394 → 4.0.396

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.
@@ -0,0 +1,24 @@
1
+ import type { MediaParserLogLevel } from '@remotion/media-parser';
2
+ import type { WebCodecsController } from './webcodecs-controller';
3
+ export type WebCodecsAudioDecoder = {
4
+ decode: (audioSample: EncodedAudioChunkInit | EncodedAudioChunk) => void;
5
+ close: () => void;
6
+ flush: () => Promise<void>;
7
+ waitForFinish: () => Promise<void>;
8
+ waitForQueueToBeLessThan: (items: number) => Promise<void>;
9
+ };
10
+ export type CreateAudioDecoderInit = {
11
+ onFrame: (frame: AudioData) => Promise<void> | void;
12
+ onError: (error: Error) => void;
13
+ controller: WebCodecsController | null;
14
+ config: AudioDecoderConfig;
15
+ logLevel: MediaParserLogLevel;
16
+ };
17
+ export declare const internalCreateAudioDecoder: ({ onFrame, onError, controller, config, logLevel, }: CreateAudioDecoderInit) => WebCodecsAudioDecoder;
18
+ export declare const createAudioDecoder: ({ onFrame, onError, controller, track, logLevel, }: {
19
+ track: AudioDecoderConfig;
20
+ onFrame: (frame: AudioData) => Promise<void> | void;
21
+ onError: (error: Error) => void;
22
+ controller?: WebCodecsController | null;
23
+ logLevel?: MediaParserLogLevel;
24
+ }) => WebCodecsAudioDecoder;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAudioDecoder = exports.internalCreateAudioDecoder = void 0;
4
+ const get_wave_audio_decoder_1 = require("./get-wave-audio-decoder");
5
+ const io_synchronizer_1 = require("./io-manager/io-synchronizer");
6
+ const internalCreateAudioDecoder = ({ onFrame, onError, controller, config, logLevel, }) => {
7
+ if (controller &&
8
+ controller._internals._mediaParserController._internals.signal.aborted) {
9
+ throw new Error('Not creating audio decoder, already aborted');
10
+ }
11
+ const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
12
+ logLevel,
13
+ label: 'Audio decoder',
14
+ controller,
15
+ });
16
+ if (config.codec === 'pcm-s16') {
17
+ return (0, get_wave_audio_decoder_1.getWaveAudioDecoder)({
18
+ onFrame,
19
+ config,
20
+ sampleFormat: 's16',
21
+ logLevel,
22
+ ioSynchronizer,
23
+ onError,
24
+ });
25
+ }
26
+ const audioDecoder = new AudioDecoder({
27
+ async output(frame) {
28
+ try {
29
+ await onFrame(frame);
30
+ }
31
+ catch (err) {
32
+ frame.close();
33
+ onError(err);
34
+ }
35
+ ioSynchronizer.onOutput(frame.timestamp + (frame.duration ?? 0));
36
+ },
37
+ error(error) {
38
+ onError(error);
39
+ },
40
+ });
41
+ const close = () => {
42
+ if (controller) {
43
+ controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
44
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
45
+ onAbort);
46
+ }
47
+ if (audioDecoder.state === 'closed') {
48
+ return;
49
+ }
50
+ audioDecoder.close();
51
+ };
52
+ const onAbort = () => {
53
+ close();
54
+ };
55
+ if (controller) {
56
+ controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
57
+ }
58
+ audioDecoder.configure(config);
59
+ const processSample = (audioSample) => {
60
+ if (audioDecoder.state === 'closed') {
61
+ return;
62
+ }
63
+ // Don't flush, it messes up the audio
64
+ const chunk = audioSample instanceof EncodedAudioChunk
65
+ ? audioSample
66
+ : new EncodedAudioChunk(audioSample);
67
+ audioDecoder.decode(chunk);
68
+ // https://test-streams.mux.dev/x36xhzz/url_0/url_525/193039199_mp4_h264_aac_hd_7.ts
69
+ // has a 16 byte audio sample at the end which chrome does not decode
70
+ // Might be empty audio
71
+ // For now only reporting chunks that are bigger than that
72
+ // 16 was chosen arbitrarily, can be improved
73
+ if (chunk.byteLength > 16) {
74
+ ioSynchronizer.inputItem(chunk.timestamp);
75
+ }
76
+ };
77
+ return {
78
+ decode: (sample) => {
79
+ processSample(sample);
80
+ },
81
+ waitForFinish: async () => {
82
+ // Firefox might throw "Needs to be configured first"
83
+ try {
84
+ await audioDecoder.flush();
85
+ }
86
+ catch { }
87
+ await ioSynchronizer.waitForFinish();
88
+ },
89
+ close,
90
+ flush: async () => {
91
+ await audioDecoder.flush();
92
+ },
93
+ waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
94
+ };
95
+ };
96
+ exports.internalCreateAudioDecoder = internalCreateAudioDecoder;
97
+ const createAudioDecoder = ({ onFrame, onError, controller, track, logLevel, }) => {
98
+ return (0, exports.internalCreateAudioDecoder)({
99
+ onFrame,
100
+ onError,
101
+ controller: controller ?? null,
102
+ config: track,
103
+ logLevel: logLevel ?? 'error',
104
+ });
105
+ };
106
+ exports.createAudioDecoder = createAudioDecoder;
@@ -0,0 +1 @@
1
+ export declare const canUseWebFsWriter: () => Promise<boolean>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canUseWebFsWriter = void 0;
4
+ const canUseWebFsWriter = async () => {
5
+ if (!('storage' in navigator)) {
6
+ return false;
7
+ }
8
+ if (!('getDirectory' in navigator.storage)) {
9
+ return false;
10
+ }
11
+ try {
12
+ const directoryHandle = await navigator.storage.getDirectory();
13
+ const fileHandle = await directoryHandle.getFileHandle('remotion-probe-web-fs-support', {
14
+ create: true,
15
+ });
16
+ const canUse = fileHandle.createWritable !== undefined;
17
+ return canUse;
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ };
23
+ exports.canUseWebFsWriter = canUseWebFsWriter;
@@ -1,4 +1,4 @@
1
- export declare const stringsToUint8Array: (str: string) => Uint8Array<ArrayBuffer>;
1
+ export declare const stringsToUint8Array: (str: string) => Uint8Array<ArrayBufferLike>;
2
2
  export declare const numberTo32BitUIntOrInt: (num: number) => Uint8Array<ArrayBuffer>;
3
3
  export declare const numberTo64BitUIntOrInt: (num: number | bigint) => Uint8Array<ArrayBuffer>;
4
4
  export declare const numberTo32BitUIntOrIntLeading128: (num: number) => Uint8Array<ArrayBuffer>;
@@ -0,0 +1,8 @@
1
+ export declare const extractFrames: ({ fromSeconds, toSeconds, width, height, src, onFrame, }: {
2
+ fromSeconds: number;
3
+ toSeconds: number;
4
+ width: number;
5
+ height: number;
6
+ src: string;
7
+ onFrame: (frame: VideoFrame) => void;
8
+ }) => Promise<void>;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractFrames = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
5
+ const worker_1 = require("@remotion/media-parser/worker");
6
+ const create_video_decoder_1 = require("./create-video-decoder");
7
+ const extractFrames = async ({ fromSeconds, toSeconds, width, height, src, onFrame, }) => {
8
+ const controller = (0, media_parser_1.mediaParserController)();
9
+ controller.seek(fromSeconds);
10
+ const segmentDuration = toSeconds - fromSeconds;
11
+ const expectedFrames = [];
12
+ try {
13
+ await (0, worker_1.parseMediaOnWebWorker)({
14
+ src: new URL(src, window.location.href).toString(),
15
+ acknowledgeRemotionLicense: true,
16
+ controller,
17
+ onVideoTrack: ({ track }) => {
18
+ const aspectRatio = track.width / track.height;
19
+ const framesFitInWidth = Math.ceil(width / (height * aspectRatio));
20
+ const timestampTargets = [];
21
+ for (let i = 0; i < framesFitInWidth; i++) {
22
+ timestampTargets.push(fromSeconds +
23
+ ((segmentDuration * media_parser_1.WEBCODECS_TIMESCALE) / framesFitInWidth) *
24
+ (i + 0.5));
25
+ }
26
+ const decoder = (0, create_video_decoder_1.createVideoDecoder)({
27
+ onFrame: (frame) => {
28
+ if (frame.timestamp >= expectedFrames[0] - 1) {
29
+ expectedFrames.shift();
30
+ onFrame(frame);
31
+ }
32
+ frame.close();
33
+ },
34
+ onError: console.error,
35
+ track,
36
+ });
37
+ const queued = [];
38
+ return async (sample) => {
39
+ const nextTimestampWeWant = timestampTargets[0];
40
+ if (nextTimestampWeWant === undefined) {
41
+ throw new Error('this should not happen');
42
+ }
43
+ if (sample.type === 'key') {
44
+ queued.length = 0;
45
+ }
46
+ queued.push(sample);
47
+ if (sample.timestamp > nextTimestampWeWant) {
48
+ expectedFrames.push(timestampTargets.shift());
49
+ while (queued.length > 0) {
50
+ const sam = queued.shift();
51
+ await decoder.waitForQueueToBeLessThan(10);
52
+ await decoder.decode(sam);
53
+ }
54
+ if (timestampTargets.length === 0) {
55
+ await decoder.flush();
56
+ controller.abort();
57
+ }
58
+ }
59
+ };
60
+ },
61
+ });
62
+ }
63
+ catch (e) {
64
+ if (!(0, media_parser_1.hasBeenAborted)(e)) {
65
+ throw e;
66
+ }
67
+ }
68
+ };
69
+ exports.extractFrames = extractFrames;
@@ -1,111 +1,3 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __toESM = (mod, isNodeMode, target) => {
7
- target = mod != null ? __create(__getProtoOf(mod)) : {};
8
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
9
- for (let key of __getOwnPropNames(mod))
10
- if (!__hasOwnProp.call(to, key))
11
- __defProp(to, key, {
12
- get: () => mod[key],
13
- enumerable: true
14
- });
15
- return to;
16
- };
17
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
18
-
19
- // ../licensing/dist/register-usage-point.js
20
- var require_register_usage_point = __commonJS((exports) => {
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.registerUsageEvent = exports.HOST = undefined;
23
- exports.HOST = "https://www.remotion.pro";
24
- var registerUsageEvent = async ({ apiKey, host, succeeded, event }) => {
25
- const abortController = new AbortController;
26
- const timeout = setTimeout(() => {
27
- abortController.abort();
28
- }, 1e4);
29
- try {
30
- const res = await fetch(`${exports.HOST}/api/track/register-usage-point`, {
31
- method: "POST",
32
- body: JSON.stringify({
33
- event,
34
- apiKey,
35
- host,
36
- succeeded
37
- }),
38
- headers: {
39
- "Content-Type": "application/json"
40
- },
41
- signal: abortController.signal
42
- });
43
- clearTimeout(timeout);
44
- const json = await res.json();
45
- if (json.success) {
46
- return {
47
- billable: json.billable,
48
- classification: json.classification
49
- };
50
- }
51
- if (!res.ok) {
52
- throw new Error(json.error);
53
- }
54
- const read = await res.json();
55
- return read;
56
- } catch (err) {
57
- clearTimeout(timeout);
58
- if (err instanceof Error && err.name === "AbortError") {
59
- throw new Error("Request timed out after 10 seconds");
60
- }
61
- throw err;
62
- }
63
- };
64
- exports.registerUsageEvent = registerUsageEvent;
65
- });
66
-
67
- // ../licensing/dist/get-usage.js
68
- var require_get_usage = __commonJS((exports) => {
69
- Object.defineProperty(exports, "__esModule", { value: true });
70
- exports.getUsage = undefined;
71
- var register_usage_point_1 = require_register_usage_point();
72
- var getUsage = async ({ apiKey, since }) => {
73
- const res = await fetch(`${register_usage_point_1.HOST}/api/track/get-usage`, {
74
- method: "POST",
75
- body: JSON.stringify({
76
- apiKey,
77
- since: since !== null && since !== undefined ? since : null
78
- }),
79
- headers: {
80
- "Content-Type": "application/json"
81
- }
82
- });
83
- const json = await res.json();
84
- if (json.success) {
85
- return {
86
- cloudRenders: json.cloudRenders,
87
- webcodecConversions: json.webcodecConversions
88
- };
89
- }
90
- throw new Error(json.error);
91
- };
92
- exports.getUsage = getUsage;
93
- });
94
-
95
- // ../licensing/dist/index.js
96
- var require_dist = __commonJS((exports) => {
97
- Object.defineProperty(exports, "__esModule", { value: true });
98
- exports.getUsage = exports.registerUsageEvent = undefined;
99
- var register_usage_point_1 = require_register_usage_point();
100
- Object.defineProperty(exports, "registerUsageEvent", { enumerable: true, get: function() {
101
- return register_usage_point_1.registerUsageEvent;
102
- } });
103
- var get_usage_1 = require_get_usage();
104
- Object.defineProperty(exports, "getUsage", { enumerable: true, get: function() {
105
- return get_usage_1.getUsage;
106
- } });
107
- });
108
-
109
1
  // src/writers/web-fs.ts
110
2
  var createContent = async ({
111
3
  filename
@@ -5353,8 +5245,55 @@ var makeVideoTrackHandler = ({
5353
5245
  });
5354
5246
  };
5355
5247
 
5248
+ // ../licensing/dist/esm/index.mjs
5249
+ var HOST = "https://www.remotion.pro";
5250
+ var registerUsageEvent = async ({
5251
+ apiKey,
5252
+ host,
5253
+ succeeded,
5254
+ event
5255
+ }) => {
5256
+ const abortController = new AbortController;
5257
+ const timeout = setTimeout(() => {
5258
+ abortController.abort();
5259
+ }, 1e4);
5260
+ try {
5261
+ const res = await fetch(`${HOST}/api/track/register-usage-point`, {
5262
+ method: "POST",
5263
+ body: JSON.stringify({
5264
+ event,
5265
+ apiKey,
5266
+ host,
5267
+ succeeded
5268
+ }),
5269
+ headers: {
5270
+ "Content-Type": "application/json"
5271
+ },
5272
+ signal: abortController.signal
5273
+ });
5274
+ clearTimeout(timeout);
5275
+ const json = await res.json();
5276
+ if (json.success) {
5277
+ return {
5278
+ billable: json.billable,
5279
+ classification: json.classification
5280
+ };
5281
+ }
5282
+ if (!res.ok) {
5283
+ throw new Error(json.error);
5284
+ }
5285
+ const read = await res.json();
5286
+ return read;
5287
+ } catch (err) {
5288
+ clearTimeout(timeout);
5289
+ if (err instanceof Error && err.name === "AbortError") {
5290
+ throw new Error("Request timed out after 10 seconds");
5291
+ }
5292
+ throw err;
5293
+ }
5294
+ };
5295
+
5356
5296
  // src/send-telemetry-event.ts
5357
- var import_licensing = __toESM(require_dist(), 1);
5358
5297
  var sendUsageEvent = async ({
5359
5298
  apiKey,
5360
5299
  succeeded
@@ -5363,7 +5302,7 @@ var sendUsageEvent = async ({
5363
5302
  if (host === null) {
5364
5303
  return;
5365
5304
  }
5366
- await import_licensing.registerUsageEvent({
5305
+ await registerUsageEvent({
5367
5306
  apiKey,
5368
5307
  event: "webcodec-conversion",
5369
5308
  host,
@@ -1,21 +1,3 @@
1
- var __create = Object.create;
2
- var __getProtoOf = Object.getPrototypeOf;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __toESM = (mod, isNodeMode, target) => {
7
- target = mod != null ? __create(__getProtoOf(mod)) : {};
8
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
9
- for (let key of __getOwnPropNames(mod))
10
- if (!__hasOwnProp.call(to, key))
11
- __defProp(to, key, {
12
- get: () => mod[key],
13
- enumerable: true
14
- });
15
- return to;
16
- };
17
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
18
-
19
1
  // src/extract-frames-on-web-worker.ts
20
2
  import { parseMediaOnWebWorker } from "@remotion/media-parser/worker";
21
3
 
@@ -0,0 +1,23 @@
1
+ import type { MediaParserLogLevel, MediaParserVideoSample } from '@remotion/media-parser';
2
+ import type { WebCodecsController } from './webcodecs-controller';
3
+ export type WebCodecsVideoDecoder = {
4
+ decode: (videoSample: MediaParserVideoSample | EncodedVideoChunk) => void;
5
+ close: () => void;
6
+ flush: () => Promise<void>;
7
+ waitForFinish: () => Promise<void>;
8
+ waitForQueueToBeLessThan: (items: number) => Promise<void>;
9
+ };
10
+ export declare const internalCreateVideoDecoder: ({ onFrame, onError, controller, config, logLevel, }: {
11
+ onFrame: (frame: VideoFrame) => Promise<void> | void;
12
+ onError: (error: Error) => void;
13
+ controller: WebCodecsController | null;
14
+ config: VideoDecoderConfig;
15
+ logLevel: MediaParserLogLevel;
16
+ }) => WebCodecsVideoDecoder;
17
+ export declare const createVideoDecoder: ({ onFrame, onError, controller, track, logLevel, }: {
18
+ track: VideoDecoderConfig;
19
+ onFrame: (frame: VideoFrame) => Promise<void> | void;
20
+ onError: (error: Error) => void;
21
+ controller?: WebCodecsController;
22
+ logLevel?: MediaParserLogLevel;
23
+ }) => WebCodecsVideoDecoder;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createVideoDecoder = exports.internalCreateVideoDecoder = void 0;
4
+ const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
+ const log_1 = require("./log");
6
+ const internalCreateVideoDecoder = ({ onFrame, onError, controller, config, logLevel, }) => {
7
+ const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
8
+ logLevel,
9
+ label: 'Video decoder',
10
+ controller,
11
+ });
12
+ const videoDecoder = new VideoDecoder({
13
+ async output(frame) {
14
+ try {
15
+ await onFrame(frame);
16
+ }
17
+ catch (err) {
18
+ onError(err);
19
+ frame.close();
20
+ }
21
+ ioSynchronizer.onOutput(frame.timestamp);
22
+ },
23
+ error(error) {
24
+ onError(error);
25
+ },
26
+ });
27
+ const close = () => {
28
+ if (controller) {
29
+ controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
30
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
31
+ onAbort);
32
+ }
33
+ if (videoDecoder.state === 'closed') {
34
+ return;
35
+ }
36
+ videoDecoder.close();
37
+ };
38
+ const onAbort = () => {
39
+ close();
40
+ };
41
+ if (controller) {
42
+ controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
43
+ }
44
+ videoDecoder.configure(config);
45
+ const decode = (sample) => {
46
+ if (videoDecoder.state === 'closed') {
47
+ return;
48
+ }
49
+ const encodedChunk = sample instanceof EncodedVideoChunk
50
+ ? sample
51
+ : new EncodedVideoChunk(sample);
52
+ videoDecoder.decode(encodedChunk);
53
+ ioSynchronizer.inputItem(sample.timestamp);
54
+ };
55
+ return {
56
+ decode,
57
+ waitForFinish: async () => {
58
+ await videoDecoder.flush();
59
+ log_1.Log.verbose(logLevel, 'Flushed video decoder');
60
+ await ioSynchronizer.waitForFinish();
61
+ log_1.Log.verbose(logLevel, 'IO synchro finished');
62
+ },
63
+ close,
64
+ flush: async () => {
65
+ await videoDecoder.flush();
66
+ },
67
+ waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
68
+ };
69
+ };
70
+ exports.internalCreateVideoDecoder = internalCreateVideoDecoder;
71
+ const createVideoDecoder = ({ onFrame, onError, controller, track, logLevel, }) => {
72
+ return (0, exports.internalCreateVideoDecoder)({
73
+ onFrame,
74
+ onError,
75
+ controller: controller ?? null,
76
+ config: track,
77
+ logLevel: logLevel ?? 'info',
78
+ });
79
+ };
80
+ exports.createVideoDecoder = createVideoDecoder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/webcodecs",
3
- "version": "4.0.394",
3
+ "version": "4.0.396",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -27,14 +27,14 @@
27
27
  "author": "Jonny Burger <jonny@remotion.dev>",
28
28
  "license": "Remotion License (See https://remotion.dev/docs/webcodecs#license)",
29
29
  "dependencies": {
30
- "@remotion/media-parser": "4.0.394",
31
- "@remotion/licensing": "4.0.394"
30
+ "@remotion/media-parser": "4.0.396",
31
+ "@remotion/licensing": "4.0.396"
32
32
  },
33
33
  "peerDependencies": {},
34
34
  "devDependencies": {
35
35
  "@types/dom-webcodecs": "0.1.11",
36
- "@remotion/example-videos": "4.0.394",
37
- "@remotion/eslint-config-internal": "4.0.394",
36
+ "@remotion/example-videos": "4.0.396",
37
+ "@remotion/eslint-config-internal": "4.0.396",
38
38
  "playwright": "1.55.1",
39
39
  "vite": "5.4.21",
40
40
  "@playwright/test": "1.55.1",