Fix two ad insertion related bugs in DefaultPlaybackSessionManager.

1. A content session after an ad has been played was not re-marked as active,
   leading to new ad session being marked as active too early.
2. Switching from content to post-roll ended the content session because
   the return value of getAdGroupTimeUs of C.TIME_END_OF_SOURCE was not
   handled. Using the nextAdGroupIndex instead.

PiperOrigin-RevId: 246977327
This commit is contained in:
tonihei 2019-05-07 09:37:33 +01:00 committed by Oliver Woodman
parent 2a0ead1b29
commit 7d5558881d
6 changed files with 114 additions and 14 deletions

View File

@ -63,7 +63,7 @@ android {
dependencies {
implementation 'androidx.annotation:annotation:1.0.2'
implementation 'androidx.legacy:legacy-support-core-ui:1.0.0'
implementation 'com.android.support:support-core-ui:' + supportLibraryVersion
implementation 'androidx.fragment:fragment:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation project(modulePrefix + 'library-core')

View File

@ -25,7 +25,7 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.support.v4.view.ViewPager;
import androidx.appcompat.app.AppCompatDialog;
import android.util.SparseArray;
import android.view.LayoutInflater;

View File

@ -19,7 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager.widget.ViewPager
<android.support.v4.view.ViewPager
android:id="@+id/track_selection_dialog_view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
@ -32,7 +32,7 @@
app:tabGravity="fill"
app:tabMode="fixed"/>
</androidx.viewpager.widget.ViewPager>
</android.support.v4.view.ViewPager>
<LinearLayout
android:orientation="horizontal"

View File

@ -396,7 +396,8 @@ public abstract class Timeline {
* microseconds.
*
* @param adGroupIndex The ad group index.
* @return The time of the ad group at the index, in microseconds.
* @return The time of the ad group at the index, in microseconds, or {@link
* C#TIME_END_OF_SOURCE} for a post-roll ad group.
*/
public long getAdGroupTimeUs(int adGroupIndex) {
return adPlaybackState.adGroupTimesUs[adGroupIndex];

View File

@ -202,10 +202,12 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
@RequiresNonNull("listener")
private void updateActiveSession(EventTime eventTime, SessionDescriptor sessionDescriptor) {
currentMediaPeriodId = eventTime.mediaPeriodId;
if (sessionDescriptor.isCreated && !sessionDescriptor.isActive) {
sessionDescriptor.isActive = true;
if (sessionDescriptor.isCreated) {
activeSessionId = sessionDescriptor.sessionId;
listener.onSessionActive(eventTime, sessionDescriptor.sessionId);
if (!sessionDescriptor.isActive) {
sessionDescriptor.isActive = true;
listener.onSessionActive(eventTime, sessionDescriptor.sessionId);
}
}
}
@ -326,13 +328,9 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
|| (eventAdGroup == adMediaPeriodId.adGroupIndex
&& eventAdIndex > adMediaPeriodId.adIndexInAdGroup);
} else {
eventTime.timeline.getPeriod(adPeriodIndex, period);
long adGroupTimeMs =
adMediaPeriodId.adGroupIndex < period.getAdGroupCount()
? C.usToMs(period.getAdGroupTimeUs(adMediaPeriodId.adGroupIndex))
: 0;
// Finished if the event is for content after this ad.
return adGroupTimeMs <= eventTime.currentPlaybackPositionMs;
return eventTime.mediaPeriodId.nextAdGroupIndex == C.INDEX_UNSET
|| eventTime.mediaPeriodId.nextAdGroupIndex > adMediaPeriodId.adGroupIndex;
}
}

View File

@ -598,6 +598,43 @@ public final class DefaultPlaybackSessionManagerTest {
assertThat(updatedSessionId300).isEqualTo(sessionId300);
}
@Test
public void timelineUpdate_withContent_doesNotFinishFuturePostrollAd() {
Timeline adTimeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* durationUs =*/ 10 * C.MICROS_PER_SECOND,
new AdPlaybackState(/* adGroupTimesUs= */ C.TIME_END_OF_SOURCE)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)));
EventTime adEventTime =
createEventTime(
adTimeline,
/* windowIndex= */ 0,
new MediaPeriodId(
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 0));
EventTime contentEventTime =
createEventTime(
adTimeline,
/* windowIndex= */ 0,
new MediaPeriodId(
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
/* windowSequenceNumber= */ 0,
/* nextAdGroupIndex= */ 0));
sessionManager.updateSessions(contentEventTime);
sessionManager.updateSessions(adEventTime);
sessionManager.handleTimelineUpdate(contentEventTime);
verify(mockListener, never()).onSessionFinished(any(), anyString(), anyBoolean());
}
@Test
public void positionDiscontinuity_withinWindow_doesNotFinishSession() {
Timeline timeline =
@ -943,6 +980,70 @@ public final class DefaultPlaybackSessionManagerTest {
verifyNoMoreInteractions(mockListener);
}
@Test
public void
updateSessions_withNewAd_afterDiscontinuitiesFromContentToAdAndBack_doesNotActivateNewAd() {
Timeline adTimeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* durationUs =*/ 10 * C.MICROS_PER_SECOND,
new AdPlaybackState(
/* adGroupTimesUs= */ 2 * C.MICROS_PER_SECOND, 5 * C.MICROS_PER_SECOND)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdCount(/* adGroupIndex= */ 1, /* adCount= */ 1)));
EventTime adEventTime1 =
createEventTime(
adTimeline,
/* windowIndex= */ 0,
new MediaPeriodId(
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 0));
EventTime adEventTime2 =
createEventTime(
adTimeline,
/* windowIndex= */ 0,
new MediaPeriodId(
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
/* adGroupIndex= */ 1,
/* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 0));
EventTime contentEventTime1 =
createEventTime(
adTimeline,
/* windowIndex= */ 0,
new MediaPeriodId(
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
/* windowSequenceNumber= */ 0,
/* nextAdGroupIndex= */ 0));
EventTime contentEventTime2 =
createEventTime(
adTimeline,
/* windowIndex= */ 0,
new MediaPeriodId(
adTimeline.getUidOfPeriod(/* periodIndex= */ 0),
/* windowSequenceNumber= */ 0,
/* nextAdGroupIndex= */ 1));
sessionManager.handleTimelineUpdate(contentEventTime1);
sessionManager.updateSessions(contentEventTime1);
sessionManager.updateSessions(adEventTime1);
sessionManager.handlePositionDiscontinuity(
adEventTime1, Player.DISCONTINUITY_REASON_AD_INSERTION);
sessionManager.handlePositionDiscontinuity(
contentEventTime2, Player.DISCONTINUITY_REASON_AD_INSERTION);
String adSessionId2 =
sessionManager.getSessionForMediaPeriodId(adTimeline, adEventTime2.mediaPeriodId);
sessionManager.updateSessions(adEventTime2);
verify(mockListener, never()).onSessionActive(any(), eq(adSessionId2));
}
private static EventTime createEventTime(
Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
return new EventTime(