mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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:
parent
2a0ead1b29
commit
7d5558881d
6 changed files with 114 additions and 14 deletions
|
|
@ -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;
|
||||||
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 @@
|
||||||
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 @@
|
||||||
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 {
|
||||||
* 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
|
||||||
@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
|
||||||
|| (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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -598,6 +598,43 @@ public final class DefaultPlaybackSessionManagerTest {
|
||||||
assertThat(updatedSessionId300).isEqualTo(sessionId300);
|
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
|
@Test
|
||||||
public void positionDiscontinuity_withinWindow_doesNotFinishSession() {
|
public void positionDiscontinuity_withinWindow_doesNotFinishSession() {
|
||||||
Timeline timeline =
|
Timeline timeline =
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue