mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
fix(android,ios): the track tick moves when a track is chosen, and no file inherits another's languages
Choosing an audio or subtitle track read the track list back before mpv had made the switch, so the tick stayed on the old track until the next poll. The tick now moves on the choice, and the list is read again 300 ms later to confirm it. On Android, `load` set `alang` and `slang` only when they were not empty. mpv keeps both across `loadfile`, so a file with no language preference took the previous file's. Both are now set on every load. This closes the last open item of B41.
This commit is contained in:
1 parent
973a8f2749
commit
3fe4900337
4 files changed
+49
-15
No files matched your search
@@ -323,8 +323,11 @@ class Player {
|
||||
// needs no window, and a load that cannot get one plays audio anyway —
|
||||
// waiting for a surface here is what made background playback fail after
|
||||
// a five-second stall.
|
||||
if (!audio.empty()) set_string("alang", audio);
|
||||
if (!subtitle.empty()) set_string("slang", subtitle);
|
||||
//
|
||||
// Set even when empty: mpv keeps both across `loadfile`, so a file
|
||||
// with no language preference used to inherit the last one's.
|
||||
set_string("alang", audio);
|
||||
set_string("slang", subtitle);
|
||||
set_string("sid", subtitles ? "auto" : "no");
|
||||
pending_start_.store(std::max(0.0, start));
|
||||
// Retire the outgoing file's position, duration and end flag *before*
|
||||
|
||||
@@ -322,7 +322,17 @@ class NativeMpvHost private constructor(private val nativeHandle: Long) : Libmpv
|
||||
fun selectTrack(track: MpvTrack?) {
|
||||
val audio = track?.type == "audio"
|
||||
withOpenHandle { nativeSelectTrack(it, audio, track?.id ?: -1) }
|
||||
refreshTracks()
|
||||
// mpv makes the switch on its own thread and lists it a moment later,
|
||||
// so a track list read back now still ticks the old track. The tick
|
||||
// moves here, and a read shortly after confirms it.
|
||||
val type = if (audio) "audio" else "sub"
|
||||
mutableTracks.value = mutableTracks.value.map {
|
||||
if (it.type == type) it.copy(selected = it.id == track?.id) else it
|
||||
}
|
||||
scope.launch(Dispatchers.Default) {
|
||||
delay(TRACK_SETTLE_MS)
|
||||
refreshTracks()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,6 +568,9 @@ class NativeMpvHost private constructor(private val nativeHandle: Long) : Libmpv
|
||||
private const val CACHED_UNTIL = 13
|
||||
private const val LOADED = 14
|
||||
|
||||
/** How long after a track switch mpv's track list is read again. */
|
||||
private const val TRACK_SETTLE_MS = 300L
|
||||
|
||||
init {
|
||||
System.loadLibrary("pstr_android")
|
||||
System.loadLibrary("pstr_mpv")
|
||||
|
||||
+17
-10
@@ -12,20 +12,27 @@ All of B14–B33 came out of a static review of the Android client
|
||||
device** — there was no Android acceptance harness to reproduce them with, which
|
||||
is itself B42. Each states the reasoning that identifies it; treat the
|
||||
reproduction step as part of the fix. The remediation order is `plan.md` at the
|
||||
repository root; Phases 1 to 5 of it have landed except for the tail of B41 and
|
||||
one row of Phase 5's table — everything from B14 to B39 is below in **Fixed**,
|
||||
and B41 states which of its items are done.
|
||||
repository root; Phases 1 to 5 of it have landed except for one row of Phase
|
||||
5's table — everything from B14 to B39 is below in **Fixed**, and B41's items
|
||||
have all landed.
|
||||
|
||||
### B41 — assorted correctness and hygiene defects
|
||||
|
||||
Individually small, grouped so they are not lost. The items marked **Landed**
|
||||
came with Phases 4 and 5; the rest are still open, which is why this entry is.
|
||||
What remains is one item: the asynchronous track-list read-back.
|
||||
Individually small, grouped so they are not lost. Every item has now landed;
|
||||
the entry stays here, with B42, until a device run confirms them.
|
||||
|
||||
- `NativeMpvHost.kt:131-135` reads the track list back immediately after an
|
||||
asynchronous property set, so the selection tick lags by up to a second;
|
||||
`alang`/`slang` are applied only at load (`pstr_mpv.cpp:225-227`), so a
|
||||
language change takes effect one episode late.
|
||||
- **Landed.** `NativeMpvHost.selectTrack` read the track list back straight
|
||||
after setting `aid`/`sid`, before mpv had made the switch, so the tick in the
|
||||
track chooser stayed on the old track until the next poll, up to a second
|
||||
later. The tick now moves when the track is chosen, and the list is read again
|
||||
300 ms after. The second half of the report, a language change landing one
|
||||
episode late, did not hold up: the choice is saved from the chosen track, and
|
||||
the next episode loads with it. What was wrong nearby is that `load` set
|
||||
`alang` and `slang` only when they were not empty, and mpv keeps both across
|
||||
`loadfile`, so a file with no language preference took the previous file's.
|
||||
Both are set on every load now. The iOS client had the same early read in
|
||||
`MpvPlayer.select` and takes the same fix; it already set both languages on
|
||||
every load.
|
||||
- **Landed.** `NativeMpvHost.kt` polled on `Dispatchers.Main.immediate`, and
|
||||
`tracks_json` issues five `mpv_get_property` calls per track — 51 synchronous
|
||||
property reads on the UI thread for a 10-track file, contending on the core
|
||||
|
||||
@@ -32,7 +32,7 @@ struct MpvTrack: Identifiable, Hashable {
|
||||
let type: String
|
||||
let language: String
|
||||
let title: String
|
||||
let selected: Bool
|
||||
var selected: Bool
|
||||
/// The container marks it as the default choice.
|
||||
var isDefault = false
|
||||
/// The container marks it as forced: on-screen text only, by convention.
|
||||
@@ -303,7 +303,18 @@ final class MpvPlayer {
|
||||
/// A track, or none: `nil` turns subtitles off.
|
||||
func select(type: String, track: MpvTrack?) {
|
||||
setString(type == "audio" ? "aid" : "sid", track.map { String($0.id) } ?? "no")
|
||||
refreshTracks()
|
||||
// mpv makes the switch on its own thread and lists it a moment later,
|
||||
// so a track list read back now still ticks the old track. The tick
|
||||
// moves here, and a read shortly after confirms it.
|
||||
tracks = tracks.map { listed in
|
||||
var listed = listed
|
||||
if listed.type == type { listed.selected = listed.id == track?.id }
|
||||
return listed
|
||||
}
|
||||
Task { [weak self] in
|
||||
try? await Task.sleep(for: .milliseconds(300))
|
||||
self?.refreshTracks()
|
||||
}
|
||||
}
|
||||
|
||||
/// Showing no picture while backgrounded keeps the audio playing: iOS
|
||||
|
||||
Reference in new issue
Block a user