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