源碼在此 僅供參考
一、簡(jiǎn)介
react-native-video是一個(gè)可以播放視頻的組件,它的用法簡(jiǎn)單,功能完備,是React native項(xiàng)目中很好用的一個(gè)Video播放的組件。
它的功能主要有以下10個(gè):
- 控制播放速率
- 控制音量大小
- 支持靜音功能
- 支持播放和暫停
- 支持后臺(tái)音頻播放
- 支持定制樣式,比如設(shè)置寬高
- 豐富的事件調(diào)用,如onLoad,onEnd,onProgress,onBuffer等等,可以通過對(duì)應(yīng)的事件進(jìn)行UI上的定制處理,如onBuffer時(shí)我們可以顯示一個(gè)進(jìn)度條提示用戶視頻正在緩沖。
- 支持全屏播放,使用presentFullscreenPlayer方法。這個(gè)方法在iOS上可行,在android上不起作用。參看issue#534,#726也是同樣的問題。
- 支持跳轉(zhuǎn)進(jìn)度,使用seek方法跳轉(zhuǎn)到指定的地方進(jìn)行播放
- 可以加載遠(yuǎn)程視頻地址進(jìn)行播放,也可以加載RN本地存放的視頻。
二、使用
使用步驟如下:
- 導(dǎo)入依賴庫
在項(xiàng)目Terminal中輸入命令行:
npm install react-native-video --save
或者用yarn:
yarn add react-native-video
IOS環(huán)境下:
直接運(yùn)行下面命令來鏈接react-native-video庫。
react-native link
如果你想允許其他應(yīng)用在你的視頻組件上播放音樂,請(qǐng)打開AppDelegate.m文件并且添加:
AppDelegate.m
#import <AVFoundation/AVFoundation.h> // import
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; // allow
...
}
Android環(huán)境下:
直接運(yùn)行下面命令來鏈接react-native-video庫。
react-native link
如果上面的方式失敗了,那么你可以嘗試手動(dòng)添加配置,這步比較麻煩。
android/settings.gradle
較新的ExoPlayer庫適用于大多數(shù)人。(推薦使用這個(gè)庫)
include ':react-native-video'
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer')
如果您需要使用舊的基于Android MediaPlayer的播放器,請(qǐng)改用以下內(nèi)容:
include ':react-native-video'
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
android/app/build.gradle
dependencies {
...
compile project(':react-native-video')
}
MainApplication.java
在java文件頂部位置記得添加import:
import com.brentvatne.react.ReactVideoPackage;
將ReactVideoPackage類添加到導(dǎo)出的包列表中:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.asList(
new MainReactPackage(),
new ReactVideoPackage()
);
}
三、案例分析

上面這張動(dòng)圖可以看出,我們可以通過自定義的樣式和功能來使用react-native-video。
其中實(shí)現(xiàn)的功能有:調(diào)節(jié)播放速率,調(diào)節(jié)視頻音量,以及調(diào)節(jié)視頻播放器的樣式,這三個(gè)功能分別對(duì)應(yīng)下圖中的三個(gè)紅色邊框離的選項(xiàng)。

此外,我們還給這個(gè)視頻播放器組件添加了很多事件屬性,例如播放進(jìn)度條的監(jiān)聽(onProgress),播放完回到視頻起點(diǎn)的設(shè)置(onEnd)等,下面我們跟隨代碼一步步分析這個(gè)組件的一些屬性和用法。
- 首先我們聲明一個(gè)組件來使用這個(gè)Video,在此之前先將它導(dǎo)進(jìn)來
import React, {
Component
} from 'react';
import {
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import Video from 'react-native-video';
export default class VideoPlayer extends Component {
··· ···
}
- 接著初始化一些state,之后會(huì)用到。比如rate表示視頻播放速率初始化為1,即正常速率。
state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'contain',
duration: 0.0,
currentTime: 0.0,
paused: true,
};
- 渲染Video組件
<Video
ref={(ref: Video) => { //方法對(duì)引用Video元素的ref引用進(jìn)行操作
this.video = ref
}}
/* source={{ uri: 'https://gslb.miaopai.com/stream/HNkFfNMuhjRzDd-q6j9qycf54OaKqInVMu0YhQ__.mp4?ssig=bbabfd7684cae53660dc2d4c2103984e&time_stamp=1533631567740&cookie_id=&vend=1&os=3&partner=1&platform=2&cookie_id=&refer=miaopai&scid=HNkFfNMuhjRzDd-q6j9qycf54OaKqInVMu0YhQ__', type: 'mpd' }} */
source={require('../../background.mp4')}//設(shè)置視頻源
style={styles.fullScreen}//組件樣式
rate={this.state.rate}//播放速率
paused={this.state.paused}//暫停
volume={this.state.volume}//調(diào)節(jié)音量
muted={this.state.muted}//控制音頻是否靜音
resizeMode={this.state.resizeMode}//縮放模式
onLoad={this.onLoad}//加載媒體并準(zhǔn)備播放時(shí)調(diào)用的回調(diào)函數(shù)。
onProgress={this.onProgress}//視頻播放過程中每個(gè)間隔進(jìn)度單位調(diào)用的回調(diào)函數(shù)
onEnd={this.onEnd}//視頻播放結(jié)束時(shí)的回調(diào)函數(shù)
onAudioBecomingNoisy={this.onAudioBecomingNoisy}//音頻變得嘈雜時(shí)的回調(diào) - 應(yīng)暫停視頻
onAudioFocusChanged={this.onAudioFocusChanged}//音頻焦點(diǎn)丟失時(shí)的回調(diào) - 如果焦點(diǎn)丟失則暫停
repeat={false}//確定在到達(dá)結(jié)尾時(shí)是否重復(fù)播放視頻。
/>
ps:這里要注意!
react-native-video通過source屬性設(shè)置視頻,播放遠(yuǎn)程視頻時(shí)使用uri來設(shè)置視頻地址,如下:
source={{uri: "http://www.xxx.com/xxx/xxx/xxx.mp4"}}
復(fù)制代碼播放本地視頻時(shí),使用方式如下:
source={require('../assets/video/turntable.mp4')}
復(fù)制代碼需要注意的是,source屬性不能為空,uri或本地資源是必須要設(shè)置的,否則會(huì)導(dǎo)致app閃退。uri不能設(shè)置為空字符串,必須是一個(gè)具體的地址。
- 關(guān)于進(jìn)度條
在render中聲明兩個(gè)量:flexCompleted和flexRemaining,前者表示已經(jīng)播放完成的進(jìn)度,后者表示剩余的進(jìn)度,,這兩個(gè)量分別作為包裹在View中組件的flex屬性的值:
render() {
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
... ...
<View style={styles.progress}>
<View style={[styles.innerProgressCompleted, {flex: flexCompleted}]}/>
<View style={[styles.innerProgressRemaining, {flex: flexRemaining}]}/>
</View>
}
getCurrentTimePercentage() {
if (this.state.currentTime > 0) {
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
}
return 0;
};
- 點(diǎn)擊事件的處理
通過點(diǎn)擊組件中的選項(xiàng),我們可以調(diào)節(jié)視頻的音量、播放速率、縮放的樣式等。
先把布局定義下:
<View style={styles.controls}>
<View style={styles.generalControls}>
<View style={styles.rateControl}>
{this.renderRateControl(0.25)}
{this.renderRateControl(0.5)}
{this.renderRateControl(1.0)}
{this.renderRateControl(1.5)}
{this.renderRateControl(2.0)}
</View>
<View style={styles.volumeControl}>
{this.renderVolumeControl(0.5)}
{this.renderVolumeControl(1)}
{this.renderVolumeControl(1.5)}
</View>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
</View>
</View>
需要重寫三個(gè)方法來填充事件的內(nèi)容:
renderRateControl(rate):設(shè)置播放速率,改方法接收一個(gè)值表示速率的大小,1.0表示正常速率播放。注意:對(duì)于Android的播放器,rate屬性僅在Android6.0或者更高版本中生效。
renderRateControl(rate) {
const isSelected = (this.state.rate === rate);
return (
<TouchableOpacity onPress={() => {
this.setState({rate})
}}>
<Text style={[styles.controlOption, {fontWeight: isSelected ? 'bold' : 'normal'}]}>
{rate}x
</Text>
</TouchableOpacity>
);
}
renderVolumeControl(volume):該方法接收一個(gè)值作為音量大小,1.0為正常音量
renderVolumeControl(volume) {
const isSelected = (this.state.volume === volume);
return (
<TouchableOpacity onPress={() => {
this.setState({volume})
}}>
<Text style={[styles.controlOption, {fontWeight: isSelected ? 'bold' : 'normal'}]}>
{volume * 100}%
</Text>
</TouchableOpacity>
)
}
renderResizeModeControl(resizeMode):該方法接收的參數(shù)resizeMode有四種模式:
"none"(默認(rèn)) - 不匹配大小
"contain" - 均勻縮放視頻(保持視頻的寬高比),使視頻的尺寸(寬度和高度)等于或小于視圖的相應(yīng)尺寸(減去填充)
"cover" - 均勻縮放視頻(保持視頻的寬高比),使圖像的尺寸(寬度和高度)等于或大于視圖的相應(yīng)尺寸(減去填充)
"stretch" - 獨(dú)立縮放寬度和高度,這可能會(huì)改變src的寬高比
renderResizeModeControl(resizeMode) {
const isSelected = (this.state.resizeMode === resizeMode);
return (
<TouchableOpacity onPress={() => {
this.setState({resizeMode})
}}>
<Text style={[styles.controlOption, {fontWeight: isSelected ? 'bold' : 'normal'}]}>
{resizeMode}
</Text>
</TouchableOpacity>
)
}
- 最后,重新定義事件屬性onLoad、onEnd、onProgress等
video: Video;
onLoad = (data) => {
this.setState({duration: data.duration});
};
onProgress = (data) => {
this.setState({currentTime: data.currentTime});
};
onEnd = () => {
this.setState({paused: true});
this.video.seek(0)
};
onAudioBecomingNoisy = () => {
this.setState({paused: true})
};
onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
this.setState({paused: !event.hasAudioFocus})
};
注意一下onEnd的回調(diào)中用到了seek()方法,這個(gè)方法接收的參數(shù)的單位是秒,意味著seek(定位)到由秒表示的指定位置,seek(0)也就是定位到視頻開始播放的位置。
附錄
這樣,一個(gè)可靈活使用的視頻播放器Demo就已經(jīng)做好了,當(dāng)然我們也可以自己定制喜歡的樣式和布局,例如下面這種:

實(shí)現(xiàn)上圖的源碼在這