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
}
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 setSize(_: Double, _: Double) {}
func setNeedsNetworkStateUpdates(_: Bool) {}

View File

@ -512,4 +512,36 @@ final class MPVBackend: PlayerBackend {
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 startMusicMode()
func stopMusicMode()
func updateControls()
func startControlsUpdates()
func stopControlsUpdates()
func didChangeTo()
func setNeedsNetworkStateUpdates(_ needsUpdates: Bool)
func setNeedsDrawing(_ needsDrawing: Bool)

View File

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

View File

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

View File

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