Repository navigation
Allow invalidation of resolved assets in X-ASSET-LIST interstitials #3322
Description
Use case description
Our use case requires live HLS streams with HLS Interstitials where EXT-X-DATERANGE interstitials use X-ASSET-LIST rather than X-ASSET-URI. The asset-list endpoint is dynamic and is provided by a third party, so a fresh request can return a different set of ads depending on a number of business rules.
We need an opt-in way for playback to resolve asset lists for interstitials that are behind the current playback position when the user later scrubs back before them. For example, if the user joins at the live edge and several interstitial ad groups are already behind that position, the app should be able to avoid playing those immediately, but still allow them to resolve normally if the user scrubs back before any of them.
Likewise, we also need an opt-in way to re-request an asset list for an ad group that was already resolved/played earlier. When the user scrubs back before that ad group and playback reaches the ad opportunity again, we want to invalidate the previous asset-list result and request the endpoint again, rather than replaying the old resolved media items.
With current Media3 behavior, joining a live HLS interstitial stream at the live edge may immediately play the latest unplayed ad group before the current playback position. To avoid that, our app marks ad groups behind the current position as skipped and groups ahead as available. We repeat this after seek/skip events.
This prevents immediate playback of past ads, but it does not allow those past X-ASSET-LIST ad groups to resolve later. Marking a group skipped removes its pending asset-list entry from the loader's internal unresolvedAssetLists. If the user then scrubs back before that ad group and the app tries to make it available, the ad has no mediaItems, and there is no pending asset-list entry for the loader to resolve.
Similarly, once an asset list has been resolved, Media3 stores the resolved ad media items in AdPlaybackState and removes the asset-list entry from the pending unresolvedAssetLists. There does not appear to be a public API to invalidate that resolved result and schedule resolution again.
I've gone through this closed issue, but I still can't find any public API that would allow us to achieve this behavior without altering media3 internals in a fork. Please do correct me if this is already possible.
Proposed solution
We suggest to add an opt-in API that lets apps invalidate and requeue HLS X-ASSET-LIST interstitial resolution without requiring Media3 to adopt any default scrub-back ad policy.
One possibility we propose is to add a method that lets implementors invalidate and requeue X-ASSET-LIST interstitial resolution. E.g.
void invalidateAssetListAdGroup(int adGroupIndex);Expected behavior:
- Applies to ad groups resulting from
X-ASSET-LIST - Clears any previously resolved media items for the given ad group.
- Restores the ad group to an unresolved state so the loader can apply a new asset list
- Schedules resolution of assets (if appropriate)
We could then leverage this function in onPositionDiscontinuity to do something like:
if (group.timeUs <= playbackPositionInPeriodUs) {
if (isSkippable(group)) {
hlsInterstitialsAdsLoader.setWithSkippedAdGroup(i);
}
} else if (isReplayable(group)) {
hlsInterstitialsAdsLoader.invalidateAssetListAdGroup(i)
hlsInterstitialsAdsLoader.setWithAvailableAdGroup(i);
}Alternatively, Media3 could expose an opt-in general request strategy on HlsInterstitialsAdsLoader, so apps can decide when an asset-list backed interstitial should be invalidated/re-requested during seek or boundary crossing.
Alternatives considered
We tried using existing public apis to:
- mark ad groups behind the current playback position as skipped
- mark future/replayable groups as available
This is not sufficient for X-ASSET-LIST interstitials.
-
For past ad groups that have never been resolved, setting the group available cannot work because the ad has no resolved mediaItems. The asset-list entry was also removed from
unresolvedAssetListswhen the group was marked skipped, so there is no request for the loader to resolve. -
For already-resolved/played ad groups, setting the group available reuses the previously resolved media items. It does not invalidate the old asset-list response or request a new one.
The currently available function setWithAvailableAdMediaItem is not enough for our needs since we work off multiple mediaItems. It also would require to duplicate the loading logic, which would be redundant.
Activity
[-]Allow invalidation of resolved asset in X-ASSET-LIST interstitials[/-][+]Allow invalidation of resolved assets in X-ASSET-LIST interstitials[/+]marcbaechinger commentedon Jul 7, 2026
Thanks for your feature request.
With the next release the ads loader will include
setWithAssetListResetand variants of this method which, as I understand, pretty much does what you ask for in theexpected behaviorsection of your post:Can you check in [1] whether this is sufficient for your use case and if not, let us know what is missing to achieve what you try to achieve?
EDIT: I updated the comment in #2749 (comment) to name the new API added with the commit. This wasn't clear before my change. Sorry for this.
[1] https://github.com/androidx/media/blob/release-1.11.0-beta01/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsInterstitialsAdsLoader.java#L891-L1114
kelmer44 commentedon Jul 8, 2026
well isnt this great timing, thanks for pointing me to the right version. Let me test this one and come back to report
kelmer44 commentedon Jul 8, 2026
So far i havent been able to get 1.11.0-beta01 to even play interstitials, even without the business logic laid out above. Just doing this on the demo app:
and
gets me an interstitial ad right after joining the stream, then never again.
same code in 1.10.0 gets me there. Has anything else changed between this versions that im missing?
marcbaechinger commentedon Jul 8, 2026
Lemme check this with the test streams I have! Any specifics to the live stream like pre rolls?
kelmer44 commentedon Jul 8, 2026
Nope, here's a sample manifest of my local test server.
marcbaechinger commentedon Jul 8, 2026
Yeah, something is odd. I can repro problems with live. When going back before commit [1] live streams work again without issues. I need to work out whether this commit is the causing the problem in
HlsInterstitialsAdsLoader. Thanks for your report and sorry for the inconvenience. I'll update this issue when I have news.EDIT: the actual culprit is #3126
[1] 1835f0d
kelmer44 commentedon Jul 14, 2026
I saw the offending commit was reverted, thanks for that @marcbaechinger.
I tried the new
setWithAssetListResetfunction. It does re-request the asset list as expected, but I’m seeing an issue with the ad group’scontentResumeOffsetUsafter reset.Our scrub-back logic is roughly:
The asset list is fetched again, but after
setWithAssetListResetthe ad group’scontentResumeOffsetUsappears to become 0. After the refreshed asset list resolves and plays, playback resumes at the original interstitial start time instead of after the underlying ad break, so we end up playing the broadcast ads underneath the interstitial.Our manifests may or may not include an explicit
X-RESUME-OFFSETdepending on the break. When it is absent, I understand from the HLS interstitial spec that the resume offset should be derived fromX-PLAYOUT-LIMIT/PLANNED-DURATIONor the asset-list durations.IMO resetting the asset list should not leave the ad group with a zero resume offset. Either the previous derived resume offset should be preserved until the new asset list resolves (which assumes same duration between different asset-lists), or the resume offset should be recalculated from the refreshed asset list / manifest metadata after reset.
marcbaechinger commentedon Jul 14, 2026
Thanks for following up and confirming that the asset list can be reset.
I agree with your interpretation that it should not be 0 if there is a duration for the ad/interstitial.
Can you post the snippet of the DATERANGE tag in the playlist and the asset list JSON that produces this result? You can remove/hide the URIs, it's only about the numbers regarding durations. You can send this to android-media-github@google.com using a subject in the format "Issue #3322" if you don't want to share this here.
With this information I can craft a unit test for repro and then provide a fix.
Some thoughts/points of discussion:
In absense of an X-RESUME-OFFSET we would use the duration of the asset list accordingly. Does this make sense in your understanding as well?
X-PLAYOUT-LIMITis not relevant regarding the offset because we have a duration from the asset list.My interpretation of the spec is that,
X-PLAYOUT-LIMITsays how long to play the ad, but it does not say something about the resume offset in case we have a explicitly defined duration of the ad. This explicit duration is then used as the resume offset as defined above asduration of the interstitial. So with an asset list that has a mandatory duration,X-PLAYOUT-LIMITdoesn't determine the resume offset AIUI.The playout limit can be different to the resume offset. What I mean is that an ad of 2 seconds of duration, can jump over 30 seconds of the primary stream. This would make sense to allow apps to splice in an ad with an arbitrary duration and still skip over a different duration in the primary stream.
Your snippet would be helpfull as a first step, so we can sort out the bug in our code first, then the interpretation of the spec. Happy to hear whether you have made different exprience with yout manifest on other platforms.
kelmer44 commentedon Jul 14, 2026
I dont mind sharing as this all comes from my test server. Here's the manifest, asset list and sample interstitial m3u8.
responses.zip
I agree with you when reading the spec, but media3's code currently has this:
and
if i am reading this correctly, when resumeOffsetUs is not present, then it falls back to
resolveInterstitialDurationUs, which checks playout limit first. So it seems you are already using playout limit to calculate duration.marcbaechinger commentedon Jul 14, 2026
Yeah, I didn't say the code is correct. Will check and update the issue when I found something. Many thanks!
Fix content resume offset calculation for asset list interstitials
marcbaechinger commentedon Jul 16, 2026
I submitted a first fix above that updates the resume offset with the duration of the ads in the asset JSON. I added a unit test that reflects your playlist/JSON samples. Thanks for that.
I aim for another fix for
resolveInterstitialDurationUsto not take the playout limit into account. However, I consider this as a potentially difficult change with side effects on the behavior. I don't want to push this as late as for an rc01 without having certainty that I don't produce side effects. I'm not sure whether I can squeeze this in for rc01. It feels like a kind of a change that should not be squeezed in in any way. Let's see. I do my best but I can not promise that will land in 1.11 I'm afraid.kelmer44 commentedon Jul 16, 2026
Thats understandable, no expectation to have this in any particular version. Thanks @marcbaechinger. Will test and report back!
Fix content resume offset calculation for asset list interstitials
Fix content resume offset calculation for asset list interstitials
Fix content resume offset calculation for asset list interstitials
Fix content resume offset calculation for asset list interstitials
kelmer44 commentedon Jul 17, 2026
The new version worked great when reseting the asset lists, but i think there's a regression now on incoming interstitials; i believe the resumeOffset calculation is trying in
getClosestSegmentBoundaryUsto snap to a position that doesn't exist yet in the timeline and thus it's falling back to zero (my streams have X-SNAP="OUT,IN")The result is that interstitials that enter the live window "naturally" seem to have a resume position of 0 now.
marcbaechinger commentedon Jul 20, 2026
Thanks. That's embarrassing. :( Sorry. I have a fix for that with a test case. Will try to expand on the test cases.
Fix calculation of content resume offset when segments not in window
kelmer44 commentedon Jul 30, 2026
I just tested with the latest main and on a first glance everything seems to be working as expected. Thanks a lot! I think this can be closed now
I assume this will not go into 1.11.0 but into 1.12.0 instead, right?
marcbaechinger commentedon Aug 3, 2026
Thanks again for testing and giving feedback! Your contribution is very important to us to do the right thing for stream variants and use cases that we haven't seen yet. Your help is very much appreciated!
Yeah, I'm afraid but given we were already on rc01 and the required change to fix that was of considerable size I decided to keep it out of 1.11. for now. I know this is suboptimal, but I think prioritizing a safe procedure here makes sense.
Fix calculation of content resume offset when segments not in window
build(android): raise Media3 to 1.11.1