diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 74c75cf3..cd5a57a0 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -789,7 +789,7 @@ repositoryURL = "https://github.com/edde746/MPVKit"; requirement = { kind = revision; - revision = 1fe345d46bac91f4e85239ad268430e037040b2b; + revision = 245b69dce22156e81b79c84cfcb748358b674e47; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 42fd6d7f..c7426475 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -40,7 +40,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/edde746/MPVKit", "state" : { - "revision" : "1fe345d46bac91f4e85239ad268430e037040b2b" + "revision" : "245b69dce22156e81b79c84cfcb748358b674e47" } }, { diff --git a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index dae38a86..c7426475 100644 --- a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -40,7 +40,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/edde746/MPVKit", "state" : { - "revision" : "1fc33029bc0317583866c62811dc0ab2aa2415b6" + "revision" : "245b69dce22156e81b79c84cfcb748358b674e47" } }, { diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 18e0dce0..18b46655 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -763,7 +763,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin _audioFocusFuture = currentPlayer.requestAudioFocus(); _audioFocusFuture!.ignore(); } - await currentPlayer.setProperty('msg-level', debugLoggingEnabled ? 'all=debug' : 'all=error'); + await currentPlayer.setProperty('msg-level', debugLoggingEnabled ? 'all=debug,ffmpeg/video=warn' : 'all=error'); await currentPlayer.setLogLevel(debugLoggingEnabled ? 'v' : 'warn'); await currentPlayer.setProperty('hwdec', _getHwdecValue(enableHardwareDecoding)); diff --git a/lib/utils/stream_buffer_sizing.dart b/lib/utils/stream_buffer_sizing.dart index 6f5a29b0..a043636b 100644 --- a/lib/utils/stream_buffer_sizing.dart +++ b/lib/utils/stream_buffer_sizing.dart @@ -10,9 +10,15 @@ const mpvDefaultStreamBufferBytes = 128 * 1024; /// in bytes, seconds × video byterate — which makes ffmpeg's DTS-ordered /// reads ping-pong across the file. Over HTTP every ping-pong that escapes /// mpv's stream ring buffer is a byte seek, and ffmpeg's http layer drops and -/// redials the connection on every seek, collapsing throughput. MKV is -/// interleaved by spec and excluded. -const poorlyInterleavedContainers = {'mp4', 'mov', 'm4v', '3gp', '3g2'}; +/// redials the connection on every seek, collapsing throughput. +const quickTimeFamilyContainers = {'mp4', 'mov', 'm4v', '3gp', '3g2'}; + +/// Matroska/WebM is normally well-interleaved, but UHD direct-play remuxes can +/// still overrun mpv's tiny 128 KiB default stream ring during normal demuxer +/// read bursts. Only opt in when the server reports a high bitrate so common +/// MKV playback keeps the default memory profile. +const matroskaFamilyContainers = {'mkv', 'matroska', 'webm'}; +const _highBitrateMatroskaThresholdKbps = 40 * 1000; const minStreamRingBytes = 16 * 1024 * 1024; const maxStreamRingBytes = 128 * 1024 * 1024; @@ -60,10 +66,15 @@ int? networkStreamRingBytes({ if (container == null) return null; // Jellyfin may report ffmpeg demuxer alias lists ('mov,mp4,m4a,3gp,3g2,mj2'). final tokens = container.toLowerCase().split(',').map((token) => token.trim()); - if (!tokens.any(poorlyInterleavedContainers.contains)) return null; + final isQuickTimeFamily = tokens.any(quickTimeFamilyContainers.contains); + final isMatroskaFamily = tokens.any(matroskaFamilyContainers.contains); + if (!isQuickTimeFamily && !isMatroskaFamily) return null; final cap = max(minStreamRingBytes, min(maxBytes, maxStreamRingBytes)); - if (bitrateKbps == null || bitrateKbps <= 0) return min(unknownBitrateStreamRingBytes, cap); + if (bitrateKbps == null || bitrateKbps <= 0) { + return isQuickTimeFamily ? min(unknownBitrateStreamRingBytes, cap) : null; + } + if (!isQuickTimeFamily && bitrateKbps < _highBitrateMatroskaThresholdKbps) return null; final bytesPerSecond = bitrateKbps * 1000 ~/ 8; final ring = nextPowerOfTwo(bytesPerSecond * _streamRingContentSeconds); diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index a1ad0632..d7e8e977 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -883,7 +883,7 @@ repositoryURL = "https://github.com/edde746/MPVKit"; requirement = { kind = revision; - revision = 1fe345d46bac91f4e85239ad268430e037040b2b; + revision = 245b69dce22156e81b79c84cfcb748358b674e47; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1ca1da90..229e1ab0 100644 --- a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -13,7 +13,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/edde746/MPVKit", "state" : { - "revision" : "1fc33029bc0317583866c62811dc0ab2aa2415b6" + "revision" : "245b69dce22156e81b79c84cfcb748358b674e47" } }, { diff --git a/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1ca1da90..229e1ab0 100644 --- a/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -13,7 +13,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/edde746/MPVKit", "state" : { - "revision" : "1fc33029bc0317583866c62811dc0ab2aa2415b6" + "revision" : "245b69dce22156e81b79c84cfcb748358b674e47" } }, { diff --git a/test/utils/stream_buffer_sizing_test.dart b/test/utils/stream_buffer_sizing_test.dart index 893a5b4e..e2b2ea50 100644 --- a/test/utils/stream_buffer_sizing_test.dart +++ b/test/utils/stream_buffer_sizing_test.dart @@ -21,12 +21,22 @@ void main() { }); test('returns null for well-interleaved or unknown containers', () { - expect(networkStreamRingBytes(container: 'mkv', bitrateKbps: 69140), isNull); expect(networkStreamRingBytes(container: 'avi', bitrateKbps: 69140), isNull); expect(networkStreamRingBytes(container: null, bitrateKbps: 69140), isNull); expect(networkStreamRingBytes(container: '', bitrateKbps: 69140), isNull); }); + test('sizes high-bitrate Matroska direct play to 64MiB', () { + expect(networkStreamRingBytes(container: 'mkv', bitrateKbps: 69140), 64 * 1024 * 1024); + expect(networkStreamRingBytes(container: 'matroska', bitrateKbps: 69140), 64 * 1024 * 1024); + }); + + test('keeps ordinary Matroska direct play on the default stream ring', () { + expect(networkStreamRingBytes(container: 'mkv', bitrateKbps: 39000), isNull); + expect(networkStreamRingBytes(container: 'mkv', bitrateKbps: null), isNull); + expect(networkStreamRingBytes(container: 'mkv', bitrateKbps: 0), isNull); + }); + test('matches QuickTime-family containers case-insensitively', () { expect(networkStreamRingBytes(container: 'MOV', bitrateKbps: 69140), 64 * 1024 * 1024); expect(networkStreamRingBytes(container: 'mp4', bitrateKbps: 69140), 64 * 1024 * 1024); @@ -34,7 +44,7 @@ void main() { test('matches ffmpeg demuxer alias lists from Jellyfin', () { expect(networkStreamRingBytes(container: 'mov,mp4,m4a,3gp,3g2,mj2', bitrateKbps: 69140), 64 * 1024 * 1024); - expect(networkStreamRingBytes(container: 'matroska,webm', bitrateKbps: 69140), isNull); + expect(networkStreamRingBytes(container: 'matroska,webm', bitrateKbps: 69140), 64 * 1024 * 1024); }); test('falls back to 64MiB when bitrate is unknown', () { diff --git a/tvos/Runner.xcodeproj/project.pbxproj b/tvos/Runner.xcodeproj/project.pbxproj index dedb76fb..9c31fb9d 100644 --- a/tvos/Runner.xcodeproj/project.pbxproj +++ b/tvos/Runner.xcodeproj/project.pbxproj @@ -933,7 +933,7 @@ repositoryURL = "https://github.com/edde746/MPVKit"; requirement = { kind = revision; - revision = 1fe345d46bac91f4e85239ad268430e037040b2b; + revision = 245b69dce22156e81b79c84cfcb748358b674e47; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/tvos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/tvos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index 643b4a29..c1529bef 100644 --- a/tvos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/tvos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,7 +6,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/edde746/MPVKit", "state" : { - "revision" : "1fe345d46bac91f4e85239ad268430e037040b2b" + "revision" : "245b69dce22156e81b79c84cfcb748358b674e47" } } ],