I instrumented this endâtoâend and found the actual root cause â in the webOS appâs own trackâselection code, down to the exact function. Sharing the full chain because it explains every symptom in this thread, including why it looks random across files.
Setup: rooted LG (2019, webOS 4.5). I attached Chrome DevTools to the live Plex app (itâs the app.plex.tv/tv-v5-webos web build running in webOS Chromium) and simultaneously tailed LGâs mediaâpipeline log (starfish-media-pipeline), so I can see the exact track each layer selects.
Repro file: 2 audio tracks â #1 Russian AC3 5.1, #2 English EâAC3 5.1 (Atmos) (an Amazon WEBâDL). Direct Play. Selecting English plays Russian and viceâversa.
Everything below the app is correct
LGâs pipeline maps track index â audio faithfully (Atmos flag as ground truth, since only the English track is Atmos):
SELECT_TRACK audio index 0 â audio path: NONE (Russian)
SELECT_TRACK audio index 1 â audio path: ATMOS (English)
The HTML audioTracks list is also correctly ordered: idx0 {language:"ru"}, idx1 {language:"en"}.
But with English selected in Plexâs menu, the app enables audioTracks[0] (Russian). Forceâenabling audioTracks[1] from the DevTools console makes the pipeline switch instantly (SELECT_TRACK index 1) and the correct language plays. So the player is innocent â the app hands it the wrong index.
The bug in the app code
HTMLPlayer._selectAudioTrack (in packages_app_components_player_HTMLPlayer_tsx.js) delegates to a webOSâspecific helper injected from getAppConfig_ts.js. Deobfuscated:
function getSelectedAudioStreamIndex(serverStreams, elementTracks) {
// (filters truehd/dca/pcm when list lengths differ â not relevant here)
const r = serverStreams.findIndex(s => s.selected);
// sanity check: per-position language match, EXACT string equality
const ok = serverStreams.every((s, i) => {
const elemLang = elementTracks[i].language;
if (!(s.languageCode || (elemLang && elemLang !== "und"))) return true;
return s.languageCode === elemLang
|| (!!s.languageCode && (s.languageTag || isoMap[s.languageCode]) === elemLang);
});
return ok ? r : elementTracks.length - r - 1; // â check fails â MIRROR the index
}
If any position fails the language comparison, the app assumes the elementâs track list is reversed (presumably a workaround for some old webOS firmware) and enables the mirrored entry.
My fileâs English track carries the Matroska LanguageIETF tag en-US (typical of Amazon WEBâDLs), so PMS reports languageTag: "en-US" â but the webOS media element normalizes audioTracks[i].language to "en". The exactâequality check fails on that position, the helper takes the âreversedâ branch, and with English selected (r = 1) it returns 2 â 1 â 1 = 0 â Russian. Selecting Russian returns 1 â English. A perfect swap, both ways.
I replayed this exact logic in the live page against the real server metadata: langCheckPassed: false, indexAppWouldEnable: 0. Matches the observed behavior bitâforâbit. (The helper even logs "Unexpected number of audio tracks available on media element" in a related path â searchable in the source.)
The real trigger (verified)
It is not about codecs. I tested three multiâtrack files, all Direct Play:
| File |
Audio |
Language tags (PMS languageTag vs element) |
Result |
| AC3 rus + EâAC3 eng (Atmos) |
mixed |
ru/ru â, en-US/en â |
swapped |
| AC3 rus + EâAC3 eng (Atmos), different release |
mixed |
ru/ru â, en/en â |
correct |
| AC3 rus + AC3 rus + EâAC3 eng |
mixed |
all â |
correct |
Same codec mix, opposite outcomes â the only difference is the containerâs IETF language tag. The trigger is: any DirectâPlayed multiâtrack file where one streamâs BCPâ47 tag doesnât exactly equal the elementâs normalized language (en-US vs en, regional variants, missing/und mismatches). Thatâs why this bug looks fileâdependent and random across release groups, and why it survives reinstalls and different PMS versions.
Why the known workarounds work
- Disable âAllow Direct Playâ / disable EâAC3 (earlier in this thread): forces a transcode to a single audio stream â with one element track the helper is skipped entirely (
tracks[0].enabled = true unconditionally). Same reason the desktop web client is unaffected.
- Fixing the fileâs tag also fixes playback:
mkvpropedit file.mkv --edit track:a2 --set language-ietf=en (seconds, no remux) makes the check pass and the correct track play.
The ask
The fix is one line in the webOS client: compare primary subtags (languageTag.split("-")[0]) instead of exact strings â or better, drop the guessâreversal heuristic and match element tracks by language directly. The audioStreamID sent to the server is already correct; only the local audioTracks.enabled mapping is mirrored. Relevant because the webOS app has been frozen for many of us, and en-USâtagged WEBâDLs are extremely common.
Happy to share the DevTools dumps, pipeline SELECT_TRACK logs, and PMS stream JSON if it helps repro.