mirror of
https://github.com/androidx/media.git
synced 2025-05-10 17:22:13 +08:00
Fix incorrect rounding of ad cue points
We currently get float ad cue points from IMA, but store these as longs in microseconds. The cast from double to long would take the floor of the value, which could lead to stored ad cue points being off-by-one. Use Math.round to avoid this. ImaAdsLoader also has code to map a double AdPodInfo position (which should match a cue point) onto the corresponding ad group index by searching the long ad cue points. Match the calculation used where we map float cue points, including narrowing the position to a float first to avoid regressions if IMA SDK behavior changes to represent positions in more than float precision later, and also remove the requirement that the ad positions match exactly as a defensive measure. PiperOrigin-RevId: 317607017
This commit is contained in:
parent
dbe16cd268
commit
aec5ff8be1
@ -228,6 +228,7 @@
|
|||||||
* Work around unexpected `pauseAd`/`stopAd` for ads that have preloaded
|
* Work around unexpected `pauseAd`/`stopAd` for ads that have preloaded
|
||||||
on seeking to another position
|
on seeking to another position
|
||||||
([#7492](https://github.com/google/ExoPlayer/issues/7492)).
|
([#7492](https://github.com/google/ExoPlayer/issues/7492)).
|
||||||
|
* Fix incorrect rounding of ad cue points.
|
||||||
|
|
||||||
### 2.11.5 (2020-06-05) ###
|
### 2.11.5 (2020-06-05) ###
|
||||||
|
|
||||||
|
@ -487,6 +487,11 @@
|
|||||||
"name": "VMAP full, empty, full midrolls",
|
"name": "VMAP full, empty, full midrolls",
|
||||||
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
|
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
|
||||||
"ad_tag_uri": "https://vastsynthesizer.appspot.com/empty-midroll-2"
|
"ad_tag_uri": "https://vastsynthesizer.appspot.com/empty-midroll-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VMAP midroll at 1765 s",
|
||||||
|
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mp4/frame-counter-one-hour.mp4",
|
||||||
|
"ad_tag_uri": "https://vastsynthesizer.appspot.com/midroll-large"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -46,7 +46,7 @@ import java.util.List;
|
|||||||
if (cuePoint == -1.0) {
|
if (cuePoint == -1.0) {
|
||||||
adGroupTimesUs[count - 1] = C.TIME_END_OF_SOURCE;
|
adGroupTimesUs[count - 1] = C.TIME_END_OF_SOURCE;
|
||||||
} else {
|
} else {
|
||||||
adGroupTimesUs[adGroupIndex++] = (long) (C.MICROS_PER_SECOND * cuePoint);
|
adGroupTimesUs[adGroupIndex++] = Math.round(C.MICROS_PER_SECOND * cuePoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cue points may be out of order, so sort them.
|
// Cue points may be out of order, so sort them.
|
||||||
|
@ -343,6 +343,8 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
|||||||
* milliseconds.
|
* milliseconds.
|
||||||
*/
|
*/
|
||||||
private static final long THRESHOLD_AD_PRELOAD_MS = 4000;
|
private static final long THRESHOLD_AD_PRELOAD_MS = 4000;
|
||||||
|
/** The threshold below which ad cue points are treated as matching, in microseconds. */
|
||||||
|
private static final long THRESHOLD_AD_MATCH_US = 1000;
|
||||||
|
|
||||||
private static final int TIMEOUT_UNSET = -1;
|
private static final int TIMEOUT_UNSET = -1;
|
||||||
private static final int BITRATE_UNSET = -1;
|
private static final int BITRATE_UNSET = -1;
|
||||||
@ -1261,9 +1263,15 @@ public final class ImaAdsLoader implements Player.EventListener, AdsLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// adPodInfo.podIndex may be 0-based or 1-based, so for now look up the cue point instead.
|
// adPodInfo.podIndex may be 0-based or 1-based, so for now look up the cue point instead.
|
||||||
long adGroupTimeUs = (long) (((float) adPodInfo.getTimeOffset()) * C.MICROS_PER_SECOND);
|
// We receive cue points from IMA SDK as floats. This code replicates the same calculation used
|
||||||
|
// to populate adGroupTimesUs (having truncated input back to float, to avoid failures if the
|
||||||
|
// behavior of the IMA SDK changes to provide greater precision in AdPodInfo).
|
||||||
|
long adPodTimeUs =
|
||||||
|
Math.round((double) ((float) adPodInfo.getTimeOffset()) * C.MICROS_PER_SECOND);
|
||||||
for (int adGroupIndex = 0; adGroupIndex < adPlaybackState.adGroupCount; adGroupIndex++) {
|
for (int adGroupIndex = 0; adGroupIndex < adPlaybackState.adGroupCount; adGroupIndex++) {
|
||||||
if (adPlaybackState.adGroupTimesUs[adGroupIndex] == adGroupTimeUs) {
|
long adGroupTimeUs = adPlaybackState.adGroupTimesUs[adGroupIndex];
|
||||||
|
if (adGroupTimeUs != C.TIME_END_OF_SOURCE
|
||||||
|
&& Math.abs(adGroupTimeUs - adPodTimeUs) < THRESHOLD_AD_MATCH_US) {
|
||||||
return adGroupIndex;
|
return adGroupIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,6 +641,55 @@ public final class ImaAdsLoaderTest {
|
|||||||
inOrder.verify(mockAdDisplayContainer).unregisterAllVideoControlsOverlays();
|
inOrder.verify(mockAdDisplayContainer).unregisterAllVideoControlsOverlays();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadAd_withLargeAdCuePoint_updatesAdPlaybackStateWithLoadedAd() {
|
||||||
|
float midrollTimeSecs = 1_765f;
|
||||||
|
ImmutableList<Float> cuePoints = ImmutableList.of(midrollTimeSecs);
|
||||||
|
setupPlayback(CONTENT_TIMELINE, cuePoints);
|
||||||
|
imaAdsLoader.start(adsLoaderListener, adViewProvider);
|
||||||
|
videoAdPlayer.loadAd(
|
||||||
|
TEST_AD_MEDIA_INFO,
|
||||||
|
new AdPodInfo() {
|
||||||
|
@Override
|
||||||
|
public int getTotalAds() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAdPosition() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBumper() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getMaxDuration() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPodIndex() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getTimeOffset() {
|
||||||
|
return midrollTimeSecs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThat(adsLoaderListener.adPlaybackState)
|
||||||
|
.isEqualTo(
|
||||||
|
AdPlaybackStateFactory.fromCuePoints(cuePoints)
|
||||||
|
.withContentDurationUs(CONTENT_PERIOD_DURATION_US)
|
||||||
|
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
|
||||||
|
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
|
||||||
|
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}));
|
||||||
|
}
|
||||||
|
|
||||||
private void setupPlayback(Timeline contentTimeline, List<Float> cuePoints) {
|
private void setupPlayback(Timeline contentTimeline, List<Float> cuePoints) {
|
||||||
setupPlayback(
|
setupPlayback(
|
||||||
contentTimeline,
|
contentTimeline,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user