@remotion/media 4.0.374 → 4.0.375
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio-extraction/extract-audio.js +15 -8
- package/dist/convert-audiodata/convert-audiodata.js +5 -4
- package/dist/esm/index.mjs +68 -18
- package/dist/extract-frame-and-audio.js +13 -1
- package/dist/video/video-for-rendering.js +2 -8
- package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +1 -1
- package/dist/video-extraction/extract-frame-via-broadcast-channel.js +3 -7
- package/package.json +4 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { audioManager } from '../caches';
|
|
2
2
|
import { combineAudioDataAndClosePrevious } from '../convert-audiodata/combine-audiodata';
|
|
3
|
-
import { convertAudioData } from '../convert-audiodata/convert-audiodata';
|
|
3
|
+
import { convertAudioData, fixFloatingPoint, } from '../convert-audiodata/convert-audiodata';
|
|
4
|
+
import { TARGET_NUMBER_OF_CHANNELS, TARGET_SAMPLE_RATE, } from '../convert-audiodata/resample-audiodata';
|
|
4
5
|
import { getSink } from '../get-sink';
|
|
5
6
|
import { getTimeInSeconds } from '../get-time-in-seconds';
|
|
6
7
|
const extractAudioInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds, durationInSeconds: durationNotYetApplyingPlaybackRate, logLevel, loop, playbackRate, audioStreamIndex, trimBefore, trimAfter, fps, }) => {
|
|
@@ -44,7 +45,6 @@ const extractAudioInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
44
45
|
const durationInSeconds = durationNotYetApplyingPlaybackRate * playbackRate;
|
|
45
46
|
const samples = await sampleIterator.getSamples(timeInSeconds, durationInSeconds);
|
|
46
47
|
audioManager.logOpenFrames();
|
|
47
|
-
const trimStartToleranceInSeconds = 0.002;
|
|
48
48
|
const audioDataArray = [];
|
|
49
49
|
for (let i = 0; i < samples.length; i++) {
|
|
50
50
|
const sample = samples[i];
|
|
@@ -64,14 +64,18 @@ const extractAudioInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
64
64
|
// amount of samples to shave from start and end
|
|
65
65
|
let trimStartInSeconds = 0;
|
|
66
66
|
let trimEndInSeconds = 0;
|
|
67
|
+
let leadingSilence = null;
|
|
67
68
|
if (isFirstSample) {
|
|
68
|
-
trimStartInSeconds = timeInSeconds - sample.timestamp;
|
|
69
|
-
if (trimStartInSeconds < 0 &&
|
|
70
|
-
trimStartInSeconds > -trimStartToleranceInSeconds) {
|
|
71
|
-
trimStartInSeconds = 0;
|
|
72
|
-
}
|
|
69
|
+
trimStartInSeconds = fixFloatingPoint(timeInSeconds - sample.timestamp);
|
|
73
70
|
if (trimStartInSeconds < 0) {
|
|
74
|
-
|
|
71
|
+
const silenceFrames = Math.ceil(fixFloatingPoint(-trimStartInSeconds * TARGET_SAMPLE_RATE));
|
|
72
|
+
leadingSilence = {
|
|
73
|
+
data: new Int16Array(silenceFrames * TARGET_NUMBER_OF_CHANNELS),
|
|
74
|
+
numberOfFrames: silenceFrames,
|
|
75
|
+
timestamp: timeInSeconds * 1000000,
|
|
76
|
+
durationInMicroSeconds: (silenceFrames / TARGET_SAMPLE_RATE) * 1000000,
|
|
77
|
+
};
|
|
78
|
+
trimStartInSeconds = 0;
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
if (isLastSample) {
|
|
@@ -93,6 +97,9 @@ const extractAudioInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
93
97
|
if (audioData.numberOfFrames === 0) {
|
|
94
98
|
continue;
|
|
95
99
|
}
|
|
100
|
+
if (leadingSilence) {
|
|
101
|
+
audioDataArray.push(leadingSilence);
|
|
102
|
+
}
|
|
96
103
|
audioDataArray.push(audioData);
|
|
97
104
|
}
|
|
98
105
|
if (audioDataArray.length === 0) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { resampleAudioData, TARGET_NUMBER_OF_CHANNELS, TARGET_SAMPLE_RATE, } from './resample-audiodata';
|
|
2
2
|
const FORMAT = 's16';
|
|
3
3
|
export const fixFloatingPoint = (value) => {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const decimal = Math.abs(value % 1);
|
|
5
|
+
if (decimal < 0.0000001) {
|
|
6
|
+
return value < 0 ? Math.ceil(value) : Math.floor(value);
|
|
6
7
|
}
|
|
7
|
-
if (
|
|
8
|
-
return Math.ceil(value);
|
|
8
|
+
if (decimal > 0.9999999) {
|
|
9
|
+
return value < 0 ? Math.floor(value) : Math.ceil(value);
|
|
9
10
|
}
|
|
10
11
|
return value;
|
|
11
12
|
};
|
package/dist/esm/index.mjs
CHANGED
|
@@ -2733,11 +2733,12 @@ var getMaxVideoCacheSize = (logLevel) => {
|
|
|
2733
2733
|
// src/convert-audiodata/convert-audiodata.ts
|
|
2734
2734
|
var FORMAT = "s16";
|
|
2735
2735
|
var fixFloatingPoint2 = (value) => {
|
|
2736
|
-
|
|
2737
|
-
|
|
2736
|
+
const decimal = Math.abs(value % 1);
|
|
2737
|
+
if (decimal < 0.0000001) {
|
|
2738
|
+
return value < 0 ? Math.ceil(value) : Math.floor(value);
|
|
2738
2739
|
}
|
|
2739
|
-
if (
|
|
2740
|
-
return Math.ceil(value);
|
|
2740
|
+
if (decimal > 0.9999999) {
|
|
2741
|
+
return value < 0 ? Math.floor(value) : Math.ceil(value);
|
|
2741
2742
|
}
|
|
2742
2743
|
return value;
|
|
2743
2744
|
};
|
|
@@ -2892,7 +2893,6 @@ var extractAudioInternal = async ({
|
|
|
2892
2893
|
const durationInSeconds = durationNotYetApplyingPlaybackRate * playbackRate;
|
|
2893
2894
|
const samples = await sampleIterator.getSamples(timeInSeconds, durationInSeconds);
|
|
2894
2895
|
audioManager.logOpenFrames();
|
|
2895
|
-
const trimStartToleranceInSeconds = 0.002;
|
|
2896
2896
|
const audioDataArray = [];
|
|
2897
2897
|
for (let i = 0;i < samples.length; i++) {
|
|
2898
2898
|
const sample = samples[i];
|
|
@@ -2907,13 +2907,18 @@ var extractAudioInternal = async ({
|
|
|
2907
2907
|
const audioDataRaw = sample.toAudioData();
|
|
2908
2908
|
let trimStartInSeconds = 0;
|
|
2909
2909
|
let trimEndInSeconds = 0;
|
|
2910
|
+
let leadingSilence = null;
|
|
2910
2911
|
if (isFirstSample) {
|
|
2911
|
-
trimStartInSeconds = timeInSeconds - sample.timestamp;
|
|
2912
|
-
if (trimStartInSeconds < 0 && trimStartInSeconds > -trimStartToleranceInSeconds) {
|
|
2913
|
-
trimStartInSeconds = 0;
|
|
2914
|
-
}
|
|
2912
|
+
trimStartInSeconds = fixFloatingPoint2(timeInSeconds - sample.timestamp);
|
|
2915
2913
|
if (trimStartInSeconds < 0) {
|
|
2916
|
-
|
|
2914
|
+
const silenceFrames = Math.ceil(fixFloatingPoint2(-trimStartInSeconds * TARGET_SAMPLE_RATE));
|
|
2915
|
+
leadingSilence = {
|
|
2916
|
+
data: new Int16Array(silenceFrames * TARGET_NUMBER_OF_CHANNELS),
|
|
2917
|
+
numberOfFrames: silenceFrames,
|
|
2918
|
+
timestamp: timeInSeconds * 1e6,
|
|
2919
|
+
durationInMicroSeconds: silenceFrames / TARGET_SAMPLE_RATE * 1e6
|
|
2920
|
+
};
|
|
2921
|
+
trimStartInSeconds = 0;
|
|
2917
2922
|
}
|
|
2918
2923
|
}
|
|
2919
2924
|
if (isLastSample) {
|
|
@@ -2931,6 +2936,9 @@ var extractAudioInternal = async ({
|
|
|
2931
2936
|
if (audioData.numberOfFrames === 0) {
|
|
2932
2937
|
continue;
|
|
2933
2938
|
}
|
|
2939
|
+
if (leadingSilence) {
|
|
2940
|
+
audioDataArray.push(leadingSilence);
|
|
2941
|
+
}
|
|
2934
2942
|
audioDataArray.push(audioData);
|
|
2935
2943
|
}
|
|
2936
2944
|
if (audioDataArray.length === 0) {
|
|
@@ -3018,6 +3026,39 @@ var extractFrame = (params) => {
|
|
|
3018
3026
|
return queue2;
|
|
3019
3027
|
};
|
|
3020
3028
|
|
|
3029
|
+
// src/video-extraction/rotate-frame.ts
|
|
3030
|
+
var rotateFrame = async ({
|
|
3031
|
+
frame,
|
|
3032
|
+
rotation
|
|
3033
|
+
}) => {
|
|
3034
|
+
if (rotation === 0) {
|
|
3035
|
+
const directBitmap = await createImageBitmap(frame);
|
|
3036
|
+
frame.close();
|
|
3037
|
+
return directBitmap;
|
|
3038
|
+
}
|
|
3039
|
+
const width = rotation === 90 || rotation === 270 ? frame.displayHeight : frame.displayWidth;
|
|
3040
|
+
const height = rotation === 90 || rotation === 270 ? frame.displayWidth : frame.displayHeight;
|
|
3041
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
3042
|
+
const ctx = canvas.getContext("2d");
|
|
3043
|
+
if (!ctx) {
|
|
3044
|
+
throw new Error("Could not get 2d context");
|
|
3045
|
+
}
|
|
3046
|
+
canvas.width = width;
|
|
3047
|
+
canvas.height = height;
|
|
3048
|
+
if (rotation === 90) {
|
|
3049
|
+
ctx.translate(width, 0);
|
|
3050
|
+
} else if (rotation === 180) {
|
|
3051
|
+
ctx.translate(width, height);
|
|
3052
|
+
} else if (rotation === 270) {
|
|
3053
|
+
ctx.translate(0, height);
|
|
3054
|
+
}
|
|
3055
|
+
ctx.rotate(rotation * (Math.PI / 180));
|
|
3056
|
+
ctx.drawImage(frame, 0, 0);
|
|
3057
|
+
const bitmap = await createImageBitmap(canvas);
|
|
3058
|
+
frame.close();
|
|
3059
|
+
return bitmap;
|
|
3060
|
+
};
|
|
3061
|
+
|
|
3021
3062
|
// src/extract-frame-and-audio.ts
|
|
3022
3063
|
var extractFrameAndAudio = async ({
|
|
3023
3064
|
src,
|
|
@@ -3088,9 +3129,20 @@ var extractFrameAndAudio = async ({
|
|
|
3088
3129
|
durationInSeconds: frame?.type === "success" ? frame.durationInSeconds : null
|
|
3089
3130
|
};
|
|
3090
3131
|
}
|
|
3132
|
+
if (!frame?.frame) {
|
|
3133
|
+
return {
|
|
3134
|
+
type: "success",
|
|
3135
|
+
frame: null,
|
|
3136
|
+
audio: audio?.data ?? null,
|
|
3137
|
+
durationInSeconds: audio?.durationInSeconds ?? null
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3091
3140
|
return {
|
|
3092
3141
|
type: "success",
|
|
3093
|
-
frame:
|
|
3142
|
+
frame: await rotateFrame({
|
|
3143
|
+
frame: frame.frame.toVideoFrame(),
|
|
3144
|
+
rotation: frame.frame.rotation
|
|
3145
|
+
}),
|
|
3094
3146
|
audio: audio?.data ?? null,
|
|
3095
3147
|
durationInSeconds: audio?.durationInSeconds ?? null
|
|
3096
3148
|
};
|
|
@@ -3158,10 +3210,9 @@ if (typeof window !== "undefined" && window.remotion_broadcastChannel && window.
|
|
|
3158
3210
|
return;
|
|
3159
3211
|
}
|
|
3160
3212
|
const { frame, audio, durationInSeconds } = result;
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
videoFrame.close();
|
|
3213
|
+
const imageBitmap = frame ? await createImageBitmap(frame) : null;
|
|
3214
|
+
if (frame) {
|
|
3215
|
+
frame.close();
|
|
3165
3216
|
}
|
|
3166
3217
|
const response = {
|
|
3167
3218
|
type: "response-success",
|
|
@@ -3171,7 +3222,6 @@ if (typeof window !== "undefined" && window.remotion_broadcastChannel && window.
|
|
|
3171
3222
|
durationInSeconds: durationInSeconds ?? null
|
|
3172
3223
|
};
|
|
3173
3224
|
window.remotion_broadcastChannel.postMessage(response);
|
|
3174
|
-
videoFrame?.close();
|
|
3175
3225
|
} catch (error) {
|
|
3176
3226
|
const response = {
|
|
3177
3227
|
type: "response-error",
|
|
@@ -4064,8 +4114,8 @@ var VideoForRendering = ({
|
|
|
4064
4114
|
if (!context) {
|
|
4065
4115
|
return;
|
|
4066
4116
|
}
|
|
4067
|
-
context.canvas.width = imageBitmap
|
|
4068
|
-
context.canvas.height = imageBitmap
|
|
4117
|
+
context.canvas.width = imageBitmap.width;
|
|
4118
|
+
context.canvas.height = imageBitmap.height;
|
|
4069
4119
|
context.canvas.style.aspectRatio = `${context.canvas.width} / ${context.canvas.height}`;
|
|
4070
4120
|
context.drawImage(imageBitmap, 0, 0);
|
|
4071
4121
|
imageBitmap.close();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { extractAudio } from './audio-extraction/extract-audio';
|
|
2
2
|
import { isNetworkError } from './is-network-error';
|
|
3
3
|
import { extractFrame } from './video-extraction/extract-frame';
|
|
4
|
+
import { rotateFrame } from './video-extraction/rotate-frame';
|
|
4
5
|
export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, }) => {
|
|
5
6
|
try {
|
|
6
7
|
const [frame, audio] = await Promise.all([
|
|
@@ -61,9 +62,20 @@ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durat
|
|
|
61
62
|
durationInSeconds: frame?.type === 'success' ? frame.durationInSeconds : null,
|
|
62
63
|
};
|
|
63
64
|
}
|
|
65
|
+
if (!frame?.frame) {
|
|
66
|
+
return {
|
|
67
|
+
type: 'success',
|
|
68
|
+
frame: null,
|
|
69
|
+
audio: audio?.data ?? null,
|
|
70
|
+
durationInSeconds: audio?.durationInSeconds ?? null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
64
73
|
return {
|
|
65
74
|
type: 'success',
|
|
66
|
-
frame:
|
|
75
|
+
frame: await rotateFrame({
|
|
76
|
+
frame: frame.frame.toVideoFrame(),
|
|
77
|
+
rotation: frame.frame.rotation,
|
|
78
|
+
}),
|
|
67
79
|
audio: audio?.data ?? null,
|
|
68
80
|
durationInSeconds: audio?.durationInSeconds ?? null,
|
|
69
81
|
};
|
|
@@ -120,14 +120,8 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
120
120
|
if (!context) {
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
|
-
context.canvas.width =
|
|
124
|
-
|
|
125
|
-
? imageBitmap.width
|
|
126
|
-
: imageBitmap.displayWidth;
|
|
127
|
-
context.canvas.height =
|
|
128
|
-
imageBitmap instanceof ImageBitmap
|
|
129
|
-
? imageBitmap.height
|
|
130
|
-
: imageBitmap.displayHeight;
|
|
123
|
+
context.canvas.width = imageBitmap.width;
|
|
124
|
+
context.canvas.height = imageBitmap.height;
|
|
131
125
|
context.canvas.style.aspectRatio = `${context.canvas.width} / ${context.canvas.height}`;
|
|
132
126
|
context.drawImage(imageBitmap, 0, 0);
|
|
133
127
|
imageBitmap.close();
|
|
@@ -2,7 +2,7 @@ import { type LogLevel } from 'remotion';
|
|
|
2
2
|
import type { PcmS16AudioData } from '../convert-audiodata/convert-audiodata';
|
|
3
3
|
export type ExtractFrameViaBroadcastChannelResult = {
|
|
4
4
|
type: 'success';
|
|
5
|
-
frame: ImageBitmap |
|
|
5
|
+
frame: ImageBitmap | null;
|
|
6
6
|
audio: PcmS16AudioData | null;
|
|
7
7
|
durationInSeconds: number | null;
|
|
8
8
|
} | {
|
|
@@ -56,12 +56,9 @@ if (typeof window !== 'undefined' &&
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
const { frame, audio, durationInSeconds } = result;
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
: null;
|
|
63
|
-
if (videoFrame) {
|
|
64
|
-
videoFrame.close();
|
|
59
|
+
const imageBitmap = frame ? await createImageBitmap(frame) : null;
|
|
60
|
+
if (frame) {
|
|
61
|
+
frame.close();
|
|
65
62
|
}
|
|
66
63
|
const response = {
|
|
67
64
|
type: 'response-success',
|
|
@@ -71,7 +68,6 @@ if (typeof window !== 'undefined' &&
|
|
|
71
68
|
durationInSeconds: durationInSeconds ?? null,
|
|
72
69
|
};
|
|
73
70
|
window.remotion_broadcastChannel.postMessage(response);
|
|
74
|
-
videoFrame?.close();
|
|
75
71
|
}
|
|
76
72
|
catch (error) {
|
|
77
73
|
const response = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/media",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.375",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"make": "tsc -d && bun --env-file=../.env.bundle bundle.ts"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"mediabunny": "1.24.
|
|
25
|
-
"remotion": "4.0.
|
|
24
|
+
"mediabunny": "1.24.5",
|
|
25
|
+
"remotion": "4.0.375"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": ">=16.8.0",
|
|
29
29
|
"react-dom": ">=16.8.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
32
|
+
"@remotion/eslint-config-internal": "4.0.375",
|
|
33
33
|
"@vitest/browser-webdriverio": "4.0.7",
|
|
34
34
|
"eslint": "9.19.0",
|
|
35
35
|
"react": "19.0.0",
|