Skip to content

Commit 625d765

Browse files
author
NghiNV
committedAug 12, 2018
fist commit
0 parents  commit 625d765

34 files changed

+3382
-0
lines changed
 

‎LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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.

‎VLCPlayer.js

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import React, { PureComponent } from 'react';
2+
import {
3+
StyleSheet,
4+
requireNativeComponent,
5+
NativeModules,
6+
View
7+
} from 'react-native';
8+
import PropTypes from 'prop-types';
9+
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
10+
11+
export default class VLCPlayer extends PureComponent {
12+
constructor(props, context) {
13+
super(props, context);
14+
this.seek = this.seek.bind(this);
15+
this.resume = this.resume.bind(this);
16+
this.snapshot = this.snapshot.bind(this);
17+
this._assignRoot = this._assignRoot.bind(this);
18+
this._onError = this._onError.bind(this);
19+
this._onProgress = this._onProgress.bind(this);
20+
this._onEnded = this._onEnded.bind(this);
21+
this._onPlaying = this._onPlaying.bind(this);
22+
this._onStopped = this._onStopped.bind(this);
23+
this._onPaused = this._onPaused.bind(this);
24+
this._onBuffering = this._onBuffering.bind(this);
25+
this._onOpen = this._onOpen.bind(this);
26+
this._onLoadStart = this._onLoadStart.bind(this);
27+
}
28+
29+
setNativeProps(nativeProps) {
30+
this._root.setNativeProps(nativeProps);
31+
}
32+
33+
seek(pos) {
34+
this.setNativeProps({ seek: pos });
35+
}
36+
37+
resume(isResume) {
38+
this.setNativeProps({ resume: isResume });
39+
}
40+
41+
snapshot(path) {
42+
this.setNativeProps({ snapshotPath: path });
43+
}
44+
45+
_assignRoot(component) {
46+
this._root = component;
47+
}
48+
49+
_onBuffering(event) {
50+
if (this.props.onBuffering) {
51+
this.props.onBuffering(event.nativeEvent);
52+
}
53+
}
54+
55+
_onError(event) {
56+
if (this.props.onError) {
57+
this.props.onError(event.nativeEvent);
58+
}
59+
}
60+
61+
_onOpen(event) {
62+
if (this.props.onOpen) {
63+
this.props.onOpen(event.nativeEvent);
64+
}
65+
}
66+
67+
_onLoadStart(event) {
68+
if (this.props.onLoadStart) {
69+
this.props.onLoadStart(event.nativeEvent);
70+
}
71+
}
72+
73+
_onProgress(event) {
74+
if (this.props.onProgress) {
75+
this.props.onProgress(event.nativeEvent);
76+
}
77+
}
78+
79+
_onEnded(event) {
80+
if (this.props.onEnd) {
81+
this.props.onEnd(event.nativeEvent);
82+
}
83+
}
84+
85+
_onStopped(event) {
86+
this.setNativeProps({ paused: true });
87+
if (this.props.onStopped) {
88+
this.props.onStopped(event.nativeEvent);
89+
}
90+
}
91+
92+
_onPaused(event) {
93+
if (this.props.onPaused) {
94+
this.props.onPaused(event.nativeEvent);
95+
}
96+
}
97+
98+
_onPlaying(event) {
99+
if (this.props.onPlaying) {
100+
this.props.onPlaying(event.nativeEvent);
101+
}
102+
}
103+
104+
render() {
105+
/* const {
106+
source
107+
} = this.props;*/
108+
const source = resolveAssetSource(this.props.source) || {};
109+
110+
let uri = source.uri || '';
111+
if (uri && uri.match(/^\//)) {
112+
uri = `file://${uri}`;
113+
}
114+
115+
let isNetwork = !!(uri && uri.match(/^https?:/));
116+
const isAsset = !!(uri && uri.match(/^(assets-library|file|content|ms-appx|ms-appdata):/));
117+
if (!isAsset) {
118+
isNetwork = true;
119+
}
120+
source.initOptions = source.initOptions || [];
121+
//repeat the input media
122+
source.initOptions.push('--input-repeat=1000');
123+
const nativeProps = Object.assign({}, this.props);
124+
Object.assign(nativeProps, {
125+
style: [styles.base, nativeProps.style],
126+
source: source,
127+
src: {
128+
uri,
129+
isNetwork,
130+
isAsset,
131+
type: source.type || '',
132+
mainVer: source.mainVer || 0,
133+
patchVer: source.patchVer || 0,
134+
},
135+
onVideoLoadStart: this._onLoadStart,
136+
onVideoOpen: this._onOpen,
137+
onVideoError: this._onError,
138+
onVideoProgress: this._onProgress,
139+
onVideoEnded: this._onEnded,
140+
onVideoEnd: this._onEnded,
141+
onVideoPlaying: this._onPlaying,
142+
onVideoPaused: this._onPaused,
143+
onVideoStopped: this._onStopped,
144+
onVideoBuffering: this._onBuffering,
145+
progressUpdateInterval: 250,
146+
});
147+
148+
return <RCTVLCPlayer ref={this._assignRoot} {...nativeProps} />;
149+
}
150+
}
151+
152+
VLCPlayer.propTypes = {
153+
/* Native only */
154+
rate: PropTypes.number,
155+
seek: PropTypes.number,
156+
resume: PropTypes.bool,
157+
snapshotPath: PropTypes.string,
158+
paused: PropTypes.bool,
159+
160+
videoAspectRatio: PropTypes.string,
161+
volume: PropTypes.number,
162+
disableFocus: PropTypes.bool,
163+
src: PropTypes.string,
164+
playInBackground: PropTypes.bool,
165+
playWhenInactive: PropTypes.bool,
166+
resizeMode: PropTypes.string,
167+
poster: PropTypes.string,
168+
repeat: PropTypes.bool,
169+
muted: PropTypes.bool,
170+
171+
172+
onVideoLoadStart: PropTypes.func,
173+
onVideoError: PropTypes.func,
174+
onVideoProgress: PropTypes.func,
175+
onVideoEnded: PropTypes.func,
176+
onVideoPlaying: PropTypes.func,
177+
onVideoPaused: PropTypes.func,
178+
onVideoStopped: PropTypes.func,
179+
onVideoBuffering: PropTypes.func,
180+
onVideoOpen: PropTypes.func,
181+
182+
/* Wrapper component */
183+
source: PropTypes.object,
184+
185+
onError: PropTypes.func,
186+
onProgress: PropTypes.func,
187+
onEnded: PropTypes.func,
188+
onStopped: PropTypes.func,
189+
onPlaying: PropTypes.func,
190+
onPaused: PropTypes.func,
191+
192+
/* Required by react-native */
193+
scaleX: PropTypes.number,
194+
scaleY: PropTypes.number,
195+
translateX: PropTypes.number,
196+
translateY: PropTypes.number,
197+
rotation: PropTypes.number,
198+
...View.propTypes,
199+
};
200+
201+
const styles = StyleSheet.create({
202+
base: {
203+
overflow: 'hidden',
204+
},
205+
});
206+
const RCTVLCPlayer = requireNativeComponent('RCTVLCPlayer', VLCPlayer);

‎android/build.gradle

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
apply plugin: 'com.android.library'
2+
3+
android {
4+
compileSdkVersion 25
5+
buildToolsVersion '25.0.0'
6+
7+
defaultConfig {
8+
minSdkVersion 16
9+
targetSdkVersion 23
10+
versionCode 1
11+
versionName "1.0"
12+
ndk {
13+
abiFilters 'armeabi-v7a'//,'x86_64','arm64-v8a','x86'
14+
}
15+
}
16+
buildTypes {
17+
release {
18+
minifyEnabled false
19+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20+
}
21+
}
22+
}
23+
24+
dependencies {
25+
provided fileTree(dir: 'libs', include: ['*.jar'])
26+
compile "com.facebook.react:react-native:+"
27+
compile 'com.yyl.vlc:vlc-android-sdk:3.0.10'
28+
}

‎android/proguard-rules.pro

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
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+
}

‎android/src/main/AndroidManifest.xml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
3+
package="com.yuanzhou.vlc">
4+
5+
<application android:label="@string/app_name"
6+
android:supportsRtl="true">
7+
8+
</application>
9+
10+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
19+
return Collections.emptyList();
20+
}
21+
22+
// Deprecated RN 0.47
23+
public List<Class<? extends JavaScriptModule>> createJSModules() {
24+
return Collections.emptyList();
25+
}
26+
27+
@Override
28+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
29+
return Collections.<ViewManager>singletonList(new ReactVlcPlayerViewManager());
30+
}
31+
}

‎android/src/main/java/com/yuanzhou/vlc/vlcplayer/ReactVlcPlayerView.java

+492
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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<ReactVlcPlayerView> {
18+
19+
private static final String REACT_CLASS = "RCTVLCPlayer";
20+
21+
private static final String PROP_SRC = "src";
22+
private static final String PROP_SRC_URI = "uri";
23+
private static final String PROP_SRC_TYPE = "type";
24+
private static final String PROP_RESIZE_MODE = "resizeMode";
25+
private static final String PROP_REPEAT = "repeat";
26+
private static final String PROP_PAUSED = "paused";
27+
private static final String PROP_MUTED = "muted";
28+
private static final String PROP_VOLUME = "volume";
29+
private static final String PROP_PROGRESS_UPDATE_INTERVAL = "progressUpdateInterval";
30+
private static final String PROP_SEEK = "seek";
31+
private static final String PROP_RESUME = "resume";
32+
private static final String PROP_RATE = "rate";
33+
private static final String PROP_PLAY_IN_BACKGROUND = "playInBackground";
34+
private static final String PROP_DISABLE_FOCUS = "disableFocus";
35+
private static final String PROP_VIDEO_ASPECT_RATIO = "videoAspectRatio";
36+
public static final String PROP_SRC_IS_NETWORK = "isNetwork";
37+
public static final String PROP_SRC_IS_ASSET = "isAsset";
38+
39+
@Override
40+
public String getName() {
41+
return REACT_CLASS;
42+
}
43+
44+
@Override
45+
protected ReactVlcPlayerView createViewInstance(ThemedReactContext themedReactContext) {
46+
return new ReactVlcPlayerView(themedReactContext);
47+
}
48+
49+
@Override
50+
public void onDropViewInstance(ReactVlcPlayerView view) {
51+
view.cleanUpResources();
52+
}
53+
54+
@Override
55+
public @Nullable Map<String, Object> getExportedCustomDirectEventTypeConstants() {
56+
MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
57+
for (String event : VideoEventEmitter.Events) {
58+
builder.put(event, MapBuilder.of("registrationName", event));
59+
}
60+
return builder.build();
61+
}
62+
63+
@ReactProp(name = PROP_SRC)
64+
public void setSrc(final ReactVlcPlayerView videoView, @Nullable ReadableMap src) {
65+
Context context = videoView.getContext().getApplicationContext();
66+
String uriString = src.hasKey(PROP_SRC_URI) ? src.getString(PROP_SRC_URI) : null;
67+
String extension = src.hasKey(PROP_SRC_TYPE) ? src.getString(PROP_SRC_TYPE) : null;
68+
boolean isNetStr = src.getBoolean(PROP_SRC_IS_NETWORK) ? src.getBoolean(PROP_SRC_IS_NETWORK) : false;
69+
70+
if (TextUtils.isEmpty(uriString)) {
71+
return;
72+
}
73+
videoView.setSrc(uriString, isNetStr);
74+
75+
}
76+
77+
@ReactProp(name = PROP_RESIZE_MODE)
78+
public void setResizeMode(final ReactVlcPlayerView videoView, final String resizeModeOrdinalString) {
79+
//videoView.setResizeModeModifier(convertToIntDef(resizeModeOrdinalString));
80+
}
81+
82+
@ReactProp(name = PROP_REPEAT, defaultBoolean = false)
83+
public void setRepeat(final ReactVlcPlayerView videoView, final boolean repeat) {
84+
videoView.setRepeatModifier(repeat);
85+
}
86+
87+
@ReactProp(name = PROP_PAUSED, defaultBoolean = false)
88+
public void setPaused(final ReactVlcPlayerView videoView, final boolean paused) {
89+
videoView.setPausedModifier(paused);
90+
}
91+
92+
@ReactProp(name = PROP_MUTED, defaultBoolean = false)
93+
public void setMuted(final ReactVlcPlayerView videoView, final boolean muted) {
94+
//videoView.setMutedModifier(muted);
95+
}
96+
97+
@ReactProp(name = PROP_VOLUME, defaultFloat = 1.0f)
98+
public void setVolume(final ReactVlcPlayerView videoView, final float volume) {
99+
videoView.setVolumeModifier((int)volume);
100+
}
101+
102+
/*@ReactProp(name = PROP_PROGRESS_UPDATE_INTERVAL, defaultFloat = 250.0f)
103+
public void setProgressUpdateInterval(final ReactVlcPlayerView videoView, final float progressUpdateInterval) {
104+
//videoView.setProgressUpdateInterval(progressUpdateInterval);
105+
}*/
106+
107+
@ReactProp(name = PROP_SEEK)
108+
public void setSeek(final ReactVlcPlayerView videoView, final float seek) {
109+
videoView.seekTo(Math.round(seek * 1000f));
110+
//videoView.seekTo(seek);
111+
}
112+
113+
@ReactProp(name = PROP_RESUME, defaultBoolean = false)
114+
public void setResume(final ReactVlcPlayerView videoView, final boolean autoPlay) {
115+
videoView.doResume(autoPlay);
116+
}
117+
118+
@ReactProp(name = PROP_RATE)
119+
public void setRate(final ReactVlcPlayerView videoView, final float rate) {
120+
videoView.setRateModifier(rate);
121+
}
122+
123+
@ReactProp(name = PROP_PLAY_IN_BACKGROUND, defaultBoolean = false)
124+
public void setPlayInBackground(final ReactVlcPlayerView videoView, final boolean playInBackground) {
125+
videoView.setPlayInBackground(playInBackground);
126+
}
127+
128+
@ReactProp(name = PROP_DISABLE_FOCUS, defaultBoolean = false)
129+
public void setDisableFocus(final ReactVlcPlayerView videoView, final boolean disableFocus) {
130+
videoView.setDisableFocus(disableFocus);
131+
}
132+
133+
@ReactProp(name = PROP_VIDEO_ASPECT_RATIO)
134+
public void setVideoAspectRatio(final ReactVlcPlayerView videoView, final String aspectRatio) {
135+
videoView.setAspectRatio(aspectRatio);
136+
}
137+
138+
139+
private boolean startsWithValidScheme(String uriString) {
140+
return uriString.startsWith("http://")
141+
|| uriString.startsWith("https://")
142+
|| uriString.startsWith("content://")
143+
|| uriString.startsWith("file://")
144+
|| uriString.startsWith("asset://");
145+
}
146+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
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_LOAD = "onVideoLoad";
29+
private static final String EVENT_ERROR = "onVideoError";
30+
private static final String EVENT_PROGRESS = "onVideoProgress";
31+
private static final String EVENT_SEEK = "onVideoSeek";
32+
private static final String EVENT_END = "onVideoEnd";
33+
private static final String EVENT_PLAYING = "onVideoPlaying";
34+
private static final String EVENT_STOPPED = "onVideoStopped";
35+
private static final String EVENT_STALLED = "onPlaybackStalled";
36+
private static final String EVENT_RESUME = "onPlaybackResume";
37+
private static final String EVENT_READY = "onReadyForDisplay";
38+
private static final String EVENT_BUFFER = "onVideoBuffer";
39+
private static final String EVENT_IDLE = "onVideoIdle";
40+
private static final String EVENT_PAUSED = "onVideoPaused";
41+
private static final String EVENT_OPEN = "onVideoOpen";
42+
private static final String EVENT_TIMED_METADATA = "onTimedMetadata";
43+
private static final String EVENT_AUDIO_BECOMING_NOISY = "onAudioBecomingNoisy";
44+
private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged";
45+
private static final String EVENT_PLAYBACK_RATE_CHANGE = "onPlaybackRateChange";
46+
47+
static final String[] Events = {
48+
EVENT_LOAD_START,
49+
EVENT_LOAD,
50+
EVENT_ERROR,
51+
EVENT_PROGRESS,
52+
EVENT_SEEK,
53+
EVENT_END,
54+
EVENT_STALLED,
55+
EVENT_RESUME,
56+
EVENT_READY,
57+
EVENT_BUFFER,
58+
EVENT_IDLE,
59+
EVENT_TIMED_METADATA,
60+
EVENT_AUDIO_BECOMING_NOISY,
61+
EVENT_AUDIO_FOCUS_CHANGE,
62+
EVENT_PLAYBACK_RATE_CHANGE,
63+
EVENT_PAUSED,
64+
EVENT_PLAYING,
65+
EVENT_STOPPED,
66+
EVENT_OPEN,
67+
68+
};
69+
70+
@Retention(RetentionPolicy.SOURCE)
71+
@StringDef({
72+
EVENT_LOAD_START,
73+
EVENT_LOAD,
74+
EVENT_ERROR,
75+
EVENT_PROGRESS,
76+
EVENT_SEEK,
77+
EVENT_END,
78+
EVENT_STALLED,
79+
EVENT_RESUME,
80+
EVENT_READY,
81+
EVENT_BUFFER,
82+
EVENT_IDLE,
83+
EVENT_TIMED_METADATA,
84+
EVENT_AUDIO_BECOMING_NOISY,
85+
EVENT_AUDIO_FOCUS_CHANGE,
86+
EVENT_PLAYBACK_RATE_CHANGE,
87+
EVENT_PAUSED,
88+
EVENT_PLAYING,
89+
EVENT_STOPPED,
90+
EVENT_OPEN,
91+
})
92+
93+
@interface VideoEvents {
94+
}
95+
96+
private static final String EVENT_PROP_FAST_FORWARD = "canPlayFastForward";
97+
private static final String EVENT_PROP_SLOW_FORWARD = "canPlaySlowForward";
98+
private static final String EVENT_PROP_SLOW_REVERSE = "canPlaySlowReverse";
99+
private static final String EVENT_PROP_REVERSE = "canPlayReverse";
100+
private static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
101+
private static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";
102+
103+
private static final String EVENT_PROP_DURATION = "duration";
104+
private static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
105+
private static final String EVENT_PROP_CURRENT_TIME = "currentTime";
106+
private static final String EVENT_PROP_SEEK_TIME = "seekTime";
107+
private static final String EVENT_PROP_NATURAL_SIZE = "naturalSize";
108+
private static final String EVENT_PROP_WIDTH = "width";
109+
private static final String EVENT_PROP_HEIGHT = "height";
110+
private static final String EVENT_PROP_ORIENTATION = "orientation";
111+
private static final String EVENT_PROP_HAS_AUDIO_FOCUS = "hasAudioFocus";
112+
private static final String EVENT_PROP_IS_BUFFERING = "isBuffering";
113+
private static final String EVENT_PROP_BUFFERING_RATE = "bufferRate";
114+
private static final String EVENT_PROP_PLAYBACK_RATE = "playbackRate";
115+
116+
private static final String EVENT_PROP_ERROR = "error";
117+
private static final String EVENT_PROP_ERROR_STRING = "errorString";
118+
private static final String EVENT_PROP_ERROR_EXCEPTION = "";
119+
120+
private static final String EVENT_PROP_TIMED_METADATA = "metadata";
121+
122+
123+
void setViewId(int viewId) {
124+
this.viewId = viewId;
125+
}
126+
127+
void loadStart() {
128+
WritableMap event = Arguments.createMap();
129+
receiveEvent(EVENT_LOAD_START, event);
130+
}
131+
132+
void load(double duration, double currentPosition, int videoWidth, int videoHeight) {
133+
WritableMap event = Arguments.createMap();
134+
event.putDouble(EVENT_PROP_DURATION, duration / 1000D);
135+
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
136+
137+
WritableMap naturalSize = Arguments.createMap();
138+
naturalSize.putInt(EVENT_PROP_WIDTH, videoWidth);
139+
naturalSize.putInt(EVENT_PROP_HEIGHT, videoHeight);
140+
if (videoWidth > videoHeight) {
141+
naturalSize.putString(EVENT_PROP_ORIENTATION, "landscape");
142+
} else {
143+
naturalSize.putString(EVENT_PROP_ORIENTATION, "portrait");
144+
}
145+
event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize);
146+
147+
// TODO: Actually check if you can.
148+
event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
149+
event.putBoolean(EVENT_PROP_SLOW_FORWARD, true);
150+
event.putBoolean(EVENT_PROP_SLOW_REVERSE, true);
151+
event.putBoolean(EVENT_PROP_REVERSE, true);
152+
event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
153+
event.putBoolean(EVENT_PROP_STEP_BACKWARD, true);
154+
event.putBoolean(EVENT_PROP_STEP_FORWARD, true);
155+
156+
receiveEvent(EVENT_LOAD, event);
157+
}
158+
159+
void progressChanged(double currentPosition, double bufferedDuration) {
160+
WritableMap event = Arguments.createMap();
161+
// event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
162+
//event.putDouble(EVENT_PROP_PLAYABLE_DURATION, bufferedDuration / 1000D);
163+
event.putDouble("currentTime", currentPosition);
164+
event.putDouble("duration", bufferedDuration);
165+
receiveEvent(EVENT_PROGRESS, event);
166+
}
167+
168+
void seek(long currentPosition, long seekTime) {
169+
WritableMap event = Arguments.createMap();
170+
event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);
171+
event.putDouble(EVENT_PROP_SEEK_TIME, seekTime / 1000D);
172+
receiveEvent(EVENT_SEEK, event);
173+
}
174+
175+
void ready() {
176+
receiveEvent(EVENT_READY, null);
177+
}
178+
179+
void buffering(boolean isBuffering, float bufferRate) {
180+
WritableMap map = Arguments.createMap();
181+
map.putBoolean(EVENT_PROP_IS_BUFFERING, isBuffering);
182+
map.putDouble(EVENT_PROP_BUFFERING_RATE, bufferRate);
183+
receiveEvent(EVENT_BUFFER, map);
184+
}
185+
186+
void idle() {
187+
receiveEvent(EVENT_IDLE, null);
188+
}
189+
190+
void end() {
191+
receiveEvent(EVENT_END, null);
192+
}
193+
194+
void error(String errorString, Exception exception) {
195+
WritableMap error = Arguments.createMap();
196+
error.putString(EVENT_PROP_ERROR_STRING, errorString);
197+
error.putString(EVENT_PROP_ERROR_EXCEPTION, exception.getMessage());
198+
WritableMap event = Arguments.createMap();
199+
event.putMap(EVENT_PROP_ERROR, error);
200+
receiveEvent(EVENT_ERROR, event);
201+
}
202+
203+
void playbackRateChange(float rate) {
204+
WritableMap map = Arguments.createMap();
205+
map.putDouble(EVENT_PROP_PLAYBACK_RATE, (double)rate);
206+
receiveEvent(EVENT_PLAYBACK_RATE_CHANGE, map);
207+
}
208+
209+
/*void timedMetadata(Metadata metadata) {
210+
WritableArray metadataArray = Arguments.createArray();
211+
212+
for (int i = 0; i < metadata.length(); i++) {
213+
214+
215+
Id3Frame frame = (Id3Frame) metadata.get(i);
216+
217+
String value = "";
218+
219+
if (frame instanceof TextInformationFrame) {
220+
TextInformationFrame txxxFrame = (TextInformationFrame) frame;
221+
value = txxxFrame.value;
222+
}
223+
224+
String identifier = frame.id;
225+
226+
WritableMap map = Arguments.createMap();
227+
map.putString("identifier", identifier);
228+
map.putString("value", value);
229+
230+
metadataArray.pushMap(map);
231+
232+
}
233+
234+
WritableMap event = Arguments.createMap();
235+
event.putArray(EVENT_PROP_TIMED_METADATA, metadataArray);
236+
receiveEvent(EVENT_TIMED_METADATA, event);
237+
}
238+
*/
239+
void audioFocusChanged(boolean hasFocus) {
240+
WritableMap map = Arguments.createMap();
241+
map.putBoolean(EVENT_PROP_HAS_AUDIO_FOCUS, hasFocus);
242+
receiveEvent(EVENT_AUDIO_FOCUS_CHANGE, map);
243+
}
244+
245+
246+
void audioBecomingNoisy() {
247+
receiveEvent(EVENT_AUDIO_BECOMING_NOISY, null);
248+
}
249+
250+
void paused(boolean paused){
251+
WritableMap map = Arguments.createMap();
252+
map.putBoolean("paused", paused);
253+
receiveEvent(EVENT_PAUSED, map);
254+
}
255+
256+
void playing(){
257+
WritableMap map = Arguments.createMap();
258+
receiveEvent(EVENT_PLAYING, map);
259+
}
260+
261+
void stopped(){
262+
WritableMap map = Arguments.createMap();
263+
receiveEvent(EVENT_STOPPED, map);
264+
}
265+
266+
void onOpen(){
267+
WritableMap map = Arguments.createMap();
268+
receiveEvent(EVENT_OPEN, map);
269+
}
270+
271+
private void receiveEvent(@VideoEvents String type, WritableMap event) {
272+
eventEmitter.receiveEvent(viewId, type, event);
273+
}
274+
275+
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<resources>
2+
<string name="app_name">vlcplayer</string>
3+
</resources>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
11+
*/
12+
public class ExampleUnitTest {
13+
@Test
14+
public void addition_isCorrect() throws Exception {
15+
assertEquals(4, 2 + 2);
16+
}
17+
}

‎images/1.png

151 KB
Loading

‎images/2.png

47.8 KB
Loading

‎images/3.png

85.5 KB
Loading

‎images/4.png

57.7 KB
Loading

‎index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
const VLCPlayerControl = {
3+
VLCPlayer: require('./VLCPlayer').default,
4+
VlCPlayerView: require('./playerView/index').default,
5+
}
6+
7+
module.exports = VLCPlayerControl;
+288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
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 = "<group>"; };
30+
0C1A0EC91D07E18700441684 /* RCTVLCPlayerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVLCPlayerManager.h; sourceTree = "<group>"; };
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 = "<group>"; };
33+
0CA30C491D07E0DB003B09F9 /* RCTVLCPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTVLCPlayer.m; sourceTree = "<group>"; };
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 = "<group>";
54+
};
55+
0CA30C451D07E0DB003B09F9 /* Products */ = {
56+
isa = PBXGroup;
57+
children = (
58+
0CA30C441D07E0DB003B09F9 /* libRCTVLCPlayer.a */,
59+
);
60+
name = Products;
61+
sourceTree = "<group>";
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 = "<group>";
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 = (
226+
"$(SRCROOT)/../../../vlcKit",
227+
"\"$(SRCROOT)/../../../ios/Pods/MobileVLCKit-unstable/MobileVLCKit-binary\"",
228+
);
229+
HEADER_SEARCH_PATHS = (
230+
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
231+
"$(SRCROOT)/../../react-native/React/**",
232+
"$(SRCROOT)/node_modules/react-native/React/**",
233+
"$(inherited)",
234+
);
235+
ONLY_ACTIVE_ARCH = YES;
236+
OTHER_LDFLAGS = "-ObjC";
237+
PRODUCT_NAME = "$(TARGET_NAME)";
238+
SKIP_INSTALL = YES;
239+
VALID_ARCHS = "arm64 armv7 armv7s";
240+
};
241+
name = Debug;
242+
};
243+
0CA30C4F1D07E0DB003B09F9 /* Release */ = {
244+
isa = XCBuildConfiguration;
245+
buildSettings = {
246+
FRAMEWORK_SEARCH_PATHS = (
247+
"$(SRCROOT)/../../../vlcKit",
248+
"\"$(SRCROOT)/../../../ios/Pods/MobileVLCKit-unstable/MobileVLCKit-binary\"",
249+
);
250+
HEADER_SEARCH_PATHS = (
251+
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
252+
"$(SRCROOT)/../../react-native/React/**",
253+
"$(SRCROOT)/node_modules/react-native/React/**",
254+
"$(inherited)",
255+
);
256+
ONLY_ACTIVE_ARCH = NO;
257+
OTHER_LDFLAGS = "-ObjC";
258+
PRODUCT_NAME = "$(TARGET_NAME)";
259+
SKIP_INSTALL = YES;
260+
VALID_ARCHS = "arm64 armv7 armv7s";
261+
};
262+
name = Release;
263+
};
264+
/* End XCBuildConfiguration section */
265+
266+
/* Begin XCConfigurationList section */
267+
0CA30C3F1D07E0DB003B09F9 /* Build configuration list for PBXProject "RCTVLCPlayer" */ = {
268+
isa = XCConfigurationList;
269+
buildConfigurations = (
270+
0CA30C4B1D07E0DB003B09F9 /* Debug */,
271+
0CA30C4C1D07E0DB003B09F9 /* Release */,
272+
);
273+
defaultConfigurationIsVisible = 0;
274+
defaultConfigurationName = Release;
275+
};
276+
0CA30C4D1D07E0DB003B09F9 /* Build configuration list for PBXNativeTarget "RCTVLCPlayer" */ = {
277+
isa = XCConfigurationList;
278+
buildConfigurations = (
279+
0CA30C4E1D07E0DB003B09F9 /* Debug */,
280+
0CA30C4F1D07E0DB003B09F9 /* Release */,
281+
);
282+
defaultConfigurationIsVisible = 0;
283+
defaultConfigurationName = Release;
284+
};
285+
/* End XCConfigurationList section */
286+
};
287+
rootObject = 0CA30C3C1D07E0DB003B09F9 /* Project object */;
288+
}

‎ios/RCTVLCPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>SchemeUserState</key>
6+
<dict>
7+
<key>RCTVLCPlayer.xcscheme</key>
8+
<dict>
9+
<key>orderHint</key>
10+
<integer>0</integer>
11+
</dict>
12+
</dict>
13+
</dict>
14+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>SchemeUserState</key>
6+
<dict>
7+
<key>RCTVLCPlayer.xcscheme</key>
8+
<dict>
9+
<key>orderHint</key>
10+
<integer>52</integer>
11+
</dict>
12+
</dict>
13+
</dict>
14+
</plist>

‎ios/RCTVLCPlayer/RCTVLCPlayer.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
17+
18+
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
19+
20+
@end

‎ios/RCTVLCPlayer/RCTVLCPlayer.m

+311
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
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 <MobileVLCKit/MobileVLCKit.h>
7+
#import <AVFoundation/AVFoundation.h>
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+
@implementation RCTVLCPlayer
15+
{
16+
17+
/* Required to publish events */
18+
RCTEventDispatcher *_eventDispatcher;
19+
VLCMediaPlayer *_player;
20+
21+
NSDictionary * _source;
22+
BOOL _paused;
23+
BOOL _started;
24+
25+
}
26+
27+
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
28+
{
29+
if ((self = [super init])) {
30+
_eventDispatcher = eventDispatcher;
31+
32+
[[NSNotificationCenter defaultCenter] addObserver:self
33+
selector:@selector(applicationWillResignActive:)
34+
name:UIApplicationWillResignActiveNotification
35+
object:nil];
36+
37+
[[NSNotificationCenter defaultCenter] addObserver:self
38+
selector:@selector(applicationWillEnterForeground:)
39+
name:UIApplicationWillEnterForegroundNotification
40+
object:nil];
41+
42+
}
43+
44+
return self;
45+
}
46+
47+
- (void)applicationWillResignActive:(NSNotification *)notification
48+
{
49+
if (!_paused) {
50+
[self setPaused:_paused];
51+
}
52+
}
53+
54+
- (void)applicationWillEnterForeground:(NSNotification *)notification
55+
{
56+
[self applyModifiers];
57+
}
58+
59+
- (void)applyModifiers
60+
{
61+
if(!_paused)
62+
[self play];
63+
}
64+
65+
- (void)setPaused:(BOOL)paused
66+
{
67+
if(_player){
68+
if(!paused){
69+
[self play];
70+
}else {
71+
[_player pause];
72+
_paused = YES;
73+
_started = NO;
74+
}
75+
}
76+
}
77+
78+
-(void)play
79+
{
80+
if(_player){
81+
[_player play];
82+
_paused = NO;
83+
_started = YES;
84+
}
85+
}
86+
87+
-(void)setResume:(BOOL)autoplay
88+
{
89+
if(_player){
90+
[self _release];
91+
}
92+
// [bavv edit start]
93+
// NSArray* options = [_source objectForKey:@"initOptions"];
94+
// NSArray* options = @[@"--rtsp-tcp", @"-vv", @"--network-caching=300"];
95+
NSArray *options = [NSArray arrayWithObject:@"--rtsp-tcp"];
96+
NSString* uri = [_source objectForKey:@"uri"];
97+
NSURL* _uri = [NSURL URLWithString:uri];
98+
99+
_player = [[VLCMediaPlayer alloc] initWithOptions:options];
100+
// _player = [[VLCMediaPlayer alloc] init];
101+
// [bavv edit end]
102+
103+
[_player setDrawable:self];
104+
_player.delegate = self;
105+
_player.scaleFactor = 0;
106+
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerStateChanged:) name:VLCMediaPlayerStateChanged object:nil];
107+
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerTimeChanged:) name:VLCMediaPlayerTimeChanged object:nil];
108+
// NSMutableDictionary *mediaDictonary = [NSMutableDictionary new];
109+
//设置缓存多少毫秒
110+
// [mediaDictonary setObject:@"0" forKey:@"network-caching"];
111+
VLCMedia *media = [VLCMedia mediaWithURL:_uri];
112+
// [media addOptions:mediaDictonary];
113+
_player.media = media;
114+
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
115+
NSLog(@"autoplay: %i",autoplay);
116+
self.onVideoLoadStart(@{
117+
@"target": self.reactTag
118+
});
119+
}
120+
121+
-(void)setSource:(NSDictionary *)source
122+
{
123+
if(_player){
124+
[self _release];
125+
}
126+
_source = source;
127+
// [bavv edit start]
128+
// NSArray* options = [source objectForKey:@"initOptions"];
129+
// NSArray* options = @[@"--rtsp-tcp", @"-vv", @"--network-caching=300"];
130+
NSArray *options = [NSArray arrayWithObject:@"--rtsp-tcp"];
131+
NSString* uri = [source objectForKey:@"uri"];
132+
BOOL autoplay = [RCTConvert BOOL:[source objectForKey:@"autoplay"]];
133+
NSURL* _uri = [NSURL URLWithString:uri];
134+
135+
//init player && play
136+
_player = [[VLCMediaPlayer alloc] initWithOptions:options];
137+
// _player = [[VLCMediaPlayer alloc] init];
138+
// [bavv edit end]
139+
140+
[_player setDrawable:self];
141+
_player.delegate = self;
142+
_player.scaleFactor = 0;
143+
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerStateChanged:) name:VLCMediaPlayerStateChanged object:nil];
144+
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerTimeChanged:) name:VLCMediaPlayerTimeChanged object:nil];
145+
// NSMutableDictionary *mediaDictonary = [NSMutableDictionary new];
146+
//设置缓存多少毫秒
147+
// [mediaDictonary setObject:@"0" forKey:@"network-caching"];
148+
VLCMedia *media = [VLCMedia mediaWithURL:_uri];
149+
// [media addOptions:mediaDictonary];
150+
_player.media = media;
151+
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
152+
NSLog(@"autoplay: %i",autoplay);
153+
self.onVideoLoadStart(@{
154+
@"target": self.reactTag
155+
});
156+
// if(autoplay)
157+
[self play];
158+
}
159+
160+
- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification
161+
{
162+
[self updateVideoProgress];
163+
}
164+
165+
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
166+
{
167+
168+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
169+
NSLog(@"userInfo %@",[aNotification userInfo]);
170+
NSLog(@"standardUserDefaults %@",defaults);
171+
if(_player){
172+
VLCMediaPlayerState state = _player.state;
173+
switch (state) {
174+
case VLCMediaPlayerStateOpening:
175+
NSLog(@"VLCMediaPlayerStateOpening %i",1);
176+
self.onVideoOpen(@{
177+
@"target": self.reactTag
178+
});
179+
break;
180+
case VLCMediaPlayerStatePaused:
181+
_paused = YES;
182+
NSLog(@"VLCMediaPlayerStatePaused %i",1);
183+
self.onVideoPaused(@{
184+
@"target": self.reactTag
185+
});
186+
break;
187+
case VLCMediaPlayerStateStopped:
188+
NSLog(@"VLCMediaPlayerStateStopped %i",1);
189+
self.onVideoStopped(@{
190+
@"target": self.reactTag
191+
});
192+
break;
193+
case VLCMediaPlayerStateBuffering:
194+
NSLog(@"VLCMediaPlayerStateBuffering %i",1);
195+
self.onVideoBuffering(@{
196+
@"target": self.reactTag
197+
});
198+
break;
199+
case VLCMediaPlayerStatePlaying:
200+
_paused = NO;
201+
NSLog(@"VLCMediaPlayerStatePlaying %i",1);
202+
self.onVideoPlaying(@{
203+
@"target": self.reactTag,
204+
@"seekable": [NSNumber numberWithBool:[_player isSeekable]],
205+
@"duration":[NSNumber numberWithInt:[_player.media.length intValue]]
206+
});
207+
break;
208+
case VLCMediaPlayerStateEnded:
209+
NSLog(@"VLCMediaPlayerStateEnded %i",1);
210+
int currentTime = [[_player time] intValue];
211+
int remainingTime = [[_player remainingTime] intValue];
212+
int duration = [_player.media.length intValue];
213+
214+
self.onVideoEnded(@{
215+
@"target": self.reactTag,
216+
@"currentTime": [NSNumber numberWithInt:currentTime],
217+
@"remainingTime": [NSNumber numberWithInt:remainingTime],
218+
@"duration":[NSNumber numberWithInt:duration],
219+
@"position":[NSNumber numberWithFloat:_player.position]
220+
});
221+
break;
222+
case VLCMediaPlayerStateError:
223+
NSLog(@"VLCMediaPlayerStateError %i",1);
224+
self.onVideoError(@{
225+
@"target": self.reactTag
226+
});
227+
[self _release];
228+
break;
229+
default:
230+
break;
231+
}
232+
}
233+
}
234+
235+
-(void)updateVideoProgress
236+
{
237+
if(_player){
238+
int currentTime = [[_player time] intValue];
239+
int remainingTime = [[_player remainingTime] intValue];
240+
int duration = [_player.media.length intValue];
241+
242+
if( currentTime >= 0 && currentTime < duration) {
243+
self.onVideoProgress(@{
244+
@"target": self.reactTag,
245+
@"currentTime": [NSNumber numberWithInt:currentTime],
246+
@"remainingTime": [NSNumber numberWithInt:remainingTime],
247+
@"duration":[NSNumber numberWithInt:duration],
248+
@"position":[NSNumber numberWithFloat:_player.position]
249+
});
250+
}
251+
}
252+
}
253+
254+
- (void)jumpBackward:(int)interval
255+
{
256+
if(interval>=0 && interval <= [_player.media.length intValue])
257+
[_player jumpBackward:interval];
258+
}
259+
260+
- (void)jumpForward:(int)interval
261+
{
262+
if(interval>=0 && interval <= [_player.media.length intValue])
263+
[_player jumpForward:interval];
264+
}
265+
266+
-(void)setSeek:(float)pos
267+
{
268+
if([_player isSeekable]){
269+
if(pos>=0 && pos <= 1){
270+
[_player setPosition:pos];
271+
}
272+
}
273+
}
274+
275+
-(void)setSnapshotPath:(NSString*)path
276+
{
277+
if(_player)
278+
[_player saveVideoSnapshotAt:path withWidth:0 andHeight:0];
279+
}
280+
281+
-(void)setRate:(float)rate
282+
{
283+
[_player setRate:rate];
284+
}
285+
286+
-(void)setVideoAspectRatio:(NSString *)ratio{
287+
char *char_content = [ratio cStringUsingEncoding:NSASCIIStringEncoding];
288+
[_player setVideoAspectRatio:char_content];
289+
}
290+
291+
- (void)_release
292+
{
293+
if(_player){
294+
[_player pause];
295+
[_player stop];
296+
_player = nil;
297+
_eventDispatcher = nil;
298+
[[NSNotificationCenter defaultCenter] removeObserver:self];
299+
}
300+
}
301+
302+
303+
#pragma mark - Lifecycle
304+
- (void)removeFromSuperview
305+
{
306+
NSLog(@"removeFromSuperview");
307+
[self _release];
308+
[super removeFromSuperview];
309+
}
310+
311+
@end
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#import "React/RCTViewManager.h"
2+
3+
@interface RCTVLCPlayerManager : RCTViewManager
4+
5+
@end
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
/* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */
17+
RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock);
18+
RCT_EXPORT_VIEW_PROPERTY(onVideoPaused, RCTBubblingEventBlock);
19+
RCT_EXPORT_VIEW_PROPERTY(onVideoStopped, RCTBubblingEventBlock);
20+
RCT_EXPORT_VIEW_PROPERTY(onVideoBuffering, RCTBubblingEventBlock);
21+
RCT_EXPORT_VIEW_PROPERTY(onVideoPlaying, RCTBubblingEventBlock);
22+
RCT_EXPORT_VIEW_PROPERTY(onVideoEnded, RCTBubblingEventBlock);
23+
RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock);
24+
RCT_EXPORT_VIEW_PROPERTY(onVideoOpen, RCTBubblingEventBlock);
25+
RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTBubblingEventBlock);
26+
27+
- (dispatch_queue_t)methodQueue
28+
{
29+
return dispatch_get_main_queue();
30+
}
31+
32+
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary);
33+
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL);
34+
RCT_EXPORT_VIEW_PROPERTY(seek, float);
35+
RCT_EXPORT_VIEW_PROPERTY(rate, float);
36+
RCT_EXPORT_VIEW_PROPERTY(resume, BOOL);
37+
RCT_EXPORT_VIEW_PROPERTY(videoAspectRatio, NSString);
38+
RCT_EXPORT_VIEW_PROPERTY(snapshotPath, NSString);
39+
40+
@end

‎package.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "react-native-vlcplayer",
3+
"version": "1.0.0",
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": "",
18+
"repository": {
19+
"type": "git",
20+
"url": ""
21+
},
22+
"license": "MIT"
23+
}

‎playerView/BackHandle.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
};

‎playerView/ControlBtn.js

+249
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
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+
showGG,
51+
showSlider,
52+
showGoLive,
53+
onGoLivePress,
54+
onReplayPress,
55+
onPausedPress,
56+
onFullPress,
57+
onValueChange,
58+
onSlidingComplete,
59+
currentTime,
60+
totalTime,
61+
onLeftPress,
62+
title,
63+
onEnd,
64+
style
65+
} = this.props;
66+
return (
67+
<View style={[styles.controls, style]}>
68+
<View style={styles.controlContainer}>
69+
<TouchableOpacity style={styles.controlContent} activeOpacity={1}>
70+
<View style={styles.controlContent2}>
71+
<View style={styles.right}>
72+
<TouchableOpacity
73+
activeOpacity={1}
74+
onPress={() => {
75+
onReplayPress && onReplayPress();
76+
}}
77+
style={{ width: 50, alignItems: 'center', justifyContent: 'center' }}>
78+
<Icon name={'replay'} size={30} color="#fff" />
79+
</TouchableOpacity>
80+
<Text
81+
style={{ fontSize: 11, color: '#fff' }}> </Text>
82+
</View>
83+
84+
<TouchableOpacity
85+
activeOpacity={1}
86+
onPress={() => {
87+
onPausedPress && onPausedPress(!paused);
88+
}}
89+
style={{ width: 50, alignItems: 'center', justifyContent: 'center' }}>
90+
<Icon name={paused ? 'play' : 'pause'} size={30} color="#fff" />
91+
</TouchableOpacity>
92+
93+
{/* {showSlider && totalTime > 0 &&(
94+
<View
95+
style={{
96+
flex: 1,
97+
alignItems: 'center',
98+
flexDirection: 'row',
99+
//justifyContent: 'space-between',
100+
}}>
101+
<View style={{justifyContent:'center',alignItems:'center',height:50, minWidth: 50,}}>
102+
<Text style={{fontSize: 11,color: '#fff',}}>
103+
{this._getTime(currentTime) || 0}
104+
</Text>
105+
</View>
106+
<View style={styles.progress}>
107+
<Slider
108+
minimumTrackTintColor="#30a935"
109+
thumbStyle={styles.thumb}
110+
style={{ width: '100%' }}
111+
value={currentTime}
112+
maximumValue={totalTime}
113+
step={1}
114+
onValueChange={value => {
115+
onValueChange && onValueChange(value);
116+
}}
117+
onSlidingComplete={value => {
118+
onSlidingComplete && onSlidingComplete(value);
119+
}}
120+
/>
121+
</View>
122+
<View style={{justifyContent:'center',alignItems:'center',height:50, minWidth: 50}}>
123+
<Text
124+
style={{fontSize: 11,color: '#fff'}}>
125+
{this._getTime(totalTime) || 0}
126+
</Text>
127+
</View>
128+
</View>
129+
)} */}
130+
131+
<View style={styles.right}>
132+
<TouchableOpacity
133+
activeOpacity={1}
134+
onPress={() => {
135+
onGoLivePress && onGoLivePress();
136+
}}>
137+
<Text
138+
style={{ fontSize: 11, color: '#fff' }}>{showGoLive ? 'Go live' : ' '}</Text>
139+
</TouchableOpacity>
140+
<TouchableOpacity
141+
activeOpacity={1}
142+
onPress={() => {
143+
onFullPress && onFullPress(!isFull);
144+
}}
145+
style={{ width: 50, alignItems: 'center', justifyContent: 'center' }}>
146+
<Icon name={isFull ? 'fullscreen-exit' : 'fullscreen'} size={30} color="#fff" />
147+
</TouchableOpacity>
148+
</View>
149+
150+
</View>
151+
</TouchableOpacity>
152+
</View>
153+
</View>
154+
);
155+
}
156+
}
157+
158+
const styles = StyleSheet.create({
159+
container: {
160+
flex: 1,
161+
//backgroundColor: '#000',
162+
},
163+
controls: {
164+
width: '100%',
165+
height: 50,
166+
},
167+
rateControl: {
168+
flex: 0,
169+
flexDirection: 'row',
170+
marginTop: 10,
171+
marginLeft: 10,
172+
//backgroundColor: 'rgba(0,0,0,0.5)',
173+
width: 120,
174+
height: 30,
175+
justifyContent: 'space-around',
176+
alignItems: 'center',
177+
borderRadius: 10,
178+
},
179+
controlOption: {
180+
textAlign: 'center',
181+
fontSize: 13,
182+
color: '#fff',
183+
width: 30,
184+
//lineHeight: 12,
185+
},
186+
controlContainer: {
187+
flex: 1,
188+
//padding: 5,
189+
alignItems: 'center',
190+
justifyContent: 'center',
191+
},
192+
193+
controlContent: {
194+
width: '100%',
195+
height: 50,
196+
//borderRadius: 10,
197+
backgroundColor: 'rgba(255,255,255,0.1)',
198+
},
199+
controlContent2: {
200+
flex: 1,
201+
flexDirection: 'row',
202+
backgroundColor: 'rgba(0,0,0,0.5)',
203+
alignItems: 'center',
204+
justifyContent: 'space-between',
205+
},
206+
207+
progress: {
208+
flex: 1,
209+
borderRadius: 3,
210+
alignItems: 'center',
211+
justifyContent: 'center',
212+
},
213+
left: {
214+
flexDirection: 'row',
215+
alignItems: 'center',
216+
justifyContent: 'center',
217+
},
218+
right: {
219+
flexDirection: 'row',
220+
alignItems: 'center',
221+
justifyContent: 'center',
222+
},
223+
thumb: {
224+
width: 6,
225+
height: 18,
226+
backgroundColor: '#fff',
227+
borderRadius: 4,
228+
},
229+
loading: {
230+
position: 'absolute',
231+
left: 0,
232+
top: 0,
233+
zIndex: 0,
234+
width: '100%',
235+
height: '100%',
236+
justifyContent: 'center',
237+
alignItems: 'center',
238+
},
239+
240+
GG: {
241+
backgroundColor: 'rgba(255,255,255,1)',
242+
height: 30,
243+
paddingLeft: 10,
244+
paddingRight: 10,
245+
borderRadius: 20,
246+
justifyContent: 'center',
247+
alignItems: 'center',
248+
},
249+
});

‎playerView/SizeController.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
}

‎playerView/TimeLimit.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
<TouchableOpacity
54+
style={{ flexDirection: 'row' }}
55+
onPress={this._onEnd}
56+
activeOpacity={1}>
57+
{maxTime > 0 && (
58+
<View style={styles.timeView}>
59+
<Text style={{ color: 'green', fontSize: 13 }}>{maxTime - timeNumber}</Text>
60+
</View>
61+
)}
62+
<View style={styles.nameView}>
63+
<Text style={{ fontSize: 13 }}>跳过片头</Text>
64+
</View>
65+
</TouchableOpacity>
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+
});

‎playerView/VLCPlayerView.js

+550
Large diffs are not rendered by default.

‎playerView/index.js

+326
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
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+
isEndGG: false,
38+
isFull: false,
39+
currentUrl: '',
40+
storeUrl: '',
41+
};
42+
43+
static defaultProps = {
44+
height: 250,
45+
showGG: false,
46+
ggUrl: '',
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+
onGGEnd: 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+
onGoLivePress: PropTypes.func,
88+
89+
onReplayPress: PropTypes.func,
90+
};
91+
92+
static getDerivedStateFromProps(nextProps, preState) {
93+
let { url } = nextProps;
94+
let { currentUrl, storeUrl } = preState;
95+
if (url && url !== storeUrl) {
96+
if (storeUrl === "") {
97+
return {
98+
currentUrl: url,
99+
storeUrl: url,
100+
isEndGG: false,
101+
};
102+
} else {
103+
return {
104+
currentUrl: "",
105+
storeUrl: url,
106+
isEndGG: false,
107+
};
108+
}
109+
}
110+
return null;
111+
}
112+
113+
114+
componentDidUpdate(prevProps, prevState) {
115+
if (this.props.url !== prevState.storeUrl) {
116+
this.setState({
117+
storeUrl: this.props.url,
118+
currentUrl: this.props.url
119+
})
120+
}
121+
}
122+
123+
componentDidMount() {
124+
StatusBar.setBarStyle("light-content");
125+
let { style, isGG } = this.props;
126+
127+
if (style && style.height && !isNaN(style.height)) {
128+
this.initialHeight = style.height;
129+
}
130+
this.setState({
131+
currentVideoAspectRatio: deviceWidth + ":" + this.initialHeight,
132+
});
133+
134+
// [bav add start]
135+
let { isFull } = this.props;
136+
console.log(`isFull == ${isFull}`);
137+
if (isFull) {
138+
this._toFullScreen();
139+
}
140+
// [bav add end]
141+
}
142+
143+
componentWillUnmount() {
144+
let { isFull } = this.props;
145+
if (isFull) {
146+
this._closeFullScreen();
147+
}
148+
}
149+
150+
_closeFullScreen = () => {
151+
let { closeFullScreen, BackHandle, Orientation } = this.props;
152+
this.setState({ isFull: false, currentVideoAspectRatio: deviceWidth + ":" + this.initialHeight, });
153+
BackHandle && BackHandle.removeBackFunction(_fullKey);
154+
Orientation && Orientation.lockToPortrait();
155+
StatusBar.setHidden(false);
156+
//StatusBar.setTranslucent(false);
157+
closeFullScreen && closeFullScreen();
158+
};
159+
160+
_toFullScreen = () => {
161+
let { startFullScreen, BackHandle, Orientation } = this.props;
162+
//StatusBar.setTranslucent(true);
163+
this.setState({ isFull: true, currentVideoAspectRatio: deviceHeight + ":" + deviceWidth, });
164+
StatusBar.setHidden(true);
165+
BackHandle && BackHandle.addBackFunction(_fullKey, this._closeFullScreen);
166+
startFullScreen && startFullScreen();
167+
Orientation && Orientation.lockToLandscape && Orientation.lockToLandscape();
168+
};
169+
170+
_onLayout = (e) => {
171+
let { width, height } = e.nativeEvent.layout;
172+
console.log(e.nativeEvent.layout);
173+
if (width * height > 0) {
174+
this.width = width;
175+
this.height = height;
176+
if (!this.initialHeight) {
177+
this.initialHeight = height;
178+
}
179+
}
180+
}
181+
182+
render() {
183+
let { url, ggUrl, showGG, onGGEnd, onEnd, style, height, title, onLeftPress, showBack, showTitle, closeFullScreen, videoAspectRatio, fullVideoAspectRatio } = this.props;
184+
let { isEndGG, isFull, currentUrl } = this.state;
185+
let currentVideoAspectRatio = '';
186+
if (isFull) {
187+
currentVideoAspectRatio = fullVideoAspectRatio;
188+
} else {
189+
currentVideoAspectRatio = videoAspectRatio;
190+
}
191+
if (!currentVideoAspectRatio) {
192+
let { width, height } = this.state;
193+
currentVideoAspectRatio = this.state.currentVideoAspectRatio;
194+
}
195+
let realShowGG = false;
196+
let type = '';
197+
let ggType = '';
198+
let showVideo = false;
199+
let showTop = false;
200+
if (showGG && ggUrl && !isEndGG) {
201+
realShowGG = true;
202+
}
203+
if (currentUrl) {
204+
if (!showGG || (showGG && isEndGG)) {
205+
showVideo = true;
206+
}
207+
if (currentUrl.split) {
208+
let types = currentUrl.split('.');
209+
if (types && types.length > 0) {
210+
type = types[types.length - 1];
211+
}
212+
}
213+
}
214+
if (ggUrl && ggUrl.split) {
215+
let types = ggUrl.split('.');
216+
if (types && types.length > 0) {
217+
ggType = types[types.length - 1];
218+
}
219+
}
220+
if (!showVideo && !realShowGG) {
221+
showTop = true;
222+
}
223+
return (
224+
<View
225+
//onLayout={this._onLayout}
226+
style={[isFull ? styles.container : { height: 200, backgroundColor: '#000' }, style]}>
227+
{showTop && <View style={styles.topView}>
228+
<View style={styles.backBtn}>
229+
{showBack && <TouchableOpacity
230+
onPress={() => {
231+
if (isFull) {
232+
closeFullScreen && closeFullScreen();
233+
} else {
234+
onLeftPress && onLeftPress();
235+
}
236+
}}
237+
style={styles.btn}
238+
activeOpacity={0.8}>
239+
<Icon name={'chevron-left'} size={30} color="#fff" />
240+
</TouchableOpacity>
241+
}
242+
<View style={{ justifyContent: 'center', flex: 1, marginRight: 10 }}>
243+
{showTitle &&
244+
<Text style={{ color: '#fff', fontSize: 16 }} numberOfLines={1}>{title}</Text>
245+
}
246+
</View>
247+
</View>
248+
</View>
249+
}
250+
{realShowGG && (
251+
<VLCPlayerView
252+
{...this.props}
253+
videoAspectRatio={currentVideoAspectRatio}
254+
uri={ggUrl}
255+
source={{ uri: ggUrl, type: ggType }}
256+
type={ggType}
257+
isGG={true}
258+
showBack={showBack}
259+
showTitle={showTitle}
260+
isFull={isFull}
261+
onEnd={() => {
262+
onGGEnd && onGGEnd();
263+
this.setState({ isEndGG: true });
264+
}}
265+
startFullScreen={this._toFullScreen}
266+
closeFullScreen={this._closeFullScreen}
267+
/>
268+
)}
269+
270+
{showVideo && (
271+
<VLCPlayerView
272+
{...this.props}
273+
uri={currentUrl}
274+
videoAspectRatio={currentVideoAspectRatio}
275+
onLeftPress={onLeftPress}
276+
title={title}
277+
type={type}
278+
isFull={isFull}
279+
showBack={showBack}
280+
showTitle={showTitle}
281+
hadGG={true}
282+
isEndGG={isEndGG}
283+
//initPaused={this.state.paused}
284+
style={showGG && !isEndGG ? { position: 'absolute', zIndex: -1 } : {}}
285+
source={{ uri: currentUrl, type: type }}
286+
startFullScreen={this._toFullScreen}
287+
closeFullScreen={this._closeFullScreen}
288+
onEnd={() => {
289+
onEnd && onEnd();
290+
}}
291+
/>
292+
)}
293+
</View>
294+
);
295+
}
296+
}
297+
298+
const styles = StyleSheet.create({
299+
container: {
300+
flex: 1,
301+
backgroundColor: '#000',
302+
},
303+
topView: {
304+
top: Platform.OS === 'ios' ? statusBarHeight : 0,
305+
left: 0,
306+
height: 45,
307+
position: 'absolute',
308+
width: '100%'
309+
},
310+
backBtn: {
311+
height: 45,
312+
width: '100%',
313+
flexDirection: 'row',
314+
alignItems: 'center'
315+
},
316+
btn: {
317+
marginLeft: 10,
318+
marginRight: 10,
319+
justifyContent: 'center',
320+
alignItems: 'center',
321+
backgroundColor: 'rgba(0,0,0,0.1)',
322+
height: 40,
323+
borderRadius: 20,
324+
width: 40,
325+
}
326+
});

0 commit comments

Comments
 (0)
Please sign in to comment.