Commit 7d555888 by tonihei Committed by Oliver Woodman

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
parent 2a0ead1b
...@@ -63,7 +63,7 @@ android { ...@@ -63,7 +63,7 @@ android {
dependencies { dependencies {
implementation 'androidx.annotation:annotation:1.0.2' 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 'androidx.fragment:fragment:1.0.0'
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.0.0'
implementation project(modulePrefix + 'library-core') implementation project(modulePrefix + 'library-core')
......
...@@ -25,7 +25,7 @@ import androidx.fragment.app.DialogFragment; ...@@ -25,7 +25,7 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager; import android.support.v4.view.ViewPager;
import androidx.appcompat.app.AppCompatDialog; import androidx.appcompat.app.AppCompatDialog;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.LayoutInflater; import android.view.LayoutInflater;
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="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:id="@+id/track_selection_dialog_view_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
app:tabGravity="fill" app:tabGravity="fill"
app:tabMode="fixed"/> app:tabMode="fixed"/>
</androidx.viewpager.widget.ViewPager> </android.support.v4.view.ViewPager>
<LinearLayout <LinearLayout
android:orientation="horizontal" android:orientation="horizontal"
......
...@@ -396,7 +396,8 @@ public abstract class Timeline { ...@@ -396,7 +396,8 @@ public abstract class Timeline {
* microseconds. * microseconds.
* *
* @param adGroupIndex The ad group index. * @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) { public long getAdGroupTimeUs(int adGroupIndex) {
return adPlaybackState.adGroupTimesUs[adGroupIndex]; return adPlaybackState.adGroupTimesUs[adGroupIndex];
......
...@@ -202,10 +202,12 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag ...@@ -202,10 +202,12 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
@RequiresNonNull("listener") @RequiresNonNull("listener")
private void updateActiveSession(EventTime eventTime, SessionDescriptor sessionDescriptor) { private void updateActiveSession(EventTime eventTime, SessionDescriptor sessionDescriptor) {
currentMediaPeriodId = eventTime.mediaPeriodId; currentMediaPeriodId = eventTime.mediaPeriodId;
if (sessionDescriptor.isCreated && !sessionDescriptor.isActive) { if (sessionDescriptor.isCreated) {
sessionDescriptor.isActive = true;
activeSessionId = sessionDescriptor.sessionId; 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 ...@@ -326,13 +328,9 @@ public final class DefaultPlaybackSessionManager implements PlaybackSessionManag
|| (eventAdGroup == adMediaPeriodId.adGroupIndex || (eventAdGroup == adMediaPeriodId.adGroupIndex
&& eventAdIndex > adMediaPeriodId.adIndexInAdGroup); && eventAdIndex > adMediaPeriodId.adIndexInAdGroup);
} else { } 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. // 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;
} }
} }
......
...@@ -599,6 +599,43 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -599,6 +599,43 @@ public final class DefaultPlaybackSessionManagerTest {
} }
@Test @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() { public void positionDiscontinuity_withinWindow_doesNotFinishSession() {
Timeline timeline = Timeline timeline =
new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 100)); new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 100));
...@@ -943,6 +980,70 @@ public final class DefaultPlaybackSessionManagerTest { ...@@ -943,6 +980,70 @@ public final class DefaultPlaybackSessionManagerTest {
verifyNoMoreInteractions(mockListener); 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( private static EventTime createEventTime(
Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { Timeline timeline, int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
return new EventTime( return new EventTime(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment