Skip to content

Commit

Permalink
Move ad playback state into ImaAdsLoader
Browse files Browse the repository at this point in the history
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
  • Loading branch information
andrewlewis authored and ojw28 committed Jul 12, 2017
1 parent b31fd5b commit ef56c9f
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 190 deletions.
Original file line number Diff line number Diff line change
@@ -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]++;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,35 +66,11 @@
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.
Expand Down Expand Up @@ -126,10 +102,9 @@ public interface EventListener {
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;

Expand Down Expand Up @@ -263,9 +238,9 @@ public void onAdsManagerLoaded(AdsManagerLoadedEvent adsManagerLoadedEvent) {
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.
Expand All @@ -285,15 +260,16 @@ public void onAdEvent(AdEvent adEvent) {
// 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();
if (DEBUG) {
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
Expand Down Expand Up @@ -332,7 +308,7 @@ public VideoProgressUpdate getContentProgress() {
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;
}
Expand All @@ -357,11 +333,11 @@ public VideoProgressUpdate getAdProgress() {

@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
Expand Down Expand Up @@ -518,7 +494,7 @@ public void onPositionDiscontinuity() {
// 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");
Expand Down Expand Up @@ -569,8 +545,9 @@ private void pauseContentInternal() {
private void stopAdInternal() {
Assertions.checkState(playingAd);
player.setPlayWhenReady(false);
adPlaybackState.playedAd(adGroupIndex);
updateAdPlaybackState();
if (!player.isPlayingAd()) {
eventListener.onAdGroupPlayedToEnd(adGroupIndex);
adGroupIndex = C.INDEX_UNSET;
}
clearFlags();
Expand All @@ -595,6 +572,10 @@ private void checkForContentComplete() {
}
}

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.
Expand Down
Loading

0 comments on commit ef56c9f

Please sign in to comment.