expo-libmpv 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +5 -0
- package/LICENSE.txt +674 -0
- package/README.md +46 -0
- package/android/build.gradle +52 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/libmpv/LibmpvVideoModule.kt +105 -0
- package/android/src/main/java/com/libmpv/LibmpvVideoView.kt +240 -0
- package/android/src/main/java/com/libmpv/LibmpvWrapper.kt +276 -0
- package/app.json +14 -0
- package/build/LibmpvVideo.types.d.ts +15 -0
- package/build/LibmpvVideo.types.d.ts.map +1 -0
- package/build/LibmpvVideo.types.js +2 -0
- package/build/LibmpvVideo.types.js.map +1 -0
- package/build/LibmpvVideoView.d.ts +4 -0
- package/build/LibmpvVideoView.d.ts.map +1 -0
- package/build/LibmpvVideoView.js +84 -0
- package/build/LibmpvVideoView.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +10 -0
- package/package.json +45 -0
- package/script/clean.sh +13 -0
- package/script/develop.sh +5 -0
- package/src/LibmpvVideo.types.ts +14 -0
- package/src/LibmpvVideoView.tsx +108 -0
- package/src/index.ts +2 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
package com.libmpv
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import android.view.SurfaceView
|
|
6
|
+
import dev.jdtech.mpv.MPVLib
|
|
7
|
+
import java.io.File
|
|
8
|
+
import java.io.FileOutputStream
|
|
9
|
+
import java.io.InputStream
|
|
10
|
+
import java.io.OutputStream
|
|
11
|
+
|
|
12
|
+
class LibmpvWrapper(private val applicationContext: Context) {
|
|
13
|
+
companion object {
|
|
14
|
+
private const val TAG = "react-native-libmpv"
|
|
15
|
+
private var swallow = true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private var created = false
|
|
19
|
+
private var isPlaying = false
|
|
20
|
+
private var hasPlayedOnce = false
|
|
21
|
+
private var eventObserver: MPVLib.EventObserver? = null
|
|
22
|
+
private var logObserver: MPVLib.LogObserver? = null
|
|
23
|
+
private var mpvDirectory: String? = null
|
|
24
|
+
private var surfaceWidth: Int = -1
|
|
25
|
+
private var surfaceHeight: Int = -1
|
|
26
|
+
private var surfaceView: SurfaceView? = null
|
|
27
|
+
private val mpv: MPVLib = MPVLib()
|
|
28
|
+
|
|
29
|
+
fun create(): Boolean {
|
|
30
|
+
mpv.create(applicationContext)
|
|
31
|
+
createMpvDirectory()
|
|
32
|
+
created = true
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun isCreated(): Boolean = created
|
|
37
|
+
fun isPlaying(): Boolean = isPlaying
|
|
38
|
+
fun hasPlayedOnce(): Boolean = hasPlayedOnce
|
|
39
|
+
fun getMpvDirectoryPath(): String? = mpvDirectory
|
|
40
|
+
|
|
41
|
+
private fun createMpvDirectory() {
|
|
42
|
+
val mpvDir = File(applicationContext.getExternalFilesDir("mpv"), "mpv")
|
|
43
|
+
try {
|
|
44
|
+
mpvDirectory = mpvDir.absolutePath
|
|
45
|
+
if (!mpvDir.exists() && !mpvDir.mkdirs()) {
|
|
46
|
+
Log.e(TAG, "exception", IllegalArgumentException("Unable to create $mpvDir"))
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Copy font
|
|
51
|
+
val mpvFontPath = "${mpvDir}/subfont.ttf"
|
|
52
|
+
applicationContext.assets.open("subfont.ttf").use { subfontIn ->
|
|
53
|
+
FileOutputStream(mpvFontPath).use { fontOut ->
|
|
54
|
+
subfontIn.copyTo(fontOut)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Copy conf
|
|
59
|
+
val mpvConfPath = "${mpvDir}/mpv.conf"
|
|
60
|
+
applicationContext.assets.open("mpv.conf").use { mpvConfIn ->
|
|
61
|
+
FileOutputStream(mpvConfPath).use { confOut ->
|
|
62
|
+
mpvConfIn.copyTo(confOut)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (e: Exception) {
|
|
66
|
+
Log.e(TAG, "Unable to create the directory $mpvDir", e)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private fun logException(exception: Exception) {
|
|
71
|
+
try {
|
|
72
|
+
val message: String = (exception.message as? String) ?: "Unable to read error message"
|
|
73
|
+
logObserver?.logMessage("RNLE", 20, message)
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
if (!swallow) throw e
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fun addEventObserver(observer: MPVLib.EventObserver) {
|
|
80
|
+
try {
|
|
81
|
+
if (!created) return
|
|
82
|
+
mpv.removeObservers()
|
|
83
|
+
eventObserver = observer
|
|
84
|
+
mpv.addObserver(eventObserver)
|
|
85
|
+
mpv.observeProperty("demuxer-cache-time", MPVLib.MPV_FORMAT_INT64)
|
|
86
|
+
mpv.observeProperty("duration", MPVLib.MPV_FORMAT_INT64)
|
|
87
|
+
mpv.observeProperty("eof-reached", MPVLib.MPV_FORMAT_FLAG)
|
|
88
|
+
mpv.observeProperty("paused-for-cache", MPVLib.MPV_FORMAT_FLAG)
|
|
89
|
+
mpv.observeProperty("seekable", MPVLib.MPV_FORMAT_FLAG)
|
|
90
|
+
mpv.observeProperty("speed", MPVLib.MPV_FORMAT_DOUBLE)
|
|
91
|
+
mpv.observeProperty("time-pos", MPVLib.MPV_FORMAT_INT64)
|
|
92
|
+
mpv.observeProperty("track-list", MPVLib.MPV_FORMAT_STRING)
|
|
93
|
+
} catch (e: Exception) {
|
|
94
|
+
logException(e)
|
|
95
|
+
if (!swallow) throw e
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fun addLogObserver(observer: MPVLib.LogObserver) {
|
|
100
|
+
try {
|
|
101
|
+
if (!created) return
|
|
102
|
+
mpv.removeLogObservers()
|
|
103
|
+
logObserver = observer
|
|
104
|
+
mpv.addLogObserver(logObserver)
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
logException(e)
|
|
107
|
+
if (!swallow) throw e
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fun setOptionString(option: String, setting: String) {
|
|
112
|
+
try {
|
|
113
|
+
if (created) mpv.setOptionString(option, setting)
|
|
114
|
+
} catch (e: Exception) {
|
|
115
|
+
logException(e)
|
|
116
|
+
if (!swallow) throw e
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fun setPropertyString(property: String, setting: String) {
|
|
121
|
+
try {
|
|
122
|
+
if (created) mpv.setPropertyString(property, setting)
|
|
123
|
+
} catch (e: Exception) {
|
|
124
|
+
logException(e)
|
|
125
|
+
if (!swallow) throw e
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fun init() {
|
|
130
|
+
try {
|
|
131
|
+
if (created) mpv.init()
|
|
132
|
+
} catch (e: Exception) {
|
|
133
|
+
logException(e)
|
|
134
|
+
if (!swallow) throw e
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fun command(orders: Array<String>) {
|
|
139
|
+
try {
|
|
140
|
+
if (created) mpv.command(orders)
|
|
141
|
+
} catch (e: Exception) {
|
|
142
|
+
logException(e)
|
|
143
|
+
if (!swallow) throw e
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fun attachSurface(surfaceView: SurfaceView) {
|
|
148
|
+
try {
|
|
149
|
+
if (created) {
|
|
150
|
+
this.surfaceView = surfaceView
|
|
151
|
+
applySurfaceDimensions()
|
|
152
|
+
mpv.attachSurface(surfaceView.holder.surface)
|
|
153
|
+
}
|
|
154
|
+
} catch (e: Exception) {
|
|
155
|
+
logException(e)
|
|
156
|
+
if (!swallow) throw e
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
fun play(url: String, options: String? = null) {
|
|
161
|
+
if (!isPlaying) {
|
|
162
|
+
if (options == null) {
|
|
163
|
+
command(arrayOf("loadfile", url))
|
|
164
|
+
} else {
|
|
165
|
+
command(arrayOf("loadfile", url, "replace", "0", options))
|
|
166
|
+
}
|
|
167
|
+
command(arrayOf("set", "pause", "no"))
|
|
168
|
+
hasPlayedOnce = true
|
|
169
|
+
isPlaying = true
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fun pauseOrUnpause() {
|
|
174
|
+
if (!hasPlayedOnce) return
|
|
175
|
+
if (isPlaying) pause() else unpause()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
fun pause() {
|
|
179
|
+
if (!hasPlayedOnce) return
|
|
180
|
+
if (isPlaying) {
|
|
181
|
+
command(arrayOf("set", "pause", "yes"))
|
|
182
|
+
isPlaying = false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fun unpause() {
|
|
187
|
+
if (!hasPlayedOnce) return
|
|
188
|
+
if (!isPlaying) {
|
|
189
|
+
command(arrayOf("set", "pause", "no"))
|
|
190
|
+
isPlaying = true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fun seekToSeconds(seconds: Int) {
|
|
195
|
+
if (created) {
|
|
196
|
+
command(arrayOf("seek", seconds.toString(), "absolute"))
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private fun applySurfaceDimensions() {
|
|
201
|
+
if (surfaceHeight != -1 && surfaceWidth != -1 && surfaceView != null) {
|
|
202
|
+
surfaceView?.holder?.setFixedSize(surfaceWidth, surfaceHeight)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fun setSurfaceWidth(width: Int) {
|
|
207
|
+
surfaceWidth = width
|
|
208
|
+
applySurfaceDimensions()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
fun setSurfaceHeight(height: Int) {
|
|
212
|
+
surfaceHeight = height
|
|
213
|
+
applySurfaceDimensions()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
fun detachSurface() {
|
|
217
|
+
try {
|
|
218
|
+
mpv.detachSurface()
|
|
219
|
+
} catch (e: Exception) {
|
|
220
|
+
logException(e)
|
|
221
|
+
if (!swallow) throw e
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fun destroy() {
|
|
226
|
+
try {
|
|
227
|
+
mpv.removeObservers()
|
|
228
|
+
} catch (e: Exception) {
|
|
229
|
+
logException(e)
|
|
230
|
+
if (!swallow) throw e
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
mpv.removeLogObservers()
|
|
234
|
+
} catch (e: Exception) {
|
|
235
|
+
if (!swallow) throw e
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
mpv.destroy()
|
|
239
|
+
created = false
|
|
240
|
+
} catch (e: Exception) {
|
|
241
|
+
logException(e)
|
|
242
|
+
if (!swallow) throw e
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
fun cleanup() {
|
|
247
|
+
if (created) {
|
|
248
|
+
try {
|
|
249
|
+
pause()
|
|
250
|
+
setPropertyString("vo", "null")
|
|
251
|
+
setPropertyString("ao", "null")
|
|
252
|
+
} catch (e: Exception) {
|
|
253
|
+
logException(e)
|
|
254
|
+
if (!swallow) throw e
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
setOptionString("force-window", "no")
|
|
258
|
+
} catch (e: Exception) {
|
|
259
|
+
logException(e)
|
|
260
|
+
if (!swallow) throw e
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
detachSurface()
|
|
264
|
+
} catch (e: Exception) {
|
|
265
|
+
logException(e)
|
|
266
|
+
if (!swallow) throw e
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
destroy()
|
|
270
|
+
} catch (e: Exception) {
|
|
271
|
+
logException(e)
|
|
272
|
+
if (!swallow) throw e
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
package/app.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type LibmpvVideoViewProps = {
|
|
2
|
+
ref: any;
|
|
3
|
+
style: any;
|
|
4
|
+
playUrl: string;
|
|
5
|
+
isPlaying: boolean;
|
|
6
|
+
useHardwareDecoder: boolean;
|
|
7
|
+
selectedAudioTrack: number;
|
|
8
|
+
selectedSubtitleTrack: number;
|
|
9
|
+
seekToSeconds: number;
|
|
10
|
+
surfaceWidth: number;
|
|
11
|
+
surfaceHeight: number;
|
|
12
|
+
onLibmpvEvent: (libmpvEvent: any) => void;
|
|
13
|
+
onLibmpvLog: (libmpvLog: any) => void;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=LibmpvVideo.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LibmpvVideo.types.d.ts","sourceRoot":"","sources":["../src/LibmpvVideo.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,WAAW,EAAE,GAAG,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,SAAS,EAAE,GAAG,KAAK,IAAI,CAAC;CACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LibmpvVideo.types.js","sourceRoot":"","sources":["../src/LibmpvVideo.types.ts"],"names":[],"mappings":"","sourcesContent":["export type LibmpvVideoViewProps = {\n ref: any,\n style: any,\n playUrl: string,\n isPlaying: boolean,\n useHardwareDecoder: boolean,\n selectedAudioTrack: number,\n selectedSubtitleTrack: number,\n seekToSeconds: number,\n surfaceWidth: number,\n surfaceHeight: number,\n onLibmpvEvent: (libmpvEvent: any) => void,\n onLibmpvLog: (libmpvLog: any) => void,\n};"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LibmpvVideoView.d.ts","sourceRoot":"","sources":["../src/LibmpvVideoView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqC/B,eAAO,MAAM,WAAW,kFAmEtB,CAAA;AAEF,eAAe,WAAW,CAAA"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
const styles = {
|
|
4
|
+
videoPlayer: {
|
|
5
|
+
position: "absolute",
|
|
6
|
+
left: 0,
|
|
7
|
+
bottom: 0,
|
|
8
|
+
right: 0,
|
|
9
|
+
top: 0
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const EVENT_LOOKUP = {
|
|
13
|
+
0: 'NONE',
|
|
14
|
+
1: 'SHUTDOWN',
|
|
15
|
+
2: 'LOG_MESSAGE',
|
|
16
|
+
3: 'GET_PROPERTY_REPLY',
|
|
17
|
+
4: 'SET_PROPERTY_REPLY',
|
|
18
|
+
5: 'COMMAND_REPLY',
|
|
19
|
+
6: 'START_FILE',
|
|
20
|
+
7: 'END_FILE',
|
|
21
|
+
8: 'FILE_LOADED',
|
|
22
|
+
16: 'CLIENT_MESSAGE',
|
|
23
|
+
17: 'VIDEO_RECONFIG',
|
|
24
|
+
18: 'AUDIO_RECONFIG',
|
|
25
|
+
20: 'SEEK',
|
|
26
|
+
21: 'PLAYBACK_RESTART',
|
|
27
|
+
22: 'PROPERTY_CHANGE',
|
|
28
|
+
24: 'QUEUE_OVERFLOW',
|
|
29
|
+
25: 'HOOK'
|
|
30
|
+
};
|
|
31
|
+
const LibmpvVideoView = requireNativeView('LibmpvVideo');
|
|
32
|
+
export const LibmpvVideo = React.forwardRef((props, parentRef) => {
|
|
33
|
+
const nativeRef = React.useRef(null);
|
|
34
|
+
// Pass mpv events and logs back up to the parent
|
|
35
|
+
const onLogEvent = (libmpvEvent) => {
|
|
36
|
+
if (props.onLibmpvEvent) {
|
|
37
|
+
if (libmpvEvent && libmpvEvent.nativeEvent) {
|
|
38
|
+
libmpvEvent = libmpvEvent.nativeEvent;
|
|
39
|
+
}
|
|
40
|
+
if (libmpvEvent.eventId) {
|
|
41
|
+
libmpvEvent.value = parseInt(libmpvEvent.eventId, 10);
|
|
42
|
+
libmpvEvent.eventKind = EVENT_LOOKUP[libmpvEvent.eventId];
|
|
43
|
+
}
|
|
44
|
+
else if (libmpvEvent.kind === 'long' || libmpvEvent.kind === 'double') {
|
|
45
|
+
libmpvEvent.value = Number(libmpvEvent.value);
|
|
46
|
+
}
|
|
47
|
+
else if (libmpvEvent.kind === 'boolean') {
|
|
48
|
+
libmpvEvent.value = libmpvEvent.value === 'true';
|
|
49
|
+
}
|
|
50
|
+
return props.onLibmpvEvent(libmpvEvent);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const onLibmpvLog = (libmpvLog) => {
|
|
54
|
+
if (props.onLibmpvLog) {
|
|
55
|
+
if (libmpvLog && libmpvLog.nativeEvent) {
|
|
56
|
+
libmpvLog = libmpvLog.nativeEvent;
|
|
57
|
+
}
|
|
58
|
+
return props.onLibmpvLog(libmpvLog);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
// Allow a parent to call native methods, such as tweaking subtitle properties
|
|
62
|
+
const callNativeMethod = (target) => {
|
|
63
|
+
return (pipeDelimitedArguments) => {
|
|
64
|
+
if (nativeRef.current) {
|
|
65
|
+
if (target === 'runCommand') {
|
|
66
|
+
nativeRef.current.runCommand(pipeDelimitedArguments);
|
|
67
|
+
}
|
|
68
|
+
else if (target === 'setOptionString') {
|
|
69
|
+
nativeRef.current.setOptionString(pipeDelimitedArguments);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
React.useImperativeHandle(parentRef, () => ({
|
|
75
|
+
runCommand: callNativeMethod("runCommand"),
|
|
76
|
+
setOptionString: callNativeMethod("setOptionString")
|
|
77
|
+
}));
|
|
78
|
+
// The order props are handled in the native code is non-deterministic
|
|
79
|
+
// Each native prop setter checks to see if all required props are set
|
|
80
|
+
// Only then will it try to create an instance of mpv
|
|
81
|
+
return <LibmpvVideoView ref={nativeRef} style={props.surfaceStyle ? props.surfaceStyle : styles.videoPlayer} playUrl={props.playUrl} isPlaying={props.isPlaying} useHardwareDecoder={props.useHardwareDecoder} surfaceWidth={props.surfaceWidth} surfaceHeight={props.surfaceHeight} selectedAudioTrack={props.selectedAudioTrack} selectedSubtitleTrack={props.selectedSubtitleTrack} seekToSeconds={props.seekToSeconds} onLibmpvEvent={onLogEvent} onLibmpvLog={onLibmpvLog}/>;
|
|
82
|
+
});
|
|
83
|
+
export default LibmpvVideo;
|
|
84
|
+
//# sourceMappingURL=LibmpvVideoView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LibmpvVideoView.js","sourceRoot":"","sources":["../src/LibmpvVideoView.tsx"],"names":[],"mappings":"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","sourcesContent":["import { requireNativeView } from 'expo';\nimport * as React from 'react';\n\nimport { LibmpvVideoViewProps } from './LibmpvVideo.types';\n\nconst styles: any = {\n videoPlayer: {\n position: \"absolute\",\n left: 0,\n bottom: 0,\n right: 0,\n top: 0\n }\n};\n\nconst EVENT_LOOKUP: any = {\n 0: 'NONE',\n 1: 'SHUTDOWN',\n 2: 'LOG_MESSAGE',\n 3: 'GET_PROPERTY_REPLY',\n 4: 'SET_PROPERTY_REPLY',\n 5: 'COMMAND_REPLY',\n 6: 'START_FILE',\n 7: 'END_FILE',\n 8: 'FILE_LOADED',\n 16: 'CLIENT_MESSAGE',\n 17: 'VIDEO_RECONFIG',\n 18: 'AUDIO_RECONFIG',\n 20: 'SEEK',\n 21: 'PLAYBACK_RESTART',\n 22: 'PROPERTY_CHANGE',\n 24: 'QUEUE_OVERFLOW',\n 25: 'HOOK'\n}\n\nconst LibmpvVideoView: React.ComponentType<LibmpvVideoViewProps> =\n requireNativeView('LibmpvVideo');\n\nexport const LibmpvVideo = React.forwardRef((props: any, parentRef: any) => {\n const nativeRef = React.useRef<any>(null);\n\n // Pass mpv events and logs back up to the parent\n const onLogEvent = (libmpvEvent: any) => {\n if (props.onLibmpvEvent) {\n if (libmpvEvent && libmpvEvent.nativeEvent) {\n libmpvEvent = libmpvEvent.nativeEvent\n }\n if (libmpvEvent.eventId) {\n libmpvEvent.value = parseInt(libmpvEvent.eventId, 10)\n libmpvEvent.eventKind = EVENT_LOOKUP[libmpvEvent.eventId]\n }\n else if (libmpvEvent.kind === 'long' || libmpvEvent.kind === 'double') {\n libmpvEvent.value = Number(libmpvEvent.value)\n }\n else if (libmpvEvent.kind === 'boolean') {\n libmpvEvent.value = libmpvEvent.value === 'true'\n }\n return props.onLibmpvEvent(libmpvEvent)\n }\n }\n const onLibmpvLog = (libmpvLog: any) => {\n if (props.onLibmpvLog) {\n if (libmpvLog && libmpvLog.nativeEvent) {\n libmpvLog = libmpvLog.nativeEvent\n }\n return props.onLibmpvLog(libmpvLog);\n }\n }\n\n // Allow a parent to call native methods, such as tweaking subtitle properties\n const callNativeMethod = (target: string) => {\n return (pipeDelimitedArguments: string) => {\n if (nativeRef.current) {\n if (target === 'runCommand') {\n nativeRef.current.runCommand(pipeDelimitedArguments)\n }\n else if (target === 'setOptionString') {\n nativeRef.current.setOptionString(pipeDelimitedArguments)\n }\n }\n }\n }\n React.useImperativeHandle(parentRef, () => ({\n runCommand: callNativeMethod(\"runCommand\"),\n setOptionString: callNativeMethod(\"setOptionString\")\n }));\n\n\n // The order props are handled in the native code is non-deterministic\n // Each native prop setter checks to see if all required props are set\n // Only then will it try to create an instance of mpv\n return <LibmpvVideoView\n ref={nativeRef}\n style={props.surfaceStyle ? props.surfaceStyle : styles.videoPlayer}\n playUrl={props.playUrl}\n isPlaying={props.isPlaying}\n useHardwareDecoder={props.useHardwareDecoder}\n surfaceWidth={props.surfaceWidth}\n surfaceHeight={props.surfaceHeight}\n selectedAudioTrack={props.selectedAudioTrack}\n selectedSubtitleTrack={props.selectedSubtitleTrack}\n seekToSeconds={props.seekToSeconds}\n onLibmpvEvent={onLogEvent}\n onLibmpvLog={onLibmpvLog}\n />\n})\n\nexport default LibmpvVideo\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,cAAc,qBAAqB,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,cAAc,qBAAqB,CAAC","sourcesContent":["export { default as LibmpvVideo } from './LibmpvVideoView';\nexport * from './LibmpvVideo.types';\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "expo-libmpv",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A libmpv native component for Android",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "expo-module build",
|
|
9
|
+
"clean": "expo-module clean",
|
|
10
|
+
"lint": "expo-module lint",
|
|
11
|
+
"test": "expo-module test",
|
|
12
|
+
"prepare": "expo-module prepare",
|
|
13
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
14
|
+
"expo-module": "expo-module",
|
|
15
|
+
"open:android": "open -a \"Android Studio\" example/android"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"react-native",
|
|
19
|
+
"expo",
|
|
20
|
+
"expo-libmpv",
|
|
21
|
+
"LibmpvVideo"
|
|
22
|
+
],
|
|
23
|
+
"repository": "https://github.com/XBigTK13X/expo-libmpv",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/XBigTK13X/expo-libmpv/issues"
|
|
26
|
+
},
|
|
27
|
+
"author": "XBigTK13X <xbigtk13x@gmail.com> (https://github.com/XBigTK13X)",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"homepage": "https://github.com/XBigTK13X/expo-libmpv#readme",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"expo-build-properties": "^0.14.8",
|
|
32
|
+
"expo-screen-orientation": "^8.1.7"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/react": "~19.0.0",
|
|
36
|
+
"expo": "~53.0.0",
|
|
37
|
+
"expo-module-scripts": "^4.1.10",
|
|
38
|
+
"react-native": "0.79.1"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"expo": "*",
|
|
42
|
+
"react": "*",
|
|
43
|
+
"react-native": "*"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/script/clean.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type LibmpvVideoViewProps = {
|
|
2
|
+
ref: any,
|
|
3
|
+
style: any,
|
|
4
|
+
playUrl: string,
|
|
5
|
+
isPlaying: boolean,
|
|
6
|
+
useHardwareDecoder: boolean,
|
|
7
|
+
selectedAudioTrack: number,
|
|
8
|
+
selectedSubtitleTrack: number,
|
|
9
|
+
seekToSeconds: number,
|
|
10
|
+
surfaceWidth: number,
|
|
11
|
+
surfaceHeight: number,
|
|
12
|
+
onLibmpvEvent: (libmpvEvent: any) => void,
|
|
13
|
+
onLibmpvLog: (libmpvLog: any) => void,
|
|
14
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import { LibmpvVideoViewProps } from './LibmpvVideo.types';
|
|
5
|
+
|
|
6
|
+
const styles: any = {
|
|
7
|
+
videoPlayer: {
|
|
8
|
+
position: "absolute",
|
|
9
|
+
left: 0,
|
|
10
|
+
bottom: 0,
|
|
11
|
+
right: 0,
|
|
12
|
+
top: 0
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const EVENT_LOOKUP: any = {
|
|
17
|
+
0: 'NONE',
|
|
18
|
+
1: 'SHUTDOWN',
|
|
19
|
+
2: 'LOG_MESSAGE',
|
|
20
|
+
3: 'GET_PROPERTY_REPLY',
|
|
21
|
+
4: 'SET_PROPERTY_REPLY',
|
|
22
|
+
5: 'COMMAND_REPLY',
|
|
23
|
+
6: 'START_FILE',
|
|
24
|
+
7: 'END_FILE',
|
|
25
|
+
8: 'FILE_LOADED',
|
|
26
|
+
16: 'CLIENT_MESSAGE',
|
|
27
|
+
17: 'VIDEO_RECONFIG',
|
|
28
|
+
18: 'AUDIO_RECONFIG',
|
|
29
|
+
20: 'SEEK',
|
|
30
|
+
21: 'PLAYBACK_RESTART',
|
|
31
|
+
22: 'PROPERTY_CHANGE',
|
|
32
|
+
24: 'QUEUE_OVERFLOW',
|
|
33
|
+
25: 'HOOK'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const LibmpvVideoView: React.ComponentType<LibmpvVideoViewProps> =
|
|
37
|
+
requireNativeView('LibmpvVideo');
|
|
38
|
+
|
|
39
|
+
export const LibmpvVideo = React.forwardRef((props: any, parentRef: any) => {
|
|
40
|
+
const nativeRef = React.useRef<any>(null);
|
|
41
|
+
|
|
42
|
+
// Pass mpv events and logs back up to the parent
|
|
43
|
+
const onLogEvent = (libmpvEvent: any) => {
|
|
44
|
+
if (props.onLibmpvEvent) {
|
|
45
|
+
if (libmpvEvent && libmpvEvent.nativeEvent) {
|
|
46
|
+
libmpvEvent = libmpvEvent.nativeEvent
|
|
47
|
+
}
|
|
48
|
+
if (libmpvEvent.eventId) {
|
|
49
|
+
libmpvEvent.value = parseInt(libmpvEvent.eventId, 10)
|
|
50
|
+
libmpvEvent.eventKind = EVENT_LOOKUP[libmpvEvent.eventId]
|
|
51
|
+
}
|
|
52
|
+
else if (libmpvEvent.kind === 'long' || libmpvEvent.kind === 'double') {
|
|
53
|
+
libmpvEvent.value = Number(libmpvEvent.value)
|
|
54
|
+
}
|
|
55
|
+
else if (libmpvEvent.kind === 'boolean') {
|
|
56
|
+
libmpvEvent.value = libmpvEvent.value === 'true'
|
|
57
|
+
}
|
|
58
|
+
return props.onLibmpvEvent(libmpvEvent)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const onLibmpvLog = (libmpvLog: any) => {
|
|
62
|
+
if (props.onLibmpvLog) {
|
|
63
|
+
if (libmpvLog && libmpvLog.nativeEvent) {
|
|
64
|
+
libmpvLog = libmpvLog.nativeEvent
|
|
65
|
+
}
|
|
66
|
+
return props.onLibmpvLog(libmpvLog);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Allow a parent to call native methods, such as tweaking subtitle properties
|
|
71
|
+
const callNativeMethod = (target: string) => {
|
|
72
|
+
return (pipeDelimitedArguments: string) => {
|
|
73
|
+
if (nativeRef.current) {
|
|
74
|
+
if (target === 'runCommand') {
|
|
75
|
+
nativeRef.current.runCommand(pipeDelimitedArguments)
|
|
76
|
+
}
|
|
77
|
+
else if (target === 'setOptionString') {
|
|
78
|
+
nativeRef.current.setOptionString(pipeDelimitedArguments)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
React.useImperativeHandle(parentRef, () => ({
|
|
84
|
+
runCommand: callNativeMethod("runCommand"),
|
|
85
|
+
setOptionString: callNativeMethod("setOptionString")
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
// The order props are handled in the native code is non-deterministic
|
|
90
|
+
// Each native prop setter checks to see if all required props are set
|
|
91
|
+
// Only then will it try to create an instance of mpv
|
|
92
|
+
return <LibmpvVideoView
|
|
93
|
+
ref={nativeRef}
|
|
94
|
+
style={props.surfaceStyle ? props.surfaceStyle : styles.videoPlayer}
|
|
95
|
+
playUrl={props.playUrl}
|
|
96
|
+
isPlaying={props.isPlaying}
|
|
97
|
+
useHardwareDecoder={props.useHardwareDecoder}
|
|
98
|
+
surfaceWidth={props.surfaceWidth}
|
|
99
|
+
surfaceHeight={props.surfaceHeight}
|
|
100
|
+
selectedAudioTrack={props.selectedAudioTrack}
|
|
101
|
+
selectedSubtitleTrack={props.selectedSubtitleTrack}
|
|
102
|
+
seekToSeconds={props.seekToSeconds}
|
|
103
|
+
onLibmpvEvent={onLogEvent}
|
|
104
|
+
onLibmpvLog={onLibmpvLog}
|
|
105
|
+
/>
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
export default LibmpvVideo
|
package/src/index.ts
ADDED
package/tsconfig.json
ADDED