Version
Media3 main branch
More version details
Media3 1.10.1 (and all prior versions)
Devices that reproduce the issue
All devices
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Yes
Reproduction steps
- Create a multivariant playlist that defines a variable:
master.m3u8:
#EXTM3U
#EXT-X-DEFINE:NAME="cdnPrefix",VALUE="https://cdn.example.com/ (https://cdn.example.com/)"
#EXT-X-STREAM-INF:BANDWIDTH=500000
media.m3u8
media.m3u8:
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:8
#EXTINF:10,
{$cdnPrefix}segment001.ts
#EXTINF:10,
{$cdnPrefix}segment002.ts
#EXT-X-ENDLIST
- Host these files on any HTTP server (or use
FakeDataSource in a test)
- Create a
DownloadRequest for master.m3u8 and add it to DownloadManager
- Observe that segment requests go to the literal URL
{$cdnPrefix}segment001.ts instead of https://cdn.example.com/segment001.ts
Note
Playback works correctly because HlsPlaylistTracker passes the multivariant playlist to the parser. The download path (HlsDownloader → SegmentDownloader) uses a single HlsPlaylistParser() that never
stores the parsed multivariant playlist, so variable definitions are unavailable when media playlists are parsed.
Expected result
Variables defined in the multivariant playlist should resolve in media playlist segment URLs during download, matching the playback behavior.
Actual result
HlsDownloader uses a single HlsPlaylistParser() instance (constructed with HlsMultivariantPlaylist.EMPTY). After parsing the multivariant playlist, the result
is not stored back into the parser. When media playlists are subsequently parsed by the same instance, multivariantPlaylist is still EMPTY, so
variableDefinitions is unavailable and {$variable} references pass through unsubstituted.
This causes:
- Download failures (HTTP 404/400 on malformed URLs)
IndexOutOfBoundsException in HlsMediaPeriod.getStreamKeys() when DownloadHelper.getDownloadRequest() is called
Stack trace:
java.lang.IndexOutOfBoundsException: index (0) must be less than size (0)
at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1372)
at com.google.common.collect.RegularImmutableList.get(RegularImmutableList.java:84)
at androidx.media3.exoplayer.hls.HlsMediaPeriod.getStreamKeys(HlsMediaPeriod.java:289)
at androidx.media3.exoplayer.offline.DownloadHelper.getDownloadRequestBuilder(DownloadHelper.java:939)
at androidx.media3.exoplayer.offline.DownloadHelper.getDownloadRequest(DownloadHelper.java:891)
Analysis
- Playback works because
HlsPlaylistTracker creates HlsPlaylistParser(multivariantPlaylist, ...) with the real multivariant playlist.
- Download fails because
HlsDownloader (via SegmentDownloader) uses the same parser instance for both master and media playlists, but the parser never stores
the parsed multivariant playlist for reuse.
The fix is to make HlsPlaylistParser store the multivariant playlist after parsing it in parse(), so subsequent media playlist parses on the same instance
inherit the variable definitions.
Media
No DRM
Bug Report
Version
Media3 main branch
More version details
Media3 1.10.1 (and all prior versions)
Devices that reproduce the issue
All devices
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Yes
Reproduction steps
master.m3u8:
#EXTM3U
#EXT-X-DEFINE:NAME="cdnPrefix",VALUE="https://cdn.example.com/ (https://cdn.example.com/)"
#EXT-X-STREAM-INF:BANDWIDTH=500000
media.m3u8
media.m3u8:
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:8
#EXTINF:10,
{$cdnPrefix}segment001.ts
#EXTINF:10,
{$cdnPrefix}segment002.ts
#EXT-X-ENDLIST
FakeDataSourcein a test)DownloadRequestformaster.m3u8and add it toDownloadManager{$cdnPrefix}segment001.tsinstead ofhttps://cdn.example.com/segment001.tsNote
Playback works correctly because
HlsPlaylistTrackerpasses the multivariant playlist to the parser. The download path (HlsDownloader→SegmentDownloader) uses a singleHlsPlaylistParser()that neverstores the parsed multivariant playlist, so variable definitions are unavailable when media playlists are parsed.
Expected result
Variables defined in the multivariant playlist should resolve in media playlist segment URLs during download, matching the playback behavior.
Actual result
HlsDownloaderuses a singleHlsPlaylistParser()instance (constructed withHlsMultivariantPlaylist.EMPTY). After parsing the multivariant playlist, the resultis not stored back into the parser. When media playlists are subsequently parsed by the same instance,
multivariantPlaylistis stillEMPTY, sovariableDefinitionsis unavailable and{$variable}references pass through unsubstituted.This causes:
IndexOutOfBoundsExceptioninHlsMediaPeriod.getStreamKeys()whenDownloadHelper.getDownloadRequest()is calledStack trace:
java.lang.IndexOutOfBoundsException: index (0) must be less than size (0)
at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1372)
at com.google.common.collect.RegularImmutableList.get(RegularImmutableList.java:84)
at androidx.media3.exoplayer.hls.HlsMediaPeriod.getStreamKeys(HlsMediaPeriod.java:289)
at androidx.media3.exoplayer.offline.DownloadHelper.getDownloadRequestBuilder(DownloadHelper.java:939)
at androidx.media3.exoplayer.offline.DownloadHelper.getDownloadRequest(DownloadHelper.java:891)
Analysis
HlsPlaylistTrackercreatesHlsPlaylistParser(multivariantPlaylist, ...)with the real multivariant playlist.HlsDownloader(viaSegmentDownloader) uses the same parser instance for both master and media playlists, but the parser never storesthe parsed multivariant playlist for reuse.
The fix is to make
HlsPlaylistParserstore the multivariant playlist after parsing it inparse(), so subsequent media playlist parses on the same instanceinherit the variable definitions.
Media
No DRM
Bug Report
adb bugreportto android-media-github@google.com after filing this issue.