fix(exoplayer): correct transcode seek position double-count

This commit is contained in:
edde746
2026-06-02 06:23:08 +02:00
parent cb2e56442d
commit 985c279b1f
2 changed files with 55 additions and 2 deletions
+5 -2
View File
@@ -103,9 +103,12 @@ class PlayerAndroid extends PlayerBase {
if (disposed) return;
await _ensureInitialized();
final startPosition = media.start ?? Duration.zero;
configureTimeline(offset: timelineOffset, duration: timelineDuration);
// ExoPlayer reports Plex copyts transcodes in source-time coordinates,
// unlike mpv which rebases them to zero. Do not add the timeline offset
// again on Android ExoPlayer or seeks/progress jump to roughly 2x (#1221).
configureTimeline(offset: Duration.zero, duration: timelineDuration);
clearTracks();
resetPlaybackProgress(startPosition);
resetPlaybackProgress(media.start ?? timelineOffset);
setSeekable(false);
// Show the video layer
+50
View File
@@ -133,6 +133,56 @@ void main() {
);
});
test('ExoPlayer maps copyts transcode streams as absolute timeline positions', () async {
final calls = <MethodCall>[];
await _withMockChannels(
methodChannelName: 'com.plezy/exo_player',
eventChannelName: 'com.plezy/exo_player/events',
methodHandler: (call) {
calls.add(call);
switch (call.method) {
case 'initialize':
return Future.value(true);
default:
return Future.value(null);
}
},
testBody: () async {
final player = PlayerAndroid();
try {
const timelineStart = Duration(seconds: 2058); // 34:18
const timelineDuration = Duration(seconds: 2903); // 48:23
await player.open(
Media('https://example.test/transcode.mkv'),
timelineOffset: timelineStart,
timelineDuration: timelineDuration,
);
expect(player.state.position, timelineStart);
expect(player.state.duration, timelineDuration);
final openCall = calls.singleWhere((call) => call.method == 'open');
final openArgs = Map<Object?, Object?>.from(openCall.arguments as Map);
expect(openArgs['startPositionMs'], 0);
await Future<void>.delayed(const Duration(milliseconds: 260));
player.handlePropertyChange('time-pos', 2058.0);
expect(player.state.position, timelineStart);
await player.seek(const Duration(minutes: 40));
final seekCall = calls.lastWhere((call) => call.method == 'seek');
final seekArgs = Map<Object?, Object?>.from(seekCall.arguments as Map);
expect(seekArgs['positionMs'], const Duration(minutes: 40).inMilliseconds);
expect(player.state.position, const Duration(minutes: 40));
} finally {
await player.dispose();
}
},
);
});
test('MPV clears stale Dart track state before opening new media', () async {
await _withMockChannels(
methodChannelName: 'com.plezy/mpv_player',