mirror of
https://github.com/androidx/media.git
synced 2025-05-09 16:40:55 +08:00
Propagate RemoteMediaClient's current duration to timeline if necessary
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=183390851
This commit is contained in:
parent
8ba3335145
commit
ac630b616c
@ -38,6 +38,10 @@ dependencies {
|
|||||||
compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion
|
compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion
|
||||||
compile project(modulePrefix + 'library-core')
|
compile project(modulePrefix + 'library-core')
|
||||||
compile project(modulePrefix + 'library-ui')
|
compile project(modulePrefix + 'library-ui')
|
||||||
|
compile project(modulePrefix + 'testutils')
|
||||||
|
testCompile 'junit:junit:' + junitVersion
|
||||||
|
testCompile 'org.mockito:mockito-core:' + mockitoVersion
|
||||||
|
testCompile 'org.robolectric:robolectric:' + robolectricVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -91,6 +91,8 @@ public final class CastPlayer implements Player {
|
|||||||
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0];
|
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0];
|
||||||
|
|
||||||
private final CastContext castContext;
|
private final CastContext castContext;
|
||||||
|
// TODO: Allow custom implementations of CastTimelineTracker.
|
||||||
|
private final CastTimelineTracker timelineTracker;
|
||||||
private final Timeline.Window window;
|
private final Timeline.Window window;
|
||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
|
|
||||||
@ -123,6 +125,7 @@ public final class CastPlayer implements Player {
|
|||||||
*/
|
*/
|
||||||
public CastPlayer(CastContext castContext) {
|
public CastPlayer(CastContext castContext) {
|
||||||
this.castContext = castContext;
|
this.castContext = castContext;
|
||||||
|
timelineTracker = new CastTimelineTracker();
|
||||||
window = new Timeline.Window();
|
window = new Timeline.Window();
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
statusListener = new StatusListener();
|
statusListener = new StatusListener();
|
||||||
@ -487,9 +490,11 @@ public final class CastPlayer implements Player {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getCurrentPosition() {
|
public long getCurrentPosition() {
|
||||||
return pendingSeekPositionMs != C.TIME_UNSET ? pendingSeekPositionMs
|
return pendingSeekPositionMs != C.TIME_UNSET
|
||||||
: remoteMediaClient != null ? remoteMediaClient.getApproximateStreamPosition()
|
? pendingSeekPositionMs
|
||||||
: lastReportedPositionMs;
|
: remoteMediaClient != null
|
||||||
|
? remoteMediaClient.getApproximateStreamPosition()
|
||||||
|
: lastReportedPositionMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -501,9 +506,9 @@ public final class CastPlayer implements Player {
|
|||||||
public int getBufferedPercentage() {
|
public int getBufferedPercentage() {
|
||||||
long position = getBufferedPosition();
|
long position = getBufferedPosition();
|
||||||
long duration = getDuration();
|
long duration = getDuration();
|
||||||
return position == C.TIME_UNSET || duration == C.TIME_UNSET ? 0
|
return position == C.TIME_UNSET || duration == C.TIME_UNSET
|
||||||
: duration == 0 ? 100
|
? 0
|
||||||
: Util.constrainValue((int) ((position * 100) / duration), 0, 100);
|
: duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -598,20 +603,11 @@ public final class CastPlayer implements Player {
|
|||||||
* Updates the current timeline and returns whether it has changed.
|
* Updates the current timeline and returns whether it has changed.
|
||||||
*/
|
*/
|
||||||
private boolean updateTimeline() {
|
private boolean updateTimeline() {
|
||||||
MediaStatus mediaStatus = getMediaStatus();
|
CastTimeline oldTimeline = currentTimeline;
|
||||||
if (mediaStatus == null) {
|
MediaStatus status = getMediaStatus();
|
||||||
boolean hasChanged = currentTimeline != CastTimeline.EMPTY_CAST_TIMELINE;
|
currentTimeline =
|
||||||
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
|
status != null ? timelineTracker.getCastTimeline(status) : CastTimeline.EMPTY_CAST_TIMELINE;
|
||||||
return hasChanged;
|
return !oldTimeline.equals(currentTimeline);
|
||||||
}
|
|
||||||
|
|
||||||
List<MediaQueueItem> items = mediaStatus.getQueueItems();
|
|
||||||
if (!currentTimeline.represents(items)) {
|
|
||||||
currentTimeline = !items.isEmpty() ? new CastTimeline(mediaStatus.getQueueItems())
|
|
||||||
: CastTimeline.EMPTY_CAST_TIMELINE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -755,10 +751,11 @@ public final class CastPlayer implements Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getRendererIndexForTrackType(int trackType) {
|
private static int getRendererIndexForTrackType(int trackType) {
|
||||||
return trackType == C.TRACK_TYPE_VIDEO ? RENDERER_INDEX_VIDEO
|
return trackType == C.TRACK_TYPE_VIDEO
|
||||||
: trackType == C.TRACK_TYPE_AUDIO ? RENDERER_INDEX_AUDIO
|
? RENDERER_INDEX_VIDEO
|
||||||
: trackType == C.TRACK_TYPE_TEXT ? RENDERER_INDEX_TEXT
|
: trackType == C.TRACK_TYPE_AUDIO
|
||||||
: C.INDEX_UNSET;
|
? RENDERER_INDEX_AUDIO
|
||||||
|
: trackType == C.TRACK_TYPE_TEXT ? RENDERER_INDEX_TEXT : C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getCastRepeatMode(@RepeatMode int repeatMode) {
|
private static int getCastRepeatMode(@RepeatMode int repeatMode) {
|
||||||
|
@ -20,8 +20,10 @@ import com.google.android.exoplayer2.C;
|
|||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.gms.cast.MediaInfo;
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
import com.google.android.gms.cast.MediaQueueItem;
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Timeline} for Cast media queues.
|
* A {@link Timeline} for Cast media queues.
|
||||||
@ -29,14 +31,19 @@ import java.util.List;
|
|||||||
/* package */ final class CastTimeline extends Timeline {
|
/* package */ final class CastTimeline extends Timeline {
|
||||||
|
|
||||||
public static final CastTimeline EMPTY_CAST_TIMELINE =
|
public static final CastTimeline EMPTY_CAST_TIMELINE =
|
||||||
new CastTimeline(Collections.<MediaQueueItem>emptyList());
|
new CastTimeline(
|
||||||
|
Collections.<MediaQueueItem>emptyList(), Collections.<String, Long>emptyMap());
|
||||||
|
|
||||||
private final SparseIntArray idsToIndex;
|
private final SparseIntArray idsToIndex;
|
||||||
private final int[] ids;
|
private final int[] ids;
|
||||||
private final long[] durationsUs;
|
private final long[] durationsUs;
|
||||||
private final long[] defaultPositionsUs;
|
private final long[] defaultPositionsUs;
|
||||||
|
|
||||||
public CastTimeline(List<MediaQueueItem> items) {
|
/**
|
||||||
|
* @param items A list of cast media queue items to represent.
|
||||||
|
* @param contentIdToDurationUsMap A map of content id to duration in microseconds.
|
||||||
|
*/
|
||||||
|
public CastTimeline(List<MediaQueueItem> items, Map<String, Long> contentIdToDurationUsMap) {
|
||||||
int itemCount = items.size();
|
int itemCount = items.size();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
idsToIndex = new SparseIntArray(itemCount);
|
idsToIndex = new SparseIntArray(itemCount);
|
||||||
@ -47,12 +54,19 @@ import java.util.List;
|
|||||||
int itemId = item.getItemId();
|
int itemId = item.getItemId();
|
||||||
ids[index] = itemId;
|
ids[index] = itemId;
|
||||||
idsToIndex.put(itemId, index);
|
idsToIndex.put(itemId, index);
|
||||||
durationsUs[index] = getStreamDurationUs(item.getMedia());
|
MediaInfo mediaInfo = item.getMedia();
|
||||||
|
String contentId = mediaInfo.getContentId();
|
||||||
|
durationsUs[index] =
|
||||||
|
contentIdToDurationUsMap.containsKey(contentId)
|
||||||
|
? contentIdToDurationUsMap.get(contentId)
|
||||||
|
: CastUtils.getStreamDurationUs(mediaInfo);
|
||||||
defaultPositionsUs[index] = (long) (item.getStartTime() * C.MICROS_PER_SECOND);
|
defaultPositionsUs[index] = (long) (item.getStartTime() * C.MICROS_PER_SECOND);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timeline implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getWindowCount() {
|
public int getWindowCount() {
|
||||||
return ids.length;
|
return ids.length;
|
||||||
@ -83,32 +97,27 @@ import java.util.List;
|
|||||||
return uid instanceof Integer ? idsToIndex.get((int) uid, C.INDEX_UNSET) : C.INDEX_UNSET;
|
return uid instanceof Integer ? idsToIndex.get((int) uid, C.INDEX_UNSET) : C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// equals and hashCode implementations.
|
||||||
* Returns whether the timeline represents a given {@code MediaQueueItem} list.
|
|
||||||
*
|
@Override
|
||||||
* @param items The {@code MediaQueueItem} list.
|
public boolean equals(Object other) {
|
||||||
* @return Whether the timeline represents {@code items}.
|
if (this == other) {
|
||||||
*/
|
return true;
|
||||||
/* package */ boolean represents(List<MediaQueueItem> items) {
|
} else if (!(other instanceof CastTimeline)) {
|
||||||
if (ids.length != items.size()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int index = 0;
|
CastTimeline that = (CastTimeline) other;
|
||||||
for (MediaQueueItem item : items) {
|
return Arrays.equals(ids, that.ids)
|
||||||
if (ids[index] != item.getItemId()
|
&& Arrays.equals(durationsUs, that.durationsUs)
|
||||||
|| durationsUs[index] != getStreamDurationUs(item.getMedia())
|
&& Arrays.equals(defaultPositionsUs, that.defaultPositionsUs);
|
||||||
|| defaultPositionsUs[index] != (long) (item.getStartTime() * C.MICROS_PER_SECOND)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long getStreamDurationUs(MediaInfo mediaInfo) {
|
@Override
|
||||||
long durationMs = mediaInfo != null ? mediaInfo.getStreamDuration()
|
public int hashCode() {
|
||||||
: MediaInfo.UNKNOWN_DURATION;
|
int result = Arrays.hashCode(ids);
|
||||||
return durationMs != MediaInfo.UNKNOWN_DURATION ? C.msToUs(durationMs) : C.TIME_UNSET;
|
result = 31 * result + Arrays.hashCode(durationsUs);
|
||||||
|
result = 31 * result + Arrays.hashCode(defaultPositionsUs);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.ext.cast;
|
||||||
|
|
||||||
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
|
import com.google.android.gms.cast.MediaStatus;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates {@link CastTimeline}s from cast receiver app media status.
|
||||||
|
*
|
||||||
|
* <p>This class keeps track of the duration reported by the current item to fill any missing
|
||||||
|
* durations in the media queue items [See internal: b/65152553].
|
||||||
|
*/
|
||||||
|
/* package */ final class CastTimelineTracker {
|
||||||
|
|
||||||
|
private final HashMap<String, Long> contentIdToDurationUsMap;
|
||||||
|
private final HashSet<String> scratchContentIdSet;
|
||||||
|
|
||||||
|
public CastTimelineTracker() {
|
||||||
|
contentIdToDurationUsMap = new HashMap<>();
|
||||||
|
scratchContentIdSet = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link CastTimeline} that represent the given {@code status}.
|
||||||
|
*
|
||||||
|
* @param status The Cast media status.
|
||||||
|
* @return A {@link CastTimeline} that represent the given {@code status}.
|
||||||
|
*/
|
||||||
|
public CastTimeline getCastTimeline(MediaStatus status) {
|
||||||
|
MediaInfo mediaInfo = status.getMediaInfo();
|
||||||
|
List<MediaQueueItem> items = status.getQueueItems();
|
||||||
|
removeUnusedDurationEntries(items);
|
||||||
|
|
||||||
|
if (mediaInfo != null) {
|
||||||
|
String contentId = mediaInfo.getContentId();
|
||||||
|
long durationUs = CastUtils.getStreamDurationUs(mediaInfo);
|
||||||
|
contentIdToDurationUsMap.put(contentId, durationUs);
|
||||||
|
}
|
||||||
|
return new CastTimeline(items, contentIdToDurationUsMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeUnusedDurationEntries(List<MediaQueueItem> items) {
|
||||||
|
scratchContentIdSet.clear();
|
||||||
|
for (MediaQueueItem item : items) {
|
||||||
|
scratchContentIdSet.add(item.getMedia().getContentId());
|
||||||
|
}
|
||||||
|
contentIdToDurationUsMap.keySet().retainAll(scratchContentIdSet);
|
||||||
|
}
|
||||||
|
}
|
@ -15,8 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.cast;
|
package com.google.android.exoplayer2.ext.cast;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.gms.cast.CastStatusCodes;
|
import com.google.android.gms.cast.CastStatusCodes;
|
||||||
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
import com.google.android.gms.cast.MediaTrack;
|
import com.google.android.gms.cast.MediaTrack;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -29,6 +31,19 @@ import java.util.Map;
|
|||||||
|
|
||||||
private static final Map<Integer, String> CAST_STATUS_CODE_TO_STRING;
|
private static final Map<Integer, String> CAST_STATUS_CODE_TO_STRING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the duration in microseconds advertised by a media info, or {@link C#TIME_UNSET} if
|
||||||
|
* unknown or not applicable.
|
||||||
|
*
|
||||||
|
* @param mediaInfo The media info to get the duration from.
|
||||||
|
* @return The duration in microseconds.
|
||||||
|
*/
|
||||||
|
public static long getStreamDurationUs(MediaInfo mediaInfo) {
|
||||||
|
long durationMs =
|
||||||
|
mediaInfo != null ? mediaInfo.getStreamDuration() : MediaInfo.UNKNOWN_DURATION;
|
||||||
|
return durationMs != MediaInfo.UNKNOWN_DURATION ? C.msToUs(durationMs) : C.TIME_UNSET;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a descriptive log string for the given {@code statusCode}, or "Unknown." if not one of
|
* Returns a descriptive log string for the given {@code statusCode}, or "Unknown." if not one of
|
||||||
* {@link CastStatusCodes}.
|
* {@link CastStatusCodes}.
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.ext.cast;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
|
import com.google.android.gms.cast.MediaStatus;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
/** Tests for {@link CastTimelineTracker}. */
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE)
|
||||||
|
public class CastTimelineTrackerTest {
|
||||||
|
|
||||||
|
private static final long DURATION_1_MS = 1000;
|
||||||
|
private static final long DURATION_2_MS = 2000;
|
||||||
|
private static final long DURATION_3_MS = 3000;
|
||||||
|
private static final long DURATION_4_MS = 4000;
|
||||||
|
private static final long DURATION_5_MS = 5000;
|
||||||
|
|
||||||
|
/** Tests that duration of the current media info is correctly propagated to the timeline. */
|
||||||
|
@Test
|
||||||
|
public void testGetCastTimeline() {
|
||||||
|
MediaInfo mediaInfo;
|
||||||
|
MediaStatus status =
|
||||||
|
mockMediaStatus(
|
||||||
|
new int[] {1, 2, 3},
|
||||||
|
new String[] {"contentId1", "contentId2", "contentId3"},
|
||||||
|
new long[] {DURATION_1_MS, MediaInfo.UNKNOWN_DURATION, MediaInfo.UNKNOWN_DURATION});
|
||||||
|
|
||||||
|
CastTimelineTracker tracker = new CastTimelineTracker();
|
||||||
|
mediaInfo = mockMediaInfo("contentId1", DURATION_1_MS);
|
||||||
|
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
|
TimelineAsserts.assertPeriodDurations(
|
||||||
|
tracker.getCastTimeline(status), C.msToUs(DURATION_1_MS), C.TIME_UNSET, C.TIME_UNSET);
|
||||||
|
|
||||||
|
mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS);
|
||||||
|
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
|
TimelineAsserts.assertPeriodDurations(
|
||||||
|
tracker.getCastTimeline(status),
|
||||||
|
C.msToUs(DURATION_1_MS),
|
||||||
|
C.TIME_UNSET,
|
||||||
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
|
mediaInfo = mockMediaInfo("contentId2", DURATION_2_MS);
|
||||||
|
Mockito.when(status.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
|
TimelineAsserts.assertPeriodDurations(
|
||||||
|
tracker.getCastTimeline(status),
|
||||||
|
C.msToUs(DURATION_1_MS),
|
||||||
|
C.msToUs(DURATION_2_MS),
|
||||||
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
|
MediaStatus newStatus =
|
||||||
|
mockMediaStatus(
|
||||||
|
new int[] {4, 1, 5, 3},
|
||||||
|
new String[] {"contentId4", "contentId1", "contentId5", "contentId3"},
|
||||||
|
new long[] {
|
||||||
|
MediaInfo.UNKNOWN_DURATION,
|
||||||
|
MediaInfo.UNKNOWN_DURATION,
|
||||||
|
DURATION_5_MS,
|
||||||
|
MediaInfo.UNKNOWN_DURATION
|
||||||
|
});
|
||||||
|
mediaInfo = mockMediaInfo("contentId5", DURATION_5_MS);
|
||||||
|
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
|
TimelineAsserts.assertPeriodDurations(
|
||||||
|
tracker.getCastTimeline(newStatus),
|
||||||
|
C.TIME_UNSET,
|
||||||
|
C.msToUs(DURATION_1_MS),
|
||||||
|
C.msToUs(DURATION_5_MS),
|
||||||
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
|
mediaInfo = mockMediaInfo("contentId3", DURATION_3_MS);
|
||||||
|
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
|
TimelineAsserts.assertPeriodDurations(
|
||||||
|
tracker.getCastTimeline(newStatus),
|
||||||
|
C.TIME_UNSET,
|
||||||
|
C.msToUs(DURATION_1_MS),
|
||||||
|
C.msToUs(DURATION_5_MS),
|
||||||
|
C.msToUs(DURATION_3_MS));
|
||||||
|
|
||||||
|
mediaInfo = mockMediaInfo("contentId4", DURATION_4_MS);
|
||||||
|
Mockito.when(newStatus.getMediaInfo()).thenReturn(mediaInfo);
|
||||||
|
TimelineAsserts.assertPeriodDurations(
|
||||||
|
tracker.getCastTimeline(newStatus),
|
||||||
|
C.msToUs(DURATION_4_MS),
|
||||||
|
C.msToUs(DURATION_1_MS),
|
||||||
|
C.msToUs(DURATION_5_MS),
|
||||||
|
C.msToUs(DURATION_3_MS));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MediaStatus mockMediaStatus(
|
||||||
|
int[] itemIds, String[] contentIds, long[] durationsMs) {
|
||||||
|
ArrayList<MediaQueueItem> items = new ArrayList<>();
|
||||||
|
for (int i = 0; i < contentIds.length; i++) {
|
||||||
|
MediaInfo mediaInfo = mockMediaInfo(contentIds[i], durationsMs[i]);
|
||||||
|
MediaQueueItem item = Mockito.mock(MediaQueueItem.class);
|
||||||
|
Mockito.when(item.getMedia()).thenReturn(mediaInfo);
|
||||||
|
Mockito.when(item.getItemId()).thenReturn(itemIds[i]);
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
MediaStatus status = Mockito.mock(MediaStatus.class);
|
||||||
|
Mockito.when(status.getQueueItems()).thenReturn(items);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MediaInfo mockMediaInfo(String contentId, long durationMs) {
|
||||||
|
MediaInfo mediaInfo = Mockito.mock(MediaInfo.class);
|
||||||
|
Mockito.when(mediaInfo.getContentId()).thenReturn(contentId);
|
||||||
|
Mockito.when(mediaInfo.getStreamDuration()).thenReturn(durationMs);
|
||||||
|
return mediaInfo;
|
||||||
|
}
|
||||||
|
}
|
@ -99,6 +99,19 @@ public final class TimelineAsserts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the durations of the periods in the {@link Timeline} and the durations in the
|
||||||
|
* given sequence are equal.
|
||||||
|
*/
|
||||||
|
public static void assertPeriodDurations(Timeline timeline, long... durationsUs) {
|
||||||
|
int periodCount = timeline.getPeriodCount();
|
||||||
|
assertThat(periodCount).isEqualTo(durationsUs.length);
|
||||||
|
Period period = new Period();
|
||||||
|
for (int i = 0; i < periodCount; i++) {
|
||||||
|
assertThat(timeline.getPeriod(i, period).durationUs).isEqualTo(durationsUs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that period counts for each window are set correctly. Also asserts that
|
* Asserts that period counts for each window are set correctly. Also asserts that
|
||||||
* {@link Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it
|
* {@link Window#firstPeriodIndex} and {@link Window#lastPeriodIndex} are set correctly, and it
|
||||||
|
Loading…
x
Reference in New Issue
Block a user