fix(mpv): memory leak, stuck loading icon, anr, deprecated methods (#273)

* fix: memory leak and stuck loading icon

Also replace deprecated audio focus methods

* fix: use global scope for posting playback stopped

This fixes ANRs that people are having when leaving the player
This commit is contained in:
Jarne Demeulemeester 2023-02-05 01:15:43 +01:00 committed by GitHub
parent dff297513a
commit 7b5745acf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 20 deletions

View file

@ -3,6 +3,7 @@ package dev.jdtech.jellyfin.mpv
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.content.res.AssetManager import android.content.res.AssetManager
import android.media.AudioFocusRequest
import android.media.AudioManager import android.media.AudioManager
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -49,14 +50,14 @@ import timber.log.Timber
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
class MPVPlayer( class MPVPlayer(
context: Context, context: Context,
requestAudioFocus: Boolean, private val requestAudioFocus: Boolean,
private val appPreferences: AppPreferences private val appPreferences: AppPreferences
) : BasePlayer(), MPVLib.EventObserver, AudioManager.OnAudioFocusChangeListener { ) : BasePlayer(), MPVLib.EventObserver, AudioManager.OnAudioFocusChangeListener {
private val audioManager: AudioManager by lazy { context.getSystemService()!! } private val audioManager: AudioManager by lazy { context.getSystemService()!! }
private var audioFocusCallback: () -> Unit = {} private var audioFocusCallback: () -> Unit = {}
private var currentIndex = 0 private var currentIndex = 0
private var audioFocusRequest = AudioManager.AUDIOFOCUS_REQUEST_FAILED private lateinit var audioFocusRequest: AudioFocusRequest
private val handler = Handler(context.mainLooper) private val handler = Handler(context.mainLooper)
init { init {
@ -132,13 +133,16 @@ class MPVPlayer(
} }
if (requestAudioFocus) { if (requestAudioFocus) {
@Suppress("DEPRECATION") val audioAttributes = android.media.AudioAttributes.Builder()
audioFocusRequest = audioManager.requestAudioFocus( .setUsage(android.media.AudioAttributes.USAGE_MEDIA)
/* l = */ this, .setContentType(android.media.AudioAttributes.CONTENT_TYPE_MOVIE)
/* streamType = */ AudioManager.STREAM_MUSIC, .build()
/* durationHint = */ AudioManager.AUDIOFOCUS_GAIN audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
) .setAudioAttributes(audioAttributes)
if (audioFocusRequest != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { .setOnAudioFocusChangeListener(this)
.build()
val res = audioManager.requestAudioFocus(audioFocusRequest)
if (res != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
MPVLib.setPropertyBoolean("pause", true) MPVLib.setPropertyBoolean("pause", true)
} }
} }
@ -312,11 +316,9 @@ class MPVPlayer(
videoListener.onRenderedFirstFrame() videoListener.onRenderedFirstFrame()
} }
} else { } else {
if (playbackState == Player.STATE_BUFFERING && bufferedPosition > currentPosition) {
setPlayerStateAndNotifyIfChanged(playbackState = Player.STATE_READY) setPlayerStateAndNotifyIfChanged(playbackState = Player.STATE_READY)
} }
} }
}
else -> Unit else -> Unit
} }
} }
@ -344,9 +346,8 @@ class MPVPlayer(
playerStateChanged = true playerStateChanged = true
} }
if (playerStateChanged) { if (playerStateChanged) {
listeners.queueEvent( /* eventFlag = */ C.INDEX_UNSET) { listener -> listeners.queueEvent(C.INDEX_UNSET) { listener ->
@Suppress("DEPRECATION") listener.onPlaybackStateChanged(playbackState)
listener.onPlayerStateChanged(playWhenReady, playbackState)
} }
} }
if (wasPlaying != isPlaying) { if (wasPlaying != isPlaying) {
@ -899,11 +900,11 @@ class MPVPlayer(
* player must not be used after calling this method. * player must not be used after calling this method.
*/ */
override fun release() { override fun release() {
if (audioFocusRequest == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { if (requestAudioFocus) {
@Suppress("DEPRECATION") audioManager.abandonAudioFocusRequest(audioFocusRequest)
audioManager.abandonAudioFocus(this)
} }
resetInternalState() resetInternalState()
MPVLib.removeObserver(this)
MPVLib.destroy() MPVLib.destroy()
currentIndex = 0 currentIndex = 0
} }

View file

@ -24,9 +24,10 @@ import dev.jdtech.jellyfin.mpv.MPVPlayer
import dev.jdtech.jellyfin.mpv.TrackType import dev.jdtech.jellyfin.mpv.TrackType
import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.repository.JellyfinRepository
import dev.jdtech.jellyfin.utils.postDownloadPlaybackProgress import dev.jdtech.jellyfin.utils.postDownloadPlaybackProgress
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.util.UUID import java.util.UUID
@ -163,9 +164,10 @@ constructor(
} }
} }
@OptIn(DelicateCoroutinesApi::class)
private fun releasePlayer() { private fun releasePlayer() {
player.let { player -> player.let { player ->
runBlocking { GlobalScope.launch {
try { try {
jellyfinRepository.postPlaybackStop( jellyfinRepository.postPlaybackStop(
UUID.fromString(player.currentMediaItem?.mediaId), UUID.fromString(player.currentMediaItem?.mediaId),