显示缓冲状态
自 Remotion v4.0.111 起,Remotion 具有 本机缓冲状态。本页描述的技术仅适用于 Remotion 的旧版本。
在您的 <Player>
中,您可能会有视频和其他资产,这些资产在进入场景后可能需要一些时间来加载。
您可以预加载这些资产,但有时浏览器策略会阻止预加载,并且在浏览器需要解码视频之前播放时可能会出现短暂的闪烁。
在这种情况下,如果媒体正在加载,您可能希望暂停播放器并显示一个旋转图标,一旦媒体准备好播放,就恢复视频播放。这可以使用常规 Web API 和 React 原语来实现。
参考应用程序
访问此 GitHub 存储库 查看此技术的完全功能示例。
实现缓冲状态
我 们创建一个新的 React 上下文,可以处理播放器内媒体的缓冲状态。我们实现了默认函数,因为在渲染期间不需要缓冲状态。
BufferManager.tsxtsx
import { createContext } from "react";type BufferState = { [key: string]: boolean };type BufferContextType = {canPlay: (id: string) => void;needsToBuffer: (id: string) => void;};export const BufferContext = createContext<BufferContextType>({// By default, do nothing if the context is not set, for example in renderingcanPlay: () => {},needsToBuffer: () => {},});
BufferManager.tsxtsx
import { createContext } from "react";type BufferState = { [key: string]: boolean };type BufferContextType = {canPlay: (id: string) => void;needsToBuffer: (id: string) => void;};export const BufferContext = createContext<BufferContextType>({// By default, do nothing if the context is not set, for example in renderingcanPlay: () => {},needsToBuffer: () => {},});
可以将以下组件包装在播放器周围,以为其提供 onBuffer
和 onContinue
函数。通过使用上下文,我们不必将这些函数作为 props 传递给每个媒体元素,尽管这也是可能的。
如果一个媒体元素正在缓冲,它可以使用 onBuffer()
将其注册到管理器中。如果所有媒体元素都已加载,缓冲管理器将调用 onContinue()
事件。
BufferManager.tsxtsx
import {useCallback ,useMemo ,useRef } from "react";export constBufferManager :React .FC <{children :React .ReactNode ;onBuffer : () => void;onContinue : () => void;}> = ({children ,onBuffer ,onContinue }) => {constbufferState =useRef <BufferState >({});constcurrentState =useRef (false);constsendEvents =useCallback (() => {letpreviousState =currentState .current ;currentState .current =Object .values (bufferState .current ).some (Boolean );if (currentState .current && !previousState ) {onBuffer ();} else if (!currentState .current &&previousState ) {onContinue ();}}, [onBuffer ,onContinue ]);constcanPlay =useCallback ((id : string) => {bufferState .current [id ] = false;sendEvents ();},[sendEvents ],);constneedsToBuffer =useCallback ((id : string) => {bufferState .current [id ] = true;sendEvents ();},[sendEvents ],);constbufferEvents =useMemo (() => {return {canPlay ,needsToBuffer ,};}, [canPlay ,needsToBuffer ]);return (<BufferContext .Provider value ={bufferEvents }>{children }</BufferContext .Provider >);};
BufferManager.tsxtsx
import {useCallback ,useMemo ,useRef } from "react";export constBufferManager :React .FC <{children :React .ReactNode ;onBuffer : () => void;onContinue : () => void;}> = ({children ,onBuffer ,onContinue }) => {constbufferState =useRef <BufferState >({});constcurrentState =useRef (false);constsendEvents =useCallback (() => {letpreviousState =currentState .current ;currentState .current =Object .values (bufferState .current ).some (Boolean );if (currentState .current && !previousState ) {onBuffer ();} else if (!currentState .current &&previousState ) {onContinue ();}}, [onBuffer ,onContinue ]);constcanPlay =useCallback ((id : string) => {bufferState .current [id ] = false;sendEvents ();},[sendEvents ],);constneedsToBuffer =useCallback ((id : string) => {bufferState .current [id ] = true;sendEvents ();},[sendEvents ],);constbufferEvents =useMemo (() => {return {canPlay ,needsToBuffer ,};}, [canPlay ,needsToBuffer ]);return (<BufferContext .Provider value ={bufferEvents }>{children }</BufferContext .Provider >);};
使 <Video>
报告缓冲
以下组件 <PausableVideo>
包装了 <Video>
标签,因此您可以使用它来替代它。它获取了我们之前定义的上下文,并向 BufferManager
报告视频的缓冲和恢复。
如果您正在使用<OffthreadVideo>
,则不能将引用附加到它上。
使用此技术 仅在渲染期间使用<OffthreadVideo>
。
将您 Remotion 组件中的 <Video>
元素替换为 <PausableVideoFunction>
,以使其报告缓冲状态。
暂停视频并显示加载 UI
将您的播放器包装在新创建的 <BufferManager>
中。创建两个函数 onBuffer
和 onContinue
,实现视频进入缓冲状态时应该发生的情况。将它们传递给 <BufferManager>
。
在此示例中,正在使用引用来跟踪视频是否因缓冲而暂停,以便在这种情况下仅恢复视频。
通过使用引用,我们消除了异步 React 状态导致竞争条件的风险。
App.tsxtsx
import{ Pla yer,PlayerRef } from "@remotion /player";import React, { useState, useRef, useCallback } from "react";import { BufferManager } from"./BufferManager"; function App() {const playerRef = useRef<PlayerRef>(null);const [buffering, setBuffering] = useState(false);constpausedBecauseOfBuffering = useRef(false); const onBuffer = useCallback(() => {setBuffering(true);playerRef.curren t?.pause();pausedBecauseOfBuffering.current = true;}, []);const onContinue = useCallback(() => {setBuffering(false); // Play only if we paused because of bufferingif (pausedBecauseOfBuffering.current) {pausedBecauseOfBufferin g.current = false;playerRef.current?.play(); }}, []);return (<BufferManager onBuffer={onBuffer} onContinue={onContinue}><Playerref={playerRef}component={MyComp} compositionHeight={720} compositionWidth={1280}durationInFrames={200}fps={30}controls/></BufferManager>);}export default App;
App.tsxtsx
import{ Pla yer,PlayerRef } from "@remotion /player";import React, { useState, useRef, useCallback } from "react";import { BufferManager } from"./BufferManager"; function App() {const playerRef = useRef<PlayerRef>(null);const [buffering, setBuffering] = useState(false);constpausedBecauseOfBuffering = useRef(false); const onBuffer = useCallback(() => {setBuffering(true);playerRef.curren t?.pause();pausedBecauseOfBuffering.current = true;}, []);const onContinue = useCallback(() => {setBuffering(false); // Play only if we paused because of bufferingif (pausedBecauseOfBuffering.current) {pausedBecauseOfBufferin g.current = false;playerRef.current?.play(); }}, []);return (<BufferManager onBuffer={onBuffer} onContinue={onContinue}><Playerref={playerRef}component={MyComp} compositionHeight={720} compositionWidth={1280}durationInFrames={200}fps={30}controls/></BufferManager>);}export default App;
除了暂停视频外,您还可以显示自定义 UI,该 UI 将在视频缓冲时覆盖视频。通常,您会显示一个品牌化的旋转器,在这个简化的示例中,我们显示了一个 ⏳ 表情符号。
App.tsxtsx
import {Player ,RenderPoster } from "@remotion/player";import {useCallback ,useState } from "react";import {AbsoluteFill } from "remotion";functionApp () {const [buffering ,setBuffering ] =useState ();// Add this to your component rendering the <Player>constrenderPoster :RenderPoster =useCallback (() => {if (buffering ) {return (<AbsoluteFill style ={{justifyContent : "center",alignItems : "center",fontSize : 100,}}>⏳</AbsoluteFill >);}return null;}, [buffering ]);return (<Player fps ={30}component ={MyComp }compositionHeight ={720}compositionWidth ={1280}durationInFrames ={200}// Add these two props to the PlayershowPosterWhenPaused renderPoster ={renderPoster }/>);}
App.tsxtsx
import {Player ,RenderPoster } from "@remotion/player";import {useCallback ,useState } from "react";import {AbsoluteFill } from "remotion";functionApp () {const [buffering ,setBuffering ] =useState ();// Add this to your component rendering the <Player>constrenderPoster :RenderPoster =useCallback (() => {if (buffering ) {return (<AbsoluteFill style ={{justifyContent : "center",alignItems : "center",fontSize : 100,}}>⏳</AbsoluteFill >);}return null;}, [buffering ]);return (<Player fps ={30}component ={MyComp }compositionHeight ={720}compositionWidth ={1280}durationInFrames ={200}// Add these two props to the PlayershowPosterWhenPaused renderPoster ={renderPoster }/>);}