Absturz beim Abspielen, und die Annahme dahinter
Der Crashlog vom Gerät:
_dispatch_assert_queue_fail
_swift_task_checkIsolatedSwift
MainActor.assumeIsolated
VLCPlayerEngine.mediaPlayerStateChanged(_:)
VLCKit input_thread_Events
Im Code stand als Begründung für assumeIsolated, VLCKit stelle seine
Rückrufe über die Hauptwarteschlange zu. Das ist falsch — es ruft den
Delegaten von seinem eigenen Input-Thread. Die App brach deshalb hart
ab, sobald VLC den ersten Zustandswechsel meldete, also unmittelbar
beim Drücken auf Play.
Alle drei Delegat-Methoden springen jetzt mit Task { @MainActor in }
sauber hinüber. Die Reihenfolge bleibt erhalten: die Aufgaben werden aus
demselben Thread nacheinander eingereiht.
Dieselbe Annahme steckte im AVPlayer-Motor in den KVO-Beobachtern —
KVO meldet auf dem Thread, der die Änderung ausgelöst hat, und AVPlayer
ändert seinen Zustand auch aus internen Threads. Ebenfalls umgestellt.
Die beiden Rückrufe mit ausdrücklichem queue: .main behalten
assumeIsolated: dort ist die Annahme belegt statt geraten, und der
Zeitbeobachter feuert viermal je Sekunde.
Dazu die Anmeldung: Plex führt den Rücksprung über forwardUrl nicht
zuverlässig aus. Auf dem iPad landet der Browser nach dem Anmelden auf
watch.plex.tv und bleibt stehen — der Token liegt bereit, aber die App
wartete auf einen Rückruf, der nie kam. Das Fenster wird jetzt geöffnet,
ohne darauf zu warten; parallel wird im Sekundentakt beim Server
nachgefragt. Der Rücksprung ist damit eine Annehmlichkeit, keine
Voraussetzung.
132 Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -143,13 +143,24 @@ public final class VLCPlayerEngine: NSObject, VideoPlayerEngine {
|
||||
|
||||
// MARK: - VLCMediaPlayerDelegate
|
||||
|
||||
/// VLCKit stellt seine Rückrufe standardmäßig über die Hauptwarteschlange zu
|
||||
/// (siehe `VLCEventsConfiguration`). Deshalb `assumeIsolated` statt eines
|
||||
/// `Task`: ein nachgelagerter Task würde die Reihenfolge der Zustandswechsel
|
||||
/// durcheinanderbringen — „spielt" könnte nach „gestoppt" ankommen.
|
||||
/// VLCKit ruft den Delegaten von seinem **eigenen Input-Thread**, nicht über
|
||||
/// die Hauptwarteschlange.
|
||||
///
|
||||
/// Hier stand vorher das Gegenteil, und darauf aufbauend `assumeIsolated` —
|
||||
/// mit dem Ergebnis, dass die App beim Drücken auf Play hart abbrach, sobald
|
||||
/// VLC den ersten Zustandswechsel meldete:
|
||||
///
|
||||
/// _dispatch_assert_queue_fail
|
||||
/// MainActor.assumeIsolated
|
||||
/// VLCPlayerEngine.mediaPlayerStateChanged(_:)
|
||||
/// VLCKit input_thread_Events
|
||||
///
|
||||
/// Deshalb jetzt ein echter Sprung auf den Hauptakteur. Die Reihenfolge bleibt
|
||||
/// dabei erhalten: die Aufgaben werden aus demselben Thread nacheinander
|
||||
/// eingereiht und auch so abgearbeitet.
|
||||
extension VLCPlayerEngine: VLCMediaPlayerDelegate {
|
||||
public nonisolated func mediaPlayerStateChanged(_ newState: VLCMediaPlayerState) {
|
||||
MainActor.assumeIsolated {
|
||||
Task { @MainActor in
|
||||
switch newState {
|
||||
case .opening:
|
||||
state = .loading
|
||||
@@ -176,13 +187,13 @@ extension VLCPlayerEngine: VLCMediaPlayerDelegate {
|
||||
}
|
||||
|
||||
public nonisolated func mediaPlayerTimeChanged(_ aNotification: Notification) {
|
||||
MainActor.assumeIsolated {
|
||||
Task { @MainActor in
|
||||
continuation?.yield(.timeChanged(currentTime))
|
||||
}
|
||||
}
|
||||
|
||||
public nonisolated func mediaPlayerLengthChanged(_ length: Int64) {
|
||||
MainActor.assumeIsolated {
|
||||
Task { @MainActor in
|
||||
guard length > 0 else { return }
|
||||
let value = Duration.milliseconds(length)
|
||||
duration = value
|
||||
|
||||
Reference in New Issue
Block a user