Player 緩衝狀態管理
了解如何在 Remotion Player 中管理緩衝狀態,包括如何暫停播放等待內容載入,以及如何監聽緩衝事件以提供流暢的播放體驗。
Player 緩衝狀態管理
此功能自 4.0.111 版起可用
與一般影片播放器相同,Player 中顯示的內容有可能尚未完全載入。在這種情況下,最佳做法是短暫暫停影片,等待內容載入完成後再繼續播放。
Remotion 提供原生的緩衝狀態機制,可在緩衝區為空時暫停影片播放。
快速摘要
- 可在
<Video>、<Audio>、<OffthreadVideo>標籤上加入pauseWhenBufferingprop - 對於
<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
/>
</>
);
};