95 lines
3.4 KiB
Swift
95 lines
3.4 KiB
Swift
import Foundation
|
||
import AVKit
|
||
import Combine
|
||
|
||
#if os(iOS)
|
||
/// 画中画(Picture-in-Picture)控制器
|
||
/// 封装 AVPictureInPictureController,管理 PiP 生命周期
|
||
@MainActor
|
||
final class PiPController: NSObject, ObservableObject {
|
||
|
||
@Published var isPiPActive = false
|
||
@Published var isPiPSupported = false
|
||
|
||
private var pipController: AVPictureInPictureController?
|
||
private weak var playerLayer: AVPlayerLayer?
|
||
|
||
/// 用 AVPlayerLayer 初始化 PiP 控制器
|
||
func setup(with playerLayer: AVPlayerLayer) {
|
||
self.playerLayer = playerLayer
|
||
|
||
guard AVPictureInPictureController.isPictureInPictureSupported() else {
|
||
NSLog("[MiniPlayer] PiP not supported on this device")
|
||
isPiPSupported = false
|
||
return
|
||
}
|
||
|
||
isPiPSupported = true
|
||
|
||
let pip = AVPictureInPictureController(playerLayer: playerLayer)
|
||
pip?.delegate = self
|
||
self.pipController = pip
|
||
|
||
NSLog("[MiniPlayer] PiP controller initialized")
|
||
}
|
||
|
||
/// 切换画中画状态
|
||
func togglePiP() {
|
||
guard let pip = pipController else {
|
||
NSLog("[MiniPlayer] PiP controller not initialized")
|
||
return
|
||
}
|
||
|
||
if pip.isPictureInPictureActive {
|
||
pip.stopPictureInPicture()
|
||
} else if pip.isPictureInPicturePossible {
|
||
pip.startPictureInPicture()
|
||
} else {
|
||
NSLog("[MiniPlayer] PiP not possible right now (player may not be ready)")
|
||
}
|
||
}
|
||
|
||
/// 当前是否可以启动 PiP
|
||
var canStartPiP: Bool {
|
||
pipController?.isPictureInPicturePossible ?? false
|
||
}
|
||
|
||
deinit {
|
||
pipController?.delegate = nil
|
||
}
|
||
}
|
||
|
||
// MARK: - AVPictureInPictureControllerDelegate
|
||
extension PiPController: AVPictureInPictureControllerDelegate {
|
||
|
||
nonisolated func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||
NSLog("[MiniPlayer] PiP will start")
|
||
Task { @MainActor in self.isPiPActive = true }
|
||
}
|
||
|
||
nonisolated func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||
NSLog("[MiniPlayer] PiP did start")
|
||
}
|
||
|
||
nonisolated func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||
NSLog("[MiniPlayer] PiP will stop")
|
||
}
|
||
|
||
nonisolated func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
||
NSLog("[MiniPlayer] PiP did stop")
|
||
Task { @MainActor in self.isPiPActive = false }
|
||
}
|
||
|
||
nonisolated func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
|
||
NSLog("[MiniPlayer] PiP failed to start: \(error.localizedDescription)")
|
||
}
|
||
|
||
nonisolated func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
||
// 用户点击 PiP 窗口的恢复按钮时调用
|
||
// 返回 true 表示我们已处理恢复(回到 app 内播放)
|
||
NSLog("[MiniPlayer] PiP restore requested")
|
||
completionHandler(true)
|
||
}
|
||
}
|
||
#endif
|