@remotion/media 4.0.382 → 4.0.383
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 +71 -58
- package/dist/esm/index.mjs +65 -51
- package/dist/extract-frame-and-audio.js +1 -1
- package/dist/is-type-of-error.d.ts +7 -0
- package/dist/is-type-of-error.js +20 -0
- package/dist/media-player.js +1 -1
- package/dist/video/video-for-rendering.js +1 -1
- package/dist/video-extraction/get-frames-since-keyframe.js +1 -1
- package/package.json +3 -3
|
@@ -4,6 +4,7 @@ import { convertAudioData, fixFloatingPoint, } from '../convert-audiodata/conver
|
|
|
4
4
|
import { TARGET_NUMBER_OF_CHANNELS, TARGET_SAMPLE_RATE, } from '../convert-audiodata/resample-audiodata';
|
|
5
5
|
import { getSink } from '../get-sink';
|
|
6
6
|
import { getTimeInSeconds } from '../get-time-in-seconds';
|
|
7
|
+
import { isNetworkError, isUnsupportedConfigurationError, } from '../is-type-of-error';
|
|
7
8
|
const extractAudioInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds, durationInSeconds: durationNotYetApplyingPlaybackRate, logLevel, loop, playbackRate, audioStreamIndex, trimBefore, trimAfter, fps, maxCacheSize, }) => {
|
|
8
9
|
const { getAudio, actualMatroskaTimestamps, isMatroska, getDuration } = await getSink(src, logLevel);
|
|
9
10
|
let mediaDurationInSeconds = null;
|
|
@@ -47,70 +48,82 @@ const extractAudioInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds,
|
|
|
47
48
|
maxCacheSize,
|
|
48
49
|
});
|
|
49
50
|
const durationInSeconds = durationNotYetApplyingPlaybackRate * playbackRate;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
sample.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
51
|
+
try {
|
|
52
|
+
const samples = await sampleIterator.getSamples(timeInSeconds, durationInSeconds);
|
|
53
|
+
audioManager.logOpenFrames();
|
|
54
|
+
const audioDataArray = [];
|
|
55
|
+
for (let i = 0; i < samples.length; i++) {
|
|
56
|
+
const sample = samples[i];
|
|
57
|
+
// Less than 1 sample would be included - we did not need it after all!
|
|
58
|
+
if (Math.abs(sample.timestamp - (timeInSeconds + durationInSeconds)) *
|
|
59
|
+
sample.sampleRate <
|
|
60
|
+
1) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Less than 1 sample would be included - we did not need it after all!
|
|
64
|
+
if (sample.timestamp + sample.duration <= timeInSeconds) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const isFirstSample = i === 0;
|
|
68
|
+
const isLastSample = i === samples.length - 1;
|
|
69
|
+
const audioDataRaw = sample.toAudioData();
|
|
70
|
+
// amount of samples to shave from start and end
|
|
71
|
+
let trimStartInSeconds = 0;
|
|
72
|
+
let trimEndInSeconds = 0;
|
|
73
|
+
let leadingSilence = null;
|
|
74
|
+
if (isFirstSample) {
|
|
75
|
+
trimStartInSeconds = fixFloatingPoint(timeInSeconds - sample.timestamp);
|
|
76
|
+
if (trimStartInSeconds < 0) {
|
|
77
|
+
const silenceFrames = Math.ceil(fixFloatingPoint(-trimStartInSeconds * TARGET_SAMPLE_RATE));
|
|
78
|
+
leadingSilence = {
|
|
79
|
+
data: new Int16Array(silenceFrames * TARGET_NUMBER_OF_CHANNELS),
|
|
80
|
+
numberOfFrames: silenceFrames,
|
|
81
|
+
timestamp: timeInSeconds * 1000000,
|
|
82
|
+
durationInMicroSeconds: (silenceFrames / TARGET_SAMPLE_RATE) * 1000000,
|
|
83
|
+
};
|
|
84
|
+
trimStartInSeconds = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (isLastSample) {
|
|
88
|
+
trimEndInSeconds =
|
|
89
|
+
// clamp to 0 in case the audio ends early
|
|
90
|
+
Math.max(0, sample.timestamp +
|
|
91
|
+
sample.duration -
|
|
92
|
+
(timeInSeconds + durationInSeconds));
|
|
83
93
|
}
|
|
94
|
+
const audioData = convertAudioData({
|
|
95
|
+
audioData: audioDataRaw,
|
|
96
|
+
trimStartInSeconds,
|
|
97
|
+
trimEndInSeconds,
|
|
98
|
+
playbackRate,
|
|
99
|
+
audioDataTimestamp: sample.timestamp,
|
|
100
|
+
isLast: isLastSample,
|
|
101
|
+
});
|
|
102
|
+
audioDataRaw.close();
|
|
103
|
+
if (audioData.numberOfFrames === 0) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (leadingSilence) {
|
|
107
|
+
audioDataArray.push(leadingSilence);
|
|
108
|
+
}
|
|
109
|
+
audioDataArray.push(audioData);
|
|
84
110
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
// clamp to 0 in case the audio ends early
|
|
88
|
-
Math.max(0, sample.timestamp +
|
|
89
|
-
sample.duration -
|
|
90
|
-
(timeInSeconds + durationInSeconds));
|
|
111
|
+
if (audioDataArray.length === 0) {
|
|
112
|
+
return { data: null, durationInSeconds: mediaDurationInSeconds };
|
|
91
113
|
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
audioDataRaw.close();
|
|
101
|
-
if (audioData.numberOfFrames === 0) {
|
|
102
|
-
continue;
|
|
114
|
+
const combined = combineAudioDataAndClosePrevious(audioDataArray);
|
|
115
|
+
return { data: combined, durationInSeconds: mediaDurationInSeconds };
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const error = err;
|
|
119
|
+
if (isNetworkError(error)) {
|
|
120
|
+
return 'network-error';
|
|
103
121
|
}
|
|
104
|
-
if (
|
|
105
|
-
|
|
122
|
+
if (isUnsupportedConfigurationError(error)) {
|
|
123
|
+
return 'cannot-decode';
|
|
106
124
|
}
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
if (audioDataArray.length === 0) {
|
|
110
|
-
return { data: null, durationInSeconds: mediaDurationInSeconds };
|
|
125
|
+
throw err;
|
|
111
126
|
}
|
|
112
|
-
const combined = combineAudioDataAndClosePrevious(audioDataArray);
|
|
113
|
-
return { data: combined, durationInSeconds: mediaDurationInSeconds };
|
|
114
127
|
};
|
|
115
128
|
let queue = Promise.resolve(undefined);
|
|
116
129
|
export const extractAudio = (params) => {
|
package/dist/esm/index.mjs
CHANGED
|
@@ -538,13 +538,16 @@ var drawPreviewOverlay = ({
|
|
|
538
538
|
}
|
|
539
539
|
};
|
|
540
540
|
|
|
541
|
-
// src/is-
|
|
541
|
+
// src/is-type-of-error.ts
|
|
542
542
|
function isNetworkError(error) {
|
|
543
543
|
if (error.message.includes("Failed to fetch") || error.message.includes("Load failed") || error.message.includes("NetworkError when attempting to fetch resource")) {
|
|
544
544
|
return true;
|
|
545
545
|
}
|
|
546
546
|
return false;
|
|
547
547
|
}
|
|
548
|
+
function isUnsupportedConfigurationError(error) {
|
|
549
|
+
return error.message.includes("Unsupported configuration");
|
|
550
|
+
}
|
|
548
551
|
|
|
549
552
|
// src/nonce-manager.ts
|
|
550
553
|
var makeNonceManager = () => {
|
|
@@ -2902,61 +2905,72 @@ var extractAudioInternal = async ({
|
|
|
2902
2905
|
maxCacheSize
|
|
2903
2906
|
});
|
|
2904
2907
|
const durationInSeconds = durationNotYetApplyingPlaybackRate * playbackRate;
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
if (sample.timestamp + sample.duration <= timeInSeconds) {
|
|
2914
|
-
continue;
|
|
2915
|
-
}
|
|
2916
|
-
const isFirstSample = i === 0;
|
|
2917
|
-
const isLastSample = i === samples.length - 1;
|
|
2918
|
-
const audioDataRaw = sample.toAudioData();
|
|
2919
|
-
let trimStartInSeconds = 0;
|
|
2920
|
-
let trimEndInSeconds = 0;
|
|
2921
|
-
let leadingSilence = null;
|
|
2922
|
-
if (isFirstSample) {
|
|
2923
|
-
trimStartInSeconds = fixFloatingPoint2(timeInSeconds - sample.timestamp);
|
|
2924
|
-
if (trimStartInSeconds < 0) {
|
|
2925
|
-
const silenceFrames = Math.ceil(fixFloatingPoint2(-trimStartInSeconds * TARGET_SAMPLE_RATE));
|
|
2926
|
-
leadingSilence = {
|
|
2927
|
-
data: new Int16Array(silenceFrames * TARGET_NUMBER_OF_CHANNELS),
|
|
2928
|
-
numberOfFrames: silenceFrames,
|
|
2929
|
-
timestamp: timeInSeconds * 1e6,
|
|
2930
|
-
durationInMicroSeconds: silenceFrames / TARGET_SAMPLE_RATE * 1e6
|
|
2931
|
-
};
|
|
2932
|
-
trimStartInSeconds = 0;
|
|
2908
|
+
try {
|
|
2909
|
+
const samples = await sampleIterator.getSamples(timeInSeconds, durationInSeconds);
|
|
2910
|
+
audioManager.logOpenFrames();
|
|
2911
|
+
const audioDataArray = [];
|
|
2912
|
+
for (let i = 0;i < samples.length; i++) {
|
|
2913
|
+
const sample = samples[i];
|
|
2914
|
+
if (Math.abs(sample.timestamp - (timeInSeconds + durationInSeconds)) * sample.sampleRate < 1) {
|
|
2915
|
+
continue;
|
|
2933
2916
|
}
|
|
2917
|
+
if (sample.timestamp + sample.duration <= timeInSeconds) {
|
|
2918
|
+
continue;
|
|
2919
|
+
}
|
|
2920
|
+
const isFirstSample = i === 0;
|
|
2921
|
+
const isLastSample = i === samples.length - 1;
|
|
2922
|
+
const audioDataRaw = sample.toAudioData();
|
|
2923
|
+
let trimStartInSeconds = 0;
|
|
2924
|
+
let trimEndInSeconds = 0;
|
|
2925
|
+
let leadingSilence = null;
|
|
2926
|
+
if (isFirstSample) {
|
|
2927
|
+
trimStartInSeconds = fixFloatingPoint2(timeInSeconds - sample.timestamp);
|
|
2928
|
+
if (trimStartInSeconds < 0) {
|
|
2929
|
+
const silenceFrames = Math.ceil(fixFloatingPoint2(-trimStartInSeconds * TARGET_SAMPLE_RATE));
|
|
2930
|
+
leadingSilence = {
|
|
2931
|
+
data: new Int16Array(silenceFrames * TARGET_NUMBER_OF_CHANNELS),
|
|
2932
|
+
numberOfFrames: silenceFrames,
|
|
2933
|
+
timestamp: timeInSeconds * 1e6,
|
|
2934
|
+
durationInMicroSeconds: silenceFrames / TARGET_SAMPLE_RATE * 1e6
|
|
2935
|
+
};
|
|
2936
|
+
trimStartInSeconds = 0;
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
if (isLastSample) {
|
|
2940
|
+
trimEndInSeconds = Math.max(0, sample.timestamp + sample.duration - (timeInSeconds + durationInSeconds));
|
|
2941
|
+
}
|
|
2942
|
+
const audioData = convertAudioData({
|
|
2943
|
+
audioData: audioDataRaw,
|
|
2944
|
+
trimStartInSeconds,
|
|
2945
|
+
trimEndInSeconds,
|
|
2946
|
+
playbackRate,
|
|
2947
|
+
audioDataTimestamp: sample.timestamp,
|
|
2948
|
+
isLast: isLastSample
|
|
2949
|
+
});
|
|
2950
|
+
audioDataRaw.close();
|
|
2951
|
+
if (audioData.numberOfFrames === 0) {
|
|
2952
|
+
continue;
|
|
2953
|
+
}
|
|
2954
|
+
if (leadingSilence) {
|
|
2955
|
+
audioDataArray.push(leadingSilence);
|
|
2956
|
+
}
|
|
2957
|
+
audioDataArray.push(audioData);
|
|
2934
2958
|
}
|
|
2935
|
-
if (
|
|
2936
|
-
|
|
2959
|
+
if (audioDataArray.length === 0) {
|
|
2960
|
+
return { data: null, durationInSeconds: mediaDurationInSeconds };
|
|
2937
2961
|
}
|
|
2938
|
-
const
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
isLast: isLastSample
|
|
2945
|
-
});
|
|
2946
|
-
audioDataRaw.close();
|
|
2947
|
-
if (audioData.numberOfFrames === 0) {
|
|
2948
|
-
continue;
|
|
2962
|
+
const combined = combineAudioDataAndClosePrevious(audioDataArray);
|
|
2963
|
+
return { data: combined, durationInSeconds: mediaDurationInSeconds };
|
|
2964
|
+
} catch (err) {
|
|
2965
|
+
const error = err;
|
|
2966
|
+
if (isNetworkError(error)) {
|
|
2967
|
+
return "network-error";
|
|
2949
2968
|
}
|
|
2950
|
-
if (
|
|
2951
|
-
|
|
2969
|
+
if (isUnsupportedConfigurationError(error)) {
|
|
2970
|
+
return "cannot-decode";
|
|
2952
2971
|
}
|
|
2953
|
-
|
|
2954
|
-
}
|
|
2955
|
-
if (audioDataArray.length === 0) {
|
|
2956
|
-
return { data: null, durationInSeconds: mediaDurationInSeconds };
|
|
2972
|
+
throw err;
|
|
2957
2973
|
}
|
|
2958
|
-
const combined = combineAudioDataAndClosePrevious(audioDataArray);
|
|
2959
|
-
return { data: combined, durationInSeconds: mediaDurationInSeconds };
|
|
2960
2974
|
};
|
|
2961
2975
|
var queue = Promise.resolve(undefined);
|
|
2962
2976
|
var extractAudio = (params) => {
|
|
@@ -4115,7 +4129,7 @@ var VideoForRendering = ({
|
|
|
4115
4129
|
cancelRender3(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
4116
4130
|
}
|
|
4117
4131
|
if (window.remotion_isMainTab) {
|
|
4118
|
-
Internals16.Log.
|
|
4132
|
+
Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `Cannot decode ${src}, falling back to <OffthreadVideo>`);
|
|
4119
4133
|
}
|
|
4120
4134
|
setReplaceWithOffthreadVideo({
|
|
4121
4135
|
durationInSeconds: result.durationInSeconds
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { extractAudio } from './audio-extraction/extract-audio';
|
|
2
|
-
import { isNetworkError } from './is-
|
|
2
|
+
import { isNetworkError } from './is-type-of-error';
|
|
3
3
|
import { extractFrame } from './video-extraction/extract-frame';
|
|
4
4
|
import { rotateFrame } from './video-extraction/rotate-frame';
|
|
5
5
|
export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, maxCacheSize, }) => {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility to check if error is network error
|
|
3
|
+
* @param error
|
|
4
|
+
* @returns
|
|
5
|
+
*/
|
|
6
|
+
export function isNetworkError(error) {
|
|
7
|
+
if (
|
|
8
|
+
// Chrome
|
|
9
|
+
error.message.includes('Failed to fetch') ||
|
|
10
|
+
// Safari
|
|
11
|
+
error.message.includes('Load failed') ||
|
|
12
|
+
// Firefox
|
|
13
|
+
error.message.includes('NetworkError when attempting to fetch resource')) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
export function isUnsupportedConfigurationError(error) {
|
|
19
|
+
return error.message.includes('Unsupported configuration');
|
|
20
|
+
}
|
package/dist/media-player.js
CHANGED
|
@@ -4,7 +4,7 @@ import { audioIteratorManager, } from './audio-iterator-manager';
|
|
|
4
4
|
import { calculatePlaybackTime } from './calculate-playbacktime';
|
|
5
5
|
import { drawPreviewOverlay } from './debug-overlay/preview-overlay';
|
|
6
6
|
import { getTimeInSeconds } from './get-time-in-seconds';
|
|
7
|
-
import { isNetworkError } from './is-
|
|
7
|
+
import { isNetworkError } from './is-type-of-error';
|
|
8
8
|
import { makeNonceManager } from './nonce-manager';
|
|
9
9
|
import { videoIteratorManager } from './video-iterator-manager';
|
|
10
10
|
export class MediaPlayer {
|
|
@@ -92,7 +92,7 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
|
|
|
92
92
|
cancelRender(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
93
93
|
}
|
|
94
94
|
if (window.remotion_isMainTab) {
|
|
95
|
-
Internals.Log.
|
|
95
|
+
Internals.Log.warn({ logLevel, tag: '@remotion/media' }, `Cannot decode ${src}, falling back to <OffthreadVideo>`);
|
|
96
96
|
}
|
|
97
97
|
setReplaceWithOffthreadVideo({
|
|
98
98
|
durationInSeconds: result.durationInSeconds,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ALL_FORMATS, AudioSampleSink, EncodedPacketSink, Input, MATROSKA, UrlSource, VideoSampleSink, WEBM, } from 'mediabunny';
|
|
2
|
-
import { isNetworkError } from '../is-
|
|
2
|
+
import { isNetworkError } from '../is-type-of-error';
|
|
3
3
|
import { makeKeyframeBank } from './keyframe-bank';
|
|
4
4
|
import { rememberActualMatroskaTimestamps } from './remember-actual-matroska-timestamps';
|
|
5
5
|
const getRetryDelay = (() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/media",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.383",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/esm/index.mjs",
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"mediabunny": "1.25.3",
|
|
25
|
-
"remotion": "4.0.
|
|
25
|
+
"remotion": "4.0.383"
|
|
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.383",
|
|
33
33
|
"@vitest/browser-webdriverio": "4.0.7",
|
|
34
34
|
"eslint": "9.19.0",
|
|
35
35
|
"react": "19.2.1",
|