Dreamio turn document

Throttle VLC subtitle reapply

Reduced noisy VLC subtitle reapply behavior so repeated buffering notifications no longer keep writing the same already-selected subtitle track.

May 25, 2026 Issue dreamio-h5n Native VLC playback

Summary

Dreamio was auto-selecting embedded VLC subtitle tracks correctly, but VLC buffering callbacks repeatedly reapplied the same track while it was already selected. The change keeps recovery behavior for real subtitle-selection drift and startup timing, while suppressing repeated no-op reapply writes from player state changes.

Changes Made

Context

The diagnostic log showed VLC auto-selecting English (SDH), then repeatedly logging reapply subtitle during buffering even though selected=3 never changed. The external OpenSubtitles load failure was non-critical in that trace, and the working subtitle came from the MKV itself.

Important Implementation Details

The VLC state delegate still calls the reapply helper during .buffering and .playing. The helper now checks mediaPlayer.currentVideoSubTitleIndex before writing. If the intended auto-selected track is already active, state-driven calls return without touching VLC or logging. Delayed startup retries intentionally keep their confirmation logging because those are bounded and useful for diagnosing timing-sensitive subtitle attachment.

Relevant Diff Snippets

Dreamio/VLCNativePlaybackBackend.swift
-3+12
283 unmodified lines
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
283 unmodified lines
private func scheduleAutoSubtitleSelectionReapply(trackID: Int32) {
[0.3, 1.0, 2.0, 4.0].forEach { delay in
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
self?.reapplyAutoSelectedSubtitleTrackIfNeeded(reason: "delayed-\(String(format: "%.1f", delay))")
}
}
}
private func reapplyAutoSelectedSubtitleTrackIfNeeded(reason: String) {
guard !didUserSelectSubtitleTrack,
let trackID = autoSelectedSubtitleTrackID,
subtitleTracks.contains(where: { $0.id == trackID }) else {
return
}
mediaPlayer.currentVideoSubTitleIndex = trackID
#if DEBUG
print("[DreamioVLC] reapply subtitle id=\(trackID) reason=\(reason) selected=\(mediaPlayer.currentVideoSubTitleIndex)")
#endif
}
#endif
283 unmodified lines
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
283 unmodified lines
private func scheduleAutoSubtitleSelectionReapply(trackID: Int32) {
[0.3, 1.0, 2.0, 4.0].forEach { delay in
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
self?.reapplyAutoSelectedSubtitleTrackIfNeeded(
reason: "delayed-\(String(format: "%.1f", delay))",
shouldLogNoop: true
)
}
}
}
private func reapplyAutoSelectedSubtitleTrackIfNeeded(reason: String, shouldLogNoop: Bool = false) {
guard !didUserSelectSubtitleTrack,
let trackID = autoSelectedSubtitleTrackID,
subtitleTracks.contains(where: { $0.id == trackID }) else {
return
}
let selectedTrackID = mediaPlayer.currentVideoSubTitleIndex
guard selectedTrackID != trackID || shouldLogNoop else {
return
}
mediaPlayer.currentVideoSubTitleIndex = trackID
#if DEBUG
let action = selectedTrackID == trackID ? "confirm" : "recover"
print("[DreamioVLC] reapply subtitle id=\(trackID) reason=\(reason) action=\(action) selected=\(mediaPlayer.currentVideoSubTitleIndex)")
#endif
}
#endif

Expected Impact for End-Users

Playback should behave the same when the embedded subtitle is successfully auto-selected. Debug logs should become much quieter during buffering, making real subtitle failures easier to spot. If VLC drops the subtitle selection, Dreamio will still reapply the intended auto-selected track.

Validation

Issues, Limitations, and Mitigations

Follow-up Work

No follow-up issue is required for this specific buffering log noise. A separate issue would be appropriate if external OpenSubtitles subtitle downloads still fail for streams without embedded subtitles.