Skip to content

Commit f0648ec

Browse files
chore(docs): update code docs
1 parent a28a4a0 commit f0648ec

4 files changed

Lines changed: 66 additions & 44 deletions

File tree

docs/data/search.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/module-items.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/source_api_items.bs.html

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -204,26 +204,26 @@
204204
end for
205205
end if
206206

207-
' Remove AAC from codec list in two scenarios:
208-
' 1. ANY multichannel source (>2ch) when user has passthrough support - prevents transcoding to AAC which would downmix to stereo
209-
' 2. Unsupported AAC profiles (Main, HE-AAC) - TODO: Remove after server supports transcoding between AAC profiles
210-
shouldRemoveAac = false
207+
' Tailor the device profile's transcoding AudioCodec list to the source in two scenarios:
208+
' 1. Multichannel source (>2ch) with passthrough support - drops stereo-output codecs so
209+
' the server only offers surround variants (eac3/ac3/dts) in the HLS master playlist.
210+
' Without this, Roku's HLS player has been observed to pick the stereo variant.
211+
' 2. AAC source with unsupported profile (Main, HE-AAC) - drops AAC so transcoding
212+
' falls through to MP3. TODO: remove once server transcodes between AAC profiles.
213+
shouldOptimize = false
211214

212-
' Scenario 1: ANY multichannel source with passthrough support
213-
' Removes AAC to force server to use surround passthrough codecs (eac3, ac3, dts) instead of transcoding to stereo AAC
214215
if channelCount > 2 and hasPassthruSupport
215-
shouldRemoveAac = true
216+
shouldOptimize = true
216217
end if
217218

218-
' Scenario 2: AAC with unsupported profile
219219
if isValid(selectedAudioStream.Codec) and LCase(selectedAudioStream.Codec) = "aac"
220220
if isValid(selectedAudioStream.Profile) and (LCase(selectedAudioStream.Profile) = "main" or LCase(selectedAudioStream.Profile) = "he-aac")
221-
shouldRemoveAac = true
221+
shouldOptimize = true
222222
end if
223223
end if
224224

225-
if shouldRemoveAac
226-
removeUnsupportedAacFromProfile(postData.DeviceProfile, channelCount)
225+
if shouldOptimize
226+
optimizeAudioCodecListForSource(postData.DeviceProfile, channelCount)
227227
end if
228228
end if
229229
end if
@@ -516,37 +516,47 @@
516516
end if
517517
end sub
518518

519-
' Removes AAC from the device profile codec list to prevent stereo downmix of multichannel audio.
520-
' Also handles unsupported AAC profiles (Main, HE-AAC).
521-
' For stereo sources (≤2ch), also removes surround passthrough codecs (eac3, ac3, dts)
522-
' so transcoding falls through to MP3 (better compatibility + smaller files).
523-
sub removeUnsupportedAacFromProfile(deviceProfile as object, channelCount as integer)
519+
' Tailors a video transcoding profile's AudioCodec list to the current source's channel count
520+
' so the server (and downstream HLS variant selection) can't fall back to a less-desirable codec.
521+
'
522+
' Multichannel source (>2ch):
523+
' Strips ALL stereo-output codecs (aac, mp3, flac, alac, pcm, lpcm, wav, vorbis). Without this,
524+
' Jellyfin's HLS muxer offers BOTH the surround codec AND a stereo fallback as separate variants
525+
' in the master playlist, and Roku's HLS player has been observed (via in-house testing on
526+
' physical hardware; no upstream tracking issue at time of writing) to pick the stereo variant —
527+
' defeating the entire point of having surround passthrough hardware. Caller must already have
528+
' verified the device has surround passthrough before invoking with channelCount > 2.
529+
'
530+
' Stereo source (≤2ch):
531+
' Strips surround passthrough codecs (eac3, ac3, dts) plus aac so transcoding falls through to
532+
' mp3. Avoids handing the server a surround target codec for content that's only 2ch anyway.
533+
sub optimizeAudioCodecListForSource(deviceProfile as object, channelCount as integer)
524534
' Validate inputs
525535
if not isValid(deviceProfile) then return
526536
if not isValid(deviceProfile.TranscodingProfiles) then return
527537

528-
' Surround passthrough codecs that should be removed for stereo sources
538+
' Codecs that, as a server transcode target, can't carry surround channels — strip these for
539+
' multichannel sources so the server is forced to pick a surround codec from the remaining list.
540+
' Note: this list is intentionally distinct from `stereoOutputCodecs` in deviceCapabilities.bs;
541+
' that list represents Roku decode-path behavior (different concept), so the contents differ.
542+
stereoOnlyCodecs = ["aac", "mp3", "flac", "alac", "pcm", "lpcm", "wav", "vorbis"]
543+
' Surround passthrough codecs — pointless for stereo sources, strip them.
529544
surroundCodecs = ["eac3", "ac3", "dts"]
530545

531546
for each rule in deviceProfile.TranscodingProfiles
532547
if isValid(rule.Type) and rule.Type = "Video"
533548
if isValid(rule.AudioCodec)
534-
' Split codec list into array
535549
codecList = rule.AudioCodec.split(",")
536550
newCodecList = []
537551

538-
' Remove AAC always, and surround codecs for stereo sources
539552
for each codec in codecList
540553
skipCodec = false
541554

542-
' Always skip AAC to prevent downmix
543-
if codec = "aac"
555+
if channelCount > 2 and arrayHasValue(stereoOnlyCodecs, codec)
544556
skipCodec = true
545557
end if
546558

547-
' For stereo sources (≤2ch), also skip surround codecs
548-
' This ensures transcoding goes to MP3 instead of trying AC3/EAC3
549-
if channelCount <= 2 and arrayHasValue(surroundCodecs, codec)
559+
if channelCount <= 2 and (codec = "aac" or arrayHasValue(surroundCodecs, codec))
550560
skipCodec = true
551561
end if
552562

@@ -555,7 +565,10 @@
555565
end if
556566
end for
557567

558-
' Rebuild codec string
568+
if newCodecList.count() = 0 and codecList.count() > 0
569+
print "[optimizeAudioCodecListForSource] WARN: AudioCodec list for container '" + rule.Container + "' became empty after optimization (source ch=" + channelCount.toStr() + ", original=" + rule.AudioCodec + "); server may reject the transcoding rule"
570+
end if
571+
559572
rule.AudioCodec = newCodecList.join(",")
560573
end if
561574
end if

docs/source_utils_deviceCapabilities.bs.html

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,9 @@
502502
for each container in transcodingContainers
503503
audioCodecList = []
504504

505-
' 1. AAC always first (efficient for stereo, removed at playback time for multichannel sources with passthrough)
505+
' 1. AAC always first (efficient for stereo). On multichannel + passthru playback, all
506+
' stereo-output codecs (including AAC) are stripped at playback time by
507+
' optimizeAudioCodecListForSource (see items.bs).
506508
audioCodecList.push("aac")
507509

508510
' 2. Add surround passthrough codecs if supported (in preference order)
@@ -591,24 +593,30 @@
591593
resolutionConditions = getResolutionConditions()
592594

593595
' ========================================
594-
' DETECT SURROUND PASSTHROUGH SUPPORT
596+
' USER SETTING — FORCE SERVER-SIDE MULTICHANNEL HANDLING
595597
' ========================================
596-
597-
sixChannelPassthruCodecs = getSupportedPassthruCodecs(di, 6)
598-
eightChannelPassthruCodecs = getSupportedPassthruCodecs(di, 8)
599-
hasSurroundPassthru = sixChannelPassthruCodecs.count() > 0 or eightChannelPassthruCodecs.count() > 0
600-
601-
' Check user setting for multichannel decode preference
602-
' When disabled, force stereo-output codecs to 2ch max (same as passthrough behavior)
598+
'
599+
' playbackDecodeMultichannelAudio = false means the user has explicitly asked us to keep
600+
' multichannel sources off the Roku's decoder — usually because Roku's downmix sounded worse
601+
' than the server's on their system, or because they want bitstream surround output preserved
602+
' via server transcoding instead of relying on Roku PCM output.
603+
'
604+
' Effect: cap stereo-output codecs at 2ch in the codec profile so the server is forced to
605+
' transcode multichannel sources rather than offering them for direct play.
603606
userWantsDecodeLimit = not globalUserSettings.playbackDecodeMultichannelAudio
604607

605608
' ========================================
606609
' CODEC CATEGORIES
607610
' ========================================
608611

609-
' Codecs that Roku decodes multichannel but only OUTPUTS as stereo PCM
610-
' When user has a receiver, we force these to 2ch max to trigger transcoding
611-
' to surround output codecs (eac3/ac3/dts) instead of direct playing and downmixing
612+
' Codecs whose decode-then-output path on Roku does not always pass through multichannel —
613+
' depending on the user's Audio Output Mode and HDMI/eARC sink, these may downmix to 2ch PCM.
614+
' We do NOT cap these at 2ch by default: most modern HDMI sinks accept multichannel PCM and
615+
' the official Roku decode behavior produces correct multichannel output for capable users.
616+
' The cap is applied only when userWantsDecodeLimit is true (see setting above).
617+
'
618+
' Note: this list is intentionally distinct from `stereoOnlyCodecs` in items.bs; that list
619+
' represents server transcode-target capability (different concept), so the contents differ.
612620
stereoOutputCodecs = ["aac", "flac", "alac", "pcm", "lpcm", "wav", "opus", "vorbis"]
613621

614622
' ========================================
@@ -619,11 +627,12 @@
619627
audioChannels = [8, 6, 2] ' highest first
620628

621629
for each audioCodec in audioCodecs
622-
' Special handling for stereo-output codecs when user has surround passthrough OR user disabled multichannel decode
623-
' These codecs decode multichannel but Roku only outputs as stereo PCM
624-
if arrayHasValue(stereoOutputCodecs, audioCodec) and (hasSurroundPassthru or userWantsDecodeLimit)
625-
' Force to 2 channels maximum to prevent Roku from downmixing multichannel to stereo
626-
' This triggers server to transcode to surroundOutputCodecs (eac3/ac3/dts) or stereo instead
630+
' When the user has explicitly disabled on-device multichannel decoding, cap stereo-output
631+
' codecs at 2ch to force server-side transcoding for multichannel sources.
632+
if arrayHasValue(stereoOutputCodecs, audioCodec) and userWantsDecodeLimit
633+
' Force to 2 channels maximum to prevent Roku from decoding the multichannel source.
634+
' Server transcodes to a surround codec (eac3/ac3/dts) if surround passthrough is
635+
' available, otherwise transcodes to stereo.
627636
for each codecType in ["VideoAudio", "Audio"]
628637
' Special AAC profile restrictions (Main and HE-AAC not supported)
629638
if audioCodec = "aac"

0 commit comments

Comments
 (0)