mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
AVPlayer background music mode
This commit is contained in:
parent
b5f8a0fba2
commit
48e616b301
@ -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) {}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -226,9 +226,11 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
navigation.hideKeyboard()
|
navigation.hideKeyboard()
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
if !presentingPlayer {
|
||||||
withAnimation(.linear(duration: 0.25)) {
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.presentingPlayer = true
|
withAnimation(.linear(duration: 0.25)) {
|
||||||
|
self?.presentingPlayer = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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 }
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user