mirror of
https://github.com/samsonjs/media.git
synced 2026-03-28 09:55:48 +00:00
Expose ad playback information on ExoPlayer
Also update the time bar to show ad markers using in-period ads and remove support for periods being marked as ads. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=160382805
This commit is contained in:
parent
66d122710e
commit
96fa660284
7 changed files with 115 additions and 72 deletions
|
|
@ -327,7 +327,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
|||
mediaDataSourceFactory, this, adTagUri, adOverlayViewGroup);
|
||||
// The demo app has a non-null overlay frame layout.
|
||||
simpleExoPlayerView.getOverlayFrameLayout().addView(adOverlayViewGroup);
|
||||
// Show a multi-window time bar, which will include ad break position markers.
|
||||
// Show a multi-window time bar, which will include ad position markers.
|
||||
simpleExoPlayerView.setShowMultiWindowTimeBar(true);
|
||||
} catch (Exception e) {
|
||||
// Throw if the media source class was not found, or there was an error instantiating it.
|
||||
|
|
|
|||
|
|
@ -546,4 +546,21 @@ public interface ExoPlayer {
|
|||
*/
|
||||
boolean isCurrentWindowSeekable();
|
||||
|
||||
/**
|
||||
* Returns whether the player is currently playing an ad.
|
||||
*/
|
||||
boolean isPlayingAd();
|
||||
|
||||
/**
|
||||
* If {@link #isPlayingAd()} returns true, returns the index of the ad group in the period
|
||||
* currently being played. Returns {@link C#INDEX_UNSET} otherwise.
|
||||
*/
|
||||
int getCurrentAdGroupIndex();
|
||||
|
||||
/**
|
||||
* If {@link #isPlayingAd()} returns true, returns the index of the ad in its ad group. Returns
|
||||
* {@link C#INDEX_UNSET} otherwise.
|
||||
*/
|
||||
int getCurrentAdIndexInAdGroup();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -352,6 +352,21 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isSeekable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayingAd() {
|
||||
return pendingSeekAcks == 0 && playbackInfo.periodId.adGroupIndex != C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentAdGroupIndex() {
|
||||
return pendingSeekAcks == 0 ? playbackInfo.periodId.adGroupIndex : C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentAdIndexInAdGroup() {
|
||||
return pendingSeekAcks == 0 ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRendererCount() {
|
||||
return renderers.length;
|
||||
|
|
@ -471,10 +486,4 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Add to the public ExoPlayer interface.
|
||||
|
||||
private boolean isPlayingAd() {
|
||||
return pendingSeekAcks == 0 && playbackInfo.periodId.adGroupIndex != C.INDEX_UNSET;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -662,6 +662,21 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
return player.isCurrentWindowSeekable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayingAd() {
|
||||
return player.isPlayingAd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentAdGroupIndex() {
|
||||
return player.getCurrentAdGroupIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentAdIndexInAdGroup() {
|
||||
return player.getCurrentAdIndexInAdGroup();
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void removeSurfaceCallbacks() {
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||
private long duration;
|
||||
private long position;
|
||||
private long bufferedPosition;
|
||||
private int adBreakCount;
|
||||
private long[] adBreakTimesMs;
|
||||
private int adGroupCount;
|
||||
private long[] adGroupTimesMs;
|
||||
|
||||
/**
|
||||
* Creates a new time bar.
|
||||
|
|
@ -241,10 +241,10 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setAdBreakTimesMs(@Nullable long[] adBreakTimesMs, int adBreakCount) {
|
||||
Assertions.checkArgument(adBreakCount == 0 || adBreakTimesMs != null);
|
||||
this.adBreakCount = adBreakCount;
|
||||
this.adBreakTimesMs = adBreakTimesMs;
|
||||
public void setAdGroupTimesMs(@Nullable long[] adGroupTimesMs, int adGroupCount) {
|
||||
Assertions.checkArgument(adGroupCount == 0 || adGroupTimesMs != null);
|
||||
this.adGroupCount = adGroupCount;
|
||||
this.adGroupTimesMs = adGroupTimesMs;
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -529,10 +529,10 @@ public class DefaultTimeBar extends View implements TimeBar {
|
|||
canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, playedPaint);
|
||||
}
|
||||
int adMarkerOffset = adMarkerWidth / 2;
|
||||
for (int i = 0; i < adBreakCount; i++) {
|
||||
long adBreakTimeMs = Util.constrainValue(adBreakTimesMs[i], 0, duration);
|
||||
for (int i = 0; i < adGroupCount; i++) {
|
||||
long adGroupTimeMs = Util.constrainValue(adGroupTimesMs[i], 0, duration);
|
||||
int markerPositionOffset =
|
||||
(int) (progressBar.width() * adBreakTimeMs / duration) - adMarkerOffset;
|
||||
(int) (progressBar.width() * adGroupTimeMs / duration) - adMarkerOffset;
|
||||
int markerLeft = progressBar.left + Math.min(progressBar.width() - adMarkerWidth,
|
||||
Math.max(0, markerPositionOffset));
|
||||
canvas.drawRect(markerLeft, barTop, markerLeft + adMarkerWidth, barBottom, adMarkerPaint);
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
private int showTimeoutMs;
|
||||
private @RepeatToggleModes int repeatToggleModes;
|
||||
private long hideAtMs;
|
||||
private long[] adBreakTimesMs;
|
||||
private long[] adGroupTimesMs;
|
||||
|
||||
private final Runnable updateProgressAction = new Runnable() {
|
||||
@Override
|
||||
|
|
@ -363,7 +363,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
window = new Timeline.Window();
|
||||
formatBuilder = new StringBuilder();
|
||||
formatter = new Formatter(formatBuilder, Locale.getDefault());
|
||||
adBreakTimesMs = new long[0];
|
||||
adGroupTimesMs = new long[0];
|
||||
componentListener = new ComponentListener();
|
||||
controlDispatcher = DEFAULT_CONTROL_DISPATCHER;
|
||||
|
||||
|
|
@ -649,7 +649,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
enablePrevious = !timeline.isFirstWindow(windowIndex, player.getRepeatMode())
|
||||
|| isSeekable || !window.isDynamic;
|
||||
enableNext = !timeline.isLastWindow(windowIndex, player.getRepeatMode()) || window.isDynamic;
|
||||
if (timeline.getPeriod(player.getCurrentPeriodIndex(), period).isAd) {
|
||||
if (player.isPlayingAd()) {
|
||||
// Always hide player controls during ads.
|
||||
hide();
|
||||
}
|
||||
|
|
@ -712,47 +712,52 @@ public class PlaybackControlView extends FrameLayout {
|
|||
long positionUs = 0;
|
||||
long bufferedPositionUs = 0;
|
||||
long durationUs = 0;
|
||||
boolean isInAdBreak = false;
|
||||
boolean isPlayingAd = false;
|
||||
int adBreakCount = 0;
|
||||
int adGroupTimesMsCount = 0;
|
||||
for (int i = 0; i < windowCount; i++) {
|
||||
timeline.getWindow(i, window);
|
||||
for (int j = window.firstPeriodIndex; j <= window.lastPeriodIndex; j++) {
|
||||
if (timeline.getPeriod(j, period).isAd) {
|
||||
isPlayingAd |= j == periodIndex;
|
||||
if (!isInAdBreak) {
|
||||
isInAdBreak = true;
|
||||
if (adBreakCount == adBreakTimesMs.length) {
|
||||
adBreakTimesMs = Arrays.copyOf(adBreakTimesMs,
|
||||
adBreakTimesMs.length == 0 ? 1 : adBreakTimesMs.length * 2);
|
||||
}
|
||||
adBreakTimesMs[adBreakCount++] = C.usToMs(durationUs);
|
||||
}
|
||||
} else {
|
||||
isInAdBreak = false;
|
||||
long periodDurationUs = period.getDurationUs();
|
||||
Assertions.checkState(periodDurationUs != C.TIME_UNSET);
|
||||
long periodDurationInWindowUs = periodDurationUs;
|
||||
if (j == window.firstPeriodIndex) {
|
||||
periodDurationInWindowUs -= window.positionInFirstPeriodUs;
|
||||
}
|
||||
if (i < periodIndex) {
|
||||
positionUs += periodDurationInWindowUs;
|
||||
bufferedPositionUs += periodDurationInWindowUs;
|
||||
}
|
||||
durationUs += periodDurationInWindowUs;
|
||||
long periodDurationUs = timeline.getPeriod(j, period).getDurationUs();
|
||||
Assertions.checkState(periodDurationUs != C.TIME_UNSET);
|
||||
long periodDurationInWindowUs = periodDurationUs;
|
||||
if (j == window.firstPeriodIndex) {
|
||||
periodDurationInWindowUs -= window.positionInFirstPeriodUs;
|
||||
}
|
||||
for (int adGroupIndex = 0; adGroupIndex < period.getAdGroupCount(); adGroupIndex++) {
|
||||
long adGroupTimeUs = period.getAdGroupTimeUs(adGroupIndex);
|
||||
if (period.hasPlayedAdGroup(adGroupIndex)) {
|
||||
// Don't show played ad groups.
|
||||
continue;
|
||||
}
|
||||
if (adGroupTimeUs == C.TIME_END_OF_SOURCE) {
|
||||
adGroupTimeUs = periodDurationUs;
|
||||
}
|
||||
if (j == window.firstPeriodIndex) {
|
||||
adGroupTimeUs -= window.positionInFirstPeriodUs;
|
||||
}
|
||||
if (adGroupTimeUs >= 0 && adGroupTimeUs <= window.durationUs) {
|
||||
if (adGroupTimesMsCount == adGroupTimesMs.length) {
|
||||
adGroupTimesMs = Arrays.copyOf(adGroupTimesMs,
|
||||
adGroupTimesMs.length == 0 ? 1 : adGroupTimesMs.length * 2);
|
||||
}
|
||||
adGroupTimesMs[adGroupTimesMsCount++] = C.usToMs(durationUs + adGroupTimeUs);
|
||||
}
|
||||
}
|
||||
if (i < periodIndex) {
|
||||
positionUs += periodDurationInWindowUs;
|
||||
bufferedPositionUs += periodDurationInWindowUs;
|
||||
}
|
||||
durationUs += periodDurationInWindowUs;
|
||||
}
|
||||
}
|
||||
position = C.usToMs(positionUs);
|
||||
bufferedPosition = C.usToMs(bufferedPositionUs);
|
||||
duration = C.usToMs(durationUs);
|
||||
if (!isPlayingAd) {
|
||||
if (!player.isPlayingAd()) {
|
||||
position += player.getCurrentPosition();
|
||||
bufferedPosition += player.getBufferedPosition();
|
||||
}
|
||||
if (timeBar != null) {
|
||||
timeBar.setAdBreakTimesMs(adBreakTimesMs, adBreakCount);
|
||||
timeBar.setAdGroupTimesMs(adGroupTimesMs, adGroupTimesMsCount);
|
||||
}
|
||||
} else {
|
||||
position = player.getCurrentPosition();
|
||||
|
|
@ -898,7 +903,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
}
|
||||
}
|
||||
|
||||
private void seekToTimebarPosition(long timebarPositionMs) {
|
||||
private void seekToTimeBarPosition(long timebarPositionMs) {
|
||||
if (multiWindowTimeBar) {
|
||||
Timeline timeline = player.getCurrentTimeline();
|
||||
int windowCount = timeline.getWindowCount();
|
||||
|
|
@ -906,27 +911,25 @@ public class PlaybackControlView extends FrameLayout {
|
|||
for (int i = 0; i < windowCount; i++) {
|
||||
timeline.getWindow(i, window);
|
||||
for (int j = window.firstPeriodIndex; j <= window.lastPeriodIndex; j++) {
|
||||
if (!timeline.getPeriod(j, period).isAd) {
|
||||
long periodDurationMs = period.getDurationMs();
|
||||
if (periodDurationMs == C.TIME_UNSET) {
|
||||
// Should never happen as canShowMultiWindowTimeBar is true.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (j == window.firstPeriodIndex) {
|
||||
periodDurationMs -= window.getPositionInFirstPeriodMs();
|
||||
}
|
||||
if (i == windowCount - 1 && j == window.lastPeriodIndex
|
||||
&& remainingMs >= periodDurationMs) {
|
||||
// Seeking past the end of the last window should seek to the end of the timeline.
|
||||
seekTo(i, window.getDurationMs());
|
||||
return;
|
||||
}
|
||||
if (remainingMs < periodDurationMs) {
|
||||
seekTo(i, period.getPositionInWindowMs() + remainingMs);
|
||||
return;
|
||||
}
|
||||
remainingMs -= periodDurationMs;
|
||||
long periodDurationMs = timeline.getPeriod(j, period).getDurationMs();
|
||||
if (periodDurationMs == C.TIME_UNSET) {
|
||||
// Should never happen as canShowMultiWindowTimeBar is true.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (j == window.firstPeriodIndex) {
|
||||
periodDurationMs -= window.getPositionInFirstPeriodMs();
|
||||
}
|
||||
if (i == windowCount - 1 && j == window.lastPeriodIndex
|
||||
&& remainingMs >= periodDurationMs) {
|
||||
// Seeking past the end of the last window should seek to the end of the timeline.
|
||||
seekTo(i, window.getDurationMs());
|
||||
return;
|
||||
}
|
||||
if (remainingMs < periodDurationMs) {
|
||||
seekTo(i, period.getPositionInWindowMs() + remainingMs);
|
||||
return;
|
||||
}
|
||||
remainingMs -= periodDurationMs;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1028,8 +1031,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
}
|
||||
int periodCount = timeline.getPeriodCount();
|
||||
for (int i = 0; i < periodCount; i++) {
|
||||
timeline.getPeriod(i, period);
|
||||
if (!period.isAd && period.durationUs == C.TIME_UNSET) {
|
||||
if (timeline.getPeriod(i, period).durationUs == C.TIME_UNSET) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1056,7 +1058,7 @@ public class PlaybackControlView extends FrameLayout {
|
|||
public void onScrubStop(TimeBar timeBar, long position, boolean canceled) {
|
||||
scrubbing = false;
|
||||
if (!canceled && player != null) {
|
||||
seekToTimebarPosition(position);
|
||||
seekToTimeBarPosition(position);
|
||||
}
|
||||
hideAfterTimeout();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ public interface TimeBar {
|
|||
* ad breaks in milliseconds. May be {@code null} if there are no ad breaks.
|
||||
* @param adBreakCount The number of ad breaks.
|
||||
*/
|
||||
void setAdBreakTimesMs(@Nullable long[] adBreakTimesMs, int adBreakCount);
|
||||
void setAdGroupTimesMs(@Nullable long[] adBreakTimesMs, int adBreakCount);
|
||||
|
||||
/**
|
||||
* Listener for scrubbing events.
|
||||
|
|
|
|||
Loading…
Reference in a new issue