From 15cdde17a677803427870d6cdf2ea9812e97dc52 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Fri, 17 Jun 2022 12:27:01 +0200 Subject: [PATCH] Improve performance and add statistics for MPV --- Model/Player/Backends/MPVBackend.swift | 18 ++++++---- Model/Player/Backends/MPVClient.swift | 30 ++++++++++------ Model/Player/PlayerModel.swift | 8 ++--- Shared/Defaults.swift | 6 ++-- Shared/Player/Controls/PlayerControls.swift | 39 +++++++++++++++++---- Shared/Settings/PlayerSettings.swift | 16 +++++++-- Shared/Settings/SettingsView.swift | 2 +- 7 files changed, 86 insertions(+), 33 deletions(-) diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index 410f0145..37fcc291 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -6,7 +6,7 @@ import Logging import SwiftUI final class MPVBackend: PlayerBackend { - static var clientUpdatesInterval = 1.0 + static var controlsUpdateInterval = 0.5 private var logger = Logger(label: "mpv-backend") @@ -72,11 +72,19 @@ final class MPVBackend: PlayerBackend { client?.frameDropCount ?? 0 } + var outputFps: Double { + client?.outputFps ?? 0 + } + + var hwDecoder: String { + client?.hwDecoder ?? "unknown" + } + init(model: PlayerModel, controls: PlayerControlsModel? = nil) { self.model = model self.controls = controls - clientTimer = .init(timeInterval: Self.clientUpdatesInterval) + clientTimer = .init(timeInterval: Self.controlsUpdateInterval) clientTimer.eventHandler = getClientUpdates } @@ -341,8 +349,6 @@ final class MPVBackend: PlayerBackend { } func handle(_ event: UnsafePointer!) { - logger.info("\(String(cString: mpv_event_name(event.pointee.event_id)))") - switch event.pointee.event_id { case MPV_EVENT_SHUTDOWN: mpv_destroy(client.mpv) @@ -350,7 +356,7 @@ final class MPVBackend: PlayerBackend { case MPV_EVENT_LOG_MESSAGE: let logmsg = UnsafeMutablePointer(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.text)!))")) @@ -375,7 +381,7 @@ final class MPVBackend: PlayerBackend { } 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)))")) } } diff --git a/Model/Player/Backends/MPVClient.swift b/Model/Player/Backends/MPVClient.swift index 52ae8609..bbcf2bd5 100644 --- a/Model/Player/Backends/MPVClient.swift +++ b/Model/Player/Backends/MPVClient.swift @@ -35,15 +35,17 @@ final class MPVClient: ObservableObject { 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) 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 + + checkError(mpv_set_option_string(mpv, "hwdec", "auto-safe")) checkError(mpv_set_option_string(mpv, "vo", "libmpv")) checkError(mpv_initialize(mpv)) @@ -55,7 +57,7 @@ final class MPVClient: ObservableObject { extra_exts: nil ) - queue = DispatchQueue(label: "mpv", qos: .background) + queue = DispatchQueue(label: "mpv", qos: .userInteractive) withUnsafeMutablePointer(to: &initParams) { initParams in var params = [ @@ -145,6 +147,14 @@ final class MPVClient: ObservableObject { 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 { CMTime.secondsInDefaultTimescale(mpv.isNil ? -1 : getDouble("duration")) } @@ -324,11 +334,11 @@ final class MPVClient: ObservableObject { private func glUpdate(_ ctx: UnsafeMutableRawPointer?) { let glView = unsafeBitCast(ctx, to: MPVOGLView.self) - glView.queue.async { - guard glView.needsDrawing else { - return - } + guard glView.needsDrawing else { + return + } + glView.queue.async { glView.display() } } diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index df9f67f5..5895cc83 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -561,6 +561,10 @@ final class PlayerModel: ObservableObject { controls.playingFullscreen = !isFullScreen #if os(iOS) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in + self?.setNeedsDrawing(true) + } + if controls.playingFullscreen { guard !(UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape ?? true) else { return @@ -569,10 +573,6 @@ final class PlayerModel: ObservableObject { } else { Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait) } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak self] in - self?.setNeedsDrawing(true) - } #endif } diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index e34291cf..f8ac9e86 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -82,15 +82,13 @@ extension Defaults.Keys { static let visibleSections = Key>("visibleSections", default: [.favorites, .subscriptions, .trending, .playlists]) - #if os(macOS) - static let enableBetaChannel = Key("enableBetaChannel", default: false) - #endif - #if os(iOS) static let honorSystemOrientationLock = Key("honorSystemOrientationLock", default: true) static let enterFullscreenInLandscape = Key("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone) static let lockOrientationInFullScreen = Key("lockOrientationInFullScreen", default: false) #endif + + static let showMPVPlaybackStats = Key("showMPVPlaybackStats", default: false) } enum ResolutionSetting: String, CaseIterable, Defaults.Serializable { diff --git a/Shared/Player/Controls/PlayerControls.swift b/Shared/Player/Controls/PlayerControls.swift index 8fe10190..b12e270b 100644 --- a/Shared/Player/Controls/PlayerControls.swift +++ b/Shared/Player/Controls/PlayerControls.swift @@ -1,3 +1,4 @@ +import Defaults import Foundation import SDWebImageSwiftUI import SwiftUI @@ -22,6 +23,8 @@ struct PlayerControls: View { @FocusState private var focusedField: Field? #endif + @Default(.showMPVPlaybackStats) private var showMPVPlaybackStats + init(player: PlayerModel, thumbnails: ThumbnailsModel) { self.player = player self.thumbnails = thumbnails @@ -49,6 +52,10 @@ struct PlayerControls: View { Spacer() Group { + if player.activeBackend == .mpv, showMPVPlaybackStats { + mpvPlaybackStats + } + timeline .offset(y: 10) .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 { TimelineView(duration: durationBinding, current: currentTimeBinding, cornerRadius: 0) } @@ -164,14 +195,11 @@ struct PlayerControls: View { var buttonsBar: some View { HStack { #if !os(tvOS) - #if os(iOS) - hidePlayerButton - #endif - fullscreenButton #if os(iOS) pipButton + .padding(.leading, 5) #endif Spacer() @@ -257,8 +285,7 @@ struct PlayerControls: View { } private var backendButton: some View { - let label = "\(player.activeBackend.label)\(player.activeBackend == .mpv ? " - \(player.mpvBackend.frameDropCount)" : "")" - return button(label, width: 120) { + button(player.activeBackend.label, width: 100) { player.saveTime { player.changeActiveBackend(from: player.activeBackend, to: player.activeBackend.next()) model.resetTimer() diff --git a/Shared/Settings/PlayerSettings.swift b/Shared/Settings/PlayerSettings.swift index 19857c0b..714f32b4 100644 --- a/Shared/Settings/PlayerSettings.swift +++ b/Shared/Settings/PlayerSettings.swift @@ -28,6 +28,8 @@ struct PlayerSettings: View { @Default(.enableReturnYouTubeDislike) private var enableReturnYouTubeDislike + @Default(.showMPVPlaybackStats) private var showMPVPlaybackStats + #if os(iOS) private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom @@ -103,6 +105,10 @@ struct PlayerSettings: View { lockOrientationInFullScreenToggle } #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) } #endif + + private var showMPVPlaybackStatsToggle: some View { + Toggle("Show MPV playback statistics", isOn: $showMPVPlaybackStats) + } } struct PlaybackSettings_Previews: PreviewProvider { static var previews: some View { - PlayerSettings() - .injectFixtureEnvironmentObjects() + VStack(alignment: .leading) { + PlayerSettings() + } + .injectFixtureEnvironmentObjects() } } diff --git a/Shared/Settings/SettingsView.swift b/Shared/Settings/SettingsView.swift index 624ab435..9fd37eb9 100644 --- a/Shared/Settings/SettingsView.swift +++ b/Shared/Settings/SettingsView.swift @@ -171,7 +171,7 @@ struct SettingsView: View { case .browsing: return 350 case .player: - return 450 + return 500 case .history: return 480 case .sponsorBlock: