mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Avoid recalculation of whether a MediaPeriod is part of a SSAI stream
We can instead just save this information in MediaPeriodInfo, similar to how we store whether the MediaPeriod is last in the timeline etc. PiperOrigin-RevId: 374835918
This commit is contained in:
parent
0de6bc861a
commit
5ff4211c5e
3 changed files with 165 additions and 28 deletions
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
/** Stores the information required to load and play a {@link MediaPeriod}. */
|
/** Stores the information required to load and play a {@link MediaPeriod}. */
|
||||||
|
|
@ -48,6 +49,12 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
* known.
|
* known.
|
||||||
*/
|
*/
|
||||||
public final long durationUs;
|
public final long durationUs;
|
||||||
|
/**
|
||||||
|
* Whether this media period is followed by a transition to another media period of the same
|
||||||
|
* server-side inserted ad stream. If true, {@link #isLastInTimelinePeriod}, {@link
|
||||||
|
* #isLastInTimelineWindow} and {@link #isFinal} will all be false.
|
||||||
|
*/
|
||||||
|
public final boolean isFollowedByTransitionToSameStream;
|
||||||
/**
|
/**
|
||||||
* Whether this is the last media period in its timeline period (e.g., a postroll ad, or a media
|
* Whether this is the last media period in its timeline period (e.g., a postroll ad, or a media
|
||||||
* period corresponding to a timeline period without ads).
|
* period corresponding to a timeline period without ads).
|
||||||
|
|
@ -67,14 +74,21 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
long requestedContentPositionUs,
|
long requestedContentPositionUs,
|
||||||
long endPositionUs,
|
long endPositionUs,
|
||||||
long durationUs,
|
long durationUs,
|
||||||
|
boolean isFollowedByTransitionToSameStream,
|
||||||
boolean isLastInTimelinePeriod,
|
boolean isLastInTimelinePeriod,
|
||||||
boolean isLastInTimelineWindow,
|
boolean isLastInTimelineWindow,
|
||||||
boolean isFinal) {
|
boolean isFinal) {
|
||||||
|
Assertions.checkArgument(!isFinal || isLastInTimelinePeriod);
|
||||||
|
Assertions.checkArgument(!isLastInTimelineWindow || isLastInTimelinePeriod);
|
||||||
|
Assertions.checkArgument(
|
||||||
|
!isFollowedByTransitionToSameStream
|
||||||
|
|| (!isLastInTimelinePeriod && !isLastInTimelineWindow && !isFinal));
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.startPositionUs = startPositionUs;
|
this.startPositionUs = startPositionUs;
|
||||||
this.requestedContentPositionUs = requestedContentPositionUs;
|
this.requestedContentPositionUs = requestedContentPositionUs;
|
||||||
this.endPositionUs = endPositionUs;
|
this.endPositionUs = endPositionUs;
|
||||||
this.durationUs = durationUs;
|
this.durationUs = durationUs;
|
||||||
|
this.isFollowedByTransitionToSameStream = isFollowedByTransitionToSameStream;
|
||||||
this.isLastInTimelinePeriod = isLastInTimelinePeriod;
|
this.isLastInTimelinePeriod = isLastInTimelinePeriod;
|
||||||
this.isLastInTimelineWindow = isLastInTimelineWindow;
|
this.isLastInTimelineWindow = isLastInTimelineWindow;
|
||||||
this.isFinal = isFinal;
|
this.isFinal = isFinal;
|
||||||
|
|
@ -93,6 +107,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
requestedContentPositionUs,
|
requestedContentPositionUs,
|
||||||
endPositionUs,
|
endPositionUs,
|
||||||
durationUs,
|
durationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
isLastInTimelinePeriod,
|
isLastInTimelinePeriod,
|
||||||
isLastInTimelineWindow,
|
isLastInTimelineWindow,
|
||||||
isFinal);
|
isFinal);
|
||||||
|
|
@ -111,6 +126,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
requestedContentPositionUs,
|
requestedContentPositionUs,
|
||||||
endPositionUs,
|
endPositionUs,
|
||||||
durationUs,
|
durationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
isLastInTimelinePeriod,
|
isLastInTimelinePeriod,
|
||||||
isLastInTimelineWindow,
|
isLastInTimelineWindow,
|
||||||
isFinal);
|
isFinal);
|
||||||
|
|
@ -129,6 +145,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
&& requestedContentPositionUs == that.requestedContentPositionUs
|
&& requestedContentPositionUs == that.requestedContentPositionUs
|
||||||
&& endPositionUs == that.endPositionUs
|
&& endPositionUs == that.endPositionUs
|
||||||
&& durationUs == that.durationUs
|
&& durationUs == that.durationUs
|
||||||
|
&& isFollowedByTransitionToSameStream == that.isFollowedByTransitionToSameStream
|
||||||
&& isLastInTimelinePeriod == that.isLastInTimelinePeriod
|
&& isLastInTimelinePeriod == that.isLastInTimelinePeriod
|
||||||
&& isLastInTimelineWindow == that.isLastInTimelineWindow
|
&& isLastInTimelineWindow == that.isLastInTimelineWindow
|
||||||
&& isFinal == that.isFinal
|
&& isFinal == that.isFinal
|
||||||
|
|
@ -143,6 +160,7 @@ import com.google.android.exoplayer2.util.Util;
|
||||||
result = 31 * result + (int) requestedContentPositionUs;
|
result = 31 * result + (int) requestedContentPositionUs;
|
||||||
result = 31 * result + (int) endPositionUs;
|
result = 31 * result + (int) endPositionUs;
|
||||||
result = 31 * result + (int) durationUs;
|
result = 31 * result + (int) durationUs;
|
||||||
|
result = 31 * result + (isFollowedByTransitionToSameStream ? 1 : 0);
|
||||||
result = 31 * result + (isLastInTimelinePeriod ? 1 : 0);
|
result = 31 * result + (isLastInTimelinePeriod ? 1 : 0);
|
||||||
result = 31 * result + (isLastInTimelineWindow ? 1 : 0);
|
result = 31 * result + (isLastInTimelineWindow ? 1 : 0);
|
||||||
result = 31 * result + (isFinal ? 1 : 0);
|
result = 31 * result + (isFinal ? 1 : 0);
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ import com.google.common.collect.ImmutableList;
|
||||||
: periodHolder.toRendererTime(newPeriodInfo.durationUs);
|
: periodHolder.toRendererTime(newPeriodInfo.durationUs);
|
||||||
boolean isReadingAndReadBeyondNewDuration =
|
boolean isReadingAndReadBeyondNewDuration =
|
||||||
periodHolder == reading
|
periodHolder == reading
|
||||||
&& !isUsingSameStreamForNextMediaPeriod(timeline, periodHolder.info.id)
|
&& !periodHolder.info.isFollowedByTransitionToSameStream
|
||||||
&& (maxRendererReadPositionUs == C.TIME_END_OF_SOURCE
|
&& (maxRendererReadPositionUs == C.TIME_END_OF_SOURCE
|
||||||
|| maxRendererReadPositionUs >= newDurationInRendererTime);
|
|| maxRendererReadPositionUs >= newDurationInRendererTime);
|
||||||
boolean readingPeriodRemoved = removeAfter(periodHolder);
|
boolean readingPeriodRemoved = removeAfter(periodHolder);
|
||||||
|
|
@ -396,12 +396,18 @@ import com.google.common.collect.ImmutableList;
|
||||||
: (endPositionUs == C.TIME_UNSET || endPositionUs == C.TIME_END_OF_SOURCE
|
: (endPositionUs == C.TIME_UNSET || endPositionUs == C.TIME_END_OF_SOURCE
|
||||||
? period.getDurationUs()
|
? period.getDurationUs()
|
||||||
: endPositionUs);
|
: endPositionUs);
|
||||||
|
boolean isFollowedByTransitionToSameStream =
|
||||||
|
id.isAd()
|
||||||
|
? period.isServerSideInsertedAdGroup(id.adGroupIndex)
|
||||||
|
: (id.nextAdGroupIndex != C.INDEX_UNSET
|
||||||
|
&& period.isServerSideInsertedAdGroup(id.nextAdGroupIndex));
|
||||||
return new MediaPeriodInfo(
|
return new MediaPeriodInfo(
|
||||||
id,
|
id,
|
||||||
info.startPositionUs,
|
info.startPositionUs,
|
||||||
info.requestedContentPositionUs,
|
info.requestedContentPositionUs,
|
||||||
endPositionUs,
|
endPositionUs,
|
||||||
durationUs,
|
durationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
isLastInPeriod,
|
isLastInPeriod,
|
||||||
isLastInWindow,
|
isLastInWindow,
|
||||||
isLastInTimeline);
|
isLastInTimeline);
|
||||||
|
|
@ -778,6 +784,8 @@ import com.google.common.collect.ImmutableList;
|
||||||
adIndexInAdGroup == period.getFirstAdIndexToPlay(adGroupIndex)
|
adIndexInAdGroup == period.getFirstAdIndexToPlay(adGroupIndex)
|
||||||
? period.getAdResumePositionUs()
|
? period.getAdResumePositionUs()
|
||||||
: 0;
|
: 0;
|
||||||
|
boolean isFollowedByTransitionToSameStream =
|
||||||
|
period.isServerSideInsertedAdGroup(id.adGroupIndex);
|
||||||
if (durationUs != C.TIME_UNSET && startPositionUs >= durationUs) {
|
if (durationUs != C.TIME_UNSET && startPositionUs >= durationUs) {
|
||||||
// Ensure start position doesn't exceed duration.
|
// Ensure start position doesn't exceed duration.
|
||||||
startPositionUs = max(0, durationUs - 1);
|
startPositionUs = max(0, durationUs - 1);
|
||||||
|
|
@ -788,6 +796,7 @@ import com.google.common.collect.ImmutableList;
|
||||||
contentPositionUs,
|
contentPositionUs,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
durationUs,
|
durationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
/* isLastInTimelinePeriod= */ false,
|
/* isLastInTimelinePeriod= */ false,
|
||||||
/* isLastInTimelineWindow= */ false,
|
/* isLastInTimelineWindow= */ false,
|
||||||
/* isFinal= */ false);
|
/* isFinal= */ false);
|
||||||
|
|
@ -805,6 +814,8 @@ import com.google.common.collect.ImmutableList;
|
||||||
boolean isLastInPeriod = isLastInPeriod(id);
|
boolean isLastInPeriod = isLastInPeriod(id);
|
||||||
boolean isLastInWindow = isLastInWindow(timeline, id);
|
boolean isLastInWindow = isLastInWindow(timeline, id);
|
||||||
boolean isLastInTimeline = isLastInTimeline(timeline, id, isLastInPeriod);
|
boolean isLastInTimeline = isLastInTimeline(timeline, id, isLastInPeriod);
|
||||||
|
boolean isFollowedByTransitionToSameStream =
|
||||||
|
nextAdGroupIndex != C.INDEX_UNSET && period.isServerSideInsertedAdGroup(nextAdGroupIndex);
|
||||||
long endPositionUs =
|
long endPositionUs =
|
||||||
nextAdGroupIndex != C.INDEX_UNSET
|
nextAdGroupIndex != C.INDEX_UNSET
|
||||||
? period.getAdGroupTimeUs(nextAdGroupIndex)
|
? period.getAdGroupTimeUs(nextAdGroupIndex)
|
||||||
|
|
@ -823,6 +834,7 @@ import com.google.common.collect.ImmutableList;
|
||||||
requestedContentPositionUs,
|
requestedContentPositionUs,
|
||||||
endPositionUs,
|
endPositionUs,
|
||||||
durationUs,
|
durationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
isLastInPeriod,
|
isLastInPeriod,
|
||||||
isLastInWindow,
|
isLastInWindow,
|
||||||
isLastInTimeline);
|
isLastInTimeline);
|
||||||
|
|
@ -859,20 +871,4 @@ import com.google.common.collect.ImmutableList;
|
||||||
}
|
}
|
||||||
return startPositionUs + period.getContentResumeOffsetUs(adGroupIndex);
|
return startPositionUs + period.getContentResumeOffsetUs(adGroupIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUsingSameStreamForNextMediaPeriod(
|
|
||||||
Timeline timeline, MediaPeriodId mediaPeriodId) {
|
|
||||||
// Server-side inserted ads or content after them will use the same underlying stream.
|
|
||||||
if (mediaPeriodId.isAd()) {
|
|
||||||
return timeline
|
|
||||||
.getPeriodByUid(mediaPeriodId.periodUid, period)
|
|
||||||
.isServerSideInsertedAdGroup(mediaPeriodId.adGroupIndex);
|
|
||||||
} else if (mediaPeriodId.nextAdGroupIndex == C.INDEX_UNSET) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return timeline
|
|
||||||
.getPeriodByUid(mediaPeriodId.periodUid, period)
|
|
||||||
.isServerSideInsertedAdGroup(mediaPeriodId.nextAdGroupIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -103,7 +104,10 @@ public final class MediaPeriodQueueTest {
|
||||||
setupAdTimeline(/* adGroupTimesUs...= */ 0);
|
setupAdTimeline(/* adGroupTimesUs...= */ 0);
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ C.TIME_UNSET);
|
/* adGroupIndex= */ 0,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ C.TIME_UNSET,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -111,6 +115,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -125,6 +130,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* durationUs= */ FIRST_AD_START_TIME_US,
|
/* durationUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 0);
|
/* nextAdGroupIndex= */ 0);
|
||||||
|
|
@ -132,10 +138,14 @@ public final class MediaPeriodQueueTest {
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 0,
|
/* adGroupIndex= */ 0,
|
||||||
/* adDurationUs= */ C.TIME_UNSET,
|
/* adDurationUs= */ C.TIME_UNSET,
|
||||||
/* contentPositionUs= */ FIRST_AD_START_TIME_US);
|
/* contentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ FIRST_AD_START_TIME_US);
|
/* adGroupIndex= */ 0,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -143,13 +153,17 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* endPositionUs= */ SECOND_AD_START_TIME_US,
|
/* endPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
/* durationUs= */ SECOND_AD_START_TIME_US,
|
/* durationUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 1);
|
/* nextAdGroupIndex= */ 1);
|
||||||
advance();
|
advance();
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 1, AD_DURATION_US, /* contentPositionUs= */ SECOND_AD_START_TIME_US);
|
/* adGroupIndex= */ 1,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -157,6 +171,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ SECOND_AD_START_TIME_US,
|
/* requestedContentPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -171,13 +186,17 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* durationUs= */ FIRST_AD_START_TIME_US,
|
/* durationUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 0);
|
/* nextAdGroupIndex= */ 0);
|
||||||
advance();
|
advance();
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ FIRST_AD_START_TIME_US);
|
/* adGroupIndex= */ 0,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -185,13 +204,17 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 1);
|
/* nextAdGroupIndex= */ 1);
|
||||||
advance();
|
advance();
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 1, AD_DURATION_US, /* contentPositionUs= */ CONTENT_DURATION_US);
|
/* adGroupIndex= */ 1,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -199,6 +222,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ CONTENT_DURATION_US,
|
/* requestedContentPositionUs= */ CONTENT_DURATION_US,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -222,7 +246,10 @@ public final class MediaPeriodQueueTest {
|
||||||
|
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ C.TIME_UNSET);
|
/* adGroupIndex= */ 0,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ C.TIME_UNSET,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -230,13 +257,17 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* durationUs= */ FIRST_AD_START_TIME_US,
|
/* durationUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 1);
|
/* nextAdGroupIndex= */ 1);
|
||||||
advance();
|
advance();
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 1, AD_DURATION_US, /* contentPositionUs= */ FIRST_AD_START_TIME_US);
|
/* adGroupIndex= */ 1,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -244,13 +275,17 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 2);
|
/* nextAdGroupIndex= */ 2);
|
||||||
advance();
|
advance();
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 2);
|
setAdGroupLoaded(/* adGroupIndex= */ 2);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 2, AD_DURATION_US, /* contentPositionUs= */ CONTENT_DURATION_US);
|
/* adGroupIndex= */ 2,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
advance();
|
advance();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
/* periodUid= */ firstPeriodUid,
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
|
@ -258,6 +293,78 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ CONTENT_DURATION_US,
|
/* requestedContentPositionUs= */ CONTENT_DURATION_US,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
|
/* isLastInPeriod= */ true,
|
||||||
|
/* isLastInWindow= */ true,
|
||||||
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getNextMediaPeriodInfo_withServerSideInsertedAds_returnsCorrectMediaPeriodInfos() {
|
||||||
|
adPlaybackState =
|
||||||
|
new AdPlaybackState(
|
||||||
|
/* adsId= */ new Object(),
|
||||||
|
/* adGroupTimesUs...= */ 0,
|
||||||
|
FIRST_AD_START_TIME_US,
|
||||||
|
SECOND_AD_START_TIME_US)
|
||||||
|
.withContentDurationUs(CONTENT_DURATION_US)
|
||||||
|
.withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true)
|
||||||
|
.withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true)
|
||||||
|
.withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true);
|
||||||
|
SinglePeriodAdTimeline adTimeline =
|
||||||
|
new SinglePeriodAdTimeline(CONTENT_TIMELINE, adPlaybackState);
|
||||||
|
setupTimeline(adTimeline);
|
||||||
|
|
||||||
|
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||||
|
assertNextMediaPeriodInfoIsAd(
|
||||||
|
/* adGroupIndex= */ 0,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ C.TIME_UNSET,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ true);
|
||||||
|
advance();
|
||||||
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
/* startPositionUs= */ 0,
|
||||||
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
|
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* durationUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ true,
|
||||||
|
/* isLastInPeriod= */ false,
|
||||||
|
/* isLastInWindow= */ false,
|
||||||
|
/* nextAdGroupIndex= */ 1);
|
||||||
|
advance();
|
||||||
|
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||||
|
assertNextMediaPeriodInfoIsAd(
|
||||||
|
/* adGroupIndex= */ 1,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ true);
|
||||||
|
advance();
|
||||||
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
/* startPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* requestedContentPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* endPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* durationUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ true,
|
||||||
|
/* isLastInPeriod= */ false,
|
||||||
|
/* isLastInWindow= */ false,
|
||||||
|
/* nextAdGroupIndex= */ 2);
|
||||||
|
advance();
|
||||||
|
setAdGroupLoaded(/* adGroupIndex= */ 2);
|
||||||
|
assertNextMediaPeriodInfoIsAd(
|
||||||
|
/* adGroupIndex= */ 2,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ true);
|
||||||
|
advance();
|
||||||
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
|
/* periodUid= */ firstPeriodUid,
|
||||||
|
/* startPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* requestedContentPositionUs= */ SECOND_AD_START_TIME_US,
|
||||||
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -272,6 +379,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 0);
|
/* nextAdGroupIndex= */ 0);
|
||||||
|
|
@ -283,6 +391,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ CONTENT_DURATION_US,
|
/* requestedContentPositionUs= */ CONTENT_DURATION_US,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -295,7 +404,10 @@ public final class MediaPeriodQueueTest {
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||||
setAdGroupLoaded(/* adGroupIndex= */ 2);
|
setAdGroupLoaded(/* adGroupIndex= */ 2);
|
||||||
assertNextMediaPeriodInfoIsAd(
|
assertNextMediaPeriodInfoIsAd(
|
||||||
/* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ C.TIME_UNSET);
|
/* adGroupIndex= */ 0,
|
||||||
|
AD_DURATION_US,
|
||||||
|
/* contentPositionUs= */ C.TIME_UNSET,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false);
|
||||||
setAdGroupPlayed(/* adGroupIndex= */ 0);
|
setAdGroupPlayed(/* adGroupIndex= */ 0);
|
||||||
clear();
|
clear();
|
||||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||||
|
|
@ -304,6 +416,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
/* endPositionUs= */ FIRST_AD_START_TIME_US,
|
||||||
/* durationUs= */ FIRST_AD_START_TIME_US,
|
/* durationUs= */ FIRST_AD_START_TIME_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 1);
|
/* nextAdGroupIndex= */ 1);
|
||||||
|
|
@ -315,6 +428,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ false,
|
/* isLastInPeriod= */ false,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ 2);
|
/* nextAdGroupIndex= */ 2);
|
||||||
|
|
@ -326,6 +440,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
/* requestedContentPositionUs= */ C.TIME_UNSET,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -349,6 +464,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US
|
/* durationUs= */ CONTENT_DURATION_US
|
||||||
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
|
+ TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ false,
|
/* isLastInWindow= */ false,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -359,6 +475,7 @@ public final class MediaPeriodQueueTest {
|
||||||
/* requestedContentPositionUs= */ 0,
|
/* requestedContentPositionUs= */ 0,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
/* durationUs= */ CONTENT_DURATION_US,
|
/* durationUs= */ CONTENT_DURATION_US,
|
||||||
|
/* isFollowedByTransitionToSameStream= */ false,
|
||||||
/* isLastInPeriod= */ true,
|
/* isLastInPeriod= */ true,
|
||||||
/* isLastInWindow= */ true,
|
/* isLastInWindow= */ true,
|
||||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||||
|
|
@ -727,6 +844,7 @@ public final class MediaPeriodQueueTest {
|
||||||
long requestedContentPositionUs,
|
long requestedContentPositionUs,
|
||||||
long endPositionUs,
|
long endPositionUs,
|
||||||
long durationUs,
|
long durationUs,
|
||||||
|
boolean isFollowedByTransitionToSameStream,
|
||||||
boolean isLastInPeriod,
|
boolean isLastInPeriod,
|
||||||
boolean isLastInWindow,
|
boolean isLastInWindow,
|
||||||
int nextAdGroupIndex) {
|
int nextAdGroupIndex) {
|
||||||
|
|
@ -738,13 +856,17 @@ public final class MediaPeriodQueueTest {
|
||||||
requestedContentPositionUs,
|
requestedContentPositionUs,
|
||||||
endPositionUs,
|
endPositionUs,
|
||||||
durationUs,
|
durationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
isLastInPeriod,
|
isLastInPeriod,
|
||||||
isLastInWindow,
|
isLastInWindow,
|
||||||
/* isFinal= */ isLastInWindow));
|
/* isFinal= */ isLastInWindow));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertNextMediaPeriodInfoIsAd(
|
private void assertNextMediaPeriodInfoIsAd(
|
||||||
int adGroupIndex, long adDurationUs, long contentPositionUs) {
|
int adGroupIndex,
|
||||||
|
long adDurationUs,
|
||||||
|
long contentPositionUs,
|
||||||
|
boolean isFollowedByTransitionToSameStream) {
|
||||||
assertThat(getNextMediaPeriodInfo())
|
assertThat(getNextMediaPeriodInfo())
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
new MediaPeriodInfo(
|
new MediaPeriodInfo(
|
||||||
|
|
@ -757,6 +879,7 @@ public final class MediaPeriodQueueTest {
|
||||||
contentPositionUs,
|
contentPositionUs,
|
||||||
/* endPositionUs= */ C.TIME_UNSET,
|
/* endPositionUs= */ C.TIME_UNSET,
|
||||||
adDurationUs,
|
adDurationUs,
|
||||||
|
isFollowedByTransitionToSameStream,
|
||||||
/* isLastInTimelinePeriod= */ false,
|
/* isLastInTimelinePeriod= */ false,
|
||||||
/* isLastInTimelineWindow= */ false,
|
/* isLastInTimelineWindow= */ false,
|
||||||
/* isFinal= */ false));
|
/* isFinal= */ false));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue