fix(player): scale mpv stream ring buffer for poorly interleaved mp4/mov

This commit is contained in:
edde746
2026-06-12 22:21:17 +02:00
parent 24980aff95
commit 90662e2173
8 changed files with 217 additions and 13 deletions
+70
View File
@@ -0,0 +1,70 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/utils/stream_buffer_sizing.dart';
void main() {
group('nextPowerOfTwo', () {
test('rounds up to the next power of two', () {
expect(nextPowerOfTwo(1), 1);
expect(nextPowerOfTwo(2), 2);
expect(nextPowerOfTwo(3), 4);
expect(nextPowerOfTwo(52 * 1024 * 1024), 64 * 1024 * 1024);
expect(nextPowerOfTwo(64 * 1024 * 1024), 64 * 1024 * 1024);
expect(nextPowerOfTwo(64 * 1024 * 1024 + 1), 128 * 1024 * 1024);
});
});
group('networkStreamRingBytes', () {
test('sizes the iPhone 4K60 HDR10 repro file to 64MiB', () {
// 69140 kbps total → 8.6 MB/s × 6s ≈ 52MB → pow2 64MiB; the guaranteed
// seek-back half (32MiB) covers the measured ~24MB interleave gap.
expect(networkStreamRingBytes(container: 'mov', bitrateKbps: 69140), 64 * 1024 * 1024);
});
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('matches QuickTime-family containers case-insensitively', () {
expect(networkStreamRingBytes(container: 'MOV', bitrateKbps: 69140), 64 * 1024 * 1024);
expect(networkStreamRingBytes(container: 'mp4', bitrateKbps: 69140), 64 * 1024 * 1024);
});
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);
});
test('falls back to 64MiB when bitrate is unknown', () {
expect(networkStreamRingBytes(container: 'mov', bitrateKbps: null), 64 * 1024 * 1024);
expect(networkStreamRingBytes(container: 'mov', bitrateKbps: 0), 64 * 1024 * 1024);
});
test('clamps to the 16MiB floor for low bitrates', () {
expect(networkStreamRingBytes(container: 'mp4', bitrateKbps: 1000), 16 * 1024 * 1024);
});
test('clamps to the 128MiB ceiling for very high bitrates', () {
expect(networkStreamRingBytes(container: 'mov', bitrateKbps: 400000), 128 * 1024 * 1024);
});
test('respects a lower Android heap cap, including the unknown-bitrate path', () {
expect(
networkStreamRingBytes(container: 'mov', bitrateKbps: 69140, maxBytes: 32 * 1024 * 1024),
32 * 1024 * 1024,
);
expect(networkStreamRingBytes(container: 'mov', bitrateKbps: null, maxBytes: 32 * 1024 * 1024), 32 * 1024 * 1024);
});
});
group('androidStreamRingCapBytes', () {
test('tiers by device heap with a conservative unknown fallback', () {
expect(androidStreamRingCapBytes(0), 64 * 1024 * 1024);
expect(androidStreamRingCapBytes(256), 32 * 1024 * 1024);
expect(androidStreamRingCapBytes(512), 64 * 1024 * 1024);
expect(androidStreamRingCapBytes(1024), 128 * 1024 * 1024);
});
});
}