fix(player): seek Plex transcodes in-band instead of pre-warming at the resume offset
A quality switch or resumed open at a nonzero position sent offset=T on the HLS start URL, waited for the readiness probe to touch the segment at T, and then had mpv seek to T anyway. mpv's stream probing always reads segment zero first, and a Plex segment request is a seek, so the transcoder was dragged through seek(T) -> seek(0) -> seek(T) within seconds of the open. Measured against PMS 1.43, a segment response that races such a restart can be left open with headers sent and no data or error, and ffmpeg's HLS segment reads have no default timeout, so playback buffered forever after the first frame (issue #1859). Starting the session plain and letting the player's start=T request the resume segment performs the one unavoidable transcoder seek. The offset request parameter, the readiness probe, and the probe-only getStatus HTTP helper are removed; live TV time-shift keeps its own offset path. Transcode opens now also set an explicit network-timeout with demuxer-level reconnect options: mpv's stream-layer reconnect settings never reach ffmpeg's HLS segment fetches, so a silently hung segment response now times out after 20s and is re-requested on a fresh connection instead of buffering indefinitely. Verified against a live PMS (resume plays from the requested position) and a stall harness (hung segment re-requested at 20s with no content skip).
This commit is contained in:
@@ -75,7 +75,6 @@ import '../providers/shader_provider.dart';
|
||||
import '../providers/user_profile_provider.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import '../utils/dialogs.dart';
|
||||
import '../utils/media_server_http_client.dart' show AbortController;
|
||||
import '../utils/log_redaction_manager.dart';
|
||||
import '../utils/live_tv_player_navigation.dart';
|
||||
import '../utils/player_utils.dart';
|
||||
@@ -473,9 +472,6 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
Completer<void>? _playbackTransitionIdleCompleter;
|
||||
bool _playbackIntentShouldPlay = true;
|
||||
|
||||
/// In-flight transcode readiness probe, aborted by the next probe or by
|
||||
/// dispose so a superseded open never leaves it polling out its window.
|
||||
AbortController? _transcodeReadinessAbort;
|
||||
int _pendingSubtitleCycleCount = 0;
|
||||
bool _subtitleCycleDrainActive = false;
|
||||
|
||||
@@ -1307,13 +1303,6 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
preferredSubtitleTrack: _preferredSubtitleTrack,
|
||||
sessionIdentifier: _playbackSessionIdentifier,
|
||||
transcodeSessionId: _playbackTranscodeSessionId,
|
||||
// The initial resume position is the server view offset (the
|
||||
// online open resolves the same value later), so a resumed
|
||||
// transcode starts producing at the resume point instead of
|
||||
// seeking a stream that begins at zero.
|
||||
transcodeOffset: _currentMetadata.viewOffsetMs != null
|
||||
? Duration(milliseconds: _currentMetadata.viewOffsetMs!)
|
||||
: null,
|
||||
),
|
||||
offlineLibraryMode: false,
|
||||
);
|
||||
@@ -1828,7 +1817,6 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
@override
|
||||
void dispose() {
|
||||
unawaited(AndroidExitDiagnostics.markUiState(AndroidUiState.mainScreen));
|
||||
_transcodeReadinessAbort?.abort();
|
||||
_playerInitializationGeneration++;
|
||||
_frameRate.dispose();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
|
||||
Reference in New Issue
Block a user