@remotion/webcodecs 4.0.228 → 4.0.230

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.
Files changed (62) hide show
  1. package/dist/arraybuffer-to-uint8-array.d.ts +1 -0
  2. package/dist/arraybuffer-to-uint8-array.js +7 -0
  3. package/dist/audio-decoder-config.js +3 -0
  4. package/dist/audio-decoder.d.ts +2 -2
  5. package/dist/audio-encoder-config.js +15 -2
  6. package/dist/audio-encoder.d.ts +2 -1
  7. package/dist/audio-encoder.js +16 -4
  8. package/dist/calculate-progress.d.ts +2 -2
  9. package/dist/calculate-progress.js +3 -3
  10. package/dist/can-copy-audio-track.d.ts +6 -0
  11. package/dist/can-copy-audio-track.js +13 -0
  12. package/dist/can-copy-video-track.d.ts +6 -0
  13. package/dist/can-copy-video-track.js +13 -0
  14. package/dist/can-reencode-audio-track.d.ts +7 -0
  15. package/dist/can-reencode-audio-track.js +16 -0
  16. package/dist/can-reencode-video-track.d.ts +6 -0
  17. package/dist/can-reencode-video-track.js +16 -0
  18. package/dist/choose-correct-avc1-profile.d.ts +5 -0
  19. package/dist/choose-correct-avc1-profile.js +54 -0
  20. package/dist/codec-id.d.ts +10 -2
  21. package/dist/codec-id.js +30 -0
  22. package/dist/convert-encoded-chunk.d.ts +3 -0
  23. package/dist/convert-encoded-chunk.js +38 -0
  24. package/dist/convert-media.d.ts +22 -14
  25. package/dist/convert-media.js +25 -20
  26. package/dist/default-on-audio-track-handler.d.ts +2 -0
  27. package/dist/default-on-audio-track-handler.js +36 -0
  28. package/dist/default-on-video-track-handler.d.ts +2 -0
  29. package/dist/default-on-video-track-handler.js +29 -0
  30. package/dist/esm/index.mjs +497 -210
  31. package/dist/get-default-audio-codec.d.ts +4 -0
  32. package/dist/get-default-audio-codec.js +13 -0
  33. package/dist/get-default-video-codec.d.ts +4 -0
  34. package/dist/get-default-video-codec.js +10 -0
  35. package/dist/index.d.ts +12 -1
  36. package/dist/index.js +21 -1
  37. package/dist/io-manager/io-synchronizer.js +2 -2
  38. package/dist/on-audio-track-handler.d.ts +19 -0
  39. package/dist/on-audio-track-handler.js +2 -0
  40. package/dist/on-audio-track.d.ts +6 -6
  41. package/dist/on-audio-track.js +54 -27
  42. package/dist/on-frame.d.ts +11 -0
  43. package/dist/on-frame.js +32 -0
  44. package/dist/on-video-track-handler.d.ts +18 -0
  45. package/dist/on-video-track-handler.js +2 -0
  46. package/dist/on-video-track.d.ts +10 -9
  47. package/dist/on-video-track.js +55 -25
  48. package/dist/video-decoder.d.ts +2 -2
  49. package/dist/video-decoder.js +5 -0
  50. package/dist/video-encoder-config.d.ts +7 -1
  51. package/dist/video-encoder-config.js +11 -1
  52. package/dist/video-encoder.d.ts +2 -2
  53. package/dist/video-encoder.js +4 -6
  54. package/package.json +4 -4
  55. package/dist/event-emitter.d.ts +0 -25
  56. package/dist/event-emitter.js +0 -23
  57. package/dist/resolve-audio-action.d.ts +0 -15
  58. package/dist/resolve-audio-action.js +0 -30
  59. package/dist/resolve-video-action.d.ts +0 -15
  60. package/dist/resolve-video-action.js +0 -33
  61. package/dist/wait-until-return.d.ts +0 -4
  62. package/dist/wait-until-return.js +0 -14
@@ -0,0 +1 @@
1
+ export declare const arrayBufferToUint8Array: (buffer: ArrayBuffer | null) => Uint8Array | null;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.arrayBufferToUint8Array = void 0;
4
+ const arrayBufferToUint8Array = (buffer) => {
5
+ return buffer ? new Uint8Array(buffer) : null;
6
+ };
7
+ exports.arrayBufferToUint8Array = arrayBufferToUint8Array;
@@ -5,6 +5,9 @@ const getAudioDecoderConfig = async (config) => {
5
5
  if (typeof AudioDecoder === 'undefined') {
6
6
  return null;
7
7
  }
8
+ if (typeof EncodedAudioChunk === 'undefined') {
9
+ return null;
10
+ }
8
11
  if ((await AudioDecoder.isConfigSupported(config)).supported) {
9
12
  return config;
10
13
  }
@@ -1,6 +1,6 @@
1
- import type { AudioSample, LogLevel } from '@remotion/media-parser';
1
+ import type { AudioOrVideoSample, LogLevel } from '@remotion/media-parser';
2
2
  export type WebCodecsAudioDecoder = {
3
- processSample: (audioSample: AudioSample) => Promise<void>;
3
+ processSample: (audioSample: AudioOrVideoSample) => Promise<void>;
4
4
  waitForFinish: () => Promise<void>;
5
5
  close: () => void;
6
6
  flush: () => Promise<void>;
@@ -1,12 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getAudioEncoderConfig = void 0;
4
+ const getCodecString = (audioCodec) => {
5
+ if (audioCodec === 'opus') {
6
+ return 'opus';
7
+ }
8
+ if (audioCodec === 'aac') {
9
+ return 'mp4a.40.02';
10
+ }
11
+ throw new Error(`Unsupported audio codec: ${audioCodec}`);
12
+ };
4
13
  const getAudioEncoderConfig = async (config) => {
14
+ const actualConfig = {
15
+ ...config,
16
+ codec: getCodecString(config.codec),
17
+ };
5
18
  if (typeof AudioEncoder === 'undefined') {
6
19
  return null;
7
20
  }
8
- if ((await AudioEncoder.isConfigSupported(config)).supported) {
9
- return config;
21
+ if ((await AudioEncoder.isConfigSupported(actualConfig)).supported) {
22
+ return actualConfig;
10
23
  }
11
24
  return null;
12
25
  };
@@ -6,11 +6,12 @@ export type WebCodecsAudioEncoder = {
6
6
  close: () => void;
7
7
  flush: () => Promise<void>;
8
8
  };
9
- export declare const createAudioEncoder: ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, }: {
9
+ export declare const createAudioEncoder: ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, onNewAudioSampleRate, }: {
10
10
  onChunk: (chunk: EncodedAudioChunk) => Promise<void>;
11
11
  onError: (error: DOMException) => void;
12
12
  codec: ConvertMediaAudioCodec;
13
13
  signal: AbortSignal;
14
14
  config: AudioEncoderConfig;
15
15
  logLevel: LogLevel;
16
+ onNewAudioSampleRate: (sampleRate: number) => void;
16
17
  }) => WebCodecsAudioEncoder;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAudioEncoder = void 0;
4
4
  const io_synchronizer_1 = require("./io-manager/io-synchronizer");
5
- const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, }) => {
5
+ const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEncoderConfig, logLevel, onNewAudioSampleRate, }) => {
6
6
  if (signal.aborted) {
7
7
  throw new Error('Not creating audio encoder, already aborted');
8
8
  }
@@ -42,10 +42,10 @@ const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEnco
42
42
  close();
43
43
  };
44
44
  signal.addEventListener('abort', onAbort);
45
- if (codec !== 'opus') {
46
- throw new Error('Only `codec: "opus"` is supported currently');
45
+ if (codec !== 'opus' && codec !== 'aac') {
46
+ throw new Error('Only `codec: "opus"` and `codec: "aac"` is supported currently');
47
47
  }
48
- encoder.configure(audioEncoderConfig);
48
+ const wantedSampleRate = audioEncoderConfig.sampleRate;
49
49
  const encodeFrame = async (audioData) => {
50
50
  if (encoder.state === 'closed') {
51
51
  return;
@@ -55,6 +55,18 @@ const createAudioEncoder = ({ onChunk, onError, codec, signal, config: audioEnco
55
55
  if (encoder.state === 'closed') {
56
56
  return;
57
57
  }
58
+ if (encoder.state === 'unconfigured') {
59
+ if (audioData.sampleRate === wantedSampleRate) {
60
+ encoder.configure(audioEncoderConfig);
61
+ }
62
+ else {
63
+ encoder.configure({
64
+ ...audioEncoderConfig,
65
+ sampleRate: audioData.sampleRate,
66
+ });
67
+ onNewAudioSampleRate(audioData.sampleRate);
68
+ }
69
+ }
58
70
  encoder.encode(audioData);
59
71
  ioSynchronizer.inputItem(audioData.timestamp, true);
60
72
  };
@@ -1,4 +1,4 @@
1
- export declare const calculateProgress: ({ millisecondsWritten, expectedOutputMilliseconds, }: {
1
+ export declare const calculateProgress: ({ millisecondsWritten, expectedOutputDurationInMs, }: {
2
2
  millisecondsWritten: number;
3
- expectedOutputMilliseconds: number | null;
3
+ expectedOutputDurationInMs: number | null;
4
4
  }) => number | null;
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.calculateProgress = void 0;
4
- const calculateProgress = ({ millisecondsWritten, expectedOutputMilliseconds, }) => {
5
- if (expectedOutputMilliseconds === null) {
4
+ const calculateProgress = ({ millisecondsWritten, expectedOutputDurationInMs, }) => {
5
+ if (expectedOutputDurationInMs === null) {
6
6
  return null;
7
7
  }
8
- return millisecondsWritten / expectedOutputMilliseconds;
8
+ return millisecondsWritten / expectedOutputDurationInMs;
9
9
  };
10
10
  exports.calculateProgress = calculateProgress;
@@ -0,0 +1,6 @@
1
+ import type { MediaParserAudioCodec } from '@remotion/media-parser';
2
+ import type { ConvertMediaContainer } from './codec-id';
3
+ export declare const canCopyAudioTrack: ({ inputCodec, container, }: {
4
+ inputCodec: MediaParserAudioCodec;
5
+ container: ConvertMediaContainer;
6
+ }) => boolean;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canCopyAudioTrack = void 0;
4
+ const canCopyAudioTrack = ({ inputCodec, container, }) => {
5
+ if (container === 'webm') {
6
+ return inputCodec === 'opus';
7
+ }
8
+ if (container === 'mp4') {
9
+ return inputCodec === 'aac';
10
+ }
11
+ throw new Error(`Unhandled codec: ${container}`);
12
+ };
13
+ exports.canCopyAudioTrack = canCopyAudioTrack;
@@ -0,0 +1,6 @@
1
+ import type { MediaParserVideoCodec } from '@remotion/media-parser';
2
+ import type { ConvertMediaContainer } from './codec-id';
3
+ export declare const canCopyVideoTrack: ({ inputCodec, container, }: {
4
+ inputCodec: MediaParserVideoCodec;
5
+ container: ConvertMediaContainer;
6
+ }) => boolean;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canCopyVideoTrack = void 0;
4
+ const canCopyVideoTrack = ({ inputCodec, container, }) => {
5
+ if (container === 'webm') {
6
+ return inputCodec === 'vp8' || inputCodec === 'vp9';
7
+ }
8
+ if (container === 'mp4') {
9
+ return inputCodec === 'h264' || inputCodec === 'h265';
10
+ }
11
+ throw new Error(`Unhandled codec: ${container}`);
12
+ };
13
+ exports.canCopyVideoTrack = canCopyVideoTrack;
@@ -0,0 +1,7 @@
1
+ import type { AudioTrack } from '@remotion/media-parser';
2
+ import type { ConvertMediaAudioCodec } from './codec-id';
3
+ export declare const canReencodeAudioTrack: ({ track, audioCodec, bitrate, }: {
4
+ track: AudioTrack;
5
+ audioCodec: ConvertMediaAudioCodec;
6
+ bitrate: number;
7
+ }) => Promise<boolean>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canReencodeAudioTrack = void 0;
4
+ const audio_decoder_config_1 = require("./audio-decoder-config");
5
+ const audio_encoder_config_1 = require("./audio-encoder-config");
6
+ const canReencodeAudioTrack = async ({ track, audioCodec, bitrate, }) => {
7
+ const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)(track);
8
+ const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
9
+ codec: audioCodec,
10
+ numberOfChannels: track.numberOfChannels,
11
+ sampleRate: track.sampleRate,
12
+ bitrate,
13
+ });
14
+ return Boolean(audioDecoderConfig && audioEncoderConfig);
15
+ };
16
+ exports.canReencodeAudioTrack = canReencodeAudioTrack;
@@ -0,0 +1,6 @@
1
+ import type { VideoTrack } from '@remotion/media-parser';
2
+ import type { ConvertMediaVideoCodec } from './codec-id';
3
+ export declare const canReencodeVideoTrack: ({ videoCodec, track, }: {
4
+ videoCodec: ConvertMediaVideoCodec;
5
+ track: VideoTrack;
6
+ }) => Promise<boolean>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canReencodeVideoTrack = void 0;
4
+ const video_decoder_config_1 = require("./video-decoder-config");
5
+ const video_encoder_config_1 = require("./video-encoder-config");
6
+ const canReencodeVideoTrack = async ({ videoCodec, track, }) => {
7
+ const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
8
+ codec: videoCodec,
9
+ height: track.displayAspectHeight,
10
+ width: track.displayAspectWidth,
11
+ fps: track.fps,
12
+ });
13
+ const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
14
+ return Boolean(videoDecoderConfig && videoEncoderConfig);
15
+ };
16
+ exports.canReencodeVideoTrack = canReencodeVideoTrack;
@@ -0,0 +1,5 @@
1
+ export declare const chooseCorrectAvc1Profile: ({ width, height, fps, }: {
2
+ width: number;
3
+ height: number;
4
+ fps: number | null;
5
+ }) => string;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chooseCorrectAvc1Profile = void 0;
4
+ const chooseCorrectAvc1Profile = ({ width, height, fps, }) => {
5
+ // 0x42 = Baseline profile
6
+ // 0x4D = Main profile
7
+ // 0x58 = Extended profile
8
+ // 0x64 = High profile
9
+ // According to Wikipedia, 0x64 is the most widely supported profile.
10
+ // So we always choose 0x64.
11
+ /**
12
+ Ignoring lower levels of <720p, let's only support Players that can play 720p and above.
13
+ • Level 3.1 = 1F (hex) -> 1,280×720@30.0 (5)
14
+ • Level 3.2 = 20 (hex) -> 1,280×1,024@42.2 (4)
15
+ • Level 4.0 = 28 (hex) -> 2,048×1,024@30.0 (4)
16
+ • Level 4.1 = 29 (hex) -> 2,048×1,024@30.0 (4)
17
+ • Level 4.2 = 2A (hex) -> 2,048×1,080@60.0 (4)
18
+ • Level 5.0 = 32 (hex) -> 3,672×1,536@26.7 (5)
19
+ • Level 5.1 = 33 (hex) -> 4,096×2,304@26.7 (5)
20
+ • Level 5.2 = 34 (hex) -> 4,096×2,304@56.3 (5)
21
+ • Level 6.0 = 3C (hex) -> 8,192×4,320@30.2 (5)
22
+ • Level 6.1 = 3D (hex) -> 8,192×4,320@60.4 (5)
23
+ • Level 6.2 = 3E (hex) -> 8,192×4,320@120.8 (5)
24
+ */
25
+ const profiles = [
26
+ { level: '3.1', hex: '1F', width: 1280, height: 720, fps: 30.0 },
27
+ { level: '3.2', hex: '20', width: 1280, height: 1024, fps: 42.2 },
28
+ { level: '4.0', hex: '28', width: 2048, height: 1024, fps: 30.0 },
29
+ { level: '4.1', hex: '29', width: 2048, height: 1024, fps: 30.0 },
30
+ { level: '4.2', hex: '2A', width: 2048, height: 1080, fps: 60.0 },
31
+ { level: '5.0', hex: '32', width: 3672, height: 1536, fps: 26.7 },
32
+ { level: '5.1', hex: '33', width: 4096, height: 2304, fps: 26.7 },
33
+ { level: '5.2', hex: '34', width: 4096, height: 2304, fps: 56.3 },
34
+ { level: '6.0', hex: '3C', width: 8192, height: 4320, fps: 30.2 },
35
+ { level: '6.1', hex: '3D', width: 8192, height: 4320, fps: 60.4 },
36
+ { level: '6.2', hex: '3E', width: 8192, height: 4320, fps: 120.8 },
37
+ ];
38
+ const profile = profiles.find((p) => {
39
+ if (width > p.width) {
40
+ return false;
41
+ }
42
+ if (height > p.height) {
43
+ return false;
44
+ }
45
+ // if has no fps, use 60 as a conservative fallback
46
+ const fallbackFps = fps !== null && fps !== void 0 ? fps : 60;
47
+ return fallbackFps <= p.fps;
48
+ });
49
+ if (!profile) {
50
+ throw new Error(`No suitable AVC1 profile found for ${width}x${height}@${fps}fps`);
51
+ }
52
+ return `avc1.6400${profile.hex}`;
53
+ };
54
+ exports.chooseCorrectAvc1Profile = chooseCorrectAvc1Profile;
@@ -1,2 +1,10 @@
1
- export type ConvertMediaVideoCodec = 'vp8' | 'vp9';
2
- export type ConvertMediaAudioCodec = 'opus';
1
+ declare const availableContainers: readonly ["webm", "mp4"];
2
+ export type ConvertMediaContainer = (typeof availableContainers)[number];
3
+ export declare const getAvailableContainers: () => readonly ConvertMediaContainer[];
4
+ declare const availableVideoCodecs: readonly ["vp8", "vp9", "h264"];
5
+ export type ConvertMediaVideoCodec = (typeof availableVideoCodecs)[number];
6
+ export declare const getAvailableVideoCodecs: (container: ConvertMediaContainer) => ConvertMediaVideoCodec[];
7
+ declare const availableAudioCodecs: readonly ["opus", "aac"];
8
+ export declare const getAvailableAudioCodecs: (container: ConvertMediaContainer) => ConvertMediaAudioCodec[];
9
+ export type ConvertMediaAudioCodec = (typeof availableAudioCodecs)[number];
10
+ export {};
package/dist/codec-id.js CHANGED
@@ -1,2 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAvailableAudioCodecs = exports.getAvailableVideoCodecs = exports.getAvailableContainers = void 0;
4
+ const availableContainers = ['webm', 'mp4'];
5
+ const getAvailableContainers = () => {
6
+ return availableContainers;
7
+ };
8
+ exports.getAvailableContainers = getAvailableContainers;
9
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
10
+ const availableVideoCodecs = ['vp8', 'vp9', 'h264'];
11
+ const getAvailableVideoCodecs = (container) => {
12
+ if (container === 'mp4') {
13
+ return ['h264'];
14
+ }
15
+ if (container === 'webm') {
16
+ return ['vp8', 'vp9'];
17
+ }
18
+ throw new Error(`Unsupported container: ${container}`);
19
+ };
20
+ exports.getAvailableVideoCodecs = getAvailableVideoCodecs;
21
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
+ const availableAudioCodecs = ['opus', 'aac'];
23
+ const getAvailableAudioCodecs = (container) => {
24
+ if (container === 'mp4') {
25
+ return ['aac'];
26
+ }
27
+ if (container === 'webm') {
28
+ return ['opus'];
29
+ }
30
+ throw new Error(`Unsupported container: ${container}`);
31
+ };
32
+ exports.getAvailableAudioCodecs = getAvailableAudioCodecs;
@@ -0,0 +1,3 @@
1
+ import type { AudioOrVideoSample } from '@remotion/media-parser';
2
+ export declare const combineUint8Arrays: (arrays: Uint8Array[]) => Uint8Array;
3
+ export declare const convertEncodedChunk: (chunk: EncodedAudioChunk | EncodedVideoChunk, trackId: number) => AudioOrVideoSample;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertEncodedChunk = exports.combineUint8Arrays = void 0;
4
+ const combineUint8Arrays = (arrays) => {
5
+ if (arrays.length === 0) {
6
+ return new Uint8Array([]);
7
+ }
8
+ if (arrays.length === 1) {
9
+ return arrays[0];
10
+ }
11
+ let totalLength = 0;
12
+ for (const array of arrays) {
13
+ totalLength += array.length;
14
+ }
15
+ const result = new Uint8Array(totalLength);
16
+ let offset = 0;
17
+ for (const array of arrays) {
18
+ result.set(array, offset);
19
+ offset += array.length;
20
+ }
21
+ return result;
22
+ };
23
+ exports.combineUint8Arrays = combineUint8Arrays;
24
+ const convertEncodedChunk = (chunk, trackId) => {
25
+ var _a;
26
+ const arr = new Uint8Array(chunk.byteLength);
27
+ chunk.copyTo(arr);
28
+ return {
29
+ data: arr,
30
+ duration: (_a = chunk.duration) !== null && _a !== void 0 ? _a : undefined,
31
+ timestamp: chunk.timestamp,
32
+ type: chunk.type,
33
+ cts: chunk.timestamp,
34
+ dts: chunk.timestamp,
35
+ trackId,
36
+ };
37
+ };
38
+ exports.convertEncodedChunk = convertEncodedChunk;
@@ -1,7 +1,11 @@
1
+ /**
2
+ * Copyright (c) 2024 Remotion AG
3
+ * For licensing, see: https://remotion.dev/docs/webcodecs#license
4
+ */
1
5
  import type { LogLevel, Options, ParseMediaDynamicOptions, ParseMediaFields, ParseMediaOptions, VideoTrack, WriterInterface } from '@remotion/media-parser';
2
- import type { ConvertMediaAudioCodec, ConvertMediaVideoCodec } from './codec-id';
3
- import { type ResolveAudioActionFn } from './resolve-audio-action';
4
- import { type ResolveVideoActionFn } from './resolve-video-action';
6
+ import type { ConvertMediaAudioCodec, ConvertMediaContainer, ConvertMediaVideoCodec } from './codec-id';
7
+ import { type ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
8
+ import { type ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
5
9
  export type ConvertMediaState = {
6
10
  decodedVideoFrames: number;
7
11
  decodedAudioFrames: number;
@@ -9,24 +13,28 @@ export type ConvertMediaState = {
9
13
  encodedAudioFrames: number;
10
14
  bytesWritten: number;
11
15
  millisecondsWritten: number;
12
- expectedOutputMilliseconds: number | null;
16
+ expectedOutputDurationInMs: number | null;
13
17
  overallProgress: number | null;
14
18
  };
15
- export type ConvertMediaTo = 'webm';
16
19
  export type ConvertMediaResult = {
17
- save: () => Promise<File>;
20
+ save: () => Promise<Blob>;
18
21
  remove: () => Promise<void>;
19
22
  };
20
- export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame, onMediaStateUpdate: onMediaStateDoNoCallDirectly, audioCodec, to, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel, writer, ...more }: {
23
+ export type ConvertMediaOnMediaStateUpdate = (state: ConvertMediaState) => void;
24
+ export type ConvertMediaOnVideoFrame = (options: {
25
+ frame: VideoFrame;
26
+ track: VideoTrack;
27
+ }) => Promise<VideoFrame> | VideoFrame;
28
+ export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame, onMediaStateUpdate: onMediaStateDoNoCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel, writer, ...more }: {
21
29
  src: ParseMediaOptions<F>["src"];
22
- to: ConvertMediaTo;
23
- onVideoFrame?: (inputFrame: VideoFrame, track: VideoTrack) => Promise<void>;
24
- onMediaStateUpdate?: (state: ConvertMediaState) => void;
25
- videoCodec: ConvertMediaVideoCodec;
26
- audioCodec: ConvertMediaAudioCodec;
30
+ container: ConvertMediaContainer;
31
+ onVideoFrame?: ConvertMediaOnVideoFrame;
32
+ onMediaStateUpdate?: ConvertMediaOnMediaStateUpdate;
33
+ videoCodec?: ConvertMediaVideoCodec;
34
+ audioCodec?: ConvertMediaAudioCodec;
27
35
  signal?: AbortSignal;
28
- onAudioTrack?: ResolveAudioActionFn;
29
- onVideoTrack?: ResolveVideoActionFn;
36
+ onAudioTrack?: ConvertMediaOnAudioTrackHandler;
37
+ onVideoTrack?: ConvertMediaOnVideoTrackHandler;
30
38
  reader?: ParseMediaOptions<F>["reader"];
31
39
  logLevel?: LogLevel;
32
40
  writer?: WriterInterface;
@@ -1,4 +1,8 @@
1
1
  "use strict";
2
+ /**
3
+ * Copyright (c) 2024 Remotion AG
4
+ * For licensing, see: https://remotion.dev/docs/webcodecs#license
5
+ */
2
6
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
8
  };
@@ -10,20 +14,15 @@ const calculate_progress_1 = require("./calculate-progress");
10
14
  const error_cause_1 = __importDefault(require("./error-cause"));
11
15
  const on_audio_track_1 = require("./on-audio-track");
12
16
  const on_video_track_1 = require("./on-video-track");
13
- const resolve_audio_action_1 = require("./resolve-audio-action");
14
- const resolve_video_action_1 = require("./resolve-video-action");
15
17
  const with_resolvers_1 = require("./with-resolvers");
16
- const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: onMediaStateDoNoCallDirectly, audioCodec, to, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel = 'info', writer, ...more }) {
18
+ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: onMediaStateDoNoCallDirectly, audioCodec, container, videoCodec, signal: userPassedAbortSignal, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel = 'info', writer, ...more }) {
17
19
  if (userPassedAbortSignal === null || userPassedAbortSignal === void 0 ? void 0 : userPassedAbortSignal.aborted) {
18
20
  return Promise.reject(new error_cause_1.default('Aborted'));
19
21
  }
20
- if (to !== 'webm') {
21
- return Promise.reject(new TypeError('Only `to: "webm"` is supported currently'));
22
+ if (container !== 'webm' && container !== 'mp4') {
23
+ return Promise.reject(new TypeError('Only `to: "webm"` and `to: "mp4"` is supported currently'));
22
24
  }
23
- if (audioCodec !== 'opus') {
24
- return Promise.reject(new TypeError('Only `audioCodec: "opus"` is supported currently'));
25
- }
26
- if (videoCodec !== 'vp8' && videoCodec !== 'vp9') {
25
+ if (videoCodec && videoCodec !== 'vp8' && videoCodec !== 'vp9') {
27
26
  return Promise.reject(new TypeError('Only `videoCodec: "vp8"` and `videoCodec: "vp9"` are supported currently'));
28
27
  }
29
28
  const { resolve, reject, getPromiseToImmediatelyReturn } = (0, with_resolvers_1.withResolversAndWaitForReturn)();
@@ -45,7 +44,7 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
45
44
  encodedAudioFrames: 0,
46
45
  bytesWritten: 0,
47
46
  millisecondsWritten: 0,
48
- expectedOutputMilliseconds: null,
47
+ expectedOutputDurationInMs: null,
49
48
  overallProgress: 0,
50
49
  };
51
50
  const onMediaStateUpdate = (newState) => {
@@ -54,7 +53,10 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
54
53
  }
55
54
  onMediaStateDoNoCallDirectly === null || onMediaStateDoNoCallDirectly === void 0 ? void 0 : onMediaStateDoNoCallDirectly(newState);
56
55
  };
57
- const state = await media_parser_1.MediaParserInternals.createMedia({
56
+ const creator = container === 'webm'
57
+ ? media_parser_1.MediaParserInternals.createMatroskaMedia
58
+ : media_parser_1.MediaParserInternals.createIsoBaseMedia;
59
+ const state = await creator({
58
60
  writer: await (0, auto_select_writer_1.autoSelectWriter)(writer, logLevel),
59
61
  onBytesProgress: (bytesWritten) => {
60
62
  convertMediaState.bytesWritten = bytesWritten;
@@ -65,11 +67,12 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
65
67
  convertMediaState.millisecondsWritten = millisecondsWritten;
66
68
  convertMediaState.overallProgress = (0, calculate_progress_1.calculateProgress)({
67
69
  millisecondsWritten: convertMediaState.millisecondsWritten,
68
- expectedOutputMilliseconds: convertMediaState.expectedOutputMilliseconds,
70
+ expectedOutputDurationInMs: convertMediaState.expectedOutputDurationInMs,
69
71
  });
70
72
  onMediaStateUpdate === null || onMediaStateUpdate === void 0 ? void 0 : onMediaStateUpdate(convertMediaState);
71
73
  }
72
74
  },
75
+ logLevel,
73
76
  });
74
77
  const onVideoTrack = (0, on_video_track_1.makeVideoTrackHandler)({
75
78
  state,
@@ -78,22 +81,24 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
78
81
  abortConversion,
79
82
  convertMediaState,
80
83
  controller,
81
- videoCodec,
82
- onVideoTrack: userVideoResolver !== null && userVideoResolver !== void 0 ? userVideoResolver : resolve_video_action_1.defaultResolveVideoAction,
84
+ defaultVideoCodec: videoCodec !== null && videoCodec !== void 0 ? videoCodec : null,
85
+ onVideoTrack: userVideoResolver !== null && userVideoResolver !== void 0 ? userVideoResolver : null,
83
86
  logLevel,
87
+ container,
84
88
  });
85
89
  const onAudioTrack = (0, on_audio_track_1.makeAudioTrackHandler)({
86
90
  abortConversion,
87
- audioCodec,
91
+ defaultAudioCodec: audioCodec !== null && audioCodec !== void 0 ? audioCodec : null,
88
92
  controller,
89
93
  convertMediaState,
90
94
  onMediaStateUpdate: onMediaStateUpdate !== null && onMediaStateUpdate !== void 0 ? onMediaStateUpdate : null,
91
95
  state,
92
- onAudioTrack: userAudioResolver !== null && userAudioResolver !== void 0 ? userAudioResolver : resolve_audio_action_1.defaultResolveAudioAction,
93
- bitrate: 128000,
96
+ onAudioTrack: userAudioResolver !== null && userAudioResolver !== void 0 ? userAudioResolver : null,
94
97
  logLevel,
98
+ container,
95
99
  });
96
100
  (0, media_parser_1.parseMedia)({
101
+ logLevel,
97
102
  src,
98
103
  onVideoTrack,
99
104
  onAudioTrack,
@@ -112,11 +117,11 @@ const convertMedia = async function ({ src, onVideoFrame, onMediaStateUpdate: on
112
117
  if (casted.onDurationInSeconds) {
113
118
  casted.onDurationInSeconds(durationInSeconds);
114
119
  }
115
- const expectedOutputMilliseconds = durationInSeconds * 1000;
116
- convertMediaState.expectedOutputMilliseconds = expectedOutputMilliseconds;
120
+ const expectedOutputDurationInMs = durationInSeconds * 1000;
121
+ convertMediaState.expectedOutputDurationInMs = expectedOutputDurationInMs;
117
122
  convertMediaState.overallProgress = (0, calculate_progress_1.calculateProgress)({
118
123
  millisecondsWritten: convertMediaState.millisecondsWritten,
119
- expectedOutputMilliseconds,
124
+ expectedOutputDurationInMs,
120
125
  });
121
126
  onMediaStateUpdate(convertMediaState);
122
127
  },
@@ -0,0 +1,2 @@
1
+ import type { ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
2
+ export declare const defaultOnAudioTrackHandler: ConvertMediaOnAudioTrackHandler;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultOnAudioTrackHandler = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
5
+ const can_copy_audio_track_1 = require("./can-copy-audio-track");
6
+ const can_reencode_audio_track_1 = require("./can-reencode-audio-track");
7
+ const get_default_audio_codec_1 = require("./get-default-audio-codec");
8
+ const DEFAULT_BITRATE = 128000;
9
+ const defaultOnAudioTrackHandler = async ({ track, defaultAudioCodec, logLevel, container, }) => {
10
+ const bitrate = DEFAULT_BITRATE;
11
+ const canCopy = (0, can_copy_audio_track_1.canCopyAudioTrack)({
12
+ inputCodec: track.codecWithoutConfig,
13
+ container,
14
+ });
15
+ if (canCopy) {
16
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can copy track, therefore copying`);
17
+ return Promise.resolve({ type: 'copy' });
18
+ }
19
+ const audioCodec = defaultAudioCodec !== null && defaultAudioCodec !== void 0 ? defaultAudioCodec : (0, get_default_audio_codec_1.getDefaultAudioCodec)({ container });
20
+ const canReencode = await (0, can_reencode_audio_track_1.canReencodeAudioTrack)({
21
+ audioCodec,
22
+ track,
23
+ bitrate,
24
+ });
25
+ if (canReencode) {
26
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Cannot copy, but re-encode, therefore re-encoding`);
27
+ return Promise.resolve({
28
+ type: 'reencode',
29
+ bitrate,
30
+ audioCodec,
31
+ });
32
+ }
33
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (audio): Can neither re-encode nor copy, failing render`);
34
+ return Promise.resolve({ type: 'fail' });
35
+ };
36
+ exports.defaultOnAudioTrackHandler = defaultOnAudioTrackHandler;
@@ -0,0 +1,2 @@
1
+ import type { ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
2
+ export declare const defaultOnVideoTrackHandler: ConvertMediaOnVideoTrackHandler;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultOnVideoTrackHandler = void 0;
4
+ const media_parser_1 = require("@remotion/media-parser");
5
+ const can_copy_video_track_1 = require("./can-copy-video-track");
6
+ const can_reencode_video_track_1 = require("./can-reencode-video-track");
7
+ const get_default_video_codec_1 = require("./get-default-video-codec");
8
+ const defaultOnVideoTrackHandler = async ({ track, defaultVideoCodec, logLevel, container, }) => {
9
+ const canCopy = (0, can_copy_video_track_1.canCopyVideoTrack)({
10
+ inputCodec: track.codecWithoutConfig,
11
+ container,
12
+ });
13
+ if (canCopy) {
14
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Can copy, therefore copying`);
15
+ return Promise.resolve({ type: 'copy' });
16
+ }
17
+ const videoCodec = defaultVideoCodec !== null && defaultVideoCodec !== void 0 ? defaultVideoCodec : (0, get_default_video_codec_1.getDefaultVideoCodec)({ container });
18
+ const canReencode = await (0, can_reencode_video_track_1.canReencodeVideoTrack)({
19
+ videoCodec,
20
+ track,
21
+ });
22
+ if (canReencode) {
23
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Cannot copy, but re-enconde, therefore re-encoding`);
24
+ return Promise.resolve({ type: 'reencode', videoCodec });
25
+ }
26
+ media_parser_1.MediaParserInternals.Log.verbose(logLevel, `Track ${track.trackId} (video): Can neither copy nor re-encode, therefore failing`);
27
+ return Promise.resolve({ type: 'fail' });
28
+ };
29
+ exports.defaultOnVideoTrackHandler = defaultOnVideoTrackHandler;