Auto-select VLC subtitle tracks
Dreamio now asks MobileVLCKit to enable the first real subtitle track when VLC discovers embedded MKV subtitles and playback is still on “Disable.” This targets the log pattern where VLC sees English (SDH) but leaves selected=-1.
Summary
Fixed the remaining native playback subtitle issue for streams that provide no external subtitle candidates but do contain embedded subtitle tracks. VLC can discover those tracks after playback starts; Dreamio now auto-selects the first selectable one once it appears.
Changes Made
- Added per-playback state to track whether Dreamio has already auto-selected subtitles.
- Added per-playback state to detect when the user manually chooses a caption option, including disabling captions.
- Auto-selects the first subtitle track with a non-negative VLC track id when VLC reports
.esAddedor after an external subtitle attach refresh. - Added a debug log line for automatic subtitle selection so future device logs should show the selected track id and reason.
Context
The reported logs showed subtitle candidates=0 from the native player, followed by VLC reporting subtitle tracks named Disable and English (SDH) - [English] with selected track -1. That means stream parsing and VLC track discovery were both working; the remaining gap was that Dreamio never changed VLC away from the disabled subtitle track.
Important Implementation Details
- The selection guard only runs when
currentVideoSubTitleIndexis below zero, so it will not replace an already active subtitle track. - The auto-selection runs only once per playback item.
- User interaction wins: once
selectSubtitleTrack(id:)is called from the captions menu, Dreamio stops automatic caption selection for that playback item. - The state resets in
play(request:), alongside the existing attached subtitle URL reset.
Relevant Diff Snippets
Rendered with @pierre/diffs/ssr.
22 unmodified lines23242526272812 unmodified lines41424344454653 unmodified lines100101102103104105133 unmodified lines2392402412422432449 unmodified lines25425525625725825912 unmodified lines27227327427527627722 unmodified linesprivate let mediaPlayer = VLCMediaPlayer()#endifprivate var attachedSubtitleURLs = Set<URL>()override init() {super.init()12 unmodified linesfunc play(request: NativePlaybackRequest) {#if canImport(MobileVLCKit)attachedSubtitleURLs.removeAll()let media = VLCMedia(url: request.playbackURL)let headerValue = request.headers.map { "\($0.key): \($0.value)" }53 unmodified linesfunc selectSubtitleTrack(id: Int32) {#if canImport(MobileVLCKit)#if DEBUGlogSubtitleTracks(reason: "before-select-\(id)")#endif133 unmodified linesreturn attachedCount}DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in#if DEBUGself?.logSubtitleTracks(reason: "delayed-refresh")#endif9 unmodified linesprint("[DreamioVLC] subtitle tracks reason=\(reason) names=\(names) indexes=\(indexes.map { $0.int32Value }) selected=\(mediaPlayer.currentVideoSubTitleIndex)")}#endif#endif}12 unmodified linescase .paused, .stopped, .ended:onStateChange?()case .esAdded:#if DEBUGlogSubtitleTracks(reason: "esAdded")#endif22 unmodified lines232425262728293012 unmodified lines434445464748495053 unmodified lines104105106107108109110133 unmodified lines2442452462472482492509 unmodified lines26026126226326426526626726826927027127227327427527627727827928012 unmodified lines29329429529629729829922 unmodified linesprivate let mediaPlayer = VLCMediaPlayer()#endifprivate var attachedSubtitleURLs = Set<URL>()private var didAutoSelectSubtitleTrack = falseprivate var didUserSelectSubtitleTrack = falseoverride init() {super.init()12 unmodified linesfunc play(request: NativePlaybackRequest) {#if canImport(MobileVLCKit)attachedSubtitleURLs.removeAll()didAutoSelectSubtitleTrack = falsedidUserSelectSubtitleTrack = falselet media = VLCMedia(url: request.playbackURL)let headerValue = request.headers.map { "\($0.key): \($0.value)" }53 unmodified linesfunc selectSubtitleTrack(id: Int32) {#if canImport(MobileVLCKit)didUserSelectSubtitleTrack = true#if DEBUGlogSubtitleTracks(reason: "before-select-\(id)")#endif133 unmodified linesreturn attachedCount}DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] inself?.selectInitialSubtitleTrackIfNeeded(reason: "delayed-refresh")#if DEBUGself?.logSubtitleTracks(reason: "delayed-refresh")#endif9 unmodified linesprint("[DreamioVLC] subtitle tracks reason=\(reason) names=\(names) indexes=\(indexes.map { $0.int32Value }) selected=\(mediaPlayer.currentVideoSubTitleIndex)")}#endifprivate func selectInitialSubtitleTrackIfNeeded(reason: String) {guard !didUserSelectSubtitleTrack,!didAutoSelectSubtitleTrack,mediaPlayer.currentVideoSubTitleIndex < 0,let track = subtitleTracks.first(where: { $0.id >= 0 }) else {return}didAutoSelectSubtitleTrack = true#if DEBUGprint("[DreamioVLC] auto-select subtitle id=\(track.id) name=\(track.name) reason=\(reason)")#endifmediaPlayer.currentVideoSubTitleIndex = track.id}#endif}12 unmodified linescase .paused, .stopped, .ended:onStateChange?()case .esAdded:selectInitialSubtitleTrackIfNeeded(reason: "esAdded")#if DEBUGlogSubtitleTracks(reason: "esAdded")#endif
Expected Impact for End-Users
MKV streams with embedded subtitles should show captions automatically instead of requiring the user to open the captions menu and pick the embedded track manually. Users can still disable captions or switch tracks afterward.
Validation
- Ran
xcodebuild -scheme Dreamio -project Dreamio.xcodeproj -destination 'generic/platform=iOS' build. - The build succeeded against MobileVLCKit.
Issues, Limitations, and Mitigations
- This was validated by build, not by replaying the exact Real-Debrid stream on device in this turn.
- If a file has multiple embedded subtitle tracks, Dreamio chooses the first selectable track VLC exposes. The captions menu remains available for manual switching.
- If a stream intentionally starts with subtitles disabled and the user never touches the captions menu, Dreamio will now enable the first discovered track by default.
Follow-up Work
- Test the same South Park MKV on device and confirm the logs include
[DreamioVLC] auto-select subtitlefollowed by a non-negative selected subtitle id. - Consider a future setting for “auto-enable embedded subtitles” if users want control over the default behavior.