Commit 66d12271 by andrewlewis Committed by Oliver Woodman

Add support for in-period ads

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=160294524
parent d5c2cf79
...@@ -24,6 +24,7 @@ import android.util.Log; ...@@ -24,6 +24,7 @@ import android.util.Log;
import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo; import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo;
import com.google.android.exoplayer2.ExoPlayerImplInternal.SourceInfo; import com.google.android.exoplayer2.ExoPlayerImplInternal.SourceInfo;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
...@@ -299,8 +300,15 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -299,8 +300,15 @@ import java.util.concurrent.CopyOnWriteArraySet;
if (timeline.isEmpty()) { if (timeline.isEmpty()) {
return C.TIME_UNSET; return C.TIME_UNSET;
} }
if (isPlayingAd()) {
MediaPeriodId periodId = playbackInfo.periodId;
timeline.getPeriod(periodId.periodIndex, period);
long adDurationUs = period.getAdDurationUs(periodId.adGroupIndex, periodId.adIndexInAdGroup);
return C.usToMs(adDurationUs);
} else {
return timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs(); return timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
} }
}
@Override @Override
public long getCurrentPosition() { public long getCurrentPosition() {
...@@ -463,4 +471,10 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -463,4 +471,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
} }
// TODO: Add to the public ExoPlayer interface.
private boolean isPlayingAd() {
return pendingSeekAcks == 0 && playbackInfo.periodId.adGroupIndex != C.INDEX_UNSET;
}
} }
...@@ -235,7 +235,7 @@ public abstract class Timeline { ...@@ -235,7 +235,7 @@ public abstract class Timeline {
/** /**
* Holds information about a period in a {@link Timeline}. A period defines a single logical piece * Holds information about a period in a {@link Timeline}. A period defines a single logical piece
* of media, for example a a media file. See {@link Timeline} for more details. The figure below * of media, for example a media file. See {@link Timeline} for more details. The figure below
* shows some of the information defined by a period, as well as how this information relates to a * shows some of the information defined by a period, as well as how this information relates to a
* corresponding {@link Window} in the timeline. * corresponding {@link Window} in the timeline.
* <p align="center"> * <p align="center">
...@@ -264,24 +264,77 @@ public abstract class Timeline { ...@@ -264,24 +264,77 @@ public abstract class Timeline {
*/ */
public long durationUs; public long durationUs;
// TODO: Remove this flag now that in-period ads are supported.
/** /**
* Whether this period contains an ad. * Whether this period contains an ad.
*/ */
public boolean isAd; public boolean isAd;
private long positionInWindowUs; private long positionInWindowUs;
private long[] adGroupTimesUs;
private boolean[] hasPlayedAdGroup;
private int[] adCounts;
private boolean[][] isAdAvailable;
private long[][] adDurationsUs;
/** /**
* Sets the data held by this period. * Sets the data held by this period.
*
* @param id An identifier for the period. Not necessarily unique.
* @param uid A unique identifier for the period.
* @param windowIndex The index of the window to which this period belongs.
* @param durationUs The duration of this period in microseconds, or {@link C#TIME_UNSET} if
* unknown.
* @param positionInWindowUs The position of the start of this period relative to the start of
* the window to which it belongs, in milliseconds. May be negative if the start of the
* period is not within the window.
* @param isAd Whether this period is an ad.
* @return This period, for convenience.
*/ */
public Period set(Object id, Object uid, int windowIndex, long durationUs, public Period set(Object id, Object uid, int windowIndex, long durationUs,
long positionInWindowUs, boolean isAd) { long positionInWindowUs, boolean isAd) {
return set(id, uid, windowIndex, durationUs, positionInWindowUs, isAd, null, null, null, null,
null);
}
/**
* Sets the data held by this period.
*
* @param id An identifier for the period. Not necessarily unique.
* @param uid A unique identifier for the period.
* @param windowIndex The index of the window to which this period belongs.
* @param durationUs The duration of this period in microseconds, or {@link C#TIME_UNSET} if
* unknown.
* @param positionInWindowUs The position of the start of this period relative to the start of
* the window to which it belongs, in milliseconds. May be negative if the start of the
* period is not within the window.
* @param isAd Whether this period is an ad.
* @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 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, boolean isAd, long[] adGroupTimesUs, boolean[] hasPlayedAdGroup,
int[] adCounts, boolean[][] isAdAvailable, long[][] adDurationsUs) {
this.id = id; this.id = id;
this.uid = uid; this.uid = uid;
this.windowIndex = windowIndex; this.windowIndex = windowIndex;
this.durationUs = durationUs; this.durationUs = durationUs;
this.positionInWindowUs = positionInWindowUs; this.positionInWindowUs = positionInWindowUs;
this.isAd = isAd; this.isAd = isAd;
this.adGroupTimesUs = adGroupTimesUs;
this.hasPlayedAdGroup = hasPlayedAdGroup;
this.adCounts = adCounts;
this.isAdAvailable = isAdAvailable;
this.adDurationsUs = adDurationsUs;
return this; return this;
} }
...@@ -317,6 +370,128 @@ public abstract class Timeline { ...@@ -317,6 +370,128 @@ public abstract class Timeline {
return positionInWindowUs; return positionInWindowUs;
} }
/**
* Returns the number of ad groups in the period.
*/
public int getAdGroupCount() {
return adGroupTimesUs == null ? 0 : adGroupTimesUs.length;
}
/**
* Returns the time of the ad group at index {@code adGroupIndex} in the period, in
* microseconds.
*
* @param adGroupIndex The ad group index.
* @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];
}
/**
* Returns whether the ad group at index {@code adGroupIndex} has been played.
*
* @param adGroupIndex The ad group index.
* @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];
}
/**
* Returns the index of the ad group at or before {@code positionUs}, if that ad group is
* unplayed. Returns {@link C#INDEX_UNSET} if the ad group before {@code positionUs} has been
* played, or if there is no such ad group.
*
* @param positionUs The position at or before which to find an ad group, in microseconds.
* @return The index of the ad group, or {@link C#INDEX_UNSET}.
*/
public int getAdGroupIndexForPositionUs(long positionUs) {
if (adGroupTimesUs == null) {
return C.INDEX_UNSET;
}
// Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE.
// In practice we expect there to be few ad groups so the search shouldn't be expensive.
int index = adGroupTimesUs.length - 1;
while (index >= 0 && (adGroupTimesUs[index] == C.TIME_END_OF_SOURCE
|| adGroupTimesUs[index] > positionUs)) {
index--;
}
return index >= 0 && !hasPlayedAdGroup(index) ? index : C.INDEX_UNSET;
}
/**
* Returns the index of the next unplayed ad group after {@code positionUs}. Returns
* {@link C#INDEX_UNSET} if there is no such ad group.
*
* @param positionUs The position after which to find an ad group, in microseconds.
* @return The index of the ad group, or {@link C#INDEX_UNSET}.
*/
public int getAdGroupIndexAfterPositionUs(long positionUs) {
if (adGroupTimesUs == null) {
return C.INDEX_UNSET;
}
// Use a linear search as the array elements may not be increasing due to TIME_END_OF_SOURCE.
// In practice we expect there to be few ad groups so the search shouldn't be expensive.
int index = 0;
while (index < adGroupTimesUs.length && adGroupTimesUs[index] != C.TIME_END_OF_SOURCE
&& (positionUs >= adGroupTimesUs[index] || hasPlayedAdGroup(index))) {
index++;
}
return index < adGroupTimesUs.length ? index : C.INDEX_UNSET;
}
/**
* Returns the number of ads in the ad group at index {@code adGroupIndex}, or
* {@link C#LENGTH_UNSET} if not yet known.
*
* @param adGroupIndex The ad group index.
* @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];
}
/**
* Returns whether the URL for the specified ad is known.
*
* @param adGroupIndex The ad group index.
* @param adIndexInAdGroup The ad index in the ad group.
* @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];
}
/**
* Returns the duration of the ad at index {@code adIndexInAdGroup} in the ad group at
* {@code adGroupIndex}, in microseconds, or {@link C#TIME_UNSET} if not yet known.
*
* @param adGroupIndex The ad group index.
* @param adIndexInAdGroup The ad index in the ad group.
* @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;
}
return adDurationsUs[adGroupIndex][adIndexInAdGroup];
}
} }
/** /**
......
...@@ -48,21 +48,27 @@ public interface MediaSource { ...@@ -48,21 +48,27 @@ public interface MediaSource {
final class MediaPeriodId { final class MediaPeriodId {
/** /**
* Value for unset media period identifiers.
*/
public static final MediaPeriodId UNSET =
new MediaPeriodId(C.INDEX_UNSET, C.INDEX_UNSET, C.INDEX_UNSET);
/**
* The timeline period index. * The timeline period index.
*/ */
public final int periodIndex; public final int periodIndex;
/** /**
* If the media period is in an ad break, the index of the ad break in the period. * If the media period is in an ad group, the index of the ad group in the period.
* {@link C#INDEX_UNSET} otherwise. * {@link C#INDEX_UNSET} otherwise.
*/ */
public final int adBreakIndex; public final int adGroupIndex;
/** /**
* If the media period is in an ad break, the index of the ad in its ad break in the period. * If the media period is in an ad group, the index of the ad in its ad group in the period.
* {@link C#INDEX_UNSET} otherwise. * {@link C#INDEX_UNSET} otherwise.
*/ */
public final int adIndexInAdBreak; public final int adIndexInAdGroup;
/** /**
* Creates a media period identifier for the specified period in the timeline. * Creates a media period identifier for the specified period in the timeline.
...@@ -70,10 +76,59 @@ public interface MediaSource { ...@@ -70,10 +76,59 @@ public interface MediaSource {
* @param periodIndex The timeline period index. * @param periodIndex The timeline period index.
*/ */
public MediaPeriodId(int periodIndex) { public MediaPeriodId(int periodIndex) {
// TODO: Allow creation of MediaPeriodIds for ad breaks. this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET);
}
/**
* Creates a media period identifier that identifies an ad within an ad group at the specified
* timeline period.
*
* @param periodIndex The index of the timeline period that contains the ad group.
* @param adGroupIndex The index of the ad group.
* @param adIndexInAdGroup The index of the ad in the ad group.
*/
public MediaPeriodId(int periodIndex, int adGroupIndex, int adIndexInAdGroup) {
this.periodIndex = periodIndex; this.periodIndex = periodIndex;
adBreakIndex = C.INDEX_UNSET; this.adGroupIndex = adGroupIndex;
adIndexInAdBreak = C.INDEX_UNSET; this.adIndexInAdGroup = adIndexInAdGroup;
}
/**
* Returns a copy of this period identifier but with {@code newPeriodIndex} as its period index.
*/
public MediaPeriodId copyWithPeriodIndex(int newPeriodIndex) {
return periodIndex == newPeriodIndex ? this
: new MediaPeriodId(newPeriodIndex, adGroupIndex, adIndexInAdGroup);
}
/**
* Returns whether this period identifier identifies an ad in an ad group in a period.
*/
public boolean isAd() {
return adGroupIndex != C.INDEX_UNSET;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
MediaPeriodId periodId = (MediaPeriodId) obj;
return periodIndex == periodId.periodIndex && adGroupIndex == periodId.adGroupIndex
&& adIndexInAdGroup == periodId.adIndexInAdGroup;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + periodIndex;
result = 31 * result + adGroupIndex;
result = 31 * result + adIndexInAdGroup;
return result;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment