From 0b274f92bd4fa51398edca74d53e9e1650e49877 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Sat, 21 May 2022 21:30:01 +0200 Subject: [PATCH] Prefer VP9/WEBM over H.264/MP4 (fix #128) --- Model/Player/Backends/MPVBackend.swift | 21 ++++++++- Model/Stream.swift | 63 +++++++++++++++++++------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index c41af091..2c511bba 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -69,16 +69,33 @@ final class MPVBackend: PlayerBackend { clientTimer.eventHandler = getClientUpdates } + typealias AreInIncreasingOrder = (Stream, Stream) -> Bool + func bestPlayable(_ streams: [Stream], maxResolution: ResolutionSetting) -> Stream? { streams .filter { $0.kind == .adaptive && $0.resolution <= maxResolution.value } - .max { $0.resolution < $1.resolution } ?? + .max { lhs, rhs in + let predicates: [AreInIncreasingOrder] = [ + { $0.format > $1.format }, + { $0.resolution < $1.resolution } + ] + + for predicate in predicates { + if !predicate(lhs, rhs), !predicate(rhs, lhs) { + continue + } + + return predicate(lhs, rhs) + } + + return false + } ?? streams.first { $0.kind == .hls } ?? streams.first } func canPlay(_ stream: Stream) -> Bool { - stream.resolution != .unknown && stream.format != "AV1" + stream.resolution != .unknown && stream.format != .av1 } func playStream(_ stream: Stream, of video: Video, preservingTime: Bool, upgrading _: Bool) { diff --git a/Model/Stream.swift b/Model/Stream.swift index 5f600603..3ad7f9cd 100644 --- a/Model/Stream.swift +++ b/Model/Stream.swift @@ -79,6 +79,49 @@ class Stream: Equatable, Hashable, Identifiable { } } + enum Format: String, Comparable { + case webm + case avc1 + case av1 + case mp4 + case unknown + + private var sortOrder: Int { + switch self { + case .webm: + return 0 + case .mp4: + return 1 + case .avc1: + return 2 + case .av1: + return 3 + case .unknown: + return 4 + } + } + + static func < (lhs: Self, rhs: Self) -> Bool { + lhs.sortOrder < rhs.sortOrder + } + + static func from(_ string: String) -> Self { + let lowercased = string.lowercased() + + if lowercased.contains("webm") { + return .webm + } else if lowercased.contains("avc1") { + return .avc1 + } else if lowercased.contains("av01") { + return .av1 + } else if lowercased.contains("mpeg_4") || lowercased.contains("mp4") { + return .mp4 + } else { + return .unknown + } + } + } + let id = UUID() var instance: Instance! @@ -88,6 +131,7 @@ class Stream: Equatable, Hashable, Identifiable { var resolution: Resolution! var kind: Kind! + var format: Format! var encoding: String! var videoFormat: String! @@ -109,7 +153,7 @@ class Stream: Equatable, Hashable, Identifiable { self.resolution = resolution self.kind = kind self.encoding = encoding - self.videoFormat = videoFormat + format = .from(videoFormat ?? "") } var quality: String { @@ -120,23 +164,8 @@ class Stream: Equatable, Hashable, Identifiable { return kind == .hls ? "adaptive (HLS)" : "\(resolution.name)\(kind == .stream ? " (\(kind.rawValue))" : "")" } - var format: String { - let lowercasedFormat = (videoFormat ?? "unknown").lowercased() - if lowercasedFormat.contains("webm") { - return "WEBM" - } else if lowercasedFormat.contains("avc1") { - return "avc1" - } else if lowercasedFormat.contains("av01") { - return "AV1" - } else if lowercasedFormat.contains("mpeg_4") || lowercasedFormat.contains("mp4") { - return "MP4" - } else { - return lowercasedFormat - } - } - var description: String { - let formatString = format == "unknown" ? "" : " (\(format))" + let formatString = format == .unknown ? "" : " (\(format.rawValue))" return "\(quality)\(formatString) - \(instance?.description ?? "")" }