mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Improve performance and add statistics for MPV
This commit is contained in:
parent
81b8cbd5f1
commit
15cdde17a6
@ -6,7 +6,7 @@ import Logging
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
final class MPVBackend: PlayerBackend {
|
final class MPVBackend: PlayerBackend {
|
||||||
static var clientUpdatesInterval = 1.0
|
static var controlsUpdateInterval = 0.5
|
||||||
|
|
||||||
private var logger = Logger(label: "mpv-backend")
|
private var logger = Logger(label: "mpv-backend")
|
||||||
|
|
||||||
@ -72,11 +72,19 @@ final class MPVBackend: PlayerBackend {
|
|||||||
client?.frameDropCount ?? 0
|
client?.frameDropCount ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outputFps: Double {
|
||||||
|
client?.outputFps ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var hwDecoder: String {
|
||||||
|
client?.hwDecoder ?? "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
init(model: PlayerModel, controls: PlayerControlsModel? = nil) {
|
init(model: PlayerModel, controls: PlayerControlsModel? = nil) {
|
||||||
self.model = model
|
self.model = model
|
||||||
self.controls = controls
|
self.controls = controls
|
||||||
|
|
||||||
clientTimer = .init(timeInterval: Self.clientUpdatesInterval)
|
clientTimer = .init(timeInterval: Self.controlsUpdateInterval)
|
||||||
clientTimer.eventHandler = getClientUpdates
|
clientTimer.eventHandler = getClientUpdates
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,8 +349,6 @@ final class MPVBackend: PlayerBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handle(_ event: UnsafePointer<mpv_event>!) {
|
func handle(_ event: UnsafePointer<mpv_event>!) {
|
||||||
logger.info("\(String(cString: mpv_event_name(event.pointee.event_id)))")
|
|
||||||
|
|
||||||
switch event.pointee.event_id {
|
switch event.pointee.event_id {
|
||||||
case MPV_EVENT_SHUTDOWN:
|
case MPV_EVENT_SHUTDOWN:
|
||||||
mpv_destroy(client.mpv)
|
mpv_destroy(client.mpv)
|
||||||
@ -350,7 +356,7 @@ final class MPVBackend: PlayerBackend {
|
|||||||
|
|
||||||
case MPV_EVENT_LOG_MESSAGE:
|
case MPV_EVENT_LOG_MESSAGE:
|
||||||
let logmsg = UnsafeMutablePointer<mpv_event_log_message>(OpaquePointer(event.pointee.data))
|
let logmsg = UnsafeMutablePointer<mpv_event_log_message>(OpaquePointer(event.pointee.data))
|
||||||
logger.info(.init(stringLiteral: "log: \(String(cString: (logmsg!.pointee.prefix)!)), "
|
logger.info(.init(stringLiteral: "\(String(cString: (logmsg!.pointee.prefix)!)), "
|
||||||
+ "\(String(cString: (logmsg!.pointee.level)!)), "
|
+ "\(String(cString: (logmsg!.pointee.level)!)), "
|
||||||
+ "\(String(cString: (logmsg!.pointee.text)!))"))
|
+ "\(String(cString: (logmsg!.pointee.text)!))"))
|
||||||
|
|
||||||
@ -375,7 +381,7 @@ final class MPVBackend: PlayerBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger.info(.init(stringLiteral: "event: \(String(cString: mpv_event_name(event.pointee.event_id)))"))
|
logger.info(.init(stringLiteral: "UNHANDLED event: \(String(cString: mpv_event_name(event.pointee.event_id)))"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,15 +35,17 @@ final class MPVClient: ObservableObject {
|
|||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkError(mpv_request_log_messages(mpv, "warn"))
|
#if DEBUG
|
||||||
|
checkError(mpv_request_log_messages(mpv, "debug"))
|
||||||
|
#else
|
||||||
|
checkError(mpv_request_log_messages(mpv, "warn"))
|
||||||
|
#endif
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
checkError(mpv_set_option_string(mpv, "input-media-keys", "yes"))
|
checkError(mpv_set_option_string(mpv, "input-media-keys", "yes"))
|
||||||
#else
|
|
||||||
checkError(mpv_set_option_string(mpv, "hwdec", "yes"))
|
|
||||||
checkError(mpv_set_option_string(mpv, "override-display-fps", "\(UIScreen.main.maximumFramesPerSecond)"))
|
|
||||||
checkError(mpv_set_option_string(mpv, "video-sync", "display-resample"))
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
checkError(mpv_set_option_string(mpv, "hwdec", "auto-safe"))
|
||||||
checkError(mpv_set_option_string(mpv, "vo", "libmpv"))
|
checkError(mpv_set_option_string(mpv, "vo", "libmpv"))
|
||||||
|
|
||||||
checkError(mpv_initialize(mpv))
|
checkError(mpv_initialize(mpv))
|
||||||
@ -55,7 +57,7 @@ final class MPVClient: ObservableObject {
|
|||||||
extra_exts: nil
|
extra_exts: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
queue = DispatchQueue(label: "mpv", qos: .background)
|
queue = DispatchQueue(label: "mpv", qos: .userInteractive)
|
||||||
|
|
||||||
withUnsafeMutablePointer(to: &initParams) { initParams in
|
withUnsafeMutablePointer(to: &initParams) { initParams in
|
||||||
var params = [
|
var params = [
|
||||||
@ -145,6 +147,14 @@ final class MPVClient: ObservableObject {
|
|||||||
mpv.isNil ? 0 : getInt("frame-drop-count")
|
mpv.isNil ? 0 : getInt("frame-drop-count")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outputFps: Double {
|
||||||
|
mpv.isNil ? 0.0 : getDouble("estimated-vf-fps")
|
||||||
|
}
|
||||||
|
|
||||||
|
var hwDecoder: String {
|
||||||
|
mpv.isNil ? "unknown" : (getString("hwdec-current") ?? "unknown")
|
||||||
|
}
|
||||||
|
|
||||||
var duration: CMTime {
|
var duration: CMTime {
|
||||||
CMTime.secondsInDefaultTimescale(mpv.isNil ? -1 : getDouble("duration"))
|
CMTime.secondsInDefaultTimescale(mpv.isNil ? -1 : getDouble("duration"))
|
||||||
}
|
}
|
||||||
@ -324,11 +334,11 @@ final class MPVClient: ObservableObject {
|
|||||||
private func glUpdate(_ ctx: UnsafeMutableRawPointer?) {
|
private func glUpdate(_ ctx: UnsafeMutableRawPointer?) {
|
||||||
let glView = unsafeBitCast(ctx, to: MPVOGLView.self)
|
let glView = unsafeBitCast(ctx, to: MPVOGLView.self)
|
||||||
|
|
||||||
glView.queue.async {
|
guard glView.needsDrawing else {
|
||||||
guard glView.needsDrawing else {
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
glView.queue.async {
|
||||||
glView.display()
|
glView.display()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,6 +561,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
controls.playingFullscreen = !isFullScreen
|
controls.playingFullscreen = !isFullScreen
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
||||||
|
self?.setNeedsDrawing(true)
|
||||||
|
}
|
||||||
|
|
||||||
if controls.playingFullscreen {
|
if controls.playingFullscreen {
|
||||||
guard !(UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape ?? true) else {
|
guard !(UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape ?? true) else {
|
||||||
return
|
return
|
||||||
@ -569,10 +573,6 @@ final class PlayerModel: ObservableObject {
|
|||||||
} else {
|
} else {
|
||||||
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in
|
|
||||||
self?.setNeedsDrawing(true)
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,15 +82,13 @@ extension Defaults.Keys {
|
|||||||
|
|
||||||
static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.favorites, .subscriptions, .trending, .playlists])
|
static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.favorites, .subscriptions, .trending, .playlists])
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
static let enableBetaChannel = Key<Bool>("enableBetaChannel", default: false)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||||
static let lockOrientationInFullScreen = Key<Bool>("lockOrientationInFullScreen", default: false)
|
static let lockOrientationInFullScreen = Key<Bool>("lockOrientationInFullScreen", default: false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ResolutionSetting: String, CaseIterable, Defaults.Serializable {
|
enum ResolutionSetting: String, CaseIterable, Defaults.Serializable {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import Defaults
|
||||||
import Foundation
|
import Foundation
|
||||||
import SDWebImageSwiftUI
|
import SDWebImageSwiftUI
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
@ -22,6 +23,8 @@ struct PlayerControls: View {
|
|||||||
@FocusState private var focusedField: Field?
|
@FocusState private var focusedField: Field?
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@Default(.showMPVPlaybackStats) private var showMPVPlaybackStats
|
||||||
|
|
||||||
init(player: PlayerModel, thumbnails: ThumbnailsModel) {
|
init(player: PlayerModel, thumbnails: ThumbnailsModel) {
|
||||||
self.player = player
|
self.player = player
|
||||||
self.thumbnails = thumbnails
|
self.thumbnails = thumbnails
|
||||||
@ -49,6 +52,10 @@ struct PlayerControls: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
|
if player.activeBackend == .mpv, showMPVPlaybackStats {
|
||||||
|
mpvPlaybackStats
|
||||||
|
}
|
||||||
|
|
||||||
timeline
|
timeline
|
||||||
.offset(y: 10)
|
.offset(y: 10)
|
||||||
.zIndex(1)
|
.zIndex(1)
|
||||||
@ -103,6 +110,30 @@ struct PlayerControls: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mpvPlaybackStats: some View {
|
||||||
|
HStack {
|
||||||
|
Group {
|
||||||
|
Text("hw decoder: \(player.mpvBackend.hwDecoder)")
|
||||||
|
Text("dropped: \(player.mpvBackend.frameDropCount)")
|
||||||
|
Text("output fps: \(player.mpvBackend.outputFps)")
|
||||||
|
}
|
||||||
|
.padding(4)
|
||||||
|
#if os(macOS)
|
||||||
|
.background(VisualEffectBlur(material: .hudWindow))
|
||||||
|
#elseif os(iOS)
|
||||||
|
.background(VisualEffectBlur(blurStyle: .systemThinMaterial))
|
||||||
|
#else
|
||||||
|
.background(.thinMaterial)
|
||||||
|
#endif
|
||||||
|
.mask(RoundedRectangle(cornerRadius: 3))
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
#if !os(tvOS)
|
||||||
|
.font(.system(size: 9))
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
var timeline: some View {
|
var timeline: some View {
|
||||||
TimelineView(duration: durationBinding, current: currentTimeBinding, cornerRadius: 0)
|
TimelineView(duration: durationBinding, current: currentTimeBinding, cornerRadius: 0)
|
||||||
}
|
}
|
||||||
@ -164,14 +195,11 @@ struct PlayerControls: View {
|
|||||||
var buttonsBar: some View {
|
var buttonsBar: some View {
|
||||||
HStack {
|
HStack {
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
#if os(iOS)
|
|
||||||
hidePlayerButton
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fullscreenButton
|
fullscreenButton
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
pipButton
|
pipButton
|
||||||
|
.padding(.leading, 5)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -257,8 +285,7 @@ struct PlayerControls: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var backendButton: some View {
|
private var backendButton: some View {
|
||||||
let label = "\(player.activeBackend.label)\(player.activeBackend == .mpv ? " - \(player.mpvBackend.frameDropCount)" : "")"
|
button(player.activeBackend.label, width: 100) {
|
||||||
return button(label, width: 120) {
|
|
||||||
player.saveTime {
|
player.saveTime {
|
||||||
player.changeActiveBackend(from: player.activeBackend, to: player.activeBackend.next())
|
player.changeActiveBackend(from: player.activeBackend, to: player.activeBackend.next())
|
||||||
model.resetTimer()
|
model.resetTimer()
|
||||||
|
@ -28,6 +28,8 @@ struct PlayerSettings: View {
|
|||||||
|
|
||||||
@Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike
|
@Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike
|
||||||
|
|
||||||
|
@Default(.showMPVPlaybackStats) private var showMPVPlaybackStats
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private var idiom: UIUserInterfaceIdiom {
|
private var idiom: UIUserInterfaceIdiom {
|
||||||
UIDevice.current.userInterfaceIdiom
|
UIDevice.current.userInterfaceIdiom
|
||||||
@ -103,6 +105,10 @@ struct PlayerSettings: View {
|
|||||||
lockOrientationInFullScreenToggle
|
lockOrientationInFullScreenToggle
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Section(header: SettingsHeader(text: "Debugging")) {
|
||||||
|
showMPVPlaybackStatsToggle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,11 +238,17 @@ struct PlayerSettings: View {
|
|||||||
Toggle("Close PiP and open player when application enters foreground", isOn: $closePiPAndOpenPlayerOnEnteringForeground)
|
Toggle("Close PiP and open player when application enters foreground", isOn: $closePiPAndOpenPlayerOnEnteringForeground)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private var showMPVPlaybackStatsToggle: some View {
|
||||||
|
Toggle("Show MPV playback statistics", isOn: $showMPVPlaybackStats)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PlaybackSettings_Previews: PreviewProvider {
|
struct PlaybackSettings_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
PlayerSettings()
|
VStack(alignment: .leading) {
|
||||||
.injectFixtureEnvironmentObjects()
|
PlayerSettings()
|
||||||
|
}
|
||||||
|
.injectFixtureEnvironmentObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ struct SettingsView: View {
|
|||||||
case .browsing:
|
case .browsing:
|
||||||
return 350
|
return 350
|
||||||
case .player:
|
case .player:
|
||||||
return 450
|
return 500
|
||||||
case .history:
|
case .history:
|
||||||
return 480
|
return 480
|
||||||
case .sponsorBlock:
|
case .sponsorBlock:
|
||||||
|
Loading…
Reference in New Issue
Block a user