Remove rounding errors of ad durations when converting from double
#minor-release PiperOrigin-RevId: 435360232
This commit is contained in:
parent
7eb01e2125
commit
3cc1d06b6e
@ -17,6 +17,8 @@ package com.google.android.exoplayer2.ext.ima;
|
|||||||
|
|
||||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.expandAdGroupPlaceholder;
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.expandAdGroupPlaceholder;
|
||||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.getAdGroupAndIndexInMultiPeriodWindow;
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.getAdGroupAndIndexInMultiPeriodWindow;
|
||||||
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.secToMsRounded;
|
||||||
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.secToUsRounded;
|
||||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.splitAdPlaybackStateForPeriods;
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.splitAdPlaybackStateForPeriods;
|
||||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.updateAdDurationAndPropagate;
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.updateAdDurationAndPropagate;
|
||||||
import static com.google.android.exoplayer2.ext.ima.ImaUtil.updateAdDurationInAdGroup;
|
import static com.google.android.exoplayer2.ext.ima.ImaUtil.updateAdDurationInAdGroup;
|
||||||
@ -24,7 +26,6 @@ import static com.google.android.exoplayer2.source.ads.ServerSideAdInsertionUtil
|
|||||||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||||
import static com.google.android.exoplayer2.util.Util.msToUs;
|
import static com.google.android.exoplayer2.util.Util.msToUs;
|
||||||
import static com.google.android.exoplayer2.util.Util.secToUs;
|
|
||||||
import static com.google.android.exoplayer2.util.Util.sum;
|
import static com.google.android.exoplayer2.util.Util.sum;
|
||||||
import static com.google.android.exoplayer2.util.Util.usToMs;
|
import static com.google.android.exoplayer2.util.Util.usToMs;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
@ -659,19 +660,29 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
|
|
||||||
private static AdPlaybackState setVodAdGroupPlaceholders(
|
private static AdPlaybackState setVodAdGroupPlaceholders(
|
||||||
List<CuePoint> cuePoints, AdPlaybackState adPlaybackState) {
|
List<CuePoint> cuePoints, AdPlaybackState adPlaybackState) {
|
||||||
|
// TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed
|
||||||
for (int i = 0; i < cuePoints.size(); i++) {
|
for (int i = 0; i < cuePoints.size(); i++) {
|
||||||
CuePoint cuePoint = cuePoints.get(i);
|
CuePoint cuePoint = cuePoints.get(i);
|
||||||
|
long fromPositionUs = msToUs(secToMsRounded(cuePoint.getStartTime()));
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
addAdGroupToAdPlaybackState(
|
addAdGroupToAdPlaybackState(
|
||||||
adPlaybackState,
|
adPlaybackState,
|
||||||
/* fromPositionUs= */ secToUs(cuePoint.getStartTime()),
|
/* fromPositionUs= */ fromPositionUs,
|
||||||
/* contentResumeOffsetUs= */ 0,
|
/* contentResumeOffsetUs= */ 0,
|
||||||
// TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed
|
/* adDurationsUs...= */ getAdDuration(
|
||||||
/* adDurationsUs...= */ secToUs(cuePoint.getEndTime() - cuePoint.getStartTime()));
|
/* startTimeSeconds= */ cuePoint.getStartTime(),
|
||||||
|
/* endTimeSeconds= */ cuePoint.getEndTime()));
|
||||||
}
|
}
|
||||||
return adPlaybackState;
|
return adPlaybackState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long getAdDuration(double startTimeSeconds, double endTimeSeconds) {
|
||||||
|
// startTimeSeconds and endTimeSeconds that are coming from the SDK, only have a precision of
|
||||||
|
// milliseconds so everything that is below a millisecond can be safely considered as coming
|
||||||
|
// from rounding issues.
|
||||||
|
return msToUs(secToMsRounded(endTimeSeconds - startTimeSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
private static AdPlaybackState setVodAdInPlaceholder(Ad ad, AdPlaybackState adPlaybackState) {
|
private static AdPlaybackState setVodAdInPlaceholder(Ad ad, AdPlaybackState adPlaybackState) {
|
||||||
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
||||||
// Handle post rolls that have a podIndex of -1.
|
// Handle post rolls that have a podIndex of -1.
|
||||||
@ -683,9 +694,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
expandAdGroupPlaceholder(
|
expandAdGroupPlaceholder(
|
||||||
adGroupIndex,
|
adGroupIndex,
|
||||||
/* adGroupDurationUs= */ secToUs(adPodInfo.getMaxDuration()),
|
/* adGroupDurationUs= */ msToUs(secToMsRounded(adPodInfo.getMaxDuration())),
|
||||||
adIndexInAdGroup,
|
adIndexInAdGroup,
|
||||||
/* adDurationUs= */ secToUs(ad.getDuration()),
|
/* adDurationUs= */ msToUs(secToMsRounded(ad.getDuration())),
|
||||||
/* adsInAdGroupCount= */ adPodInfo.getTotalAds(),
|
/* adsInAdGroupCount= */ adPodInfo.getTotalAds(),
|
||||||
adPlaybackState);
|
adPlaybackState);
|
||||||
} else if (adIndexInAdGroup < adGroup.count - 1) {
|
} else if (adIndexInAdGroup < adGroup.count - 1) {
|
||||||
@ -693,7 +704,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
updateAdDurationInAdGroup(
|
updateAdDurationInAdGroup(
|
||||||
adGroupIndex,
|
adGroupIndex,
|
||||||
adIndexInAdGroup,
|
adIndexInAdGroup,
|
||||||
/* adDurationUs= */ secToUs(ad.getDuration()),
|
/* adDurationUs= */ msToUs(secToMsRounded(ad.getDuration())),
|
||||||
adPlaybackState);
|
adPlaybackState);
|
||||||
}
|
}
|
||||||
return adPlaybackState;
|
return adPlaybackState;
|
||||||
@ -702,7 +713,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
private AdPlaybackState addLiveAdBreak(
|
private AdPlaybackState addLiveAdBreak(
|
||||||
Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) {
|
Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) {
|
||||||
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
||||||
long adDurationUs = secToUs(ad.getDuration());
|
long adDurationUs = secToUsRounded(ad.getDuration());
|
||||||
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
|
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
|
||||||
// TODO(b/208398934) Support seeking backwards.
|
// TODO(b/208398934) Support seeking backwards.
|
||||||
if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) {
|
if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) {
|
||||||
@ -716,7 +727,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
new long[adCount],
|
new long[adCount],
|
||||||
adIndexInAdGroup,
|
adIndexInAdGroup,
|
||||||
adDurationUs,
|
adDurationUs,
|
||||||
secToUs(adPodInfo.getMaxDuration()));
|
msToUs(secToMsRounded(adPodInfo.getMaxDuration())));
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
addAdGroupToAdPlaybackState(
|
addAdGroupToAdPlaybackState(
|
||||||
adPlaybackState,
|
adPlaybackState,
|
||||||
|
@ -54,7 +54,10 @@ import com.google.android.exoplayer2.upstream.DataSpec;
|
|||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.math.DoubleMath;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -398,17 +401,13 @@ import java.util.Set;
|
|||||||
long elapsedAdGroupAdDurationUs = 0;
|
long elapsedAdGroupAdDurationUs = 0;
|
||||||
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
||||||
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
||||||
// TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK.
|
if (totalElapsedContentDurationUs < adGroup.timeUs) {
|
||||||
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
|
|
||||||
// Period starts before the ad group, so it is a content period.
|
// Period starts before the ad group, so it is a content period.
|
||||||
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
|
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
|
||||||
totalElapsedContentDurationUs += period.durationUs;
|
totalElapsedContentDurationUs += period.durationUs;
|
||||||
} else {
|
} else {
|
||||||
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
||||||
// TODO(b/192231683) Remove additional US when we can upgrade the SDK.
|
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs) {
|
||||||
// Add one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
|
|
||||||
// The period ends before the end of the ad group, so it is an ad period (Note: A VOD ad
|
// The period ends before the end of the ad group, so it is an ad period (Note: A VOD ad
|
||||||
// reported by the IMA SDK spans multiple periods before the LOADED event arrives).
|
// reported by the IMA SDK spans multiple periods before the LOADED event arrives).
|
||||||
adPlaybackStates.put(
|
adPlaybackStates.put(
|
||||||
@ -490,16 +489,12 @@ import java.util.Set;
|
|||||||
long elapsedAdGroupAdDurationUs = 0;
|
long elapsedAdGroupAdDurationUs = 0;
|
||||||
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
||||||
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
||||||
// TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK.
|
if (totalElapsedContentDurationUs < adGroup.timeUs) {
|
||||||
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
|
|
||||||
// Period starts before the ad group, so it is a content period.
|
// Period starts before the ad group, so it is a content period.
|
||||||
totalElapsedContentDurationUs += period.durationUs;
|
totalElapsedContentDurationUs += period.durationUs;
|
||||||
} else {
|
} else {
|
||||||
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
||||||
// TODO(b/192231683) Remove additional US when we can upgrade the SDK.
|
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs) {
|
||||||
// Add one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
|
|
||||||
// The period ends before the end of the ad group, so it is an ad period.
|
// The period ends before the end of the ad group, so it is an ad period.
|
||||||
if (j == adPeriodIndex) {
|
if (j == adPeriodIndex) {
|
||||||
return new Pair<>(/* adGroupIndex= */ i, adIndexInAdGroup);
|
return new Pair<>(/* adGroupIndex= */ i, adIndexInAdGroup);
|
||||||
@ -518,5 +513,31 @@ import java.util.Set;
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time in seconds to the corresponding time in microseconds.
|
||||||
|
*
|
||||||
|
* <p>Fractional values are rounded to the nearest microsecond using {@link RoundingMode#HALF_UP}.
|
||||||
|
*
|
||||||
|
* @param timeSec The time in seconds.
|
||||||
|
* @return The corresponding time in microseconds.
|
||||||
|
*/
|
||||||
|
public static long secToUsRounded(double timeSec) {
|
||||||
|
return DoubleMath.roundToLong(
|
||||||
|
BigDecimal.valueOf(timeSec).scaleByPowerOfTen(6).doubleValue(), RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time in seconds to the corresponding time in milliseconds.
|
||||||
|
*
|
||||||
|
* <p>Fractional values are rounded to the nearest millisecond using {@link RoundingMode#HALF_UP}.
|
||||||
|
*
|
||||||
|
* @param timeSec The time in seconds.
|
||||||
|
* @return The corresponding time in milliseconds.
|
||||||
|
*/
|
||||||
|
public static long secToMsRounded(double timeSec) {
|
||||||
|
return DoubleMath.roundToLong(
|
||||||
|
BigDecimal.valueOf(timeSec).scaleByPowerOfTen(3).doubleValue(), RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
private ImaUtil() {}
|
private ImaUtil() {}
|
||||||
}
|
}
|
||||||
|
@ -1141,18 +1141,6 @@ public final class Util {
|
|||||||
return (timeMs == C.TIME_UNSET || timeMs == C.TIME_END_OF_SOURCE) ? timeMs : (timeMs * 1000);
|
return (timeMs == C.TIME_UNSET || timeMs == C.TIME_END_OF_SOURCE) ? timeMs : (timeMs * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a time in seconds to the corresponding time in microseconds.
|
|
||||||
*
|
|
||||||
* @param timeSec The time in seconds.
|
|
||||||
* @return The corresponding time in microseconds.
|
|
||||||
*/
|
|
||||||
public static long secToUs(double timeSec) {
|
|
||||||
return BigDecimal.valueOf(timeSec)
|
|
||||||
.multiply(BigDecimal.valueOf(C.MICROS_PER_SECOND))
|
|
||||||
.longValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
|
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user