Add .gitignore to exclude all node packages and lock files
This commit is contained in:
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const arrayBufferToUint8Array: (buffer: ArrayBuffer | null) => Uint8Array | null;
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.arrayBufferToUint8Array = void 0;
|
||||
const arrayBufferToUint8Array = (buffer) => {
|
||||
return buffer ? new Uint8Array(buffer) : null;
|
||||
};
|
||||
exports.arrayBufferToUint8Array = arrayBufferToUint8Array;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const getDataTypeForAudioFormat: (format: AudioSampleFormat) => Uint8ArrayConstructor | Float32ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor;
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getDataTypeForAudioFormat = void 0;
|
||||
const getDataTypeForAudioFormat = (format) => {
|
||||
switch (format) {
|
||||
case 'f32':
|
||||
return Float32Array;
|
||||
case 'f32-planar':
|
||||
return Float32Array;
|
||||
case 's16':
|
||||
return Int16Array;
|
||||
case 's16-planar':
|
||||
return Int16Array;
|
||||
case 'u8':
|
||||
return Uint8Array;
|
||||
case 'u8-planar':
|
||||
return Uint8Array;
|
||||
case 's32':
|
||||
return Int32Array;
|
||||
case 's32-planar':
|
||||
return Int32Array;
|
||||
default:
|
||||
throw new Error(`Unsupported audio format: ${format}`);
|
||||
}
|
||||
};
|
||||
exports.getDataTypeForAudioFormat = getDataTypeForAudioFormat;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const isPlanarFormat: (format: AudioSampleFormat) => boolean;
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isPlanarFormat = void 0;
|
||||
const isPlanarFormat = (format) => {
|
||||
return format.includes('-planar');
|
||||
};
|
||||
exports.isPlanarFormat = isPlanarFormat;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const getAudioDecoderConfig: (config: AudioDecoderConfig) => Promise<AudioDecoderConfig | null>;
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getAudioDecoderConfig = void 0;
|
||||
const getAudioDecoderConfig = async (config) => {
|
||||
if (config.codec === 'pcm-s16') {
|
||||
return config;
|
||||
}
|
||||
if (config.codec === 'pcm-s24') {
|
||||
return config;
|
||||
}
|
||||
if (typeof AudioDecoder === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if (typeof EncodedAudioChunk === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if ((await AudioDecoder.isConfigSupported(config)).supported) {
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.getAudioDecoderConfig = getAudioDecoderConfig;
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
|
||||
export declare const getAudioEncoderConfig: (config: AudioEncoderConfig & {
|
||||
codec: ConvertMediaAudioCodec;
|
||||
}) => Promise<AudioEncoderConfig | null>;
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getAudioEncoderConfig = void 0;
|
||||
const getCodecString = (audioCodec) => {
|
||||
if (audioCodec === 'opus') {
|
||||
return 'opus';
|
||||
}
|
||||
if (audioCodec === 'aac') {
|
||||
return 'mp4a.40.02';
|
||||
}
|
||||
if (audioCodec === 'wav') {
|
||||
return 'wav-should-not-to-into-audio-encoder';
|
||||
}
|
||||
throw new Error(`Unsupported audio codec: ${audioCodec}`);
|
||||
};
|
||||
const getAudioEncoderConfig = async (config) => {
|
||||
const actualConfig = {
|
||||
...config,
|
||||
codec: getCodecString(config.codec),
|
||||
};
|
||||
if (config.codec === 'wav') {
|
||||
return actualConfig;
|
||||
}
|
||||
if (typeof AudioEncoder === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
if ((await AudioEncoder.isConfigSupported(actualConfig)).supported) {
|
||||
return actualConfig;
|
||||
}
|
||||
const maybeItIsTheSampleRateThatIsTheProblem = config.sampleRate !== 48000 && config.sampleRate !== 44100;
|
||||
if (maybeItIsTheSampleRateThatIsTheProblem) {
|
||||
return (0, exports.getAudioEncoderConfig)({
|
||||
...config,
|
||||
sampleRate: config.sampleRate === 22050 ? 44100 : 48000,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
};
|
||||
exports.getAudioEncoderConfig = getAudioEncoderConfig;
|
||||
Generated
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
import { type MediaParserLogLevel } from '@remotion/media-parser';
|
||||
import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
|
||||
import type { IoSynchronizer } from './io-manager/io-synchronizer';
|
||||
import type { WebCodecsController } from './webcodecs-controller';
|
||||
export type WebCodecsAudioEncoder = {
|
||||
encode: (audioData: AudioData) => void;
|
||||
waitForFinish: () => Promise<void>;
|
||||
close: () => void;
|
||||
flush: () => Promise<void>;
|
||||
ioSynchronizer: IoSynchronizer;
|
||||
};
|
||||
export type AudioEncoderInit = {
|
||||
onChunk: (chunk: EncodedAudioChunk) => Promise<void>;
|
||||
onError: (error: Error) => void;
|
||||
codec: ConvertMediaAudioCodec;
|
||||
controller: WebCodecsController;
|
||||
config: AudioEncoderConfig;
|
||||
logLevel: MediaParserLogLevel;
|
||||
onNewAudioSampleRate: (sampleRate: number) => void;
|
||||
};
|
||||
export declare const createAudioEncoder: ({ onChunk, onError, codec, controller, config: audioEncoderConfig, logLevel, onNewAudioSampleRate, }: AudioEncoderInit) => WebCodecsAudioEncoder;
|
||||
Generated
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAudioEncoder = void 0;
|
||||
const media_parser_1 = require("@remotion/media-parser");
|
||||
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
||||
const wav_audio_encoder_1 = require("./wav-audio-encoder");
|
||||
const createAudioEncoder = ({ onChunk, onError, codec, controller, config: audioEncoderConfig, logLevel, onNewAudioSampleRate, }) => {
|
||||
if (controller._internals._mediaParserController._internals.signal.aborted) {
|
||||
throw new media_parser_1.MediaParserAbortError('Not creating audio encoder, already aborted');
|
||||
}
|
||||
const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
|
||||
logLevel,
|
||||
label: 'Audio encoder',
|
||||
controller,
|
||||
});
|
||||
if (codec === 'wav') {
|
||||
return (0, wav_audio_encoder_1.getWaveAudioEncoder)({
|
||||
onChunk,
|
||||
controller,
|
||||
config: audioEncoderConfig,
|
||||
ioSynchronizer,
|
||||
});
|
||||
}
|
||||
const encoder = new AudioEncoder({
|
||||
output: async (chunk) => {
|
||||
try {
|
||||
await onChunk(chunk);
|
||||
}
|
||||
catch (err) {
|
||||
onError(err);
|
||||
}
|
||||
ioSynchronizer.onOutput(chunk.timestamp);
|
||||
},
|
||||
error(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
const close = () => {
|
||||
controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
onAbort);
|
||||
if (encoder.state === 'closed') {
|
||||
return;
|
||||
}
|
||||
encoder.close();
|
||||
};
|
||||
const onAbort = () => {
|
||||
close();
|
||||
};
|
||||
controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
|
||||
if (codec !== 'opus' && codec !== 'aac') {
|
||||
throw new Error('Only `codec: "opus"` and `codec: "aac"` is supported currently');
|
||||
}
|
||||
const wantedSampleRate = audioEncoderConfig.sampleRate;
|
||||
const encodeFrame = (audioData) => {
|
||||
if (encoder.state === 'closed') {
|
||||
return;
|
||||
}
|
||||
if (encoder.state === 'unconfigured') {
|
||||
if (audioData.sampleRate === wantedSampleRate) {
|
||||
encoder.configure(audioEncoderConfig);
|
||||
}
|
||||
else {
|
||||
encoder.configure({
|
||||
...audioEncoderConfig,
|
||||
sampleRate: audioData.sampleRate,
|
||||
});
|
||||
onNewAudioSampleRate(audioData.sampleRate);
|
||||
}
|
||||
}
|
||||
encoder.encode(audioData);
|
||||
ioSynchronizer.inputItem(audioData.timestamp);
|
||||
};
|
||||
return {
|
||||
encode: (audioData) => {
|
||||
encodeFrame(audioData);
|
||||
},
|
||||
waitForFinish: async () => {
|
||||
await encoder.flush();
|
||||
await ioSynchronizer.waitForQueueSize(0);
|
||||
},
|
||||
close,
|
||||
flush: async () => {
|
||||
await encoder.flush();
|
||||
},
|
||||
ioSynchronizer,
|
||||
};
|
||||
};
|
||||
exports.createAudioEncoder = createAudioEncoder;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { MediaParserInternalTypes, MediaParserLogLevel } from '@remotion/media-parser';
|
||||
export declare const autoSelectWriter: (writer: MediaParserInternalTypes["WriterInterface"] | undefined, logLevel: MediaParserLogLevel) => Promise<MediaParserInternalTypes["WriterInterface"]>;
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.autoSelectWriter = void 0;
|
||||
const web_fs_1 = require("./writers/web-fs");
|
||||
const with_resolvers_1 = require("./create/with-resolvers");
|
||||
const log_1 = require("./log");
|
||||
const buffer_1 = require("./writers/buffer");
|
||||
const autoSelectWriter = async (writer, logLevel) => {
|
||||
if (writer) {
|
||||
log_1.Log.verbose(logLevel, 'Using writer provided by user');
|
||||
return writer;
|
||||
}
|
||||
log_1.Log.verbose(logLevel, 'Determining best writer');
|
||||
const hasNavigator = typeof navigator !== 'undefined';
|
||||
if (!hasNavigator) {
|
||||
log_1.Log.verbose(logLevel, 'No navigator API detected, using buffer writer');
|
||||
return buffer_1.bufferWriter;
|
||||
}
|
||||
// Check if we're offline using the navigator API
|
||||
const isOffline = !navigator.onLine;
|
||||
if (isOffline) {
|
||||
log_1.Log.verbose(logLevel, 'Offline mode detected, using buffer writer');
|
||||
return buffer_1.bufferWriter;
|
||||
}
|
||||
try {
|
||||
const { promise: timeout, reject, resolve } = (0, with_resolvers_1.withResolvers)();
|
||||
const time = setTimeout(() => reject(new Error('WebFS check timeout')), 2000);
|
||||
const webFsSupported = await Promise.race([(0, web_fs_1.canUseWebFsWriter)(), timeout]);
|
||||
resolve();
|
||||
clearTimeout(time);
|
||||
if (webFsSupported) {
|
||||
log_1.Log.verbose(logLevel, 'Using WebFS writer because it is supported');
|
||||
return web_fs_1.webFsWriter;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
log_1.Log.verbose(logLevel, `WebFS check failed: ${err}. Falling back to buffer writer`);
|
||||
}
|
||||
log_1.Log.verbose(logLevel, 'Using buffer writer because WebFS writer is not supported or unavailable');
|
||||
return buffer_1.bufferWriter;
|
||||
};
|
||||
exports.autoSelectWriter = autoSelectWriter;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
export declare const isFirefox: () => boolean;
|
||||
export declare const isSafari: () => boolean;
|
||||
export declare const isChrome: () => boolean;
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isChrome = exports.isSafari = exports.isFirefox = void 0;
|
||||
const isFirefox = () => {
|
||||
return navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
||||
};
|
||||
exports.isFirefox = isFirefox;
|
||||
const isSafari = () => {
|
||||
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
};
|
||||
exports.isSafari = isSafari;
|
||||
const isChrome = () => {
|
||||
return navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
|
||||
};
|
||||
exports.isChrome = isChrome;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './writers/buffer';
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./writers/buffer"), exports);
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export declare const calculateProgress: ({ millisecondsWritten, expectedOutputDurationInMs, }: {
|
||||
millisecondsWritten: number;
|
||||
expectedOutputDurationInMs: number | null;
|
||||
}) => number | null;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.calculateProgress = void 0;
|
||||
const calculateProgress = ({ millisecondsWritten, expectedOutputDurationInMs, }) => {
|
||||
if (expectedOutputDurationInMs === null) {
|
||||
return null;
|
||||
}
|
||||
return millisecondsWritten / expectedOutputDurationInMs;
|
||||
};
|
||||
exports.calculateProgress = calculateProgress;
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import type { MediaParserAudioCodec, MediaParserContainer } from '@remotion/media-parser';
|
||||
import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
|
||||
import type { ConvertMediaContainer } from './get-available-containers';
|
||||
export declare const canCopyAudioTrack: ({ inputCodec, outputContainer, inputContainer, outputAudioCodec, }: {
|
||||
inputCodec: MediaParserAudioCodec;
|
||||
outputContainer: ConvertMediaContainer;
|
||||
inputContainer: MediaParserContainer;
|
||||
outputAudioCodec: ConvertMediaAudioCodec | null;
|
||||
}) => boolean;
|
||||
Generated
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.canCopyAudioTrack = void 0;
|
||||
const is_different_video_codec_1 = require("./is-different-video-codec");
|
||||
const canCopyAudioTrack = ({ inputCodec, outputContainer, inputContainer, outputAudioCodec, }) => {
|
||||
if (outputAudioCodec) {
|
||||
if (!(0, is_different_video_codec_1.isSameAudioCodec)({
|
||||
inputAudioCodec: inputCodec,
|
||||
outputCodec: outputAudioCodec,
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (outputContainer === 'webm') {
|
||||
return inputCodec === 'opus';
|
||||
}
|
||||
if (outputContainer === 'mp4') {
|
||||
return (inputCodec === 'aac' &&
|
||||
(inputContainer === 'mp4' ||
|
||||
inputContainer === 'avi' ||
|
||||
inputContainer === 'm3u8'));
|
||||
}
|
||||
if (outputContainer === 'wav') {
|
||||
return false;
|
||||
}
|
||||
throw new Error(`Unhandled container: ${outputContainer}`);
|
||||
};
|
||||
exports.canCopyAudioTrack = canCopyAudioTrack;
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
import type { MediaParserContainer, MediaParserVideoTrack } from '@remotion/media-parser';
|
||||
import type { ConvertMediaContainer } from './get-available-containers';
|
||||
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
||||
import type { ResizeOperation } from './resizing/mode';
|
||||
export declare const canCopyVideoTrack: ({ outputContainer, rotationToApply, inputContainer, resizeOperation, inputTrack, outputVideoCodec, }: {
|
||||
inputContainer: MediaParserContainer;
|
||||
inputTrack: MediaParserVideoTrack;
|
||||
rotationToApply: number;
|
||||
outputContainer: ConvertMediaContainer;
|
||||
outputVideoCodec: ConvertMediaVideoCodec | null;
|
||||
resizeOperation: ResizeOperation | null;
|
||||
}) => boolean;
|
||||
Generated
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.canCopyVideoTrack = void 0;
|
||||
const is_different_video_codec_1 = require("./is-different-video-codec");
|
||||
const rotate_and_resize_video_frame_1 = require("./rotate-and-resize-video-frame");
|
||||
const rotation_1 = require("./rotation");
|
||||
const canCopyVideoTrack = ({ outputContainer, rotationToApply, inputContainer, resizeOperation, inputTrack, outputVideoCodec, }) => {
|
||||
if ((0, rotate_and_resize_video_frame_1.normalizeVideoRotation)(inputTrack.rotation) !==
|
||||
(0, rotate_and_resize_video_frame_1.normalizeVideoRotation)(rotationToApply)) {
|
||||
return false;
|
||||
}
|
||||
if (outputVideoCodec) {
|
||||
if (!(0, is_different_video_codec_1.isSameVideoCodec)({
|
||||
inputVideoCodec: inputTrack.codecEnum,
|
||||
outputCodec: outputVideoCodec,
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const needsToBeMultipleOfTwo = inputTrack.codecEnum === 'h264';
|
||||
const newDimensions = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
|
||||
height: inputTrack.height,
|
||||
resizeOperation,
|
||||
rotation: rotationToApply,
|
||||
width: inputTrack.width,
|
||||
needsToBeMultipleOfTwo,
|
||||
});
|
||||
if (newDimensions.height !== inputTrack.height ||
|
||||
newDimensions.width !== inputTrack.width) {
|
||||
return false;
|
||||
}
|
||||
if (outputContainer === 'webm') {
|
||||
return inputTrack.codecEnum === 'vp8' || inputTrack.codecEnum === 'vp9';
|
||||
}
|
||||
if (outputContainer === 'mp4') {
|
||||
return ((inputTrack.codecEnum === 'h264' || inputTrack.codecEnum === 'h265') &&
|
||||
(inputContainer === 'mp4' ||
|
||||
inputContainer === 'avi' ||
|
||||
(inputContainer === 'm3u8' && inputTrack.m3uStreamFormat === 'mp4')));
|
||||
}
|
||||
if (outputContainer === 'wav') {
|
||||
return false;
|
||||
}
|
||||
throw new Error(`Unhandled codec: ${outputContainer}`);
|
||||
};
|
||||
exports.canCopyVideoTrack = canCopyVideoTrack;
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import type { MediaParserAudioTrack } from '@remotion/media-parser';
|
||||
import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
|
||||
export declare const canReencodeAudioTrack: ({ track, audioCodec, bitrate, sampleRate, }: {
|
||||
track: MediaParserAudioTrack;
|
||||
audioCodec: ConvertMediaAudioCodec;
|
||||
bitrate: number;
|
||||
sampleRate: number | null;
|
||||
}) => Promise<boolean>;
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.canReencodeAudioTrack = void 0;
|
||||
const audio_decoder_config_1 = require("./audio-decoder-config");
|
||||
const audio_encoder_config_1 = require("./audio-encoder-config");
|
||||
const canReencodeAudioTrack = async ({ track, audioCodec, bitrate, sampleRate, }) => {
|
||||
const audioDecoderConfig = await (0, audio_decoder_config_1.getAudioDecoderConfig)(track);
|
||||
if (audioCodec === 'wav' && audioDecoderConfig) {
|
||||
return true;
|
||||
}
|
||||
const audioEncoderConfig = await (0, audio_encoder_config_1.getAudioEncoderConfig)({
|
||||
codec: audioCodec,
|
||||
numberOfChannels: track.numberOfChannels,
|
||||
sampleRate: sampleRate ?? track.sampleRate,
|
||||
bitrate,
|
||||
});
|
||||
return Boolean(audioDecoderConfig && audioEncoderConfig);
|
||||
};
|
||||
exports.canReencodeAudioTrack = canReencodeAudioTrack;
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import type { MediaParserVideoTrack } from '@remotion/media-parser';
|
||||
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
||||
import type { ResizeOperation } from './resizing/mode';
|
||||
export declare const canReencodeVideoTrack: ({ videoCodec, track, resizeOperation, rotate, }: {
|
||||
videoCodec: ConvertMediaVideoCodec;
|
||||
track: MediaParserVideoTrack;
|
||||
resizeOperation: ResizeOperation | null;
|
||||
rotate: number | null;
|
||||
}) => Promise<boolean>;
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.canReencodeVideoTrack = void 0;
|
||||
const rotation_1 = require("./rotation");
|
||||
const video_decoder_config_1 = require("./video-decoder-config");
|
||||
const video_encoder_config_1 = require("./video-encoder-config");
|
||||
const canReencodeVideoTrack = async ({ videoCodec, track, resizeOperation, rotate, }) => {
|
||||
const { height, width } = (0, rotation_1.calculateNewDimensionsFromRotateAndScale)({
|
||||
height: track.displayAspectHeight,
|
||||
resizeOperation,
|
||||
rotation: rotate ?? 0,
|
||||
needsToBeMultipleOfTwo: videoCodec === 'h264',
|
||||
width: track.displayAspectWidth,
|
||||
});
|
||||
const videoEncoderConfig = await (0, video_encoder_config_1.getVideoEncoderConfig)({
|
||||
codec: videoCodec,
|
||||
height,
|
||||
width,
|
||||
fps: track.fps,
|
||||
});
|
||||
const videoDecoderConfig = await (0, video_decoder_config_1.getVideoDecoderConfigWithHardwareAcceleration)(track);
|
||||
return Boolean(videoDecoderConfig && videoEncoderConfig);
|
||||
};
|
||||
exports.canReencodeVideoTrack = canReencodeVideoTrack;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export declare const chooseCorrectAvc1Profile: ({ width, height, fps, }: {
|
||||
width: number;
|
||||
height: number;
|
||||
fps: number | null;
|
||||
}) => string;
|
||||
Generated
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.chooseCorrectAvc1Profile = void 0;
|
||||
const chooseCorrectAvc1Profile = ({ width, height, fps, }) => {
|
||||
// 0x42 = Baseline profile
|
||||
// 0x4D = Main profile
|
||||
// 0x58 = Extended profile
|
||||
// 0x64 = High profile
|
||||
// According to Wikipedia, 0x64 is the most widely supported profile.
|
||||
// So we always choose 0x64.
|
||||
/**
|
||||
Ignoring lower levels of <720p, let's only support Players that can play 720p and above.
|
||||
• Level 3.1 = 1F (hex) -> 1,280×720@30.0 (5)
|
||||
• Level 3.2 = 20 (hex) -> 1,280×1,024@42.2 (4)
|
||||
• Level 4.0 = 28 (hex) -> 2,048×1,024@30.0 (4)
|
||||
• Level 4.1 = 29 (hex) -> 2,048×1,024@30.0 (4)
|
||||
• Level 4.2 = 2A (hex) -> 2,048×1,080@60.0 (4)
|
||||
• Level 5.0 = 32 (hex) -> 3,672×1,536@26.7 (5)
|
||||
• Level 5.1 = 33 (hex) -> 4,096×2,304@26.7 (5)
|
||||
• Level 5.2 = 34 (hex) -> 4,096×2,304@56.3 (5)
|
||||
• Level 6.0 = 3C (hex) -> 8,192×4,320@30.2 (5)
|
||||
• Level 6.1 = 3D (hex) -> 8,192×4,320@60.4 (5)
|
||||
• Level 6.2 = 3E (hex) -> 8,192×4,320@120.8 (5)
|
||||
*/
|
||||
const profiles = [
|
||||
{ level: '3.1', hex: '1F', width: 1280, height: 720, fps: 30.0 },
|
||||
{ level: '3.2', hex: '20', width: 1280, height: 1024, fps: 42.2 },
|
||||
{ level: '4.0', hex: '28', width: 2048, height: 1024, fps: 30.0 },
|
||||
{ level: '4.1', hex: '29', width: 2048, height: 1024, fps: 30.0 },
|
||||
{ level: '4.2', hex: '2A', width: 2048, height: 1080, fps: 60.0 },
|
||||
{ level: '5.0', hex: '32', width: 3672, height: 1536, fps: 26.7 },
|
||||
{ level: '5.1', hex: '33', width: 4096, height: 2304, fps: 26.7 },
|
||||
{ level: '5.2', hex: '34', width: 4096, height: 2304, fps: 56.3 },
|
||||
{ level: '6.0', hex: '3C', width: 8192, height: 4320, fps: 30.2 },
|
||||
{ level: '6.1', hex: '3D', width: 8192, height: 4320, fps: 60.4 },
|
||||
{ level: '6.2', hex: '3E', width: 8192, height: 4320, fps: 120.8 },
|
||||
];
|
||||
const profile = profiles.find((p) => {
|
||||
if (width > p.width) {
|
||||
return false;
|
||||
}
|
||||
if (height > p.height) {
|
||||
return false;
|
||||
}
|
||||
// if has no fps, use 60 as a conservative fallback
|
||||
const fallbackFps = fps ?? 60;
|
||||
return fallbackFps <= p.fps;
|
||||
});
|
||||
if (!profile) {
|
||||
throw new Error(`No suitable AVC1 profile found for ${width}x${height}@${fps}fps`);
|
||||
}
|
||||
return `avc1.6400${profile.hex}`;
|
||||
};
|
||||
exports.chooseCorrectAvc1Profile = chooseCorrectAvc1Profile;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export declare const chooseCorrectHevcProfile: ({ width, height, fps, }: {
|
||||
width: number;
|
||||
height: number;
|
||||
fps: number | null;
|
||||
}) => string;
|
||||
Generated
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.chooseCorrectHevcProfile = void 0;
|
||||
const hevc_levels_1 = require("./hevc-levels");
|
||||
const chooseCorrectHevcProfile = ({ width, height, fps, }) => {
|
||||
const profile = hevc_levels_1.hevcLevels.find((p) => {
|
||||
return p.maxResolutionsAndFrameRates.some((max) => {
|
||||
if (width > max.width) {
|
||||
return false;
|
||||
}
|
||||
if (height > max.height) {
|
||||
return false;
|
||||
}
|
||||
// if has no fps, use 60 as a conservative fallback
|
||||
const fallbackFps = fps ?? 60;
|
||||
return fallbackFps <= max.fps;
|
||||
});
|
||||
});
|
||||
if (!profile) {
|
||||
throw new Error(`No suitable HEVC profile found for ${width}x${height}@${fps}fps`);
|
||||
}
|
||||
// HEVC codec string format: hev1.2.${level_hex} or hvc1.2.${level_hex}
|
||||
// We'll use hvc1 as it's more widely supported
|
||||
return `hvc1.${
|
||||
// Profile
|
||||
// 1 = Main
|
||||
// 2 = Main 10
|
||||
// Chrome seems to support only Main
|
||||
1}.${
|
||||
// Profile space
|
||||
// Unclear which value to set, but 0 works
|
||||
0}.${
|
||||
// L = Main tier
|
||||
// H = High tier
|
||||
// TODO: Select high tier if resolution is big
|
||||
'L'}${
|
||||
// Level
|
||||
Math.round(Number(profile.level) * 30)}.${
|
||||
// Bit depth
|
||||
'b0'}`;
|
||||
};
|
||||
exports.chooseCorrectHevcProfile = chooseCorrectHevcProfile;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
export type ConvertAudioDataOptions = {
|
||||
audioData: AudioData;
|
||||
newSampleRate?: number;
|
||||
format?: AudioSampleFormat | null;
|
||||
};
|
||||
/**
|
||||
* Converts an `AudioData` object to a new `AudioData` object with a different sample rate or format.
|
||||
* @see [Documentation](https://remotion.dev/docs/webcodecs/convert-audiodata)
|
||||
*/
|
||||
export declare const convertAudioData: ({ audioData, newSampleRate, format, }: ConvertAudioDataOptions) => AudioData;
|
||||
Generated
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.convertAudioData = void 0;
|
||||
const data_types_1 = require("./audio-data/data-types");
|
||||
const is_planar_format_1 = require("./audio-data/is-planar-format");
|
||||
const validateRange = (format, value) => {
|
||||
if (format === 'f32' || format === 'f32-planar') {
|
||||
if (value < -1 || value > 1) {
|
||||
throw new Error('All values in a Float32 array must be between -1 and 1');
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Converts an `AudioData` object to a new `AudioData` object with a different sample rate or format.
|
||||
* @see [Documentation](https://remotion.dev/docs/webcodecs/convert-audiodata)
|
||||
*/
|
||||
const convertAudioData = ({ audioData, newSampleRate = audioData.sampleRate, format = audioData.format, }) => {
|
||||
const { numberOfChannels, sampleRate: currentSampleRate, numberOfFrames: currentNumberOfFrames, } = audioData;
|
||||
const ratio = currentSampleRate / newSampleRate;
|
||||
const newNumberOfFrames = Math.floor(currentNumberOfFrames / ratio);
|
||||
if (newNumberOfFrames === 0) {
|
||||
throw new Error('Cannot resample - the given sample rate would result in less than 1 sample');
|
||||
}
|
||||
if (newSampleRate < 3000 || newSampleRate > 768000) {
|
||||
throw new Error('newSampleRate must be between 3000 and 768000');
|
||||
}
|
||||
if (!format) {
|
||||
throw new Error('AudioData format is not set');
|
||||
}
|
||||
if (format === audioData.format &&
|
||||
newNumberOfFrames === currentNumberOfFrames) {
|
||||
return audioData.clone();
|
||||
}
|
||||
const DataType = (0, data_types_1.getDataTypeForAudioFormat)(format);
|
||||
const isPlanar = (0, is_planar_format_1.isPlanarFormat)(format);
|
||||
const planes = isPlanar ? numberOfChannels : 1;
|
||||
const srcChannels = new Array(planes)
|
||||
.fill(true)
|
||||
.map(() => new DataType((isPlanar ? 1 : numberOfChannels) * currentNumberOfFrames));
|
||||
for (let i = 0; i < planes; i++) {
|
||||
audioData.clone().copyTo(srcChannels[i], {
|
||||
planeIndex: i,
|
||||
format,
|
||||
});
|
||||
}
|
||||
const data = new DataType(newNumberOfFrames * numberOfChannels);
|
||||
const chunkSize = currentNumberOfFrames / newNumberOfFrames;
|
||||
for (let newFrameIndex = 0; newFrameIndex < newNumberOfFrames; newFrameIndex++) {
|
||||
const start = Math.floor(newFrameIndex * chunkSize);
|
||||
const end = Math.max(Math.floor(start + chunkSize), start + 1);
|
||||
if (isPlanar) {
|
||||
for (let channelIndex = 0; channelIndex < numberOfChannels; channelIndex++) {
|
||||
const chunk = srcChannels[channelIndex].slice(start, end);
|
||||
const average = chunk.reduce((a, b) => {
|
||||
return a + b;
|
||||
}, 0) / chunk.length;
|
||||
validateRange(format, average);
|
||||
data[newFrameIndex + channelIndex * newNumberOfFrames] = average;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const sampleCountAvg = end - start;
|
||||
for (let channelIndex = 0; channelIndex < numberOfChannels; channelIndex++) {
|
||||
const items = [];
|
||||
for (let k = 0; k < sampleCountAvg; k++) {
|
||||
const num = srcChannels[0][(start + k) * numberOfChannels + channelIndex];
|
||||
items.push(num);
|
||||
}
|
||||
const average = items.reduce((a, b) => a + b, 0) / items.length;
|
||||
validateRange(format, average);
|
||||
data[newFrameIndex * numberOfChannels + channelIndex] = average;
|
||||
}
|
||||
}
|
||||
}
|
||||
const newAudioData = new AudioData({
|
||||
data,
|
||||
format,
|
||||
numberOfChannels,
|
||||
numberOfFrames: newNumberOfFrames,
|
||||
sampleRate: newSampleRate,
|
||||
timestamp: audioData.timestamp,
|
||||
});
|
||||
return newAudioData;
|
||||
};
|
||||
exports.convertAudioData = convertAudioData;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { MediaParserAudioSample, MediaParserVideoSample } from '@remotion/media-parser';
|
||||
export declare const convertEncodedChunk: <T extends MediaParserAudioSample | MediaParserVideoSample>(chunk: EncodedAudioChunk | EncodedVideoChunk) => T;
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.convertEncodedChunk = void 0;
|
||||
const convertEncodedChunk = (chunk) => {
|
||||
const arr = new Uint8Array(chunk.byteLength);
|
||||
chunk.copyTo(arr);
|
||||
return {
|
||||
data: arr,
|
||||
duration: chunk.duration ?? undefined,
|
||||
timestamp: chunk.timestamp,
|
||||
type: chunk.type,
|
||||
decodingTimestamp: chunk.timestamp,
|
||||
offset: 0,
|
||||
};
|
||||
};
|
||||
exports.convertEncodedChunk = convertEncodedChunk;
|
||||
Generated
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Remotion AG
|
||||
* For licensing, see: https://remotion.dev/docs/webcodecs#license
|
||||
*/
|
||||
import type { MediaParserAudioTrack, MediaParserInternalTypes, MediaParserLogLevel, MediaParserVideoTrack, Options, ParseMediaFields, ParseMediaOptions } from '@remotion/media-parser';
|
||||
import type { ConvertMediaAudioCodec } from './get-available-audio-codecs';
|
||||
import { type ConvertMediaContainer } from './get-available-containers';
|
||||
import { type ConvertMediaVideoCodec } from './get-available-video-codecs';
|
||||
import { type ConvertMediaOnAudioTrackHandler } from './on-audio-track-handler';
|
||||
import { type ConvertMediaOnVideoTrackHandler } from './on-video-track-handler';
|
||||
import type { ResizeOperation } from './resizing/mode';
|
||||
import { type WebCodecsController } from './webcodecs-controller';
|
||||
export type ConvertMediaProgress = {
|
||||
decodedVideoFrames: number;
|
||||
decodedAudioFrames: number;
|
||||
encodedVideoFrames: number;
|
||||
encodedAudioFrames: number;
|
||||
bytesWritten: number;
|
||||
millisecondsWritten: number;
|
||||
expectedOutputDurationInMs: number | null;
|
||||
overallProgress: number | null;
|
||||
};
|
||||
export type ConvertMediaResult = {
|
||||
save: () => Promise<Blob>;
|
||||
remove: () => Promise<void>;
|
||||
finalState: ConvertMediaProgress;
|
||||
};
|
||||
export type ConvertMediaOnProgress = (state: ConvertMediaProgress) => void;
|
||||
export type ConvertMediaOnVideoFrame = (options: {
|
||||
frame: VideoFrame;
|
||||
track: MediaParserVideoTrack;
|
||||
}) => Promise<VideoFrame> | VideoFrame;
|
||||
export type ConvertMediaOnAudioData = (options: {
|
||||
audioData: AudioData;
|
||||
track: MediaParserAudioTrack;
|
||||
}) => Promise<AudioData> | AudioData;
|
||||
export declare const convertMedia: <F extends Options<ParseMediaFields>>({ src, onVideoFrame, onAudioData, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, controller, onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel, writer, progressIntervalInMs, rotate, resize, onAudioCodec, onContainer, onDimensions, onDurationInSeconds, onFps, onImages, onInternalStats, onIsHdr, onKeyframes, onLocation, onMetadata, onMimeType, onName, onNumberOfAudioChannels, onRotation, onSampleRate, onSize, onSlowAudioBitrate, onSlowDurationInSeconds, onSlowFps, onSlowKeyframes, onSlowNumberOfFrames, onSlowVideoBitrate, onSlowStructure, onTracks, onUnrotatedDimensions, onVideoCodec, onM3uStreams, selectM3uStream, selectM3uAssociatedPlaylists, expectedDurationInSeconds, expectedFrameRate, seekingHints, ...more }: {
|
||||
src: ParseMediaOptions<F>["src"];
|
||||
container: ConvertMediaContainer;
|
||||
onVideoFrame?: ConvertMediaOnVideoFrame;
|
||||
onAudioData?: ConvertMediaOnAudioData;
|
||||
onProgress?: ConvertMediaOnProgress;
|
||||
videoCodec?: ConvertMediaVideoCodec;
|
||||
audioCodec?: ConvertMediaAudioCodec;
|
||||
controller?: WebCodecsController;
|
||||
onAudioTrack?: ConvertMediaOnAudioTrackHandler;
|
||||
onVideoTrack?: ConvertMediaOnVideoTrackHandler;
|
||||
selectM3uStream?: ParseMediaOptions<F>["selectM3uStream"];
|
||||
selectM3uAssociatedPlaylists?: ParseMediaOptions<F>["selectM3uAssociatedPlaylists"];
|
||||
expectedDurationInSeconds?: number | null;
|
||||
expectedFrameRate?: number | null;
|
||||
reader?: ParseMediaOptions<F>["reader"];
|
||||
logLevel?: MediaParserLogLevel;
|
||||
writer?: MediaParserInternalTypes["WriterInterface"];
|
||||
progressIntervalInMs?: number;
|
||||
rotate?: number;
|
||||
resize?: ResizeOperation;
|
||||
fields?: F;
|
||||
seekingHints?: ParseMediaOptions<F>["seekingHints"];
|
||||
} & MediaParserInternalTypes["ParseMediaCallbacks"]) => Promise<ConvertMediaResult>;
|
||||
Generated
Vendored
+200
@@ -0,0 +1,200 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Copyright (c) 2025 Remotion AG
|
||||
* For licensing, see: https://remotion.dev/docs/webcodecs#license
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.convertMedia = void 0;
|
||||
const media_parser_1 = require("@remotion/media-parser");
|
||||
const web_1 = require("@remotion/media-parser/web");
|
||||
const auto_select_writer_1 = require("./auto-select-writer");
|
||||
const calculate_progress_1 = require("./calculate-progress");
|
||||
const create_media_1 = require("./create-media");
|
||||
const progress_tracker_1 = require("./create/progress-tracker");
|
||||
const with_resolvers_1 = require("./create/with-resolvers");
|
||||
const generate_output_filename_1 = require("./generate-output-filename");
|
||||
const get_available_containers_1 = require("./get-available-containers");
|
||||
const get_available_video_codecs_1 = require("./get-available-video-codecs");
|
||||
const on_audio_track_1 = require("./on-audio-track");
|
||||
const on_video_track_1 = require("./on-video-track");
|
||||
const throttled_state_update_1 = require("./throttled-state-update");
|
||||
const webcodecs_controller_1 = require("./webcodecs-controller");
|
||||
const convertMedia = async function ({ src, onVideoFrame, onAudioData, onProgress: onProgressDoNotCallDirectly, audioCodec, container, videoCodec, controller = (0, webcodecs_controller_1.webcodecsController)(), onAudioTrack: userAudioResolver, onVideoTrack: userVideoResolver, reader, fields, logLevel = 'info', writer, progressIntervalInMs, rotate, resize, onAudioCodec, onContainer, onDimensions, onDurationInSeconds, onFps, onImages, onInternalStats, onIsHdr, onKeyframes, onLocation, onMetadata, onMimeType, onName, onNumberOfAudioChannels, onRotation, onSampleRate, onSize, onSlowAudioBitrate, onSlowDurationInSeconds, onSlowFps, onSlowKeyframes, onSlowNumberOfFrames, onSlowVideoBitrate, onSlowStructure, onTracks, onUnrotatedDimensions, onVideoCodec, onM3uStreams, selectM3uStream, selectM3uAssociatedPlaylists, expectedDurationInSeconds, expectedFrameRate, seekingHints, ...more }) {
|
||||
if (controller._internals._mediaParserController._internals.signal.aborted) {
|
||||
return Promise.reject(new media_parser_1.MediaParserAbortError('Aborted'));
|
||||
}
|
||||
if (get_available_containers_1.availableContainers.indexOf(container) === -1) {
|
||||
return Promise.reject(new TypeError(`Only the following values for "container" are supported currently: ${JSON.stringify(get_available_containers_1.availableContainers)}`));
|
||||
}
|
||||
if (videoCodec && get_available_video_codecs_1.availableVideoCodecs.indexOf(videoCodec) === -1) {
|
||||
return Promise.reject(new TypeError(`Only the following values for "videoCodec" are supported currently: ${JSON.stringify(get_available_video_codecs_1.availableVideoCodecs)}`));
|
||||
}
|
||||
const { resolve, reject, getPromiseToImmediatelyReturn } = (0, with_resolvers_1.withResolversAndWaitForReturn)();
|
||||
const abortConversion = (errCause) => {
|
||||
reject(errCause);
|
||||
if (!controller._internals._mediaParserController._internals.signal.aborted) {
|
||||
controller.abort();
|
||||
}
|
||||
};
|
||||
const onUserAbort = () => {
|
||||
abortConversion(new media_parser_1.MediaParserAbortError('Conversion aborted by user'));
|
||||
};
|
||||
controller._internals._mediaParserController._internals.signal.addEventListener('abort', onUserAbort);
|
||||
const throttledState = (0, throttled_state_update_1.throttledStateUpdate)({
|
||||
updateFn: onProgressDoNotCallDirectly ?? null,
|
||||
everyMilliseconds: progressIntervalInMs ?? 100,
|
||||
signal: controller._internals._mediaParserController._internals.signal,
|
||||
});
|
||||
const progressTracker = (0, progress_tracker_1.makeProgressTracker)();
|
||||
const state = await (0, create_media_1.createMedia)({
|
||||
container,
|
||||
filename: (0, generate_output_filename_1.generateOutputFilename)(src, container),
|
||||
writer: await (0, auto_select_writer_1.autoSelectWriter)(writer, logLevel),
|
||||
onBytesProgress: (bytesWritten) => {
|
||||
throttledState.update?.((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
bytesWritten,
|
||||
};
|
||||
});
|
||||
},
|
||||
onMillisecondsProgress: (millisecondsWritten) => {
|
||||
throttledState.update?.((prevState) => {
|
||||
if (millisecondsWritten > prevState.millisecondsWritten) {
|
||||
return {
|
||||
...prevState,
|
||||
millisecondsWritten,
|
||||
overallProgress: (0, calculate_progress_1.calculateProgress)({
|
||||
millisecondsWritten: prevState.millisecondsWritten,
|
||||
expectedOutputDurationInMs: prevState.expectedOutputDurationInMs,
|
||||
}),
|
||||
};
|
||||
}
|
||||
return prevState;
|
||||
});
|
||||
},
|
||||
logLevel,
|
||||
progressTracker,
|
||||
expectedDurationInSeconds: expectedDurationInSeconds ?? null,
|
||||
expectedFrameRate: expectedFrameRate ?? null,
|
||||
});
|
||||
const onVideoTrack = (0, on_video_track_1.makeVideoTrackHandler)({
|
||||
progressTracker,
|
||||
state,
|
||||
onVideoFrame: onVideoFrame ?? null,
|
||||
onMediaStateUpdate: throttledState.update ?? null,
|
||||
abortConversion,
|
||||
controller,
|
||||
defaultVideoCodec: videoCodec ?? null,
|
||||
onVideoTrack: userVideoResolver ?? null,
|
||||
logLevel,
|
||||
outputContainer: container,
|
||||
rotate: rotate ?? 0,
|
||||
resizeOperation: resize ?? null,
|
||||
});
|
||||
const onAudioTrack = (0, on_audio_track_1.makeAudioTrackHandler)({
|
||||
progressTracker,
|
||||
abortConversion,
|
||||
defaultAudioCodec: audioCodec ?? null,
|
||||
controller,
|
||||
onMediaStateUpdate: throttledState.update ?? null,
|
||||
state,
|
||||
onAudioTrack: userAudioResolver ?? null,
|
||||
logLevel,
|
||||
outputContainer: container,
|
||||
onAudioData: onAudioData ?? null,
|
||||
});
|
||||
media_parser_1.MediaParserInternals.internalParseMedia({
|
||||
logLevel,
|
||||
src,
|
||||
onVideoTrack,
|
||||
onAudioTrack,
|
||||
controller: controller._internals._mediaParserController,
|
||||
fields: {
|
||||
...fields,
|
||||
durationInSeconds: true,
|
||||
},
|
||||
reader: reader ?? web_1.webReader,
|
||||
...more,
|
||||
onDurationInSeconds: (durationInSeconds) => {
|
||||
if (durationInSeconds === null) {
|
||||
return null;
|
||||
}
|
||||
const casted = more;
|
||||
if (casted.onDurationInSeconds) {
|
||||
casted.onDurationInSeconds(durationInSeconds);
|
||||
}
|
||||
const expectedOutputDurationInMs = durationInSeconds * 1000;
|
||||
throttledState.update?.((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
expectedOutputDurationInMs,
|
||||
overallProgress: (0, calculate_progress_1.calculateProgress)({
|
||||
millisecondsWritten: prevState.millisecondsWritten,
|
||||
expectedOutputDurationInMs,
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
acknowledgeRemotionLicense: true,
|
||||
mode: 'query',
|
||||
onDiscardedData: null,
|
||||
onError: () => ({ action: 'fail' }),
|
||||
onParseProgress: null,
|
||||
progressIntervalInMs: null,
|
||||
onAudioCodec: onAudioCodec ?? null,
|
||||
onContainer: onContainer ?? null,
|
||||
onDimensions: onDimensions ?? null,
|
||||
onFps: onFps ?? null,
|
||||
onImages: onImages ?? null,
|
||||
onInternalStats: onInternalStats ?? null,
|
||||
onIsHdr: onIsHdr ?? null,
|
||||
onKeyframes: onKeyframes ?? null,
|
||||
onLocation: onLocation ?? null,
|
||||
onMetadata: onMetadata ?? null,
|
||||
onMimeType: onMimeType ?? null,
|
||||
onName: onName ?? null,
|
||||
onNumberOfAudioChannels: onNumberOfAudioChannels ?? null,
|
||||
onRotation: onRotation ?? null,
|
||||
onSampleRate: onSampleRate ?? null,
|
||||
onSize: onSize ?? null,
|
||||
onSlowAudioBitrate: onSlowAudioBitrate ?? null,
|
||||
onSlowDurationInSeconds: onSlowDurationInSeconds ?? null,
|
||||
onSlowFps: onSlowFps ?? null,
|
||||
onSlowKeyframes: onSlowKeyframes ?? null,
|
||||
onSlowNumberOfFrames: onSlowNumberOfFrames ?? null,
|
||||
onSlowVideoBitrate: onSlowVideoBitrate ?? null,
|
||||
onSlowStructure: onSlowStructure ?? null,
|
||||
onTracks: onTracks ?? null,
|
||||
onUnrotatedDimensions: onUnrotatedDimensions ?? null,
|
||||
onVideoCodec: onVideoCodec ?? null,
|
||||
apiName: 'convertMedia()',
|
||||
onM3uStreams: onM3uStreams ?? null,
|
||||
selectM3uStream: selectM3uStream ?? media_parser_1.defaultSelectM3uStreamFn,
|
||||
selectM3uAssociatedPlaylists: selectM3uAssociatedPlaylists ?? media_parser_1.defaultSelectM3uAssociatedPlaylists,
|
||||
makeSamplesStartAtZero: false,
|
||||
m3uPlaylistContext: null,
|
||||
seekingHints: seekingHints ?? null,
|
||||
})
|
||||
.then(() => {
|
||||
return state.waitForFinish();
|
||||
})
|
||||
.then(() => {
|
||||
resolve({
|
||||
save: state.getBlob,
|
||||
remove: state.remove,
|
||||
finalState: throttledState.get(),
|
||||
});
|
||||
})
|
||||
.then(() => { })
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
throttledState.stopAndGetLastProgress();
|
||||
});
|
||||
return getPromiseToImmediatelyReturn().finally(() => {
|
||||
controller._internals._mediaParserController._internals.signal.removeEventListener('abort', onUserAbort);
|
||||
});
|
||||
};
|
||||
exports.convertMedia = convertMedia;
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import type { ConvertMediaVideoCodec } from './get-available-video-codecs';
|
||||
export declare const needsToCorrectVideoFrame: ({ videoFrame, outputCodec, }: {
|
||||
videoFrame: VideoFrame;
|
||||
outputCodec: ConvertMediaVideoCodec;
|
||||
}) => boolean;
|
||||
export declare const convertToCorrectVideoFrame: ({ videoFrame, outputCodec, }: {
|
||||
videoFrame: VideoFrame;
|
||||
outputCodec: ConvertMediaVideoCodec;
|
||||
}) => VideoFrame;
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.convertToCorrectVideoFrame = exports.needsToCorrectVideoFrame = void 0;
|
||||
const browser_quirks_1 = require("./browser-quirks");
|
||||
const needsToCorrectVideoFrame = ({ videoFrame, outputCodec, }) => {
|
||||
// On Chrome when dropping a vertical iPhone video
|
||||
if (videoFrame.format === null) {
|
||||
return true;
|
||||
}
|
||||
// copy8f9178c2-e8ab-4538-9591-f8336602e49b-3mp4 - HDR videos
|
||||
if (videoFrame.format === 'I420P10') {
|
||||
return true;
|
||||
}
|
||||
return (0, browser_quirks_1.isFirefox)() && videoFrame.format === 'BGRX' && outputCodec === 'h264';
|
||||
};
|
||||
exports.needsToCorrectVideoFrame = needsToCorrectVideoFrame;
|
||||
const convertToCorrectVideoFrame = ({ videoFrame, outputCodec, }) => {
|
||||
if (!(0, exports.needsToCorrectVideoFrame)({ videoFrame, outputCodec })) {
|
||||
return videoFrame;
|
||||
}
|
||||
const canvas = new OffscreenCanvas(videoFrame.displayWidth, videoFrame.displayHeight);
|
||||
canvas.width = videoFrame.displayWidth;
|
||||
canvas.height = videoFrame.displayHeight;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
throw new Error('Could not get 2d context');
|
||||
}
|
||||
ctx.drawImage(videoFrame, 0, 0);
|
||||
return new VideoFrame(canvas, {
|
||||
displayHeight: videoFrame.displayHeight,
|
||||
displayWidth: videoFrame.displayWidth,
|
||||
duration: videoFrame.duration,
|
||||
timestamp: videoFrame.timestamp,
|
||||
});
|
||||
};
|
||||
exports.convertToCorrectVideoFrame = convertToCorrectVideoFrame;
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import type { MediaParserAudioTrack, MediaParserLogLevel, MediaParserOnAudioSample } from '@remotion/media-parser';
|
||||
import type { MediaFn } from './create/media-fn';
|
||||
import type { ProgressTracker } from './create/progress-tracker';
|
||||
import type { ConvertMediaProgressFn } from './throttled-state-update';
|
||||
export declare const copyAudioTrack: ({ state, track, logLevel, onMediaStateUpdate, progressTracker, }: {
|
||||
state: MediaFn;
|
||||
track: MediaParserAudioTrack;
|
||||
logLevel: MediaParserLogLevel;
|
||||
onMediaStateUpdate: ConvertMediaProgressFn | null;
|
||||
progressTracker: ProgressTracker;
|
||||
}) => Promise<MediaParserOnAudioSample>;
|
||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.copyAudioTrack = void 0;
|
||||
const log_1 = require("./log");
|
||||
const copyAudioTrack = async ({ state, track, logLevel, onMediaStateUpdate, progressTracker, }) => {
|
||||
const addedTrack = await state.addTrack({
|
||||
type: 'audio',
|
||||
codec: track.codecEnum,
|
||||
numberOfChannels: track.numberOfChannels,
|
||||
sampleRate: track.sampleRate,
|
||||
codecPrivate: track.codecData?.data ?? null,
|
||||
timescale: track.originalTimescale,
|
||||
});
|
||||
log_1.Log.verbose(logLevel, `Copying audio track ${track.trackId} as track ${addedTrack.trackNumber}. Timescale = ${track.originalTimescale}, codec = ${track.codecEnum} (${track.codec}) `);
|
||||
return async (audioSample) => {
|
||||
progressTracker.setPossibleLowestTimestamp(Math.min(audioSample.timestamp, audioSample.decodingTimestamp ?? Infinity));
|
||||
await state.addSample({
|
||||
chunk: audioSample,
|
||||
trackNumber: addedTrack.trackNumber,
|
||||
isVideo: false,
|
||||
codecPrivate: track.codecData?.data ?? null,
|
||||
});
|
||||
onMediaStateUpdate?.((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
encodedAudioFrames: prevState.encodedAudioFrames + 1,
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
exports.copyAudioTrack = copyAudioTrack;
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import type { MediaParserLogLevel, MediaParserOnVideoSample, MediaParserVideoTrack } from '@remotion/media-parser';
|
||||
import type { MediaFn } from './create/media-fn';
|
||||
import type { ProgressTracker } from './create/progress-tracker';
|
||||
import type { ConvertMediaProgressFn } from './throttled-state-update';
|
||||
export declare const copyVideoTrack: ({ logLevel, state, track, onMediaStateUpdate, progressTracker, }: {
|
||||
logLevel: MediaParserLogLevel;
|
||||
state: MediaFn;
|
||||
track: MediaParserVideoTrack;
|
||||
onMediaStateUpdate: null | ConvertMediaProgressFn;
|
||||
progressTracker: ProgressTracker;
|
||||
}) => Promise<MediaParserOnVideoSample>;
|
||||
Generated
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.copyVideoTrack = void 0;
|
||||
const log_1 = require("./log");
|
||||
const copyVideoTrack = async ({ logLevel, state, track, onMediaStateUpdate, progressTracker, }) => {
|
||||
log_1.Log.verbose(logLevel, `Copying video track with codec ${track.codec} and timescale ${track.originalTimescale}`);
|
||||
const videoTrack = await state.addTrack({
|
||||
type: 'video',
|
||||
color: track.advancedColor,
|
||||
width: track.codedWidth,
|
||||
height: track.codedHeight,
|
||||
codec: track.codecEnum,
|
||||
codecPrivate: track.codecData?.data ?? null,
|
||||
timescale: track.originalTimescale,
|
||||
});
|
||||
return async (sample) => {
|
||||
progressTracker.setPossibleLowestTimestamp(Math.min(sample.timestamp, sample.decodingTimestamp ?? Infinity));
|
||||
await state.addSample({
|
||||
chunk: sample,
|
||||
trackNumber: videoTrack.trackNumber,
|
||||
isVideo: true,
|
||||
codecPrivate: track.codecData?.data ?? null,
|
||||
});
|
||||
onMediaStateUpdate?.((prevState) => {
|
||||
return {
|
||||
...prevState,
|
||||
decodedVideoFrames: prevState.decodedVideoFrames + 1,
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
exports.copyVideoTrack = copyVideoTrack;
|
||||
Generated
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
import type { MediaParserLogLevel } from '@remotion/media-parser';
|
||||
import type { WebCodecsController } from './webcodecs-controller';
|
||||
export type WebCodecsAudioDecoder = {
|
||||
decode: (audioSample: EncodedAudioChunkInit | EncodedAudioChunk) => Promise<void>;
|
||||
close: () => void;
|
||||
flush: () => Promise<void>;
|
||||
waitForQueueToBeLessThan: (items: number) => Promise<void>;
|
||||
reset: () => void;
|
||||
checkReset: () => {
|
||||
wasReset: () => boolean;
|
||||
};
|
||||
getMostRecentSampleInput: () => number | null;
|
||||
};
|
||||
export type CreateAudioDecoderInit = {
|
||||
onFrame: (frame: AudioData) => Promise<void> | void;
|
||||
onError: (error: Error) => void;
|
||||
controller: WebCodecsController | null;
|
||||
config: AudioDecoderConfig;
|
||||
logLevel: MediaParserLogLevel;
|
||||
};
|
||||
export declare const internalCreateAudioDecoder: ({ onFrame, onError, controller, config, logLevel, }: CreateAudioDecoderInit) => Promise<WebCodecsAudioDecoder>;
|
||||
export declare const createAudioDecoder: ({ track, onFrame, onError, controller, logLevel, }: {
|
||||
track: AudioDecoderConfig;
|
||||
onFrame: (frame: AudioData) => Promise<void> | void;
|
||||
onError: (error: Error) => void;
|
||||
controller?: WebCodecsController | null;
|
||||
logLevel?: MediaParserLogLevel;
|
||||
}) => Promise<WebCodecsAudioDecoder>;
|
||||
Generated
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAudioDecoder = exports.internalCreateAudioDecoder = void 0;
|
||||
const flush_pending_1 = require("./flush-pending");
|
||||
const get_wave_audio_decoder_1 = require("./get-wave-audio-decoder");
|
||||
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
||||
const undecodable_error_1 = require("./undecodable-error");
|
||||
const internalCreateAudioDecoder = async ({ onFrame, onError, controller, config, logLevel, }) => {
|
||||
if (controller &&
|
||||
controller._internals._mediaParserController._internals.signal.aborted) {
|
||||
throw new Error('Not creating audio decoder, already aborted');
|
||||
}
|
||||
const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
|
||||
logLevel,
|
||||
label: 'Audio decoder',
|
||||
controller,
|
||||
});
|
||||
let mostRecentSampleReceived = null;
|
||||
if (config.codec === 'pcm-s16') {
|
||||
return (0, get_wave_audio_decoder_1.getWaveAudioDecoder)({
|
||||
onFrame,
|
||||
config,
|
||||
sampleFormat: 's16',
|
||||
logLevel,
|
||||
ioSynchronizer,
|
||||
onError,
|
||||
});
|
||||
}
|
||||
if (config.codec === 'pcm-s24') {
|
||||
return (0, get_wave_audio_decoder_1.getWaveAudioDecoder)({
|
||||
onFrame,
|
||||
config,
|
||||
sampleFormat: 's24',
|
||||
logLevel,
|
||||
ioSynchronizer,
|
||||
onError,
|
||||
});
|
||||
}
|
||||
const audioDecoder = new AudioDecoder({
|
||||
async output(frame) {
|
||||
try {
|
||||
await onFrame(frame);
|
||||
}
|
||||
catch (err) {
|
||||
frame.close();
|
||||
onError(err);
|
||||
}
|
||||
ioSynchronizer.onOutput(frame.timestamp + (frame.duration ?? 0));
|
||||
},
|
||||
error(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
const close = () => {
|
||||
if (controller) {
|
||||
controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
onAbort);
|
||||
}
|
||||
if (audioDecoder.state === 'closed') {
|
||||
return;
|
||||
}
|
||||
audioDecoder.close();
|
||||
};
|
||||
const onAbort = () => {
|
||||
close();
|
||||
};
|
||||
if (controller) {
|
||||
controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
|
||||
}
|
||||
const isConfigSupported = await AudioDecoder.isConfigSupported(config);
|
||||
if (!isConfigSupported) {
|
||||
throw new undecodable_error_1.AudioUndecodableError({
|
||||
message: 'Audio cannot be decoded by this browser',
|
||||
config,
|
||||
});
|
||||
}
|
||||
audioDecoder.configure(config);
|
||||
const decode = async (audioSample) => {
|
||||
if (audioDecoder.state === 'closed') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await controller?._internals._mediaParserController._internals.checkForAbortAndPause();
|
||||
}
|
||||
catch (err) {
|
||||
onError(err);
|
||||
return;
|
||||
}
|
||||
mostRecentSampleReceived = audioSample.timestamp;
|
||||
// Don't flush, it messes up the audio
|
||||
const chunk = audioSample instanceof EncodedAudioChunk
|
||||
? audioSample
|
||||
: new EncodedAudioChunk(audioSample);
|
||||
audioDecoder.decode(chunk);
|
||||
// https://test-streams.mux.dev/x36xhzz/url_0/url_525/193039199_mp4_h264_aac_hd_7.ts
|
||||
// has a 16 byte audio sample at the end which chrome does not decode
|
||||
// Might be empty audio
|
||||
// For now only reporting chunks that are bigger than that
|
||||
// 16 was chosen arbitrarily, can be improved
|
||||
if (chunk.byteLength > 16) {
|
||||
ioSynchronizer.inputItem(chunk.timestamp);
|
||||
}
|
||||
};
|
||||
let flushPending = null;
|
||||
const lastReset = null;
|
||||
return {
|
||||
decode,
|
||||
close,
|
||||
flush: () => {
|
||||
if (flushPending) {
|
||||
throw new Error('Flush already pending');
|
||||
}
|
||||
const pendingFlush = (0, flush_pending_1.makeFlushPending)();
|
||||
flushPending = pendingFlush;
|
||||
Promise.resolve()
|
||||
.then(() => {
|
||||
return audioDecoder.flush();
|
||||
})
|
||||
.catch(() => {
|
||||
// Firefox might throw "Needs to be configured first"
|
||||
})
|
||||
.finally(() => {
|
||||
pendingFlush.resolve();
|
||||
flushPending = null;
|
||||
});
|
||||
return pendingFlush.promise;
|
||||
},
|
||||
waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
|
||||
reset: () => {
|
||||
audioDecoder.reset();
|
||||
audioDecoder.configure(config);
|
||||
},
|
||||
checkReset: () => {
|
||||
const initTime = Date.now();
|
||||
return {
|
||||
wasReset: () => lastReset !== null && lastReset > initTime,
|
||||
};
|
||||
},
|
||||
getMostRecentSampleInput() {
|
||||
return mostRecentSampleReceived;
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.internalCreateAudioDecoder = internalCreateAudioDecoder;
|
||||
const createAudioDecoder = ({ track, onFrame, onError, controller, logLevel, }) => {
|
||||
return (0, exports.internalCreateAudioDecoder)({
|
||||
onFrame,
|
||||
onError,
|
||||
controller: controller ?? null,
|
||||
config: track,
|
||||
logLevel: logLevel ?? 'error',
|
||||
});
|
||||
};
|
||||
exports.createAudioDecoder = createAudioDecoder;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { MediaFnGeneratorInput } from './create/media-fn';
|
||||
export declare const createMedia: (params: MediaFnGeneratorInput) => Promise<import("./create/media-fn").MediaFn>;
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMedia = void 0;
|
||||
const create_iso_base_media_1 = require("./create/iso-base-media/create-iso-base-media");
|
||||
const create_matroska_media_1 = require("./create/matroska/create-matroska-media");
|
||||
const create_wav_1 = require("./create/wav/create-wav");
|
||||
const createMedia = (params) => {
|
||||
if (params.container === 'mp4') {
|
||||
return (0, create_iso_base_media_1.createIsoBaseMedia)(params);
|
||||
}
|
||||
if (params.container === 'wav') {
|
||||
return (0, create_wav_1.createWav)(params);
|
||||
}
|
||||
if (params.container === 'webm') {
|
||||
return (0, create_matroska_media_1.createMatroskaMedia)(params);
|
||||
}
|
||||
throw new Error(`Unsupported container: ${params.container}`);
|
||||
};
|
||||
exports.createMedia = createMedia;
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
import type { MediaParserLogLevel } from '@remotion/media-parser';
|
||||
import type { WebCodecsController } from './webcodecs-controller';
|
||||
export type WebCodecsVideoDecoder = {
|
||||
decode: (videoSample: EncodedVideoChunkInit | EncodedVideoChunk) => Promise<void>;
|
||||
close: () => void;
|
||||
flush: () => Promise<void>;
|
||||
waitForQueueToBeLessThan: (items: number) => Promise<void>;
|
||||
reset: () => void;
|
||||
checkReset: () => {
|
||||
wasReset: () => boolean;
|
||||
};
|
||||
getMostRecentSampleInput: () => number | null;
|
||||
};
|
||||
export declare const internalCreateVideoDecoder: ({ onFrame, onError, controller, config, logLevel, }: {
|
||||
onFrame: (frame: VideoFrame) => Promise<void> | void;
|
||||
onError: (error: Error) => void;
|
||||
controller: WebCodecsController | null;
|
||||
config: VideoDecoderConfig;
|
||||
logLevel: MediaParserLogLevel;
|
||||
}) => Promise<WebCodecsVideoDecoder>;
|
||||
export declare const createVideoDecoder: ({ onFrame, onError, controller, track, logLevel, }: {
|
||||
track: VideoDecoderConfig;
|
||||
onFrame: (frame: VideoFrame) => Promise<void> | void;
|
||||
onError: (error: Error) => void;
|
||||
controller?: WebCodecsController;
|
||||
logLevel?: MediaParserLogLevel;
|
||||
}) => Promise<WebCodecsVideoDecoder>;
|
||||
Generated
Vendored
+129
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createVideoDecoder = exports.internalCreateVideoDecoder = void 0;
|
||||
const flush_pending_1 = require("./flush-pending");
|
||||
const io_synchronizer_1 = require("./io-manager/io-synchronizer");
|
||||
const undecodable_error_1 = require("./undecodable-error");
|
||||
const internalCreateVideoDecoder = async ({ onFrame, onError, controller, config, logLevel, }) => {
|
||||
if (controller &&
|
||||
controller._internals._mediaParserController._internals.signal.aborted) {
|
||||
throw new Error('Not creating audio decoder, already aborted');
|
||||
}
|
||||
const ioSynchronizer = (0, io_synchronizer_1.makeIoSynchronizer)({
|
||||
logLevel,
|
||||
label: 'Video decoder',
|
||||
controller,
|
||||
});
|
||||
let mostRecentSampleReceived = null;
|
||||
const videoDecoder = new VideoDecoder({
|
||||
async output(frame) {
|
||||
try {
|
||||
await onFrame(frame);
|
||||
}
|
||||
catch (err) {
|
||||
onError(err);
|
||||
frame.close();
|
||||
}
|
||||
ioSynchronizer.onOutput(frame.timestamp);
|
||||
},
|
||||
error(error) {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
const close = () => {
|
||||
if (controller) {
|
||||
controller._internals._mediaParserController._internals.signal.removeEventListener('abort',
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
onAbort);
|
||||
}
|
||||
if (videoDecoder.state === 'closed') {
|
||||
return;
|
||||
}
|
||||
videoDecoder.close();
|
||||
};
|
||||
const onAbort = () => {
|
||||
close();
|
||||
};
|
||||
if (controller) {
|
||||
controller._internals._mediaParserController._internals.signal.addEventListener('abort', onAbort);
|
||||
}
|
||||
const isConfigSupported = await VideoDecoder.isConfigSupported(config);
|
||||
if (!isConfigSupported) {
|
||||
throw new undecodable_error_1.VideoUndecodableError({
|
||||
message: 'Video cannot be decoded by this browser',
|
||||
config,
|
||||
});
|
||||
}
|
||||
videoDecoder.configure(config);
|
||||
const decode = async (sample) => {
|
||||
if (videoDecoder.state === 'closed') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await controller?._internals._mediaParserController._internals.checkForAbortAndPause();
|
||||
}
|
||||
catch (err) {
|
||||
onError(err);
|
||||
return;
|
||||
}
|
||||
mostRecentSampleReceived = sample.timestamp;
|
||||
const encodedChunk = sample instanceof EncodedVideoChunk
|
||||
? sample
|
||||
: new EncodedVideoChunk(sample);
|
||||
videoDecoder.decode(encodedChunk);
|
||||
ioSynchronizer.inputItem(sample.timestamp);
|
||||
};
|
||||
let flushPending = null;
|
||||
let lastReset = null;
|
||||
return {
|
||||
decode,
|
||||
close,
|
||||
flush: () => {
|
||||
if (flushPending) {
|
||||
throw new Error('Flush already pending');
|
||||
}
|
||||
const pendingFlush = (0, flush_pending_1.makeFlushPending)();
|
||||
flushPending = pendingFlush;
|
||||
Promise.resolve()
|
||||
.then(() => {
|
||||
return videoDecoder.flush();
|
||||
})
|
||||
.catch(() => {
|
||||
// Firefox might throw "Needs to be configured first"
|
||||
})
|
||||
.finally(() => {
|
||||
pendingFlush.resolve();
|
||||
flushPending = null;
|
||||
});
|
||||
return pendingFlush.promise;
|
||||
},
|
||||
waitForQueueToBeLessThan: ioSynchronizer.waitForQueueSize,
|
||||
reset: () => {
|
||||
lastReset = Date.now();
|
||||
flushPending?.resolve();
|
||||
ioSynchronizer.clearQueue();
|
||||
videoDecoder.reset();
|
||||
videoDecoder.configure(config);
|
||||
},
|
||||
checkReset: () => {
|
||||
const initTime = Date.now();
|
||||
return {
|
||||
wasReset: () => lastReset !== null && lastReset > initTime,
|
||||
};
|
||||
},
|
||||
getMostRecentSampleInput() {
|
||||
return mostRecentSampleReceived;
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.internalCreateVideoDecoder = internalCreateVideoDecoder;
|
||||
const createVideoDecoder = ({ onFrame, onError, controller, track, logLevel, }) => {
|
||||
return (0, exports.internalCreateVideoDecoder)({
|
||||
onFrame,
|
||||
onError,
|
||||
controller: controller ?? null,
|
||||
config: track,
|
||||
logLevel: logLevel ?? 'info',
|
||||
});
|
||||
};
|
||||
exports.createVideoDecoder = createVideoDecoder;
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
type Input = {
|
||||
timestamp: number;
|
||||
};
|
||||
type Output = {
|
||||
timestamp: number;
|
||||
};
|
||||
type Processed = {};
|
||||
type Progress = {
|
||||
smallestProgress: number;
|
||||
};
|
||||
type IoEventMap = {
|
||||
input: Input;
|
||||
output: Output;
|
||||
processed: Processed;
|
||||
progress: Progress;
|
||||
};
|
||||
export type IoEventTypes = keyof IoEventMap;
|
||||
export type CallbackListener<T extends IoEventTypes> = (data: {
|
||||
detail: IoEventMap[T];
|
||||
}) => void;
|
||||
type IoListeners = {
|
||||
[EventType in IoEventTypes]: CallbackListener<EventType>[];
|
||||
};
|
||||
export declare class IoEventEmitter {
|
||||
listeners: IoListeners;
|
||||
addEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
|
||||
removeEventListener<Q extends IoEventTypes>(name: Q, callback: CallbackListener<Q>): void;
|
||||
dispatchEvent<T extends IoEventTypes>(dispatchName: T, context: IoEventMap[T]): void;
|
||||
}
|
||||
export {};
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.IoEventEmitter = void 0;
|
||||
class IoEventEmitter {
|
||||
constructor() {
|
||||
this.listeners = {
|
||||
input: [],
|
||||
output: [],
|
||||
processed: [],
|
||||
progress: [],
|
||||
};
|
||||
}
|
||||
addEventListener(name, callback) {
|
||||
this.listeners[name].push(callback);
|
||||
}
|
||||
removeEventListener(name, callback) {
|
||||
this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
|
||||
}
|
||||
dispatchEvent(dispatchName, context) {
|
||||
this.listeners[dispatchName].forEach((callback) => {
|
||||
callback({ detail: context });
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.IoEventEmitter = IoEventEmitter;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { Avc1Data } from './create-codec-specific-data';
|
||||
export declare const createAvc1Data: ({ avccBox, pasp, width, height, horizontalResolution, verticalResolution, compressorName, depth, }: Avc1Data) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAvc1Data = void 0;
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
const createAvc1Data = ({ avccBox, pasp, width, height, horizontalResolution, verticalResolution, compressorName, depth, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('avc1'),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0, 0, 0]),
|
||||
// data_reference_index
|
||||
new Uint8Array([0, 1]),
|
||||
// version
|
||||
new Uint8Array([0, 0]),
|
||||
// revisionLevel
|
||||
new Uint8Array([0, 0]),
|
||||
// vendor
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// temporalQuality
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// spatialQuality
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// width
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(width),
|
||||
// height
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(height),
|
||||
// horizontalResolution
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(horizontalResolution),
|
||||
// verticalResolution
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(verticalResolution),
|
||||
// dataSize
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// frame count per sample
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(1),
|
||||
// compressor name
|
||||
(0, primitives_1.stringToPascalString)(compressorName),
|
||||
// depth
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(depth),
|
||||
// colorTableId
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(-1),
|
||||
// avcc box
|
||||
avccBox,
|
||||
// pasp
|
||||
pasp,
|
||||
]));
|
||||
};
|
||||
exports.createAvc1Data = createAvc1Data;
|
||||
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
import type { MakeTrackAudio, MakeTrackVideo } from '../../make-track-info';
|
||||
export type Avc1Data = {
|
||||
pasp: Uint8Array;
|
||||
avccBox: Uint8Array;
|
||||
width: number;
|
||||
height: number;
|
||||
horizontalResolution: number;
|
||||
verticalResolution: number;
|
||||
compressorName: string;
|
||||
depth: number;
|
||||
type: 'avc1-data';
|
||||
};
|
||||
export type Hvc1Data = {
|
||||
pasp: Uint8Array;
|
||||
hvccBox: Uint8Array;
|
||||
width: number;
|
||||
height: number;
|
||||
horizontalResolution: number;
|
||||
verticalResolution: number;
|
||||
compressorName: string;
|
||||
depth: number;
|
||||
type: 'hvc1-data';
|
||||
};
|
||||
export type Mp4aData = {
|
||||
type: 'mp4a-data';
|
||||
sampleRate: number;
|
||||
channelCount: number;
|
||||
maxBitrate: number;
|
||||
avgBitrate: number;
|
||||
codecPrivate: Uint8Array | null;
|
||||
};
|
||||
export type CodecSpecificData = Avc1Data | Mp4aData;
|
||||
export declare const createCodecSpecificData: (track: MakeTrackAudio | MakeTrackVideo) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createCodecSpecificData = void 0;
|
||||
const create_avcc_1 = require("../trak/mdia/minf/stbl/stsd/create-avcc");
|
||||
const create_hvcc_1 = require("../trak/mdia/minf/stbl/stsd/create-hvcc");
|
||||
const create_pasp_1 = require("../trak/mdia/minf/stbl/stsd/create-pasp");
|
||||
const avc1_1 = require("./avc1");
|
||||
const hvc1_1 = require("./hvc1");
|
||||
const mp4a_1 = require("./mp4a");
|
||||
const createCodecSpecificData = (track) => {
|
||||
if (track.type === 'video') {
|
||||
if (track.codec === 'h264') {
|
||||
// May not have it initially
|
||||
if (!track.codecPrivate) {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
return (0, avc1_1.createAvc1Data)({
|
||||
avccBox: (0, create_avcc_1.createAvccBox)(track.codecPrivate),
|
||||
compressorName: 'WebCodecs',
|
||||
depth: 24,
|
||||
horizontalResolution: 72,
|
||||
verticalResolution: 72,
|
||||
height: track.height,
|
||||
width: track.width,
|
||||
pasp: (0, create_pasp_1.createPasp)(1, 1),
|
||||
type: 'avc1-data',
|
||||
});
|
||||
}
|
||||
if (track.codec === 'h265') {
|
||||
// May not have it initially
|
||||
if (!track.codecPrivate) {
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
return (0, hvc1_1.createHvc1Data)({
|
||||
hvccBox: (0, create_hvcc_1.createHvccBox)(track.codecPrivate),
|
||||
compressorName: 'WebCodecs',
|
||||
depth: 24,
|
||||
horizontalResolution: 72,
|
||||
verticalResolution: 72,
|
||||
height: track.height,
|
||||
width: track.width,
|
||||
pasp: (0, create_pasp_1.createPasp)(1, 1),
|
||||
type: 'hvc1-data',
|
||||
});
|
||||
}
|
||||
throw new Error('Unsupported codec specific data ' + track.codec);
|
||||
}
|
||||
if (track.type === 'audio') {
|
||||
return (0, mp4a_1.createMp4a)({
|
||||
type: 'mp4a-data',
|
||||
// TODO: Put in values based on real data,
|
||||
// this seems to work though
|
||||
avgBitrate: 128 * 1024,
|
||||
maxBitrate: 128 * 1024,
|
||||
channelCount: track.numberOfChannels,
|
||||
sampleRate: track.sampleRate,
|
||||
codecPrivate: track.codecPrivate,
|
||||
});
|
||||
}
|
||||
throw new Error('Unsupported codec specific data ' + track);
|
||||
};
|
||||
exports.createCodecSpecificData = createCodecSpecificData;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { Hvc1Data } from './create-codec-specific-data';
|
||||
export declare const createHvc1Data: ({ compressorName, depth, height, horizontalResolution, hvccBox, pasp, verticalResolution, width, }: Hvc1Data) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createHvc1Data = void 0;
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
const createHvc1Data = ({ compressorName, depth, height, horizontalResolution, hvccBox, pasp, verticalResolution, width, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('hvc1'),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0, 0, 0]),
|
||||
// data_reference_index
|
||||
new Uint8Array([0, 1]),
|
||||
// version
|
||||
new Uint8Array([0, 0]),
|
||||
// revisionLevel
|
||||
new Uint8Array([0, 0]),
|
||||
// vendor
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// temporalQuality
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// spatialQuality
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// width
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(width),
|
||||
// height
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(height),
|
||||
// horizontalResolution
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(horizontalResolution),
|
||||
// verticalResolution
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(verticalResolution),
|
||||
// dataSize
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// frame count per sample
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(1),
|
||||
// compressor name
|
||||
(0, primitives_1.stringToPascalString)(compressorName),
|
||||
// depth
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(depth),
|
||||
// colorTableId
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(-1),
|
||||
// hvcc box
|
||||
hvccBox,
|
||||
// pasp
|
||||
pasp,
|
||||
]));
|
||||
};
|
||||
exports.createHvc1Data = createHvc1Data;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { Mp4aData } from './create-codec-specific-data';
|
||||
export declare const createMp4a: ({ sampleRate, channelCount, avgBitrate, maxBitrate, codecPrivate, }: Mp4aData) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMp4a = void 0;
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
const createMp4a = ({ sampleRate, channelCount, avgBitrate, maxBitrate, codecPrivate, }) => {
|
||||
if (!codecPrivate) {
|
||||
throw new Error('Need codecPrivate for mp4a');
|
||||
}
|
||||
const esdsAtom = (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('esds'),
|
||||
// version
|
||||
new Uint8Array([0]),
|
||||
// flags
|
||||
new Uint8Array([0, 0, 0]),
|
||||
// tag = 'ES_DescrTag'
|
||||
new Uint8Array([3]),
|
||||
(0, primitives_1.addLeading128Size)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// ES_ID
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(2),
|
||||
// streamDependenceFlag, URL_Flag, OCRstreamFlag
|
||||
new Uint8Array([0]),
|
||||
// DecoderConfigDescrTag
|
||||
new Uint8Array([4]),
|
||||
(0, primitives_1.addLeading128Size)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// objectTypeIndication
|
||||
new Uint8Array([0x40]),
|
||||
// streamType, upStream
|
||||
new Uint8Array([21]),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0]),
|
||||
// maxBitrate
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(maxBitrate),
|
||||
// avgBitrate
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(avgBitrate),
|
||||
// DecoderSpecificInfoTag
|
||||
new Uint8Array([5]),
|
||||
// see create-aac-codecprivate.ts
|
||||
(0, primitives_1.addLeading128Size)(codecPrivate),
|
||||
])),
|
||||
// SLConfigDescrTag
|
||||
new Uint8Array([6]),
|
||||
(0, primitives_1.addLeading128Size)(new Uint8Array([2])),
|
||||
])),
|
||||
]));
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('mp4a'),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0, 0, 0]),
|
||||
// data_reference_index
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(1),
|
||||
// version
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(0),
|
||||
// revision level
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(0),
|
||||
// vendor
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// channelCount
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(channelCount),
|
||||
// sampleSize
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(16),
|
||||
// compressionId
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(0),
|
||||
// packet size
|
||||
(0, primitives_1.numberTo16BitUIntOrInt)(0),
|
||||
// sample rate
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(sampleRate),
|
||||
// esds atom
|
||||
esdsAtom,
|
||||
]));
|
||||
};
|
||||
exports.createMp4a = createMp4a;
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
export declare const createColr: ({ fullRange, matrixIndex, primaries, transferFunction, }: {
|
||||
fullRange: boolean;
|
||||
matrixIndex: number;
|
||||
primaries: number;
|
||||
transferFunction: number;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createColr = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
// TODO: Not used in creation of MP4 yet
|
||||
const createColr = ({ fullRange, matrixIndex, primaries, transferFunction, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('colr'),
|
||||
// colour_type
|
||||
(0, primitives_1.stringsToUint8Array)('nclx'),
|
||||
// primaries
|
||||
// 1 = 'ITU-R BT.7090
|
||||
new Uint8Array([0, primaries]),
|
||||
// transfer_function
|
||||
// 1 = 'ITU-R BT.7090
|
||||
new Uint8Array([0, transferFunction]),
|
||||
// matrix_index
|
||||
// 1 = 'ITU-R BT.7090
|
||||
new Uint8Array([0, matrixIndex]),
|
||||
// full_range_flag
|
||||
new Uint8Array([fullRange ? 1 : 0]),
|
||||
]));
|
||||
};
|
||||
exports.createColr = createColr;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
export declare const createFtyp: ({ majorBrand, minorBrand, compatibleBrands, }: {
|
||||
majorBrand: string;
|
||||
minorBrand: number;
|
||||
compatibleBrands: string[];
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
export declare const createIsoBaseMediaFtyp: ({ majorBrand, minorBrand, compatibleBrands, }: {
|
||||
majorBrand: string;
|
||||
minorBrand: number;
|
||||
compatibleBrands: string[];
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createIsoBaseMediaFtyp = exports.createFtyp = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createFtyp = ({ majorBrand, minorBrand, compatibleBrands, }) => {
|
||||
const type = (0, primitives_1.stringsToUint8Array)('ftyp');
|
||||
const majorBrandArr = (0, primitives_1.stringsToUint8Array)(majorBrand);
|
||||
const minorBrandArr = (0, primitives_1.numberTo32BitUIntOrInt)(minorBrand);
|
||||
const compatibleBrandsArr = (0, matroska_utils_1.combineUint8Arrays)(compatibleBrands.map((b) => (0, primitives_1.stringsToUint8Array)(b)));
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
type,
|
||||
majorBrandArr,
|
||||
minorBrandArr,
|
||||
compatibleBrandsArr,
|
||||
]));
|
||||
};
|
||||
exports.createFtyp = createFtyp;
|
||||
const createIsoBaseMediaFtyp = ({ majorBrand, minorBrand, compatibleBrands, }) => {
|
||||
return (0, exports.createFtyp)({ compatibleBrands, majorBrand, minorBrand });
|
||||
};
|
||||
exports.createIsoBaseMediaFtyp = createIsoBaseMediaFtyp;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const createIlst: (items: Uint8Array[]) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createIlst = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createIlst = (items) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// name
|
||||
(0, primitives_1.stringsToUint8Array)('ilst'),
|
||||
// items
|
||||
...items,
|
||||
]));
|
||||
};
|
||||
exports.createIlst = createIlst;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { MediaFn, MediaFnGeneratorInput } from '../media-fn';
|
||||
export declare const createIsoBaseMedia: ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, progressTracker, expectedDurationInSeconds, expectedFrameRate, }: MediaFnGeneratorInput) => Promise<MediaFn>;
|
||||
Generated
Vendored
+191
@@ -0,0 +1,191 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createIsoBaseMedia = void 0;
|
||||
const media_parser_1 = require("@remotion/media-parser");
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const create_ftyp_1 = require("./create-ftyp");
|
||||
const mp4_header_1 = require("./mp4-header");
|
||||
const primitives_1 = require("./primitives");
|
||||
const CONTAINER_TIMESCALE = 1000;
|
||||
const createIsoBaseMedia = async ({ writer, onBytesProgress, onMillisecondsProgress, logLevel, filename, progressTracker, expectedDurationInSeconds, expectedFrameRate, }) => {
|
||||
const header = (0, create_ftyp_1.createIsoBaseMediaFtyp)({
|
||||
compatibleBrands: ['isom', 'iso2', 'avc1', 'mp42'],
|
||||
majorBrand: 'isom',
|
||||
minorBrand: 512,
|
||||
});
|
||||
const w = await writer.createContent({
|
||||
filename,
|
||||
mimeType: 'video/mp4',
|
||||
logLevel,
|
||||
});
|
||||
await w.write(header);
|
||||
let globalDurationInUnits = 0;
|
||||
const lowestTrackTimestamps = {};
|
||||
const trackDurations = {};
|
||||
const currentTracks = [];
|
||||
const samplePositions = [];
|
||||
const sampleChunkIndices = [];
|
||||
const moovOffset = w.getWrittenByteCount();
|
||||
const getPaddedMoovAtom = () => {
|
||||
return (0, mp4_header_1.createPaddedMoovAtom)({
|
||||
durationInUnits: globalDurationInUnits,
|
||||
trackInfo: currentTracks.map((track) => {
|
||||
return {
|
||||
track,
|
||||
durationInUnits: trackDurations[track.trackNumber] ?? 0,
|
||||
samplePositions: samplePositions[track.trackNumber] ?? [],
|
||||
timescale: track.timescale,
|
||||
};
|
||||
}),
|
||||
timescale: CONTAINER_TIMESCALE,
|
||||
expectedDurationInSeconds,
|
||||
logLevel,
|
||||
expectedFrameRate,
|
||||
});
|
||||
};
|
||||
await w.write(getPaddedMoovAtom());
|
||||
let mdatSize = 8;
|
||||
const mdatSizeOffset = w.getWrittenByteCount();
|
||||
await w.write((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// size
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(mdatSize),
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('mdat'),
|
||||
]));
|
||||
const updateMdatSize = async () => {
|
||||
await w.updateDataAt(mdatSizeOffset, (0, primitives_1.numberTo32BitUIntOrInt)(mdatSize));
|
||||
onBytesProgress(w.getWrittenByteCount());
|
||||
};
|
||||
const operationProm = { current: Promise.resolve() };
|
||||
const updateMoov = async () => {
|
||||
await w.updateDataAt(moovOffset, getPaddedMoovAtom());
|
||||
onBytesProgress(w.getWrittenByteCount());
|
||||
};
|
||||
const addCodecPrivateToTrack = ({ trackNumber, codecPrivate, }) => {
|
||||
currentTracks.forEach((track) => {
|
||||
if (track.trackNumber === trackNumber) {
|
||||
track.codecPrivate = codecPrivate;
|
||||
}
|
||||
});
|
||||
};
|
||||
let lastChunkWasVideo = false;
|
||||
const addSample = async ({ chunk, trackNumber, isVideo, codecPrivate, }) => {
|
||||
const position = w.getWrittenByteCount();
|
||||
await w.write(chunk.data);
|
||||
mdatSize += chunk.data.length;
|
||||
onBytesProgress(w.getWrittenByteCount());
|
||||
progressTracker.setPossibleLowestTimestamp(Math.min(chunk.timestamp, chunk.decodingTimestamp ?? Infinity));
|
||||
progressTracker.updateTrackProgress(trackNumber, chunk.timestamp);
|
||||
if (codecPrivate) {
|
||||
addCodecPrivateToTrack({ trackNumber, codecPrivate });
|
||||
}
|
||||
const currentTrack = currentTracks.find((t) => t.trackNumber === trackNumber);
|
||||
if (!currentTrack) {
|
||||
throw new Error(`Tried to add sample to track ${trackNumber}, but it doesn't exist`);
|
||||
}
|
||||
if (!lowestTrackTimestamps[trackNumber] ||
|
||||
chunk.timestamp < lowestTrackTimestamps[trackNumber]) {
|
||||
lowestTrackTimestamps[trackNumber] = chunk.timestamp;
|
||||
}
|
||||
if (typeof lowestTrackTimestamps[trackNumber] !== 'number') {
|
||||
throw new Error(`Tried to add sample to track ${trackNumber}, but it has no timestamp`);
|
||||
}
|
||||
const newDurationInMicroSeconds = chunk.timestamp +
|
||||
(chunk.duration ?? 0) -
|
||||
lowestTrackTimestamps[trackNumber];
|
||||
const newDurationInTrackTimeUnits = Math.round(newDurationInMicroSeconds / (1000000 / currentTrack.timescale));
|
||||
trackDurations[trackNumber] = newDurationInTrackTimeUnits;
|
||||
// webcodecs returns frame duration in microseconds
|
||||
const newDurationInMilliseconds = Math.round((newDurationInMicroSeconds / 1000000) * CONTAINER_TIMESCALE);
|
||||
if (newDurationInMilliseconds > globalDurationInUnits) {
|
||||
globalDurationInUnits = newDurationInMilliseconds;
|
||||
onMillisecondsProgress(newDurationInMilliseconds);
|
||||
}
|
||||
if (!samplePositions[trackNumber]) {
|
||||
samplePositions[trackNumber] = [];
|
||||
}
|
||||
if (typeof sampleChunkIndices[trackNumber] === 'undefined') {
|
||||
sampleChunkIndices[trackNumber] = 0;
|
||||
}
|
||||
// For video, make a new chunk if it's a keyframe
|
||||
if (isVideo && chunk.type === 'key') {
|
||||
sampleChunkIndices[trackNumber]++;
|
||||
} // For audio, make a new chunk every 22 samples, that's how bbb.mp4 is encoded
|
||||
else if (!isVideo && samplePositions[trackNumber].length % 22 === 0) {
|
||||
sampleChunkIndices[trackNumber]++;
|
||||
}
|
||||
// Need to create a new chunk if the last chunk was a different type
|
||||
else if (lastChunkWasVideo !== isVideo) {
|
||||
sampleChunkIndices[trackNumber]++;
|
||||
}
|
||||
// media parser and EncodedVideoChunk returns timestamps in microseconds
|
||||
// need to normalize the timestamps to milliseconds
|
||||
const samplePositionToAdd = {
|
||||
isKeyframe: chunk.type === 'key',
|
||||
offset: position,
|
||||
chunk: sampleChunkIndices[trackNumber],
|
||||
timestamp: Math.round((chunk.timestamp / 1000000) * currentTrack.timescale),
|
||||
decodingTimestamp: Math.round((chunk.decodingTimestamp / 1000000) * currentTrack.timescale),
|
||||
duration: Math.round(((chunk.duration ?? 0) / 1000000) * currentTrack.timescale),
|
||||
size: chunk.data.length,
|
||||
bigEndian: false,
|
||||
chunkSize: null,
|
||||
};
|
||||
lastChunkWasVideo = isVideo;
|
||||
samplePositions[trackNumber].push(samplePositionToAdd);
|
||||
};
|
||||
const addTrack = (track) => {
|
||||
const trackNumber = currentTracks.length + 1;
|
||||
currentTracks.push({ ...track, trackNumber });
|
||||
progressTracker.registerTrack(trackNumber);
|
||||
return Promise.resolve({ trackNumber });
|
||||
};
|
||||
const waitForFinishPromises = [];
|
||||
return {
|
||||
getBlob: () => {
|
||||
return w.getBlob();
|
||||
},
|
||||
remove: async () => {
|
||||
await w.remove();
|
||||
},
|
||||
addSample: ({ chunk, trackNumber, isVideo, codecPrivate }) => {
|
||||
operationProm.current = operationProm.current.then(() => {
|
||||
return addSample({
|
||||
chunk,
|
||||
trackNumber,
|
||||
isVideo,
|
||||
codecPrivate,
|
||||
});
|
||||
});
|
||||
return operationProm.current;
|
||||
},
|
||||
addTrack: (track) => {
|
||||
operationProm.current = operationProm.current.then(() => addTrack(track));
|
||||
return operationProm.current;
|
||||
},
|
||||
updateTrackSampleRate: ({ sampleRate, trackNumber }) => {
|
||||
currentTracks.forEach((track) => {
|
||||
if (track.trackNumber === trackNumber) {
|
||||
if (track.type !== 'audio') {
|
||||
throw new Error(`Tried to update sample rate of track ${trackNumber}, but it's not an audio track`);
|
||||
}
|
||||
track.sampleRate = sampleRate;
|
||||
}
|
||||
});
|
||||
},
|
||||
addWaitForFinishPromise: (promise) => {
|
||||
waitForFinishPromises.push(promise);
|
||||
},
|
||||
async waitForFinish() {
|
||||
media_parser_1.MediaParserInternals.Log.verbose(logLevel, 'All write operations queued. Waiting for finish...');
|
||||
await Promise.all(waitForFinishPromises.map((p) => p()));
|
||||
media_parser_1.MediaParserInternals.Log.verbose(logLevel, 'Cleanup tasks executed');
|
||||
await operationProm.current;
|
||||
await updateMoov();
|
||||
await updateMdatSize();
|
||||
media_parser_1.MediaParserInternals.Log.verbose(logLevel, 'All write operations done. Waiting for finish...');
|
||||
await w.finish();
|
||||
},
|
||||
};
|
||||
};
|
||||
exports.createIsoBaseMedia = createIsoBaseMedia;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export declare const createMdia: ({ mdhd, hdlr, minf, }: {
|
||||
mdhd: Uint8Array;
|
||||
hdlr: Uint8Array;
|
||||
minf: Uint8Array;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMdia = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createMdia = ({ mdhd, hdlr, minf, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('mdia'),
|
||||
// mdhd
|
||||
mdhd,
|
||||
// hdlr
|
||||
hdlr,
|
||||
// minf
|
||||
minf,
|
||||
]));
|
||||
};
|
||||
exports.createMdia = createMdia;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
export declare const createMoov: ({ mvhd, traks, udta, }: {
|
||||
mvhd: Uint8Array;
|
||||
traks: Uint8Array[];
|
||||
udta: Uint8Array;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMoov = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createMoov = ({ mvhd, traks, udta, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('moov'),
|
||||
// moov atom
|
||||
mvhd,
|
||||
// traks
|
||||
...traks,
|
||||
// udta
|
||||
udta,
|
||||
]));
|
||||
};
|
||||
exports.createMoov = createMoov;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
export declare const createMvhd: ({ timescale, durationInUnits, rate, volume, nextTrackId, matrix, creationTime, modificationTime, }: {
|
||||
timescale: number;
|
||||
durationInUnits: number;
|
||||
rate: number;
|
||||
volume: number;
|
||||
nextTrackId: number;
|
||||
matrix: number[];
|
||||
creationTime: number | null;
|
||||
modificationTime: number | null;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMvhd = void 0;
|
||||
const from_unix_timestamp_1 = require("../../from-unix-timestamp");
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createMvhd = ({ timescale, durationInUnits, rate, volume, nextTrackId, matrix, creationTime, modificationTime, }) => {
|
||||
if (matrix.length !== 9) {
|
||||
throw new Error('Matrix must be 9 elements long');
|
||||
}
|
||||
const content = (0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('mvhd'),
|
||||
// version
|
||||
new Uint8Array([0]),
|
||||
// flags
|
||||
new Uint8Array([0, 0, 0]),
|
||||
// creation time
|
||||
creationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(creationTime)),
|
||||
// modification time
|
||||
modificationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(modificationTime)),
|
||||
// timescale
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(timescale),
|
||||
// duration in units, 32bit for version 0
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(durationInUnits),
|
||||
// rate
|
||||
(0, primitives_1.floatTo16Point1632Bit)(rate),
|
||||
// volume
|
||||
(0, primitives_1.floatTo16Point16_16Bit)(volume),
|
||||
// reserved
|
||||
new Uint8Array([0, 0]),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
(0, primitives_1.serializeMatrix)(matrix),
|
||||
// predefined
|
||||
(0, matroska_utils_1.combineUint8Arrays)(new Array(6).fill(new Uint8Array([0, 0, 0, 0]))),
|
||||
// next track id
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(nextTrackId),
|
||||
]);
|
||||
return (0, primitives_1.addSize)(content);
|
||||
};
|
||||
exports.createMvhd = createMvhd;
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export declare const createTrak: ({ tkhd, mdia, }: {
|
||||
tkhd: Uint8Array;
|
||||
mdia: Uint8Array;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createTrak = void 0;
|
||||
const truthy_1 = require("../../truthy");
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createTrak = ({ tkhd, mdia, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// name
|
||||
(0, primitives_1.stringsToUint8Array)('trak'),
|
||||
// tkhd
|
||||
tkhd,
|
||||
// mdia
|
||||
mdia,
|
||||
].filter(truthy_1.truthy)));
|
||||
};
|
||||
exports.createTrak = createTrak;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const createUdta: (children: Uint8Array) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createUdta = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createUdta = (children) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('udta'),
|
||||
// children
|
||||
children,
|
||||
]));
|
||||
};
|
||||
exports.createUdta = createUdta;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const createUrlAtom: () => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createUrlAtom = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const primitives_1 = require("./primitives");
|
||||
const createUrlAtom = () => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('url '),
|
||||
// version
|
||||
new Uint8Array([0]),
|
||||
// flags
|
||||
new Uint8Array([0, 0, 1]),
|
||||
]));
|
||||
};
|
||||
exports.createUrlAtom = createUrlAtom;
|
||||
Generated
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import type { MediaParserInternalTypes } from '@remotion/media-parser';
|
||||
export declare const exampleVideoSamplePositions: MediaParserInternalTypes['SamplePosition'][];
|
||||
export declare const exampleAudioSamplePositions: MediaParserInternalTypes['SamplePosition'][];
|
||||
Generated
Vendored
+3417
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export declare const calculateAReasonableMp4HeaderLength: ({ expectedDurationInSeconds, expectedFrameRate, }: {
|
||||
expectedDurationInSeconds: number | null;
|
||||
expectedFrameRate: number | null;
|
||||
}) => number;
|
||||
Generated
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.calculateAReasonableMp4HeaderLength = void 0;
|
||||
const calculateAReasonableMp4HeaderLength = ({ expectedDurationInSeconds, expectedFrameRate, }) => {
|
||||
if (expectedDurationInSeconds === null) {
|
||||
return 2048000;
|
||||
}
|
||||
/**
|
||||
* we had a video that was 1 hour 40 minutes long and the header ended up being 3.7MB. the header approximately grows linearly to the video length in seconds, but we should reserve enough, like at least 50KB in any case.
|
||||
* it's better to be safe than to fail, so let's add a 30% safety margin
|
||||
*/
|
||||
// This was however with 30fps, and there can also be 60fps videos, so let's
|
||||
// estimate even more conservatively and estimate 60fps
|
||||
const assumedFrameRate = expectedFrameRate ?? 60;
|
||||
const frameRateMultiplier = assumedFrameRate / 30;
|
||||
// 1h40m = 6000 seconds resulted in 3.7MB header
|
||||
// So bytes per second = 3.7MB / 6000 = ~616 bytes/second
|
||||
const bytesPerSecond = (3.7 * 1024 * 1024) / 6000;
|
||||
// Add 20% safety margin and multiply by frame rate multiplier
|
||||
const bytesWithSafetyMargin = bytesPerSecond * 1.2 * frameRateMultiplier;
|
||||
// Calculate based on duration, with minimum 50KB
|
||||
const calculatedBytes = Math.max(50 * 1024, Math.ceil(expectedDurationInSeconds * bytesWithSafetyMargin));
|
||||
return calculatedBytes;
|
||||
};
|
||||
exports.calculateAReasonableMp4HeaderLength = calculateAReasonableMp4HeaderLength;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const createCmt: (comment: string) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createCmt = void 0;
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
const createCmt = (comment) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// ©cmt
|
||||
new Uint8Array([0xa9, 0x63, 0x6d, 0x74]),
|
||||
(0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// data
|
||||
(0, primitives_1.stringsToUint8Array)('data'),
|
||||
// type indicator
|
||||
new Uint8Array([0, 0]),
|
||||
// well-known type
|
||||
new Uint8Array([0, 1]),
|
||||
// country indicator
|
||||
new Uint8Array([0, 0]),
|
||||
// language indicator
|
||||
new Uint8Array([0, 0]),
|
||||
// value
|
||||
(0, primitives_1.stringsToUint8Array)(comment),
|
||||
])),
|
||||
]));
|
||||
};
|
||||
exports.createCmt = createCmt;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const createToo: (value: string) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createToo = void 0;
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
const createToo = (value) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type: ©too
|
||||
new Uint8Array([0xa9, 0x74, 0x6f, 0x6f]),
|
||||
// data
|
||||
(0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// data
|
||||
new Uint8Array([0x64, 0x61, 0x74, 0x61]),
|
||||
// type indicator
|
||||
new Uint8Array([0, 0]),
|
||||
// well-known type
|
||||
new Uint8Array([0, 1]),
|
||||
// country indicator
|
||||
new Uint8Array([0, 0]),
|
||||
// language indicator
|
||||
new Uint8Array([0, 0]),
|
||||
// value
|
||||
(0, primitives_1.stringsToUint8Array)(value),
|
||||
])),
|
||||
]));
|
||||
};
|
||||
exports.createToo = createToo;
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
export declare const createMdhd: ({ creationTime, modificationTime, timescale, duration, }: {
|
||||
creationTime: number | null;
|
||||
modificationTime: number | null;
|
||||
timescale: number;
|
||||
duration: number;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMdhd = void 0;
|
||||
const from_unix_timestamp_1 = require("../../../from-unix-timestamp");
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
const createMdhd = ({ creationTime, modificationTime, timescale, duration, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('mdhd'),
|
||||
// version
|
||||
new Uint8Array([0]),
|
||||
// flags
|
||||
new Uint8Array([0, 0, 0]),
|
||||
// creation time
|
||||
creationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(creationTime)),
|
||||
// modification time
|
||||
modificationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(modificationTime)),
|
||||
// timescale
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(timescale),
|
||||
// duration
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(Math.round((duration / 1000) * timescale)),
|
||||
// language: unspecified
|
||||
new Uint8Array([0x55, 0xc4]),
|
||||
// quality
|
||||
new Uint8Array([0, 0]),
|
||||
]));
|
||||
};
|
||||
exports.createMdhd = createMdhd;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import type { MediaParserLogLevel } from '@remotion/media-parser';
|
||||
import type { IsoBaseMediaTrackData } from './serialize-track';
|
||||
export declare const createPaddedMoovAtom: ({ durationInUnits, trackInfo, timescale, expectedDurationInSeconds, logLevel, expectedFrameRate, }: {
|
||||
durationInUnits: number;
|
||||
trackInfo: IsoBaseMediaTrackData[];
|
||||
timescale: number;
|
||||
expectedDurationInSeconds: number | null;
|
||||
logLevel: MediaParserLogLevel;
|
||||
expectedFrameRate: number | null;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createPaddedMoovAtom = void 0;
|
||||
const media_parser_1 = require("@remotion/media-parser");
|
||||
const log_1 = require("../../log");
|
||||
const create_ilst_1 = require("./create-ilst");
|
||||
const create_moov_1 = require("./create-moov");
|
||||
const create_mvhd_1 = require("./create-mvhd");
|
||||
const create_udta_1 = require("./create-udta");
|
||||
const header_length_1 = require("./header-length");
|
||||
const create_cmt_1 = require("./ilst/create-cmt");
|
||||
const create_too_1 = require("./ilst/create-too");
|
||||
const primitives_1 = require("./primitives");
|
||||
const serialize_track_1 = require("./serialize-track");
|
||||
const create_meta_1 = require("./udta/create-meta");
|
||||
const create_hdlr_1 = require("./udta/meta/create-hdlr");
|
||||
const createPaddedMoovAtom = ({ durationInUnits, trackInfo, timescale, expectedDurationInSeconds, logLevel, expectedFrameRate, }) => {
|
||||
const headerLength = (0, header_length_1.calculateAReasonableMp4HeaderLength)({
|
||||
expectedDurationInSeconds,
|
||||
expectedFrameRate,
|
||||
});
|
||||
if (expectedDurationInSeconds !== null) {
|
||||
log_1.Log.verbose(logLevel, `Expecting duration of the video to be ${expectedDurationInSeconds} seconds, allocating ${headerLength} bytes for the MP4 header.`);
|
||||
}
|
||||
else {
|
||||
log_1.Log.verbose(logLevel, `No duration was provided, allocating ${headerLength} bytes for the MP4 header.`);
|
||||
}
|
||||
return (0, primitives_1.padIsoBaseMediaBytes)((0, create_moov_1.createMoov)({
|
||||
mvhd: (0, create_mvhd_1.createMvhd)({
|
||||
timescale,
|
||||
durationInUnits,
|
||||
matrix: primitives_1.IDENTITY_MATRIX,
|
||||
nextTrackId: trackInfo
|
||||
.map((t) => t.track.trackNumber)
|
||||
.reduce((a, b) => Math.max(a, b), 0) + 1,
|
||||
rate: 1,
|
||||
volume: 1,
|
||||
creationTime: Date.now(),
|
||||
modificationTime: Date.now(),
|
||||
}),
|
||||
traks: trackInfo.map((track) => {
|
||||
return (0, serialize_track_1.serializeTrack)({
|
||||
timescale,
|
||||
track: track.track,
|
||||
durationInUnits,
|
||||
samplePositions: track.samplePositions,
|
||||
});
|
||||
}),
|
||||
udta: (0, create_udta_1.createUdta)((0, create_meta_1.createMeta)({
|
||||
hdlr: (0, create_hdlr_1.createHdlr)('mdir'),
|
||||
ilst: (0, create_ilst_1.createIlst)([
|
||||
(0, create_too_1.createToo)('WebCodecs'),
|
||||
(0, create_cmt_1.createCmt)(`Made with @remotion/webcodecs ${media_parser_1.VERSION}`),
|
||||
]),
|
||||
})),
|
||||
}), headerLength);
|
||||
};
|
||||
exports.createPaddedMoovAtom = createPaddedMoovAtom;
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
export declare const stringsToUint8Array: (str: string) => Uint8Array<ArrayBuffer>;
|
||||
export declare const numberTo32BitUIntOrInt: (num: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const numberTo64BitUIntOrInt: (num: number | bigint) => Uint8Array<ArrayBuffer>;
|
||||
export declare const numberTo32BitUIntOrIntLeading128: (num: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const numberTo16BitUIntOrInt: (num: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const setFixedPointSignedOrUnsigned1616Number: (num: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const setFixedPointSigned230Number: (num: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const addSize: (arr: Uint8Array) => Uint8Array<ArrayBufferLike>;
|
||||
export declare const addLeading128Size: (arr: Uint8Array) => Uint8Array<ArrayBufferLike>;
|
||||
export declare const floatTo16Point1632Bit: (number: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const floatTo16Point16_16Bit: (number: number) => Uint8Array<ArrayBuffer>;
|
||||
export declare const serializeMatrix: (matrix: number[]) => Uint8Array<ArrayBufferLike>;
|
||||
export declare const stringToPascalString: (str: string) => Uint8Array<ArrayBuffer>;
|
||||
export declare const padIsoBaseMediaBytes: (data: Uint8Array, totalLength: number) => Uint8Array<ArrayBufferLike>;
|
||||
type ThreeDMatrix = [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number
|
||||
];
|
||||
export declare const IDENTITY_MATRIX: ThreeDMatrix;
|
||||
export {};
|
||||
Generated
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.IDENTITY_MATRIX = exports.padIsoBaseMediaBytes = exports.stringToPascalString = exports.serializeMatrix = exports.floatTo16Point16_16Bit = exports.floatTo16Point1632Bit = exports.addLeading128Size = exports.addSize = exports.setFixedPointSigned230Number = exports.setFixedPointSignedOrUnsigned1616Number = exports.numberTo16BitUIntOrInt = exports.numberTo32BitUIntOrIntLeading128 = exports.numberTo64BitUIntOrInt = exports.numberTo32BitUIntOrInt = exports.stringsToUint8Array = void 0;
|
||||
const matroska_utils_1 = require("../matroska/matroska-utils");
|
||||
const stringsToUint8Array = (str) => {
|
||||
return new TextEncoder().encode(str);
|
||||
};
|
||||
exports.stringsToUint8Array = stringsToUint8Array;
|
||||
const numberTo32BitUIntOrInt = (num) => {
|
||||
return new Uint8Array([
|
||||
(num >> 24) & 0xff,
|
||||
(num >> 16) & 0xff,
|
||||
(num >> 8) & 0xff,
|
||||
num & 0xff,
|
||||
]);
|
||||
};
|
||||
exports.numberTo32BitUIntOrInt = numberTo32BitUIntOrInt;
|
||||
const numberTo64BitUIntOrInt = (num) => {
|
||||
const bigNum = BigInt(num);
|
||||
return new Uint8Array([
|
||||
Number((bigNum >> 56n) & 0xffn),
|
||||
Number((bigNum >> 48n) & 0xffn),
|
||||
Number((bigNum >> 40n) & 0xffn),
|
||||
Number((bigNum >> 32n) & 0xffn),
|
||||
Number((bigNum >> 24n) & 0xffn),
|
||||
Number((bigNum >> 16n) & 0xffn),
|
||||
Number((bigNum >> 8n) & 0xffn),
|
||||
Number(bigNum & 0xffn),
|
||||
]);
|
||||
};
|
||||
exports.numberTo64BitUIntOrInt = numberTo64BitUIntOrInt;
|
||||
const numberTo32BitUIntOrIntLeading128 = (num) => {
|
||||
const arr = [
|
||||
(num >> 24) & 0xff,
|
||||
(num >> 16) & 0xff,
|
||||
(num >> 8) & 0xff,
|
||||
num & 0xff,
|
||||
];
|
||||
for (const i in arr) {
|
||||
if (arr[i] === 0) {
|
||||
arr[i] = 128;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new Uint8Array(arr);
|
||||
};
|
||||
exports.numberTo32BitUIntOrIntLeading128 = numberTo32BitUIntOrIntLeading128;
|
||||
const numberTo16BitUIntOrInt = (num) => {
|
||||
return new Uint8Array([(num >> 8) & 0xff, num & 0xff]);
|
||||
};
|
||||
exports.numberTo16BitUIntOrInt = numberTo16BitUIntOrInt;
|
||||
const setFixedPointSignedOrUnsigned1616Number = (num) => {
|
||||
const val = Math.round(num * 2 ** 16);
|
||||
return (0, exports.numberTo32BitUIntOrInt)(val);
|
||||
};
|
||||
exports.setFixedPointSignedOrUnsigned1616Number = setFixedPointSignedOrUnsigned1616Number;
|
||||
const setFixedPointSigned230Number = (num) => {
|
||||
const val = Math.round(num * 2 ** 30);
|
||||
return (0, exports.numberTo32BitUIntOrInt)(val);
|
||||
};
|
||||
exports.setFixedPointSigned230Number = setFixedPointSigned230Number;
|
||||
const addSize = (arr) => {
|
||||
return (0, matroska_utils_1.combineUint8Arrays)([(0, exports.numberTo32BitUIntOrInt)(arr.length + 4), arr]);
|
||||
};
|
||||
exports.addSize = addSize;
|
||||
const addLeading128Size = (arr) => {
|
||||
return (0, matroska_utils_1.combineUint8Arrays)([
|
||||
(0, exports.numberTo32BitUIntOrIntLeading128)(arr.length),
|
||||
arr,
|
||||
]);
|
||||
};
|
||||
exports.addLeading128Size = addLeading128Size;
|
||||
const floatTo16Point1632Bit = (number) => {
|
||||
// Ensure the number has exactly 2 decimal places
|
||||
const fixedNumber = Number(number.toFixed(2));
|
||||
// Create a new Uint8Array of 4 bytes
|
||||
const result = new Uint8Array(4);
|
||||
// Extract digits
|
||||
const tens = Math.floor(fixedNumber / 10);
|
||||
const ones = Math.floor(fixedNumber % 10);
|
||||
const tenths = Math.floor((fixedNumber * 10) % 10);
|
||||
const hundredths = Math.floor((fixedNumber * 100) % 10);
|
||||
// Assign to array
|
||||
result[0] = tens;
|
||||
result[1] = ones;
|
||||
result[2] = tenths;
|
||||
result[3] = hundredths;
|
||||
return result;
|
||||
};
|
||||
exports.floatTo16Point1632Bit = floatTo16Point1632Bit;
|
||||
const floatTo16Point16_16Bit = (number) => {
|
||||
// Ensure the number has exactly 2 decimal places
|
||||
const fixedNumber = Number(number.toFixed(2));
|
||||
// Create a new Uint8Array of 4 bytes
|
||||
const result = new Uint8Array(2);
|
||||
// Extract digits
|
||||
const ones = Math.floor(fixedNumber % 10);
|
||||
const tenths = Math.floor((fixedNumber * 10) % 10);
|
||||
// Assign to array
|
||||
result[0] = ones;
|
||||
result[1] = tenths;
|
||||
return result;
|
||||
};
|
||||
exports.floatTo16Point16_16Bit = floatTo16Point16_16Bit;
|
||||
const serializeMatrix = (matrix) => {
|
||||
return (0, matroska_utils_1.combineUint8Arrays)([
|
||||
(0, exports.setFixedPointSignedOrUnsigned1616Number)(matrix[0]),
|
||||
(0, exports.setFixedPointSignedOrUnsigned1616Number)(matrix[1]),
|
||||
(0, exports.setFixedPointSigned230Number)(matrix[2]),
|
||||
(0, exports.setFixedPointSignedOrUnsigned1616Number)(matrix[3]),
|
||||
(0, exports.setFixedPointSignedOrUnsigned1616Number)(matrix[4]),
|
||||
(0, exports.setFixedPointSigned230Number)(matrix[5]),
|
||||
(0, exports.setFixedPointSignedOrUnsigned1616Number)(matrix[6]),
|
||||
(0, exports.setFixedPointSignedOrUnsigned1616Number)(matrix[7]),
|
||||
(0, exports.setFixedPointSigned230Number)(matrix[8]),
|
||||
]);
|
||||
};
|
||||
exports.serializeMatrix = serializeMatrix;
|
||||
const stringToPascalString = (str) => {
|
||||
// Create a fixed 32-byte Uint8Array
|
||||
const buffer = new Uint8Array(32);
|
||||
// Convert the string characters to bytes
|
||||
for (let i = 0; i < Math.min(str.length, 32); i++) {
|
||||
buffer[i] = str.charCodeAt(i);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
exports.stringToPascalString = stringToPascalString;
|
||||
const padIsoBaseMediaBytes = (data, totalLength) => {
|
||||
if (data.length - 8 > totalLength) {
|
||||
throw new Error(`Data is longer than the total length: ${data.length - 8} > ${totalLength}. Set the 'expectedDurationInSeconds' value to avoid this problem: https://www.remotion.dev/docs/webcodecs/convert-media#expecteddurationinseconds`);
|
||||
}
|
||||
if (data.length - 8 === totalLength) {
|
||||
return data;
|
||||
}
|
||||
return (0, matroska_utils_1.combineUint8Arrays)([
|
||||
data,
|
||||
(0, exports.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
(0, exports.stringsToUint8Array)('free'),
|
||||
new Uint8Array(totalLength - (data.length - 8)),
|
||||
])),
|
||||
]);
|
||||
};
|
||||
exports.padIsoBaseMediaBytes = padIsoBaseMediaBytes;
|
||||
exports.IDENTITY_MATRIX = [1, 0, 0, 0, 1, 0, 0, 0, 1];
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import type { MediaParserInternalTypes } from '@remotion/media-parser';
|
||||
import type { MakeTrackAudio, MakeTrackVideo } from '../make-track-info';
|
||||
export type IsoBaseMediaTrackData = {
|
||||
track: MakeTrackVideo | MakeTrackAudio;
|
||||
durationInUnits: number;
|
||||
samplePositions: MediaParserInternalTypes['SamplePosition'][];
|
||||
timescale: number;
|
||||
};
|
||||
export declare const serializeTrack: ({ track, durationInUnits, samplePositions, timescale, }: IsoBaseMediaTrackData) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.serializeTrack = void 0;
|
||||
const create_codec_specific_data_1 = require("./codec-specific/create-codec-specific-data");
|
||||
const create_mdia_1 = require("./create-mdia");
|
||||
const create_trak_1 = require("./create-trak");
|
||||
const create_mdhd_1 = require("./mdia/create-mdhd");
|
||||
const primitives_1 = require("./primitives");
|
||||
const create_tkhd_1 = require("./trak/create-tkhd");
|
||||
const create_minf_1 = require("./trak/mdia/create-minf");
|
||||
const create_smhd_1 = require("./trak/mdia/minf/create-smhd");
|
||||
const create_stbl_1 = require("./trak/mdia/minf/create-stbl");
|
||||
const create_vmhd_1 = require("./trak/mdia/minf/create-vmhd");
|
||||
const create_hdlr_1 = require("./udta/meta/create-hdlr");
|
||||
const serializeTrack = ({ track, durationInUnits, samplePositions, timescale, }) => {
|
||||
if (track.codec !== 'h264' &&
|
||||
track.codec !== 'h265' &&
|
||||
track.codec !== 'aac') {
|
||||
throw new Error('Currently only H.264 and AAC is supported');
|
||||
}
|
||||
return (0, create_trak_1.createTrak)({
|
||||
tkhd: track.codec === 'aac'
|
||||
? (0, create_tkhd_1.createTkhdForAudio)({
|
||||
creationTime: Date.now(),
|
||||
flags: create_tkhd_1.TKHD_FLAGS.TRACK_ENABLED | create_tkhd_1.TKHD_FLAGS.TRACK_IN_MOVIE,
|
||||
modificationTime: Date.now(),
|
||||
duration: durationInUnits,
|
||||
trackId: track.trackNumber,
|
||||
volume: 1,
|
||||
timescale,
|
||||
})
|
||||
: track.type === 'video'
|
||||
? (0, create_tkhd_1.createTkhdForVideo)({
|
||||
creationTime: Date.now(),
|
||||
modificationTime: Date.now(),
|
||||
duration: durationInUnits,
|
||||
flags: create_tkhd_1.TKHD_FLAGS.TRACK_ENABLED | create_tkhd_1.TKHD_FLAGS.TRACK_IN_MOVIE,
|
||||
height: track.height,
|
||||
width: track.width,
|
||||
matrix: primitives_1.IDENTITY_MATRIX,
|
||||
trackId: track.trackNumber,
|
||||
volume: 0,
|
||||
timescale,
|
||||
})
|
||||
: new Uint8Array((0, primitives_1.stringsToUint8Array)('wrong')),
|
||||
mdia: (0, create_mdia_1.createMdia)({
|
||||
mdhd: (0, create_mdhd_1.createMdhd)({
|
||||
creationTime: null,
|
||||
modificationTime: null,
|
||||
duration: durationInUnits,
|
||||
timescale: track.timescale,
|
||||
}),
|
||||
hdlr: track.type === 'video' ? (0, create_hdlr_1.createHdlr)('video') : (0, create_hdlr_1.createHdlr)('audio'),
|
||||
minf: (0, create_minf_1.createMinf)({
|
||||
stblAtom: (0, create_stbl_1.createStbl)({
|
||||
samplePositions,
|
||||
isVideo: track.type === 'video',
|
||||
codecSpecificData: (0, create_codec_specific_data_1.createCodecSpecificData)(track),
|
||||
}),
|
||||
vmhdAtom: track.type === 'audio' ? (0, create_smhd_1.createSmhd)() : (0, create_vmhd_1.createVmhd)(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
exports.serializeTrack = serializeTrack;
|
||||
Generated
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
export declare const TKHD_FLAGS: {
|
||||
TRACK_ENABLED: number;
|
||||
TRACK_IN_MOVIE: number;
|
||||
TRACK_IN_PREVIEW: number;
|
||||
TRACK_IN_POSTER: number;
|
||||
};
|
||||
export declare const createTkhdForAudio: ({ creationTime, modificationTime, flags, trackId, duration, volume, timescale, }: {
|
||||
creationTime: number | null;
|
||||
modificationTime: number | null;
|
||||
flags: number;
|
||||
trackId: number;
|
||||
duration: number;
|
||||
volume: number;
|
||||
timescale: number;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
export declare const createTkhdForVideo: ({ creationTime, modificationTime, duration, trackId, volume, matrix, width, height, flags, timescale, }: {
|
||||
creationTime: number | null;
|
||||
modificationTime: number | null;
|
||||
trackId: number;
|
||||
duration: number;
|
||||
volume: number;
|
||||
matrix: number[];
|
||||
width: number;
|
||||
height: number;
|
||||
flags: number;
|
||||
timescale: number;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createTkhdForVideo = exports.createTkhdForAudio = exports.TKHD_FLAGS = void 0;
|
||||
const from_unix_timestamp_1 = require("../../../from-unix-timestamp");
|
||||
const matroska_utils_1 = require("../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../primitives");
|
||||
exports.TKHD_FLAGS = {
|
||||
TRACK_ENABLED: 0x000001,
|
||||
TRACK_IN_MOVIE: 0x000002,
|
||||
TRACK_IN_PREVIEW: 0x000004,
|
||||
TRACK_IN_POSTER: 0x000008,
|
||||
};
|
||||
const createTkhdForAudio = ({ creationTime, modificationTime, flags, trackId, duration, volume, timescale, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// name
|
||||
(0, primitives_1.stringsToUint8Array)('tkhd'),
|
||||
// version
|
||||
new Uint8Array([0]),
|
||||
// flags
|
||||
new Uint8Array([0, 0, flags]),
|
||||
// creation time
|
||||
creationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(creationTime)),
|
||||
// modification time
|
||||
modificationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(modificationTime)),
|
||||
// trackId
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(trackId),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// duration
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(Math.round((duration / 1000) * timescale)),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// layer
|
||||
new Uint8Array([0, 0]),
|
||||
// alternate group, 1 = 'sound'
|
||||
new Uint8Array([0, 1]),
|
||||
// volume
|
||||
(0, primitives_1.floatTo16Point16_16Bit)(volume),
|
||||
// reserved
|
||||
new Uint8Array([0, 0]),
|
||||
// matrix
|
||||
(0, primitives_1.serializeMatrix)(primitives_1.IDENTITY_MATRIX),
|
||||
// width
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(0),
|
||||
// height
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(0),
|
||||
]));
|
||||
};
|
||||
exports.createTkhdForAudio = createTkhdForAudio;
|
||||
const createTkhdForVideo = ({ creationTime, modificationTime, duration, trackId, volume, matrix, width, height, flags, timescale, }) => {
|
||||
const content = (0, matroska_utils_1.combineUint8Arrays)([
|
||||
// name
|
||||
(0, primitives_1.stringsToUint8Array)('tkhd'),
|
||||
// version
|
||||
new Uint8Array([0]),
|
||||
// flags
|
||||
new Uint8Array([0, 0, flags]),
|
||||
// creation time
|
||||
creationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(creationTime)),
|
||||
// modification time
|
||||
modificationTime === null
|
||||
? (0, primitives_1.numberTo32BitUIntOrInt)(0)
|
||||
: (0, primitives_1.numberTo32BitUIntOrInt)((0, from_unix_timestamp_1.fromUnixTimestamp)(modificationTime)),
|
||||
// trackId
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)(trackId),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// duration
|
||||
(0, primitives_1.numberTo32BitUIntOrInt)((duration / 1000) * timescale),
|
||||
// reserved
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
new Uint8Array([0, 0, 0, 0]),
|
||||
// layer
|
||||
new Uint8Array([0, 0]),
|
||||
// alternate group, 0 = 'video'
|
||||
new Uint8Array([0, 0]),
|
||||
// volume
|
||||
(0, primitives_1.floatTo16Point16_16Bit)(volume),
|
||||
// reserved
|
||||
new Uint8Array([0, 0]),
|
||||
// matrix
|
||||
(0, primitives_1.serializeMatrix)(matrix),
|
||||
// width
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(width),
|
||||
// height
|
||||
(0, primitives_1.setFixedPointSignedOrUnsigned1616Number)(height),
|
||||
]);
|
||||
return (0, primitives_1.addSize)(content);
|
||||
};
|
||||
exports.createTkhdForVideo = createTkhdForVideo;
|
||||
Generated
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
export declare const createMinf: ({ vmhdAtom, stblAtom, }: {
|
||||
vmhdAtom: Uint8Array;
|
||||
stblAtom: Uint8Array;
|
||||
}) => Uint8Array<ArrayBufferLike>;
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createMinf = void 0;
|
||||
const matroska_utils_1 = require("../../../matroska/matroska-utils");
|
||||
const primitives_1 = require("../../primitives");
|
||||
const create_dinf_1 = require("./minf/create-dinf");
|
||||
const createMinf = ({ vmhdAtom, stblAtom, }) => {
|
||||
return (0, primitives_1.addSize)((0, matroska_utils_1.combineUint8Arrays)([
|
||||
// type
|
||||
(0, primitives_1.stringsToUint8Array)('minf'),
|
||||
// vmhd
|
||||
vmhdAtom,
|
||||
// dinf
|
||||
(0, create_dinf_1.createDinf)(),
|
||||
// stbl
|
||||
stblAtom,
|
||||
]));
|
||||
};
|
||||
exports.createMinf = createMinf;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user