├── images
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
└── dsym.png
├── android
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── yuanzhou
│ │ │ └── vlc
│ │ │ ├── ReactVlcPlayerPackage.java
│ │ │ └── vlcplayer
│ │ │ ├── VideoEventEmitter.java
│ │ │ ├── ReactVlcPlayerViewManager.java
│ │ │ └── ReactVlcPlayerView.java
│ ├── test
│ │ └── java
│ │ │ └── react
│ │ │ └── yuanzhou
│ │ │ └── com
│ │ │ └── vlcplayer
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── react
│ │ └── yuanzhou
│ │ └── com
│ │ └── vlcplayer
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
├── build.gradle
└── react-native-yz-vlcplayer.iml
├── ios
├── RCTVLCPlayer
│ ├── RCTVLCPlayerManager.h
│ ├── RCTVLCPlayer.h
│ ├── RCTVLCPlayerManager.m
│ └── RCTVLCPlayer.m
└── RCTVLCPlayer.xcodeproj
│ ├── xcuserdata
│ └── aolc.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── project.pbxproj
├── index.js
├── package.json
├── LICENSE
├── playerView
├── BackHandle.js
├── TimeLimit.js
├── SizeController.js
├── ControlBtn.js
├── index.js
└── VLCPlayerView.js
├── playerViewByMethod
├── BackHandle.js
├── TimeLimit.js
├── SizeController.js
├── ControlBtn.js
└── VLCPlayerView.js
├── VLCPlayer.js
└── README.md
/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/1.png
--------------------------------------------------------------------------------
/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/2.png
--------------------------------------------------------------------------------
/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/3.png
--------------------------------------------------------------------------------
/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/4.png
--------------------------------------------------------------------------------
/images/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/5.png
--------------------------------------------------------------------------------
/images/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/6.png
--------------------------------------------------------------------------------
/images/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/7.png
--------------------------------------------------------------------------------
/images/dsym.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xuyuanzhou/react-native-yz-vlcplayer/HEAD/images/dsym.png
--------------------------------------------------------------------------------
/android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | vlcplayer
3 |
4 |
--------------------------------------------------------------------------------
/ios/RCTVLCPlayer/RCTVLCPlayerManager.h:
--------------------------------------------------------------------------------
1 | #import "React/RCTViewManager.h"
2 |
3 | @interface RCTVLCPlayerManager : RCTViewManager
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | const VLCPlayerControl = {
3 | VLCPlayer: require('./VLCPlayer').default,
4 | VlCPlayerView: require('./playerView/index').default,
5 | VlCPlayerViewByMethod: require('./playerViewByMethod/index').default,
6 | VlcSimplePlayer: require('./playerViewByMethod/index').default,
7 | };
8 |
9 | module.exports = VLCPlayerControl;
--------------------------------------------------------------------------------
/ios/RCTVLCPlayer.xcodeproj/xcuserdata/aolc.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RCTVLCPlayer.xcscheme
8 |
9 | orderHint
10 | 48
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/android/src/test/java/react/yuanzhou/com/vlcplayer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package react.yuanzhou.com.vlcplayer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-yz-vlcplayer",
3 | "version": "1.1.1-beta7",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "vlc",
11 | "player",
12 | "android",
13 | "ios",
14 | "react-native",
15 | "mp4"
16 | ],
17 | "author": "yuanzhou.xu",
18 | "email": "635043894@qq.com",
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/xuyuanzhou/react-native-yz-vlcplayer.git"
22 | },
23 | "license": "MIT"
24 | }
25 |
--------------------------------------------------------------------------------
/android/src/androidTest/java/react/yuanzhou/com/vlcplayer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package react.yuanzhou.com.vlcplayer;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("react.yuanzhou.com.vlcplayer.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/aolc/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/ios/RCTVLCPlayer/RCTVLCPlayer.h:
--------------------------------------------------------------------------------
1 | #import "React/RCTView.h"
2 |
3 | @class RCTEventDispatcher;
4 |
5 | @interface RCTVLCPlayer : UIView
6 |
7 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress;
8 | /*@property (nonatomic, copy) RCTBubblingEventBlock onVideoPaused;
9 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoStopped;
10 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoBuffering;
11 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoPlaying;
12 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoEnded;
13 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoError;
14 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoOpen;*/
15 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart;
16 | @property (nonatomic, copy) RCTBubblingEventBlock onSnapshot;
17 | @property (nonatomic, copy) RCTBubblingEventBlock onIsPlaying;
18 | @property (nonatomic, copy) RCTBubblingEventBlock onVideoStateChange;
19 |
20 |
21 |
22 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/android/src/main/java/com/yuanzhou/vlc/ReactVlcPlayerPackage.java:
--------------------------------------------------------------------------------
1 | package com.yuanzhou.vlc;
2 |
3 |
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.bridge.JavaScriptModule;
6 | import com.facebook.react.bridge.NativeModule;
7 | import com.facebook.react.bridge.ReactApplicationContext;
8 | import com.facebook.react.uimanager.ViewManager;
9 |
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | import com.yuanzhou.vlc.vlcplayer.ReactVlcPlayerViewManager;
14 |
15 | public class ReactVlcPlayerPackage implements ReactPackage {
16 |
17 | @Override
18 | public List createNativeModules(ReactApplicationContext reactContext) {
19 | return Collections.emptyList();
20 | }
21 |
22 | // Deprecated RN 0.47
23 | public List> createJSModules() {
24 | return Collections.emptyList();
25 | }
26 |
27 | @Override
28 | public List createViewManagers(ReactApplicationContext reactContext) {
29 | return Collections.singletonList(new ReactVlcPlayerViewManager());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Brent Vatne, Baris Sencan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/playerView/BackHandle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by aolc on 2018/5/22.
3 | */
4 |
5 | let backFunctionKeys = [];
6 | let backFunctionsMap = new Map();
7 |
8 | function removeIndex(array, index) {
9 | let newArray = [];
10 | for (let i = 0; i < array.length; i++) {
11 | if (i !== index) {
12 | newArray.push(array[i]);
13 | }
14 | }
15 | return newArray;
16 | }
17 |
18 | function removeKey(array, key) {
19 | let newArray = [];
20 | for (let i = 0; i < array.length; i++) {
21 | if (array[i] !== key) {
22 | newArray.push(array[i]);
23 | }
24 | }
25 | return newArray;
26 | }
27 |
28 | const handleBack = () => {
29 | if (backFunctionKeys.length > 0) {
30 | let functionKey = backFunctionKeys[backFunctionKeys.length - 1];
31 | backFunctionKeys = removeIndex(backFunctionKeys, backFunctionKeys.length - 1);
32 | let functionA = backFunctionsMap.get(functionKey);
33 | backFunctionsMap.delete(functionKey);
34 | functionA && functionA();
35 | return false;
36 | }
37 | return true;
38 | };
39 |
40 | const addBackFunction = (key, functionA) => {
41 | backFunctionsMap.set(key, functionA);
42 | backFunctionKeys.push(key);
43 | };
44 |
45 | const removeBackFunction = key => {
46 | backFunctionKeys = removeKey(backFunctionKeys, key);
47 | backFunctionsMap.delete(key);
48 | };
49 |
50 | export default {
51 | handleBack,
52 | addBackFunction,
53 | removeBackFunction,
54 | };
55 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 |
4 | def _ext = rootProject.ext;
5 |
6 | def _reactNativeVersion = _ext.has('reactNative') ? _ext.reactNative : '+';
7 | def _compileSdkVersion = _ext.has('compileSdkVersion') ? _ext.compileSdkVersion : 26;
8 | def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : '26.0.3';
9 | def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 16;
10 | def _targetSdkVersion = _ext.has('targetSdkVersion') ? _ext.targetSdkVersion : 26;
11 |
12 | android {
13 | compileSdkVersion _compileSdkVersion
14 | buildToolsVersion _buildToolsVersion
15 |
16 | defaultConfig {
17 | minSdkVersion _minSdkVersion
18 | targetSdkVersion _targetSdkVersion
19 | versionCode 1
20 | versionName "1.0"
21 | ndk {
22 | //支持的abi 可选 精简的库
23 | abiFilters 'armeabi-v7a'//,'x86_64','arm64-v8a','x86'
24 | }
25 | }
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 | }
33 |
34 | dependencies {
35 | provided fileTree(dir: 'libs', include: ['*.jar'])
36 | provided "com.facebook.react:react-native:${_reactNativeVersion}"
37 | compile 'com.yyl.vlc:vlc-android-sdk:3.0.13'
38 | //compile 'de.mrmaffen:vlc-android-sdk:2.0.6'
39 | }
40 |
--------------------------------------------------------------------------------
/playerViewByMethod/BackHandle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by aolc on 2018/5/22.
3 | */
4 |
5 | let backFunctionKeys = [];
6 | let backFunctionsMap = new Map();
7 |
8 | function removeIndex(array, index) {
9 | let newArray = [];
10 | for (let i = 0; i < array.length; i++) {
11 | if (i !== index) {
12 | newArray.push(array[i]);
13 | }
14 | }
15 | return newArray;
16 | }
17 |
18 | function removeKey(array, key) {
19 | let newArray = [];
20 | for (let i = 0; i < array.length; i++) {
21 | if (array[i] !== key) {
22 | newArray.push(array[i]);
23 | }
24 | }
25 | return newArray;
26 | }
27 |
28 | const handleBack = () => {
29 | if (backFunctionKeys.length > 0) {
30 | let functionKey = backFunctionKeys[backFunctionKeys.length - 1];
31 | backFunctionKeys = removeIndex(backFunctionKeys, backFunctionKeys.length - 1);
32 | let functionA = backFunctionsMap.get(functionKey);
33 | backFunctionsMap.delete(functionKey);
34 | functionA && functionA();
35 | return false;
36 | }
37 | return true;
38 | };
39 |
40 | const addBackFunction = (key, functionA) => {
41 | backFunctionsMap.set(key, functionA);
42 | backFunctionKeys.push(key);
43 | };
44 |
45 | const removeBackFunction = key => {
46 | backFunctionKeys = removeKey(backFunctionKeys, key);
47 | backFunctionsMap.delete(key);
48 | };
49 |
50 | export default {
51 | handleBack,
52 | addBackFunction,
53 | removeBackFunction,
54 | };
55 |
--------------------------------------------------------------------------------
/ios/RCTVLCPlayer/RCTVLCPlayerManager.m:
--------------------------------------------------------------------------------
1 | #import "RCTVLCPlayerManager.h"
2 | #import "RCTVLCPlayer.h"
3 | #import "React/RCTBridge.h"
4 |
5 | @implementation RCTVLCPlayerManager
6 |
7 | RCT_EXPORT_MODULE();
8 |
9 | @synthesize bridge = _bridge;
10 |
11 | - (UIView *)view
12 | {
13 | return [[RCTVLCPlayer alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
14 | }
15 |
16 |
17 | - (dispatch_queue_t)methodQueue
18 | {
19 | return dispatch_get_main_queue();
20 | }
21 |
22 | RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary);
23 | RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
24 | RCT_EXPORT_VIEW_PROPERTY(seek, float);
25 | RCT_EXPORT_VIEW_PROPERTY(rate, float);
26 | RCT_EXPORT_VIEW_PROPERTY(muted, BOOL);
27 | RCT_EXPORT_VIEW_PROPERTY(volume, int);
28 | RCT_EXPORT_VIEW_PROPERTY(volumeUp, int);
29 | RCT_EXPORT_VIEW_PROPERTY(volumeDown, int);
30 | RCT_EXPORT_VIEW_PROPERTY(resume, BOOL);
31 | RCT_EXPORT_VIEW_PROPERTY(clear, BOOL);
32 | RCT_EXPORT_VIEW_PROPERTY(seekTime, int);
33 | RCT_EXPORT_VIEW_PROPERTY(videoAspectRatio, NSString);
34 | RCT_EXPORT_VIEW_PROPERTY(snapshotPath, NSString);
35 | /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
36 | /*RCT_EXPORT_VIEW_PROPERTY(onVideoPaused, RCTBubblingEventBlock);
37 | RCT_EXPORT_VIEW_PROPERTY(onVideoStopped, RCTBubblingEventBlock);
38 | RCT_EXPORT_VIEW_PROPERTY(onVideoBuffering, RCTBubblingEventBlock);
39 | RCT_EXPORT_VIEW_PROPERTY(onVideoPlaying, RCTBubblingEventBlock);
40 | RCT_EXPORT_VIEW_PROPERTY(onVideoEnded, RCTBubblingEventBlock);
41 | RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock);
42 | RCT_EXPORT_VIEW_PROPERTY(onVideoOpen, RCTBubblingEventBlock);
43 | */
44 | RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
45 | RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock);
46 | RCT_EXPORT_VIEW_PROPERTY(onSnapshot, RCTBubblingEventBlock);
47 | RCT_EXPORT_VIEW_PROPERTY(onIsPlaying, RCTBubblingEventBlock);
48 | RCT_EXPORT_VIEW_PROPERTY(onVideoStateChange, RCTBubblingEventBlock);
49 |
50 |
51 | + (BOOL)requiresMainQueueSetup
52 | {
53 | return YES;
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/playerView/TimeLimit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/16.
3 | */
4 | import React, { Component } from 'react';
5 | import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
6 |
7 | export default class TimeLimt extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.timer = null;
11 | }
12 |
13 | static defaultProps = {
14 | maxTime: 0,
15 | };
16 |
17 | state = {
18 | timeNumber: 0,
19 | };
20 |
21 | componentDidMount() {
22 | if(this.props.maxTime > 0){
23 | this.timer = setInterval(this._updateTimer, 1000);
24 | }
25 | }
26 |
27 | _updateTimer = () => {
28 | let { timeNumber } = this.state;
29 | let { maxTime } = this.props;
30 | let newTimeNumber = timeNumber + 1;
31 | this.setState({
32 | timeNumber: newTimeNumber,
33 | });
34 | if (newTimeNumber >= maxTime) {
35 | this._onEnd();
36 | }
37 | };
38 |
39 | componentWillUnmount() {
40 | clearInterval(this.timer);
41 | }
42 |
43 | _onEnd = () => {
44 | let { onEnd } = this.props;
45 | clearInterval(this.timer);
46 | onEnd && onEnd();
47 | };
48 |
49 | render() {
50 | let { timeNumber } = this.state;
51 | let { maxTime } = this.props;
52 | return (
53 |
57 | {maxTime > 0 && (
58 |
59 | {maxTime - timeNumber}
60 |
61 | )}
62 |
63 | 跳过片头
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | const styles = StyleSheet.create({
71 | container: {
72 | flex: 1,
73 | //backgroundColor: '#000',
74 | },
75 | timeView: {
76 | height: '100%',
77 | justifyContent: 'center',
78 | alignItems: 'center',
79 | marginRight: 5,
80 | },
81 | nameView: {
82 | height: '100%',
83 | justifyContent: 'center',
84 | alignItems: 'center',
85 | },
86 | });
87 |
--------------------------------------------------------------------------------
/playerViewByMethod/TimeLimit.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/16.
3 | */
4 | import React, { Component } from 'react';
5 | import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
6 |
7 | export default class TimeLimt extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.timer = null;
11 | }
12 |
13 | static defaultProps = {
14 | maxTime: 0,
15 | };
16 |
17 | state = {
18 | timeNumber: 0,
19 | };
20 |
21 | componentDidMount() {
22 | if (this.props.maxTime > 0) {
23 | this.timer = setInterval(this._updateTimer, 1000);
24 | }
25 | }
26 |
27 | _updateTimer = () => {
28 | let { timeNumber } = this.state;
29 | let { maxTime } = this.props;
30 | let newTimeNumber = timeNumber + 1;
31 | this.setState({
32 | timeNumber: newTimeNumber,
33 | });
34 | if (newTimeNumber >= maxTime) {
35 | this._onEnd();
36 | }
37 | };
38 |
39 | componentWillUnmount() {
40 | clearInterval(this.timer);
41 | }
42 |
43 | _onEnd = () => {
44 | let { onEnd } = this.props;
45 | clearInterval(this.timer);
46 | onEnd && onEnd();
47 | };
48 |
49 | render() {
50 | let { timeNumber } = this.state;
51 | let { maxTime } = this.props;
52 | return (
53 |
57 | {maxTime > 0 && (
58 |
59 | {maxTime - timeNumber}
60 |
61 | )}
62 |
63 | 跳过片头
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | const styles = StyleSheet.create({
71 | container: {
72 | flex: 1,
73 | //backgroundColor: '#000',
74 | },
75 | timeView: {
76 | height: '100%',
77 | justifyContent: 'center',
78 | alignItems: 'center',
79 | marginRight: 5,
80 | },
81 | nameView: {
82 | height: '100%',
83 | justifyContent: 'center',
84 | alignItems: 'center',
85 | },
86 | });
87 |
--------------------------------------------------------------------------------
/playerView/SizeController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 高度定义
3 | * Created by yuanzhou.xu on 17/2/18.
4 | */
5 | import { PixelRatio, Dimensions, Platform, StatusBar } from 'react-native';
6 | let initialDeviceHeight = 667;
7 | let initialDeviceWidth = 375;
8 | let initialPixelRatio = 2;
9 | let deviceHeight = Dimensions.get('window').height;
10 | let deviceWidth = Dimensions.get('window').width;
11 | let pixelRatio = PixelRatio.get();
12 | let statusBarHeight = 20; //初始状态栏高度
13 | let topBarHeight = 44; //初始导航栏高度
14 | let tabBarHeight = 49; //初始标签栏高度
15 | let IS_IPHONEX = false;
16 | let changeRatio = Math.min(
17 | deviceHeight / initialDeviceHeight,
18 | deviceWidth / initialDeviceWidth,
19 | ); //pixelRatio/initialPixelRatio;
20 | changeRatio = changeRatio.toFixed(2);
21 | if (deviceWidth > 375 && deviceWidth <= 1125 / 2) {
22 | statusBarHeight = 27;
23 | topBarHeight = 66;
24 | tabBarHeight = 60;
25 | } else if (deviceWidth > 1125 / 2) {
26 | statusBarHeight = 30;
27 | topBarHeight = 66;
28 | tabBarHeight = 60;
29 | }
30 | if (Platform.OS !== 'ios') {
31 | statusBarHeight = 20;
32 | if (deviceWidth > 375 && deviceWidth <= 1125 / 2) {
33 | statusBarHeight = 25;
34 | } else if (deviceWidth > 1125 / 2 && deviceWidth < 812) {
35 | statusBarHeight = 25;
36 | }
37 | if (StatusBar.currentHeight) {
38 | statusBarHeight = StatusBar.currentHeight;
39 | }
40 | }
41 |
42 | if (deviceWidth >= 375 && deviceWidth < 768) {
43 | topBarHeight = 44; //初始导航栏高度
44 | tabBarHeight = 49;
45 | changeRatio = 1;
46 | }
47 | if (deviceHeight >= 812) {
48 | statusBarHeight = 44;
49 | //topBarHeight = 60;
50 | IS_IPHONEX = true;
51 | }
52 | /**
53 | * 返回状态栏高度
54 | */
55 | export function getStatusBarHeight() {
56 | return statusBarHeight;
57 | }
58 | /**
59 | * 返回导航栏高度
60 | */
61 | export function getTopBarHeight() {
62 | return topBarHeight;
63 | }
64 | /**
65 | * 返回标签栏高度
66 | */
67 | export function getTabBarHeight() {
68 | return tabBarHeight;
69 | }
70 | /**
71 | *
72 | */
73 | export function getTopHeight() {
74 | if (Platform.OS === 'ios') {
75 | return topBarHeight + statusBarHeight;
76 | } else {
77 | return topBarHeight + statusBarHeight;
78 | }
79 | }
80 | /**
81 | * 返回变更比例
82 | */
83 | export function getChangeRatio() {
84 | return changeRatio;
85 | }
86 | /** 获取tabBar比例**/
87 | export function getTabBarRatio() {
88 | return tabBarHeight / 49;
89 | }
90 |
91 | /**
92 | * 获取TopBar比例
93 | */
94 | export function getTopBarRatio() {
95 | return changeRatio;
96 | }
97 |
98 | export function isIphoneX() {
99 | return IS_IPHONEX;
100 | }
101 |
--------------------------------------------------------------------------------
/playerViewByMethod/SizeController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 高度定义
3 | * Created by yuanzhou.xu on 17/2/18.
4 | */
5 | import { PixelRatio, Dimensions, Platform, StatusBar } from 'react-native';
6 | let initialDeviceHeight = 667;
7 | let initialDeviceWidth = 375;
8 | let initialPixelRatio = 2;
9 | let deviceHeight = Dimensions.get('window').height;
10 | let deviceWidth = Dimensions.get('window').width;
11 | let pixelRatio = PixelRatio.get();
12 | let statusBarHeight = 20; //初始状态栏高度
13 | let topBarHeight = 44; //初始导航栏高度
14 | let tabBarHeight = 49; //初始标签栏高度
15 | let IS_IPHONEX = false;
16 | let changeRatio = Math.min(
17 | deviceHeight / initialDeviceHeight,
18 | deviceWidth / initialDeviceWidth,
19 | ); //pixelRatio/initialPixelRatio;
20 |
21 | let pxWidth = deviceWidth * pixelRatio;
22 |
23 | changeRatio = changeRatio.toFixed(2);
24 | if (deviceWidth > 375 && deviceWidth <= 1125 / 2) {
25 | statusBarHeight = 27;
26 | topBarHeight = 66;
27 | tabBarHeight = 60;
28 | } else if (deviceWidth > 1125 / 2) {
29 | statusBarHeight = 30;
30 | topBarHeight = 66;
31 | tabBarHeight = 60;
32 | }
33 |
34 | /**
35 | * iphone4 ---- iphone6
36 | */
37 | if (pxWidth <= 750) {
38 | statusBarHeight = 40 / pixelRatio;
39 | topBarHeight = 44 / pixelRatio;
40 | tabBarHeight = 49 / pixelRatio;
41 | } else if (750 < pxWidth && pxWidth <= 1125) {
42 | statusBarHeight = 54 / pixelRatio;
43 | topBarHeight = 132 / pixelRatio;
44 | tabBarHeight = 147 / pixelRatio;
45 | } else {
46 | statusBarHeight = 60 / pixelRatio;
47 | topBarHeight = 132 / pixelRatio;
48 | tabBarHeight = 147 / pixelRatio;
49 | }
50 |
51 | if (Platform.OS !== 'ios') {
52 | statusBarHeight = 20;
53 | if (deviceWidth > 375 && deviceWidth <= 1125 / 2) {
54 | statusBarHeight = 25;
55 | } else if (deviceWidth > 1125 / 2 && deviceWidth < 812) {
56 | statusBarHeight = 25;
57 | }
58 | if (StatusBar.currentHeight) {
59 | statusBarHeight = StatusBar.currentHeight;
60 | }
61 | }
62 |
63 | if (deviceWidth >= 375 && deviceWidth < 768) {
64 | changeRatio = 1;
65 | }
66 | if (deviceHeight >= 812) {
67 | statusBarHeight = 44;
68 | IS_IPHONEX = true;
69 | }
70 | /**
71 | * 返回状态栏高度
72 | */
73 | export function getStatusBarHeight() {
74 | return statusBarHeight;
75 | }
76 | /**
77 | * 返回导航栏高度
78 | */
79 | export function getTopBarHeight() {
80 | return topBarHeight;
81 | }
82 | /**
83 | * 返回标签栏高度
84 | */
85 | export function getTabBarHeight() {
86 | return tabBarHeight;
87 | }
88 | /**
89 | *
90 | */
91 | export function getTopHeight() {
92 | if (Platform.OS === 'ios') {
93 | return topBarHeight + statusBarHeight;
94 | } else {
95 | return topBarHeight + statusBarHeight;
96 | }
97 | }
98 | /**
99 | * 返回变更比例
100 | */
101 | export function getChangeRatio() {
102 | return changeRatio;
103 | }
104 | /** 获取tabBar比例**/
105 | export function getTabBarRatio() {
106 | return tabBarHeight / 49;
107 | }
108 |
109 | /**
110 | * 获取TopBar比例
111 | */
112 | export function getTopBarRatio() {
113 | return changeRatio;
114 | }
115 |
116 | export function isIphoneX() {
117 | return IS_IPHONEX;
118 | }
119 |
--------------------------------------------------------------------------------
/android/src/main/java/com/yuanzhou/vlc/vlcplayer/VideoEventEmitter.java:
--------------------------------------------------------------------------------
1 | package com.yuanzhou.vlc.vlcplayer;
2 |
3 | import android.support.annotation.StringDef;
4 | import android.util.Log;
5 | import android.view.View;
6 |
7 | import com.facebook.react.bridge.Arguments;
8 | import com.facebook.react.bridge.ReactContext;
9 | import com.facebook.react.bridge.WritableArray;
10 | import com.facebook.react.bridge.WritableMap;
11 | import com.facebook.react.uimanager.events.RCTEventEmitter;
12 |
13 |
14 | import java.lang.annotation.Retention;
15 | import java.lang.annotation.RetentionPolicy;
16 |
17 | class VideoEventEmitter {
18 |
19 | private final RCTEventEmitter eventEmitter;
20 |
21 | private int viewId = View.NO_ID;
22 |
23 | VideoEventEmitter(ReactContext reactContext) {
24 | this.eventEmitter = reactContext.getJSModule(RCTEventEmitter.class);
25 | }
26 |
27 | private static final String EVENT_LOAD_START = "onVideoLoadStart";
28 | private static final String EVENT_PROGRESS = "onVideoProgress";
29 | private static final String EVENT_SEEK = "onVideoSeek";
30 | private static final String EVENT_END = "onVideoEnd";
31 | private static final String EVENT_SNAPSHOT = "onSnapshot";
32 | private static final String EVENT_ON_IS_PLAYING= "onIsPlaying";
33 | private static final String EVENT_ON_VIDEO_STATE_CHANGE = "onVideoStateChange";
34 |
35 | static final String[] Events = {
36 | EVENT_LOAD_START,
37 | EVENT_PROGRESS,
38 | EVENT_SEEK,
39 | EVENT_END,
40 | EVENT_SNAPSHOT,
41 | EVENT_ON_IS_PLAYING,
42 | EVENT_ON_VIDEO_STATE_CHANGE
43 | };
44 |
45 | @Retention(RetentionPolicy.SOURCE)
46 | @StringDef({
47 | EVENT_LOAD_START,
48 | EVENT_PROGRESS,
49 | EVENT_SEEK,
50 | EVENT_END,
51 | EVENT_SNAPSHOT,
52 | EVENT_ON_IS_PLAYING,
53 | EVENT_ON_VIDEO_STATE_CHANGE
54 | })
55 |
56 | @interface VideoEvents {
57 | }
58 |
59 | private static final String EVENT_PROP_ERROR = "error";
60 | private static final String EVENT_PROP_ERROR_STRING = "errorString";
61 | private static final String EVENT_PROP_ERROR_EXCEPTION = "";
62 |
63 |
64 | void setViewId(int viewId) {
65 | this.viewId = viewId;
66 | }
67 |
68 | /**
69 | * MideaPlayer初始化完毕回调
70 | */
71 | void loadStart() {
72 | WritableMap event = Arguments.createMap();
73 | receiveEvent(EVENT_LOAD_START, event);
74 | }
75 |
76 |
77 | /**
78 | * 视频进度改变回调
79 | * @param currentPosition
80 | * @param bufferedDuration
81 | */
82 | void progressChanged(double currentPosition, double bufferedDuration) {
83 | WritableMap event = Arguments.createMap();
84 | event.putDouble("currentTime", currentPosition);
85 | event.putDouble("duration", bufferedDuration);
86 | receiveEvent(EVENT_PROGRESS, event);
87 | }
88 |
89 |
90 | void error(String errorString, Exception exception) {
91 | WritableMap error = Arguments.createMap();
92 | error.putString(EVENT_PROP_ERROR_STRING, errorString);
93 | error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.getMessage());
94 | WritableMap event = Arguments.createMap();
95 | event.putMap(EVENT_PROP_ERROR, error);
96 | //receiveEvent(EVENT_ERROR, event);
97 | }
98 |
99 | /**
100 | * 截图回调
101 | * @param result
102 | */
103 | void onSnapshot(int result){
104 | WritableMap map = Arguments.createMap();
105 | map.putInt("isSuccess",result);
106 | receiveEvent(EVENT_SNAPSHOT, map);
107 | }
108 |
109 | /**
110 | * 是否播放回调
111 | * @param isPlaying
112 | */
113 | void isPlaying(boolean isPlaying){
114 | WritableMap map = Arguments.createMap();
115 | map.putBoolean("isPlaying",isPlaying);
116 | receiveEvent(EVENT_ON_IS_PLAYING, map);
117 | }
118 |
119 | /**
120 | * 视频状态改变回调
121 | * @param map
122 | */
123 | void onVideoStateChange(WritableMap map){
124 | receiveEvent(EVENT_ON_VIDEO_STATE_CHANGE, map);
125 | }
126 |
127 | private void receiveEvent(@VideoEvents String type, WritableMap event) {
128 | eventEmitter.receiveEvent(viewId, type, event);
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/android/src/main/java/com/yuanzhou/vlc/vlcplayer/ReactVlcPlayerViewManager.java:
--------------------------------------------------------------------------------
1 | package com.yuanzhou.vlc.vlcplayer;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.text.TextUtils;
6 |
7 | import com.facebook.react.bridge.ReadableMap;
8 | import com.facebook.react.common.MapBuilder;
9 | import com.facebook.react.uimanager.SimpleViewManager;
10 | import com.facebook.react.uimanager.ThemedReactContext;
11 | import com.facebook.react.uimanager.annotations.ReactProp;
12 |
13 | import java.util.Map;
14 |
15 | import javax.annotation.Nullable;
16 |
17 | public class ReactVlcPlayerViewManager extends SimpleViewManager {
18 |
19 | private static final String REACT_CLASS = "RCTVLCPlayer";
20 |
21 | private static final String PROP_SRC = "source";
22 | private static final String PROP_SRC_URI = "uri";
23 | private static final String PROP_SRC_TYPE = "type";
24 | private static final String PROP_REPEAT = "repeat";
25 | private static final String PROP_PAUSED = "paused";
26 | private static final String PROP_MUTED = "muted";
27 | private static final String PROP_VOLUME = "volume";
28 | private static final String PROP_SEEK = "seek";
29 | private static final String PROP_RESUME = "resume";
30 | private static final String PROP_RATE = "rate";
31 | private static final String PROP_POTISION = "position";
32 | private static final String PROP_VIDEO_ASPECT_RATIO = "videoAspectRatio";
33 | private static final String PROP_SRC_IS_NETWORK = "isNetwork";
34 | private static final String PROP_SNAPSHOT_PATH = "snapshotPath";
35 | private static final String PROP_AUTO_ASPECT_RATIO = "autoAspectRatio";
36 | private static final String PROP_CLEAR = "clear";
37 |
38 |
39 |
40 | @Override
41 | public String getName() {
42 | return REACT_CLASS;
43 | }
44 |
45 | @Override
46 | protected ReactVlcPlayerView createViewInstance(ThemedReactContext themedReactContext) {
47 | return new ReactVlcPlayerView(themedReactContext);
48 | }
49 |
50 | @Override
51 | public void onDropViewInstance(ReactVlcPlayerView view) {
52 | view.cleanUpResources();
53 | }
54 |
55 | @Override
56 | public @Nullable Map getExportedCustomDirectEventTypeConstants() {
57 | MapBuilder.Builder builder = MapBuilder.builder();
58 | for (String event : VideoEventEmitter.Events) {
59 | builder.put(event, MapBuilder.of("registrationName", event));
60 | }
61 | return builder.build();
62 | }
63 |
64 | @ReactProp(name = PROP_CLEAR)
65 | public void setClear(final ReactVlcPlayerView videoView, final boolean clear) {
66 | videoView.cleanUpResources();
67 | }
68 |
69 |
70 | @ReactProp(name = PROP_SRC)
71 | public void setSrc(final ReactVlcPlayerView videoView, @Nullable ReadableMap src) {
72 | Context context = videoView.getContext().getApplicationContext();
73 | String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
74 | String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
75 | boolean isNetStr = src.getBoolean(PROP_SRC_IS_NETWORK) ? src.getBoolean(PROP_SRC_IS_NETWORK) : false;
76 | boolean autoplay = src.getBoolean("autoplay") ? src.getBoolean("autoplay") : true;
77 | if (TextUtils.isEmpty(uriString)) {
78 | return;
79 | }
80 | videoView.setSrc(src);
81 |
82 | }
83 |
84 | @ReactProp(name = PROP_REPEAT, defaultBoolean = false)
85 | public void setRepeat(final ReactVlcPlayerView videoView, final boolean repeat) {
86 | videoView.setRepeatModifier(repeat);
87 | }
88 |
89 | @ReactProp(name = PROP_PAUSED, defaultBoolean = false)
90 | public void setPaused(final ReactVlcPlayerView videoView, final boolean paused) {
91 | videoView.setPausedModifier(paused);
92 | }
93 |
94 | @ReactProp(name = PROP_MUTED, defaultBoolean = false)
95 | public void setMuted(final ReactVlcPlayerView videoView, final boolean muted) {
96 | videoView.setMutedModifier(muted);
97 | }
98 |
99 | @ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
100 | public void setVolume(final ReactVlcPlayerView videoView, final float volume) {
101 | videoView.setVolumeModifier((int)volume);
102 | }
103 |
104 |
105 | @ReactProp(name = PROP_SEEK)
106 | public void setSeek(final ReactVlcPlayerView videoView, final float seek) {
107 | videoView.seekTo(Math.round(seek * 1000f));
108 | //videoView.seekTo(seek);
109 | }
110 |
111 | @ReactProp(name = PROP_AUTO_ASPECT_RATIO, defaultBoolean = false)
112 | public void setAutoAspectRatio(final ReactVlcPlayerView videoView, final boolean autoPlay) {
113 | videoView.setAutoAspectRatio(autoPlay);
114 | }
115 |
116 | @ReactProp(name = PROP_RESUME, defaultBoolean = true)
117 | public void setResume(final ReactVlcPlayerView videoView, final boolean autoPlay) {
118 | videoView.doResume(autoPlay);
119 | }
120 |
121 |
122 | @ReactProp(name = PROP_RATE)
123 | public void setRate(final ReactVlcPlayerView videoView, final float rate) {
124 | videoView.setRateModifier(rate);
125 | }
126 |
127 | @ReactProp(name = PROP_POTISION)
128 | public void setPosition(final ReactVlcPlayerView videoView, final float potision) {
129 | videoView.setPosition(potision);
130 | }
131 |
132 |
133 |
134 | @ReactProp(name = PROP_VIDEO_ASPECT_RATIO)
135 | public void setVideoAspectRatio(final ReactVlcPlayerView videoView, final String aspectRatio) {
136 | videoView.setAspectRatio(aspectRatio);
137 | }
138 |
139 | @ReactProp(name = PROP_SNAPSHOT_PATH)
140 | public void setSnapshotPath(final ReactVlcPlayerView videoView, final String snapshotPath) {
141 | videoView.doSnapshot(snapshotPath);
142 | }
143 |
144 |
145 |
146 | private boolean startsWithValidScheme(String uriString) {
147 | return uriString.startsWith("http://")
148 | || uriString.startsWith("https://")
149 | || uriString.startsWith("content://")
150 | || uriString.startsWith("file://")
151 | || uriString.startsWith("asset://");
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/playerView/ControlBtn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/16.
3 | */
4 | import React, { Component } from 'react';
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | Dimensions,
10 | TouchableOpacity,
11 | ActivityIndicator,
12 | StatusBar,
13 | } from 'react-native';
14 | import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
15 | import Slider from 'react-native-slider';
16 | import PropTypes from 'prop-types';
17 | import TimeLimt from './TimeLimit';
18 |
19 | export default class ControlBtn extends Component {
20 | _getTime = (data = 0) => {
21 | let hourCourse = Math.floor(data / 3600);
22 | let diffCourse = data % 3600;
23 | let minCourse = Math.floor(diffCourse / 60);
24 | let secondCourse = Math.floor(diffCourse % 60);
25 | let courseReal = '';
26 | if (hourCourse) {
27 | if (hourCourse < 10) {
28 | courseReal += '0' + hourCourse + ':';
29 | } else {
30 | courseReal += hourCourse + ':';
31 | }
32 | }
33 | if (minCourse < 10) {
34 | courseReal += '0' + minCourse + ':';
35 | } else {
36 | courseReal += minCourse + ':';
37 | }
38 | if (secondCourse < 10) {
39 | courseReal += '0' + secondCourse;
40 | } else {
41 | courseReal += secondCourse;
42 | }
43 | return courseReal;
44 | };
45 |
46 | render() {
47 | let {
48 | paused,
49 | isFull,
50 | showSlider,
51 | onPausedPress,
52 | onFullPress,
53 | onValueChange,
54 | onSlidingComplete,
55 | currentTime,
56 | totalTime,
57 | style
58 | } = this.props;
59 | return (
60 |
61 |
62 |
63 |
64 | {
67 | onPausedPress && onPausedPress(!paused);
68 | }}
69 | style={{ width: 50, alignItems: 'center', justifyContent: 'center' }}>
70 |
71 |
72 | {showSlider && totalTime > 0 &&(
73 |
80 |
81 |
82 | {this._getTime(currentTime) || 0}
83 |
84 |
85 |
86 | {
94 | onValueChange && onValueChange(value);
95 | }}
96 | onSlidingComplete={value => {
97 | onSlidingComplete && onSlidingComplete(value);
98 | }}
99 | />
100 |
101 |
102 |
104 | {this._getTime(totalTime) || 0}
105 |
106 |
107 |
108 | )}
109 | {
112 | onFullPress && onFullPress(!isFull);
113 | }}
114 | style={{ width: 50, alignItems: 'center', justifyContent: 'center' }}>
115 |
116 |
117 |
118 |
119 |
120 |
121 | );
122 | }
123 | }
124 |
125 | const styles = StyleSheet.create({
126 | container: {
127 | flex: 1,
128 | //backgroundColor: '#000',
129 | },
130 | controls: {
131 | width:'100%',
132 | height:50,
133 | },
134 | rateControl: {
135 | flex: 0,
136 | flexDirection: 'row',
137 | marginTop: 10,
138 | marginLeft: 10,
139 | //backgroundColor: 'rgba(0,0,0,0.5)',
140 | width: 120,
141 | height: 30,
142 | justifyContent: 'space-around',
143 | alignItems: 'center',
144 | borderRadius: 10,
145 | },
146 | controlOption: {
147 | textAlign: 'center',
148 | fontSize: 13,
149 | color: '#fff',
150 | width: 30,
151 | //lineHeight: 12,
152 | },
153 | controlContainer: {
154 | flex:1,
155 | //padding: 5,
156 | alignItems: 'center',
157 | justifyContent: 'center',
158 | },
159 |
160 | controlContent: {
161 | width: '100%',
162 | height: 50,
163 | //borderRadius: 10,
164 | backgroundColor: 'rgba(255,255,255,0.6)',
165 | },
166 | controlContent2: {
167 | flex: 1,
168 | flexDirection: 'row',
169 | backgroundColor: 'rgba(0,0,0,0.5)',
170 | alignItems: 'center',
171 | justifyContent: 'space-between',
172 | },
173 |
174 | progress: {
175 | flex: 1,
176 | borderRadius: 3,
177 | alignItems: 'center',
178 | justifyContent: 'center',
179 | },
180 | thumb: {
181 | width: 6,
182 | height: 18,
183 | backgroundColor: '#fff',
184 | borderRadius: 4,
185 | },
186 | loading: {
187 | position: 'absolute',
188 | left: 0,
189 | top: 0,
190 | zIndex: 0,
191 | width: '100%',
192 | height: '100%',
193 | justifyContent: 'center',
194 | alignItems: 'center',
195 | },
196 |
197 | ad: {
198 | backgroundColor: 'rgba(255,255,255,1)',
199 | height: 30,
200 | paddingLeft: 10,
201 | paddingRight: 10,
202 | borderRadius: 20,
203 | justifyContent: 'center',
204 | alignItems: 'center',
205 | },
206 | });
207 |
--------------------------------------------------------------------------------
/playerViewByMethod/ControlBtn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/16.
3 | */
4 | import React, { Component } from 'react';
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | Dimensions,
10 | TouchableOpacity,
11 | ActivityIndicator,
12 | StatusBar,
13 | } from 'react-native';
14 | import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
15 | import Slider from 'react-native-slider';
16 | import PropTypes from 'prop-types';
17 | import TimeLimt from './TimeLimit';
18 |
19 | export default class ControlBtn extends Component {
20 | _getTime = (data = 0) => {
21 | let hourCourse = Math.floor(data / 3600);
22 | let diffCourse = data % 3600;
23 | let minCourse = Math.floor(diffCourse / 60);
24 | let secondCourse = Math.floor(diffCourse % 60);
25 | let courseReal = '';
26 | if (hourCourse) {
27 | if (hourCourse < 10) {
28 | courseReal += '0' + hourCourse + ':';
29 | } else {
30 | courseReal += hourCourse + ':';
31 | }
32 | }
33 | if (minCourse < 10) {
34 | courseReal += '0' + minCourse + ':';
35 | } else {
36 | courseReal += minCourse + ':';
37 | }
38 | if (secondCourse < 10) {
39 | courseReal += '0' + secondCourse;
40 | } else {
41 | courseReal += secondCourse;
42 | }
43 | return courseReal;
44 | };
45 |
46 | render() {
47 | let {
48 | paused,
49 | muted,
50 | isFull,
51 | showSlider,
52 | onPausedPress,
53 | onMutePress,
54 | onFullPress,
55 | onValueChange,
56 | onSlidingComplete,
57 | currentTime,
58 | totalTime,
59 | style,
60 | onReload
61 | } = this.props;
62 | return (
63 |
64 |
65 |
66 |
67 | {
70 | onPausedPress && onPausedPress(!paused);
71 | }}
72 | style={{ width: 35, alignItems: 'center', justifyContent: 'center' }}>
73 |
74 |
75 | {
78 | onMutePress && onMutePress(!paused);
79 | }}
80 | style={{ width: 35, alignItems: 'center', justifyContent: 'center' }}>
81 |
82 |
83 | {
86 | onReload && onReload();
87 | }}
88 | style={{ width: 35, alignItems: 'center', justifyContent: 'center' }}>
89 |
90 |
91 |
92 | {showSlider &&
93 | totalTime > 0 && (
94 |
101 |
108 |
109 | {this._getTime(currentTime) || 0}
110 |
111 |
112 |
113 | {
121 | console.log('onSlidingStart',value)
122 | }}
123 | onValueChange={value => {
124 | onValueChange && onValueChange(value);
125 | }}
126 | onSlidingComplete={value => {
127 | onSlidingComplete && onSlidingComplete(value);
128 | }}
129 | />
130 |
131 |
138 |
139 | {this._getTime(totalTime) || 0}
140 |
141 |
142 |
143 | )}
144 | {
147 | onFullPress && onFullPress(!isFull);
148 | }}
149 | style={{ width: 35, alignItems: 'center', justifyContent: 'center' }}>
150 |
151 |
152 |
153 |
154 |
155 | );
156 | }
157 | }
158 |
159 | const styles = StyleSheet.create({
160 | container: {
161 | flex: 1,
162 | //backgroundColor: '#000',
163 | },
164 | controls: {
165 | width: '100%',
166 | height: 37,
167 | },
168 | rateControl: {
169 | flex: 0,
170 | flexDirection: 'row',
171 | marginTop: 10,
172 | marginLeft: 10,
173 | //backgroundColor: 'rgba(0,0,0,0.5)',
174 | width: 120,
175 | height: 30,
176 | justifyContent: 'space-around',
177 | alignItems: 'center',
178 | borderRadius: 10,
179 | },
180 | controlOption: {
181 | textAlign: 'center',
182 | fontSize: 13,
183 | color: '#fff',
184 | width: 30,
185 | //lineHeight: 12,
186 | },
187 | controlContainer: {
188 | flex: 1,
189 | //padding: 5,
190 | alignItems: 'center',
191 | justifyContent: 'center',
192 | },
193 |
194 | controlContent: {
195 | width: '100%',
196 | height: 37,
197 | //borderRadius: 10,
198 | backgroundColor: 'rgba(255,255,255,0.6)',
199 | },
200 | controlContent2: {
201 | flex: 1,
202 | flexDirection: 'row',
203 | backgroundColor: 'rgba(0,0,0,0.5)',
204 | alignItems: 'center',
205 | justifyContent: 'space-between',
206 | },
207 |
208 | progress: {
209 | flex: 1,
210 | borderRadius: 3,
211 | alignItems: 'center',
212 | justifyContent: 'center',
213 | },
214 | thumb: {
215 | width: 6,
216 | height: 18,
217 | backgroundColor: '#fff',
218 | borderRadius: 4,
219 | },
220 | loading: {
221 | position: 'absolute',
222 | left: 0,
223 | top: 0,
224 | zIndex: 0,
225 | width: '100%',
226 | height: '100%',
227 | justifyContent: 'center',
228 | alignItems: 'center',
229 | },
230 |
231 | ad: {
232 | backgroundColor: 'rgba(255,255,255,1)',
233 | height: 30,
234 | paddingLeft: 10,
235 | paddingRight: 10,
236 | borderRadius: 20,
237 | justifyContent: 'center',
238 | alignItems: 'center',
239 | },
240 | });
241 |
--------------------------------------------------------------------------------
/playerView/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/15.
3 | */
4 |
5 | import React, { Component } from 'react';
6 | import {
7 | StatusBar,
8 | View,
9 | StyleSheet,
10 | Platform,
11 | TouchableOpacity,
12 | Text,
13 | Dimensions,
14 | BackHandler,
15 | } from 'react-native';
16 |
17 | import VLCPlayerView from './VLCPlayerView';
18 | import PropTypes from 'prop-types';
19 | import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
20 | import {getStatusBarHeight} from './SizeController';
21 | const statusBarHeight = getStatusBarHeight();
22 | const _fullKey = 'commonVideo_android_fullKey';
23 | let deviceHeight = Dimensions.get('window').height;
24 | let deviceWidth = Dimensions.get('window').width;
25 | export default class CommonVideo extends Component {
26 | constructor(props) {
27 | super(props);
28 | this.url = '';
29 | this.initialHeight = 200;
30 | }
31 |
32 | static navigationOptions = {
33 | header: null,
34 | };
35 |
36 | state = {
37 | isEndAd: false,
38 | isFull: false,
39 | currentUrl: '',
40 | storeUrl: '',
41 | };
42 |
43 | static defaultProps = {
44 | height: 250,
45 | showAd: false,
46 | adUrl: '',
47 | url: '',
48 | showBack: false,
49 | showTitle: false,
50 | };
51 |
52 | static propTypes = {
53 | /**
54 | * 视频播放结束
55 | */
56 | onEnd: PropTypes.func,
57 |
58 | /**
59 | * 广告头播放结束
60 | */
61 | onAdEnd: PropTypes.func,
62 | /**
63 | * 开启全屏
64 | */
65 | startFullScreen: PropTypes.func,
66 | /**
67 | * 关闭全屏
68 | */
69 | closeFullScreen: PropTypes.func,
70 | /**
71 | * 返回按钮点击事件
72 | */
73 | onLeftPress: PropTypes.func,
74 | /**
75 | * 标题
76 | */
77 | title: PropTypes.string,
78 | /**
79 | * 是否显示返回按钮
80 | */
81 | showBack: PropTypes.bool,
82 | /**
83 | * 是否显示标题
84 | */
85 | showTitle: PropTypes.bool,
86 | };
87 |
88 | static getDerivedStateFromProps(nextProps, preState) {
89 | let { url } = nextProps;
90 | let { currentUrl, storeUrl } = preState;
91 | if (url && url !== storeUrl) {
92 | if(storeUrl === ""){
93 | return {
94 | currentUrl: url,
95 | storeUrl: url,
96 | isEndAd: false,
97 | };
98 | }else{
99 | return {
100 | currentUrl: "",
101 | storeUrl: url,
102 | isEndAd: false,
103 | };
104 | }
105 | }
106 | return null;
107 | }
108 |
109 |
110 | componentDidUpdate(prevProps, prevState) {
111 | if (this.props.url !== prevState.storeUrl) {
112 | this.setState({
113 | storeUrl: this.props.url,
114 | currentUrl: this.props.url
115 | })
116 | }
117 | }
118 |
119 | componentDidMount(){
120 | StatusBar.setBarStyle("light-content");
121 | let { style, isAd } = this.props;
122 |
123 | if(style && style.height && !isNaN(style.height)){
124 | this.initialHeight = style.height;
125 | }
126 | this.setState({
127 | currentVideoAspectRatio: deviceWidth + ":" + this.initialHeight,
128 | });
129 | }
130 |
131 | componentWillUnmount() {
132 | let { isFull } = this.props;
133 | if (isFull) {
134 | this._closeFullScreen();
135 | }
136 | }
137 |
138 | _closeFullScreen = () => {
139 | let { closeFullScreen, BackHandle, Orientation } = this.props;
140 | this.setState({ isFull: false, currentVideoAspectRatio: deviceWidth + ":" + this.initialHeight, });
141 | BackHandle && BackHandle.removeBackFunction(_fullKey);
142 | Orientation && Orientation.lockToPortrait();
143 | StatusBar.setHidden(false);
144 | //StatusBar.setTranslucent(false);
145 | closeFullScreen && closeFullScreen();
146 | };
147 |
148 | _toFullScreen = () => {
149 | let { startFullScreen, BackHandle, Orientation } = this.props;
150 | //StatusBar.setTranslucent(true);
151 | this.setState({ isFull: true, currentVideoAspectRatio: deviceHeight + ":" + deviceWidth,});
152 | StatusBar.setHidden(true);
153 | BackHandle && BackHandle.addBackFunction(_fullKey, this._closeFullScreen);
154 | startFullScreen && startFullScreen();
155 | Orientation && Orientation.lockToLandscape && Orientation.lockToLandscape();
156 | };
157 |
158 | _onLayout = (e)=>{
159 | let {width, height} = e.nativeEvent.layout;
160 | console.log(e.nativeEvent.layout);
161 | if(width * height > 0){
162 | this.width = width;
163 | this.height = height;
164 | if(!this.initialHeight){
165 | this.initialHeight = height;
166 | }
167 | }
168 | }
169 |
170 |
171 |
172 | render() {
173 | let { url, adUrl, showAd, onAdEnd, onEnd, style, height, title, onLeftPress, showBack, showTitle,closeFullScreen, videoAspectRatio, fullVideoAspectRatio } = this.props;
174 | let { isEndAd, isFull, currentUrl } = this.state;
175 | let currentVideoAspectRatio = '';
176 | if(isFull){
177 | currentVideoAspectRatio = fullVideoAspectRatio;
178 | }else{
179 | currentVideoAspectRatio = videoAspectRatio;
180 | }
181 | if(!currentVideoAspectRatio){
182 | let { width, height} = this.state;
183 | currentVideoAspectRatio = this.state.currentVideoAspectRatio;
184 | }
185 | let realShowAd = false;
186 | let type = '';
187 | let adType = '';
188 | let showVideo = false;
189 | let showTop = false;
190 | if (showAd && adUrl && !isEndAd) {
191 | realShowAd = true;
192 | }
193 | if (currentUrl) {
194 | if(!showAd || (showAd && isEndAd)){
195 | showVideo = true;
196 | }
197 | if(currentUrl.split){
198 | let types = currentUrl.split('.');
199 | if (types && types.length > 0) {
200 | type = types[types.length - 1];
201 | }
202 | }
203 | }
204 | if (adUrl && adUrl.split) {
205 | let types = adUrl.split('.');
206 | if (types && types.length > 0) {
207 | adType = types[types.length - 1];
208 | }
209 | }
210 | if(!showVideo && !realShowAd){
211 | showTop = true;
212 | }
213 | return (
214 |
217 | {showTop &&
218 |
219 | {showBack && {
221 | if(isFull){
222 | closeFullScreen && closeFullScreen();
223 | }else{
224 | onLeftPress && onLeftPress();
225 | }
226 | }}
227 | style={styles.btn}
228 | activeOpacity={0.8}>
229 |
230 |
231 | }
232 |
233 | {showTitle &&
234 | {title}
235 | }
236 |
237 |
238 |
239 | }
240 | {realShowAd && (
241 | {
252 | onAdEnd && onAdEnd();
253 | this.setState({ isEndAd: true });
254 | }}
255 | startFullScreen={this._toFullScreen}
256 | closeFullScreen={this._closeFullScreen}
257 | />
258 | )}
259 |
260 | {showVideo && (
261 | {
279 | onEnd && onEnd();
280 | }}
281 | />
282 | )}
283 |
284 | );
285 | }
286 | }
287 |
288 | const styles = StyleSheet.create({
289 | container: {
290 | flex: 1,
291 | backgroundColor: '#000',
292 | },
293 | topView:{
294 | top:Platform.OS === 'ios' ? statusBarHeight: 0,
295 | left:0,
296 | height:45,
297 | position:'absolute',
298 | width:'100%'
299 | },
300 | backBtn:{
301 | height:45,
302 | width:'100%',
303 | flexDirection:'row',
304 | alignItems:'center'
305 | },
306 | btn:{
307 | marginLeft:10,
308 | marginRight:10,
309 | justifyContent:'center',
310 | alignItems:'center',
311 | backgroundColor:'rgba(0,0,0,0.1)',
312 | height:40,
313 | borderRadius:20,
314 | width:40,
315 | }
316 | });
317 |
--------------------------------------------------------------------------------
/VLCPlayer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactNative from 'react-native';
3 |
4 | const { Component } = React;
5 |
6 | import PropTypes from 'prop-types';
7 |
8 | const { StyleSheet, requireNativeComponent, NativeModules, View, Platform } = ReactNative;
9 | import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
10 |
11 | export default class VLCPlayer extends Component {
12 | constructor(props, context) {
13 | super(props, context);
14 | this.seek = this.seek.bind(this);
15 | this.resume = this.resume.bind(this);
16 | this.play = this.play.bind(this);
17 | this.snapshot = this.snapshot.bind(this);
18 | this._assignRoot = this._assignRoot.bind(this);
19 | this._onProgress = this._onProgress.bind(this);
20 | this._onLoadStart = this._onLoadStart.bind(this);
21 | this._onSnapshot = this._onSnapshot.bind(this);
22 | this._onIsPlaying = this._onIsPlaying.bind(this);
23 | this._onVideoStateChange = this._onVideoStateChange.bind(this);
24 | this.clear = this.clear.bind(this);
25 | this.changeVideoAspectRatio = this.changeVideoAspectRatio.bind(this);
26 |
27 | }
28 |
29 | static defaultProps = {
30 | autoplay: true,
31 | };
32 |
33 | setNativeProps(nativeProps) {
34 | this._root.setNativeProps(nativeProps);
35 | }
36 |
37 | clear(){
38 | this.setNativeProps({ clear: true });
39 | }
40 |
41 | seek(pos) {
42 | this.setNativeProps({ seek: pos });
43 | }
44 |
45 | autoAspectRatio(isAuto){
46 | this.setNativeProps({ autoAspectRatio: isAuto });
47 | }
48 |
49 | changeVideoAspectRatio(ratio){
50 | this.setNativeProps({ videoAspectRatio: ratio });
51 | }
52 |
53 | play(paused){
54 | this.setNativeProps({ paused: paused });
55 | }
56 |
57 | position(position){
58 | this.setNativeProps({ position: position });
59 | }
60 |
61 | resume(isResume) {
62 | this.setNativeProps({ resume: isResume });
63 | }
64 |
65 | snapshot(path) {
66 | this.setNativeProps({ snapshotPath: path });
67 | }
68 |
69 | _assignRoot(component) {
70 | this._root = component;
71 | }
72 |
73 | _onVideoStateChange(event){
74 | //
75 | let type = event.nativeEvent.type;
76 | if(__DEV__ && this.props.showLog){
77 | console.log(type,event.nativeEvent);
78 | }
79 | switch (type){
80 | case 'Opening':
81 | this.props.onOpen && this.props.onOpen(event.nativeEvent);
82 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
83 | break;
84 | case 'Playing':
85 | this.props.onPlaying && this.props.onPlaying(event.nativeEvent);
86 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
87 | break;
88 | case 'Paused':
89 | this.props.onPaused && this.props.onPaused(event.nativeEvent);
90 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
91 | break;
92 | case 'Stoped':
93 | this.props.onStopped && this.props.onStopped(event.nativeEvent);
94 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
95 | break;
96 | case 'Ended':
97 | this.props.onEnd && this.props.onEnd(event.nativeEvent);
98 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
99 | break;
100 | case 'Buffering':
101 | this.props.onBuffering && this.props.onBuffering(event.nativeEvent);
102 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
103 | break;
104 | case 'onLoadStart':
105 | this.props.onLoadStart && this.props.onLoadStart(event.nativeEvent);
106 | break;
107 | case 'Error':
108 | this.props.onError && this.props.onError(event.nativeEvent);
109 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
110 | break;
111 | case 'TimeChanged':
112 | this.props.onProgress && this.props.onProgress(event.nativeEvent);
113 | this.props.onIsPlaying && this.props.onIsPlaying(event.nativeEvent);
114 | break;
115 | default:
116 | this.props.onVideoStateChange && this.props.onVideoStateChange(event);
117 | break;
118 | }
119 | }
120 |
121 | _onLoadStart(event){
122 | if (this.props.onLoadStart) {
123 | this.props.onLoadStart(event.nativeEvent);
124 | }
125 | }
126 |
127 | _onProgress(event) {
128 | if (this.props.onProgress) {
129 | this.props.onProgress(event.nativeEvent);
130 | }
131 | }
132 |
133 |
134 | _onIsPlaying(event){
135 | if(this.props.onIsPlaying){
136 | this.props.onIsPlaying(event.nativeEvent);
137 | }
138 | }
139 |
140 | _onSnapshot(event){
141 | if (this.props.onSnapshot) {
142 | this.props.onSnapshot(event.nativeEvent);
143 | }
144 | }
145 |
146 | render() {
147 | const source = resolveAssetSource({ ...this.props.source }) || {};
148 | let uri = source.uri || '';
149 | let isNetwork = !!(uri && uri.match(/^https?:/));
150 | const isAsset = !!(uri && uri.match(/^(assets-library|file|content|ms-appx|ms-appdata):/));
151 | if(!isAsset){
152 | isNetwork = true;
153 | }
154 | if (uri && uri.match(/^\//)) {
155 | isNetwork = false;
156 | }
157 | if(Platform.OS === 'ios'){
158 | source.mediaOptions = this.props.mediaOptions || {};
159 | }else{
160 | let mediaOptionsList = [];
161 | let mediaOptions = this.props.mediaOptions || {};
162 | let keys = Object.keys(mediaOptions);
163 | for(let i=0; i < keys.length - 1; i++){
164 | let optionKey = keys[i];
165 | let optionValue = mediaOptions[optionKey];
166 | mediaOptionsList.push(optionKey + '=' + optionValue);
167 | }
168 | source.mediaOptions = mediaOptionsList;
169 | }
170 | source.initOptions = this.props.initOptions || [];
171 | source.isNetwork = isNetwork;
172 | source.autoplay = this.props.autoplay;
173 | if(!isNaN(this.props.hwDecoderEnabled) && !isNaN(this.props.hwDecoderForced)){
174 | source.hwDecoderEnabled = this.props.hwDecoderEnabled;
175 | source.hwDecoderForced = this.props.hwDecoderForced;
176 | }
177 | if(this.props.initType){
178 | source.initType = this.props.initType;
179 | }else{
180 | source.initType = 1;
181 | }
182 |
183 | //repeat the input media
184 | //source.initOptions.push('--input-repeat=1000');
185 | const nativeProps = Object.assign({}, this.props);
186 | Object.assign(nativeProps, {
187 | style: [styles.base, nativeProps.style],
188 | source: source,
189 | onVideoLoadStart: this._onLoadStart,
190 | onVideoProgress: this._onProgress,
191 | onVideoStateChange: this._onVideoStateChange,
192 | onSnapshot: this._onSnapshot,
193 | onIsPlaying: this._onIsPlaying,
194 | progressUpdateInterval: 250,
195 | });
196 |
197 | return ;
198 | }
199 | }
200 |
201 | VLCPlayer.propTypes = {
202 | /* Native only */
203 | rate: PropTypes.number,
204 | seek: PropTypes.number,
205 | resume: PropTypes.bool,
206 | position: PropTypes.number,
207 | snapshotPath: PropTypes.string,
208 | paused: PropTypes.bool,
209 | autoAspectRatio: PropTypes.bool,
210 | videoAspectRatio: PropTypes.string,
211 | /**
212 | * 0 --- 200
213 | */
214 | volume: PropTypes.number,
215 | volumeUp:PropTypes.number,
216 | volumeDown: PropTypes.number,
217 | repeat: PropTypes.bool,
218 | muted: PropTypes.bool,
219 |
220 | hwDecoderEnabled: PropTypes.number,
221 | hwDecoderForced: PropTypes.number,
222 |
223 | onVideoLoadStart: PropTypes.func,
224 | onVideoStateChange: PropTypes.func,
225 | onVideoProgress: PropTypes.func,
226 | onSnapshot: PropTypes.func,
227 | onIsPlaying: PropTypes.func,
228 | onOpen: PropTypes.func,
229 | onLoadStart:PropTypes.func,
230 |
231 |
232 | /* Wrapper component */
233 | source: PropTypes.oneOfType([PropTypes.object,PropTypes.number]),
234 | play: PropTypes.func,
235 | snapshot: PropTypes.func,
236 | onError: PropTypes.func,
237 | onProgress: PropTypes.func,
238 | onEnded: PropTypes.func,
239 | onStopped: PropTypes.func,
240 | onPlaying: PropTypes.func,
241 | onPaused: PropTypes.func,
242 |
243 | /* Required by react-native */
244 | scaleX: PropTypes.number,
245 | scaleY: PropTypes.number,
246 | translateX: PropTypes.number,
247 | translateY: PropTypes.number,
248 | rotation: PropTypes.number,
249 | ...View.propTypes,
250 | };
251 |
252 | const styles = StyleSheet.create({
253 | base: {
254 | overflow: 'hidden',
255 | },
256 | });
257 | const RCTVLCPlayer = requireNativeComponent('RCTVLCPlayer', VLCPlayer);
258 |
--------------------------------------------------------------------------------
/ios/RCTVLCPlayer.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0C1A0ECA1D07E18700441684 /* RCTVLCPlayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C1A0EC81D07E18700441684 /* RCTVLCPlayerManager.m */; };
11 | 0CA30C481D07E0DB003B09F9 /* RCTVLCPlayer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0CA30C471D07E0DB003B09F9 /* RCTVLCPlayer.h */; };
12 | 0CA30C4A1D07E0DB003B09F9 /* RCTVLCPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA30C491D07E0DB003B09F9 /* RCTVLCPlayer.m */; };
13 | /* End PBXBuildFile section */
14 |
15 | /* Begin PBXCopyFilesBuildPhase section */
16 | 0CA30C421D07E0DB003B09F9 /* CopyFiles */ = {
17 | isa = PBXCopyFilesBuildPhase;
18 | buildActionMask = 2147483647;
19 | dstPath = "include/$(PRODUCT_NAME)";
20 | dstSubfolderSpec = 16;
21 | files = (
22 | 0CA30C481D07E0DB003B09F9 /* RCTVLCPlayer.h in CopyFiles */,
23 | );
24 | runOnlyForDeploymentPostprocessing = 0;
25 | };
26 | /* End PBXCopyFilesBuildPhase section */
27 |
28 | /* Begin PBXFileReference section */
29 | 0C1A0EC81D07E18700441684 /* RCTVLCPlayerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVLCPlayerManager.m; sourceTree = ""; };
30 | 0C1A0EC91D07E18700441684 /* RCTVLCPlayerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVLCPlayerManager.h; sourceTree = ""; };
31 | 0CA30C441D07E0DB003B09F9 /* libRCTVLCPlayer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVLCPlayer.a; sourceTree = BUILT_PRODUCTS_DIR; };
32 | 0CA30C471D07E0DB003B09F9 /* RCTVLCPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTVLCPlayer.h; sourceTree = ""; };
33 | 0CA30C491D07E0DB003B09F9 /* RCTVLCPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTVLCPlayer.m; sourceTree = ""; };
34 | /* End PBXFileReference section */
35 |
36 | /* Begin PBXFrameworksBuildPhase section */
37 | 0CA30C411D07E0DB003B09F9 /* Frameworks */ = {
38 | isa = PBXFrameworksBuildPhase;
39 | buildActionMask = 2147483647;
40 | files = (
41 | );
42 | runOnlyForDeploymentPostprocessing = 0;
43 | };
44 | /* End PBXFrameworksBuildPhase section */
45 |
46 | /* Begin PBXGroup section */
47 | 0CA30C3B1D07E0DB003B09F9 = {
48 | isa = PBXGroup;
49 | children = (
50 | 0CA30C461D07E0DB003B09F9 /* RCTVLCPlayer */,
51 | 0CA30C451D07E0DB003B09F9 /* Products */,
52 | );
53 | sourceTree = "";
54 | };
55 | 0CA30C451D07E0DB003B09F9 /* Products */ = {
56 | isa = PBXGroup;
57 | children = (
58 | 0CA30C441D07E0DB003B09F9 /* libRCTVLCPlayer.a */,
59 | );
60 | name = Products;
61 | sourceTree = "";
62 | };
63 | 0CA30C461D07E0DB003B09F9 /* RCTVLCPlayer */ = {
64 | isa = PBXGroup;
65 | children = (
66 | 0C1A0EC81D07E18700441684 /* RCTVLCPlayerManager.m */,
67 | 0C1A0EC91D07E18700441684 /* RCTVLCPlayerManager.h */,
68 | 0CA30C471D07E0DB003B09F9 /* RCTVLCPlayer.h */,
69 | 0CA30C491D07E0DB003B09F9 /* RCTVLCPlayer.m */,
70 | );
71 | path = RCTVLCPlayer;
72 | sourceTree = "";
73 | };
74 | /* End PBXGroup section */
75 |
76 | /* Begin PBXNativeTarget section */
77 | 0CA30C431D07E0DB003B09F9 /* RCTVLCPlayer */ = {
78 | isa = PBXNativeTarget;
79 | buildConfigurationList = 0CA30C4D1D07E0DB003B09F9 /* Build configuration list for PBXNativeTarget "RCTVLCPlayer" */;
80 | buildPhases = (
81 | 0CA30C401D07E0DB003B09F9 /* Sources */,
82 | 0CA30C411D07E0DB003B09F9 /* Frameworks */,
83 | 0CA30C421D07E0DB003B09F9 /* CopyFiles */,
84 | );
85 | buildRules = (
86 | );
87 | dependencies = (
88 | );
89 | name = RCTVLCPlayer;
90 | productName = RCTVLCPlayer;
91 | productReference = 0CA30C441D07E0DB003B09F9 /* libRCTVLCPlayer.a */;
92 | productType = "com.apple.product-type.library.static";
93 | };
94 | /* End PBXNativeTarget section */
95 |
96 | /* Begin PBXProject section */
97 | 0CA30C3C1D07E0DB003B09F9 /* Project object */ = {
98 | isa = PBXProject;
99 | attributes = {
100 | LastUpgradeCheck = 0730;
101 | ORGANIZATIONNAME = "熊川";
102 | TargetAttributes = {
103 | 0CA30C431D07E0DB003B09F9 = {
104 | CreatedOnToolsVersion = 7.3.1;
105 | };
106 | };
107 | };
108 | buildConfigurationList = 0CA30C3F1D07E0DB003B09F9 /* Build configuration list for PBXProject "RCTVLCPlayer" */;
109 | compatibilityVersion = "Xcode 3.2";
110 | developmentRegion = English;
111 | hasScannedForEncodings = 0;
112 | knownRegions = (
113 | en,
114 | );
115 | mainGroup = 0CA30C3B1D07E0DB003B09F9;
116 | productRefGroup = 0CA30C451D07E0DB003B09F9 /* Products */;
117 | projectDirPath = "";
118 | projectRoot = "";
119 | targets = (
120 | 0CA30C431D07E0DB003B09F9 /* RCTVLCPlayer */,
121 | );
122 | };
123 | /* End PBXProject section */
124 |
125 | /* Begin PBXSourcesBuildPhase section */
126 | 0CA30C401D07E0DB003B09F9 /* Sources */ = {
127 | isa = PBXSourcesBuildPhase;
128 | buildActionMask = 2147483647;
129 | files = (
130 | 0C1A0ECA1D07E18700441684 /* RCTVLCPlayerManager.m in Sources */,
131 | 0CA30C4A1D07E0DB003B09F9 /* RCTVLCPlayer.m in Sources */,
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | /* End PBXSourcesBuildPhase section */
136 |
137 | /* Begin XCBuildConfiguration section */
138 | 0CA30C4B1D07E0DB003B09F9 /* Debug */ = {
139 | isa = XCBuildConfiguration;
140 | buildSettings = {
141 | ALWAYS_SEARCH_USER_PATHS = NO;
142 | CLANG_ANALYZER_NONNULL = YES;
143 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
144 | CLANG_CXX_LIBRARY = "libc++";
145 | CLANG_ENABLE_MODULES = YES;
146 | CLANG_ENABLE_OBJC_ARC = YES;
147 | CLANG_WARN_BOOL_CONVERSION = YES;
148 | CLANG_WARN_CONSTANT_CONVERSION = YES;
149 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
150 | CLANG_WARN_EMPTY_BODY = YES;
151 | CLANG_WARN_ENUM_CONVERSION = YES;
152 | CLANG_WARN_INT_CONVERSION = YES;
153 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
154 | CLANG_WARN_UNREACHABLE_CODE = YES;
155 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
156 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
157 | COPY_PHASE_STRIP = NO;
158 | DEBUG_INFORMATION_FORMAT = dwarf;
159 | ENABLE_STRICT_OBJC_MSGSEND = YES;
160 | ENABLE_TESTABILITY = YES;
161 | GCC_C_LANGUAGE_STANDARD = gnu99;
162 | GCC_DYNAMIC_NO_PIC = NO;
163 | GCC_NO_COMMON_BLOCKS = YES;
164 | GCC_OPTIMIZATION_LEVEL = 0;
165 | GCC_PREPROCESSOR_DEFINITIONS = (
166 | "DEBUG=1",
167 | "$(inherited)",
168 | );
169 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
170 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
171 | GCC_WARN_UNDECLARED_SELECTOR = YES;
172 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
173 | GCC_WARN_UNUSED_FUNCTION = YES;
174 | GCC_WARN_UNUSED_VARIABLE = YES;
175 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
176 | MTL_ENABLE_DEBUG_INFO = YES;
177 | ONLY_ACTIVE_ARCH = YES;
178 | SDKROOT = iphoneos;
179 | VALID_ARCHS = "arm64 armv7 armv7s";
180 | };
181 | name = Debug;
182 | };
183 | 0CA30C4C1D07E0DB003B09F9 /* Release */ = {
184 | isa = XCBuildConfiguration;
185 | buildSettings = {
186 | ALWAYS_SEARCH_USER_PATHS = NO;
187 | CLANG_ANALYZER_NONNULL = YES;
188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
189 | CLANG_CXX_LIBRARY = "libc++";
190 | CLANG_ENABLE_MODULES = YES;
191 | CLANG_ENABLE_OBJC_ARC = YES;
192 | CLANG_WARN_BOOL_CONVERSION = YES;
193 | CLANG_WARN_CONSTANT_CONVERSION = YES;
194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
195 | CLANG_WARN_EMPTY_BODY = YES;
196 | CLANG_WARN_ENUM_CONVERSION = YES;
197 | CLANG_WARN_INT_CONVERSION = YES;
198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
202 | COPY_PHASE_STRIP = NO;
203 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
204 | ENABLE_NS_ASSERTIONS = NO;
205 | ENABLE_STRICT_OBJC_MSGSEND = YES;
206 | GCC_C_LANGUAGE_STANDARD = gnu99;
207 | GCC_NO_COMMON_BLOCKS = YES;
208 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
209 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
210 | GCC_WARN_UNDECLARED_SELECTOR = YES;
211 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
212 | GCC_WARN_UNUSED_FUNCTION = YES;
213 | GCC_WARN_UNUSED_VARIABLE = YES;
214 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
215 | MTL_ENABLE_DEBUG_INFO = NO;
216 | SDKROOT = iphoneos;
217 | VALIDATE_PRODUCT = YES;
218 | VALID_ARCHS = "arm64 armv7 armv7s";
219 | };
220 | name = Release;
221 | };
222 | 0CA30C4E1D07E0DB003B09F9 /* Debug */ = {
223 | isa = XCBuildConfiguration;
224 | buildSettings = {
225 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../../../vlcKit";
226 | HEADER_SEARCH_PATHS = (
227 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
228 | "$(SRCROOT)/../../react-native/React/**",
229 | "$(SRCROOT)/node_modules/react-native/React/**",
230 | "$(inherited)",
231 | );
232 | ONLY_ACTIVE_ARCH = YES;
233 | OTHER_LDFLAGS = "-ObjC";
234 | PRODUCT_NAME = "$(TARGET_NAME)";
235 | SKIP_INSTALL = YES;
236 | VALID_ARCHS = "arm64 armv7 armv7s";
237 | };
238 | name = Debug;
239 | };
240 | 0CA30C4F1D07E0DB003B09F9 /* Release */ = {
241 | isa = XCBuildConfiguration;
242 | buildSettings = {
243 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../../../vlcKit";
244 | HEADER_SEARCH_PATHS = (
245 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
246 | "$(SRCROOT)/../../react-native/React/**",
247 | "$(SRCROOT)/node_modules/react-native/React/**",
248 | "$(inherited)",
249 | );
250 | ONLY_ACTIVE_ARCH = NO;
251 | OTHER_LDFLAGS = "-ObjC";
252 | PRODUCT_NAME = "$(TARGET_NAME)";
253 | SKIP_INSTALL = YES;
254 | VALID_ARCHS = "arm64 armv7 armv7s";
255 | };
256 | name = Release;
257 | };
258 | /* End XCBuildConfiguration section */
259 |
260 | /* Begin XCConfigurationList section */
261 | 0CA30C3F1D07E0DB003B09F9 /* Build configuration list for PBXProject "RCTVLCPlayer" */ = {
262 | isa = XCConfigurationList;
263 | buildConfigurations = (
264 | 0CA30C4B1D07E0DB003B09F9 /* Debug */,
265 | 0CA30C4C1D07E0DB003B09F9 /* Release */,
266 | );
267 | defaultConfigurationIsVisible = 0;
268 | defaultConfigurationName = Release;
269 | };
270 | 0CA30C4D1D07E0DB003B09F9 /* Build configuration list for PBXNativeTarget "RCTVLCPlayer" */ = {
271 | isa = XCConfigurationList;
272 | buildConfigurations = (
273 | 0CA30C4E1D07E0DB003B09F9 /* Debug */,
274 | 0CA30C4F1D07E0DB003B09F9 /* Release */,
275 | );
276 | defaultConfigurationIsVisible = 0;
277 | defaultConfigurationName = Release;
278 | };
279 | /* End XCConfigurationList section */
280 | };
281 | rootObject = 0CA30C3C1D07E0DB003B09F9 /* Project object */;
282 | }
283 |
--------------------------------------------------------------------------------
/playerViewByMethod/VLCPlayerView.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/14.
3 | */
4 | import React, { Component } from 'react';
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | ActivityIndicator,
10 | Platform,
11 | Animated,
12 | Dimensions
13 | } from 'react-native';
14 | import VLCPlayer from '../VLCPlayer';
15 | import PropTypes from 'prop-types';
16 | const deviceHeight = Dimensions.get('window').height;
17 | const deviceWidth = Dimensions.get('window').width;
18 |
19 | export default class VLCPlayerView extends Component {
20 | static propTypes = {
21 | //uri: PropTypes.string,
22 | };
23 |
24 |
25 | constructor(props) {
26 | super(props);
27 | this.state = {
28 | paused: true,
29 | showLoading: true,
30 | showAdLoading: true,
31 | loadingSuccess: false,
32 | isFull: false,
33 | currentTime: 0,
34 | totalTime: 0,
35 | showControls: false,
36 | seek: 0,
37 | volume: 200,
38 | muted: false,
39 | isError: false,
40 | };
41 | this.touchTime = 0;
42 | this.viewingTime = 0;
43 | this.initSuccess = false;
44 | }
45 |
46 | static defaultProps = {
47 | initPaused: false,
48 | source: null,
49 | seek: 0,
50 | playInBackground: false,
51 | isAd: false,
52 | autoplay: false,
53 | lookTime: 0,
54 | totalTime: 0,
55 | };
56 |
57 |
58 | componentWillUnmount(){
59 | this.clear();
60 | }
61 |
62 | /*****************************
63 | * *
64 | * VLCPlayer callback *
65 | * *
66 | *****************************/
67 |
68 | /**
69 | * when the video is play
70 | * @param event
71 | */
72 | _onPlaying = (event) => {
73 | console.log('onPlaying');
74 | }
75 |
76 | /**
77 | * when then video is paused
78 | * @param event
79 | */
80 | _onPaused = (event) => {
81 | console.log('onPaused');
82 | }
83 |
84 | /**
85 | * when the video is buffering
86 | * @param event
87 | */
88 | _onBuffering = (event) => {
89 | this.props.onBuffering && this.props.onBuffering(event);
90 | }
91 |
92 | /**
93 | * get status of video if isPlaying
94 | * @param event
95 | * @private
96 | */
97 | _onIsPlaying =(event)=>{
98 | this.props.onIsPlaying && this.props.onIsPlaying(event)
99 | }
100 |
101 | /**
102 | * when video is error
103 | * @param e
104 | * @private
105 | */
106 | _onError = e => {
107 | this.props.onError && this.props.onError(e);
108 | };
109 |
110 | _onSnapshot = e => {
111 | this.props.onSnapshot && this.props.onSnapshot(e);
112 | }
113 |
114 | /**
115 | * when the vido is open
116 | * @param e
117 | * @private
118 | */
119 | _onOpen = e => {
120 | console.log('onOpen');
121 | console.log(e);
122 | };
123 |
124 | /**
125 | * when the video is init
126 | * @param e
127 | * @private
128 | */
129 | _onLoadStart = e => {
130 | this.props.onLoadStart && this.props.onLoadStart(e);
131 | };
132 |
133 | /**
134 | * when the video progress is change
135 | * @param event
136 | */
137 | _onProgress = (event) => {
138 | this.props.onProgressChange &&
139 | this.props.onProgressChange({
140 | currentTime: event.currentTime / 1000,
141 | totalTime: event.duration / 1000,
142 | });
143 | }
144 |
145 | /**
146 | * when the video is ended
147 | * @param event
148 | */
149 | _onEnded = (event) => {
150 | let { onEnd } = this.props;
151 | onEnd && onEnd(event);
152 | }
153 |
154 | /**
155 | * when the video is stopped
156 | * @param event
157 | * @private
158 | */
159 | _onStopped = (event) => {
160 | this.props.onStopped && this.props.onStopped(event);
161 | }
162 |
163 |
164 | /*****************************************************************
165 | * *
166 | * VLCPlayer method *
167 | * *
168 | * You can use these like: *
169 | * *
170 | * this.vlcPlayerView = ref }/> *
171 | * *
172 | * this.vlcPlayerView.play(); *
173 | * *
174 | * *
175 | * *
176 | *****************************************************************/
177 |
178 |
179 | /**
180 | * start then video
181 | */
182 | play = () => {
183 | this.vlcPlayer.play(false);
184 | };
185 |
186 | /**
187 | * paused the video
188 | */
189 | pause = () => {
190 | this.vlcPlayer.play(true);
191 | };
192 |
193 | /**
194 | * change the seek of video
195 | * @param value
196 | */
197 | seek = (value)=> {
198 | this.vlcPlayer.seek(value);
199 | }
200 |
201 | position = (position)=> {
202 | this.vlcPlayer.position(position);
203 | }
204 |
205 | volume = (volume)=>{
206 | this.setState({
207 | volume: volume
208 | })
209 | }
210 |
211 | /**
212 | * reload the video
213 | * @param value
214 | */
215 | reload = (value) => {
216 | this.vlcPlayer.resume && this.vlcPlayer.resume(value);
217 | };
218 |
219 | snapshot = (path)=>{
220 | this.vlcPlayer && this.vlcPlayer.snapshot(path);
221 | }
222 |
223 | /**
224 | *
225 | * @param value
226 | */
227 | muted = (value) => {
228 | this.setState({
229 | muted: value
230 | });
231 | }
232 |
233 | clear = ()=> {
234 | this.vlcPlayer && this.vlcPlayer.clear && this.vlcPlayer.clear();
235 | }
236 |
237 | changeVideoAspectRatio = (ratio)=>{
238 | this.vlcPlayer && this.vlcPlayer.changeVideoAspectRatio && this.vlcPlayer.changeVideoAspectRatio(ratio);
239 | }
240 |
241 |
242 | /******************************
243 | * *
244 | * UI *
245 | * *
246 | ******************************/
247 |
248 | /**
249 | * 渲染loading
250 | * @return {*}
251 | * @private
252 | */
253 | _renderLoading = ()=>{
254 | let { showAd, isEndAd, isAd, pauseByAutoplay} = this.props;
255 | let { showLoading, showAdLoading } = this.state;
256 | if(!pauseByAutoplay){
257 | if(isAd){
258 | if(showAdLoading){
259 | return(
260 |
261 |
262 |
263 | )
264 | }
265 | }else{
266 | if(showLoading && ((showAd && isEndAd) || !showAd)){
267 | return(
268 |
269 |
270 |
271 | )
272 | }
273 | }
274 | }
275 |
276 | return null;
277 | }
278 |
279 | /**
280 | * 布局发生变化
281 | * @param e
282 | * @private
283 | */
284 | onLayout = e => {
285 | let { width, height } = e.nativeEvent.layout;
286 | this.setState({
287 | width,
288 | height
289 | })
290 | };
291 |
292 |
293 | render() {
294 | let {
295 | onEnd,
296 | style,
297 | isAd,
298 | url,
299 | volume,
300 | muted,
301 | videoAspectRatio,
302 | mediaOptions,
303 | initOptions,
304 | initType,
305 | autoplay,
306 | isFull
307 | } = this.props;
308 | let { width, height} = this.state;
309 | let source = {};
310 | if (url) {
311 | if (url.split) {
312 | source = { uri: url };
313 | } else {
314 | source = url;
315 | }
316 | }
317 | /*if(!videoAspectRatio){
318 | if(isFull){
319 | if(width + height - deviceWidth+deviceHeight <= 50){
320 | videoAspectRatio = width + ':' + height;
321 | }
322 | }else{
323 | if(width*height > 0){
324 | videoAspectRatio = width + ':' + height;
325 | }
326 | }
327 | }
328 | console.log(videoAspectRatio);*/
329 | return (
330 |
331 |
332 | (this.vlcPlayer = ref)}
338 | style={styles.video}
339 | autoplay={autoplay}
340 | source={source}
341 | volume={this.state.volume}
342 | muted={this.state.muted}
343 | videoAspectRatio={videoAspectRatio}
344 | onProgress={this._onProgress}
345 | onEnd={this._onEnded}
346 | onStopped={this._onStopped}
347 | onPlaying={this._onPlaying}
348 | onBuffering={this._onBuffering}
349 | onPaused={this.onPaused}
350 | onError={this._onError}
351 | onOpen={this._onOpen}
352 | onLoadStart={this._onLoadStart}
353 | onSnapshot={this._onSnapshot}
354 | onIsPlaying={this._onIsPlaying}
355 | mediaOptions={mediaOptions ||
356 | {
357 | ':network-caching': 250,
358 | ':live-caching': 250,
359 | }
360 | }
361 | initOptions={initOptions || []}
362 | initType={initType || 1}
363 | />
364 |
365 | {/* {this._renderLoading()}*/}
366 |
367 | );
368 | }
369 |
370 | }
371 |
372 | const styles = StyleSheet.create({
373 | container: {
374 | flex: 1,
375 | },
376 | videoBtn: {
377 | flex: 1,
378 | },
379 | video: {
380 | flex:1
381 | },
382 | loading: {
383 | position: 'absolute',
384 | left: 0,
385 | top: 0,
386 | zIndex: 0,
387 | width: '100%',
388 | height: '100%',
389 | justifyContent: 'center',
390 | alignItems: 'center',
391 | },
392 | ad: {
393 | backgroundColor: 'rgba(255,255,255,1)',
394 | height: 30,
395 | marginRight: 10,
396 | paddingLeft: 10,
397 | paddingRight: 10,
398 | borderRadius: 20,
399 | justifyContent: 'center',
400 | alignItems: 'center',
401 | },
402 |
403 | topView: {
404 | top: 0,//Platform.OS === 'ios' ? statusBarHeight : 0,
405 | backgroundColor: 'rgba(0,0,0,0.6)',
406 | left: 0,
407 | height: 37,
408 | zIndex: 999,
409 | position: 'absolute',
410 | width: '100%',
411 | },
412 | backBtn: {
413 | height: 37,
414 | width: '100%',
415 | flexDirection: 'row',
416 | justifyContent: 'center',
417 | alignItems: 'center',
418 | },
419 | btn: {
420 | //marginLeft: 10,
421 | marginRight: 8,
422 | paddingTop: 3,
423 | justifyContent: 'center',
424 | alignItems: 'center',
425 | height: 37,
426 | width: 40,
427 | },
428 |
429 | bottomView: {
430 | bottom: 0,
431 | left: 0,
432 | height: 37,
433 | zIndex:666,
434 | position: 'absolute',
435 | width: '100%',
436 | backgroundColor: 'rgba(0,0,0,0)',
437 | },
438 | });
439 |
--------------------------------------------------------------------------------
/android/react-native-yz-vlcplayer.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/playerView/VLCPlayerView.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yuanzhou.xu on 2018/5/14.
3 | */
4 | import React, { Component } from 'react';
5 | import {
6 | StyleSheet,
7 | Text,
8 | View,
9 | Dimensions,
10 | TouchableOpacity,
11 | ActivityIndicator,
12 | StatusBar,
13 | BackHandler,
14 | Modal,
15 | Platform,
16 | } from 'react-native';
17 | import VLCPlayer from '../VLCPlayer';
18 | import PropTypes from 'prop-types';
19 | import TimeLimt from './TimeLimit';
20 | import ControlBtn from './ControlBtn';
21 | import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
22 | import { getStatusBarHeight } from './SizeController';
23 | const statusBarHeight = getStatusBarHeight();
24 | let deviceHeight = Dimensions.get('window').height;
25 | let deviceWidth = Dimensions.get('window').width;
26 | export default class VLCPlayerView extends Component {
27 | static propTypes = {
28 | uri: PropTypes.string,
29 | };
30 |
31 | constructor(props) {
32 | super(props);
33 | this.state = {
34 | paused: true,
35 | isLoading: true,
36 | loadingSuccess: false,
37 | isFull: false,
38 | currentTime: 0.0,
39 | totalTime: 0.0,
40 | showControls: false,
41 | seek: 0,
42 | isError: false,
43 | };
44 | this.touchTime = 0;
45 | this.changeUrl = false;
46 | this.isEnding = false;
47 | this.reloadSuccess = false;
48 | }
49 |
50 | static defaultProps = {
51 | initPaused: false,
52 | source: null,
53 | seek: 0,
54 | playInBackground: false,
55 | isAd: false,
56 | autoplay: true,
57 | };
58 |
59 | componentDidMount() {
60 | if (this.props.isFull) {
61 | this.setState({
62 | showControls: true,
63 | });
64 | }
65 | }
66 |
67 | componentWillUnmount() {
68 | if(this.bufferInterval){
69 | clearInterval(this.bufferInterval);
70 | this.bufferInterval = null;
71 | }
72 |
73 | }
74 |
75 | componentDidUpdate(prevProps, prevState) {
76 | if (this.props.uri !== prevProps.uri) {
77 | console.log("componentDidUpdate");
78 | this.changeUrl = true;
79 |
80 | }
81 | }
82 |
83 | render() {
84 | let {
85 | onEnd,
86 | style,
87 | isAd,
88 | type,
89 | isFull,
90 | uri,
91 | title,
92 | onLeftPress,
93 | closeFullScreen,
94 | showBack,
95 | showTitle,
96 | videoAspectRatio,
97 | } = this.props;
98 | let { isLoading, loadingSuccess, showControls, isError } = this.state;
99 | let showAd = false;
100 | let realShowLoding = false;
101 | let source = {};
102 | if (uri) {
103 | if (uri.split) {
104 | source = { uri: this.props.uri};
105 | } else {
106 | source = uri;
107 | }
108 | }
109 | if (Platform.OS === 'ios') {
110 | if ((loadingSuccess && isAd) || (isAd && type === 'swf')) {
111 | showAd = true;
112 | }
113 | if (isLoading && type !== 'swf') {
114 | realShowLoding = true;
115 | }
116 | } else {
117 | if (loadingSuccess && isAd) {
118 | showAd = true;
119 | }
120 | if (isLoading) {
121 | realShowLoding = true;
122 | }
123 | }
124 |
125 | return (
126 | {
130 | let currentTime = new Date().getTime();
131 | if (this.touchTime === 0) {
132 | this.touchTime = currentTime;
133 | this.setState({ showControls: !this.state.showControls });
134 | } else {
135 | if (currentTime - this.touchTime >= 500) {
136 | this.touchTime = currentTime;
137 | this.setState({ showControls: !this.state.showControls });
138 | }
139 | }
140 | }}>
141 | (this.vlcPlayer = ref)}
143 | paused={this.state.paused}
144 | //seek={this.state.seek}
145 | style={[styles.video]}
146 | source={source}
147 | videoAspectRatio={videoAspectRatio}
148 | onProgress={this.onProgress.bind(this)}
149 | onEnd={this.onEnded.bind(this)}
150 | //onEnded={this.onEnded.bind(this)}
151 | onStopped={this.onEnded.bind(this)}
152 | onPlaying={this.onPlaying.bind(this)}
153 | onBuffering={this.onBuffering.bind(this)}
154 | onPaused={this.onPaused.bind(this)}
155 | progressUpdateInterval={250}
156 | onError={this._onError}
157 | onOpen={this._onOpen}
158 | onLoadStart={this._onLoadStart}
159 | />
160 | {realShowLoding &&
161 | !isError && (
162 |
163 |
164 |
165 | )}
166 | {isError && (
167 |
168 | 视频播放出错,请重新加载
169 |
178 |
179 |
180 |
181 | )}
182 |
183 |
184 | {showBack && (
185 | {
187 | if (isFull) {
188 | closeFullScreen && closeFullScreen();
189 | } else {
190 | onLeftPress && onLeftPress();
191 | }
192 | }}
193 | style={styles.btn}
194 | activeOpacity={0.8}>
195 |
196 |
197 | )}
198 |
199 | {showTitle &&
200 | showControls && (
201 |
202 | {title}
203 |
204 | )}
205 |
206 | {showAd && (
207 |
208 | {
210 | onEnd && onEnd();
211 | }}
212 | //maxTime={Math.ceil(this.state.totalTime)}
213 | />
214 |
215 | )}
216 |
217 |
218 |
219 | {showControls && (
220 | {
234 | this.changingSlider = true;
235 | this.setState({
236 | currentTime: value,
237 | });
238 | }}
239 | onSlidingComplete={value => {
240 | this.changingSlider = false;
241 | if (Platform.OS === 'ios') {
242 | this.vlcPlayer.seek(Number((value / this.state.totalTime).toFixed(17)));
243 | } else {
244 | this.vlcPlayer.seek(value);
245 | }
246 | }}
247 | />
248 | )}
249 |
250 |
251 | );
252 | }
253 |
254 | /**
255 | * 视屏播放
256 | * @param event
257 | */
258 | onPlaying(event) {
259 | this.isEnding = false;
260 | if (this.state.paused) {
261 | this.setState({ paused: false });
262 | }
263 | console.log('onPlaying');
264 | }
265 |
266 | /**
267 | * 视屏停止
268 | * @param event
269 | */
270 | onPaused(event) {
271 | if (!this.state.paused) {
272 | this.setState({ paused: true, showControls: true });
273 | }else{
274 | this.setState({ showControls: true });
275 | }
276 | console.log('onPaused');
277 | }
278 |
279 | /**
280 | * 视屏缓冲
281 | * @param event
282 | */
283 | onBuffering(event) {
284 | this.setState({
285 | isLoading: true,
286 | isError: false,
287 | });
288 | this.bufferTime = new Date().getTime();
289 | if (!this.bufferInterval) {
290 | this.bufferInterval = setInterval(this.bufferIntervalFunction, 250);
291 | }
292 | console.log('onBuffering');
293 | console.log(event);
294 | }
295 |
296 | bufferIntervalFunction = () => {
297 | console.log('bufferIntervalFunction');
298 | let currentTime = new Date().getTime();
299 | let diffTime = currentTime - this.bufferTime;
300 | if (diffTime > 1000) {
301 | clearInterval(this.bufferInterval);
302 | this.setState({
303 | paused: true,
304 | },()=>{
305 | this.setState({
306 | paused: false,
307 | isLoading: false,
308 | });
309 | });
310 | this.bufferInterval = null;
311 | console.log('remove bufferIntervalFunction');
312 | }
313 | };
314 |
315 | _onError = e => {
316 | console.log('_onError');
317 | console.log(e);
318 | this.reloadSuccess = false;
319 | this.setState({
320 | isError: true,
321 | });
322 | };
323 |
324 | _onOpen = e => {
325 | console.log('onOpen');
326 | console.log(e);
327 | };
328 |
329 | _onLoadStart = e => {
330 | console.log('_onLoadStart');
331 | console.log(e);
332 | let { isError } = this.state;
333 | if(isError){
334 | this.reloadSuccess = true;
335 | let { currentTime, totalTime } = this.state;
336 | if (Platform.OS === 'ios') {
337 | this.vlcPlayer.seek(Number((currentTime / totalTime).toFixed(17)));
338 | } else {
339 | this.vlcPlayer.seek(currentTime);
340 | }
341 | this.setState({
342 | paused: true,
343 | isError: false,
344 | },()=>{
345 | this.setState({
346 | paused: false,
347 | });
348 | })
349 | }else{
350 | this.vlcPlayer.seek(0);
351 | this.setState({
352 | isLoading: true,
353 | isError: false,
354 | loadingSuccess: false,
355 | paused: true,
356 | currentTime: 0.0,
357 | totalTime: 0.0,
358 | },()=>{
359 | this.setState({
360 | paused: false,
361 | });
362 | })
363 | }
364 | };
365 |
366 | _reload = () => {
367 | if(!this.reloadSuccess){
368 | this.vlcPlayer.resume && this.vlcPlayer.resume(false);
369 | }
370 | };
371 |
372 | /**
373 | * 视屏进度变化
374 | * @param event
375 | */
376 | onProgress(event) {
377 | /* console.log(
378 | 'position=' +
379 | event.position +
380 | ',currentTime=' +
381 | event.currentTime +
382 | ',remainingTime=' +
383 | event.remainingTime,
384 | );*/
385 | let currentTime = event.currentTime;
386 | let loadingSuccess = false;
387 | if (currentTime > 0 || this.state.currentTime > 0) {
388 | loadingSuccess = true;
389 | }
390 | if (!this.changingSlider) {
391 | if (currentTime === 0 || currentTime === this.state.currentTime * 1000) {
392 | } else {
393 | this.setState({
394 | loadingSuccess: loadingSuccess,
395 | isLoading: false,
396 | isError: false,
397 | progress: event.position,
398 | currentTime: event.currentTime / 1000,
399 | totalTime: event.duration / 1000,
400 | });
401 | }
402 | }
403 | }
404 |
405 | /**
406 | * 视屏播放结束
407 | * @param event
408 | */
409 | onEnded(event) {
410 | console.log('onEnded ---------->')
411 | console.log(event)
412 | console.log('<---------- onEnded ')
413 | let { currentTime, totalTime } = this.state;
414 | let { onEnd, autoplay, isAd } = this.props;
415 | if (((currentTime+5) >= totalTime && totalTime > 0) || isAd) {
416 | this.setState(
417 | {
418 | paused: true,
419 | //showControls: true,
420 | },
421 | () => {
422 | if (!this.isEnding) {
423 | onEnd && onEnd();
424 | if (!isAd) {
425 | this.vlcPlayer.resume && this.vlcPlayer.resume(false);
426 | console.log(this.props.uri + ': onEnded');
427 | } else {
428 | console.log('片头:' + this.props.uri + ': onEnded');
429 | }
430 | this.isEnding = true;
431 | }
432 | },
433 | );
434 | } else {
435 | /* console.log('onEnded error:'+this.props.uri);
436 | this.vlcPlayer.resume && this.vlcPlayer.resume(false);*/
437 | /*this.setState({
438 | paused: true,
439 | },()=>{
440 | console.log('onEnded error:'+this.props.uri);
441 | this.reloadSuccess = false;
442 | this.setState({
443 | isError: true,
444 | });
445 | });*/
446 | }
447 | }
448 |
449 | /**
450 | * 全屏
451 | * @private
452 | */
453 | _toFullScreen = () => {
454 | let { startFullScreen, closeFullScreen, isFull } = this.props;
455 | if (isFull) {
456 | closeFullScreen && closeFullScreen();
457 | } else {
458 | startFullScreen && startFullScreen();
459 | }
460 | };
461 |
462 | /**
463 | * 播放/停止
464 | * @private
465 | */
466 | _play = () => {
467 | this.setState({paused: !this.state.paused});
468 | };
469 | }
470 |
471 | const styles = StyleSheet.create({
472 | container: {
473 | flex: 1,
474 | },
475 | videoBtn: {
476 | flex: 1,
477 | },
478 | video: {
479 | justifyContent: 'center',
480 | alignItems: 'center',
481 | height: '100%',
482 | width: '100%',
483 | },
484 | loading: {
485 | position: 'absolute',
486 | left: 0,
487 | top: 0,
488 | zIndex: 0,
489 | width: '100%',
490 | height: '100%',
491 | justifyContent: 'center',
492 | alignItems: 'center',
493 | },
494 | ad: {
495 | backgroundColor: 'rgba(255,255,255,1)',
496 | height: 30,
497 | marginRight: 10,
498 | paddingLeft: 10,
499 | paddingRight: 10,
500 | borderRadius: 20,
501 | justifyContent: 'center',
502 | alignItems: 'center',
503 | },
504 | topView: {
505 | top: Platform.OS === 'ios' ? statusBarHeight : 0,
506 | left: 0,
507 | height: 45,
508 | position: 'absolute',
509 | width: '100%',
510 | //backgroundColor: 'red'
511 | },
512 | bottomView: {
513 | bottom: 0,
514 | left: 0,
515 | height: 50,
516 | position: 'absolute',
517 | width: '100%',
518 | backgroundColor: 'rgba(0,0,0,0)',
519 | },
520 | backBtn: {
521 | height: 45,
522 | width: '100%',
523 | flexDirection: 'row',
524 | alignItems: 'center',
525 | },
526 | btn: {
527 | marginLeft: 10,
528 | marginRight: 10,
529 | justifyContent: 'center',
530 | alignItems: 'center',
531 | backgroundColor: 'rgba(0,0,0,0.3)',
532 | height: 40,
533 | borderRadius: 20,
534 | width: 40,
535 | paddingTop: 3,
536 | },
537 | });
538 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-yz-vlcplayer
2 |
3 | A `` component for react-native
4 | 此项目 参考[react-native-video](https://github.com/react-native-community/react-native-video),
5 | [react-native-vlcplayer](https://github.com/xiongchuan86/react-native-vlcplayer),
6 | [react-native-vlc-player](https://github.com/ghondar/react-native-vlc-player)
7 |
8 | VLCPlayer 支持各种格式(mp4,m3u8,flv,mov,rtsp,rtmp,etc.),具体参看[vlc wiki](https://wiki.videolan.org/Documentation:Documentation/)
9 |
10 | [https://code.videolan.org/videolan/VLCKit](https://code.videolan.org/videolan/VLCKit)
11 |
12 |
13 | ## Example project
14 |
15 | [https://github.com/xuyuanzhou/vlcplayerExample](https://github.com/xuyuanzhou/vlcplayerExample)
16 |
17 | 
18 |
19 |
20 | ## Xcode10+ 的一些问题
21 |
22 | (1)libstdc++.6.0.9.tbd 找不到
23 | 在Xcode10中,libstdc++.6.0.9.tbd被移除掉了,我们也移除掉它就OK了
24 |
25 | (2)编译卡死的情况(目前只能等官方修正这个问题)
26 |
27 | [https://forums.developer.apple.com/thread/107570](https://forums.developer.apple.com/thread/107570)
28 |
29 | (1)去除DSYM
30 |
31 | 项目 Build Settings --> Build Options --> Debug Information Format 设置为 DWARF.
32 |
33 | 
34 |
35 | (2)改为Xcode10以下版本编译
36 |
37 |
38 |
39 |
40 | ### install
41 |
42 | `npm install react-native-yz-vlcplayer --save`
43 |
44 |
45 | ## android setup
46 |
47 | android vlc-sdk from:[https://github.com/mengzhidaren/Vlc-sdk-lib](https://github.com/mengzhidaren/Vlc-sdk-lib)
48 |
49 | step 1:
50 |
51 | Run `react-native link react-native-yz-vlcplayer`
52 |
53 |
54 | ## ios setup
55 |
56 | combined from [react-native-vlcplayer](https://github.com/xiongchuan86/react-native-vlcplayer) 。
57 |
58 | reference: [https://code.videolan.org/videolan/VLCKit](https://code.videolan.org/videolan/VLCKit)
59 |
60 | step 1:
61 |
62 | Run `react-native link react-native-yz-vlcplayer`
63 |
64 | step 2:
65 |
66 | download MobileVLCKit.framework from [nightlies.videolan.org/build/iOS/](http://nightlies.videolan.org/build/iOS/)
67 |
68 | step 3:
69 |
70 | create a folder named vlcKit, and copy MobileVLCKit.framework in this folder.
71 |
72 | 
73 |
74 | step 4:
75 |
76 | In XCode, in the project navigator, right click Frameworks -> Add Files to [your project's name], go to `/vlckit` and add MobileVLCKit.framework
77 |
78 |
79 | 
80 |
81 | 
82 |
83 | step 5:
84 |
85 | add framework search path: `$(PROJECT_DIR)/../vlcKit`
86 |
87 | 
88 |
89 |
90 | step 6:
91 |
92 | Select your project. Add the following libraries to your project's Build Phases -> Link Binary With Libraries:
93 |
94 | * AudioToolbox.framework
95 | * VideoToolbox.framework
96 | * CoreMedia.framework
97 | * CoreVideo.framework
98 | * CoreAudio.framework
99 | * AVFoundation.framework
100 | * MediaPlayer.framework
101 | * libstdc++.6.0.9.tbd
102 | * libiconv.2.tbd
103 | * libc++.1.tbd
104 | * libz.1.tbd
105 | * libbz2.1.0.tbd
106 |
107 | step 7:
108 |
109 | set `Enable Bitcode` to `no`
110 |
111 | Build Settings ---> search Bitcode
112 |
113 | 
114 |
115 |
116 | step 8:
117 |
118 | set project deployment target `9.3`
119 |
120 |
121 |
122 | ## other react-native plugins
123 |
124 | 1. npm install react-native-orientation --save
125 |
126 | react-native link react-native-orientation
127 |
128 | 2. npm install react-native-slider --save
129 |
130 | 3. npm install react-native-vector-icons --save
131 |
132 | react-native link react-native-vector-icons
133 |
134 |
135 | ## Static Methods
136 |
137 | `seek(seconds)`
138 |
139 | ```
140 | android:
141 | this.vlcplayer.seek(100); // unit(单位) ms
142 | ios:
143 | this.vlcplayer.seek(0.1); // 0 --- 1 视频位置进度
144 |
145 |
146 | this.vlcPlayer.resume(autoplay) //重新加载视频进行播放,autopaly: true 表示播放 false表示暂停
147 |
148 | this.vlcPlayer.play(bool) // true: play the video false: paused the video
149 |
150 |
151 | this.vlcPlayer.snapshot(path) // path: string 存储的文件的路径。
152 |
153 |
154 | ```
155 |
156 | ## VLCPlayer props
157 |
158 | import { VLCPlayer } from 'react-native-yz-vlcplayer';
159 |
160 | | props | type | value | describe |
161 | | -------- | :----: | :----: | :----: |
162 | | paused | bool | | |
163 | | muted | bool | | |
164 | | volume | bool | 0---200 | |
165 | | hwDecoderEnabled | number | 0 or 1 | (Only android) need use with hwDecoderForced |
166 | | hwDecoderForced | number | 0 or 1 | (Only android) need use with hwDecoderEnabled|
167 | | initType | number | | |
168 | | initOptions | array | | |
169 | | mediaOptions| object | | |
170 | | source | object | { uri: 'http:...' }| |
171 | | autoplay | bool | | 是否自动播放(默认false) |
172 | | onLoadStart | func | | vlc视频容器初始化完毕 |
173 | | onOpen | func | | 视频被打开 |
174 | | onBuffering | func | | 正在缓冲中 |
175 | | onProgress | func | { currentTime:1000,duration:1000 } unit:ms | 视频进度发生改变 |
176 | | onEnd | func | | 视频播放结束 |
177 | | onPlaying | func | | 视频正在播放 |
178 | | onPaused | func | | 视频暂停 |
179 | | onError | func | | 播放视频出错 |
180 | | onStopped | func | | 视频停止播放(直播视频请根据这个判断) |
181 | | onIsPlaying | func | {isPlaying:true} | 视频是否正在播放 |
182 |
183 |
184 | ```
185 | initType: 1,2 default value: 1
186 | example:
187 | ios:
188 | 1: [[VLCMediaPlayer alloc] init]
189 | 2: [[VLCMediaPlayer alloc] initWithOptions:options];
190 |
191 | ```
192 |
193 | initOptions:
194 |
195 | [https://wiki.videolan.org/VLC_command-line_help](https://wiki.videolan.org/VLC_command-line_help)
196 |
197 | [https://www.cnblogs.com/waimai/p/3342739.html](https://www.cnblogs.com/waimai/p/3342739.html)
198 |
199 |
200 |
201 | ```
202 | onBuffer:
203 |
204 | android: {
205 | isPlaying: true,
206 | bufferRate: 70,
207 | duration: 0,
208 | }
209 |
210 | ios: {
211 | duration: 0,
212 | isPlaying: true,
213 | }
214 |
215 | onProgress:
216 |
217 | {
218 | currentTime: 1000 ms
219 | duration: 5000 ms
220 | }
221 |
222 | onIsPlaying:
223 |
224 | {
225 | isPlaying: true
226 | }
227 |
228 |
229 |
230 | ```
231 |
232 |
233 | ## 回调函数简单说明(目前碰到的)
234 | ```
235 | 支持平台
236 | onEnd 视频播放结束 ios android
237 | onBuffering 正在缓冲中 ios android
238 | onError 播放视频出错
239 | onPlaying 视频播放 ios android
240 | onPaused 视频暂停 ios android
241 | onOpen 视频被打开 android
242 | onLoadStart vlc视频容器初始化完毕 ios android
243 | onProgress 视频进度发生改变 ios android swf格式不支持
244 |
245 | 回调函数出现顺序: onLoadStart ---> onOpen
246 |
247 | ```
248 |
249 |
250 | ## use plugin
251 | ````
252 | import { VLCPlayer, VlCPlayerView } from 'react-native-yz-vlcplayer';
253 | import Orientation from 'react-native-orientation';
254 |
255 | (1)
256 | android:
257 | this.vlcplayer.seek(100); // 单位是 ms
258 | ios:
259 | this.vlcplayer.seek(0.1); // 0 --- 1 视频位置进度
260 | (2)
261 | (this.vlcPlayer = ref)}
263 | style={[styles.video]}
264 | /**
265 | * 增加视频宽高比,视频将按照这个比率拉伸
266 | * 不设置按照默认比例
267 | */
268 | videoAspectRatio="16:9"
269 | /**
270 | * 是否暂停播放
271 | */
272 | paused={this.state.paused}
273 | /**
274 | * 资源路径
275 | * 暂不支持本地资源
276 | */
277 | source={{ uri: this.props.uri}}
278 | /**
279 | * 进度
280 | * 返回 {currentTime:1000,duration:1000}
281 | * 单位是 ms
282 | * currentTime: 当前时间
283 | * duration: 总时间
284 | */
285 | onProgress={this.onProgress.bind(this)}
286 | /**
287 | * 视频播放结束
288 | */
289 | onEnd={this.onEnded.bind(this)}
290 | /**
291 | * 正在缓存中
292 | */
293 | onBuffering={this.onBuffering.bind(this)}
294 | /**
295 | * 播放视频出错
296 | */
297 | onError={this._onError}
298 | /**
299 | * 视频停止
300 | */
301 | onStopped={this.onStopped.bind(this)}
302 | /**
303 | * 视频播放
304 | */
305 | onPlaying={this.onPlaying.bind(this)}
306 | /**
307 | * 视频暂停
308 | */
309 | onPaused={this.onPaused.bind(this)}
310 | /**
311 | * 视频被打开
312 | /
313 | onOpen={this._onOpen}
314 | /**
315 | * vlc视频容器初始化完毕
316 | * 在这里进行设置播放的进度,是否开始播放
317 | */
318 | onLoadStart={()=>{
319 | if(Platform.OS === 'ios'){
320 | this.vlcPlayer.seek(0); //设置播放进度
321 | }else{
322 | this.vlcPlayer.seek(0); //设置播放的时间
323 | }
324 | this.setState({
325 | paused: true,
326 | },()=>{
327 | this.setState({
328 | paused: false,
329 | });
330 | })
331 | }}
332 | />
333 |
334 | ````
335 |
336 | ## 可用的源
337 |
338 | 香港财经,rtmp://202.69.69.180:443/webcast/bshdlive-pc
339 |
340 | 湖南卫视,rtmp://58.200.131.2:1935/livetv/hunantv
341 |
342 | rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov
343 |
344 |
345 | ## 版本简单说明
346 |
347 | ````
348 | 1.1.1-beta7:
349 | (1) 增加 autoAspectRatio bool (only on Android)
350 | 占满Android满屏
351 | ````
352 |
353 |
354 | ## Simple Example
355 |
356 | ````
357 | (1)
358 | 1. npm install react-native-orientation --save
359 |
360 | react-native link react-native-orientation
361 |
362 | 2. npm install react-native-slider --save
363 |
364 | 3. npm install react-native-vector-icons --save
365 |
366 | react-native link react-native-vector-icons
367 |
368 | (2)
369 |
370 | import { VLCPlayer, VlcSimplePlayer } from 'react-native-yz-vlcplayer';
371 | import Orientation from 'react-native-orientation';
372 |
373 |
374 |
375 |
376 |
377 | this.vlCPlayerView = ref}
379 | url={"rtmp://live.hkstv.hk.lxdns.com/live/hks"}
380 | Orientation={Orientation}
381 | />
382 |
383 | 注意:
384 | 《1》插件默认用了如下所示的宽高比(1.1.1-beta1及以下)
385 | fullVideoAspectRatio: deviceHeight + ':' + deviceWidth,
386 | videoAspectRatio: deviceWidth + ':' + 211.5,
387 | (1)在竖屏情况下画面比例会出现问题,请自行设置宽高比或去除内置宽高比
388 | (2)非全屏下修改了默认高度的话,请自行设置宽高比或去除内置宽高比
389 | 去除内置宽高比:
390 | fullVideoAspectRatio={""}
391 | videoAspectRatio={""}
392 | 《2》默认不会自动播放,需要自动播放请添加如下参数
393 | autoplay={true}
394 |
395 | 《3》 你可以自定义文字用以下参数:
396 | endingViewText: {
397 | endingText: '视频播放结束',
398 | reloadBtnText: '重新播放',
399 | nextBtnText: '下一个'
400 | },
401 | errorViewText: {
402 | errorText: '视频播放出错',
403 | reloadBtnText: '重新播放',
404 | },
405 | vipEndViewText: {
406 | vipEndText: '试看结束',
407 | boughtBtnText: '请购买后观看立即购买',
408 | },
409 |
410 | 下面是可用的一些参数:
411 |
412 | static propTypes = {
413 |
414 | /**
415 | * vlc 播放类型相关
416 | */
417 | //广告初始化类型
418 | initAdType: PropTypes.oneOf([1,2]),
419 | //广告初始化参数
420 | initAdOptions: PropTypes.array,
421 |
422 | //视频初始化类型
423 | initType: PropTypes.oneOf([1,2]),
424 | //视频初始化参数
425 | initOptions: PropTypes.array,
426 |
427 | /**
428 | * 直播相关
429 | */
430 | //是否直播
431 | isLive: PropTypes.bool,
432 | //是否自动reload live
433 | autoReloadLive: PropTypes.bool,
434 |
435 | /**
436 | * 广告相关
437 | */
438 | //是否显示广告
439 | showAd: PropTypes.bool,
440 | //广告url
441 | adUrl: PropTypes.oneOfType([PropTypes.string,PropTypes.number]).isRequired,
442 | //重新加载包括广告
443 | reloadWithAd: PropTypes.bool,
444 | //广告头播放结束
445 | onAdEnd: PropTypes.func,
446 | //广告是否在播放
447 | onIsAdPlaying: PropTypes.func,
448 |
449 |
450 | /**
451 | * 屏幕相关
452 | */
453 | // 以全屏初始化
454 | initWithFull: PropTypes.bool,
455 | //开启全屏回调函数
456 | onStartFullScreen: PropTypes.func,
457 | //关闭全屏回调函数
458 | onCloseFullScreen: PropTypes.func,
459 |
460 | /**
461 | * 视频相关
462 | */
463 |
464 | //视频路径:
465 | //string: 本地或者网络资源路径
466 | //number: require('./resource/1.mp4')
467 | url: PropTypes.oneOfType([PropTypes.string,PropTypes.number]).isRequired,
468 | //视频播放结束
469 | onEnd: PropTypes.func,
470 | //是否在播放
471 | onIsPlaying: PropTypes.func,
472 | //已经观看时间
473 | lookTime: PropTypes.number,
474 | //总时间
475 | totalTime: PropTypes.number,
476 | //是否有下一视频源
477 | hadNext: PropTypes.bool,
478 | //自动播放下一个视频
479 | autoPlayNext: PropTypes.bool,
480 | //自动重复播放
481 | autoRePlay: PropTypes.bool,
482 |
483 |
484 | /**
485 | * 样式相关
486 | */
487 | //视频样式
488 | style: PropTypes.object,
489 | //全屏视频样式
490 | fullStyle: PropTypes.object,
491 | //是否需要考虑statusBar only for ios
492 | considerStatusBar: PropTypes.bool,
493 | //是否显示顶部
494 | showTop: PropTypes.bool,
495 | //标题
496 | title: PropTypes.string,
497 | //是否显示标题
498 | showTitle: PropTypes.bool,
499 | //是否显示返回按钮
500 | showBack: PropTypes.bool,
501 | //返回按钮点击事件
502 | onLeftPress: PropTypes.func,
503 |
504 | /**
505 | * vip相关
506 | */
507 | //是否使用vip
508 | useVip: PropTypes.bool,
509 | //非vip观看长度
510 | vipPlayLength: PropTypes.number,
511 |
512 | };
513 |
514 | ````
515 |
516 | ````
517 | /**
518 | * Sample React Native App
519 | * https://github.com/facebook/react-native
520 | *
521 | * @format
522 | * @flow
523 | */
524 |
525 | import React, {Component} from 'react';
526 | import {StyleSheet, View} from 'react-native';
527 | import {VlcSimplePlayer, VLCPlayer} from 'react-native-yz-vlcplayer';
528 |
529 |
530 | export default class App extends Component {
531 | render() {
532 | return (
533 |
534 |
550 |
562 |
563 | );
564 | }
565 | }
566 |
567 | const styles = StyleSheet.create({
568 | container: {
569 | flex: 1,
570 | justifyContent: 'center',
571 | alignItems: 'center',
572 | backgroundColor: '#F5FCFF',
573 | },
574 | });
575 |
576 | ````
577 |
578 |
579 | **MIT Licensed**
580 |
--------------------------------------------------------------------------------
/ios/RCTVLCPlayer/RCTVLCPlayer.m:
--------------------------------------------------------------------------------
1 | #import "React/RCTConvert.h"
2 | #import "RCTVLCPlayer.h"
3 | #import "React/RCTBridgeModule.h"
4 | #import "React/RCTEventDispatcher.h"
5 | #import "React/UIView+React.h"
6 | #import
7 | #import
8 | static NSString *const statusKeyPath = @"status";
9 | static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp";
10 | static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty";
11 | static NSString *const readyForDisplayKeyPath = @"readyForDisplay";
12 | static NSString *const playbackRate = @"rate";
13 |
14 | @interface RCTVLCPlayer ()
15 | @end
16 |
17 | @implementation RCTVLCPlayer
18 | {
19 |
20 | /* Required to publish events */
21 | RCTEventDispatcher *_eventDispatcher;
22 | VLCMediaPlayer *_player;
23 |
24 | NSDictionary * _source;
25 | BOOL _paused;
26 | BOOL _started;
27 |
28 |
29 | }
30 |
31 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
32 | {
33 | if ((self = [super init])) {
34 | _eventDispatcher = eventDispatcher;
35 |
36 | [[NSNotificationCenter defaultCenter] addObserver:self
37 | selector:@selector(applicationWillResignActive:)
38 | name:UIApplicationWillResignActiveNotification
39 | object:nil];
40 |
41 | [[NSNotificationCenter defaultCenter] addObserver:self
42 | selector:@selector(applicationWillEnterForeground:)
43 | name:UIApplicationWillEnterForegroundNotification
44 | object:nil];
45 |
46 | }
47 |
48 | return self;
49 | }
50 |
51 |
52 | - (void)applicationWillResignActive:(NSNotification *)notification
53 | {
54 | if (!_paused) {
55 | [self setPaused:_paused];
56 | }
57 | }
58 |
59 | - (void)applicationWillEnterForeground:(NSNotification *)notification
60 | {
61 | [self applyModifiers];
62 | }
63 |
64 | - (void)applyModifiers
65 | {
66 | if(!_paused)
67 | [self play];
68 | }
69 |
70 | - (void)setPaused:(BOOL)paused
71 | {
72 | if(_player){
73 | if(!paused){
74 | [self play];
75 | }else {
76 | [_player pause];
77 | _paused = YES;
78 | _started = NO;
79 | }
80 | }
81 | }
82 |
83 | -(void)play
84 | {
85 | if(_player){
86 | [_player play];
87 | _paused = NO;
88 | _started = YES;
89 | }
90 | }
91 |
92 | -(void)setResume:(BOOL)autoplay
93 | {
94 | @try{
95 | char * videoRatio = nil;
96 | if(_player){
97 | videoRatio = _player.videoAspectRatio;
98 | [_player stop];
99 | _player = nil;
100 | }
101 | NSMutableDictionary* mediaOptions = [_source objectForKey:@"mediaOptions"];
102 | NSArray* options = [_source objectForKey:@"initOptions"];
103 | NSString* uri = [_source objectForKey:@"uri"];
104 | NSInteger initType = [RCTConvert NSInteger:[_source objectForKey:@"initType"]];
105 | BOOL autoplay = [RCTConvert BOOL:[_source objectForKey:@"autoplay"]];
106 | BOOL isNetWork = [RCTConvert BOOL:[_source objectForKey:@"isNetwork"]];
107 | NSURL* _uri = [NSURL URLWithString:uri];
108 | if(uri && uri.length > 0){
109 | //init player && play
110 | if(initType == 2){
111 | _player = [[VLCMediaPlayer alloc] initWithOptions:options];
112 | }else{
113 | _player = [[VLCMediaPlayer alloc] init];
114 | }
115 | [_player setDrawable:self];
116 | _player.delegate = self;
117 | _player.scaleFactor = 0;
118 | //设置缓存多少毫秒
119 | // [mediaDictonary setObject:@"1500" forKey:@"network-caching"];
120 | VLCMedia *media = nil;
121 | if(isNetWork){
122 | media = [VLCMedia mediaWithURL:_uri];
123 | }else{
124 | media = [VLCMedia mediaWithPath: uri];
125 | }
126 | media.delegate = self;
127 | if(mediaOptions){
128 | [media addOptions:mediaOptions];
129 | }
130 | /*if(videoRatio){
131 | _player.videoAspectRatio = videoRatio;
132 | }*/
133 | [media parseWithOptions:VLCMediaParseLocal|VLCMediaFetchLocal|VLCMediaParseNetwork|VLCMediaFetchNetwork];
134 | _player.media = media;
135 | if(autoplay)
136 | [self play];
137 | if(self.onVideoLoadStart){
138 | self.onVideoLoadStart(@{
139 | @"target": self.reactTag
140 | });
141 | }
142 | }
143 | }
144 | @catch(NSException *exception){
145 | NSLog(@"%@", exception);
146 | }
147 | }
148 |
149 | -(void)setSource:(NSDictionary *)source
150 | {
151 | @try{
152 | if(_player){
153 | [_player stop];
154 | _player = nil;
155 | }
156 | _source = source;
157 | NSMutableDictionary* mediaOptions = [source objectForKey:@"mediaOptions"];
158 | NSArray* options = [source objectForKey:@"initOptions"];
159 | NSString* uri = [source objectForKey:@"uri"];
160 | NSInteger initType = [RCTConvert NSInteger:[source objectForKey:@"initType"]];
161 | BOOL autoplay = [RCTConvert BOOL:[source objectForKey:@"autoplay"]];
162 | BOOL isNetWork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]];
163 | NSURL* _uri = [NSURL URLWithString:uri];
164 | if(uri && uri.length > 0){
165 | //init player && play
166 | if(initType == 2){
167 | _player = [[VLCMediaPlayer alloc] initWithOptions:options];
168 | }else{
169 | _player = [[VLCMediaPlayer alloc] init];
170 | }
171 | [_player setDrawable:self];
172 | _player.delegate = self;
173 | _player.scaleFactor = 0;
174 | //设置缓存多少毫秒
175 | // [mediaDictonary setObject:@"1500" forKey:@"network-caching"];
176 | VLCMedia *media = nil;
177 | if(isNetWork){
178 | media = [VLCMedia mediaWithURL:_uri];
179 | }else{
180 | media = [VLCMedia mediaWithPath: uri];
181 | }
182 | if(media){
183 | media.delegate = self;
184 | if(mediaOptions){
185 | [media addOptions:mediaOptions];
186 | }
187 | [media parseWithOptions:VLCMediaParseLocal|VLCMediaFetchLocal|VLCMediaParseNetwork|VLCMediaFetchNetwork];
188 | _player.media = media;
189 | }
190 | if(autoplay)
191 | [self play];
192 | if(self.onVideoLoadStart){
193 | self.onVideoLoadStart(@{
194 | @"target": self.reactTag
195 | });
196 | }
197 | }
198 | }
199 | @catch(NSException *exception){
200 | NSLog(@"%@", exception);
201 | }
202 | }
203 |
204 | - (void)mediaPlayerSnapshot:(NSNotification *)aNotification{
205 | NSLog(@"userInfo %@",[aNotification userInfo]);
206 | self.onSnapshot(@{
207 | @"target": self.reactTag,
208 | @"success": [NSNumber numberWithInt:1],
209 | });
210 | }
211 |
212 |
213 | - (void)mediaMetaDataDidChange:(VLCMedia *)aMedia{
214 | NSLog(@"mediaMetaDataDidChange");
215 | NSInteger readBytes = aMedia.numberOfReadBytesOnInput;
216 | NSLog(@"readBytes %zd", readBytes);
217 | BOOL isPlaying = _player.isPlaying;
218 | BOOL hasVideoOut = _player.hasVideoOut;
219 | self.onVideoStateChange(@{
220 | @"target": self.reactTag,
221 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
222 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
223 | @"type": @"mediaMetaDataDidChange",
224 | });
225 | }
226 |
227 | - (void)mediaDidFinishParsing:(VLCMedia *)aMedia
228 | {
229 | NSLog(@"mediaDidFinishParsing");
230 | BOOL isPlaying = _player.isPlaying;
231 | BOOL hasVideoOut = _player.hasVideoOut;
232 | self.onVideoStateChange(@{
233 | @"target": self.reactTag,
234 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
235 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
236 | @"type": @"mediaDidFinishParsing",
237 | });
238 | //NSLog(@"readBytes %zd", readBytes);
239 | }
240 |
241 | - (void)mediaPlayerTimeChanged:(NSNotification *)aNotification
242 | {
243 | [self updateVideoProgress];
244 | }
245 |
246 | - (void)mediaPlayerStateChanged:(NSNotification *)aNotification
247 | {
248 | @try{
249 | if(_player){
250 |
251 | BOOL isPlaying = _player.isPlaying;
252 | BOOL hasVideoOut = _player.hasVideoOut;
253 | /*NSInteger numberOfReadBytesOnInput = _player.media.numberOfReadBytesOnInput;
254 | NSInteger numberOfPlayedAudioBuffers = _player.media.numberOfPlayedAudioBuffers;
255 | NSInteger numberOfSentBytes = _player.media.numberOfSentBytes;
256 | NSInteger numberOfReadBytesOnDemux = _player.media.numberOfReadBytesOnDemux;
257 | NSInteger numberOfSentPackets = _player.media.numberOfSentPackets;
258 | NSInteger numberOfCorruptedDataPackets = _player.media.numberOfCorruptedDataPackets;
259 | NSInteger numberOfDisplayedPictures = _player.media.numberOfDisplayedPictures;
260 | NSInteger numberOfDecodedVideoBlocks = _player.media.numberOfDecodedVideoBlocks;
261 | */
262 | /*self.onIsPlaying(@{
263 | @"target": self.reactTag,
264 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
265 | @"numberOfReadBytesOnInput":[NSNumber numberWithInteger:numberOfReadBytesOnInput],
266 | @"numberOfPlayedAudioBuffers":[NSNumber numberWithInteger:numberOfPlayedAudioBuffers],
267 | @"numberOfSentBytes":[NSNumber numberWithInteger:numberOfSentBytes],
268 | @"numberOfReadBytesOnDemux":[NSNumber numberWithInteger:numberOfReadBytesOnDemux],
269 | @"numberOfSentPackets":[NSNumber numberWithInteger:numberOfSentPackets],
270 | @"numberOfCorruptedDataPackets":[NSNumber numberWithInteger:numberOfCorruptedDataPackets],
271 | @"numberOfDisplayedPictures":[NSNumber numberWithInteger:numberOfDisplayedPictures],
272 | @"numberOfDecodedVideoBlocks":[NSNumber numberWithInteger:numberOfDecodedVideoBlocks],
273 | });
274 | */
275 | VLCMediaPlayerState state = _player.state;
276 | CGSize videoSize = _player.videoSize;
277 | int height = videoSize.height;
278 | int width = videoSize.width;
279 | BOOL willPlay = _player.willPlay;
280 | switch (state) {
281 | case VLCMediaPlayerStateOpening:
282 | self.onVideoStateChange(@{
283 | @"target": self.reactTag,
284 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
285 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
286 | @"type": @"Opening",
287 | @"videoWidth":[NSNumber numberWithInt:width],
288 | @"videoHeight":[NSNumber numberWithInt:height],
289 | @"willPlay":[NSNumber numberWithBool:willPlay],
290 | });
291 | break;
292 | case VLCMediaPlayerStatePaused:
293 | _paused = YES;
294 | self.onVideoStateChange(@{
295 | @"target": self.reactTag,
296 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
297 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
298 | @"type": @"Paused",
299 | @"videoWidth":[NSNumber numberWithInt:width],
300 | @"videoHeight":[NSNumber numberWithInt:height],
301 | @"willPlay":[NSNumber numberWithBool:willPlay],
302 | });
303 | break;
304 | case VLCMediaPlayerStateStopped:
305 | self.onVideoStateChange(@{
306 | @"target": self.reactTag,
307 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
308 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
309 | @"type": @"Stoped",
310 | @"videoWidth":[NSNumber numberWithInt:width],
311 | @"videoHeight":[NSNumber numberWithInt:height],
312 | @"willPlay":[NSNumber numberWithBool:willPlay],
313 | });
314 | break;
315 | case VLCMediaPlayerStateBuffering:
316 | self.onVideoStateChange(@{
317 | @"target": self.reactTag,
318 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
319 | @"duration":[NSNumber numberWithInt:[_player.media.length intValue]],
320 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
321 | @"type": @"Buffering",
322 | @"videoWidth":[NSNumber numberWithInt:width],
323 | @"videoHeight":[NSNumber numberWithInt:height],
324 | @"willPlay":[NSNumber numberWithBool:willPlay],
325 | });
326 | break;
327 | case VLCMediaPlayerStatePlaying:
328 | _paused = NO;
329 | self.onVideoStateChange(@{
330 | @"target": self.reactTag,
331 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
332 | @"duration":[NSNumber numberWithInt:[_player.media.length intValue]],
333 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
334 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
335 | @"type": @"Playing",
336 | @"videoWidth":[NSNumber numberWithInt:width],
337 | @"videoHeight":[NSNumber numberWithInt:height],
338 | @"willPlay":[NSNumber numberWithBool:willPlay],
339 | });
340 | break;
341 | case VLCMediaPlayerStateESAdded:
342 | self.onVideoStateChange(@{
343 | @"target": self.reactTag,
344 | @"duration":[NSNumber numberWithInt:[_player.media.length intValue]],
345 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
346 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
347 | @"type": @"ESAdded",
348 | @"videoWidth":[NSNumber numberWithInt:width],
349 | @"videoHeight":[NSNumber numberWithInt:height],
350 | @"willPlay":[NSNumber numberWithBool:willPlay],
351 | });
352 | break;
353 | case VLCMediaPlayerStateEnded:
354 | NSLog(@"VLCMediaPlayerStateEnded %i",1);
355 | int currentTime = [[_player time] intValue];
356 | int remainingTime = [[_player remainingTime] intValue];
357 | int duration = [_player.media.length intValue];
358 | self.onVideoStateChange(@{
359 | @"target": self.reactTag,
360 | @"type": @"Ended",
361 | @"currentTime": [NSNumber numberWithInt:currentTime],
362 | @"remainingTime": [NSNumber numberWithInt:remainingTime],
363 | @"duration":[NSNumber numberWithInt:duration],
364 | @"position":[NSNumber numberWithFloat:_player.position],
365 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
366 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
367 | @"videoWidth":[NSNumber numberWithInt:width],
368 | @"videoHeight":[NSNumber numberWithInt:height],
369 | @"willPlay":[NSNumber numberWithBool:willPlay],
370 | });
371 | break;
372 | case VLCMediaPlayerStateError:
373 | self.onVideoStateChange(@{
374 | @"target": self.reactTag,
375 | @"duration":[NSNumber numberWithInt:[_player.media.length intValue]],
376 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
377 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
378 | @"type": @"Error",
379 | @"videoWidth":[NSNumber numberWithInt:width],
380 | @"videoHeight":[NSNumber numberWithInt:height],
381 | @"willPlay":[NSNumber numberWithBool:willPlay],
382 | });
383 | [self _release];
384 | break;
385 | default:
386 | self.onVideoStateChange(@{
387 | @"target": self.reactTag,
388 | @"duration":[NSNumber numberWithInt:[_player.media.length intValue]],
389 | @"isPlaying": [NSNumber numberWithBool: isPlaying],
390 | @"hasVideoOut": [NSNumber numberWithBool: hasVideoOut],
391 | @"type": [NSString stringWithCString:state encoding:(NSASCIIStringEncoding)],
392 | @"videoWidth":[NSNumber numberWithInt:width],
393 | @"videoHeight":[NSNumber numberWithInt:height],
394 | @"willPlay":[NSNumber numberWithBool:willPlay],
395 | });
396 | break;
397 | }
398 | }
399 | }@catch(NSException *exception){
400 | NSLog(@"%@", exception);
401 | }
402 | }
403 |
404 | -(void)updateVideoProgress
405 | { @try{
406 | if(_player){
407 | int currentTime = [[_player time] intValue];
408 | int remainingTime = [[_player remainingTime] intValue];
409 | int duration = [_player.media.length intValue];
410 |
411 | if( currentTime >= 0 && currentTime < duration) {
412 | self.onVideoProgress(@{
413 | @"target": self.reactTag,
414 | @"currentTime": [NSNumber numberWithInt:currentTime],
415 | @"remainingTime": [NSNumber numberWithInt:remainingTime],
416 | @"duration":[NSNumber numberWithInt:duration],
417 | @"position":[NSNumber numberWithFloat:_player.position],
418 | @"isPlaying": [NSNumber numberWithBool: _player.isPlaying],
419 | });
420 | }
421 | }
422 | }
423 | @catch(NSException *exception){
424 | NSLog(@"%@", exception);
425 | }
426 | }
427 |
428 | - (void)jumpBackward:(int)interval
429 | {
430 | if(interval>=0 && interval <= [_player.media.length intValue])
431 | [_player jumpBackward:interval];
432 | }
433 |
434 | - (void)jumpForward:(int)interval
435 | {
436 | if(interval>=0 && interval <= [_player.media.length intValue])
437 | [_player jumpForward:interval];
438 | }
439 |
440 | /**
441 | * audio -----> start
442 | */
443 | - (void)setMuted:(BOOL)muted
444 | {
445 | if(_player){
446 | VLCAudio *audio = _player.audio;
447 | [audio setMuted: muted];
448 | }
449 | }
450 |
451 | -(void)setVolume:(int)interval
452 | {
453 | if(_player){
454 | VLCAudio *audio = _player.audio;
455 | if(interval >= 0){
456 | audio.volume = interval;
457 | }
458 | }
459 | }
460 |
461 | -(void)setVolumeDown:(int)volume
462 | {
463 | if(_player){
464 |
465 | VLCAudio *audio = _player.audio;
466 | [audio volumeDown];
467 | }
468 | }
469 |
470 |
471 |
472 | -(void)setVolumeUp:(int)volume
473 | {
474 | if(_player){
475 | VLCAudio *audio = _player.audio;
476 | [audio volumeUp];
477 | }
478 | }
479 |
480 | //audio -----> end
481 |
482 |
483 | -(void)setSeek:(float)pos
484 | {
485 | if(_player != nil && [_player isSeekable]){
486 | if(pos>=0 && pos <= 1){
487 | [_player setPosition:pos];
488 | }
489 | }
490 | }
491 |
492 | -(void)setSeekTime:(int)time{
493 | if(_player){
494 | VLCTime *time = [VLCTime timeWithInt:(time)];
495 | [_player setTime:time];
496 | }
497 | }
498 |
499 | -(void)setSnapshotPath:(NSString*)path
500 | {
501 | if(_player)
502 | [_player saveVideoSnapshotAt:path withWidth:0 andHeight:0];
503 | }
504 |
505 | -(void)setRate:(float)rate
506 | {
507 | [_player setRate:rate];
508 | }
509 |
510 | -(void)setClear:(float)clear
511 | {
512 | [self _release];
513 | }
514 |
515 |
516 | -(void)setVideoAspectRatio:(NSString *)ratio{
517 | if(ratio != nil && ratio.length > 0){
518 | char *char_content = [ratio cStringUsingEncoding:NSASCIIStringEncoding];
519 | [_player setVideoAspectRatio:char_content];
520 | }
521 | }
522 |
523 | - (void)_release
524 | {
525 | if(_player){
526 | [_player stop];
527 | _player = nil;
528 | _eventDispatcher = nil;
529 | }
530 | [[NSNotificationCenter defaultCenter] removeObserver:self];
531 | }
532 |
533 | - (void)dealloc{
534 | [self _release];
535 | }
536 | #pragma mark - Lifecycle
537 |
538 | //- (void)willMoveToSuperview:(UIView *)newSuperview
539 | //- (void)didMoveToSuperview
540 |
541 | //- (void)willRemoveSubview:(UIView *)subview
542 |
543 |
544 | - (void)removeFromSuperview
545 | {
546 | NSLog(@"removeFromSuperview");
547 | [self _release];
548 | [super removeFromSuperview];
549 | }
550 |
551 | @end
552 |
--------------------------------------------------------------------------------
/android/src/main/java/com/yuanzhou/vlc/vlcplayer/ReactVlcPlayerView.java:
--------------------------------------------------------------------------------
1 | package com.yuanzhou.vlc.vlcplayer;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.graphics.Color;
6 | import android.graphics.PixelFormat;
7 | import android.graphics.SurfaceTexture;
8 | import android.media.AudioManager;
9 | import android.net.Uri;
10 | import android.os.Handler;
11 | import android.util.DisplayMetrics;
12 | import android.util.Log;
13 | import android.view.Surface;
14 | import android.view.SurfaceHolder;
15 | import android.view.SurfaceView;
16 | import android.view.TextureView;
17 | import android.view.View;
18 |
19 | import com.facebook.react.bridge.Arguments;
20 | import com.facebook.react.bridge.LifecycleEventListener;
21 | import com.facebook.react.bridge.ReadableArray;
22 | import com.facebook.react.bridge.ReadableMap;
23 | import com.facebook.react.bridge.WritableMap;
24 | import com.facebook.react.uimanager.ThemedReactContext;
25 |
26 | import org.videolan.libvlc.IVLCVout;
27 | import org.videolan.libvlc.LibVLC;
28 | import org.videolan.libvlc.Media;
29 | import org.videolan.libvlc.MediaPlayer;
30 | import org.videolan.vlc.RecordEvent;
31 | import org.videolan.vlc.util.LogUtils;
32 | import org.videolan.vlc.util.VLCInstance;
33 | import org.videolan.vlc.util.VLCOptions;
34 | import java.util.ArrayList;
35 |
36 | @SuppressLint("ViewConstructor")
37 | class ReactVlcPlayerView extends TextureView implements
38 | LifecycleEventListener,
39 | TextureView.SurfaceTextureListener,
40 | AudioManager.OnAudioFocusChangeListener{
41 |
42 | private static final String TAG = "ReactVlcPlayerView";
43 | private final String tag = "ReactVlcPlayerView";
44 |
45 | private final VideoEventEmitter eventEmitter;
46 | private LibVLC libvlc;
47 | private MediaPlayer mMediaPlayer = null;
48 | private TextureView surfaceView;
49 | private Surface surfaceVideo;//视频画布
50 | private boolean isSurfaceViewDestory;
51 | private int surfaceW, surfaceH;
52 | //资源路径
53 | private String src;
54 | //是否网络资源
55 | private boolean netStrTag;
56 | private ReadableMap srcMap;
57 |
58 |
59 | //private Handler mainHandler;
60 | /* private static final int SURFACE_BEST_FIT = 0;
61 | private static final int SURFACE_FIT_HORIZONTAL = 1;
62 | private static final int SURFACE_FIT_VERTICAL = 2;
63 | private static final int SURFACE_FILL = 3;
64 | private static final int SURFACE_16_9 = 4;
65 | private static final int SURFACE_4_3 = 5;
66 | private static final int SURFACE_ORIGINAL = 6;
67 | private int mCurrentSize = SURFACE_BEST_FIT;*/
68 | // Props from React
69 | /* private Uri srcUri;
70 | private String extension;
71 | private boolean repeat;
72 | private boolean disableFocus;
73 | private boolean playInBackground = false;*/
74 | // \ End props
75 |
76 | private int mVideoHeight = 0;
77 | private int mVideoWidth = 0;
78 | private int mVideoVisibleHeight = 0;
79 | private int mVideoVisibleWidth = 0;
80 | private int mSarNum = 0;
81 | private int mSarDen = 0;
82 | private int screenWidth = 0;
83 | private int screenHeight = 0;
84 | private boolean isPaused = true;
85 | private boolean isHostPaused = false;
86 | private int preVolume = 200;
87 | private boolean autoAspectRatio = false;
88 |
89 | // React
90 | private final ThemedReactContext themedReactContext;
91 | private final AudioManager audioManager;
92 |
93 |
94 |
95 |
96 | public ReactVlcPlayerView(ThemedReactContext context) {
97 | super(context);
98 | this.eventEmitter = new VideoEventEmitter(context);
99 | this.themedReactContext = context;
100 | audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
101 | //themedReactContext.addLifecycleEventListener(this);
102 | DisplayMetrics dm = getResources().getDisplayMetrics();
103 | screenHeight = dm.heightPixels;
104 | screenWidth = dm.widthPixels;
105 | this.setSurfaceTextureListener(this);
106 | //surfaceView = this;
107 | //surfaceView.setZOrderOnTop(false);
108 | //surfaceView.setZOrderMediaOverlay(false);
109 | // this.setZOrderOnTop(true);
110 | // this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
111 | //this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
112 | //this.setZOrderMediaOverlay(true);
113 | //
114 | //不过中间那句是OpenGl的,视情况使用,无用可注释掉了,也能实现了透明,但是GLSurfaceView就必须使用
115 |
116 | // this.setZOrderMediaOverlay(false);
117 | this.addOnLayoutChangeListener(onLayoutChangeListener);
118 | }
119 |
120 |
121 | @Override
122 | public void setId(int id) {
123 | super.setId(id);
124 | eventEmitter.setViewId(id);
125 | }
126 |
127 | @Override
128 | protected void onAttachedToWindow() {
129 | super.onAttachedToWindow();
130 | //createPlayer();
131 | }
132 |
133 | @Override
134 | protected void onDetachedFromWindow() {
135 | super.onDetachedFromWindow();
136 | stopPlayback();
137 | }
138 |
139 | // LifecycleEventListener implementation
140 |
141 | @Override
142 | public void onHostResume() {
143 | if(mMediaPlayer != null && isSurfaceViewDestory && isHostPaused){
144 | IVLCVout vlcOut = mMediaPlayer.getVLCVout();
145 | if(!vlcOut.areViewsAttached()){
146 | // vlcOut.setVideoSurface(this.getHolder().getSurface(), this.getHolder());
147 | vlcOut.attachViews(onNewVideoLayoutListener);
148 | isSurfaceViewDestory = false;
149 | isPaused = false;
150 | // this.getHolder().setKeepScreenOn(true);
151 | mMediaPlayer.play();
152 | }
153 | }
154 | }
155 |
156 |
157 | @Override
158 | public void onHostPause() {
159 | if(!isPaused && mMediaPlayer != null){
160 | isPaused = true;
161 | isHostPaused = true;
162 | mMediaPlayer.pause();
163 | // this.getHolder().setKeepScreenOn(false);
164 | WritableMap map = Arguments.createMap();
165 | map.putString("type","Paused");
166 | eventEmitter.onVideoStateChange(map);
167 | }
168 | Log.i("onHostPause","---------onHostPause------------>");
169 | }
170 |
171 |
172 |
173 | @Override
174 | public void onHostDestroy() {
175 | stopPlayback();
176 | }
177 |
178 |
179 | // AudioManager.OnAudioFocusChangeListener implementation
180 | @Override
181 | public void onAudioFocusChange(int focusChange) {
182 | }
183 |
184 |
185 | /*************
186 | * Events Listener
187 | *************/
188 |
189 | private View.OnLayoutChangeListener onLayoutChangeListener = new View.OnLayoutChangeListener(){
190 |
191 | @Override
192 | public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
193 | if(view.getWidth() > 0 && view.getHeight() > 0 ){
194 | mVideoWidth = view.getWidth(); // 获取宽度
195 | mVideoHeight = view.getHeight(); // 获取高度
196 | if(mMediaPlayer != null) {
197 | IVLCVout vlcOut = mMediaPlayer.getVLCVout();
198 | vlcOut.setWindowSize(mVideoWidth, mVideoHeight);
199 | if(autoAspectRatio){
200 | mMediaPlayer.setAspectRatio(mVideoWidth + ":" + mVideoHeight);
201 | }
202 | }
203 | }
204 | }
205 | };
206 |
207 | /**
208 | * 播放过程中的时间事件监听
209 | */
210 | private MediaPlayer.EventListener mPlayerListener = new MediaPlayer.EventListener(){
211 | long currentTime = 0;
212 | long totalLength = 0;
213 | @Override
214 | public void onEvent(MediaPlayer.Event event) {
215 | boolean isPlaying = mMediaPlayer.isPlaying();
216 | currentTime = mMediaPlayer.getTime();
217 | float position = mMediaPlayer.getPosition();
218 | totalLength = mMediaPlayer.getLength();
219 | WritableMap map = Arguments.createMap();
220 | map.putBoolean("isPlaying",isPlaying);
221 | map.putDouble("position",position);
222 | map.putDouble("currentTime",currentTime);
223 | map.putDouble("duration",totalLength);
224 | switch (event.type) {
225 | case MediaPlayer.Event.EndReached:
226 | map.putString("type","Ended");
227 | eventEmitter.onVideoStateChange(map);
228 | break;
229 | case MediaPlayer.Event.Playing:
230 | map.putString("type","Playing");
231 | eventEmitter.onVideoStateChange(map);
232 | break;
233 | case MediaPlayer.Event.Opening:
234 | map.putString("type","Opening");
235 | eventEmitter.onVideoStateChange(map);
236 | break;
237 | case MediaPlayer.Event.Paused:
238 | map.putString("type","Paused");
239 | eventEmitter.onVideoStateChange(map);
240 | break;
241 | case MediaPlayer.Event.Buffering:
242 | map.putDouble("bufferRate",event.getBuffering());
243 | map.putString("type","Buffering");
244 | eventEmitter.onVideoStateChange(map);
245 | break;
246 | case MediaPlayer.Event.Stopped:
247 | map.putString("type","Stopped");
248 | eventEmitter.onVideoStateChange(map);
249 | break;
250 | case MediaPlayer.Event.EncounteredError:
251 | map.putString("type","Error");
252 | eventEmitter.onVideoStateChange(map);
253 | break;
254 | case MediaPlayer.Event.TimeChanged:
255 | map.putString("type","TimeChanged");
256 | eventEmitter.onVideoStateChange(map);
257 | /*currentTime = mMediaPlayer.getTime();
258 | totalLength = mMediaPlayer.getLength();
259 | eventEmitter.progressChanged(currentTime, totalLength);*/
260 | break;
261 | default:
262 | map.putString("type",event.type+"");
263 | eventEmitter.onVideoStateChange(map);
264 | break;
265 | }
266 | eventEmitter.isPlaying(mMediaPlayer.isPlaying());
267 | }
268 | };
269 |
270 | private IVLCVout.OnNewVideoLayoutListener onNewVideoLayoutListener = new IVLCVout.OnNewVideoLayoutListener(){
271 | @Override
272 | public void onNewVideoLayout(IVLCVout vout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
273 | if (width * height == 0)
274 | return;
275 | // store video size
276 | mVideoWidth = width;
277 | mVideoHeight = height;
278 | mVideoVisibleWidth = visibleWidth;
279 | mVideoVisibleHeight = visibleHeight;
280 | mSarNum = sarNum;
281 | mSarDen = sarDen;
282 | WritableMap map = Arguments.createMap();
283 | map.putInt("mVideoWidth",mVideoWidth);
284 | map.putInt("mVideoHeight",mVideoHeight);
285 | map.putInt("mVideoVisibleWidth",mVideoVisibleWidth);
286 | map.putInt("mVideoVisibleHeight",mVideoVisibleHeight);
287 | map.putInt("mSarNum",mSarNum);
288 | map.putInt("mSarDen",mSarDen);
289 | map.putString("type","onNewVideoLayout");
290 | eventEmitter.onVideoStateChange(map);
291 | }
292 | };
293 |
294 | IVLCVout.Callback callback = new IVLCVout.Callback() {
295 | @Override
296 | public void onSurfacesCreated(IVLCVout ivlcVout) {
297 | isSurfaceViewDestory = false;
298 | }
299 |
300 | @Override
301 | public void onSurfacesDestroyed(IVLCVout ivlcVout) {
302 | isSurfaceViewDestory = true;
303 | }
304 |
305 | };
306 |
307 |
308 |
309 | /*************
310 | * MediaPlayer
311 | *************/
312 |
313 |
314 | private void stopPlayback() {
315 | onStopPlayback();
316 | releasePlayer();
317 | }
318 |
319 | private void onStopPlayback() {
320 | setKeepScreenOn(false);
321 | audioManager.abandonAudioFocus(this);
322 | }
323 |
324 | private void createPlayer(boolean autoplayResume, boolean isResume) {
325 | releasePlayer();
326 | if(this.getSurfaceTexture() == null){
327 | return;
328 | }
329 | try {
330 | ArrayList cOptions = VLCOptions.getLibOptions(getContext());
331 | String uriString = srcMap.hasKey("uri") ? srcMap.getString("uri") : null;
332 | //String extension = srcMap.hasKey("type") ? srcMap.getString("type") : null;
333 | boolean isNetwork = srcMap.hasKey("isNetwork") ? srcMap.getBoolean("isNetwork") : false;
334 | boolean autoplay = srcMap.hasKey("autoplay") ? srcMap.getBoolean("autoplay") : true;
335 | int initType = srcMap.hasKey("initType") ? srcMap.getInt("initType") : 1;
336 | ReadableArray mediaOptions = srcMap.hasKey("mediaOptions") ? srcMap.getArray("mediaOptions") : null;
337 | ReadableArray initOptions = srcMap.hasKey("initOptions") ? srcMap.getArray("initOptions") : null;
338 | Integer hwDecoderEnabled = srcMap.hasKey("hwDecoderEnabled") ? srcMap.getInt("hwDecoderEnabled") : null;
339 | Integer hwDecoderForced = srcMap.hasKey("hwDecoderForced") ? srcMap.getInt("hwDecoderForced") : null;
340 | if(initOptions != null){
341 | ArrayList options = initOptions.toArrayList();
342 | for(int i=0; i < options.size() - 1 ; i++){
343 | String option = (String)options.get(i);
344 | cOptions.add(option);
345 | }
346 | }
347 | // Create LibVLC
348 | if(initType == 1){
349 | libvlc = VLCInstance.get(getContext());
350 | }else{
351 | libvlc = new LibVLC(getContext(), cOptions);
352 | }
353 | //libvlc = new LibVLC(getContext(), options);
354 | // Create media player
355 | mMediaPlayer = new MediaPlayer(libvlc);
356 | mMediaPlayer.setEventListener(mPlayerListener);
357 | //this.getHolder().setKeepScreenOn(true);
358 | IVLCVout vlcOut = mMediaPlayer.getVLCVout();
359 | if(mVideoWidth > 0 && mVideoHeight > 0){
360 | vlcOut.setWindowSize(mVideoWidth,mVideoHeight);
361 | if(autoAspectRatio){
362 | mMediaPlayer.setAspectRatio(mVideoWidth + ":" + mVideoHeight);
363 | }
364 | //mMediaPlayer.setAspectRatio(mVideoWidth+":"+mVideoHeight);
365 | }
366 | DisplayMetrics dm = getResources().getDisplayMetrics();
367 | Media m = null;
368 | if(isNetwork){
369 | Uri uri = Uri.parse(uriString);
370 | m = new Media(libvlc, uri);
371 | }else{
372 | m = new Media(libvlc, uriString);
373 | }
374 | m.setEventListener(mMediaListener);
375 | if(hwDecoderEnabled != null && hwDecoderForced != null){
376 | boolean hmEnabled = false;
377 | boolean hmForced = false;
378 | if(hwDecoderEnabled >= 1){
379 | hmEnabled = true;
380 | }
381 | if(hwDecoderForced >= 1){
382 | hmForced = true;
383 | }
384 | m.setHWDecoderEnabled(hmEnabled, hmForced);
385 | }
386 | //添加media option
387 | if(mediaOptions != null){
388 | ArrayList options = mediaOptions.toArrayList();
389 | for(int i=0; i < options.size() - 1 ; i++){
390 | String option = (String)options.get(i);
391 | m.addOption(option);
392 | }
393 | }
394 | mMediaPlayer.setMedia(m);
395 | mMediaPlayer.setScale(0);
396 |
397 | if (!vlcOut.areViewsAttached()) {
398 | vlcOut.addCallback(callback);
399 | // vlcOut.setVideoSurface(this.getSurfaceTexture());
400 | //vlcOut.setVideoSurface(this.getHolder().getSurface(), this.getHolder());
401 | //vlcOut.attachViews(onNewVideoLayoutListener);
402 | vlcOut.setVideoSurface(this.getSurfaceTexture());
403 | vlcOut.attachViews(onNewVideoLayoutListener);
404 | // vlcOut.attachSurfaceSlave(surfaceVideo,null,onNewVideoLayoutListener);
405 | //vlcOut.setVideoView(this);
406 | //vlcOut.attachViews(onNewVideoLayoutListener);
407 | }
408 | if(isResume){
409 | if(autoplayResume){
410 | mMediaPlayer.play();
411 | }
412 | }else{
413 | if(autoplay){
414 | isPaused = false;
415 | mMediaPlayer.play();
416 | }
417 | }
418 | eventEmitter.loadStart();
419 | } catch (Exception e) {
420 | e.printStackTrace();
421 | //Toast.makeText(getContext(), "Error creating player!", Toast.LENGTH_LONG).show();
422 | }
423 | }
424 |
425 | private void releasePlayer() {
426 | if (libvlc == null)
427 | return;
428 | mMediaPlayer.stop();
429 | final IVLCVout vout = mMediaPlayer.getVLCVout();
430 | vout.removeCallback(callback);
431 | vout.detachViews();
432 | //surfaceView.removeOnLayoutChangeListener(onLayoutChangeListener);
433 | libvlc.release();
434 | libvlc = null;
435 | }
436 |
437 | /**
438 | * 视频进度调整
439 | * @param time
440 | */
441 | public void seekTo(long time) {
442 | if(mMediaPlayer != null){
443 | mMediaPlayer.setTime(time);
444 | }
445 | }
446 |
447 | public void setPosition(float position){
448 | if(mMediaPlayer != null) {
449 | if(position >= 0 && position <= 1){
450 | mMediaPlayer.setPosition(position);
451 | }
452 | }
453 | }
454 |
455 | /**
456 | * 设置资源路径
457 | * @param uri
458 | * @param isNetStr
459 | */
460 | public void setSrc(String uri, boolean isNetStr, boolean autoplay) {
461 | this.src = uri;
462 | this.netStrTag = isNetStr;
463 | createPlayer(autoplay,false);
464 | }
465 |
466 | public void setSrc(ReadableMap src){
467 | this.srcMap = src;
468 | createPlayer(true,false);
469 | }
470 |
471 |
472 | /**
473 | * 改变播放速率
474 | * @param rateModifier
475 | */
476 | public void setRateModifier(float rateModifier) {
477 | if(mMediaPlayer != null){
478 | mMediaPlayer.setRate(rateModifier);
479 | }
480 | }
481 |
482 |
483 | /**
484 | * 改变声音大小
485 | * @param volumeModifier
486 | */
487 | public void setVolumeModifier(int volumeModifier) {
488 | if(mMediaPlayer != null){
489 | mMediaPlayer.setVolume(volumeModifier);
490 | }
491 | }
492 |
493 | /**
494 | * 改变静音状态
495 | * @param muted
496 | */
497 | public void setMutedModifier(boolean muted) {
498 | if(mMediaPlayer != null){
499 | if(muted){
500 | this.preVolume = mMediaPlayer.getVolume();
501 | mMediaPlayer.setVolume(0);
502 | }else{
503 | mMediaPlayer.setVolume(this.preVolume);
504 | }
505 | }
506 | }
507 |
508 | /**
509 | * 改变播放状态
510 | * @param paused
511 | */
512 | public void setPausedModifier(boolean paused){
513 | Log.i("paused:",""+paused+":"+mMediaPlayer);
514 | if(mMediaPlayer != null){
515 | if(paused){
516 | isPaused = true;
517 | mMediaPlayer.pause();
518 | }else{
519 | isPaused = false;
520 | mMediaPlayer.play();
521 | Log.i("do play:",true + "");
522 | }
523 | }else{
524 | createPlayer(!paused,false);
525 | }
526 | }
527 |
528 |
529 | /**
530 | * 截图
531 | * @param path
532 | */
533 | public void doSnapshot(String path){
534 | if(mMediaPlayer != null){
535 | int result = new RecordEvent().takeSnapshot(mMediaPlayer,path,0,0);
536 | if(result == 0){
537 | eventEmitter.onSnapshot(1);
538 | }else{
539 | eventEmitter.onSnapshot(0);
540 | }
541 | }
542 |
543 | }
544 |
545 |
546 | /**
547 | * 重新加载视频
548 | * @param autoplay
549 | */
550 | public void doResume(boolean autoplay){
551 | createPlayer(autoplay,true);
552 | }
553 |
554 |
555 | public void setRepeatModifier(boolean repeat){
556 | }
557 |
558 |
559 | /**
560 | * 改变宽高比
561 | * @param aspectRatio
562 | */
563 | public void setAspectRatio(String aspectRatio){
564 | if(!autoAspectRatio && mMediaPlayer != null){
565 | mMediaPlayer.setAspectRatio(aspectRatio);
566 | }
567 | }
568 |
569 | public void setAutoAspectRatio(boolean auto){
570 | autoAspectRatio = auto;
571 | }
572 |
573 |
574 | public void cleanUpResources() {
575 | if(surfaceView != null){
576 | surfaceView.removeOnLayoutChangeListener(onLayoutChangeListener);
577 | }
578 | stopPlayback();
579 | }
580 |
581 | @Override
582 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
583 | mVideoWidth = width;
584 | mVideoHeight = height;
585 | surfaceVideo = new Surface(surface);
586 | createPlayer(true,false);
587 | }
588 |
589 | @Override
590 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
591 |
592 | }
593 |
594 | @Override
595 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
596 | return true;
597 | }
598 |
599 | @Override
600 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
601 | Log.i("onSurfaceTextureUpdated","onSurfaceTextureUpdated");
602 | }
603 |
604 | private final Media.EventListener mMediaListener = new Media.EventListener() {
605 | @Override
606 | public void onEvent(Media.Event event) {
607 | switch (event.type) {
608 | case Media.Event.MetaChanged:
609 | Log.i(tag, "Media.Event.MetaChanged: =" + event.getMetaId());
610 | break;
611 | case Media.Event.ParsedChanged:
612 | Log.i(tag, "Media.Event.ParsedChanged =" + event.getMetaId());
613 | break;
614 | case Media.Event.StateChanged:
615 | Log.i(tag, "StateChanged =" + event.getMetaId());
616 | break;
617 | default:
618 | Log.i(tag, "Media.Event.type=" + event.type + " eventgetParsedStatus=" + event.getParsedStatus());
619 | break;
620 |
621 | }
622 | }
623 | };
624 |
625 | /*private void changeSurfaceSize(boolean message) {
626 |
627 | if (mMediaPlayer != null) {
628 | final IVLCVout vlcVout = mMediaPlayer.getVLCVout();
629 | vlcVout.setWindowSize(screenWidth, screenHeight);
630 | }
631 |
632 | double displayWidth = screenWidth, displayHeight = screenHeight;
633 |
634 | if (screenWidth < screenHeight) {
635 | displayWidth = screenHeight;
636 | displayHeight = screenWidth;
637 | }
638 |
639 | // sanity check
640 | if (displayWidth * displayHeight <= 1 || mVideoWidth * mVideoHeight <= 1) {
641 | return;
642 | }
643 |
644 | // compute the aspect ratio
645 | double aspectRatio, visibleWidth;
646 | if (mSarDen == mSarNum) {
647 | *//* No indication about the density, assuming 1:1 *//*
648 | visibleWidth = mVideoVisibleWidth;
649 | aspectRatio = (double) mVideoVisibleWidth / (double) mVideoVisibleHeight;
650 | } else {
651 | *//* Use the specified aspect ratio *//*
652 | visibleWidth = mVideoVisibleWidth * (double) mSarNum / mSarDen;
653 | aspectRatio = visibleWidth / mVideoVisibleHeight;
654 | }
655 |
656 | // compute the display aspect ratio
657 | double displayAspectRatio = displayWidth / displayHeight;
658 |
659 | counter ++;
660 |
661 | switch (mCurrentSize) {
662 | case SURFACE_BEST_FIT:
663 | if(counter > 2)
664 | Toast.makeText(getContext(), "Best Fit", Toast.LENGTH_SHORT).show();
665 | if (displayAspectRatio < aspectRatio)
666 | displayHeight = displayWidth / aspectRatio;
667 | else
668 | displayWidth = displayHeight * aspectRatio;
669 | break;
670 | case SURFACE_FIT_HORIZONTAL:
671 | Toast.makeText(getContext(), "Fit Horizontal", Toast.LENGTH_SHORT).show();
672 | displayHeight = displayWidth / aspectRatio;
673 | break;
674 | case SURFACE_FIT_VERTICAL:
675 | Toast.makeText(getContext(), "Fit Horizontal", Toast.LENGTH_SHORT).show();
676 | displayWidth = displayHeight * aspectRatio;
677 | break;
678 | case SURFACE_FILL:
679 | Toast.makeText(getContext(), "Fill", Toast.LENGTH_SHORT).show();
680 | break;
681 | case SURFACE_16_9:
682 | Toast.makeText(getContext(), "16:9", Toast.LENGTH_SHORT).show();
683 | aspectRatio = 16.0 / 9.0;
684 | if (displayAspectRatio < aspectRatio)
685 | displayHeight = displayWidth / aspectRatio;
686 | else
687 | displayWidth = displayHeight * aspectRatio;
688 | break;
689 | case SURFACE_4_3:
690 | Toast.makeText(getContext(), "4:3", Toast.LENGTH_SHORT).show();
691 | aspectRatio = 4.0 / 3.0;
692 | if (displayAspectRatio < aspectRatio)
693 | displayHeight = displayWidth / aspectRatio;
694 | else
695 | displayWidth = displayHeight * aspectRatio;
696 | break;
697 | case SURFACE_ORIGINAL:
698 | Toast.makeText(getContext(), "Original", Toast.LENGTH_SHORT).show();
699 | displayHeight = mVideoVisibleHeight;
700 | displayWidth = visibleWidth;
701 | break;
702 | }
703 |
704 | // set display size
705 | int finalWidth = (int) Math.ceil(displayWidth * mVideoWidth / mVideoVisibleWidth);
706 | int finalHeight = (int) Math.ceil(displayHeight * mVideoHeight / mVideoVisibleHeight);
707 |
708 | SurfaceHolder holder = this.getHolder();
709 | holder.setFixedSize(finalWidth, finalHeight);
710 |
711 | ViewGroup.LayoutParams lp = this.getLayoutParams();
712 | lp.width = finalWidth;
713 | lp.height = finalHeight;
714 | this.setLayoutParams(lp);
715 | this.invalidate();
716 | }*/
717 | }
718 |
--------------------------------------------------------------------------------