Skip to content

Commit

Permalink
Fix period indexing for multi-period DASH DAI live streams
Browse files Browse the repository at this point in the history
The period index was calculated relative to the contentTimeline
of the DAI stream, but then used with the full timeline of the
player. This means it currently only works when the stream is the
only or first item in the playlist.

Issue: androidx#571
PiperOrigin-RevId: 560023412
  • Loading branch information
tonihei authored and microkatz committed Sep 29, 2023
1 parent b464061 commit 4dedd4b
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 70 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
`VideoFrameProcessor.queueInputBitmap` to `TimestampIterator`.
* Muxers:
* IMA extension:
* Fix bug where a multi-period DASH live stream that is not the first item
in a playlist can throw an exception
([#571](https:/androidx/media/issues/571)).
* Session:
* Set the notifications foreground service behavior to
`FOREGROUND_SERVICE_IMMEDIATE` in `DefaultMediaNotificationProvider`
Expand Down
28 changes: 28 additions & 0 deletions demos/main/src/main/assets/media.exolist.json
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,34 @@
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - DASH live: Tears of Steel (mid) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?assetKey=PSzZMzAkSXCmlJOWDmRj8Q&format=0&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - HLS live: Big Buck Bunny - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -893,17 +893,13 @@ public void onPositionDiscontinuity(
return;
}
// Map adGroupIndex and adIndexInAdGroup to multi-period window.
int periodIndexInContentTimeline = oldPosition.periodIndex - window.firstPeriodIndex;
Pair<Integer, Integer> adGroupIndexAndAdIndexInAdGroup =
window.isLive()
? getAdGroupAndIndexInLiveMultiPeriodTimeline(
oldPosition.mediaItemIndex,
oldPosition.periodIndex - window.firstPeriodIndex,
timeline,
adPlaybackState)
periodIndexInContentTimeline, adPlaybackState, checkNotNull(contentTimeline))
: getAdGroupAndIndexInVodMultiPeriodTimeline(
oldPosition.periodIndex - window.firstPeriodIndex,
adPlaybackState,
checkNotNull(contentTimeline));
periodIndexInContentTimeline, adPlaybackState, checkNotNull(contentTimeline));
adGroupIndex = adGroupIndexAndAdIndexInAdGroup.first;
adIndexInAdGroup = adGroupIndexAndAdIndexInAdGroup.second;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -801,22 +801,16 @@ private static AdPlaybackState markAdGroupAsPlayed(
* index in the timeline and the {@code adIndexInAdGroup} of the first unplayed ad in that ad
* group.
*
* @param currentMediaItemIndex The {@linkplain Player#getCurrentMediaItemIndex() current media
* item index}.
* @param adPeriodIndex The index of the ad period in the timeline.
* @param timeline The timeline that contains the period for the ad period index.
* @param adPlaybackState The ad playback state that holds the ad group and ad information.
* @param timeline The single-window timeline that contains the period for the ad period index.
* @return A pair with the ad group index (first) and the ad index in that ad group (second).
* @exception IllegalStateException If no unplayed ad is found before or at the start time of the
* ad period.
*/
public static Pair<Integer, Integer> getAdGroupAndIndexInLiveMultiPeriodTimeline(
int currentMediaItemIndex,
int adPeriodIndex,
Timeline timeline,
AdPlaybackState adPlaybackState) {
Timeline.Window window =
timeline.getWindow(/* windowIndex= */ currentMediaItemIndex, new Timeline.Window());
int adPeriodIndex, AdPlaybackState adPlaybackState, Timeline timeline) {
Timeline.Window window = timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window());
checkArgument(window.isLive());
Timeline.Period period = new Timeline.Period();
timeline.getPeriod(adPeriodIndex, period, /* setIds= */ true);
Expand Down Expand Up @@ -846,13 +840,13 @@ public static Pair<Integer, Integer> getAdGroupAndIndexInLiveMultiPeriodTimeline
*
* @param adPeriodIndex The period index of the ad period.
* @param adPlaybackState The ad playback state that holds the ad group and ad information.
* @param contentTimeline The timeline that contains the ad period.
* @param timeline The single-window timeline that contains the ad period.
* @return A pair with the ad group index (first) and the ad index in that ad group (second).
*/
public static Pair<Integer, Integer> getAdGroupAndIndexInVodMultiPeriodTimeline(
int adPeriodIndex, AdPlaybackState adPlaybackState, Timeline contentTimeline) {
Timeline.Window window = contentTimeline.getWindow(/* windowIndex= */ 0, new Timeline.Window());
checkArgument(contentTimeline.getWindowCount() == 1);
int adPeriodIndex, AdPlaybackState adPlaybackState, Timeline timeline) {
Timeline.Window window = timeline.getWindow(/* windowIndex= */ 0, new Timeline.Window());
checkArgument(timeline.getWindowCount() == 1);
int periodIndex = 0;
long totalElapsedContentDurationUs = 0;
if (window.isLive()) {
Expand All @@ -866,8 +860,8 @@ public static Pair<Integer, Integer> getAdGroupAndIndexInVodMultiPeriodTimeline(
AdGroup adGroup = adPlaybackState.getAdGroup(/* adGroupIndex= */ i);
long adGroupDurationUs = sum(adGroup.durationsUs);
long elapsedAdGroupAdDurationUs = 0;
for (int j = periodIndex; j < min(contentTimeline.getPeriodCount(), adPeriodIndex + 1); j++) {
contentTimeline.getPeriod(j, period, /* setIds= */ true);
for (int j = periodIndex; j < min(timeline.getPeriodCount(), adPeriodIndex + 1); j++) {
timeline.getPeriod(j, period, /* setIds= */ true);
if (totalElapsedContentDurationUs < adGroup.timeUs) {
// Period starts before the ad group, so it is a content period.
totalElapsedContentDurationUs += period.durationUs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2046,10 +2046,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 0,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 0, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(0, 0));

adPlaybackState =
Expand Down Expand Up @@ -2081,32 +2078,23 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 2,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 2, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(1, 0));

adPlaybackState =
adPlaybackState.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0);

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 3,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 3, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(1, 1));

adPlaybackState =
adPlaybackState.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 1);

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 4,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 4, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(1, 2));

adPlaybackState =
Expand All @@ -2122,10 +2110,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 6,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 6, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(2, 0));
}

Expand Down Expand Up @@ -2191,10 +2176,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI
IllegalStateException.class,
() ->
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 1,
contentTimeline,
finalAdPlaybackState));
/* adPeriodIndex= */ 1, finalAdPlaybackState, contentTimeline));

adPlaybackState =
adPlaybackState.withPlayedAd(/* adGroupIndex= */ 1, /* adIndexInAdGroup= */ 0);
Expand All @@ -2216,10 +2198,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI
IllegalStateException.class,
() ->
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 5,
contentTimeline,
anotherFinalAdPlaybackState));
/* adPeriodIndex= */ 5, anotherFinalAdPlaybackState, contentTimeline));
}

@Test
Expand Down Expand Up @@ -2275,10 +2254,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 0,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 0, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(0, 2));
}

Expand Down Expand Up @@ -2330,10 +2306,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 0,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 0, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(0, 1));

// Ad event for second ad in window arrives.
Expand All @@ -2350,10 +2323,7 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI

assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 1,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 1, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(0, 2));

// Move one ad period forward: c, a, a, [a, a, a, a], c
Expand All @@ -2362,19 +2332,13 @@ public void getAdGroupAndIndexInVodMultiPeriodTimeline_correctAdGroupIndexAndAdI
adPlaybackState.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 2);
assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 1,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 1, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(0, 3));
adPlaybackState =
adPlaybackState.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 3);
assertThat(
getAdGroupAndIndexInLiveMultiPeriodTimeline(
/* currentMediaItemIndex= */ 0,
/* adPeriodIndex= */ 2,
contentTimeline,
adPlaybackState))
/* adPeriodIndex= */ 2, adPlaybackState, contentTimeline))
.isEqualTo(new Pair<>(0, 4));
}

Expand Down

0 comments on commit 4dedd4b

Please sign in to comment.