fix pip screen bugs
This commit is contained in:
+58
-14
@@ -14,7 +14,6 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import platform.AVFAudio.AVAudioSession
|
||||
import platform.AVFAudio.AVAudioSessionCategoryPlayback
|
||||
import platform.AVFAudio.AVAudioSessionInterruptionNotification
|
||||
import platform.AVFAudio.AVAudioSessionInterruptionOptionKey
|
||||
import platform.AVFAudio.AVAudioSessionInterruptionOptionShouldResume
|
||||
@@ -24,7 +23,6 @@ import platform.AVFAudio.AVAudioSessionInterruptionTypeKey
|
||||
import platform.AVFAudio.AVAudioSessionRouteChangeNotification
|
||||
import platform.AVFAudio.AVAudioSessionRouteChangeReasonKey
|
||||
import platform.AVFAudio.AVAudioSessionRouteChangeReasonOldDeviceUnavailable
|
||||
import platform.AVFAudio.setActive
|
||||
import platform.AVFoundation.AVPlayer
|
||||
import platform.AVFoundation.AVPlayerItem
|
||||
import platform.AVFoundation.AVPlayerItemDidPlayToEndTimeNotification
|
||||
@@ -131,6 +129,9 @@ actual class PlayerController {
|
||||
private var lastEndMark: TimeSource.Monotonic.ValueTimeMark? = null
|
||||
|
||||
private var audioReady = false
|
||||
private var audioActive = false
|
||||
private var playWhenReady = false
|
||||
private var audioSessionReadyObserver: Any? = null
|
||||
|
||||
/** CarPlay / 锁屏 Now Playing 标题用的当前歌词;null 表示显示歌名。 */
|
||||
private var nowPlayingLyric: String? = null
|
||||
@@ -143,18 +144,50 @@ actual class PlayerController {
|
||||
observePlaybackFailed()
|
||||
observeAudioRouteChanges()
|
||||
observeAudioInterruptions()
|
||||
observeAudioSessionReady()
|
||||
}
|
||||
|
||||
private fun ensureAudioReady() {
|
||||
if (audioReady) return
|
||||
if (audioActive || audioReady) return
|
||||
audioReady = true
|
||||
runCatching {
|
||||
val session = AVAudioSession.sharedInstance()
|
||||
session.setCategory(AVAudioSessionCategoryPlayback, error = null)
|
||||
session.setActive(true, error = null)
|
||||
}.onFailure { AppLog.e(TAG, "ensureAudioReady session fail", it) }
|
||||
runCatching { setupRemoteCommands() }
|
||||
.onFailure { AppLog.e(TAG, "ensureAudioReady remoteCommands fail", it) }
|
||||
NSNotificationCenter.defaultCenter.postNotificationName(
|
||||
aName = "llmpPrepareAudioSession",
|
||||
`object` = null,
|
||||
userInfo = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun observeAudioSessionReady() {
|
||||
audioSessionReadyObserver = NSNotificationCenter.defaultCenter.addObserverForName(
|
||||
name = "llmpAudioSessionReady",
|
||||
`object` = null,
|
||||
queue = null,
|
||||
) { notification: NSNotification? ->
|
||||
val ok = (notification?.userInfo?.get("ok") as? NSNumber)?.boolValue ?: true
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
if (!ok) {
|
||||
audioReady = false
|
||||
playWhenReady = false
|
||||
AppLog.e(TAG, "audio session activate failed")
|
||||
if (_state.value.isPlaying) {
|
||||
player.pause()
|
||||
_state.update { it.copy(isPlaying = false) }
|
||||
}
|
||||
return@dispatch_async
|
||||
}
|
||||
audioActive = true
|
||||
if (playWhenReady && _state.value.isPlaying) {
|
||||
playWhenReady = false
|
||||
player.play()
|
||||
startPositionUpdates()
|
||||
updateNowPlayingInfo()
|
||||
} else {
|
||||
playWhenReady = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRemoteCommands() {
|
||||
@@ -270,7 +303,7 @@ actual class PlayerController {
|
||||
queue = null,
|
||||
) { notification: NSNotification? ->
|
||||
val ended = notification?.`object` as? AVPlayerItem
|
||||
// 桌面歌词 dummy black.mp4 也会发 DidPlayToEnd;只处理本控制器的音乐 item。
|
||||
// 只处理本控制器的音乐 item。
|
||||
// Kotlin/Native 的 === 对 ObjC 对象包装器不可靠,须用 isEqual 比较指针。
|
||||
if (ended == null || !isMusicItem(ended)) {
|
||||
return@addObserverForName
|
||||
@@ -614,17 +647,24 @@ actual class PlayerController {
|
||||
ensureAudioReady()
|
||||
val current = _state.value.current
|
||||
if (loadedUri.isNullOrBlank() && current != null) {
|
||||
playWhenReady = false
|
||||
AppLog.e(TAG, "play() abort reason=noLoadedUri id=${current.musicId}")
|
||||
_state.update { it.copy(isPlaying = false, error = "No playable URI") }
|
||||
return
|
||||
}
|
||||
player.play()
|
||||
_state.update { it.copy(isPlaying = true, error = null) }
|
||||
startPositionUpdates()
|
||||
updateNowPlayingInfo()
|
||||
if (audioActive) {
|
||||
player.play()
|
||||
startPositionUpdates()
|
||||
updateNowPlayingInfo()
|
||||
} else {
|
||||
playWhenReady = true
|
||||
updateNowPlayingInfo()
|
||||
}
|
||||
}
|
||||
|
||||
actual fun pause() {
|
||||
playWhenReady = false
|
||||
resumeAfterInterruption = false
|
||||
endScrub()
|
||||
player.pause()
|
||||
@@ -908,8 +948,12 @@ actual class PlayerController {
|
||||
watchItemStatus(item, path, music)
|
||||
if (autoPlay) {
|
||||
ensureAudioReady()
|
||||
player.play()
|
||||
startPositionUpdates()
|
||||
if (audioActive) {
|
||||
player.play()
|
||||
startPositionUpdates()
|
||||
} else {
|
||||
playWhenReady = true
|
||||
}
|
||||
} else {
|
||||
player.pause()
|
||||
positionJob?.cancel()
|
||||
|
||||
Reference in New Issue
Block a user