Remotion LabRemotion Lab
PlayerPlayer 緩衝狀態管理

Player 緩衝狀態管理

了解如何在 Remotion Player 中管理緩衝狀態,包括如何暫停播放等待內容載入,以及如何監聽緩衝事件以提供流暢的播放體驗。

Player 緩衝狀態管理

此功能自 4.0.111 版起可用

與一般影片播放器相同,Player 中顯示的內容有可能尚未完全載入。在這種情況下,最佳做法是短暫暫停影片,等待內容載入完成後再繼續播放。

Remotion 提供原生的緩衝狀態機制,可在緩衝區為空時暫停影片播放。

快速摘要

  • 可在 <Video><Audio><OffthreadVideo> 標籤上加入 pauseWhenBuffering prop
  • 對於 <Img> 標籤,此 prop 名稱為 pauseWhenLoading
  • 對於來自 @remotion/media<Video><Audio>,緩衝狀態預設已啟用

加入後,Player 將短暫暫停直到你的媒體載入完成。

運作機制

啟動緩衝狀態

元件可透過 useBufferState() hook 並呼叫 buffer.delayPlayback() 來告知 Player 切換至緩衝狀態:

// MyComp.tsx
import React from 'react';
import { useBufferState } from 'remotion';
 
const MyComp: React.FC = () => {
  const buffer = useBufferState();
 
  React.useEffect(() => {
    const delayHandle = buffer.delayPlayback();
 
    setTimeout(() => {
      delayHandle.unblock();
    }, 5000);
 
    return () => {
      delayHandle.unblock();
    };
  }, []);
 
  return null;
};

呼叫 delayPlayback() 的回傳值上的 .unblock() 方法即可清除緩衝狀態。

啟動緩衝狀態時的注意事項

元件卸載時請清除 handle

使用者可能會跳轉至影片的其他部分,而那個部分的內容是立即可用的。請使用 useEffect() 的清除函式,在元件卸載時清除 handle。

以下是一個有問題的範例(與 React 嚴格模式不相容):

// 此寫法會在 React 嚴格模式下造成問題
import React, { useState } from 'react';
import { useBufferState } from 'remotion';
 
const MyComp: React.FC = () => {
  const buffer = useBufferState();
  const [delayHandle] = useState(() => buffer.delayPlayback()); // 有問題
 
  React.useEffect(() => {
    setTimeout(() => {
      delayHandle.unblock();
    }, 5000);
  }, []);
 
  return <></>;
};

不要在 useState() 中使用 delayPlayback()

雖然以下實作在生產環境中可能有效,但在 React 嚴格模式下會失敗,因為 useState() hook 被呼叫了兩次,導致第一次呼叫的緩衝狀態永遠無法被清除。

// 此寫法在跳轉到其他影格時不會清除緩衝 handle
import React, { useState } from 'react';
import { useBufferState } from 'remotion';
 
const MyComp: React.FC = () => {
  const buffer = useBufferState();
  const [delayHandle] = useState(() => buffer.delayPlayback()); // 有問題
 
  React.useEffect(() => {
    setTimeout(() => {
      delayHandle.unblock();
    }, 5000);
 
    return () => {
      delayHandle.unblock(); // 跳轉時不會呼叫此處
    };
  }, []);
 
  return <></>;
};

關於 delayRender() 的說明

緩衝狀態不能替代 delayRender()

delayRender() 是另一個 API,用於控制渲染期間截圖的時機。如果你在載入資料,你可能同時需要:

  • 延遲合成的截圖(渲染時使用 delayRender()
  • 延遲影片在預覽中的播放(預覽時使用 delayPlayback()

在這種情況下,你需要同時使用這兩個 API:

// 同時使用 delayRender() 和 delayPlayback()
import React from 'react';
import { useBufferState, delayRender, continueRender } from 'remotion';
 
const MyComp: React.FC = () => {
  const buffer = useBufferState();
  const [handle] = React.useState(() => delayRender());
 
  React.useEffect(() => {
    const delayHandle = buffer.delayPlayback();
 
    setTimeout(() => {
      delayHandle.unblock();
      continueRender(handle);
    }, 5000);
 
    return () => {
      delayHandle.unblock();
    };
  }, []);
 
  return <></>;
};

可能的播放狀態

緩衝與否並不會改變 Player 內部的播放/暫停狀態。因此,Player 可能處於以下四種播放狀態之一:

狀態說明
playing && !buffering正在播放且不在緩衝
playing && buffering正在播放但同時在緩衝(時間暫停)
paused && !buffering已暫停且不在緩衝
paused && buffering已暫停且在緩衝

只有在狀態 1(playing && !buffering)時,時間才會前進。

預設 UI 行為

Remotion 預設會根據 Player 的狀態顯示以下 UI:

  • 狀態 1:顯示暫停按鈕
  • 狀態 2:起初顯示暫停按鈕,短暫延遲後顯示可自訂的載入旋轉圖示
  • 其他狀態:顯示播放按鈕

你可以在此基礎上添加額外的 UI,例如在 Player 緩衝時疊加一個旋轉載入圖示。

監聽緩衝事件

<Player> 進入緩衝狀態時,它會發出 waiting 事件;恢復播放後,它會發出 resume 事件。

// 監聽 waiting 和 resume 事件
import { Player, PlayerRef } from '@remotion/player';
import { useEffect, useRef, useState } from 'react';
import { MyVideo } from './remotion/MyVideo';
 
export const App: React.FC = () => {
  const playerRef = useRef<PlayerRef>(null);
  const [isBuffering, setIsBuffering] = useState(false);
 
  useEffect(() => {
    const { current } = playerRef;
    if (!current) {
      return;
    }
 
    const onWaiting = () => {
      setIsBuffering(true);
    };
 
    const onResume = () => {
      setIsBuffering(false);
    };
 
    current.addEventListener('waiting', onWaiting);
    current.addEventListener('resume', onResume);
 
    return () => {
      current.removeEventListener('waiting', onWaiting);
      current.removeEventListener('resume', onResume);
    };
  }, []);
 
  return (
    <div style={{ position: 'relative' }}>
      <Player
        ref={playerRef}
        component={MyVideo}
        durationInFrames={120}
        compositionWidth={1920}
        compositionHeight={1080}
        fps={30}
        controls
      />
      {isBuffering && (
        <div
          style={{
            position: 'absolute',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            color: 'white',
            fontSize: 24,
            backgroundColor: 'rgba(0,0,0,0.5)',
            padding: '10px 20px',
            borderRadius: 8,
          }}
        >
          載入中...
        </div>
      )}
    </div>
  );
};

在媒體標籤上使用 pauseWhenBuffering

<Video><Audio> 等媒體標籤支援 pauseWhenBuffering prop,可讓 Player 在媒體尚未準備好時自動暫停:

import { Video, Audio } from 'remotion';
 
const MyComp: React.FC = () => {
  return (
    <>
      <Video
        src="https://example.com/video.mp4"
        pauseWhenBuffering
      />
      <Audio
        src="https://example.com/audio.mp3"
        pauseWhenBuffering
      />
    </>
  );
};

相關資源