Right progress ticks for VLCPlayer
This commit is contained in:
parent
6712ef279d
commit
99445e387c
|
@ -14,9 +14,9 @@ class NativePlayerViewController: AVPlayerViewController {
|
|||
|
||||
let viewModel: VideoPlayerViewModel
|
||||
|
||||
var timeObserverToken: Any?
|
||||
private var timeObserverToken: Any?
|
||||
|
||||
var lastProgressTicks: Int64 = 0
|
||||
private var lastProgressTicks: Int64 = 0
|
||||
|
||||
init(viewModel: VideoPlayerViewModel) {
|
||||
|
||||
|
@ -99,15 +99,14 @@ class NativePlayerViewController: AVPlayerViewController {
|
|||
|
||||
private func play() {
|
||||
player?.play()
|
||||
|
||||
viewModel.sendPlayReport(startTimeTicks: viewModel.item.userData?.playbackPositionTicks ?? 0)
|
||||
viewModel.sendPlayReport()
|
||||
}
|
||||
|
||||
private func sendProgressReport(seconds: Double) {
|
||||
viewModel.sendProgressReport(ticks: Int64(seconds) * 10_000_000)
|
||||
viewModel.sendProgressReport()
|
||||
}
|
||||
|
||||
private func stop() {
|
||||
viewModel.sendStopReport(ticks: 10_000_000)
|
||||
viewModel.sendStopReport()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class VLCPlayerViewController: UIViewController {
|
|||
private let viewModel: VideoPlayerViewModel
|
||||
private var vlcMediaPlayer = VLCMediaPlayer()
|
||||
private var lastPlayerTicks: Int64
|
||||
private var lastProgressReportTicks: Int64
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var overlayDismissTimer: Timer?
|
||||
|
||||
|
@ -52,6 +53,7 @@ class VLCPlayerViewController: UIViewController {
|
|||
self.viewModel = viewModel
|
||||
|
||||
self.lastPlayerTicks = viewModel.item.userData?.playbackPositionTicks ?? 0
|
||||
self.lastProgressReportTicks = viewModel.item.userData?.playbackPositionTicks ?? 0
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
|
@ -243,7 +245,7 @@ extension VLCPlayerViewController {
|
|||
func startPlayback() {
|
||||
vlcMediaPlayer.play()
|
||||
|
||||
viewModel.sendPlayReport(startTimeTicks: viewModel.item.userData?.playbackPositionTicks ?? 0)
|
||||
viewModel.sendPlayReport()
|
||||
|
||||
// 1 second = 10,000,000 ticks
|
||||
let startTicks: Int64 = viewModel.item.userData?.playbackPositionTicks ?? 0
|
||||
|
@ -327,18 +329,18 @@ extension VLCPlayerViewController: VLCMediaPlayerDelegate {
|
|||
|
||||
viewModel.sliderPercentage = Double(vlcMediaPlayer.position)
|
||||
|
||||
if abs(currentPlayerTicks - lastPlayerTicks) >= 10000 {
|
||||
if abs(currentPlayerTicks - lastPlayerTicks) >= 10_000 {
|
||||
|
||||
viewModel.playerState = VLCMediaPlayerState.playing
|
||||
}
|
||||
|
||||
lastPlayerTicks = currentPlayerTicks
|
||||
|
||||
// if CACurrentMediaTime() - lastProgressReportTime > 5 {
|
||||
// mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
|
||||
// sendProgressReport(eventName: "timeupdate")
|
||||
// lastProgressReportTime = CACurrentMediaTime()
|
||||
// }
|
||||
|
||||
if abs(lastProgressReportTicks - currentPlayerTicks) >= 500_000_000 {
|
||||
viewModel.sendProgressReport()
|
||||
|
||||
lastProgressReportTicks = currentPlayerTicks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,7 +363,7 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
|
|||
func didSelectClose() {
|
||||
vlcMediaPlayer.stop()
|
||||
|
||||
viewModel.sendStopReport(ticks: currentPlayerTicks)
|
||||
viewModel.sendStopReport()
|
||||
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
@ -399,12 +401,20 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
|
|||
vlcMediaPlayer.jumpBackward(jumpBackwardLength.rawValue)
|
||||
|
||||
restartOverlayDismissTimer()
|
||||
|
||||
viewModel.sendProgressReport()
|
||||
|
||||
self.lastProgressReportTicks = currentPlayerTicks
|
||||
}
|
||||
|
||||
func didSelectForward() {
|
||||
vlcMediaPlayer.jumpForward(jumpForwardLength.rawValue)
|
||||
|
||||
restartOverlayDismissTimer()
|
||||
|
||||
viewModel.sendProgressReport()
|
||||
|
||||
self.lastProgressReportTicks = currentPlayerTicks
|
||||
}
|
||||
|
||||
func didSelectMain() {
|
||||
|
@ -414,9 +424,11 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
|
|||
vlcMediaPlayer.play()
|
||||
restartOverlayDismissTimer()
|
||||
case .playing:
|
||||
viewModel.sendPauseReport(paused: true)
|
||||
vlcMediaPlayer.pause()
|
||||
restartOverlayDismissTimer(interval: 5)
|
||||
case .paused:
|
||||
viewModel.sendPauseReport(paused: false)
|
||||
vlcMediaPlayer.play()
|
||||
default: ()
|
||||
}
|
||||
|
@ -433,6 +445,8 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
|
|||
}
|
||||
|
||||
func didEndScrubbing(position: Double) {
|
||||
// Necessary math as VLCMediaPlayer doesn't work well
|
||||
// by just setting the position
|
||||
let videoPosition = Double(vlcMediaPlayer.time.intValue / 1000)
|
||||
let videoDuration = Double(viewModel.item.runTimeTicks! / 10_000_000)
|
||||
let secondsScrubbedTo = round(viewModel.sliderPercentage * videoDuration)
|
||||
|
@ -445,5 +459,9 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
|
|||
}
|
||||
|
||||
restartOverlayDismissTimer()
|
||||
|
||||
viewModel.sendProgressReport()
|
||||
|
||||
self.lastProgressReportTicks = currentPlayerTicks
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import UIKit
|
||||
|
||||
#if os(tvOS)
|
||||
import TVVLCKit
|
||||
#else
|
||||
import MobileVLCKit
|
||||
#endif
|
||||
import Stinsen
|
||||
import UIKit
|
||||
|
||||
final class VideoPlayerViewModel: ObservableObject {
|
||||
final class VideoPlayerViewModel: ViewModel {
|
||||
|
||||
// Manually kept state because VLCKit doesn't properly set "played"
|
||||
// on the VLCMediaPlayer object
|
||||
|
@ -55,11 +55,18 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
// Ticks of the time the media has begun
|
||||
var startTimeTicks: Int64?
|
||||
|
||||
var currentSeconds: Double {
|
||||
let videoDuration = Double(item.runTimeTicks! / 10_000_000)
|
||||
return round(sliderPercentage * videoDuration)
|
||||
}
|
||||
|
||||
var currentSecondTicks: Int64 {
|
||||
return Int64(currentSeconds) * 10_000_000
|
||||
}
|
||||
|
||||
// Necessary PassthroughSubject to capture manual scrubbing from sliders
|
||||
let sliderScrubbingSubject = PassthroughSubject<VideoPlayerViewModel, Never>()
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(item: BaseItemDto,
|
||||
title: String,
|
||||
subtitle: String?,
|
||||
|
@ -95,15 +102,16 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
self.selectedAudioStreamIndex = selectedAudioStreamIndex
|
||||
self.selectedSubtitleStreamIndex = selectedSubtitleStreamIndex
|
||||
|
||||
super.init()
|
||||
|
||||
self.sliderPercentageChanged(newValue: (item.userData?.playedPercentage ?? 0) / 100)
|
||||
}
|
||||
|
||||
private func sliderPercentageChanged(newValue: Double) {
|
||||
let videoDuration = Double(item.runTimeTicks! / 10_000_000)
|
||||
let secondsScrubbedTo = round(sliderPercentage * videoDuration)
|
||||
let secondsScrubbedRemaining = videoDuration - secondsScrubbedTo
|
||||
let secondsScrubbedRemaining = videoDuration - currentSeconds
|
||||
|
||||
leftLabelText = calculateTimeText(from: secondsScrubbedTo)
|
||||
leftLabelText = calculateTimeText(from: currentSeconds)
|
||||
rightLabelText = calculateTimeText(from: secondsScrubbedRemaining)
|
||||
}
|
||||
|
||||
|
@ -125,9 +133,9 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
return timeText
|
||||
}
|
||||
|
||||
func sendPlayReport(startTimeTicks: Int64) {
|
||||
func sendPlayReport() {
|
||||
|
||||
self.startTimeTicks = startTimeTicks
|
||||
self.startTimeTicks = Int64(Date().timeIntervalSince1970) * 10_000_000
|
||||
|
||||
let startInfo = PlaybackStartInfo(canSeek: true,
|
||||
item: item,
|
||||
|
@ -153,16 +161,46 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
|
||||
PlaystateAPI.reportPlaybackStart(playbackStartInfo: startInfo)
|
||||
.sink { completion in
|
||||
print(completion)
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
print("Playback start report sent!")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func sendProgressReport(ticks: Int64) {
|
||||
func sendPauseReport(paused: Bool) {
|
||||
let startInfo = PlaybackStartInfo(canSeek: true,
|
||||
item: item,
|
||||
itemId: item.id,
|
||||
sessionId: response.playSessionId,
|
||||
mediaSourceId: item.id,
|
||||
audioStreamIndex: audioStreams.first(where: { $0.index! == response.mediaSources?.first?.defaultAudioStreamIndex! })?.index,
|
||||
subtitleStreamIndex: subtitleStreams.first(where: { $0.index! == response.mediaSources?.first?.defaultSubtitleStreamIndex ?? -1 })?.index,
|
||||
isPaused: paused,
|
||||
isMuted: false,
|
||||
positionTicks: currentSecondTicks,
|
||||
playbackStartTimeTicks: startTimeTicks,
|
||||
volumeLevel: 100,
|
||||
brightness: 100,
|
||||
aspectRatio: nil,
|
||||
playMethod: .directPlay,
|
||||
liveStreamId: nil,
|
||||
playSessionId: response.playSessionId,
|
||||
repeatMode: .repeatNone,
|
||||
nowPlayingQueue: nil,
|
||||
playlistItemId: "playlistItem0"
|
||||
)
|
||||
|
||||
print("Progress ticks: \(ticks)")
|
||||
PlaystateAPI.reportPlaybackStart(playbackStartInfo: startInfo)
|
||||
.sink { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
print("Pause report sent!")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func sendProgressReport() {
|
||||
|
||||
let progressInfo = PlaybackProgressInfo(canSeek: true,
|
||||
item: item,
|
||||
|
@ -173,7 +211,7 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
subtitleStreamIndex: subtitleStreams.first(where: { $0.index! == response.mediaSources?.first?.defaultSubtitleStreamIndex ?? -1 })?.index,
|
||||
isPaused: false,
|
||||
isMuted: false,
|
||||
positionTicks: ticks,
|
||||
positionTicks: currentSecondTicks,
|
||||
playbackStartTimeTicks: startTimeTicks,
|
||||
volumeLevel: nil,
|
||||
brightness: nil,
|
||||
|
@ -187,20 +225,20 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
|
||||
PlaystateAPI.reportPlaybackProgress(playbackProgressInfo: progressInfo)
|
||||
.sink { completion in
|
||||
print(completion)
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
print("Playback progress sent!")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func sendStopReport(ticks: Int64) {
|
||||
func sendStopReport() {
|
||||
|
||||
let stopInfo = PlaybackStopInfo(item: item,
|
||||
itemId: item.id,
|
||||
sessionId: response.playSessionId,
|
||||
mediaSourceId: item.id,
|
||||
positionTicks: ticks,
|
||||
positionTicks: currentSecondTicks,
|
||||
liveStreamId: nil,
|
||||
playSessionId: response.playSessionId,
|
||||
failed: nil,
|
||||
|
@ -210,7 +248,7 @@ final class VideoPlayerViewModel: ObservableObject {
|
|||
|
||||
PlaystateAPI.reportPlaybackStopped(playbackStopInfo: stopInfo)
|
||||
.sink { completion in
|
||||
print(completion)
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
print("Playback stop report sent!")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue