@remotion/media 4.0.497 → 4.0.499

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.
@@ -68,16 +68,22 @@ export declare const drawPreviewOverlay: ({ context, audioTime, audioContextStat
68
68
  videoIteratorManager: {
69
69
  startVideoIterator: (timeToSeek: number, nonce: import("../nonce-manager").Nonce) => Promise<void>;
70
70
  getVideoIteratorsCreated: () => number;
71
- seek: ({ newTime, nonce }: {
71
+ seek: ({ newTime, nonce, fps, playbackRate, isPlaying, }: {
72
72
  newTime: number;
73
73
  nonce: import("../nonce-manager").Nonce;
74
+ fps: number;
75
+ playbackRate: number;
76
+ isPlaying: boolean;
74
77
  }) => Promise<void>;
75
78
  destroy: () => void;
76
79
  getVideoFrameIterator: () => {
77
80
  destroy: () => void;
78
81
  initialFrame: import("mediabunny").WrappedCanvas | null;
79
82
  isDestroyed: () => boolean;
80
- tryToSatisfySeek: (time: number) => Promise<{
83
+ tryToSatisfySeek: (time: number, options: {
84
+ pendingFrameBehavior: "restart-iterator" | "wait";
85
+ shouldContinue: () => boolean;
86
+ }) => Promise<{
81
87
  type: "not-satisfied";
82
88
  reason: string;
83
89
  } | {
@@ -1277,7 +1277,6 @@ var makePrewarmedVideoIteratorCache = (videoSink) => {
1277
1277
  };
1278
1278
 
1279
1279
  // src/video/video-preview-iterator.ts
1280
- var MAXIMUM_AWAITED_PEEK_DISTANCE_SECONDS = 0.05;
1281
1280
  var createVideoIterator = async (timeToSeek, cache) => {
1282
1281
  let destroyed = false;
1283
1282
  const iterator = cache.makeIteratorOrUsePrewarmed(timeToSeek);
@@ -1315,22 +1314,12 @@ var createVideoIterator = async (timeToSeek, cache) => {
1315
1314
  wait: next.wait
1316
1315
  };
1317
1316
  };
1318
- const peek = async () => {
1319
- const peeked = peekIfReady();
1320
- if (peeked.type === "ready") {
1321
- return peeked.frame;
1322
- }
1323
- return setPeekedFrame(await peeked.wait());
1324
- };
1325
1317
  const getFrameEndTimestampFromPeek = (frame) => {
1326
1318
  return frame ? roundTo4Digits(frame.timestamp) : Infinity;
1327
1319
  };
1328
- const getFrameEndTimestamp = async () => {
1329
- return getFrameEndTimestampFromPeek(await peek());
1330
- };
1331
- const getFrameEndTimestampIfCloseEnough = async ({
1332
- timestamp,
1333
- frameTimestamp
1320
+ const getFrameEndTimestamp = async ({
1321
+ pendingFrameBehavior,
1322
+ shouldContinue
1334
1323
  }) => {
1335
1324
  const peeked = peekIfReady();
1336
1325
  if (peeked.type === "ready") {
@@ -1339,10 +1328,16 @@ var createVideoIterator = async (timeToSeek, cache) => {
1339
1328
  timestamp: getFrameEndTimestampFromPeek(peeked.frame)
1340
1329
  };
1341
1330
  }
1342
- if (timestamp - frameTimestamp > MAXIMUM_AWAITED_PEEK_DISTANCE_SECONDS) {
1331
+ if (pendingFrameBehavior === "restart-iterator") {
1343
1332
  return { type: "pending" };
1344
1333
  }
1334
+ if (!shouldContinue()) {
1335
+ return { type: "cancelled" };
1336
+ }
1345
1337
  const awaitedPeeked = setPeekedFrame(await peeked.wait());
1338
+ if (!shouldContinue()) {
1339
+ return { type: "cancelled" };
1340
+ }
1346
1341
  return {
1347
1342
  type: "ready",
1348
1343
  timestamp: getFrameEndTimestampFromPeek(awaitedPeeked)
@@ -1396,7 +1391,13 @@ var createVideoIterator = async (timeToSeek, cache) => {
1396
1391
  return;
1397
1392
  });
1398
1393
  };
1399
- const tryToSatisfySeek = async (time) => {
1394
+ const tryToSatisfySeek = async (time, options) => {
1395
+ if (!options.shouldContinue()) {
1396
+ return {
1397
+ type: "not-satisfied",
1398
+ reason: "seek was superseded"
1399
+ };
1400
+ }
1400
1401
  const timestamp = roundTo4Digits(time);
1401
1402
  if (lastReturnedFrame) {
1402
1403
  const frameTimestamp = roundTo4Digits(lastReturnedFrame.timestamp);
@@ -1414,8 +1415,23 @@ var createVideoIterator = async (timeToSeek, cache) => {
1414
1415
  reason: `iterator is too far, most recently returned ${frameTimestamp}`
1415
1416
  };
1416
1417
  }
1417
- const frameEndTimestamp = await getFrameEndTimestamp();
1418
- if (frameTimestamp <= timestamp && frameEndTimestamp > timestamp) {
1418
+ const frameEndTimestamp = await getFrameEndTimestamp({
1419
+ pendingFrameBehavior: options.pendingFrameBehavior,
1420
+ shouldContinue: options.shouldContinue
1421
+ });
1422
+ if (frameEndTimestamp.type === "cancelled") {
1423
+ return {
1424
+ type: "not-satisfied",
1425
+ reason: "seek was superseded"
1426
+ };
1427
+ }
1428
+ if (frameEndTimestamp.type === "pending") {
1429
+ return {
1430
+ type: "not-satisfied",
1431
+ reason: "iterator did not have next frame ready"
1432
+ };
1433
+ }
1434
+ if (frameTimestamp <= timestamp && frameEndTimestamp.timestamp > timestamp) {
1419
1435
  return {
1420
1436
  type: "satisfied",
1421
1437
  frame: lastReturnedFrame
@@ -1435,6 +1451,12 @@ var createVideoIterator = async (timeToSeek, cache) => {
1435
1451
  };
1436
1452
  }
1437
1453
  while (true) {
1454
+ if (!options.shouldContinue()) {
1455
+ return {
1456
+ type: "not-satisfied",
1457
+ reason: "seek was superseded"
1458
+ };
1459
+ }
1438
1460
  const frame = getNextOrNullIfNotAvailable();
1439
1461
  if (frame.type === "need-to-wait-for-it") {
1440
1462
  return {
@@ -1457,10 +1479,16 @@ var createVideoIterator = async (timeToSeek, cache) => {
1457
1479
  };
1458
1480
  }
1459
1481
  const frameTimestamp = roundTo4Digits(frame.frame.timestamp);
1460
- const frameEndTimestamp = await getFrameEndTimestampIfCloseEnough({
1461
- frameTimestamp,
1462
- timestamp
1482
+ const frameEndTimestamp = await getFrameEndTimestamp({
1483
+ pendingFrameBehavior: options.pendingFrameBehavior,
1484
+ shouldContinue: options.shouldContinue
1463
1485
  });
1486
+ if (frameEndTimestamp.type === "cancelled") {
1487
+ return {
1488
+ type: "not-satisfied",
1489
+ reason: "seek was superseded"
1490
+ };
1491
+ }
1464
1492
  if (frameEndTimestamp.type === "pending") {
1465
1493
  return {
1466
1494
  type: "not-satisfied",
@@ -1490,6 +1518,19 @@ var createVideoIterator = async (timeToSeek, cache) => {
1490
1518
 
1491
1519
  // src/video-iterator-manager.ts
1492
1520
  var { runEffectChain } = Internals4;
1521
+ var isSequentialMediaTimeAdvance = ({
1522
+ previousTime,
1523
+ newTime,
1524
+ fps,
1525
+ playbackRate,
1526
+ isPlaying
1527
+ }) => {
1528
+ if (!isPlaying || newTime < previousTime) {
1529
+ return false;
1530
+ }
1531
+ const maximumSequentialAdvance = Math.abs(playbackRate) / fps;
1532
+ return roundTo4Digits(newTime - previousTime) <= roundTo4Digits(maximumSequentialAdvance);
1533
+ };
1493
1534
  var videoIteratorManager = async ({
1494
1535
  delayPlaybackHandleIfNotPremounting,
1495
1536
  canvas,
@@ -1599,13 +1640,20 @@ var videoIteratorManager = async ({
1599
1640
  __callDispose(__stack, _err, _hasErr);
1600
1641
  }
1601
1642
  };
1602
- const seek = async ({ newTime, nonce }) => {
1643
+ const seek = async ({
1644
+ newTime,
1645
+ nonce,
1646
+ fps,
1647
+ playbackRate,
1648
+ isPlaying
1649
+ }) => {
1603
1650
  if (!videoFrameIterator) {
1604
1651
  return;
1605
1652
  }
1606
1653
  if (currentSeek !== null && roundTo4Digits(currentSeek) === roundTo4Digits(newTime)) {
1607
1654
  return;
1608
1655
  }
1656
+ const previousTime = currentSeek;
1609
1657
  currentSeek = newTime;
1610
1658
  if (getIsLooping()) {
1611
1659
  if (getLoopSegmentMediaEndTimestamp() - newTime < 1) {
@@ -1614,7 +1662,17 @@ var videoIteratorManager = async ({
1614
1662
  });
1615
1663
  }
1616
1664
  }
1617
- const videoSatisfyResult = await videoFrameIterator.tryToSatisfySeek(newTime);
1665
+ const pendingFrameBehavior = previousTime !== null && isSequentialMediaTimeAdvance({
1666
+ previousTime,
1667
+ newTime,
1668
+ fps,
1669
+ playbackRate,
1670
+ isPlaying
1671
+ }) ? "wait" : "restart-iterator";
1672
+ const videoSatisfyResult = await videoFrameIterator.tryToSatisfySeek(newTime, {
1673
+ pendingFrameBehavior,
1674
+ shouldContinue: () => !nonce.isStale()
1675
+ });
1618
1676
  if (videoSatisfyResult.type === "satisfied") {
1619
1677
  await drawFrame(videoSatisfyResult.frame);
1620
1678
  return;
@@ -1949,7 +2007,10 @@ class MediaPlayer {
1949
2007
  await Promise.all([
1950
2008
  this.videoIteratorManager?.seek({
1951
2009
  newTime,
1952
- nonce
2010
+ nonce,
2011
+ fps: this.fps,
2012
+ playbackRate: this.playbackRate,
2013
+ isPlaying: this.playing
1953
2014
  }),
1954
2015
  this.audioIteratorManager?.seek({
1955
2016
  newTime,
@@ -12,7 +12,10 @@ export declare const createVideoIterator: (timeToSeek: number, cache: {
12
12
  destroy: () => void;
13
13
  initialFrame: WrappedCanvas | null;
14
14
  isDestroyed: () => boolean;
15
- tryToSatisfySeek: (time: number) => Promise<{
15
+ tryToSatisfySeek: (time: number, options: {
16
+ pendingFrameBehavior: "restart-iterator" | "wait";
17
+ shouldContinue: () => boolean;
18
+ }) => Promise<{
16
19
  type: "not-satisfied";
17
20
  reason: string;
18
21
  } | {
@@ -2,6 +2,13 @@ import type { InputVideoTrack, WrappedCanvas } from 'mediabunny';
2
2
  import type { EffectChainState, EffectDefinitionAndStack } from 'remotion';
3
3
  import type { DelayPlaybackIfNotPremounting } from './delay-playback-if-not-premounting';
4
4
  import type { Nonce } from './nonce-manager';
5
+ export declare const isSequentialMediaTimeAdvance: ({ previousTime, newTime, fps, playbackRate, isPlaying, }: {
6
+ previousTime: number;
7
+ newTime: number;
8
+ fps: number;
9
+ playbackRate: number;
10
+ isPlaying: boolean;
11
+ }) => boolean;
5
12
  export declare const videoIteratorManager: ({ delayPlaybackHandleIfNotPremounting, canvas, context, drawDebugOverlay, logLevel, getOnVideoFrameCallback, videoTrack, getLoopSegmentMediaEndTimestamp, getStartTime, getIsLooping, getEffects, getEffectChainState, }: {
6
13
  videoTrack: InputVideoTrack;
7
14
  delayPlaybackHandleIfNotPremounting: () => DelayPlaybackIfNotPremounting;
@@ -18,16 +25,22 @@ export declare const videoIteratorManager: ({ delayPlaybackHandleIfNotPremountin
18
25
  }) => Promise<{
19
26
  startVideoIterator: (timeToSeek: number, nonce: Nonce) => Promise<void>;
20
27
  getVideoIteratorsCreated: () => number;
21
- seek: ({ newTime, nonce }: {
28
+ seek: ({ newTime, nonce, fps, playbackRate, isPlaying, }: {
22
29
  newTime: number;
23
30
  nonce: Nonce;
31
+ fps: number;
32
+ playbackRate: number;
33
+ isPlaying: boolean;
24
34
  }) => Promise<void>;
25
35
  destroy: () => void;
26
36
  getVideoFrameIterator: () => {
27
37
  destroy: () => void;
28
38
  initialFrame: WrappedCanvas | null;
29
39
  isDestroyed: () => boolean;
30
- tryToSatisfySeek: (time: number) => Promise<{
40
+ tryToSatisfySeek: (time: number, options: {
41
+ pendingFrameBehavior: "restart-iterator" | "wait";
42
+ shouldContinue: () => boolean;
43
+ }) => Promise<{
31
44
  type: "not-satisfied";
32
45
  reason: string;
33
46
  } | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/media",
3
- "version": "4.0.497",
3
+ "version": "4.0.499",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -23,16 +23,16 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "mediabunny": "1.50.8",
26
- "remotion": "4.0.497",
27
- "zod": "4.3.6"
26
+ "remotion": "4.0.499",
27
+ "zod": "4.4.3"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": ">=16.8.0",
31
31
  "react-dom": ">=16.8.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@remotion/eslint-config-internal": "4.0.497",
35
- "@remotion/player": "4.0.497",
34
+ "@remotion/eslint-config-internal": "4.0.499",
35
+ "@remotion/player": "4.0.499",
36
36
  "@vitest/browser-webdriverio": "4.0.9",
37
37
  "eslint": "9.19.0",
38
38
  "react": "19.2.3",