|
36 | 36 | import android.util.Pair;
|
37 | 37 | import android.view.Surface;
|
38 | 38 | import androidx.media3.common.AdPlaybackState;
|
| 39 | +import androidx.media3.common.C; |
39 | 40 | import androidx.media3.common.MediaItem;
|
40 | 41 | import androidx.media3.common.Player;
|
41 | 42 | import androidx.media3.common.Timeline;
|
42 | 43 | import androidx.media3.exoplayer.ExoPlayer;
|
43 | 44 | import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
44 | 45 | import androidx.media3.exoplayer.analytics.PlayerId;
|
45 | 46 | import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
| 47 | +import androidx.media3.exoplayer.source.SinglePeriodTimeline; |
46 | 48 | import androidx.media3.test.utils.CapturingRenderersFactory;
|
47 | 49 | import androidx.media3.test.utils.DumpFileAsserts;
|
48 | 50 | import androidx.media3.test.utils.FakeClock;
|
@@ -71,15 +73,15 @@ public final class ServerSideAdInsertionMediaSourceTest {
|
71 | 73 | private static final String TEST_ASSET_DUMP = "playbackdumps/mp4/ssai-sample.mp4.dump";
|
72 | 74 |
|
73 | 75 | @Test
|
74 |
| - public void timeline_containsAdsDefinedInAdPlaybackState() throws Exception { |
| 76 | + public void timeline_vodSinglePeriod_containsAdsDefinedInAdPlaybackState() throws Exception { |
75 | 77 | FakeTimeline wrappedTimeline =
|
76 | 78 | new FakeTimeline(
|
77 | 79 | new FakeTimeline.TimelineWindowDefinition(
|
78 | 80 | /* periodCount= */ 1,
|
79 | 81 | /* id= */ 0,
|
80 | 82 | /* isSeekable= */ true,
|
81 |
| - /* isDynamic= */ true, |
82 |
| - /* isLive= */ true, |
| 83 | + /* isDynamic= */ false, |
| 84 | + /* isLive= */ false, |
83 | 85 | /* isPlaceholder= */ false,
|
84 | 86 | /* durationUs= */ 10_000_000,
|
85 | 87 | /* defaultPositionUs= */ 3_000_000,
|
@@ -147,6 +149,83 @@ public void timeline_containsAdsDefinedInAdPlaybackState() throws Exception {
|
147 | 149 | assertThat(window.durationUs).isEqualTo(9_800_000);
|
148 | 150 | }
|
149 | 151 |
|
| 152 | + @Test |
| 153 | + public void timeline_liveSinglePeriodWithUnsetPeriodDuration_containsAdsDefinedInAdPlaybackState() |
| 154 | + throws Exception { |
| 155 | + Timeline wrappedTimeline = |
| 156 | + new SinglePeriodTimeline( |
| 157 | + /* periodDurationUs= */ C.TIME_UNSET, |
| 158 | + /* windowDurationUs= */ 10_000_000, |
| 159 | + /* windowPositionInPeriodUs= */ 42_000_000L, |
| 160 | + /* windowDefaultStartPositionUs= */ 3_000_000, |
| 161 | + /* isSeekable= */ true, |
| 162 | + /* isDynamic= */ true, |
| 163 | + /* useLiveConfiguration= */ true, |
| 164 | + /* manifest= */ null, |
| 165 | + /* mediaItem= */ MediaItem.EMPTY); |
| 166 | + ServerSideAdInsertionMediaSource mediaSource = |
| 167 | + new ServerSideAdInsertionMediaSource( |
| 168 | + new FakeMediaSource(wrappedTimeline), /* adPlaybackStateUpdater= */ null); |
| 169 | + // Test with one ad group before the window, and the window starting within the second ad group. |
| 170 | + AdPlaybackState adPlaybackState = |
| 171 | + new AdPlaybackState( |
| 172 | + /* adsId= */ new Object(), /* adGroupTimesUs= */ 15_000_000, 41_500_000, 42_200_000) |
| 173 | + .withIsServerSideInserted(/* adGroupIndex= */ 0, /* isServerSideInserted= */ true) |
| 174 | + .withIsServerSideInserted(/* adGroupIndex= */ 1, /* isServerSideInserted= */ true) |
| 175 | + .withIsServerSideInserted(/* adGroupIndex= */ 2, /* isServerSideInserted= */ true) |
| 176 | + .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) |
| 177 | + .withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 2) |
| 178 | + .withAdCount(/* adGroupIndex= */ 2, /* adCount= */ 1) |
| 179 | + .withAdDurationsUs(/* adGroupIndex= */ 0, /* adDurationsUs= */ 500_000) |
| 180 | + .withAdDurationsUs(/* adGroupIndex= */ 1, /* adDurationsUs= */ 300_000, 100_000) |
| 181 | + .withAdDurationsUs(/* adGroupIndex= */ 2, /* adDurationsUs= */ 400_000) |
| 182 | + .withContentResumeOffsetUs(/* adGroupIndex= */ 0, /* contentResumeOffsetUs= */ 100_000) |
| 183 | + .withContentResumeOffsetUs(/* adGroupIndex= */ 1, /* contentResumeOffsetUs= */ 400_000) |
| 184 | + .withContentResumeOffsetUs(/* adGroupIndex= */ 2, /* contentResumeOffsetUs= */ 200_000); |
| 185 | + AtomicReference<Timeline> timelineReference = new AtomicReference<>(); |
| 186 | + mediaSource.setAdPlaybackStates( |
| 187 | + ImmutableMap.of( |
| 188 | + wrappedTimeline.getPeriod( |
| 189 | + /* periodIndex= */ 0, new Timeline.Period(), /* setIds= */ true) |
| 190 | + .uid, |
| 191 | + adPlaybackState)); |
| 192 | + |
| 193 | + mediaSource.prepareSource( |
| 194 | + (source, timeline) -> timelineReference.set(timeline), |
| 195 | + /* mediaTransferListener= */ null, |
| 196 | + PlayerId.UNSET); |
| 197 | + runMainLooperUntil(() -> timelineReference.get() != null); |
| 198 | + |
| 199 | + Timeline timeline = timelineReference.get(); |
| 200 | + assertThat(timeline.getPeriodCount()).isEqualTo(1); |
| 201 | + Timeline.Period period = timeline.getPeriod(/* periodIndex= */ 0, new Timeline.Period()); |
| 202 | + assertThat(period.getAdGroupCount()).isEqualTo(3); |
| 203 | + assertThat(period.getAdCountInAdGroup(/* adGroupIndex= */ 0)).isEqualTo(1); |
| 204 | + assertThat(period.getAdCountInAdGroup(/* adGroupIndex= */ 1)).isEqualTo(2); |
| 205 | + assertThat(period.getAdCountInAdGroup(/* adGroupIndex= */ 2)).isEqualTo(1); |
| 206 | + assertThat(period.getAdGroupTimeUs(/* adGroupIndex= */ 0)).isEqualTo(15_000_000); |
| 207 | + assertThat(period.getAdGroupTimeUs(/* adGroupIndex= */ 1)).isEqualTo(41_500_000); |
| 208 | + assertThat(period.getAdGroupTimeUs(/* adGroupIndex= */ 2)).isEqualTo(42_200_000); |
| 209 | + assertThat(period.getAdDurationUs(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)) |
| 210 | + .isEqualTo(500_000); |
| 211 | + assertThat(period.getAdDurationUs(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0)) |
| 212 | + .isEqualTo(300_000); |
| 213 | + assertThat(period.getAdDurationUs(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1)) |
| 214 | + .isEqualTo(100_000); |
| 215 | + assertThat(period.getAdDurationUs(/* adGroupIndex= */ 2, /* adIndexInAdGroup= */ 0)) |
| 216 | + .isEqualTo(400_000); |
| 217 | + assertThat(period.getContentResumeOffsetUs(/* adGroupIndex= */ 0)).isEqualTo(100_000); |
| 218 | + assertThat(period.getContentResumeOffsetUs(/* adGroupIndex= */ 1)).isEqualTo(400_000); |
| 219 | + assertThat(period.getContentResumeOffsetUs(/* adGroupIndex= */ 2)).isEqualTo(200_000); |
| 220 | + assertThat(period.getDurationUs()).isEqualTo(C.TIME_UNSET); |
| 221 | + // positionInWindowUs + sum(adDurationsBeforeWindow) - sum(contentResumeOffsetsBeforeWindow) |
| 222 | + assertThat(period.getPositionInWindowUs()).isEqualTo(-41_600_000); |
| 223 | + Timeline.Window window = timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window()); |
| 224 | + assertThat(window.positionInFirstPeriodUs).isEqualTo(41_600_000); |
| 225 | + // windowDurationUs - sum(adDurationsInWindow) + sum(applicableContentResumeOffsetUs) |
| 226 | + assertThat(window.durationUs).isEqualTo(9_800_000); |
| 227 | + } |
| 228 | + |
150 | 229 | @Test
|
151 | 230 | public void timeline_missingAdPlaybackStateByPeriodUid_isAssertedAndThrows() {
|
152 | 231 | ServerSideAdInsertionMediaSource mediaSource =
|
|
0 commit comments