Right progress ticks for VLCPlayer

This commit is contained in:
Ethan Pippin 2021-12-28 16:45:45 -07:00
parent 6712ef279d
commit 99445e387c
3 changed files with 88 additions and 33 deletions

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -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!")
}