expo-libmpv 0.5.3 → 0.5.4

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.
@@ -36,7 +36,7 @@ if (useManagedAndroidSdkVersions) {
36
36
  }
37
37
 
38
38
  dependencies {
39
- implementation "com.libmpv:android-libmpv:0.7.3"
39
+ implementation "com.libmpv:android-libmpv:0.7.4"
40
40
  }
41
41
 
42
42
  android {
@@ -27,6 +27,7 @@ class LibmpvRenderer(
27
27
 
28
28
  companion object {
29
29
  private const val TAG = "expo-libmpv"
30
+ private const val LOG_LEVEL_INFO = 20
30
31
  private const val LOG_LEVEL_WARN = 30
31
32
  }
32
33
 
@@ -35,22 +36,32 @@ class LibmpvRenderer(
35
36
  @Volatile private var state: State = State.NEW
36
37
  @Volatile private var destroyed = false
37
38
  @Volatile private var loadedUrl: String? = null
39
+ @Volatile private var surfaceAttached = false
40
+ @Volatile var surfaceReady = false
38
41
 
39
42
  private var mpvDirectory: String? = null
40
43
 
41
44
  private inline val mpvAlive: Boolean
42
- get() = when (state) {
43
- State.CREATED, State.INITIALIZED, State.ACTIVE -> true
44
- else -> false
45
+ get() {
46
+ return when (state) {
47
+ State.CREATED,
48
+ State.INITIALIZED,
49
+ State.ACTIVE -> true
50
+ else -> false
51
+ }
45
52
  }
46
53
 
47
54
  private inline val shuttingDown: Boolean
48
- get() = state == State.SHUTTING_DOWN || state == State.DESTROYED
55
+ get() {
56
+ return state == State.SHUTTING_DOWN || state == State.DESTROYED
57
+ }
49
58
 
50
59
  private val mainHandler = Handler(Looper.getMainLooper())
51
60
 
52
61
  fun runCommand(command: Array<String>) {
53
- if (!mpvAlive || shuttingDown) return
62
+ if (!mpvAlive || shuttingDown) {
63
+ return
64
+ }
54
65
  try {
55
66
  MPVLib.command(command)
56
67
  } catch (e: Exception) {
@@ -59,7 +70,9 @@ class LibmpvRenderer(
59
70
  }
60
71
 
61
72
  fun setOptionString(option: String, value: String) {
62
- if (!mpvAlive || shuttingDown) return
73
+ if (!mpvAlive || shuttingDown) {
74
+ return
75
+ }
63
76
  try {
64
77
  MPVLib.setOptionString(option, value)
65
78
  } catch (e: Exception) {
@@ -67,70 +80,104 @@ class LibmpvRenderer(
67
80
  }
68
81
  }
69
82
 
70
-
71
83
  fun start() {
72
84
  synchronized(stateLock) {
73
- if (state != State.NEW && state != State.DESTROYED) return
74
85
  try {
75
86
  MPVLib.create(surfaceView.context.applicationContext)
76
87
  createMpvDirectory()
77
88
  state = State.CREATED
78
89
  } catch (e: Exception) {
79
90
  logException(e)
80
- return
81
91
  }
82
92
  }
83
-
84
- initMpv()
85
- attachSurfaceAndConfigure()
86
93
  }
87
94
 
88
95
  fun destroy() {
89
- val doTeardown: Boolean
90
- synchronized(stateLock) {
91
- doTeardown = when (state) {
92
- State.NEW, State.SHUTTING_DOWN, State.DESTROYED -> false
93
- else -> true
96
+ val doTeardown: Boolean
97
+ synchronized(stateLock) {
98
+ doTeardown = when (state) {
99
+ State.NEW,
100
+ State.SHUTTING_DOWN,
101
+ State.DESTROYED -> false
102
+ else -> true
103
+ }
104
+ if (doTeardown) {
105
+ state = State.SHUTTING_DOWN
106
+ }
107
+ }
108
+ if (!doTeardown) {
109
+ return
94
110
  }
95
- if (doTeardown) state = State.SHUTTING_DOWN
96
- }
97
- if (!doTeardown) return
98
-
99
- destroyed = true
100
111
 
101
- try {
102
- loadedUrl = null
103
- stopPlayback()
104
- detachSurfaceInternal()
112
+ destroyed = true
105
113
 
106
- mainHandler.post {
107
- try {
108
- MPVLib.destroy()
109
- } finally {
110
- synchronized(stateLock) {
111
- state = State.DESTROYED
114
+ try {
115
+ loadedUrl = null
116
+ stopPlayback()
117
+
118
+ mainHandler.post {
119
+ try {
120
+ detachSurfaceForDestroy()
121
+ MPVLib.destroy()
122
+ } finally {
123
+ synchronized(stateLock) {
124
+ state = State.DESTROYED
125
+ }
112
126
  }
113
127
  }
128
+ } catch (e: Exception) {
129
+ logException(e)
130
+ synchronized(stateLock) {
131
+ state = State.DESTROYED
132
+ }
133
+ }
134
+ }
135
+
136
+ fun attachSurfaceIfNeeded() {
137
+ if (!surfaceReady || surfaceAttached) {
138
+ return
114
139
  }
115
- } catch (e: Exception) {
116
- logException(e)
140
+
141
+ MPVLib.attachSurface(surfaceView.holder.surface)
142
+ surfaceAttached = true
143
+
117
144
  synchronized(stateLock) {
118
- state = State.DESTROYED
145
+ MPVLib.init()
146
+ state = State.INITIALIZED
119
147
  }
148
+
149
+ addObservers()
150
+ applyConfiguration()
151
+ state = State.ACTIVE
152
+ maybeStartPlayback()
120
153
  }
121
- }
122
154
 
155
+ private fun detachSurfaceForDestroy() {
156
+ if (!surfaceAttached) {
157
+ return
158
+ }
159
+ try {
160
+ MPVLib.detachSurface()
161
+ } catch (e: Exception) {
162
+ logException(e)
163
+ } finally {
164
+ surfaceAttached = false
165
+ }
166
+ }
123
167
 
124
168
  fun onSessionUpdated() {
125
- if (!mpvAlive || shuttingDown) return
126
- maybeStartPlayback()
169
+ if (!mpvAlive || shuttingDown) {
170
+ return
171
+ }
127
172
  applyDeferredState()
128
173
  applyContinuousState()
129
174
  }
130
175
 
131
176
  private fun initMpv() {
132
177
  synchronized(stateLock) {
133
- if (shuttingDown || state != State.CREATED) return
178
+ if (shuttingDown || state != State.CREATED) {
179
+ return
180
+ }
134
181
  try {
135
182
  MPVLib.init()
136
183
  state = State.INITIALIZED
@@ -142,25 +189,10 @@ class LibmpvRenderer(
142
189
  addObservers()
143
190
  }
144
191
 
145
- private fun attachSurfaceAndConfigure() {
146
- if (!mpvAlive || shuttingDown) return
147
- try {
148
- MPVLib.attachSurface(surfaceView.holder.surface)
149
- synchronized(stateLock) {
150
- if (state == State.CREATED || state == State.INITIALIZED) {
151
- state = State.ACTIVE
152
- }
153
- }
154
- applyConfiguration()
155
- MPVLib.setPropertyString("pause", "yes")
156
- maybeStartPlayback()
157
- } catch (e: Exception) {
158
- logException(e)
159
- }
160
- }
161
-
162
192
  private fun addObservers() {
163
- if (!mpvAlive || shuttingDown) return
193
+ if (!mpvAlive || shuttingDown) {
194
+ return
195
+ }
164
196
  try {
165
197
  MPVLib.removeObservers()
166
198
  MPVLib.addObserver(this)
@@ -181,19 +213,23 @@ class LibmpvRenderer(
181
213
  }
182
214
 
183
215
  private fun applyConfiguration() {
184
- if (!mpvAlive || shuttingDown) return
216
+ if (!mpvAlive || shuttingDown) {
217
+ return
218
+ }
185
219
  try {
186
- MPVLib.setOptionString("force-window", "no")
220
+ MPVLib.setOptionString("force-window", "yes")
187
221
  MPVLib.setOptionString("config", "yes")
188
- mpvDirectory?.let {
189
- MPVLib.setOptionString("config-dir", it)
190
- MPVLib.setOptionString("sub-font-dir", it)
222
+
223
+ mpvDirectory?.let { mpvDir ->
224
+ val fontsPath = "$mpvDir/fonts"
225
+ MPVLib.setOptionString("config-dir", mpvDir)
226
+ MPVLib.setOptionString("sub-fonts-dir", fontsPath)
227
+ MPVLib.setOptionString("sub-font", "Droid Sans Fallback")
191
228
  }
192
229
 
193
230
  MPVLib.setOptionString("keep-open", "always")
194
231
  MPVLib.setOptionString("save-position-on-quit", "no")
195
232
  MPVLib.setOptionString("ytdl", "no")
196
- MPVLib.setOptionString("msg-level", "all=no")
197
233
 
198
234
  session.videoOutput?.let { videoOutput ->
199
235
  MPVLib.setOptionString("vo", videoOutput)
@@ -203,7 +239,7 @@ class LibmpvRenderer(
203
239
  val acceleratedCodecs = session.acceleratedCodecs
204
240
  if (!decodingMode.isNullOrBlank() && !acceleratedCodecs.isNullOrBlank()) {
205
241
  MPVLib.setOptionString("hwdec", decodingMode)
206
- if (decodingMode != "no"){
242
+ if (decodingMode != "no") {
207
243
  MPVLib.setOptionString("hwdec-codecs", acceleratedCodecs)
208
244
  }
209
245
  }
@@ -212,48 +248,56 @@ class LibmpvRenderer(
212
248
  MPVLib.setOptionString("opengl-es", "yes")
213
249
 
214
250
  val videoSync = session.videoSync
215
- if(!videoSync.isNullOrBlank()){
216
- if(videoSync == "display-resample"){
251
+ if (!videoSync.isNullOrBlank()) {
252
+ if (videoSync == "display-resample") {
217
253
  MPVLib.setOptionString("video-sync", "display-resample")
218
- MPVLib.setOptionString("audio-pitch-correction","no")
254
+ MPVLib.setOptionString("audio-pitch-correction", "no")
219
255
  MPVLib.setOptionString("autosync", "1")
220
- MPVLib.setOptionString("correct-pts","no")
256
+ MPVLib.setOptionString("correct-pts", "no")
221
257
  }
222
- if(videoSync == "audio"){
258
+ if (videoSync == "audio") {
223
259
  MPVLib.setOptionString("video-sync", "audio")
224
- MPVLib.setOptionString("audio-pitch-correction","yes")
260
+ MPVLib.setOptionString("audio-pitch-correction", "yes")
225
261
  MPVLib.setOptionString("autosync", "0")
226
- MPVLib.setOptionString("correct-pts","yes")
262
+ MPVLib.setOptionString("correct-pts", "yes")
227
263
  }
228
264
  }
229
265
 
230
266
  MPVLib.setOptionString("scale", "bilinear")
231
267
  MPVLib.setOptionString("dscale", "bilinear")
232
- MPVLib.setOptionString("tscale","off")
233
- MPVLib.setOptionString("interpolation","no")
268
+ MPVLib.setOptionString("tscale", "off")
269
+ MPVLib.setOptionString("interpolation", "no")
234
270
 
235
- MPVLib.setOptionString("ao", "audiotrack")
236
271
  MPVLib.setOptionString("alang", "")
272
+ MPVLib.setOptionString("ao", "audiotrack")
237
273
 
238
- MPVLib.setOptionString("sub-font-provider", "none")
239
274
  MPVLib.setOptionString("slang", "")
240
275
  MPVLib.setOptionString("sub-scale-with-window", "yes")
241
276
  MPVLib.setOptionString("sub-use-margins", "no")
242
277
 
243
278
  MPVLib.setOptionString("cache", "yes")
244
279
  MPVLib.setOptionString("cache-pause-initial", "yes")
245
- MPVLib.setOptionString("audio-buffer","2.0")
246
-
280
+ MPVLib.setOptionString("audio-buffer", "2.0")
247
281
  } catch (e: Exception) {
248
282
  logException(e)
249
283
  }
250
284
  }
251
285
 
252
286
  private fun maybeStartPlayback() {
287
+ if (state != State.ACTIVE) {
288
+ return
289
+ }
290
+ if (!surfaceAttached) {
291
+ return
292
+ }
293
+
253
294
  val url = session.playUrl ?: return
254
- if (loadedUrl == url) return
295
+ if (loadedUrl == url) {
296
+ return
297
+ }
298
+
255
299
  try {
256
- loadedUrl = url
300
+ loadedUrl = url
257
301
  MPVLib.command(arrayOf("loadfile", url, "replace"))
258
302
  applyContinuousState()
259
303
  } catch (e: Exception) {
@@ -262,8 +306,12 @@ class LibmpvRenderer(
262
306
  }
263
307
 
264
308
  private fun applyContinuousState() {
265
- if (!mpvAlive || shuttingDown) return
266
- if (!session.hasFileLoaded) return
309
+ if (!mpvAlive || shuttingDown) {
310
+ return
311
+ }
312
+ if (!session.hasFileLoaded) {
313
+ return
314
+ }
267
315
 
268
316
  MPVLib.command(
269
317
  arrayOf("set", "pause", if (session.isPlaying) "no" else "yes")
@@ -271,7 +319,9 @@ class LibmpvRenderer(
271
319
  }
272
320
 
273
321
  private fun applyDeferredState() {
274
- if (!mpvAlive || shuttingDown) return
322
+ if (!mpvAlive || shuttingDown) {
323
+ return
324
+ }
275
325
 
276
326
  session.seekToSeconds?.let { target ->
277
327
  if (session.needsApply(LibmpvSession.MpvIntent.SEEK)) {
@@ -293,9 +343,13 @@ class LibmpvRenderer(
293
343
  }
294
344
  }
295
345
 
296
- session.selectedSubtitleTrack?.let {
297
- if (session.needsApply(LibmpvSession.MpvIntent.SUBTITLE_TRACK)) {
298
- val sid = if (it == -1) "no" else (it + 1).toString()
346
+ session.selectedSubtitleTrack?.let { trackIndex ->
347
+ if (
348
+ session.needsApply(LibmpvSession.MpvIntent.SUBTITLE_TRACK) &&
349
+ session.hasFileLoaded &&
350
+ surfaceAttached
351
+ ) {
352
+ val sid = if (trackIndex == -1) "no" else (trackIndex + 1).toString()
299
353
  MPVLib.command(arrayOf("set", "sid", sid))
300
354
  session.markApplied(LibmpvSession.MpvIntent.SUBTITLE_TRACK)
301
355
  }
@@ -303,7 +357,9 @@ class LibmpvRenderer(
303
357
  }
304
358
 
305
359
  private fun stopPlayback() {
306
- if (!mpvAlive) return
360
+ if (!mpvAlive) {
361
+ return
362
+ }
307
363
  try {
308
364
  MPVLib.command(arrayOf("stop"))
309
365
  MPVLib.setPropertyString("pause", "yes")
@@ -314,17 +370,13 @@ class LibmpvRenderer(
314
370
  }
315
371
  }
316
372
 
317
- private fun detachSurfaceInternal() {
318
- try {
319
- MPVLib.detachSurface()
320
- } catch (e: Exception) {
321
- logException(e)
322
- }
323
- }
324
-
325
373
  override fun logMessage(prefix: String, level: Int, text: String) {
326
- if (shuttingDown) return
327
- if (level <= LOG_LEVEL_WARN) Log.w(TAG, "[$prefix] $text")
374
+ if (shuttingDown) {
375
+ return
376
+ }
377
+ if (level <= LOG_LEVEL_WARN) {
378
+ Log.w(TAG, "[$prefix] $text")
379
+ }
328
380
  onLog(mapOf("prefix" to prefix, "level" to level, "text" to text))
329
381
  }
330
382
 
@@ -333,56 +385,110 @@ class LibmpvRenderer(
333
385
  MPVLib.MpvEvent.MPV_EVENT_FILE_LOADED,
334
386
  MPVLib.MpvEvent.MPV_EVENT_PLAYBACK_RESTART -> {
335
387
  session.hasFileLoaded = true
336
- mainHandler.post {
337
- applyDeferredState()
338
- MPVLib.setPropertyString("pause", if (session.isPlaying) "no" else "yes")
339
- }
388
+ applyDeferredState()
340
389
  }
341
390
  }
342
391
  }
343
392
 
344
-
345
- override fun eventProperty(property: String) =
393
+ override fun eventProperty(property: String) {
346
394
  onEvent(mapOf("property" to property, "kind" to "none"))
395
+ }
347
396
 
348
- override fun eventProperty(property: String, value: Long) =
397
+ override fun eventProperty(property: String, value: Long) {
349
398
  onEvent(mapOf("property" to property, "kind" to "long", "value" to value))
399
+ }
350
400
 
351
- override fun eventProperty(property: String, value: Double) =
401
+ override fun eventProperty(property: String, value: Double) {
352
402
  onEvent(mapOf("property" to property, "kind" to "double", "value" to value))
403
+ }
353
404
 
354
- override fun eventProperty(property: String, value: Boolean) =
405
+ override fun eventProperty(property: String, value: Boolean) {
355
406
  onEvent(mapOf("property" to property, "kind" to "boolean", "value" to value))
407
+ }
356
408
 
357
- override fun eventProperty(property: String, value: String) =
409
+ override fun eventProperty(property: String, value: String) {
358
410
  onEvent(mapOf("property" to property, "kind" to "string", "value" to value))
411
+ }
359
412
 
360
413
  private fun createMpvDirectory() {
361
- if (shuttingDown) return
414
+ if (shuttingDown) {
415
+ return
416
+ }
362
417
 
363
418
  val ctx = surfaceView.context.applicationContext
364
- val dir = File(ctx.getExternalFilesDir("mpv"), "mpv")
419
+ val mpvRoot = File(ctx.getExternalFilesDir("mpv"), "mpv")
420
+ val fontsDir = File(mpvRoot, "fonts")
365
421
 
366
422
  try {
367
- if (!dir.exists() && !dir.mkdirs()) return
368
- mpvDirectory = dir.absolutePath
423
+ logMessage(
424
+ "LimpbvRenderer->createMpvDirectory",
425
+ LOG_LEVEL_INFO,
426
+ "expo-libmpv version 0.5.4-0"
427
+ )
428
+ logMessage(
429
+ "LimpbvRenderer->createMpvDirectory",
430
+ LOG_LEVEL_INFO,
431
+ "Attempting to create the MPV dir"
432
+ )
433
+
434
+ if (!mpvRoot.exists() && !mpvRoot.mkdirs()) {
435
+ logMessage(
436
+ "LimpbvRenderer->createMpvDirectory",
437
+ LOG_LEVEL_INFO,
438
+ "Cannot make mpv directory"
439
+ )
440
+ return
441
+ }
369
442
 
370
- val subFont = File(dir, "subfont.ttf")
443
+ if (!fontsDir.exists() && !fontsDir.mkdirs()) {
444
+ logMessage(
445
+ "LimpbvRenderer->createMpvDirectory",
446
+ LOG_LEVEL_INFO,
447
+ "Cannot make fonts directory"
448
+ )
449
+ return
450
+ }
451
+
452
+ mpvDirectory = mpvRoot.absolutePath
453
+
454
+ val subFont = File(fontsDir, "subfont.ttf")
371
455
  if (!subFont.exists()) {
456
+ logMessage(
457
+ "LimpbvRenderer->createMpvDirectory",
458
+ LOG_LEVEL_INFO,
459
+ "Creating font files"
460
+ )
372
461
  ctx.assets.open("subfont.ttf").use { inS ->
373
462
  FileOutputStream(subFont).use { outS ->
374
463
  inS.copyTo(outS)
375
464
  }
376
465
  }
466
+ } else {
467
+ logMessage(
468
+ "LimpbvRenderer->createMpvDirectory",
469
+ LOG_LEVEL_INFO,
470
+ "Font files already exist"
471
+ )
377
472
  }
378
473
 
379
- val mpvConf = File(dir, "mpv.conf")
474
+ val mpvConf = File(mpvRoot, "mpv.conf")
380
475
  if (!mpvConf.exists()) {
476
+ logMessage(
477
+ "LimpbvRenderer->createMpvDirectory",
478
+ LOG_LEVEL_INFO,
479
+ "Creating mpv conf"
480
+ )
381
481
  ctx.assets.open("mpv.conf").use { inS ->
382
482
  FileOutputStream(mpvConf).use { outS ->
383
483
  inS.copyTo(outS)
384
484
  }
385
485
  }
486
+ } else {
487
+ logMessage(
488
+ "LimpbvRenderer->createMpvDirectory",
489
+ LOG_LEVEL_INFO,
490
+ "mpv conf already exists"
491
+ )
386
492
  }
387
493
  } catch (e: Exception) {
388
494
  Log.e(TAG, "mpv directory init failed", e)
@@ -390,9 +496,15 @@ class LibmpvRenderer(
390
496
  }
391
497
 
392
498
  private fun logException(e: Exception) {
393
- if (shuttingDown) return
499
+ if (shuttingDown) {
500
+ return
501
+ }
394
502
  try {
395
- MPVLib.logMessage("RNLE", 20, e.message ?: "Unknown mpv error")
503
+ MPVLib.logMessage(
504
+ "LimpbvRenderer->logException",
505
+ LOG_LEVEL_INFO,
506
+ e.message ?: "Unknown mpv error"
507
+ )
396
508
  } catch (_: Exception) {
397
509
  Log.e(TAG, "mpv error", e)
398
510
  }
@@ -48,23 +48,24 @@ class LibmpvView(
48
48
  return attached && surfaceReady
49
49
  }
50
50
 
51
- private fun reconcileRenderer() {
52
- if (shouldHaveRenderer() && renderer == null) {
53
- renderer = LibmpvRenderer(
54
- session = session,
55
- surfaceView = surfaceView,
56
- onLog = { payload -> onLibmpvLog(payload) },
57
- onEvent = { payload -> onLibmpvEvent(payload) }
58
- )
59
- renderer!!.start()
60
- return
61
- }
51
+ private fun reconcileRenderer() {
52
+ if (renderer == null && attached) {
53
+ renderer = LibmpvRenderer(
54
+ session = session,
55
+ surfaceView = surfaceView,
56
+ onLog = { payload -> onLibmpvLog(payload) },
57
+ onEvent = { payload -> onLibmpvEvent(payload) }
58
+ )
59
+ renderer!!.start()
60
+ }
62
61
 
63
- if (!shouldHaveRenderer() && renderer != null) {
64
- renderer!!.destroy()
65
- renderer = null
62
+ renderer?.let { r ->
63
+ if (surfaceReady) {
64
+ r.attachSurfaceIfNeeded()
66
65
  }
67
66
  }
67
+ }
68
+
68
69
 
69
70
  fun onSessionUpdatedFromProps() {
70
71
  reconcileRenderer()
@@ -85,11 +86,13 @@ class LibmpvView(
85
86
 
86
87
  override fun surfaceCreated(holder: SurfaceHolder) {
87
88
  surfaceReady = true
88
- reconcileRenderer()
89
+ renderer?.surfaceReady = true
90
+ renderer?.attachSurfaceIfNeeded()
89
91
  }
90
92
 
91
93
  override fun surfaceDestroyed(holder: SurfaceHolder) {
92
94
  surfaceReady = false
95
+ renderer?.surfaceReady = false
93
96
  reconcileRenderer()
94
97
  }
95
98
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-libmpv",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "A libmpv Fabric component for Android",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",