Repository navigation
HLS (fMP4) Exoplayer v2.17.1 DTS Digital Surround Passthrough Issue #10204
Description
ExoPlayer Version
2.17.1
Devices that reproduce the issue
Nvidia Shield TV Pro
Devices that do not reproduce the issue
None
Reproducible in the demo app?
Yes
Reproduction steps
Playback a HLS (fMP4) DTS Digital Surround audio stream on Nvidia Shield TV Pro (connected to an AVR via HDMI) running v2.17.1 Exoplayer demo app.
The DTS bitstream should be passthrough to the AVR for decoding. This works in earlier versions of Exoplayer, E.g. v2.14.1. In v2.17.1 the playback fails.
Expected result
The DTS bitstream should be passthrough to the AVR for decoding.
Actual result
Playback Fails.
Diagnosis
In Exoplayer, there is a variable allowChunklessPreparation in HLSMediaPeriod.java which determines whether chunkless preparation (for HLS-fMp4 streams) is allowed. If chunkless preparation is allowed, the attributes of the Codec (such as channelCount) will not be determined by downloading a segment and parsing the Moov box (AtomParsers.parseAudioSampleEntry()). Instead, it will be trying to read these attributes from the Multivariant HLS playlist. Details on Multivariant playlist is here: https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming/creating_a_multivariant_playlist
When Exoplayer is deciding whether to passthrough the bitstream, it calls DefaultAudioSink().isPassthroughPlaybackSupported(), which will return false when channelCount of the stream is unknown.
When allowChunklessPreparation is set to true, it depends on the HLS manifests (See HlsPlaylistParser::parseMultivariantPlaylist(), HLSMediaPeriod::deriveAudioFormat()) to provide information on the CODECS. However, in the test streams used, "channelCount" is not part of the information provide by the HLS Multivariant playlist. This resulted in the channelCount not being defined and DefaultAudioSink().isPassthroughPlaybackSupported() will return false causing the Digital Surround bitstream passthrough to fail.
When allowChunklessPreparation is set to false, channelCount will be read by parsing the Moov box (AtomParsers.parseAudioSampleEntry()) during the setup stage. A subsequent call to DefaultAudioSink().isPassthroughPlaybackSupported() will then return true and passthrough is possible.
In Exoplayer v2.14.1, allowChunklessPreparation in HLSMediaPeriod.java was not set explicitly and it defaults to false. Therefore passthrough works.
In Exoplayer v2.17.1, allowChunklessPreparation in HLSMediaPeriod.java was initialised to true. This caused passthrough to fail for the reasons mentioned earlier (channelCount undefined).
Solution
In order to overcome this issue, It is recommended in Exoplayer v2.17.X to initialise allowChunklessPreparation to false in the constructor of HLSMediaSource.Factory. This will allow HLS-fMP4 with DTS Digital Surround to passthrough successfully.
Media
Test media will be emailed to [email protected]
Bug Report
- You will email the zip file produced by
adb bugreportto [email protected] after filing this issue.
Activity
christosts commentedon Apr 20, 2022
@andrewlewis do you now if we can make a safe guess on the number of channels if the information is missing from
Formatfor those audio formats for which the player does audio passthrough? In this issue it's DTS, but I think the same problem may apply for the other audio encodings too. With HLS chunkless preparation, we cannot know the channel count from the playlist., henceFormat.channelCountis not set.For example, could
DefaultAudioSinkdo something similar as in the ENCODING_E_AC3_JOC case (link)?andrewlewis commentedon Apr 20, 2022
If this is for plain DTS it looks like it only goes up to 5.1 so we could conservatively check for 6 channel output capability. That should be safe in the sense that there can't be more channels in the stream when it loads. For HDMI audio capabilities we get a max channel count so that probably means that fewer channels than the max are always supported.
cedricxperi commentedon Apr 27, 2022
Hi @andrewlewis @christosts ,
Picking up on your suggestions, how about something like below in DefaultAudioSink.getEncodingAndChannelConfigForPassthrough(). I have tested that the below works for passing through DTS Digital surround on Nvidia Shield TV Pro. Thanks.
if (encoding == C.ENCODING_E_AC3_JOC) {
// E-AC3 JOC is object based so the format channel count is arbitrary. From API 29 we can get
// the channel count for this encoding, but before then there is no way to query it so we
// assume 6 channel audio is supported.
if (Util.SDK_INT >= 29) {
// Default to 48 kHz if the format doesn't have a sample rate (for example, for chunkless
// HLS preparation). See [Internal: b/222127949].
int sampleRate = format.sampleRate != NO_VALUE ? format.sampleRate : 48000;
channelCount =
getMaxSupportedChannelCountForPassthroughV29(C.ENCODING_E_AC3_JOC, sampleRate);
if (channelCount == 0) {
Log.w(TAG, "E-AC3 JOC encoding supported but no channel count supported");
return null;
}
} else {
channelCount = 6;
}
} else if (encoding == C.ENCODING_DTS) {
channelCount = format.channelCount;
if (channelCount == NO_VALUE){
channelCount = 6;
}
} else {
channelCount = format.channelCount;
if (channelCount > audioCapabilities.getMaxChannelCount()) {
return null;
}
}
christosts commentedon May 9, 2022
Yes, we will add a fix along those lines - we are planning to have the fix included in the release.
cedricxperi commentedon May 9, 2022
@christosts That's great. Thanks a lot.
christosts commentedon Jun 8, 2022
The upcoming 2.18 release will have a fix for this. The issue will be updated with a link to the commit once the dev branch is updated. The submitted fix covers not only DTS but other surround formats for which the player does audio passthrough playback and were affected by HLS chunkless preparation.
Thank you for bringing this issue to our attention and sharing test content. I will close this issue for now, feel free to re-open is the fix did not resolve your problem.
Audio passthrough: handle unset audio format channel count
Audio passthrough: handle unset audio format channel count
Audio passthrough: handle unset audio format channel count
Audio passthrough: handle unset audio format channel count