expo-mpv 0.1.7 → 0.1.8

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.
@@ -1,9 +1,6 @@
1
1
  package expo.modules.mpv
2
2
 
3
3
  import android.content.Context
4
- import android.os.Build
5
- import android.os.Handler
6
- import android.os.Looper
7
4
  import android.view.SurfaceHolder
8
5
  import android.view.SurfaceView
9
6
  import android.view.View
@@ -12,9 +9,6 @@ import expo.modules.kotlin.viewevent.EventDispatcher
12
9
  import expo.modules.kotlin.views.ExpoView
13
10
 
14
11
  class ExpoMpvView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
15
-
16
- // MARK: - Event Dispatchers
17
-
18
12
  private val onPlaybackStateChange by EventDispatcher()
19
13
  private val onProgress by EventDispatcher()
20
14
  private val onLoad by EventDispatcher()
@@ -24,453 +18,200 @@ class ExpoMpvView(context: Context, appContext: AppContext) : ExpoView(context,
24
18
  private val onSeek by EventDispatcher()
25
19
  private val onVolumeChange by EventDispatcher()
26
20
 
27
- // MARK: - State
28
-
29
- private var nativePtr: Long = 0
30
- private var isInitialized = false
31
- private var pendingSource: String? = null
32
- private var pendingHwdec: String = if (isEmulator()) "no" else "mediacodec"
33
- private val mainHandler = Handler(Looper.getMainLooper())
34
- private var progressRunnable: Runnable? = null
35
-
36
- // Cached property values from observe callbacks (thread-safe reads from main thread)
37
- @Volatile private var cachedTimePos: Double = 0.0
38
- @Volatile private var cachedDuration: Double = 0.0
39
- @Volatile private var cachedCacheDuration: Double = 0.0
40
- @Volatile private var cachedPause: Boolean = false
41
- @Volatile private var cachedVolume: Double = 100.0
42
- @Volatile private var cachedMute: Boolean = false
43
- @Volatile private var cachedSpeed: Double = 1.0
44
- @Volatile private var cachedVideoW: Long = 0
45
- @Volatile private var cachedVideoH: Long = 0
46
-
47
- // MARK: - Surface
48
-
49
- private val surfaceView: SurfaceView = SurfaceView(context).apply {
21
+ private var isDestroyed = false
22
+
23
+ private val surfaceView = SurfaceView(context).apply {
50
24
  layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
51
25
  }
52
26
 
53
- // MARK: - Init
27
+ private val player = MpvPlayer(
28
+ context,
29
+ object : MpvPlayer.Listener {
30
+ override fun onPlaybackStateChange(state: String, isPlaying: Boolean) {
31
+ onPlaybackStateChange(
32
+ mapOf(
33
+ "state" to state,
34
+ "isPlaying" to isPlaying,
35
+ )
36
+ )
37
+ }
54
38
 
55
- init {
56
- addView(surfaceView)
57
- setBackgroundColor(0xFF000000.toInt())
39
+ override fun onProgress(position: Double, duration: Double, bufferedDuration: Double) {
40
+ onProgress(
41
+ mapOf(
42
+ "position" to position,
43
+ "duration" to duration,
44
+ "bufferedDuration" to bufferedDuration,
45
+ )
46
+ )
47
+ }
58
48
 
59
- // Create mpv and set options (but don't initialize yet need surface first)
60
- createMpv()
61
-
62
- surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
63
- override fun surfaceCreated(holder: SurfaceHolder) {
64
- if (nativePtr == 0L) return
65
- if (!isInitialized) {
66
- // First time: attach surface BEFORE mpv_initialize
67
- MPVLib.nativeAttachSurface(nativePtr, holder.surface)
68
- initializeMpv()
69
- } else {
70
- // Surface recreated: reattach AFTER mpv_initialize
71
- MPVLib.nativeReattachSurface(nativePtr, holder.surface)
72
- }
73
- pendingSource?.let { src ->
74
- loadFile(src)
75
- pendingSource = null
76
- }
49
+ override fun onLoad(duration: Double, width: Int, height: Int) {
50
+ onLoad(
51
+ mapOf(
52
+ "duration" to duration,
53
+ "width" to width,
54
+ "height" to height,
55
+ )
56
+ )
77
57
  }
78
58
 
79
- override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
59
+ override fun onError(message: String) {
60
+ onError(mapOf("error" to message))
61
+ }
62
+
63
+ override fun onEnd(reason: String) {
64
+ onEnd(mapOf("reason" to reason))
65
+ }
80
66
 
81
- override fun surfaceDestroyed(holder: SurfaceHolder) {
82
- if (nativePtr != 0L && isInitialized) {
83
- MPVLib.nativeDetachSurface(nativePtr)
84
- }
67
+ override fun onBuffer(isBuffering: Boolean) {
68
+ onBuffer(mapOf("isBuffering" to isBuffering))
85
69
  }
86
- })
70
+
71
+ override fun onSeek() {
72
+ onSeek(emptyMap<String, Any>())
73
+ }
74
+
75
+ override fun onVolumeChange(volume: Double, muted: Boolean) {
76
+ onVolumeChange(
77
+ mapOf(
78
+ "volume" to volume,
79
+ "muted" to muted,
80
+ )
81
+ )
82
+ }
83
+ },
84
+ )
85
+
86
+ private val surfaceCallback = object : SurfaceHolder.Callback {
87
+ override fun surfaceCreated(holder: SurfaceHolder) {
88
+ player.setSurface(holder.surface)
89
+ }
90
+
91
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) = Unit
92
+
93
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
94
+ player.setSurface(null)
95
+ }
96
+ }
97
+
98
+ init {
99
+ addView(surfaceView)
100
+ setBackgroundColor(0xFF000000.toInt())
101
+ surfaceView.holder.addCallback(surfaceCallback)
87
102
  }
88
103
 
89
104
  override fun onDetachedFromWindow() {
90
- super.onDetachedFromWindow()
91
105
  destroy()
106
+ super.onDetachedFromWindow()
92
107
  }
93
108
 
94
109
  override fun onWindowVisibilityChanged(visibility: Int) {
95
110
  super.onWindowVisibilityChanged(visibility)
96
- if (nativePtr != 0L && isInitialized) {
97
- if (visibility == View.GONE || visibility == View.INVISIBLE) {
98
- setPropertyString("vid", "no")
99
- } else {
100
- setPropertyString("vid", "auto")
101
- }
111
+ if (visibility == View.GONE || visibility == View.INVISIBLE) {
112
+ player.setPropertyString("vid", "no")
113
+ } else {
114
+ player.setPropertyString("vid", "auto")
102
115
  }
103
116
  }
104
117
 
105
118
  fun destroy() {
106
- stopProgressTimer()
107
- val ptr = nativePtr
108
- nativePtr = 0
109
- isInitialized = false
110
- if (ptr != 0L) {
111
- // Destroy on background thread to avoid blocking the main thread
112
- // (pthread_join + mpv_terminate_destroy can take seconds)
113
- Thread {
114
- MPVLib.nativeDestroy(ptr)
115
- }.start()
116
- }
119
+ if (isDestroyed) return
120
+ isDestroyed = true
121
+ surfaceView.holder.removeCallback(surfaceCallback)
122
+ player.setSurface(null)
123
+ player.release()
117
124
  }
118
125
 
119
- // MARK: - MPV Setup (two-phase: create+options, then initialize after surface)
120
-
121
- private fun createMpv() {
122
- nativePtr = MPVLib.nativeCreate()
123
- if (nativePtr == 0L) {
124
- onError(mapOf("error" to "Failed to create mpv instance"))
125
- return
126
- }
127
-
128
- MPVLib.nativeSetCallback(nativePtr, this)
129
-
130
- // Rendering
131
- MPVLib.nativeSetOptionString(nativePtr, "vo", "gpu")
132
- MPVLib.nativeSetOptionString(nativePtr, "gpu-context", "android")
133
- MPVLib.nativeSetOptionString(nativePtr, "hwdec", pendingHwdec)
134
- if (pendingHwdec != "no") {
135
- MPVLib.nativeSetOptionString(nativePtr, "hwdec-codecs", "h264,hevc,mpeg4,mpeg2video,vp8,vp9,av1")
136
- }
137
-
138
- if (isEmulator()) {
139
- android.util.Log.w("ExpoMpv", "Emulator detected, hardware decoding disabled (using software decoding)")
140
- }
141
-
142
- // Network stream caching
143
- MPVLib.nativeSetOptionString(nativePtr, "cache", "yes")
144
- MPVLib.nativeSetOptionString(nativePtr, "demuxer-max-bytes", "150M")
145
- MPVLib.nativeSetOptionString(nativePtr, "demuxer-max-back-bytes", "50M")
146
- MPVLib.nativeSetOptionString(nativePtr, "cache-pause", "yes")
147
-
148
- // General
149
- MPVLib.nativeSetOptionString(nativePtr, "force-window", "yes")
150
- MPVLib.nativeSetOptionString(nativePtr, "keep-open", "yes")
151
- MPVLib.nativeSetOptionString(nativePtr, "idle", "yes")
152
- MPVLib.nativeSetOptionString(nativePtr, "input-default-bindings", "no")
153
- MPVLib.nativeSetOptionString(nativePtr, "input-vo-keyboard", "no")
126
+ fun loadFile(url: String) {
127
+ player.loadFile(url)
154
128
  }
155
129
 
156
- private fun initializeMpv() {
157
- if (nativePtr == 0L) return
130
+ fun play() {
131
+ player.play()
132
+ }
158
133
 
159
- val ret = MPVLib.nativeInitialize(nativePtr)
160
- if (ret < 0) {
161
- onError(mapOf("error" to "mpv_initialize failed: $ret"))
162
- return
163
- }
134
+ fun pause() {
135
+ player.pause()
136
+ }
164
137
 
165
- isInitialized = true
166
-
167
- // Observe properties
168
- MPVLib.nativeObserveProperty(nativePtr, "pause", MPVLib.FORMAT_FLAG)
169
- MPVLib.nativeObserveProperty(nativePtr, "duration", MPVLib.FORMAT_DOUBLE)
170
- MPVLib.nativeObserveProperty(nativePtr, "time-pos", MPVLib.FORMAT_DOUBLE)
171
- MPVLib.nativeObserveProperty(nativePtr, "paused-for-cache", MPVLib.FORMAT_FLAG)
172
- MPVLib.nativeObserveProperty(nativePtr, "eof-reached", MPVLib.FORMAT_FLAG)
173
- MPVLib.nativeObserveProperty(nativePtr, "volume", MPVLib.FORMAT_DOUBLE)
174
- MPVLib.nativeObserveProperty(nativePtr, "mute", MPVLib.FORMAT_FLAG)
175
- MPVLib.nativeObserveProperty(nativePtr, "speed", MPVLib.FORMAT_DOUBLE)
176
- MPVLib.nativeObserveProperty(nativePtr, "demuxer-cache-duration", MPVLib.FORMAT_DOUBLE)
177
- MPVLib.nativeObserveProperty(nativePtr, "video-params/w", MPVLib.FORMAT_INT64)
178
- MPVLib.nativeObserveProperty(nativePtr, "video-params/h", MPVLib.FORMAT_INT64)
138
+ fun togglePlay() {
139
+ player.togglePlay()
179
140
  }
180
141
 
181
- // MARK: - JNI Callbacks (called from event thread)
182
-
183
- fun onEvent(eventId: Int) {
184
- when (eventId) {
185
- MPVLib.EVENT_FILE_LOADED -> mainHandler.post {
186
- onLoad(mapOf(
187
- "duration" to (if (cachedDuration.isFinite()) cachedDuration else 0.0),
188
- "width" to cachedVideoW.toInt(),
189
- "height" to cachedVideoH.toInt()
190
- ))
191
- startProgressTimer()
192
- }
193
- MPVLib.EVENT_SEEK -> mainHandler.post {
194
- onSeek(emptyMap<String, Any>())
195
- }
196
- MPVLib.EVENT_SHUTDOWN -> {
197
- isInitialized = false
198
- }
199
- }
142
+ fun stop() {
143
+ player.stop()
200
144
  }
201
145
 
202
- fun onPropertyChange(name: String, value: Any?) {
203
- when (name) {
204
- "time-pos" -> cachedTimePos = (value as? Double) ?: 0.0
205
- "duration" -> cachedDuration = (value as? Double) ?: 0.0
206
- "demuxer-cache-duration" -> cachedCacheDuration = (value as? Double) ?: 0.0
207
- "pause" -> cachedPause = (value as? Boolean) ?: false
208
- "volume" -> cachedVolume = (value as? Double) ?: 0.0
209
- "mute" -> cachedMute = (value as? Boolean) ?: false
210
- "speed" -> cachedSpeed = (value as? Double) ?: 1.0
211
- "video-params/w" -> cachedVideoW = (value as? Long) ?: 0
212
- "video-params/h" -> cachedVideoH = (value as? Long) ?: 0
213
- }
214
- mainHandler.post {
215
- when (name) {
216
- "pause" -> {
217
- val paused = (value as? Boolean) ?: false
218
- onPlaybackStateChange(mapOf(
219
- "state" to if (paused) "paused" else "playing",
220
- "isPlaying" to !paused
221
- ))
222
- if (paused) stopProgressTimer() else startProgressTimer()
223
- }
224
- "paused-for-cache" -> {
225
- val buffering = value as? Boolean ?: false
226
- onBuffer(mapOf("isBuffering" to buffering))
227
- }
228
- "volume", "mute" -> {
229
- onVolumeChange(mapOf("volume" to cachedVolume, "muted" to cachedMute))
230
- }
231
- }
232
- }
146
+ fun seekTo(position: Double) {
147
+ player.seekTo(position)
233
148
  }
234
149
 
235
- fun onEndFile(reason: String, error: String) {
236
- mainHandler.post {
237
- stopProgressTimer()
238
- if (reason == "error" && error.isNotEmpty()) {
239
- onError(mapOf("error" to "Playback error: $error"))
240
- }
241
- onEnd(mapOf("reason" to reason))
242
- }
150
+ fun seekBy(offset: Double) {
151
+ player.seekBy(offset)
243
152
  }
244
153
 
245
- fun onLogMessage(prefix: String, level: String, text: String) {
246
- android.util.Log.d("ExpoMpv", "[$prefix] [$level] $text")
154
+ fun setSpeed(speed: Double) {
155
+ player.setSpeed(speed)
247
156
  }
248
157
 
249
- // MARK: - Progress Timer
158
+ fun setVolume(volume: Double) {
159
+ player.setVolume(volume)
160
+ }
250
161
 
251
- private fun startProgressTimer() {
252
- stopProgressTimer()
253
- progressRunnable = object : Runnable {
254
- override fun run() {
255
- emitProgressEvent()
256
- mainHandler.postDelayed(this, 250)
257
- }
258
- }
259
- mainHandler.post(progressRunnable!!)
162
+ fun setMuted(muted: Boolean) {
163
+ player.setMuted(muted)
260
164
  }
261
165
 
262
- private fun stopProgressTimer() {
263
- progressRunnable?.let { mainHandler.removeCallbacks(it) }
264
- progressRunnable = null
166
+ fun setLooping(loop: Boolean) {
167
+ player.setLooping(loop)
265
168
  }
266
169
 
267
- private fun emitProgressEvent() {
268
- if (nativePtr == 0L) return
269
- val position = cachedTimePos
270
- val duration = cachedDuration
271
- val cachedDur = cachedCacheDuration
272
- if (!position.isFinite() || !duration.isFinite()) return
273
- onProgress(mapOf(
274
- "position" to position,
275
- "duration" to duration,
276
- "bufferedDuration" to (if (cachedDur.isFinite()) cachedDur else 0.0)
277
- ))
170
+ fun setHwdec(mode: String) {
171
+ player.setHwdec(mode)
278
172
  }
279
173
 
280
- // MARK: - Public API
174
+ fun setSubtitleTrack(trackId: Int) {
175
+ player.setSubtitleTrack(trackId)
176
+ }
281
177
 
282
- fun loadFile(url: String) {
283
- if (!isInitialized || nativePtr == 0L) {
284
- pendingSource = url
285
- return
286
- }
287
- if (!surfaceView.holder.surface.isValid) {
288
- pendingSource = url
289
- return
290
- }
291
- command("loadfile", url, "replace")
178
+ fun setAudioTrack(trackId: Int) {
179
+ player.setAudioTrack(trackId)
292
180
  }
293
181
 
294
- fun play() { setPropertyBoolean("pause", false) }
295
- fun pause() { setPropertyBoolean("pause", true) }
296
- fun togglePlay() {
297
- val isPaused = getPropertyBoolean("pause")
298
- setPropertyBoolean("pause", !isPaused)
182
+ fun addSubtitle(path: String, flag: String, title: String?, lang: String?) {
183
+ player.addSubtitle(path, flag, title, lang)
299
184
  }
300
- fun stop() { command("stop"); stopProgressTimer() }
301
- fun seekTo(position: Double) { command("seek", position.toString(), "absolute") }
302
- fun seekBy(offset: Double) { command("seek", offset.toString(), "relative") }
303
- fun setSpeed(speed: Double) { setPropertyDouble("speed", speed) }
304
- fun setVolume(volume: Double) { setPropertyDouble("volume", volume) }
305
- fun setMuted(muted: Boolean) { setPropertyBoolean("mute", muted) }
306
- fun setLooping(loop: Boolean) { setPropertyString("loop-file", if (loop) "inf" else "no") }
307
185
 
308
- fun setHwdec(mode: String) {
309
- if (isInitialized && nativePtr != 0L) {
310
- // Runtime change — set as property
311
- setPropertyString("hwdec", mode)
312
- }
313
- // Also store for next createMpv if view is recreated
314
- pendingHwdec = mode
186
+ fun removeSubtitle(trackId: Int) {
187
+ player.removeSubtitle(trackId)
315
188
  }
316
- fun setSubtitleTrack(trackId: Int) { setPropertyLong("sid", trackId.toLong()) }
317
- fun setAudioTrack(trackId: Int) { setPropertyLong("aid", trackId.toLong()) }
318
189
 
319
- fun addSubtitle(path: String, flag: String, title: String?, lang: String?) {
320
- val args = mutableListOf(path, flag)
321
- if (title != null) args.add(title) else if (lang != null) args.add("")
322
- if (lang != null) args.add(lang)
323
- commandArray("sub-add", args)
190
+ fun reloadSubtitles() {
191
+ player.reloadSubtitles()
324
192
  }
325
193
 
326
- fun removeSubtitle(trackId: Int) { command("sub-remove", trackId.toString()) }
327
- fun reloadSubtitles() { command("sub-reload") }
328
- fun setSubtitleDelay(seconds: Double) { setPropertyDouble("sub-delay", seconds) }
194
+ fun setSubtitleDelay(seconds: Double) {
195
+ player.setSubtitleDelay(seconds)
196
+ }
329
197
 
330
198
  fun setPropertyString(name: String, value: String) {
331
- if (nativePtr == 0L) return
332
- MPVLib.nativeSetPropertyString(nativePtr, name, value)
199
+ player.setPropertyString(name, value)
333
200
  }
334
201
 
335
202
  fun getPlaybackInfo(): Map<String, Any> {
336
- return mapOf(
337
- "position" to (if (cachedTimePos.isFinite()) cachedTimePos else 0.0),
338
- "duration" to (if (cachedDuration.isFinite()) cachedDuration else 0.0),
339
- "isPlaying" to !cachedPause,
340
- "speed" to cachedSpeed,
341
- "volume" to cachedVolume,
342
- "muted" to cachedMute
343
- )
203
+ return player.getPlaybackInfo()
344
204
  }
345
205
 
346
206
  fun getTrackList(): List<Map<String, Any>> {
347
- if (nativePtr == 0L) return emptyList()
348
- val count = getPropertyLong("track-list/count").toInt()
349
- val tracks = mutableListOf<Map<String, Any>>()
350
- for (i in 0 until count) {
351
- val prefix = "track-list/$i"
352
- val type = getPropertyString("$prefix/type") ?: "unknown"
353
- val track = mutableMapOf<String, Any>(
354
- "id" to getPropertyLong("$prefix/id").toInt(),
355
- "type" to type,
356
- "title" to (getPropertyString("$prefix/title") ?: ""),
357
- "lang" to (getPropertyString("$prefix/lang") ?: ""),
358
- "codec" to (getPropertyString("$prefix/codec") ?: ""),
359
- "selected" to getPropertyBoolean("$prefix/selected"),
360
- "isDefault" to getPropertyBoolean("$prefix/default"),
361
- "isExternal" to getPropertyBoolean("$prefix/external")
362
- )
363
- when (type) {
364
- "audio" -> {
365
- track["channelCount"] = getPropertyLong("$prefix/demux-channel-count").toInt()
366
- track["sampleRate"] = getPropertyLong("$prefix/demux-samplerate").toInt()
367
- }
368
- "video" -> {
369
- track["width"] = getPropertyLong("$prefix/demux-w").toInt()
370
- track["height"] = getPropertyLong("$prefix/demux-h").toInt()
371
- track["fps"] = getPropertyDouble("$prefix/demux-fps")
372
- }
373
- }
374
- tracks.add(track)
375
- }
376
- return tracks
207
+ return player.getTrackList()
377
208
  }
378
209
 
379
210
  fun getCurrentTrackIds(): Map<String, Int> {
380
- if (nativePtr == 0L) return emptyMap()
381
- return mapOf(
382
- "vid" to getPropertyLong("vid").toInt(),
383
- "aid" to getPropertyLong("aid").toInt(),
384
- "sid" to getPropertyLong("sid").toInt()
385
- )
211
+ return player.getCurrentTrackIds()
386
212
  }
387
213
 
388
214
  fun getMediaInfo(): Map<String, Any> {
389
- if (nativePtr == 0L) return emptyMap()
390
-
391
- val hwdec = getPropertyString("hwdec") ?: ""
392
- val hwdecCurrent = getPropertyString("hwdec-current") ?: ""
393
- val videoCodec = getPropertyString("video-codec") ?: ""
394
- val audioCodec = getPropertyString("audio-codec-name") ?: ""
395
- val width = getPropertyLong("video-params/w").toInt()
396
- val height = getPropertyLong("video-params/h").toInt()
397
- val fps = getPropertyDouble("container-fps")
398
- val videoBitrate = getPropertyDouble("video-bitrate")
399
- val audioBitrate = getPropertyDouble("audio-bitrate")
400
- val pixelFormat = getPropertyString("video-params/pixelformat") ?: ""
401
- val colorspace = getPropertyString("video-params/colormatrix") ?: ""
402
-
403
- return mapOf(
404
- "hwdec" to hwdec,
405
- "hwdecCurrent" to hwdecCurrent,
406
- "videoCodec" to videoCodec,
407
- "audioCodec" to audioCodec,
408
- "width" to width,
409
- "height" to height,
410
- "fps" to (if (fps.isFinite()) fps else 0.0),
411
- "videoBitrate" to (if (videoBitrate.isFinite()) videoBitrate else 0.0),
412
- "audioBitrate" to (if (audioBitrate.isFinite()) audioBitrate else 0.0),
413
- "pixelFormat" to pixelFormat,
414
- "colorspace" to colorspace
415
- )
416
- }
417
-
418
- // MARK: - Property Helpers
419
-
420
- private fun getPropertyDouble(name: String): Double {
421
- if (nativePtr == 0L) return 0.0
422
- return MPVLib.nativeGetPropertyDouble(nativePtr, name)
423
- }
424
- private fun getPropertyLong(name: String): Long {
425
- if (nativePtr == 0L) return 0
426
- return MPVLib.nativeGetPropertyLong(nativePtr, name)
427
- }
428
- private fun getPropertyBoolean(name: String): Boolean {
429
- if (nativePtr == 0L) return false
430
- return MPVLib.nativeGetPropertyBoolean(nativePtr, name)
431
- }
432
- private fun getPropertyString(name: String): String? {
433
- if (nativePtr == 0L) return null
434
- return MPVLib.nativeGetPropertyString(nativePtr, name)
435
- }
436
- private fun setPropertyDouble(name: String, value: Double) {
437
- if (nativePtr == 0L) return
438
- MPVLib.nativeSetPropertyDouble(nativePtr, name, value)
439
- }
440
- private fun setPropertyLong(name: String, value: Long) {
441
- if (nativePtr == 0L) return
442
- MPVLib.nativeSetPropertyLong(nativePtr, name, value)
443
- }
444
- private fun setPropertyBoolean(name: String, value: Boolean) {
445
- if (nativePtr == 0L) return
446
- MPVLib.nativeSetPropertyBoolean(nativePtr, name, value)
447
- }
448
- private fun command(vararg args: String) {
449
- if (nativePtr == 0L) return
450
- MPVLib.nativeCommand(nativePtr, args.toList().toTypedArray())
451
- }
452
- private fun commandArray(cmd: String, args: List<String>) {
453
- if (nativePtr == 0L) return
454
- MPVLib.nativeCommand(nativePtr, (listOf(cmd) + args).toTypedArray())
455
- }
456
-
457
- companion object {
458
- private fun isEmulator(): Boolean {
459
- return (Build.FINGERPRINT.startsWith("generic")
460
- || Build.FINGERPRINT.startsWith("unknown")
461
- || Build.MODEL.contains("google_sdk")
462
- || Build.MODEL.contains("Emulator")
463
- || Build.MODEL.contains("Android SDK built for x86")
464
- || Build.BOARD == "QC_Reference_Phone"
465
- || Build.MANUFACTURER.contains("Genymotion")
466
- || Build.HOST.startsWith("Build")
467
- || Build.BRAND.startsWith("generic")
468
- || Build.DEVICE.startsWith("generic")
469
- || Build.PRODUCT == "google_sdk"
470
- || Build.PRODUCT.startsWith("sdk")
471
- || Build.PRODUCT.endsWith("_cf")
472
- || Build.HARDWARE.contains("goldfish")
473
- || Build.HARDWARE.contains("ranchu"))
474
- }
215
+ return player.getMediaInfo()
475
216
  }
476
217
  }