Move ad playback state into ImaAdsLoader

Once background and resuming is supported, the ads loader will be kept when the
player is destroyed and recreated. Move the state relating to the structure of
ads and what ads have been loaded/played out of the media source and into the
loader so the information is not lost when the source is released, in
preparation for supporting background and resuming.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=161503571
This commit is contained in:
andrewlewis 2017-07-11 02:25:21 -07:00 committed by Oliver Woodman
parent b31fd5bc7c
commit ef56c9fe39
6 changed files with 206 additions and 190 deletions

View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.ext.ima;
import android.net.Uri;
import com.google.android.exoplayer2.C;
import java.util.Arrays;
/**
* Represents the structure of ads to play and the state of loaded/played ads.
*/
/* package */ final class AdPlaybackState {
/**
* The number of ad groups.
*/
public final int adGroupCount;
/**
* The times of ad groups, in microseconds. A final element with the value
* {@link C#TIME_END_OF_SOURCE} indicates a postroll ad.
*/
public final long[] adGroupTimesUs;
/**
* The number of ads in each ad group. An element may be {@link C#LENGTH_UNSET} if the number of
* ads is not yet known.
*/
public final int[] adCounts;
/**
* The number of ads loaded so far in each ad group.
*/
public final int[] adsLoadedCounts;
/**
* The number of ads played so far in each ad group.
*/
public final int[] adsPlayedCounts;
/**
* The URI of each ad in each ad group.
*/
public final Uri[][] adUris;
/**
* Creates a new ad playback state with the specified ad group times.
*
* @param adGroupTimesUs The times of ad groups in microseconds. A final element with the value
* {@link C#TIME_END_OF_SOURCE} indicates that there is a postroll ad.
*/
public AdPlaybackState(long[] adGroupTimesUs) {
this.adGroupTimesUs = adGroupTimesUs;
adGroupCount = adGroupTimesUs.length;
adsPlayedCounts = new int[adGroupCount];
adCounts = new int[adGroupCount];
Arrays.fill(adCounts, C.LENGTH_UNSET);
adUris = new Uri[adGroupCount][];
Arrays.fill(adUris, new Uri[0]);
adsLoadedCounts = new int[adGroupTimesUs.length];
}
private AdPlaybackState(long[] adGroupTimesUs, int[] adCounts, int[] adsLoadedCounts,
int[] adsPlayedCounts, Uri[][] adUris) {
this.adGroupTimesUs = adGroupTimesUs;
this.adCounts = adCounts;
this.adsLoadedCounts = adsLoadedCounts;
this.adsPlayedCounts = adsPlayedCounts;
this.adUris = adUris;
adGroupCount = adGroupTimesUs.length;
}
/**
* Returns a deep copy of this instance.
*/
public AdPlaybackState copy() {
Uri[][] adUris = new Uri[adGroupTimesUs.length][];
for (int i = 0; i < this.adUris.length; i++) {
adUris[i] = Arrays.copyOf(this.adUris[i], this.adUris[i].length);
}
return new AdPlaybackState(Arrays.copyOf(adGroupTimesUs, adGroupCount),
Arrays.copyOf(adCounts, adGroupCount),
Arrays.copyOf(adsLoadedCounts, adGroupCount),
Arrays.copyOf(adsPlayedCounts, adGroupCount),
adUris);
}
/**
* Sets the number of ads in the specified ad group.
*/
public void setAdCount(int adGroupIndex, int adCount) {
adCounts[adGroupIndex] = adCount;
}
/**
* Adds an ad to the specified ad group.
*/
public void addAdUri(int adGroupIndex, Uri uri) {
int adIndexInAdGroup = adUris[adGroupIndex].length;
adUris[adGroupIndex] = Arrays.copyOf(adUris[adGroupIndex], adIndexInAdGroup + 1);
adUris[adGroupIndex][adIndexInAdGroup] = uri;
adsLoadedCounts[adGroupIndex]++;
}
/**
* Marks the last ad in the specified ad group as played.
*/
public void playedAd(int adGroupIndex) {
adsPlayedCounts[adGroupIndex]++;
}
}

View File

@ -66,35 +66,11 @@ import java.util.List;
public interface EventListener {
/**
* Called when the times of ad groups are known.
* Called when the ad playback state has been updated.
*
* @param adGroupTimesUs The times of ad groups, in microseconds.
* @param adPlaybackState The new ad playback state.
*/
void onAdGroupTimesUsLoaded(long[] adGroupTimesUs);
/**
* Called when an ad group has been played to the end.
*
* @param adGroupIndex The index of the ad group.
*/
void onAdGroupPlayedToEnd(int adGroupIndex);
/**
* Called when the URI for the media of an ad has been loaded.
*
* @param adGroupIndex The index of the ad group containing the ad with the media URI.
* @param adIndexInAdGroup The index of the ad in its ad group.
* @param uri The URI for the ad's media.
*/
void onAdUriLoaded(int adGroupIndex, int adIndexInAdGroup, Uri uri);
/**
* Called when an ad group has loaded.
*
* @param adGroupIndex The index of the ad group containing the ad.
* @param adCountInAdGroup The number of ads in the ad group.
*/
void onAdGroupLoaded(int adGroupIndex, int adCountInAdGroup);
void onAdPlaybackState(AdPlaybackState adPlaybackState);
/**
* Called when there was an error loading ads.
@ -126,10 +102,9 @@ import java.util.List;
private final AdsLoader adsLoader;
private AdsManager adsManager;
private long[] adGroupTimesUs;
private int[] adsLoadedInAdGroup;
private Timeline timeline;
private long contentDurationMs;
private AdPlaybackState adPlaybackState;
private boolean released;
@ -263,9 +238,9 @@ import java.util.List;
Log.d(TAG, "Initialized without preloading");
}
}
adGroupTimesUs = getAdGroupTimesUs(adsManager.getAdCuePoints());
adsLoadedInAdGroup = new int[adGroupTimesUs.length];
eventListener.onAdGroupTimesUsLoaded(adGroupTimesUs);
long[] adGroupTimesUs = getAdGroupTimesUs(adsManager.getAdCuePoints());
adPlaybackState = new AdPlaybackState(adGroupTimesUs);
updateAdPlaybackState();
}
// AdEvent.AdEventListener implementation.
@ -285,7 +260,7 @@ import java.util.List;
// The ad position is not always accurate when using preloading. See [Internal: b/62613240].
AdPodInfo adPodInfo = ad.getAdPodInfo();
int podIndex = adPodInfo.getPodIndex();
adGroupIndex = podIndex == -1 ? adGroupTimesUs.length - 1 : podIndex;
adGroupIndex = podIndex == -1 ? adPlaybackState.adGroupCount - 1 : podIndex;
int adPosition = adPodInfo.getAdPosition();
int adCountInAdGroup = adPodInfo.getTotalAds();
adsManager.start();
@ -293,7 +268,8 @@ import java.util.List;
Log.d(TAG, "Loaded ad " + adPosition + " of " + adCountInAdGroup + " in ad group "
+ adGroupIndex);
}
eventListener.onAdGroupLoaded(adGroupIndex, adCountInAdGroup);
adPlaybackState.setAdCount(adGroupIndex, adCountInAdGroup);
updateAdPlaybackState();
break;
case CONTENT_PAUSE_REQUESTED:
// After CONTENT_PAUSE_REQUESTED, IMA will playAd/pauseAd/stopAd to show one or more ads
@ -332,7 +308,7 @@ import java.util.List;
return new VideoProgressUpdate(pendingContentPositionMs, contentDurationMs);
}
if (fakeContentProgressElapsedRealtimeMs != C.TIME_UNSET) {
long adGroupTimeMs = C.usToMs(adGroupTimesUs[adGroupIndex]);
long adGroupTimeMs = C.usToMs(adPlaybackState.adGroupTimesUs[adGroupIndex]);
if (adGroupTimeMs == C.TIME_END_OF_SOURCE) {
adGroupTimeMs = contentDurationMs;
}
@ -357,11 +333,11 @@ import java.util.List;
@Override
public void loadAd(String adUriString) {
int adIndexInAdGroup = adsLoadedInAdGroup[adGroupIndex]++;
if (DEBUG) {
Log.d(TAG, "loadAd at index " + adIndexInAdGroup + " in ad group " + adGroupIndex);
Log.d(TAG, "loadAd in ad group " + adGroupIndex);
}
eventListener.onAdUriLoaded(adGroupIndex, adIndexInAdGroup, Uri.parse(adUriString));
adPlaybackState.addAdUri(adGroupIndex, Uri.parse(adUriString));
updateAdPlaybackState();
}
@Override
@ -518,7 +494,7 @@ import java.util.List;
// IMA hasn't sent CONTENT_PAUSE_REQUESTED yet, so fake the content position.
Assertions.checkState(fakeContentProgressElapsedRealtimeMs == C.TIME_UNSET);
fakeContentProgressElapsedRealtimeMs = SystemClock.elapsedRealtime();
if (adGroupIndex == adGroupTimesUs.length - 1) {
if (adGroupIndex == adPlaybackState.adGroupCount - 1) {
adsLoader.contentComplete();
if (DEBUG) {
Log.d(TAG, "adsLoader.contentComplete");
@ -569,8 +545,9 @@ import java.util.List;
private void stopAdInternal() {
Assertions.checkState(playingAd);
player.setPlayWhenReady(false);
adPlaybackState.playedAd(adGroupIndex);
updateAdPlaybackState();
if (!player.isPlayingAd()) {
eventListener.onAdGroupPlayedToEnd(adGroupIndex);
adGroupIndex = C.INDEX_UNSET;
}
clearFlags();
@ -595,6 +572,10 @@ import java.util.List;
}
}
private void updateAdPlaybackState() {
eventListener.onAdPlaybackState(adPlaybackState.copy());
}
private static long[] getAdGroupTimesUs(List<Float> cuePoints) {
if (cuePoints.isEmpty()) {
// If no cue points are specified, there is a preroll ad.

View File

@ -49,7 +49,7 @@ public final class ImaAdsMediaSource implements MediaSource {
private final ViewGroup adUiViewGroup;
private final ImaSdkSettings imaSdkSettings;
private final Handler mainHandler;
private final AdListener adLoaderListener;
private final AdsLoaderListener adsLoaderListener;
private final Map<MediaPeriod, MediaSource> adMediaSourceByMediaPeriod;
private final Timeline.Period period;
@ -60,11 +60,8 @@ public final class ImaAdsMediaSource implements MediaSource {
// Accessed on the player thread.
private Timeline contentTimeline;
private Object contentManifest;
private long[] adGroupTimesUs;
private boolean[] hasPlayedAdGroup;
private int[] adCounts;
private AdPlaybackState adPlaybackState;
private MediaSource[][] adGroupMediaSources;
private boolean[][] isAdAvailable;
private long[][] adDurationsUs;
private MediaSource.Listener listener;
private IOException adLoadError;
@ -113,11 +110,10 @@ public final class ImaAdsMediaSource implements MediaSource {
this.adUiViewGroup = adUiViewGroup;
this.imaSdkSettings = imaSdkSettings;
mainHandler = new Handler(Looper.getMainLooper());
adLoaderListener = new AdListener();
adsLoaderListener = new AdsLoaderListener();
adMediaSourceByMediaPeriod = new HashMap<>();
period = new Timeline.Period();
adGroupMediaSources = new MediaSource[0][];
isAdAvailable = new boolean[0][];
adDurationsUs = new long[0][];
}
@ -131,7 +127,7 @@ public final class ImaAdsMediaSource implements MediaSource {
@Override
public void run() {
imaAdsLoader = new ImaAdsLoader(context, adTagUri, adUiViewGroup, imaSdkSettings,
ImaAdsMediaSource.this.player, adLoaderListener);
ImaAdsMediaSource.this.player, adsLoaderListener);
}
});
contentMediaSource.prepareSource(player, false, new Listener() {
@ -158,7 +154,29 @@ public final class ImaAdsMediaSource implements MediaSource {
@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
if (id.isAd()) {
MediaSource mediaSource = adGroupMediaSources[id.adGroupIndex][id.adIndexInAdGroup];
final int adGroupIndex = id.adGroupIndex;
final int adIndexInAdGroup = id.adIndexInAdGroup;
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
MediaSource adMediaSource = new ExtractorMediaSource(
adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup], dataSourceFactory,
new DefaultExtractorsFactory(), mainHandler, adsLoaderListener);
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
if (adIndexInAdGroup >= oldAdCount) {
int adCount = adIndexInAdGroup + 1;
adGroupMediaSources[adGroupIndex] =
Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount);
adDurationsUs[adGroupIndex] = Arrays.copyOf(adDurationsUs[adGroupIndex], adCount);
Arrays.fill(adDurationsUs[adGroupIndex], oldAdCount, adCount, C.TIME_UNSET);
}
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
adMediaSource.prepareSource(player, false, new Listener() {
@Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
onAdSourceInfoRefreshed(adGroupIndex, adIndexInAdGroup, timeline);
}
});
}
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
MediaPeriod mediaPeriod = mediaSource.createPeriod(new MediaPeriodId(0), allocator);
adMediaSourceByMediaPeriod.put(mediaPeriod, mediaSource);
return mediaPeriod;
@ -200,19 +218,14 @@ public final class ImaAdsMediaSource implements MediaSource {
// Internal methods.
private void onAdGroupTimesUsLoaded(long[] adGroupTimesUs) {
Assertions.checkState(this.adGroupTimesUs == null);
int adGroupCount = adGroupTimesUs.length;
this.adGroupTimesUs = adGroupTimesUs;
hasPlayedAdGroup = new boolean[adGroupCount];
adCounts = new int[adGroupCount];
Arrays.fill(adCounts, C.LENGTH_UNSET);
adGroupMediaSources = new MediaSource[adGroupCount][];
Arrays.fill(adGroupMediaSources, new MediaSource[0]);
isAdAvailable = new boolean[adGroupCount][];
Arrays.fill(isAdAvailable, new boolean[0]);
adDurationsUs = new long[adGroupCount][];
Arrays.fill(adDurationsUs, new long[0]);
private void onAdPlaybackState(AdPlaybackState adPlaybackState) {
if (this.adPlaybackState == null) {
adGroupMediaSources = new MediaSource[adPlaybackState.adGroupCount][];
Arrays.fill(adGroupMediaSources, new MediaSource[0]);
adDurationsUs = new long[adPlaybackState.adGroupCount][];
Arrays.fill(adDurationsUs, new long[0]);
}
this.adPlaybackState = adPlaybackState;
maybeUpdateSourceInfo();
}
@ -222,49 +235,17 @@ public final class ImaAdsMediaSource implements MediaSource {
maybeUpdateSourceInfo();
}
private void onAdGroupPlayedToEnd(int adGroupIndex) {
hasPlayedAdGroup[adGroupIndex] = true;
maybeUpdateSourceInfo();
}
private void onAdUriLoaded(final int adGroupIndex, final int adIndexInAdGroup, Uri uri) {
MediaSource adMediaSource = new ExtractorMediaSource(uri, dataSourceFactory,
new DefaultExtractorsFactory(), mainHandler, adLoaderListener);
int oldAdCount = adGroupMediaSources[adGroupIndex].length;
if (adIndexInAdGroup >= oldAdCount) {
int adCount = adIndexInAdGroup + 1;
adGroupMediaSources[adGroupIndex] = Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount);
isAdAvailable[adGroupIndex] = Arrays.copyOf(isAdAvailable[adGroupIndex], adCount);
adDurationsUs[adGroupIndex] = Arrays.copyOf(adDurationsUs[adGroupIndex], adCount);
Arrays.fill(adDurationsUs[adGroupIndex], oldAdCount, adCount, C.TIME_UNSET);
}
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
isAdAvailable[adGroupIndex][adIndexInAdGroup] = true;
adMediaSource.prepareSource(player, false, new Listener() {
@Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
onAdSourceInfoRefreshed(adGroupIndex, adIndexInAdGroup, timeline);
}
});
}
private void onAdSourceInfoRefreshed(int adGroupIndex, int adIndexInAdGroup, Timeline timeline) {
Assertions.checkArgument(timeline.getPeriodCount() == 1);
adDurationsUs[adGroupIndex][adIndexInAdGroup] = timeline.getPeriod(0, period).getDurationUs();
maybeUpdateSourceInfo();
}
private void onAdGroupLoaded(int adGroupIndex, int adCountInAdGroup) {
if (adCounts[adGroupIndex] == C.LENGTH_UNSET) {
adCounts[adGroupIndex] = adCountInAdGroup;
maybeUpdateSourceInfo();
}
}
private void maybeUpdateSourceInfo() {
if (adGroupTimesUs != null && contentTimeline != null) {
SinglePeriodAdTimeline timeline = new SinglePeriodAdTimeline(contentTimeline, adGroupTimesUs,
hasPlayedAdGroup, adCounts, isAdAvailable, adDurationsUs);
if (adPlaybackState != null && contentTimeline != null) {
SinglePeriodAdTimeline timeline = new SinglePeriodAdTimeline(contentTimeline,
adPlaybackState.adGroupTimesUs, adPlaybackState.adCounts, adPlaybackState.adsLoadedCounts,
adPlaybackState.adsPlayedCounts, adDurationsUs);
listener.onSourceInfoRefreshed(timeline, contentManifest);
}
}
@ -272,11 +253,11 @@ public final class ImaAdsMediaSource implements MediaSource {
/**
* Listener for ad loading events. All methods are called on the main thread.
*/
private final class AdListener implements ImaAdsLoader.EventListener,
private final class AdsLoaderListener implements ImaAdsLoader.EventListener,
ExtractorMediaSource.EventListener {
@Override
public void onAdGroupTimesUsLoaded(final long[] adGroupTimesUs) {
public void onAdPlaybackState(final AdPlaybackState adPlaybackState) {
if (released) {
return;
}
@ -286,55 +267,7 @@ public final class ImaAdsMediaSource implements MediaSource {
if (released) {
return;
}
ImaAdsMediaSource.this.onAdGroupTimesUsLoaded(adGroupTimesUs);
}
});
}
@Override
public void onAdGroupPlayedToEnd(final int adGroupIndex) {
if (released) {
return;
}
playerHandler.post(new Runnable() {
@Override
public void run() {
if (released) {
return;
}
ImaAdsMediaSource.this.onAdGroupPlayedToEnd(adGroupIndex);
}
});
}
@Override
public void onAdUriLoaded(final int adGroupIndex, final int adIndexInAdGroup, final Uri uri) {
if (released) {
return;
}
playerHandler.post(new Runnable() {
@Override
public void run() {
if (released) {
return;
}
ImaAdsMediaSource.this.onAdUriLoaded(adGroupIndex, adIndexInAdGroup, uri);
}
});
}
@Override
public void onAdGroupLoaded(final int adGroupIndex, final int adCountInAdGroup) {
if (released) {
return;
}
playerHandler.post(new Runnable() {
@Override
public void run() {
if (released) {
return;
}
ImaAdsMediaSource.this.onAdGroupLoaded(adGroupIndex, adCountInAdGroup);
ImaAdsMediaSource.this.onAdPlaybackState(adPlaybackState);
}
});
}

View File

@ -26,9 +26,9 @@ public final class SinglePeriodAdTimeline extends Timeline {
private final Timeline contentTimeline;
private final long[] adGroupTimesUs;
private final boolean[] hasPlayedAdGroup;
private final int[] adCounts;
private final boolean[][] isAdAvailable;
private final int[] adsLoadedCounts;
private final int[] adsPlayedCounts;
private final long[][] adDurationsUs;
/**
@ -39,23 +39,22 @@ public final class SinglePeriodAdTimeline extends Timeline {
* @param adGroupTimesUs The times of ad groups relative to the start of the period, in
* microseconds. A final element with the value {@link C#TIME_END_OF_SOURCE} indicates that
* the period has a postroll ad.
* @param hasPlayedAdGroup Whether each ad group has been played.
* @param adCounts The number of ads in each ad group. An element may be {@link C#LENGTH_UNSET}
* if the number of ads is not yet known.
* @param isAdAvailable Whether each ad in each ad group is available.
* @param adsLoadedCounts The number of ads loaded so far in each ad group.
* @param adsPlayedCounts The number of ads played so far in each ad group.
* @param adDurationsUs The duration of each ad in each ad group, in microseconds. An element
* may be {@link C#TIME_UNSET} if the duration is not yet known.
*/
public SinglePeriodAdTimeline(Timeline contentTimeline, long[] adGroupTimesUs,
boolean[] hasPlayedAdGroup, int[] adCounts, boolean[][] isAdAvailable,
long[][] adDurationsUs) {
public SinglePeriodAdTimeline(Timeline contentTimeline, long[] adGroupTimesUs, int[] adCounts,
int[] adsLoadedCounts, int[] adsPlayedCounts, long[][] adDurationsUs) {
Assertions.checkState(contentTimeline.getPeriodCount() == 1);
Assertions.checkState(contentTimeline.getWindowCount() == 1);
this.contentTimeline = contentTimeline;
this.adGroupTimesUs = adGroupTimesUs;
this.hasPlayedAdGroup = hasPlayedAdGroup;
this.adCounts = adCounts;
this.isAdAvailable = isAdAvailable;
this.adsLoadedCounts = adsLoadedCounts;
this.adsPlayedCounts = adsPlayedCounts;
this.adDurationsUs = adDurationsUs;
}
@ -79,8 +78,8 @@ public final class SinglePeriodAdTimeline extends Timeline {
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
contentTimeline.getPeriod(periodIndex, period, setIds);
period.set(period.id, period.uid, period.windowIndex, period.durationUs,
period.getPositionInWindowUs(), adGroupTimesUs, hasPlayedAdGroup, adCounts,
isAdAvailable, adDurationsUs);
period.getPositionInWindowUs(), adGroupTimesUs, adCounts, adsLoadedCounts, adsPlayedCounts,
adDurationsUs);
return period;
}

View File

@ -181,8 +181,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
if (currentPeriodId.isAd()) {
int currentAdGroupIndex = currentPeriodId.adGroupIndex;
timeline.getPeriod(currentPeriodId.periodIndex, period);
int adCountInCurrentAdGroup = period.getAdGroupCount() == C.LENGTH_UNSET ? C.LENGTH_UNSET
: period.getAdCountInAdGroup(currentAdGroupIndex);
int adCountInCurrentAdGroup = period.getAdCountInAdGroup(currentAdGroupIndex);
if (adCountInCurrentAdGroup == C.LENGTH_UNSET) {
return null;
}
@ -206,7 +205,7 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
} else {
// Check if the postroll ad should be played.
int adGroupCount = period.getAdGroupCount();
if (adGroupCount == C.LENGTH_UNSET || adGroupCount == 0
if (adGroupCount == 0
|| period.getAdGroupTimeUs(adGroupCount - 1) != C.TIME_END_OF_SOURCE
|| period.hasPlayedAdGroup(adGroupCount - 1)
|| !period.isAdAvailable(adGroupCount - 1, 0)) {
@ -302,9 +301,6 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
if (adGroupCount == 0) {
return true;
}
if (adGroupCount == C.LENGTH_UNSET) {
return false;
}
int lastAdGroupIndex = adGroupCount - 1;
boolean periodHasPostrollAd = period.getAdGroupTimeUs(lastAdGroupIndex) == C.TIME_END_OF_SOURCE;
if (!id.isAd()) {

View File

@ -266,9 +266,9 @@ public abstract class Timeline {
private long positionInWindowUs;
private long[] adGroupTimesUs;
private boolean[] hasPlayedAdGroup;
private int[] adCounts;
private boolean[][] isAdAvailable;
private int[] adsLoadedCounts;
private int[] adsPlayedCounts;
private long[][] adDurationsUs;
/**
@ -304,26 +304,26 @@ public abstract class Timeline {
* @param adGroupTimesUs The times of ad groups relative to the start of the period, in
* microseconds. A final element with the value {@link C#TIME_END_OF_SOURCE} indicates that
* the period has a postroll ad.
* @param hasPlayedAdGroup Whether each ad group has been played.
* @param adCounts The number of ads in each ad group. An element may be {@link C#LENGTH_UNSET}
* if the number of ads is not yet known.
* @param isAdAvailable Whether each ad in each ad group is available.
* @param adsLoadedCounts The number of ads loaded so far in each ad group.
* @param adsPlayedCounts The number of ads played so far in each ad group.
* @param adDurationsUs The duration of each ad in each ad group, in microseconds. An element
* may be {@link C#TIME_UNSET} if the duration is not yet known.
* @return This period, for convenience.
*/
public Period set(Object id, Object uid, int windowIndex, long durationUs,
long positionInWindowUs, long[] adGroupTimesUs, boolean[] hasPlayedAdGroup, int[] adCounts,
boolean[][] isAdAvailable, long[][] adDurationsUs) {
long positionInWindowUs, long[] adGroupTimesUs, int[] adCounts, int[] adsLoadedCounts,
int[] adsPlayedCounts, long[][] adDurationsUs) {
this.id = id;
this.uid = uid;
this.windowIndex = windowIndex;
this.durationUs = durationUs;
this.positionInWindowUs = positionInWindowUs;
this.adGroupTimesUs = adGroupTimesUs;
this.hasPlayedAdGroup = hasPlayedAdGroup;
this.adCounts = adCounts;
this.isAdAvailable = isAdAvailable;
this.adsLoadedCounts = adsLoadedCounts;
this.adsPlayedCounts = adsPlayedCounts;
this.adDurationsUs = adDurationsUs;
return this;
}
@ -375,9 +375,6 @@ public abstract class Timeline {
* @return The time of the ad group at the index, in microseconds.
*/
public long getAdGroupTimeUs(int adGroupIndex) {
if (adGroupTimesUs == null) {
throw new IndexOutOfBoundsException();
}
return adGroupTimesUs[adGroupIndex];
}
@ -388,10 +385,8 @@ public abstract class Timeline {
* @return Whether the ad group at index {@code adGroupIndex} has been played.
*/
public boolean hasPlayedAdGroup(int adGroupIndex) {
if (hasPlayedAdGroup == null) {
throw new IndexOutOfBoundsException();
}
return hasPlayedAdGroup[adGroupIndex];
return adCounts[adGroupIndex] != C.INDEX_UNSET
&& adsPlayedCounts[adGroupIndex] == adCounts[adGroupIndex];
}
/**
@ -445,9 +440,6 @@ public abstract class Timeline {
* @return The number of ads in the ad group, or {@link C#LENGTH_UNSET} if not yet known.
*/
public int getAdCountInAdGroup(int adGroupIndex) {
if (adCounts == null) {
throw new IndexOutOfBoundsException();
}
return adCounts[adGroupIndex];
}
@ -459,9 +451,7 @@ public abstract class Timeline {
* @return Whether the URL for the specified ad is known.
*/
public boolean isAdAvailable(int adGroupIndex, int adIndexInAdGroup) {
return isAdAvailable != null && adGroupIndex < isAdAvailable.length
&& adIndexInAdGroup < isAdAvailable[adGroupIndex].length
&& isAdAvailable[adGroupIndex][adIndexInAdGroup];
return adIndexInAdGroup < adsLoadedCounts[adGroupIndex];
}
/**
@ -473,9 +463,6 @@ public abstract class Timeline {
* @return The duration of the ad, or {@link C#TIME_UNSET} if not yet known.
*/
public long getAdDurationUs(int adGroupIndex, int adIndexInAdGroup) {
if (adDurationsUs == null) {
throw new IndexOutOfBoundsException();
}
if (adIndexInAdGroup >= adDurationsUs[adGroupIndex].length) {
return C.TIME_UNSET;
}