Skip to content

Commit d54529b

Browse files
committed
Ensures errors are correctly attributed to ad or content
This change is part of a fix to the issue "Player Errors during Ad playback not attributed correctly" The change introduced in commit 7cf2fd9 was a partial fix to allow ad playback errors to be reported. It delayed removing the listener when an error released the AdsMediaSource. What remained was the larger issue: When an error occurs **during content preparation while an ad is still playing**, ExoPlayer cannot correctly attribute the error to the content source. Instead, the error bubbles up through `ExoPlayerImplInternal.stopInternal()` and causes the **entire CompositeMediaSource (AdsMediaSource + content)** to be torn down. This results in the `ImaAdsLoader` being stopped prematurely — even when the ad is not at fault — and breaks proper ad lifecycle handling. **User-Facing Issues** 1. **The ad is incorrectly marked as failed** A content preparation error is misattributed to the currently playing ad, resulting in an invalid `AD_STATE_ERROR`. 2. **The ad is never marked as completed** Even though `ALL_ADS_COMPLETED` is dispatched by IMA, the player is torn down before ExoPlayer updates `AdPlaybackState` to reflect completion. 3. **A single failing ad in a pod is not skipped** If one ad in a pod fails (e.g., due to a 404 or DRM error), the player stops entirely instead of skipping to the next ad in the group. 4. **A failing ad does not fall back to content** When an ad fails, the player should skip to content. Instead, the entire playback pipeline is torn down via `stopInternal()`. This change fixes the first two User-Facing issues. When combined with error recovery logic it also fixes the 4th issue. The 3rd issue may require some work on the IMA SDK side, the mid-pod failure aborts all ad playback with this fix, rather than skipping just the failing ad.
1 parent ec6ac47 commit d54529b

File tree

1 file changed

+19
-5
lines changed
  • libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima

1 file changed

+19
-5
lines changed

libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/AdTagLoader.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,23 @@ public void activate(Player player) {
391391
/** Deactivates playback. */
392392
public void deactivate() {
393393
Player player = checkNotNull(this.player);
394-
if (!AdPlaybackState.NONE.equals(adPlaybackState) && imaPausedContent) {
394+
// Post release of listener behind any already queued Player.Listener events to ensure that
395+
// any pending events are processed before the player is deferred.
396+
handler.post(
397+
() -> {
398+
deactivateInternal(player);
399+
});
400+
}
401+
402+
/**
403+
* Deactivates playback internally, after the Listener.onEvents() cycle completes so the complete
404+
* state change picture is clear. For example, if an error caused the deactivation, need to
405+
* determine whether the error was for an ad or content.
406+
*/
407+
private void deactivateInternal(Player player) {
408+
if (!AdPlaybackState.NONE.equals(adPlaybackState)
409+
&& imaPausedContent
410+
&& player.getPlayerError() == null) {
395411
if (adsManager != null) {
396412
adsManager.pause();
397413
}
@@ -402,9 +418,7 @@ public void deactivate() {
402418
lastVolumePercent = getPlayerVolumePercent();
403419
lastAdProgress = getAdVideoProgressUpdate();
404420
lastContentProgress = getContentVideoProgressUpdate();
405-
406-
// Post release of listener so that we can report any already pending errors via onPlayerError.
407-
handler.post(() -> player.removeListener(this));
421+
player.removeListener(this);
408422
this.player = null;
409423
}
410424

@@ -539,7 +553,7 @@ public void onPlayWhenReadyChanged(
539553

540554
@Override
541555
public void onPlayerError(PlaybackException error) {
542-
if (imaAdState != IMA_AD_STATE_NONE) {
556+
if (imaAdState != IMA_AD_STATE_NONE && player.isPlayingAd()) {
543557
AdMediaInfo adMediaInfo = checkNotNull(imaAdMediaInfo);
544558
for (int i = 0; i < adCallbacks.size(); i++) {
545559
adCallbacks.get(i).onError(adMediaInfo);

0 commit comments

Comments
 (0)