Improve performance and add statistics for MPV

This commit is contained in:
Arkadiusz Fal 2022-06-17 12:27:01 +02:00
parent 0158048648
commit 42264b3818
7 changed files with 86 additions and 33 deletions

View File

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

View File

@ -35,15 +35,17 @@ final class MPVClient: ObservableObject {
exit(1) exit(1)
} }
#if DEBUG
checkError(mpv_request_log_messages(mpv, "debug"))
#else
checkError(mpv_request_log_messages(mpv, "warn")) 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()
} }
} }

View File

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

View File

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

View File

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

View File

@ -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 {
VStack(alignment: .leading) {
PlayerSettings() PlayerSettings()
}
.injectFixtureEnvironmentObjects() .injectFixtureEnvironmentObjects()
} }
} }

View File

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