AVPlayer background music mode

This commit is contained in:
Arkadiusz Fal 2022-08-20 22:31:03 +02:00
parent c8ae036444
commit 78165442fe
6 changed files with 129 additions and 41 deletions

View File

@ -588,6 +588,42 @@ final class AVPlayerBackend: PlayerBackend {
controlsUpdates = false controlsUpdates = false
} }
func startMusicMode() {
if model.playingInPictureInPicture {
closePiP()
}
playerLayer.player = nil
toggleVisualTracksEnabled(false)
}
func stopMusicMode() {
playerLayer.player = avPlayer
toggleVisualTracksEnabled(true)
}
func toggleVisualTracksEnabled(_ value: Bool) {
if let item = avPlayer.currentItem {
for playerItemTrack in item.tracks {
if let assetTrack = playerItemTrack.assetTrack,
assetTrack.hasMediaCharacteristic(AVMediaCharacteristic.visual)
{
playerItemTrack.isEnabled = value
}
}
}
}
func didChangeTo() {
if model.musicMode {
startMusicMode()
} else {
stopMusicMode()
}
}
func setNeedsDrawing(_: Bool) {} func setNeedsDrawing(_: Bool) {}
func setSize(_: Double, _: Double) {} func setSize(_: Double, _: Double) {}
func setNeedsNetworkStateUpdates(_: Bool) {} func setNeedsNetworkStateUpdates(_: Bool) {}

View File

@ -512,4 +512,36 @@ final class MPVBackend: PlayerBackend {
networkStateTimer.pause() networkStateTimer.pause()
} }
} }
func startMusicMode() {
setVideoToNo()
}
func stopMusicMode() {
addVideoTrackFromStream()
setVideoToAuto()
controls.resetTimer()
}
func addVideoTrackFromStream() {
if let videoTrackURL = model.stream?.videoAsset?.url,
tracks < 2
{
logger.info("adding video track")
addVideoTrack(videoTrackURL)
}
setVideoToAuto()
}
func didChangeTo() {
setNeedsDrawing(model.presentingPlayer)
if model.musicMode {
startMusicMode()
} else {
stopMusicMode()
}
}
} }

View File

@ -47,10 +47,15 @@ protocol PlayerBackend {
func closePiP() func closePiP()
func startMusicMode()
func stopMusicMode()
func updateControls() func updateControls()
func startControlsUpdates() func startControlsUpdates()
func stopControlsUpdates() func stopControlsUpdates()
func didChangeTo()
func setNeedsNetworkStateUpdates(_ needsUpdates: Bool) func setNeedsNetworkStateUpdates(_ needsUpdates: Bool)
func setNeedsDrawing(_ needsDrawing: Bool) func setNeedsDrawing(_ needsDrawing: Bool)

View File

@ -38,14 +38,18 @@ final class PlayerControlsModel: ObservableObject {
func handlePresentationChange() { func handlePresentationChange() {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self,
let player = self.player else { return }
if self.presentingControls { if self.presentingControls {
self.player?.backend.startControlsUpdates() player.backend.startControlsUpdates()
self.resetTimer() self.resetTimer()
} else { } else {
self.player?.backend.stopControlsUpdates() if !player.musicMode {
self.timer?.invalidate() player.backend.stopControlsUpdates()
self.timer = nil self.removeTimer()
} else {
self.presentingControls = true
}
} }
} }
} }
@ -132,6 +136,8 @@ final class PlayerControlsModel: ObservableObject {
} }
func startPiP(startImmediately: Bool = true) { func startPiP(startImmediately: Bool = true) {
player?.avPlayerBackend.startPictureInPictureOnPlay = true
#if !os(macOS) #if !os(macOS)
player.exitFullScreen() player.exitFullScreen()
#endif #endif
@ -143,7 +149,6 @@ final class PlayerControlsModel: ObservableObject {
} }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak player] in DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak player] in
player?.avPlayerBackend.startPictureInPictureOnPlay = true
if startImmediately { if startImmediately {
player?.pipController?.startPictureInPicture() player?.pipController?.startPictureInPicture()
} }

View File

@ -226,11 +226,13 @@ final class PlayerModel: ObservableObject {
navigation.hideKeyboard() navigation.hideKeyboard()
if !presentingPlayer {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
withAnimation(.linear(duration: 0.25)) { withAnimation(.linear(duration: 0.25)) {
self?.presentingPlayer = true self?.presentingPlayer = true
} }
} }
}
#if os(macOS) #if os(macOS)
Windows.player.open() Windows.player.open()
@ -340,14 +342,30 @@ final class PlayerModel: ObservableObject {
func play(_ video: Video, at time: CMTime? = nil, showingPlayer: Bool = true) { func play(_ video: Video, at time: CMTime? = nil, showingPlayer: Bool = true) {
pause() pause()
var changeBackendHandler: (() -> Void)?
if let backend = qualityProfile?.backend ?? QualityProfilesModel.shared.automaticProfile?.backend,
activeBackend != backend,
backend == .appleAVPlayer || !avPlayerBackend.startPictureInPictureOnPlay
{
changeBackendHandler = { [weak self] in
guard let self = self else { return }
self.changeActiveBackend(from: self.activeBackend, to: backend)
}
}
#if os(iOS) #if os(iOS)
if !playingInPictureInPicture, showingPlayer { if !playingInPictureInPicture, showingPlayer {
onPresentPlayer = { [weak self] in self?.playNow(video, at: time) } onPresentPlayer = { [weak self] in
changeBackendHandler?()
self?.playNow(video, at: time)
}
show() show()
return return
} }
#endif #endif
changeBackendHandler?()
playNow(video, at: time) playNow(video, at: time)
guard !playingInPictureInPicture else { guard !playingInPictureInPicture else {
@ -493,16 +511,12 @@ final class PlayerModel: ObservableObject {
Defaults[.activeBackend] = to Defaults[.activeBackend] = to
self.activeBackend = to self.activeBackend = to
self.backend.didChangeTo()
guard var stream = stream else { guard var stream = stream else {
return return
} }
if to == .mpv {
addVideoTrackFromStream()
} else {
musicMode = false
}
let fromBackend: PlayerBackend = from == .appleAVPlayer ? avPlayerBackend : mpvBackend let fromBackend: PlayerBackend = from == .appleAVPlayer ? avPlayerBackend : mpvBackend
let toBackend: PlayerBackend = to == .appleAVPlayer ? avPlayerBackend : mpvBackend let toBackend: PlayerBackend = to == .appleAVPlayer ? avPlayerBackend : mpvBackend
@ -534,7 +548,6 @@ final class PlayerModel: ObservableObject {
return return
} }
self.upgradeToStream(stream, force: true) self.upgradeToStream(stream, force: true)
self.setNeedsDrawing(self.presentingPlayer)
} }
} }
@ -756,6 +769,7 @@ final class PlayerModel: ObservableObject {
#else #else
func handleEnterForeground() { func handleEnterForeground() {
setNeedsDrawing(presentingPlayer) setNeedsDrawing(presentingPlayer)
avPlayerBackend.playerLayer.player = avPlayerBackend.avPlayer
guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else { guard closePiPAndOpenPlayerOnEnteringForeground, playingInPictureInPicture else {
return return
@ -766,10 +780,10 @@ final class PlayerModel: ObservableObject {
} }
func handleEnterBackground() { func handleEnterBackground() {
setNeedsDrawing(false)
if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode { if Defaults[.pauseOnEnteringBackground], !playingInPictureInPicture, !musicMode {
pause() pause()
} else if !playingInPictureInPicture {
avPlayerBackend.playerLayer.player = nil
} }
} }
#endif #endif
@ -872,34 +886,17 @@ final class PlayerModel: ObservableObject {
musicMode.toggle() musicMode.toggle()
if musicMode { if musicMode {
if playingInPictureInPicture {
avPlayerBackend.pause()
closePiP()
}
changeActiveBackend(from: .appleAVPlayer, to: .mpv)
controls.presentingControls = true controls.presentingControls = true
controls.removeTimer() controls.removeTimer()
mpvBackend.setVideoToNo()
backend.startMusicMode()
} else { } else {
addVideoTrackFromStream() backend.stopMusicMode()
mpvBackend.setVideoToAuto()
controls.resetTimer() controls.resetTimer()
} }
} }
func addVideoTrackFromStream() {
if let videoTrackURL = stream?.videoAsset?.url,
mpvBackend.tracks < 2
{
logger.info("adding video track")
mpvBackend.addVideoTrack(videoTrackURL)
}
mpvBackend.setVideoToAuto()
}
func updateAspectRatio() { func updateAspectRatio() {
#if !os(tvOS) #if !os(tvOS)
guard aspectRatio != backend.aspectRatio else { return } guard aspectRatio != backend.aspectRatio else { return }

View File

@ -90,7 +90,7 @@ struct VideoPlayerView: View {
Spacer() Spacer()
#endif #endif
} }
#if os(macOS) #if !os(tvOS)
.frame(width: player.playerSize.width) .frame(width: player.playerSize.width)
#endif #endif
@ -103,6 +103,11 @@ struct VideoPlayerView: View {
#endif #endif
} }
} }
.onAppear {
if player.musicMode {
player.backend.startControlsUpdates()
}
}
} }
var videoPlayer: some View { var videoPlayer: some View {
@ -435,10 +440,14 @@ struct VideoPlayerView: View {
guard player.presentingPlayer, guard player.presentingPlayer,
!playerControls.presentingControlsOverlay else { return } !playerControls.presentingControlsOverlay else { return }
if playerControls.presentingControls { if playerControls.presentingControls, !player.musicMode {
playerControls.presentingControls = false playerControls.presentingControls = false
} }
if player.musicMode {
player.backend.stopControlsUpdates()
}
let drag = value.translation.height let drag = value.translation.height
guard drag > 0 else { return } guard drag > 0 else { return }
@ -479,6 +488,10 @@ struct VideoPlayerView: View {
} }
player.backend.setNeedsDrawing(true) player.backend.setNeedsDrawing(true)
player.show() player.show()
if player.musicMode {
player.backend.startControlsUpdates()
}
} }
} }