feat(session): resolve the audio track from an ordered language preference - #32
Merged
Merged
Conversation
…rence
PlayerConfiguration gains `preferredAudioLanguages` (ordered, already-normalised
bare tags supplied by the caller) and `autoEnableForcedSubtitlesForForeignAudio`.
buildPipeline resolves the audio track against that preference before
demuxer.resume(), so the choice costs no lane teardown and no seek — selecting
after open() would seek to a position that is 0 until the first frame, wiping a
startPosition VOD resume, and MediaInfo.isSeekable is true for many live IPTV
endpoints that providers then drop.
Both fields default to the previous behaviour, so an empty preference resolves
`first(where: \.isDefault) ?? first` exactly as before.
TrackLanguageMatcher carries the ISO 639-2/B alias table Foundation does not
provide — Locale.LanguageCode("ger").identifier(.alpha2) is nil while "deu"
resolves — and scores candidates by preference rank, commentary/audio-description
penalty, and exact-over-regional-variant, matching track titles per token rather
than by substring.
Paired with the Lume app change for bilipp/Lume#200; the app resolves
../LumeEngine as a local SPM package, so the two commits must land together.
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Engine half of a paired change for bilipp/Lume#200 — a preferred-audio-language setting in the Lume app. This adds the API the app drives and resolves the audio track against it at open time.
Companion app PR: bilipp/Lume#211.
Implementation
PlayerConfigurationgains two fields, both defaulting to today's behaviour:preferredAudioLanguages: [String]— ordered, already-normalised bare tags supplied by the caller. The engine does not normalise; the app does, and passes["de", "en"].autoEnableForcedSubtitlesForForeignAudio: Bool— off by default.buildPipeline(info:)resolves the audio track against that preference beforedemuxer.resume(), falling back to the existingfirst(where: \.isDefault) ?? firstwhen the list is empty or nothing matches. With an empty list the resolution is byte-for-byte what it was.Why at open, not after
Selecting after
open()routes throughselectAudioTrack→seek(to: position), andpositionis 0 until the first frame — which silently wipes aconfiguration.startPositionVOD resume. Worse for live:MediaInfo.isSeekableis true wheneverpb->seekable != 0, which many live IPTV HTTP endpoints report, and providers drop the connection on the seek. Resolving duringbuildPipelinecosts no lane teardown and no seek.Matching
TrackLanguageMatchercarries the ISO 639-2/B alias table Foundation does not provide —Locale.LanguageCode("ger").identifier(.alpha2)is nil while"deu"resolves, andger/fre/chi/cze/dut/greare exactly what IPTV containers ship. Candidates are scored by preference rank, a commentary / audio-description penalty, and exact-over-regional-variant; track titles are matched per whitespace/punctuation token, never by substring, soGER 5.1matches butHungariandoes not match ahu-seeking pass viagar.und/mul/mis/zxx/vo/vost/multi/originalresolve to nothing, which means "leave the container's choice alone".Testing
swift buildcleanswift test— 85/85 tests in 14 suites passTrackLanguageMatcherTestsplus fourPlayerSessionTestscases assertingselectedAudioTrackIndexafteropen()with a preference setTests/…/Fixtures/generate-fixtures.shnow generates the multi-audio-language MKVPLAN.md's testing section already anticipated (two audio streams taggedeng/ger,engdispositioned default). Generated media stays uncommitted, as with the other fixtures.Reviewer notes
public vars with defaults on an existingpublic structthat has apublic init(), safe under the package's-enable-library-evolution.../LumeEngineas a local SPM package, so that branch does not build against an engine checkout without this commit. Sideload CI needs anENGINE_REFbump once this is tagged — deliberately not done here, since pointing the workflow at a tag that does not exist yet breaks the build harder than the current skew.TrackLanguageMatcherlives app-side. That duplication is deliberate for now: sharing it would mean a new SPM target plus a package-product dependency on the app and test targets. Worth revisiting if the two drift.