Skip to main content

播放器缓冲状态

可从4.0.111获取

🌐 available from 4.0.111

就像普通的视频播放器一样,播放器中显示的内容可能尚未完全加载。
在这种情况下,最佳做法是短暂暂停视频,让内容加载,然后再继续播放。

🌐 Just like regular video players, it is possible that the content being displayed in the Player is not yet fully loaded.
In this case, the best practice is to briefly pause the video, let the content load and then resume playback.

Remotion有一个本地缓冲状态,可以在缓冲为空时暂停视频。

🌐 Remotion has a native buffer state, which can be used to pause the video when the buffer is empty.

摘要

🌐 TL;DR

你可以向你的 <Html5Video><OffthreadVideo><Html5Audio> 标签添加 pauseWhenBuffering 属性。
对于 <Img> 标签,该属性称为 pauseWhenLoading
对于来自 @remotion/media<Video><Audio>,缓冲状态默认启用。
这样做后,播放器将在你的媒体加载完成之前短暂暂停。

🌐 You can add a pauseWhenBuffering prop to your <Html5Video>, <OffthreadVideo>, <Html5Audio> tags.
The prop is called pauseWhenLoading for <Img> tags.
For <Video> and <Audio> from @remotion/media, the buffer state is enabled by default. By doing so, the Player will briefly pause until your media is loaded.

机制

🌐 Mechanism

激活缓冲状态

🌐 Activating the buffer state

一个组件可以通过先使用useBufferState() 钩子,然后调用 buffer.delayPlayback(),来告诉播放器切换到缓冲状态:

🌐 A component can tell the player to switch into a buffer state by first using the useBufferState() hook and then calling buffer.delayPlayback():

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()

🌐 To clear the handle, call .unblock() on the return value of delayPlayback().

在激活缓冲状态时,请注意以下事项:

🌐 When activating the buffer state, pay attention to the following:

在组件卸载时清除句柄

用户可能会跳转到视频的其他部分,该部分可以立即访问。使用 useEffect() 的清理函数在组件卸载时清除句柄。

❌ Causes problems with React strict mode
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() 钩子被调用了两次,这导致缓冲区的第一次调用永远不会被清除。

🌐 While the following implementation works in production, it fails in React Strict mode, because the useState() hook is called twice, which causes the first invocation of the buffer to never be cleared.

❌ Doesn't clear the buffer handle when seeking to a different portion of a video
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 <></>; };
Details

它并不替代 delayRender() delayRender() 是一个不同的 API,用于控制在渲染过程中何时截图。

如果你正在加载数据,你可能希望在渲染期间延迟截图你的组件,并在预览中延迟视频播放,在这种情况下你需要同时使用这两个 API。

🌐 If you are loading data, you might want to delay the screenshotting of your component during rendering and delay the playback of the video in preview, in which case you need to use both APIs together.

Using delayRender() and delayPlayback() together
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 <></>; };

可能的状态

🌐 Possible states

播放器是否在缓冲并不会内部改变 playing / paused 状态。
因此,播放器可以处于四种播放状态:

🌐 Whether a player is buffering does not internally change the playing / paused state.
Therefore, a player can be in four playback states:

1
播放中 && !缓冲中
2
播放中 && 缓冲中
3
已暂停 && !缓冲中
4
已暂停 && 缓冲中

只有在状态

1
中时间才会向前移动。


默认情况下,Remotion 将根据播放器的状态显示以下用户界面:

🌐 By default, Remotion will display the following UI based on the state of the player:

当处于状态

1
时,会显示暂停按钮。

当处于状态

2
时,开始会显示暂停按钮,然后在 一段延迟后,会显示 可自定义的加载旋转图标

否则,将显示播放按钮。

你可以为此添加额外的用户界面,例如在播放器缓冲时在播放器上覆盖一个旋转器。

🌐 You may add additional UI to this, for example by overlaying the Player with a spinner when the Player is buffering.

监听缓冲事件

🌐 Listening to buffer events

如果 <Player /> 进入缓冲状态,它将触发 waiting 事件。 一旦恢复,它将触发 resume 事件。

🌐 If the <Player /> is entering a buffer state, it will emit the waiting event.
Once it resumes, it emits the resume event.

Listening to waiting and resume events
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 [buffering, setBuffering] = useState(false); useEffect(() => { const {current} = playerRef; if (!current) { return; } const onBuffering = () => { setBuffering(true); }; const onResume = () => { setBuffering(false); }; current.addEventListener('waiting', onBuffering); current.addEventListener('resume', onResume); return () => { current.removeEventListener('waiting', onBuffering); current.removeEventListener('resume', onResume); }; }, [setBuffering]); return <Player ref={playerRef} component={MyVideo} durationInFrames={120} compositionWidth={1920} compositionHeight={1080} fps={30} />; };

带内置缓冲的组件

🌐 Components with built-in buffering

你可以在以下组件上启用缓冲:

🌐 You can enable buffering on the following components:

在用户界面中显示缓冲

🌐 Indicating buffering in the UI

当播放器正在缓冲时,默认情况下播放按钮将被一个旋转图标替代。 为了防止界面跳动,这个旋转图标只有在播放器缓冲状态持续了300毫秒后才会显示。

🌐 When the Player is buffering, by default the Play button will be replaced with a spinner.
To prevent a janky UI, this spinner will only be shown after the Player has been in a buffering state for 300ms.

你可以通过将 bufferStateDelayInMilliseconds 属性传递给 <Player /> 组件,自定义 300 毫秒的超时。

🌐 You may customize the timeout of 300 milliseconds by passing the bufferStateDelayInMilliseconds prop to the <Player /> component.

Setting the delay until the spinner is shown
import {Player, PlayerRef} from '@remotion/player'; import {useEffect, useRef, useState} from 'react'; import {MyVideo} from './remotion/MyVideo'; export const App: React.FC = () => { return ( <Player component={MyVideo} durationInFrames={120} compositionWidth={1920} compositionHeight={1080} fps={30} bufferStateDelayInMilliseconds={1000} // Or set to `0` to immediately show the spinner /> ); };

在工作室中,你可以在 配置文件 中更改延迟:

🌐 In the Studio, you can change the delay in the config file:

remotion.config.ts
import {Config} from '@remotion/cli/config'; Config.setBufferStateDelayInMilliseconds(0);

要自定义替代播放按钮显示的加载旋转器,你可以传入一个 renderPlayPauseButton() 属性:

🌐 To customize the spinner that is shown in place of the Play button, you can pass a renderPlayPauseButton() prop:

Rendering a custom spinner inside the Play button
import {Player, RenderPlayPauseButton} from '@remotion/player'; import {useCallback} from 'react'; export const App: React.FC = () => { const renderPlayPauseButton: RenderPlayPauseButton = useCallback(({playing, isBuffering}) => { if (playing && isBuffering) { return <MySpinner />; } return null; }, []); return <Player component={MyVideo} durationInFrames={120} compositionWidth={1920} compositionHeight={1080} fps={30} renderPlayPauseButton={renderPlayPauseButton} />; };

要在播放器上显示加载 UI(例如旋转图标),你可以将 showPosterWhenBuffering 设置为 true 并传递 renderPoster() 属性:

🌐 To display a loading UI layered on top of the Player (e.g. a spinner), you can set showPosterWhenBuffering to true and pass a renderPoster() prop:

Rendering a custom spinner on top of the Player
import type {RenderPoster} from '@remotion/player'; import {Player} from '@remotion/player'; const MyApp: React.FC = () => { const renderPoster: RenderPoster = useCallback(({isBuffering}) => { if (isBuffering) { return ( <AbsoluteFill style={{justifyContent: 'center', alignItems: 'center'}}> <Spinner /> </AbsoluteFill> ); } return null; }, []); return <Player fps={30} component={Component} durationInFrames={100} compositionWidth={1080} compositionHeight={1080} renderPoster={renderPoster} showPosterWhenBuffering />; };

Remotion 5.0 即将到来的变化

🌐 Upcoming changes in Remotion 5.0

在 Remotion 4.0 中,标签 <Html5Audio><Html5Video><OffthreadVideo> 需要选择加入才能使用缓冲状态。

🌐 In Remotion 4.0, the tags <Html5Audio>, <Html5Video>, <OffthreadVideo> will need to opt-in to use the buffer state.

在 Remotion 5.0 中,计划 <Html5Audio><Html5Video><OffthreadVideo> 将自动使用缓冲状态,但它们可以选择不使用。

🌐 In Remotion 5.0, it is planned that <Html5Audio>, <Html5Video> and <OffthreadVideo> will automatically use the buffer state, but they can opt out of it.

另请参阅

🌐 See also