mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Move MPV init & destroy to looper thread (#534)
## Description No user facing changes in this PR This is a continuation of #481 to move initializing and tearing down `libmpv` onto the looper thread to prevent main thread blocking. ~The only function left that runs on the main thread is `setVideoSurfaceView`. Since this has to directly interact with the UI, I need to read the docs and ExoPlayer code a bit more to see how this can safely done multi-threaded.~ It is safe/correct to attach on another thread.
This commit is contained in:
parent
60838c2a86
commit
20fe5e2626
2 changed files with 59 additions and 26 deletions
|
|
@ -38,9 +38,17 @@ object MPVLib {
|
||||||
|
|
||||||
external fun create(appctx: Context)
|
external fun create(appctx: Context)
|
||||||
|
|
||||||
external fun init()
|
fun initialize() {
|
||||||
|
synchronized(this) { init() }
|
||||||
|
}
|
||||||
|
|
||||||
external fun destroy()
|
private external fun init()
|
||||||
|
|
||||||
|
fun tearDown() {
|
||||||
|
synchronized(this) { destroy() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun destroy()
|
||||||
|
|
||||||
external fun attachSurface(surface: Surface)
|
external fun attachSurface(surface: Surface)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ class MpvPlayer(
|
||||||
|
|
||||||
private var surface: Surface? = null
|
private var surface: Surface? = null
|
||||||
private val playbackState = AtomicReference<PlaybackState>(PlaybackState.EMPTY)
|
private val playbackState = AtomicReference<PlaybackState>(PlaybackState.EMPTY)
|
||||||
|
private val mpvLogger = MpvLogger()
|
||||||
|
|
||||||
// This looper will sent events to the main thread
|
// This looper will sent events to the main thread
|
||||||
private val looper = Util.getCurrentOrMainLooper()
|
private val looper = Util.getCurrentOrMainLooper()
|
||||||
|
|
@ -87,7 +88,7 @@ class MpvPlayer(
|
||||||
) { listener, eventFlags ->
|
) { listener, eventFlags ->
|
||||||
listener.onEvents(this@MpvPlayer, Player.Events(eventFlags))
|
listener.onEvents(this@MpvPlayer, Player.Events(eventFlags))
|
||||||
}
|
}
|
||||||
private val availableCommands: Player.Commands
|
private var availableCommands: Player.Commands = Player.Commands.Builder().build()
|
||||||
private val trackSelector = DefaultTrackSelector(context)
|
private val trackSelector = DefaultTrackSelector(context)
|
||||||
|
|
||||||
// This thread/looper will receive commands from the main thread to execute
|
// This thread/looper will receive commands from the main thread to execute
|
||||||
|
|
@ -101,7 +102,13 @@ class MpvPlayer(
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
sendCommand(MpvCommand.INITIALIZE, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init() {
|
||||||
Timber.v("config-dir=${context.filesDir.path}")
|
Timber.v("config-dir=${context.filesDir.path}")
|
||||||
|
MPVLib.addLogObserver(mpvLogger)
|
||||||
|
|
||||||
MPVLib.create(context)
|
MPVLib.create(context)
|
||||||
MPVLib.setOptionString("config", "yes")
|
MPVLib.setOptionString("config", "yes")
|
||||||
MPVLib.setOptionString("config-dir", context.filesDir.path)
|
MPVLib.setOptionString("config-dir", context.filesDir.path)
|
||||||
|
|
@ -120,17 +127,14 @@ class MpvPlayer(
|
||||||
MPVLib.setOptionString("demuxer-max-bytes", "${cacheMegs * 1024 * 1024}")
|
MPVLib.setOptionString("demuxer-max-bytes", "${cacheMegs * 1024 * 1024}")
|
||||||
MPVLib.setOptionString("demuxer-max-back-bytes", "${cacheMegs * 1024 * 1024}")
|
MPVLib.setOptionString("demuxer-max-back-bytes", "${cacheMegs * 1024 * 1024}")
|
||||||
|
|
||||||
MPVLib.init()
|
MPVLib.initialize()
|
||||||
|
|
||||||
MPVLib.setOptionString("force-window", "no")
|
MPVLib.setOptionString("force-window", "no")
|
||||||
MPVLib.setOptionString("idle", "yes")
|
MPVLib.setOptionString("idle", "yes")
|
||||||
// MPVLib.setOptionString("sub-fonts-dir", File(context.filesDir, "fonts").absolutePath)
|
// MPVLib.setOptionString("sub-fonts-dir", File(context.filesDir, "fonts").absolutePath)
|
||||||
|
|
||||||
internalHandler.post {
|
|
||||||
MPVLib.addObserver(this@MpvPlayer)
|
MPVLib.addObserver(this@MpvPlayer)
|
||||||
MPVProperty.observedProperties.forEach(MPVLib::observeProperty)
|
MPVProperty.observedProperties.forEach(MPVLib::observeProperty)
|
||||||
MPVLib.addLogObserver(MpvLogger())
|
|
||||||
}
|
|
||||||
|
|
||||||
availableCommands =
|
availableCommands =
|
||||||
Player.Commands
|
Player.Commands
|
||||||
|
|
@ -298,15 +302,14 @@ class MpvPlayer(
|
||||||
|
|
||||||
override fun release() {
|
override fun release() {
|
||||||
Timber.i("release")
|
Timber.i("release")
|
||||||
internalHandler.removeCallbacks(updatePlaybackState)
|
|
||||||
playbackState.update {
|
playbackState.update {
|
||||||
PlaybackState.EMPTY
|
PlaybackState.EMPTY
|
||||||
}
|
}
|
||||||
if (!isReleased) {
|
if (!isReleased) {
|
||||||
thread.quit()
|
internalHandler.removeCallbacks(updatePlaybackState)
|
||||||
MPVLib.removeObserver(this)
|
MPVLib.removeObserver(this@MpvPlayer)
|
||||||
clearVideoSurfaceView(null)
|
sendCommand(MpvCommand.DESTROY, null)
|
||||||
MPVLib.destroy()
|
thread.quitSafely()
|
||||||
}
|
}
|
||||||
isReleased = true
|
isReleased = true
|
||||||
}
|
}
|
||||||
|
|
@ -466,15 +469,9 @@ class MpvPlayer(
|
||||||
throwIfReleased()
|
throwIfReleased()
|
||||||
if (DEBUG) Timber.v("setVideoSurfaceView")
|
if (DEBUG) Timber.v("setVideoSurfaceView")
|
||||||
val surface = surfaceView?.holder?.surface
|
val surface = surfaceView?.holder?.surface
|
||||||
if (surface != null) {
|
if (surface != null && surface.isValid) {
|
||||||
this.surface = surface
|
|
||||||
Timber.v("Queued attach")
|
Timber.v("Queued attach")
|
||||||
MPVLib.attachSurface(surface)
|
sendCommand(MpvCommand.ATTACH_SURFACE, surface)
|
||||||
MPVLib.setOptionString("force-window", "yes")
|
|
||||||
Timber.d("Attached surface")
|
|
||||||
playbackState.load().media?.let {
|
|
||||||
sendCommand(MpvCommand.LOAD_FILE, it)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
clearVideoSurfaceView(null)
|
clearVideoSurfaceView(null)
|
||||||
}
|
}
|
||||||
|
|
@ -483,9 +480,7 @@ class MpvPlayer(
|
||||||
override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
|
override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
|
||||||
if (surface == surfaceView?.holder?.surface) {
|
if (surface == surfaceView?.holder?.surface) {
|
||||||
Timber.d("clearVideoSurfaceView")
|
Timber.d("clearVideoSurfaceView")
|
||||||
MPVLib.detachSurface()
|
sendCommand(MpvCommand.ATTACH_SURFACE, null)
|
||||||
MPVLib.setPropertyString("vo", "null")
|
|
||||||
MPVLib.setPropertyString("force-window", "no")
|
|
||||||
} else {
|
} else {
|
||||||
Timber.w("clearVideoSurfaceView called with different surface")
|
Timber.w("clearVideoSurfaceView called with different surface")
|
||||||
}
|
}
|
||||||
|
|
@ -621,7 +616,7 @@ class MpvPlayer(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun event(eventId: Int) {
|
override fun event(eventId: Int) {
|
||||||
Timber.v("event: thread=${Thread.currentThread().name}, eventId=$eventId")
|
Timber.v("event: eventId=%s", eventId)
|
||||||
when (eventId) {
|
when (eventId) {
|
||||||
MPV_EVENT_START_FILE -> {
|
MPV_EVENT_START_FILE -> {
|
||||||
internalHandler.post(updatePlaybackState)
|
internalHandler.post(updatePlaybackState)
|
||||||
|
|
@ -891,6 +886,33 @@ class MpvPlayer(
|
||||||
MpvCommand.LOAD_FILE -> {
|
MpvCommand.LOAD_FILE -> {
|
||||||
loadFile(msg.obj as MediaAndPosition)
|
loadFile(msg.obj as MediaAndPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MpvCommand.ATTACH_SURFACE -> {
|
||||||
|
val surface = msg.obj as Surface?
|
||||||
|
if (surface == null || (this.surface != null && this.surface != surface)) {
|
||||||
|
// If clearing or changing the surface
|
||||||
|
MPVLib.detachSurface()
|
||||||
|
MPVLib.setPropertyString("vo", "null")
|
||||||
|
MPVLib.setPropertyString("force-window", "no")
|
||||||
|
}
|
||||||
|
if (surface != null) {
|
||||||
|
MPVLib.attachSurface(surface)
|
||||||
|
this.surface = surface
|
||||||
|
MPVLib.setOptionString("force-window", "yes")
|
||||||
|
Timber.d("Attached surface")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MpvCommand.INITIALIZE -> {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
MpvCommand.DESTROY -> {
|
||||||
|
clearVideoSurfaceView(null)
|
||||||
|
MPVLib.removeLogObserver(mpvLogger)
|
||||||
|
MPVLib.tearDown()
|
||||||
|
Timber.d("MPVLib destroyed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -1020,4 +1042,7 @@ enum class MpvCommand {
|
||||||
SET_SPEED,
|
SET_SPEED,
|
||||||
SET_SUBTITLE_DELAY,
|
SET_SUBTITLE_DELAY,
|
||||||
LOAD_FILE,
|
LOAD_FILE,
|
||||||
|
ATTACH_SURFACE,
|
||||||
|
INITIALIZE,
|
||||||
|
DESTROY,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue