fix(player): three defects native video exposed, and the logs to see them
Each of these was invisible while Linux video played in the webview, and each became reachable the moment mpv started rendering. DR-238 — a transcoded seek re-negotiates the stream on every renderer, not just the webview. `determine_video_seek_strategy` treated `is_hls` as a proxy for "seekable in place", which held only because hls.js was always the HLS renderer: it seeks within the VOD playlist it is handed and lets the server catch up. mpv's HLS demuxer cannot make Jellyfin transcode from a new offset, so with native video on, every transcoded seek became a backend seek that silently did nothing. One cell of the truth table changes; all four webview cells are byte-identical. DR-239 — properties the mpv event loop handles are now observed. libmpv delivers PropertyChange only for properties registered with observe_property, so the `pause` arm was unreachable code that read as implemented: StateChanged was never emitted and the play/pause control never moved. UT-218 asserts the two lists agree, so the class cannot recur. DR-240 — fullscreen moves whatever owns the pixels. requestFullscreen() fullscreens the *document*, which sufficed while the <video> element lived inside it and WebKit scaled it. A native surface is drawn behind the webview at window size, so a document-only fullscreen expanded the page and left the picture at its old size — on WebKitGTK, a maximised window with decorations still holding a strip of the screen. Measured on a 3440x1440 panel: 1361 tall before, 1440 after. DR-241 — a seek issued before mpv has a file to seek in is honoured rather than dropped. loadfile returns as soon as the command is queued, so `time-pos` does not resolve yet and setting it fails. The two callers that always hit that window are resume and a transcoded seek, both of which re-open the stream and then ask for a position; the failed seek was discarded and playback began at zero. Also adds the instrumentation that made the diagnosis possible rather than speculative: an entry log on player_stop, a render-size log that re-fires on change instead of latching once, and decoded-vs-display video geometry on file load. The last of those retired a wrong theory — a picture that does not fill an ultrawide turned out to be a 16:9 source with its letterbox baked in, not a rendering fault.
This commit is contained in:
@@ -39,23 +39,38 @@ pub fn determine_video_seek_strategy(
|
||||
return VideoSeekStrategy::LocalNativeSeek;
|
||||
}
|
||||
|
||||
// HLS streams and direct play (non-transcoded) support native seeking
|
||||
if is_hls || !needs_transcoding {
|
||||
if use_html5 {
|
||||
// HTML5 backend - frontend handles seeking via videoElement.currentTime
|
||||
// We don't call backend.seek() because video is in HTML5 element, not in MPV
|
||||
VideoSeekStrategy::Html5NativeSeek
|
||||
} else {
|
||||
// Native backend (MPV) - backend handles seeking
|
||||
VideoSeekStrategy::BackendNativeSeek
|
||||
}
|
||||
} else {
|
||||
// Transcoded non-HLS streams need server-side seek (reload from new position)
|
||||
if use_html5 {
|
||||
VideoSeekStrategy::Html5ReloadStream
|
||||
// A server-side transcode is produced *from* `StartTimeTicks`, so where the
|
||||
// seek lands is a property of the request, not of the stream in hand.
|
||||
//
|
||||
// hls.js is the exception: handed a VOD playlist it seeks within it and lets
|
||||
// the server catch up segment by segment. mpv's HLS demuxer cannot make
|
||||
// Jellyfin transcode from a new offset, so for the native backend a
|
||||
// transcoded seek must re-negotiate the stream regardless of container.
|
||||
//
|
||||
// Before native video shipped, `use_html5` was always true for HLS and the
|
||||
// native+HLS+transcode cell was unreachable, which is why `is_hls` alone
|
||||
// used to be a safe proxy for "seekable in place". It no longer is: turning
|
||||
// native video on routed every transcoded seek into a backend seek that
|
||||
// silently does nothing, and presents as "resume does not work".
|
||||
if needs_transcoding {
|
||||
return if use_html5 {
|
||||
if is_hls {
|
||||
VideoSeekStrategy::Html5NativeSeek
|
||||
} else {
|
||||
VideoSeekStrategy::Html5ReloadStream
|
||||
}
|
||||
} else {
|
||||
VideoSeekStrategy::BackendReloadStream
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Direct play and direct stream are seekable where they sit.
|
||||
if use_html5 {
|
||||
// The frontend seeks via videoElement.currentTime; calling backend.seek()
|
||||
// would move a player that is not the one rendering.
|
||||
VideoSeekStrategy::Html5NativeSeek
|
||||
} else {
|
||||
VideoSeekStrategy::BackendNativeSeek
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +255,30 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// A server-side transcode cannot be seeked by the native backend.
|
||||
///
|
||||
/// Jellyfin produces a transcode from `StartTimeTicks`; hls.js can seek
|
||||
/// within the VOD playlist it is handed, but mpv's HLS demuxer cannot make
|
||||
/// the server transcode from a new offset, so the stream has to be
|
||||
/// re-negotiated. Before native video existed, `use_html5` was always true
|
||||
/// for HLS and this case was unreachable — turning native video on routed
|
||||
/// every transcoded seek into a native seek that silently does nothing,
|
||||
/// which presents as "resume does not work".
|
||||
///
|
||||
/// TRACES: UR-040 | DR-238 | UT-217
|
||||
#[test]
|
||||
fn test_seek_strategy_transcoded_hls_native_backend() {
|
||||
assert_eq!(
|
||||
determine_video_seek_strategy(false, true, true, false),
|
||||
VideoSeekStrategy::BackendReloadStream
|
||||
);
|
||||
// The HTML5 side of the same case is unchanged: hls.js seeks in-playlist.
|
||||
assert_eq!(
|
||||
determine_video_seek_strategy(false, true, true, true),
|
||||
VideoSeekStrategy::Html5NativeSeek
|
||||
);
|
||||
}
|
||||
|
||||
/// Test video seek strategy for direct play (non-transcoded) streams
|
||||
#[test]
|
||||
fn test_seek_strategy_direct_play() {
|
||||
|
||||
Reference in New Issue
Block a user