mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-24 23:00:29 +01:00
feat: MPV player for both Android and iOS with added HW decoding PiP (with subtitles) (#1332)
Co-authored-by: Alex Kim <alexkim@Alexs-MacBook-Pro.local> Co-authored-by: Alex <111128610+Alexk2309@users.noreply.github.com> Co-authored-by: Simon-Eklundh <simon.eklundh@proton.me>
This commit is contained in:
committed by
GitHub
parent
df2f44e086
commit
f1575ca48b
@@ -12,6 +12,7 @@ protocol PiPControllerDelegate: AnyObject {
|
||||
func pipController(_ controller: PiPController, skipByInterval interval: CMTime)
|
||||
func pipControllerIsPlaying(_ controller: PiPController) -> Bool
|
||||
func pipControllerDuration(_ controller: PiPController) -> Double
|
||||
func pipControllerCurrentPosition(_ controller: PiPController) -> Double
|
||||
}
|
||||
|
||||
final class PiPController: NSObject {
|
||||
@@ -20,6 +21,13 @@ final class PiPController: NSObject {
|
||||
|
||||
weak var delegate: PiPControllerDelegate?
|
||||
|
||||
// Timebase for PiP progress tracking
|
||||
private var timebase: CMTimebase?
|
||||
|
||||
// Track current time for PiP progress
|
||||
private var currentTime: CMTime = .zero
|
||||
private var currentDuration: Double = 0
|
||||
|
||||
var isPictureInPictureSupported: Bool {
|
||||
return AVPictureInPictureController.isPictureInPictureSupported()
|
||||
}
|
||||
@@ -35,9 +43,29 @@ final class PiPController: NSObject {
|
||||
init(sampleBufferDisplayLayer: AVSampleBufferDisplayLayer) {
|
||||
self.sampleBufferDisplayLayer = sampleBufferDisplayLayer
|
||||
super.init()
|
||||
setupTimebase()
|
||||
setupPictureInPicture()
|
||||
}
|
||||
|
||||
private func setupTimebase() {
|
||||
// Create a timebase for tracking playback time
|
||||
var newTimebase: CMTimebase?
|
||||
let status = CMTimebaseCreateWithSourceClock(
|
||||
allocator: kCFAllocatorDefault,
|
||||
sourceClock: CMClockGetHostTimeClock(),
|
||||
timebaseOut: &newTimebase
|
||||
)
|
||||
|
||||
if status == noErr, let tb = newTimebase {
|
||||
timebase = tb
|
||||
CMTimebaseSetTime(tb, time: .zero)
|
||||
CMTimebaseSetRate(tb, rate: 0) // Start paused
|
||||
|
||||
// Set the control timebase on the display layer
|
||||
sampleBufferDisplayLayer?.controlTimebase = tb
|
||||
}
|
||||
}
|
||||
|
||||
private func setupPictureInPicture() {
|
||||
guard isPictureInPictureSupported,
|
||||
let displayLayer = sampleBufferDisplayLayer else {
|
||||
@@ -81,6 +109,9 @@ final class PiPController: NSObject {
|
||||
}
|
||||
|
||||
func updatePlaybackState() {
|
||||
// Only invalidate when PiP is active to avoid "no context menu visible" warnings
|
||||
guard isPictureInPictureActive else { return }
|
||||
|
||||
if Thread.isMainThread {
|
||||
pipController?.invalidatePlaybackState()
|
||||
} else {
|
||||
@@ -89,6 +120,36 @@ final class PiPController: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the current playback time for PiP progress display
|
||||
func setCurrentTime(_ time: CMTime) {
|
||||
currentTime = time
|
||||
|
||||
// Update the timebase to reflect current position
|
||||
if let tb = timebase {
|
||||
CMTimebaseSetTime(tb, time: time)
|
||||
}
|
||||
|
||||
// Only invalidate when PiP is active to avoid unnecessary updates
|
||||
if isPictureInPictureActive {
|
||||
updatePlaybackState()
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the current playback time from seconds
|
||||
func setCurrentTimeFromSeconds(_ seconds: Double, duration: Double) {
|
||||
guard seconds >= 0 else { return }
|
||||
currentDuration = duration
|
||||
let time = CMTime(seconds: seconds, preferredTimescale: 1000)
|
||||
setCurrentTime(time)
|
||||
}
|
||||
|
||||
/// Updates the playback rate on the timebase (1.0 = playing, 0.0 = paused)
|
||||
func setPlaybackRate(_ rate: Float) {
|
||||
if let tb = timebase {
|
||||
CMTimebaseSetRate(tb, rate: Float64(rate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AVPictureInPictureControllerDelegate
|
||||
|
||||
Reference in New Issue
Block a user