Skip to content

Commit

Permalink
Enforce unit speed for ad playback
Browse files Browse the repository at this point in the history
Ad playback shouldn't be affected by manual speed adjustments set
by the user. This change enforces unit speed for ad playback.

Issue: #9018
PiperOrigin-RevId: 424546258
  • Loading branch information
tonihei authored and andrewlewis committed Jan 28, 2022
1 parent be43ac7 commit 73d9728
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 4 deletions.
4 changes: 3 additions & 1 deletion RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@
When a `DrmSessionManager` is used by an app in a custom `MediaSource`,
the `playbackLooper` needs to be passed to `DrmSessionManager.setPlayer`
instead.
* IMA:
* Ad playback / IMA:
* Add a method to `AdPlaybackState` to allow resetting an ad group so that
it can be played again
([#9615](https://github.com/google/ExoPlayer/issues/9615)).
* Enforce playback speed of 1.0 during ad playback
([#9018](https://github.com/google/ExoPlayer/issues/9018)).
* DASH:
* Support the `forced-subtitle` track role
([#9727](https://github.com/google/ExoPlayer/issues/9727)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1913,9 +1913,12 @@ private void updatePlaybackSpeedSettingsForNewPeriod(
MediaPeriodId oldPeriodId,
long positionForTargetOffsetOverrideUs) {
if (!shouldUseLivePlaybackSpeedControl(newTimeline, newPeriodId)) {
// Live playback speed control is unused for the current period, reset speed if adjusted.
if (!mediaClock.getPlaybackParameters().equals(playbackInfo.playbackParameters)) {
mediaClock.setPlaybackParameters(playbackInfo.playbackParameters);
// Live playback speed control is unused for the current period, reset speed to user-defined
// playback parameters or 1.0 for ad playback.
PlaybackParameters targetPlaybackParameters =
newPeriodId.isAd() ? PlaybackParameters.DEFAULT : playbackInfo.playbackParameters;
if (!mediaClock.getPlaybackParameters().equals(targetPlaybackParameters)) {
mediaClock.setPlaybackParameters(targetPlaybackParameters);
}
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9324,6 +9324,85 @@ public void playerIdle_withSetPlaybackSpeed_usesPlaybackParameterSpeedWithPitchU
.onPlaybackParametersChanged(new PlaybackParameters(/* speed= */ 2, /* pitch= */ 2));
}

@Test
public void setPlaybackSpeed_withAdPlayback_onlyAppliesToContent() throws Exception {
// Create renderer with media clock to listen to playback parameter changes.
ArrayList<PlaybackParameters> playbackParameters = new ArrayList<>();
FakeMediaClockRenderer audioRenderer =
new FakeMediaClockRenderer(C.TRACK_TYPE_AUDIO) {
private long positionUs;

@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) {
this.positionUs = offsetUs;
}

@Override
public long getPositionUs() {
// Continuously increase position to let playback progress.
positionUs += 10_000;
return positionUs;
}

@Override
public void setPlaybackParameters(PlaybackParameters parameters) {
playbackParameters.add(parameters);
}

@Override
public PlaybackParameters getPlaybackParameters() {
return playbackParameters.isEmpty()
? PlaybackParameters.DEFAULT
: Iterables.getLast(playbackParameters);
}
};
ExoPlayer player = new TestExoPlayerBuilder(context).setRenderers(audioRenderer).build();
AdPlaybackState adPlaybackState =
FakeTimeline.createAdPlaybackState(
/* adsPerAdGroup= */ 1,
/* adGroupTimesUs...= */ 0,
7 * C.MICROS_PER_SECOND,
C.TIME_END_OF_SOURCE);
TimelineWindowDefinition adTimelineDefinition =
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* isLive= */ false,
/* isPlaceholder= */ false,
/* durationUs= */ 10 * C.MICROS_PER_SECOND,
/* defaultPositionUs= */ 0,
/* windowOffsetInFirstPeriodUs= */ 0,
adPlaybackState);
player.setMediaSource(
new FakeMediaSource(
new FakeTimeline(adTimelineDefinition), ExoPlayerTestRunner.AUDIO_FORMAT));
Player.Listener mockListener = mock(Player.Listener.class);
player.addListener(mockListener);

player.setPlaybackSpeed(5f);
player.prepare();
player.play();
runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();

// Assert that the renderer received the playback speed updates at each ad/content boundary.
assertThat(playbackParameters)
.containsExactly(
/* preroll ad */ new PlaybackParameters(1f),
/* content after preroll */ new PlaybackParameters(5f),
/* midroll ad */ new PlaybackParameters(1f),
/* content after midroll */ new PlaybackParameters(5f),
/* postroll ad */ new PlaybackParameters(1f),
/* content after postroll */ new PlaybackParameters(5f))
.inOrder();

// Assert that user-set speed was reported, but none of the ad overrides.
verify(mockListener).onPlaybackParametersChanged(any());
verify(mockListener).onPlaybackParametersChanged(new PlaybackParameters(5.0f));
}

@Test
public void targetLiveOffsetInMedia_withSetPlaybackParameters_usesPlaybackParameterSpeed()
throws Exception {
Expand Down

0 comments on commit 73d9728

Please sign in to comment.