@remotion/media 4.0.414 → 4.0.415
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/allow-wait.js +15 -0
- package/dist/audio/audio-for-preview.js +304 -0
- package/dist/audio/audio-for-rendering.js +194 -0
- package/dist/audio/audio-preview-iterator.js +176 -0
- package/dist/audio/audio.js +20 -0
- package/dist/audio/props.js +1 -0
- package/dist/audio-extraction/audio-cache.js +66 -0
- package/dist/audio-extraction/audio-iterator.js +132 -0
- package/dist/audio-extraction/audio-manager.js +113 -0
- package/dist/audio-extraction/extract-audio.js +132 -0
- package/dist/audio-iterator-manager.js +228 -0
- package/dist/browser-can-use-webgl2.js +13 -0
- package/dist/caches.d.ts +3 -2
- package/dist/caches.js +61 -0
- package/dist/calculate-playbacktime.js +4 -0
- package/dist/convert-audiodata/apply-volume.js +17 -0
- package/dist/convert-audiodata/combine-audiodata.js +23 -0
- package/dist/convert-audiodata/convert-audiodata.js +73 -0
- package/dist/convert-audiodata/resample-audiodata.js +94 -0
- package/dist/debug-overlay/preview-overlay.js +42 -0
- package/dist/esm/index.mjs +40 -30
- package/dist/extract-frame-and-audio.js +101 -0
- package/dist/get-sink.js +15 -0
- package/dist/get-time-in-seconds.js +40 -0
- package/dist/helpers/round-to-4-digits.js +4 -0
- package/dist/index.js +12 -0
- package/dist/is-network-error.d.ts +6 -0
- package/dist/is-network-error.js +17 -0
- package/dist/is-type-of-error.js +20 -0
- package/dist/looped-frame.js +10 -0
- package/dist/media-player.js +431 -0
- package/dist/nonce-manager.js +13 -0
- package/dist/prewarm-iterator-for-looping.js +56 -0
- package/dist/render-timestamp-range.js +9 -0
- package/dist/show-in-timeline.js +31 -0
- package/dist/use-media-in-timeline.js +103 -0
- package/dist/video/props.js +1 -0
- package/dist/video/video-for-preview.js +331 -0
- package/dist/video/video-for-rendering.js +263 -0
- package/dist/video/video-preview-iterator.js +122 -0
- package/dist/video/video.js +35 -0
- package/dist/video-extraction/add-broadcast-channel-listener.js +125 -0
- package/dist/video-extraction/extract-frame-via-broadcast-channel.js +113 -0
- package/dist/video-extraction/extract-frame.js +85 -0
- package/dist/video-extraction/get-allocation-size.js +6 -0
- package/dist/video-extraction/get-frames-since-keyframe.js +108 -0
- package/dist/video-extraction/keyframe-bank.d.ts +2 -2
- package/dist/video-extraction/keyframe-bank.js +159 -0
- package/dist/video-extraction/keyframe-manager.d.ts +2 -1
- package/dist/video-extraction/keyframe-manager.js +206 -0
- package/dist/video-extraction/remember-actual-matroska-timestamps.js +19 -0
- package/dist/video-extraction/rotate-frame.js +34 -0
- package/dist/video-iterator-manager.js +109 -0
- package/package.json +3 -3
package/dist/esm/index.mjs
CHANGED
|
@@ -2101,6 +2101,7 @@ var makeAudioCache = () => {
|
|
|
2101
2101
|
|
|
2102
2102
|
// src/audio-extraction/audio-iterator.ts
|
|
2103
2103
|
var extraThreshold = 1.5;
|
|
2104
|
+
var safetyOutOfOrderThreshold = 0.2;
|
|
2104
2105
|
var warned = {};
|
|
2105
2106
|
var warnAboutMatroskaOnce = (src, logLevel) => {
|
|
2106
2107
|
if (warned[src]) {
|
|
@@ -2143,7 +2144,7 @@ var makeAudioIterator2 = ({
|
|
|
2143
2144
|
const getSamples = async (timestamp, durationInSeconds) => {
|
|
2144
2145
|
lastUsed = Date.now();
|
|
2145
2146
|
if (fullDuration !== null && timestamp > fullDuration) {
|
|
2146
|
-
cache.clearBeforeThreshold(fullDuration -
|
|
2147
|
+
cache.clearBeforeThreshold(fullDuration - safetyOutOfOrderThreshold);
|
|
2147
2148
|
return [];
|
|
2148
2149
|
}
|
|
2149
2150
|
const samples = cache.getSamples(timestamp, durationInSeconds);
|
|
@@ -2156,7 +2157,7 @@ var makeAudioIterator2 = ({
|
|
|
2156
2157
|
while (true) {
|
|
2157
2158
|
const sample = await getNextSample();
|
|
2158
2159
|
const deleteBefore = fullDuration === null ? timestamp : Math.min(timestamp, fullDuration);
|
|
2159
|
-
cache.clearBeforeThreshold(deleteBefore -
|
|
2160
|
+
cache.clearBeforeThreshold(deleteBefore - safetyOutOfOrderThreshold);
|
|
2160
2161
|
if (sample === null) {
|
|
2161
2162
|
break;
|
|
2162
2163
|
}
|
|
@@ -2418,10 +2419,11 @@ var makeKeyframeBank = async ({
|
|
|
2418
2419
|
continue;
|
|
2419
2420
|
}
|
|
2420
2421
|
}
|
|
2421
|
-
if (frameTimestamp
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2422
|
+
if (!frames[frameTimestamp]) {
|
|
2423
|
+
continue;
|
|
2424
|
+
}
|
|
2425
|
+
const { duration } = frames[frameTimestamp];
|
|
2426
|
+
if (frameTimestamp + duration < timestampInSeconds) {
|
|
2425
2427
|
deleteFrameAtTimestamp(frameTimestamp);
|
|
2426
2428
|
deletedTimestamps.push(frameTimestamp);
|
|
2427
2429
|
}
|
|
@@ -2451,7 +2453,7 @@ var makeKeyframeBank = async ({
|
|
|
2451
2453
|
lastUsed = Date.now();
|
|
2452
2454
|
Internals9.Log.trace({ logLevel, tag: "@remotion/media" }, `Added frame at ${frame.timestamp}sec to bank`);
|
|
2453
2455
|
};
|
|
2454
|
-
const ensureEnoughFramesForTimestamp = async (timestampInSeconds, logLevel) => {
|
|
2456
|
+
const ensureEnoughFramesForTimestamp = async (timestampInSeconds, logLevel, fps) => {
|
|
2455
2457
|
while (!hasDecodedEnoughForTimestamp(timestampInSeconds)) {
|
|
2456
2458
|
const sample = await sampleIterator.next();
|
|
2457
2459
|
if (sample.value) {
|
|
@@ -2463,18 +2465,18 @@ var makeKeyframeBank = async ({
|
|
|
2463
2465
|
}
|
|
2464
2466
|
deleteFramesBeforeTimestamp({
|
|
2465
2467
|
logLevel: parentLogLevel,
|
|
2466
|
-
timestampInSeconds: timestampInSeconds -
|
|
2468
|
+
timestampInSeconds: timestampInSeconds - getSafeWindowOfMonotonicity(fps)
|
|
2467
2469
|
});
|
|
2468
2470
|
}
|
|
2469
2471
|
lastUsed = Date.now();
|
|
2470
2472
|
};
|
|
2471
|
-
const getFrameFromTimestamp = async (timestampInSeconds) => {
|
|
2473
|
+
const getFrameFromTimestamp = async (timestampInSeconds, fps) => {
|
|
2472
2474
|
lastUsed = Date.now();
|
|
2473
2475
|
let adjustedTimestamp = timestampInSeconds;
|
|
2474
2476
|
if (hasReachedEndOfVideo && roundTo4Digits(adjustedTimestamp) > roundTo4Digits(frameTimestamps[frameTimestamps.length - 1])) {
|
|
2475
2477
|
adjustedTimestamp = frameTimestamps[frameTimestamps.length - 1];
|
|
2476
2478
|
}
|
|
2477
|
-
await ensureEnoughFramesForTimestamp(adjustedTimestamp, parentLogLevel);
|
|
2479
|
+
await ensureEnoughFramesForTimestamp(adjustedTimestamp, parentLogLevel, fps);
|
|
2478
2480
|
for (let i = frameTimestamps.length - 1;i >= 0; i--) {
|
|
2479
2481
|
const sample = frames[frameTimestamps[i]];
|
|
2480
2482
|
if (!sample) {
|
|
@@ -2486,8 +2488,8 @@ var makeKeyframeBank = async ({
|
|
|
2486
2488
|
}
|
|
2487
2489
|
return frames[frameTimestamps[0]] ?? null;
|
|
2488
2490
|
};
|
|
2489
|
-
const hasTimestampInSecond = async (timestamp) => {
|
|
2490
|
-
return await getFrameFromTimestamp(timestamp) !== null;
|
|
2491
|
+
const hasTimestampInSecond = async (timestamp, fps) => {
|
|
2492
|
+
return await getFrameFromTimestamp(timestamp, fps) !== null;
|
|
2491
2493
|
};
|
|
2492
2494
|
const getOpenFrameCount = () => {
|
|
2493
2495
|
return {
|
|
@@ -2510,9 +2512,12 @@ var makeKeyframeBank = async ({
|
|
|
2510
2512
|
if (frameTimestamps.length === 0) {
|
|
2511
2513
|
return null;
|
|
2512
2514
|
}
|
|
2515
|
+
const firstTimestamp = frameTimestamps[0];
|
|
2516
|
+
const lastTimestamp = frameTimestamps[frameTimestamps.length - 1];
|
|
2517
|
+
const lastFrame = frames[lastTimestamp];
|
|
2513
2518
|
return {
|
|
2514
|
-
firstTimestamp
|
|
2515
|
-
lastTimestamp:
|
|
2519
|
+
firstTimestamp,
|
|
2520
|
+
lastTimestamp: lastTimestamp + lastFrame.duration
|
|
2516
2521
|
};
|
|
2517
2522
|
};
|
|
2518
2523
|
const prepareForDeletion = (logLevel, reason) => {
|
|
@@ -2553,13 +2558,13 @@ var makeKeyframeBank = async ({
|
|
|
2553
2558
|
return true;
|
|
2554
2559
|
};
|
|
2555
2560
|
const keyframeBank = {
|
|
2556
|
-
getFrameFromTimestamp: (timestamp) => {
|
|
2557
|
-
queue = queue.then(() => getFrameFromTimestamp(timestamp));
|
|
2561
|
+
getFrameFromTimestamp: (timestamp, fps) => {
|
|
2562
|
+
queue = queue.then(() => getFrameFromTimestamp(timestamp, fps));
|
|
2558
2563
|
return queue;
|
|
2559
2564
|
},
|
|
2560
2565
|
prepareForDeletion,
|
|
2561
|
-
hasTimestampInSecond: (timestamp) => {
|
|
2562
|
-
queue = queue.then(() => hasTimestampInSecond(timestamp));
|
|
2566
|
+
hasTimestampInSecond: (timestamp, fps) => {
|
|
2567
|
+
queue = queue.then(() => hasTimestampInSecond(timestamp, fps));
|
|
2563
2568
|
return queue;
|
|
2564
2569
|
},
|
|
2565
2570
|
addFrame,
|
|
@@ -2673,9 +2678,10 @@ var makeKeyframeManager = () => {
|
|
|
2673
2678
|
const clearKeyframeBanksBeforeTime = ({
|
|
2674
2679
|
timestampInSeconds,
|
|
2675
2680
|
src,
|
|
2676
|
-
logLevel
|
|
2681
|
+
logLevel,
|
|
2682
|
+
fps
|
|
2677
2683
|
}) => {
|
|
2678
|
-
const threshold = timestampInSeconds -
|
|
2684
|
+
const threshold = timestampInSeconds - getSafeWindowOfMonotonicity(fps);
|
|
2679
2685
|
if (!sources[src]) {
|
|
2680
2686
|
return;
|
|
2681
2687
|
}
|
|
@@ -2685,9 +2691,8 @@ var makeKeyframeManager = () => {
|
|
|
2685
2691
|
if (!range) {
|
|
2686
2692
|
continue;
|
|
2687
2693
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
bank.prepareForDeletion(logLevel, "cleared before threshold");
|
|
2694
|
+
if (range.lastTimestamp < threshold) {
|
|
2695
|
+
bank.prepareForDeletion(logLevel, "cleared before threshold " + threshold);
|
|
2691
2696
|
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `[Video] Cleared frames for src ${src} from ${range.firstTimestamp}sec to ${range.lastTimestamp}sec`);
|
|
2692
2697
|
const bankIndex = banks.indexOf(bank);
|
|
2693
2698
|
delete sources[src][bankIndex];
|
|
@@ -2741,13 +2746,15 @@ var makeKeyframeManager = () => {
|
|
|
2741
2746
|
videoSampleSink,
|
|
2742
2747
|
src,
|
|
2743
2748
|
logLevel,
|
|
2744
|
-
maxCacheSize
|
|
2749
|
+
maxCacheSize,
|
|
2750
|
+
fps
|
|
2745
2751
|
}) => {
|
|
2746
2752
|
ensureToStayUnderMaxCacheSize(logLevel, maxCacheSize);
|
|
2747
2753
|
clearKeyframeBanksBeforeTime({
|
|
2748
2754
|
timestampInSeconds: timestamp,
|
|
2749
2755
|
src,
|
|
2750
|
-
logLevel
|
|
2756
|
+
logLevel,
|
|
2757
|
+
fps
|
|
2751
2758
|
});
|
|
2752
2759
|
const keyframeBank = await getKeyframeBankOrRefetch({
|
|
2753
2760
|
timestamp,
|
|
@@ -2775,14 +2782,16 @@ var makeKeyframeManager = () => {
|
|
|
2775
2782
|
videoSampleSink,
|
|
2776
2783
|
src,
|
|
2777
2784
|
logLevel,
|
|
2778
|
-
maxCacheSize
|
|
2785
|
+
maxCacheSize,
|
|
2786
|
+
fps
|
|
2779
2787
|
}) => {
|
|
2780
2788
|
queue = queue.then(() => requestKeyframeBank({
|
|
2781
2789
|
timestamp,
|
|
2782
2790
|
videoSampleSink,
|
|
2783
2791
|
src,
|
|
2784
2792
|
logLevel,
|
|
2785
|
-
maxCacheSize
|
|
2793
|
+
maxCacheSize,
|
|
2794
|
+
fps
|
|
2786
2795
|
}));
|
|
2787
2796
|
return queue;
|
|
2788
2797
|
},
|
|
@@ -2792,7 +2801,7 @@ var makeKeyframeManager = () => {
|
|
|
2792
2801
|
};
|
|
2793
2802
|
|
|
2794
2803
|
// src/caches.ts
|
|
2795
|
-
var
|
|
2804
|
+
var getSafeWindowOfMonotonicity = (fps) => 0.2 * 30 / fps;
|
|
2796
2805
|
var keyframeManager = makeKeyframeManager();
|
|
2797
2806
|
var audioManager = makeAudioManager();
|
|
2798
2807
|
var getTotalCacheStats = () => {
|
|
@@ -3417,7 +3426,8 @@ var extractFrameInternal = async ({
|
|
|
3417
3426
|
timestamp: timeInSeconds,
|
|
3418
3427
|
src,
|
|
3419
3428
|
logLevel,
|
|
3420
|
-
maxCacheSize
|
|
3429
|
+
maxCacheSize,
|
|
3430
|
+
fps
|
|
3421
3431
|
});
|
|
3422
3432
|
if (!keyframeBank) {
|
|
3423
3433
|
return {
|
|
@@ -3427,7 +3437,7 @@ var extractFrameInternal = async ({
|
|
|
3427
3437
|
durationInSeconds: await sink.getDuration()
|
|
3428
3438
|
};
|
|
3429
3439
|
}
|
|
3430
|
-
const frame = await keyframeBank.getFrameFromTimestamp(timeInSeconds);
|
|
3440
|
+
const frame = await keyframeBank.getFrameFromTimestamp(timeInSeconds, fps);
|
|
3431
3441
|
const rotation = frame?.rotation ?? 0;
|
|
3432
3442
|
return {
|
|
3433
3443
|
type: "success",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { extractAudio } from './audio-extraction/extract-audio';
|
|
2
|
+
import { isNetworkError } from './is-type-of-error';
|
|
3
|
+
import { extractFrame } from './video-extraction/extract-frame';
|
|
4
|
+
import { rotateFrame } from './video-extraction/rotate-frame';
|
|
5
|
+
export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, maxCacheSize, }) => {
|
|
6
|
+
try {
|
|
7
|
+
const [frame, audio] = await Promise.all([
|
|
8
|
+
includeVideo
|
|
9
|
+
? extractFrame({
|
|
10
|
+
src,
|
|
11
|
+
timeInSeconds,
|
|
12
|
+
logLevel,
|
|
13
|
+
loop,
|
|
14
|
+
trimAfter,
|
|
15
|
+
playbackRate,
|
|
16
|
+
trimBefore,
|
|
17
|
+
fps,
|
|
18
|
+
maxCacheSize,
|
|
19
|
+
})
|
|
20
|
+
: null,
|
|
21
|
+
includeAudio
|
|
22
|
+
? extractAudio({
|
|
23
|
+
src,
|
|
24
|
+
timeInSeconds,
|
|
25
|
+
durationInSeconds,
|
|
26
|
+
logLevel,
|
|
27
|
+
loop,
|
|
28
|
+
playbackRate,
|
|
29
|
+
audioStreamIndex,
|
|
30
|
+
trimAfter,
|
|
31
|
+
fps,
|
|
32
|
+
trimBefore,
|
|
33
|
+
maxCacheSize,
|
|
34
|
+
})
|
|
35
|
+
: null,
|
|
36
|
+
]);
|
|
37
|
+
if (frame?.type === 'cannot-decode') {
|
|
38
|
+
return {
|
|
39
|
+
type: 'cannot-decode',
|
|
40
|
+
durationInSeconds: frame.durationInSeconds,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (frame?.type === 'unknown-container-format') {
|
|
44
|
+
return { type: 'unknown-container-format' };
|
|
45
|
+
}
|
|
46
|
+
if (frame?.type === 'cannot-decode-alpha') {
|
|
47
|
+
return {
|
|
48
|
+
type: 'cannot-decode-alpha',
|
|
49
|
+
durationInSeconds: frame.durationInSeconds,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (frame?.type === 'network-error') {
|
|
53
|
+
return { type: 'network-error' };
|
|
54
|
+
}
|
|
55
|
+
if (audio === 'unknown-container-format') {
|
|
56
|
+
if (frame !== null) {
|
|
57
|
+
frame?.frame?.close();
|
|
58
|
+
}
|
|
59
|
+
return { type: 'unknown-container-format' };
|
|
60
|
+
}
|
|
61
|
+
if (audio === 'network-error') {
|
|
62
|
+
if (frame !== null) {
|
|
63
|
+
frame?.frame?.close();
|
|
64
|
+
}
|
|
65
|
+
return { type: 'network-error' };
|
|
66
|
+
}
|
|
67
|
+
if (audio === 'cannot-decode') {
|
|
68
|
+
if (frame?.type === 'success' && frame.frame !== null) {
|
|
69
|
+
frame?.frame.close();
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
type: 'cannot-decode',
|
|
73
|
+
durationInSeconds: frame?.type === 'success' ? frame.durationInSeconds : null,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (!frame?.frame) {
|
|
77
|
+
return {
|
|
78
|
+
type: 'success',
|
|
79
|
+
frame: null,
|
|
80
|
+
audio: audio?.data ?? null,
|
|
81
|
+
durationInSeconds: audio?.durationInSeconds ?? null,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
type: 'success',
|
|
86
|
+
frame: await rotateFrame({
|
|
87
|
+
frame: frame.frame.toVideoFrame(),
|
|
88
|
+
rotation: frame.frame.rotation,
|
|
89
|
+
}),
|
|
90
|
+
audio: audio?.data ?? null,
|
|
91
|
+
durationInSeconds: audio?.durationInSeconds ?? null,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const error = err;
|
|
96
|
+
if (isNetworkError(error)) {
|
|
97
|
+
return { type: 'network-error' };
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
};
|
package/dist/get-sink.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Internals } from 'remotion';
|
|
2
|
+
import { getSinks } from './video-extraction/get-frames-since-keyframe';
|
|
3
|
+
export const sinkPromises = {};
|
|
4
|
+
export const getSink = (src, logLevel) => {
|
|
5
|
+
let promise = sinkPromises[src];
|
|
6
|
+
if (!promise) {
|
|
7
|
+
Internals.Log.verbose({
|
|
8
|
+
logLevel,
|
|
9
|
+
tag: '@remotion/media',
|
|
10
|
+
}, `Sink for ${src} was not found, creating new sink`);
|
|
11
|
+
promise = getSinks(src);
|
|
12
|
+
sinkPromises[src] = promise;
|
|
13
|
+
}
|
|
14
|
+
return promise;
|
|
15
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Internals } from 'remotion';
|
|
2
|
+
export const getTimeInSeconds = ({ loop, mediaDurationInSeconds, unloopedTimeInSeconds, src, trimAfter, trimBefore, fps, playbackRate, ifNoMediaDuration, }) => {
|
|
3
|
+
if (mediaDurationInSeconds === null && loop && ifNoMediaDuration === 'fail') {
|
|
4
|
+
throw new Error(`Could not determine duration of ${src}, but "loop" was set.`);
|
|
5
|
+
}
|
|
6
|
+
const loopDuration = loop
|
|
7
|
+
? Internals.calculateMediaDuration({
|
|
8
|
+
trimAfter,
|
|
9
|
+
mediaDurationInFrames: mediaDurationInSeconds
|
|
10
|
+
? mediaDurationInSeconds * fps
|
|
11
|
+
: Infinity,
|
|
12
|
+
// Playback rate was already specified before
|
|
13
|
+
playbackRate: 1,
|
|
14
|
+
trimBefore,
|
|
15
|
+
}) / fps
|
|
16
|
+
: Infinity;
|
|
17
|
+
const timeInSeconds = (unloopedTimeInSeconds * playbackRate) % loopDuration;
|
|
18
|
+
if ((trimAfter ?? null) !== null && !loop) {
|
|
19
|
+
const time = (trimAfter - (trimBefore ?? 0)) / fps;
|
|
20
|
+
if (timeInSeconds >= time) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return timeInSeconds + (trimBefore ?? 0) / fps;
|
|
25
|
+
};
|
|
26
|
+
export const calculateEndTime = ({ mediaDurationInSeconds, ifNoMediaDuration, src, trimAfter, trimBefore, fps, }) => {
|
|
27
|
+
if (mediaDurationInSeconds === null && ifNoMediaDuration === 'fail') {
|
|
28
|
+
throw new Error(`Could not determine duration of ${src}, but "loop" was set.`);
|
|
29
|
+
}
|
|
30
|
+
const mediaDuration = Internals.calculateMediaDuration({
|
|
31
|
+
trimAfter,
|
|
32
|
+
mediaDurationInFrames: mediaDurationInSeconds
|
|
33
|
+
? mediaDurationInSeconds * fps
|
|
34
|
+
: Infinity,
|
|
35
|
+
// Playback rate was already specified before
|
|
36
|
+
playbackRate: 1,
|
|
37
|
+
trimBefore,
|
|
38
|
+
}) / fps;
|
|
39
|
+
return mediaDuration + (trimBefore ?? 0) / fps;
|
|
40
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Audio } from './audio/audio';
|
|
2
|
+
import { Video } from './video/video';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Now just `Audio`
|
|
5
|
+
*/
|
|
6
|
+
export const experimental_Audio = Audio;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Now just `Video`
|
|
9
|
+
*/
|
|
10
|
+
export const experimental_Video = Video;
|
|
11
|
+
export { AudioForPreview } from './audio/audio-for-preview';
|
|
12
|
+
export { Audio, Video };
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const frameForVolumeProp = ({ behavior, loop, assetDurationInSeconds, fps, frame, startsAt, }) => {
|
|
2
|
+
if (!loop) {
|
|
3
|
+
return frame + startsAt;
|
|
4
|
+
}
|
|
5
|
+
if (behavior === 'extend') {
|
|
6
|
+
return frame + startsAt;
|
|
7
|
+
}
|
|
8
|
+
const assetDurationInFrames = Math.floor(assetDurationInSeconds * fps) - startsAt;
|
|
9
|
+
return (frame % assetDurationInFrames) + startsAt;
|
|
10
|
+
};
|