提供 API 以实现应用中的视频播放的库。
expo-video
是一个跨平台、高性能的视频组件,用于 React Native 和 Expo,并支持 Web。
¥expo-video
is a cross-platform, performant video component for React Native and Expo with Web support.
¥Installation
-
npx expo install expo-video
If you are installing this in an existing React Native app, start by installing expo
in your project. Then, follow the additional instructions as mentioned by the library's README under "Installation in bare React Native projects" section.
¥Configuration in app config
如果你在项目中使用配置插件(EAS 构建 或 npx expo run:[android|ios]
),则可以使用其内置的 配置插件 配置 expo-video
。该插件允许你配置无法在运行时设置的各种属性,并且需要构建新的应用二进制文件才能生效。如果你的应用不使用 EAS Build,则你需要手动配置包。
¥You can configure expo-video
using its built-in config plugin if you use config plugins in your project (EAS Build or npx expo run:[android|ios]
). The plugin allows you to configure various properties that cannot be set at runtime and require building a new app binary to take effect. If your app does not use EAS Build, then you'll need to manually configure the package.
{
"expo": {
"plugins": [
[
"expo-video",
{
"supportsBackgroundPlayback": true,
"supportsPictureInPicture": true
}
]
],
}
}
Name | Default | Description |
---|---|---|
supportsBackgroundPlayback | undefined | Only for: iOS A boolean value to enable background playback on iOS. If |
supportsPictureInPicture | undefined | A boolean value to enable Picture-in-Picture on Android and iOS. If |
¥Usage
这是一个带有播放和暂停按钮的视频的简单示例。
¥Here's a simple example of a video with a play and pause button.
import { useEvent } from 'expo';
import { useVideoPlayer, VideoView } from 'expo-video';
import { StyleSheet, View, Button } from 'react-native';
const videoSource =
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
export default function VideoScreen() {
const player = useVideoPlayer(videoSource, player => {
player.loop = true;
player.play();
});
const { isPlaying } = useEvent(player, 'playingChange', { isPlaying: player.playing });
return (
<View style={styles.contentContainer}>
<VideoView style={styles.video} player={player} allowsFullscreen allowsPictureInPicture />
<View style={styles.controlsContainer}>
<Button
title={isPlaying ? 'Pause' : 'Play'}
onPress={() => {
if (isPlaying) {
player.pause();
} else {
player.play();
}
}}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
padding: 10,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 50,
},
video: {
width: 350,
height: 275,
},
controlsContainer: {
padding: 10,
},
});
¥Receiving events
VideoPlayer
属性中的更改不会更新 React 状态。因此,要显示有关 VideoPlayer
当前状态的信息,必须监听它发出的 events。事件系统基于 expo
包中的 EventEmitter
类和 hooks。有几种方法可以监听事件:
¥The changes in properties of the VideoPlayer
do not update the React state. Therefore, to display the information about the current state of the VideoPlayer
, it is necessary to listen to the events it emits.
The event system is based on the EventEmitter
class and hooks from the expo
package. There are a few ways to listen to events:
useEvent
钩¥useEvent
hook
创建一个监听器,该监听器将返回可在组件中使用的状态值。它还会在组件卸载时自动清理。
¥Creates a listener that will return a stateful value that can be used in a component. It also cleans up automatically when the component unmounts.
import { useEvent } from 'expo';
// ... Other imports, definition of the component, creating the player etc.
const { status, error } = useEvent(player, 'statusChange', { status: player.status });
// Rest of the component...
useEventListener
钩¥useEventListener
hook
围绕 Player.addListener
和 Player.removeListener
方法构建,创建具有自动清理功能的事件监听器。
¥Built around the Player.addListener
and Player.removeListener
methods, creates an event listener with automatic cleanup.
import { useEventListener } from 'expo';
// ...Other imports, definition of the component, creating the player etc.
useEventListener(player, 'statusChange', ({ status, error }) => {
setPlayerStatus(status);
setPlayerError(error);
console.log('Player status changed: ', status);
});
// Rest of the component...
Player.addListener
方法¥Player.addListener
method
监听事件的最灵活方式,但需要手动清理和更多样板代码。
¥Most flexible way to listen to events, but requires manual cleanup and more boilerplate code.
// ...Imports, definition of the component, creating the player etc.
useEffect(() => {
const subscription = player.addListener('statusChange', ({ status, error }) => {
setPlayerStatus(status);
setPlayerError(error);
console.log('Player status changed: ', status);
});
return () => {
subscription.remove();
};
}, []);
// Rest of the component...
¥Playing local media from the assets directory
expo-video
支持播放使用 require
功能加载的本地媒体。你可以直接将结果用作源,或者如果你还想配置其他属性,则将其分配给 VideoSource
的 assetId
参数。
¥expo-video
supports playing local media loaded using the require
function. You can use the result as a source directly, or assign it to the assetId
parameter of a VideoSource
if you also want to configure other properties.
import { VideoSource } from 'expo-video';
const assetId = require('./assets/bigbuckbunny.mp4');
const videoSource: VideoSource = {
assetId,
metadata: {
title: 'Big Buck Bunny',
artist: 'The Open Movie Project',
},
};
const player1 = useVideoPlayer(assetId); // You can use the `asset` directly as a video source
const player2 = useVideoPlayer(videoSource);
¥Preloading videos
在播放另一个视频时,可以先加载视频,然后再在视图中显示它。这允许后续视频之间更快地转换并提供更好的用户体验。
¥While another video is playing, a video can be loaded before showing it in the view. This allows for quicker transitions between subsequent videos and a better user experience.
要预加载视频,你必须创建一个带有视频源的 VideoPlayer
。即使播放器未连接到 VideoView
,它也会填充缓冲区。一旦连接到 VideoView
,它将能够开始播放而无需缓冲。
¥To preload a video, you have to create a VideoPlayer
with a video source. Even when the player is not connected to a VideoView
, it will fill the buffers. Once it is connected to the VideoView
, it will be able to start playing without buffering.
在某些情况下,在屏幕生命周期的后期预加载视频是有益的。在这种情况下,应该创建一个带有 null
源的 VideoPlayer
。要开始预加载,请使用 replace()
函数将播放器源替换为视频源。
¥In some cases, it is beneficial to preload a video later in the screen lifecycle. In that case, a VideoPlayer
with a null
source should be created. To start preloading, replace the player source with a video source using the replace()
function.
以下是如何预加载视频的示例:
¥Here is an example of how to preload a video:
import { useVideoPlayer, VideoView, VideoSource } from 'expo-video';
import { useState, useCallback } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
const bigBuckBunnySource: VideoSource =
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4';
const elephantsDreamSource: VideoSource =
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4';
export default function PreloadingVideoPlayerScreen() {
const player1 = useVideoPlayer(bigBuckBunnySource, player => {
player.play();
});
const player2 = useVideoPlayer(elephantsDreamSource, player => {
player.currentTime = 20;
});
const [currentPlayer, setCurrentPlayer] = useState(player1);
const replacePlayer = useCallback(async () => {
currentPlayer.pause();
if (currentPlayer === player1) {
setCurrentPlayer(player2);
player1.pause();
player2.play();
} else {
setCurrentPlayer(player1);
player2.pause();
player1.play();
}
}, [player1, currentPlayer]);
return (
<View style={styles.contentContainer}>
<VideoView player={currentPlayer} style={styles.video} nativeControls={false} />
<TouchableOpacity style={styles.button} onPress={replacePlayer}>
<Text style={styles.buttonText}>Replace Player</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
contentContainer: {
flex: 1,
padding: 10,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 50,
},
button: {
alignItems: 'center',
justifyContent: 'center',
borderRadius: 3,
paddingVertical: 8,
paddingHorizontal: 12,
backgroundColor: '#4630ec',
},
buttonText: {
fontSize: 12,
fontWeight: 'bold',
color: '#eeeeee',
textAlign: 'center',
},
video: {
width: 300,
height: 168.75,
marginVertical: 20,
},
});
¥Using the VideoPlayer directly
在大多数情况下,应使用 useVideoPlayer
钩子来创建 VideoPlayer
实例。它管理播放器的生命周期,并确保在卸载组件时正确处理它。但是,在某些高级用例中,可能需要创建一个在卸载组件时不会自动销毁的 VideoPlayer
。在这些情况下,可以使用 createVideoPlayer
函数创建 VideoPlayer
。你需要了解这种方法带来的风险,因为当不再需要播放器时,你有责任调用 release()
方法。如果处理不当,这种方法可能会导致内存泄漏。
¥In most cases, the useVideoPlayer
hook should be used to create a VideoPlayer
instance. It manages the player's lifecycle and ensures that it is properly disposed of when the component is unmounted. However, in some advanced use cases, it might be necessary to create a VideoPlayer
that does not get automatically destroyed when the component is unmounted.
In those cases, the VideoPlayer
can be created using the createVideoPlayer
function. You need be aware of the risks that come with this approach, as it is your responsibility to call the release()
method when the player is no longer needed. If not handled properly, this approach may lead to memory leaks.
import { createVideoPlayer } from 'expo-video';
const player = createVideoPlayer(videoSource);
import { VideoView, useVideoPlayer } from 'expo-video';
Type: React.PureComponent<VideoViewProps>
boolean
• Default: true
Determines whether fullscreen mode is allowed or not.
boolean
Determines whether the player allows Picture in Picture (PiP) mode.
Note: The
supportsPictureInPicture
property of the config plugin has to be configured for the PiP to work.
boolean
• Default: true
Specifies whether to perform video frame analysis (Live Text in videos). Check official Apple documentation for more details.
VideoContentFit
• Default: 'contain'
Describes how the video should be scaled to fit in the container.
Options are 'contain'
, 'cover'
, and 'fill'
.
{
dx: number,
dy: number
}
Determines the position offset of the video inside the container.
boolean
• Default: true
Determines whether native controls should be displayed or not.
() => void
A callback to call after the video player enters fullscreen mode.
() => void
A callback to call after the video player exits fullscreen mode.
() => void
A callback to call after the video player enters Picture in Picture (PiP) mode.
() => void
A callback to call after the video player exits Picture in Picture (PiP) mode.
VideoPlayer
A video player instance. Use useVideoPlayer()
hook to create one.
boolean
• Default: false
Determines whether the player allows the user to skip media content.
boolean
• Default: true
Determines whether the timecodes should be displayed or not.
boolean
• Default: false
Determines whether the player should start Picture in Picture (PiP) automatically when the app is in the background.
Note: Only one player can be in Picture in Picture (PiP) mode at a time.
Note: The
supportsPictureInPicture
property of the config plugin has to be configured for the PiP to work.
Enters fullscreen mode.
Promise<void>
Exits fullscreen mode.
Promise<void>
Enters Picture in Picture (PiP) mode. Throws an exception if the device does not support PiP.
Note: Only one player can be in Picture in Picture (PiP) mode at a time.
Note: The
supportsPictureInPicture
property of the config plugin has to be configured for the PiP to work.
Promise<void>
Exits Picture in Picture (PiP) mode.
Promise<void>
Parameter | Type | Description |
---|---|---|
source | VideoSource | A video source that is used to initialize the player. |
setup(optional) | (player: VideoPlayer) => void | A function that allows setting up the player. It will run after the player is created. |
Creates a VideoPlayer
, which will be automatically cleaned up when the component is unmounted.
VideoPlayer
Type: Class extends SharedObject<VideoPlayerEvents>
A class that represents an instance of the video player.
VideoPlayer Properties
boolean
• Default: true
Determines whether the player should allow external playback.
AudioMixingMode
• Default: 'auto'
Determines how the player will interact with other audio playing in the system.
SubtitleTrack[]
An array of subtitle tracks available for the current video.
number
Float value indicating how far the player has buffered the video in seconds.
This value is 0 when the player has not buffered up to the current playback time. When it's impossible to determine the buffer state (for example, when the player isn't playing any media), this value is -1.
BufferOptions
Specifies buffer options which will be used by the player when buffering the video.
You should provide a
BufferOptions
object when setting this property. Setting individual buffer properties is not supported.
null | number
The exact timestamp when the currently displayed video frame was sent from the server,
based on the EXT-X-PROGRAM-DATE-TIME
tag in the livestream metadata.
If this metadata is missing, this property will return null
.
null | number
Float value indicating the latency of the live stream in seconds.
If a livestream doesn't have the required metadata, this will return null
.
number
Float value indicating the current playback time in seconds.
If the player is not yet playing, this value indicates the time position
at which playback will begin once the play()
method is called.
Setting currentTime
to a new value seeks the player to the given time.
number
Float value indicating the duration of the current video in seconds.
boolean
Boolean value indicating whether the player is currently playing a live stream.
boolean
• Default: false
Determines whether the player should automatically replay after reaching the end of the video.
boolean
• Default: false
Boolean value whether the player is currently muted.
Setting this property to true
/false
will mute/unmute the player.
number
• Default: 1.0
Float value between 0
and 16.0
indicating the current playback speed of the player.
boolean
Boolean value whether the player is currently playing.
Use
play
andpause
methods to control the playback.
boolean
• Default: true
Boolean value indicating if the player should correct audio pitch when the playback speed changes.
boolean
• Default: false
Boolean value determining whether the player should show the now playing notification.
VideoPlayerStatus
Indicates the current status of the player.
boolean
• Default: false
Determines whether the player should continue playing after the app enters the background.
null | SubtitleTrack
• Default: null
Specifies the subtitle track which is currently displayed by the player. null
when no subtitles are displayed.
To ensure a valid subtitle track, always assign one of the subtitle tracks from the
availableSubtitleTracks
array.
number
• Default: 0
Float value indicating the interval in seconds at which the player will emit the timeUpdate
event.
When the value is equal to 0
, the event will not be emitted.
VideoPlayer Methods
Parameter | Type |
---|---|
times | number | number[] |
Generates thumbnails from the currently played asset. The thumbnails are references to native images,
thus they can be used as a source of the Image
component from expo-image
.
Promise<VideoThumbnail[]>
Type: Class extends SharedRef<'image'>
Represents a video thumbnail that references a native image.
Instances of this class can be passed as a source to the Image
component from expo-image
.
VideoThumbnail Properties
Parameter | Type |
---|---|
source | VideoSource |
Creates a direct instance of VideoPlayer
that doesn't release automatically.
For most use cases you should use theuseVideoPlayer
hook instead. See the Using the VideoPlayer Directly section for more details.
VideoPlayer
Returns whether the current device supports Picture in Picture (PiP) mode.
Note: All major web browsers support Picture in Picture (PiP) mode except Firefox. For more information, see MDN web docs.
boolean
A boolean
which is true
if the device supports PiP mode, and false
otherwise.
Literal Type: string
Specifies the audio mode that the player should use. Audio mode is set on per-app basis, if there are multiple players playing and
have different a AudioMode
specified, the highest priority mode will be used. Priority order: 'doNotMix' > 'auto' > 'duckOthers' > 'mixWithOthers'.
mixWithOthers
: The player will mix its audio output with other apps.duckOthers
: The player will lower the volume of other apps if any of the active players is outputting audio.auto
: The player will allow other apps to keep playing audio only when it is muted. On iOS it will always interrupt other apps when showNowPlayingNotification
is true
due to system requirements.doNotMix
: The player will pause playback in other apps, even when it's muted.On iOS, the Now Playing notification is dependent on the audio mode. If the audio mode is different from
doNotMix
orauto
this feature will not work.
Acceptable values are: 'mixWithOthers'
| 'duckOthers'
| 'auto'
| 'doNotMix'
Specifies buffer options which will be used by the player when buffering the video.
Property | Type | Description |
---|---|---|
maxBufferBytes(optional) | number | null | Only for: Android The maximum number of bytes that the player can buffer from the network. When 0 the player will automatically decide appropriate buffer size. Default: 0 |
minBufferForPlayback(optional) | number | Only for: Android Minimum duration of the buffer in seconds required to continue playing after the player has been paused or started buffering.
Default: 2 |
preferredForwardBufferDuration(optional) | number | Only for: Android iOS The duration in seconds which determines how much media the player should buffer ahead of the current playback time. On iOS when set to Equivalent to Default: Android: 20, iOS: 0 |
prioritizeTimeOverSizeThreshold(optional) | boolean | Only for: Android A Boolean value which determines whether the player should prioritize time over size when buffering media. Default: false |
waitsToMinimizeStalling(optional) | boolean | Only for: iOS A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling. Equivalent to Default: true |
Specifies DRM options which will be used by the player while loading the video.
Property | Type | Description |
---|---|---|
base64CertificateData(optional) | string | Only for: iOS Specifies the base64 encoded certificate data for the FairPlay DRM.
When this property is set, the |
certificateUrl(optional) | string | Only for: iOS Specifies the certificate URL for the FairPlay DRM. |
contentId(optional) | string | Only for: iOS Specifies the content ID of the stream. |
headers(optional) | Record<string, string> | Determines headers sent to the license server on license requests. |
licenseServer | string | Determines the license server URL. |
multiKey(optional) | boolean | Only for: Android Specifies whether the DRM is a multi-key DRM. |
type | DRMType | Determines which type of DRM to use. |
Literal Type: string
Specifies which type of DRM to use:
Acceptable values are: 'clearkey'
| 'fairplay'
| 'playready'
| 'widevine'
Data delivered with the mutedChange
event.
Property | Type | Description |
---|---|---|
muted | boolean | Boolean value whether the player is currently muted. |
oldMuted(optional) | boolean | Previous value of the |
Data delivered with the playbackRateChange
event.
Property | Type | Description |
---|---|---|
oldPlaybackRate(optional) | number | Previous value of the |
playbackRate | number | Float value indicating the current playback speed of the player. |
Contains information about any errors that the player encountered during the playback
Property | Type | Description |
---|---|---|
message | string | - |
Data delivered with the playingChange
event.
Property | Type | Description |
---|---|---|
isPlaying | boolean | Boolean value whether the player is currently playing. |
oldIsPlaying(optional) | boolean | Previous value of the |
Data delivered with the sourceChange
event.
Property | Type | Description |
---|---|---|
oldSource(optional) | VideoSource | Previous source of the player. |
source | VideoSource | New source of the player. |
Data delivered with the statusChange
event.
Property | Type | Description |
---|---|---|
error(optional) | PlayerError | Error object containing information about the error that occurred. |
oldStatus(optional) | VideoPlayerStatus | Previous status of the player. |
status | VideoPlayerStatus | New status of the player. |
Data delivered with the timeUpdate
event, contains information about the current playback progress.
Property | Type | Description |
---|---|---|
bufferedPosition | number | Only for: Android iOS Float value indicating how far the player has buffered the video in seconds.
Same as the |
currentLiveTimestamp | number | null | Only for: Android iOS The exact timestamp when the currently displayed video frame was sent from the server,
based on the |
currentOffsetFromLive | number | null | Only for: Android iOS Float value indicating the latency of the live stream in seconds.
Same as the |
currentTime | number | Float value indicating the current playback time in seconds. Same as the |
Literal Type: string
Describes how a video should be scaled to fit in a container.
contain
: The video maintains its aspect ratio and fits inside the container, with possible letterboxing/pillarboxing.cover
: The video maintains its aspect ratio and covers the entire container, potentially cropping some portions.fill
: The video stretches/squeezes to completely fill the container, potentially causing distortion.Acceptable values are: 'contain'
| 'cover'
| 'fill'
Contains information that will be displayed in the now playing notification when the video is playing.
Property | Type | Description |
---|---|---|
artist(optional) | string | Only for: Android iOS Secondary text that will be displayed under the title. |
artwork(optional) | string | Only for: Android iOS The uri of the video artwork. |
title(optional) | string | Only for: Android iOS The title of the video. |
Handlers for events which can be emitted by the player.
Property | Type | Description |
---|---|---|
availableSubtitleTracksChange | (payload: AvailableSubtitleTracksChangeEventPayload) => void | Handler for an event emitted when the available subtitle tracks change. |
mutedChange | (payload: MutedChangeEventPayload) => void | Handler for an event emitted when the |
playbackRateChange | (payload: PlaybackRateChangeEventPayload) => void | Handler for an event emitted when the |
playingChange | (payload: PlayingChangeEventPayload) => void | Handler for an event emitted when the player starts or stops playback. |
playToEnd | () => void | Handler for an event emitted when the player plays to the end of the current source. |
sourceChange | (payload: SourceChangeEventPayload) => void | Handler for an event emitted when the current media source of the player changes. |
statusChange | (payload: StatusChangeEventPayload) => void | Handler for an event emitted when the status of the player changes. |
subtitleTrackChange | (payload: SubtitleTrackChangeEventPayload) => void | Handler for an event emitted when the current subtitle track changes. |
timeUpdate | (payload: TimeUpdateEventPayload) => void | Handler for an event emitted in a given interval specified by the |
volumeChange | (payload: VolumeChangeEventPayload) => void | Handler for an event emitted when the |
Literal Type: string
Describes the current status of the player.
idle
: The player is not playing or loading any videos.loading
: The player is loading video data from the provided sourcereadyToPlay
: The player has loaded enough data to start playing or to continue playback.error
: The player has encountered an error while loading or playing the video.Acceptable values are: 'idle'
| 'loading'
| 'readyToPlay'
| 'error'
Type: string
or number
or null
or object
shaped as below:
Property | Type | Description |
---|---|---|
assetId(optional) | number | The asset ID of a local video asset, acquired with the |
drm(optional) | DRMOptions | Specifies the DRM options which will be used by the player while loading the video. |
headers(optional) | Record<string, string> | Only for: Android iOS Specifies headers sent with the video request.
|
metadata(optional) | VideoMetadata | Only for: Android iOS Specifies information which will be displayed in the now playing notification. When undefined the player will display information contained in the video metadata. |
uri(optional) | string | The URI of the video. This property is exclusive with the |
Data delivered with the volumeChange
event.
Property | Type | Description |
---|---|---|
oldVolume(optional) | number | Previous value of the |
volume | number | Float value indicating the current volume of the player. |