private MlltSeeker(long[] referencePositions, long[] referenceTimesMs) { this.referencePositions = referencePositions; this.referenceTimesMs = referenceTimesMs; // Use the last reference point as the duration, as extrapolating variable bitrate at the end of // the stream may give a large error. durationUs = C.msToUs(referenceTimesMs[referenceTimesMs.length - 1]); }
/** * 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; }
@Override public long getTimeUs(long position) { Pair<Long, Long> positionAndTimeMs = linearlyInterpolate(position, referencePositions, referenceTimesMs); return C.msToUs(positionAndTimeMs.second); }
public final long getPeriodDurationUs(int index) { return C.msToUs(getPeriodDurationMs(index)); }
public long getFirstAvailableSegmentNum( DashManifest manifest, int periodIndex, long nowUnixTimeUs) { if (getSegmentCount() == DashSegmentIndex.INDEX_UNBOUNDED && manifest.timeShiftBufferDepthMs != C.TIME_UNSET) { // The index is itself unbounded. We need to use the current time to calculate the range of // available segments. long liveEdgeTimeUs = nowUnixTimeUs - C.msToUs(manifest.availabilityStartTimeMs); long periodStartUs = C.msToUs(manifest.getPeriod(periodIndex).startMs); long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs; long bufferDepthUs = C.msToUs(manifest.timeShiftBufferDepthMs); return Math.max( getFirstSegmentNum(), getSegmentNum(liveEdgeTimeInPeriodUs - bufferDepthUs)); } return getFirstSegmentNum(); }
@Override public long getPositionUs() { long positionUs = baseUs; if (started) { long elapsedSinceBaseMs = clock.elapsedRealtime() - baseElapsedMs; if (playbackParameters.speed == 1f) { positionUs += C.msToUs(elapsedSinceBaseMs); } else { positionUs += playbackParameters.getMediaTimeUsForPlayoutTimeMs(elapsedSinceBaseMs); } } return positionUs; }
public long getLastAvailableSegmentNum( DashManifest manifest, int periodIndex, long nowUnixTimeUs) { int availableSegmentCount = getSegmentCount(); if (availableSegmentCount == DashSegmentIndex.INDEX_UNBOUNDED) { // The index is itself unbounded. We need to use the current time to calculate the range of // available segments. long liveEdgeTimeUs = nowUnixTimeUs - C.msToUs(manifest.availabilityStartTimeMs); long periodStartUs = C.msToUs(manifest.getPeriod(periodIndex).startMs); long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs; // getSegmentNum(liveEdgeTimeInPeriodUs) will not be completed yet, so subtract one to get // the index of the last completed segment. return getSegmentNum(liveEdgeTimeInPeriodUs) - 1; } return getFirstSegmentNum() + availableSegmentCount - 1; }
@Override protected List<Segment> getSegments( DataSource dataSource, DashManifest manifest, boolean allowIncompleteList) throws InterruptedException, IOException { ArrayList<Segment> segments = new ArrayList<>(); for (int i = 0; i < manifest.getPeriodCount(); i++) { Period period = manifest.getPeriod(i); long periodStartUs = C.msToUs(period.startMs); long periodDurationUs = manifest.getPeriodDurationUs(i); List<AdaptationSet> adaptationSets = period.adaptationSets; for (int j = 0; j < adaptationSets.size(); j++) { addSegmentsForAdaptationSet( dataSource, adaptationSets.get(j), periodStartUs, periodDurationUs, allowIncompleteList, segments); } } return segments; }
private void checkForContentComplete() { if (contentDurationMs != C.TIME_UNSET && pendingContentPositionMs == C.TIME_UNSET && player.getContentPosition() + END_OF_CONTENT_POSITION_THRESHOLD_MS >= contentDurationMs && !sentContentComplete) { adsLoader.contentComplete(); if (DEBUG) { Log.d(TAG, "adsLoader.contentComplete"); } sentContentComplete = true; // After sending content complete IMA will not poll the content position, so set the expected // ad group index. expectedAdGroupIndex = adPlaybackState.getAdGroupIndexForPositionUs(C.msToUs(contentDurationMs)); } }
@Test public void testEventTimeWithinClippedRange() throws IOException { MediaLoadData mediaLoadData = getClippingMediaSourceMediaLoadData( /* clippingStartUs= */ TEST_CLIP_AMOUNT_US, /* clippingEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US, /* eventStartUs= */ TEST_CLIP_AMOUNT_US + 1000, /* eventEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US - 1000); assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(1000); assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs)) .isEqualTo(TEST_PERIOD_DURATION_US - 2 * TEST_CLIP_AMOUNT_US - 1000); }
@Test public void testEventTimeOutsideClippedRange() throws IOException { MediaLoadData mediaLoadData = getClippingMediaSourceMediaLoadData( /* clippingStartUs= */ TEST_CLIP_AMOUNT_US, /* clippingEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US, /* eventStartUs= */ TEST_CLIP_AMOUNT_US - 1000, /* eventEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US + 1000); assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(0); assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs)) .isEqualTo(TEST_PERIOD_DURATION_US - 2 * TEST_CLIP_AMOUNT_US); }
@Test public void testUnsetEventTime() throws IOException { MediaLoadData mediaLoadData = getClippingMediaSourceMediaLoadData( /* clippingStartUs= */ TEST_CLIP_AMOUNT_US, /* clippingEndUs= */ TEST_PERIOD_DURATION_US - TEST_CLIP_AMOUNT_US, /* eventStartUs= */ C.TIME_UNSET, /* eventEndUs= */ C.TIME_UNSET); assertThat(C.msToUs(mediaLoadData.mediaStartTimeMs)).isEqualTo(C.TIME_UNSET); assertThat(C.msToUs(mediaLoadData.mediaEndTimeMs)).isEqualTo(C.TIME_UNSET); }
@Override public SeekPoints getSeekPoints(long timeUs) { timeUs = Util.constrainValue(timeUs, 0, durationUs); Pair<Long, Long> timeMsAndPosition = linearlyInterpolate(C.usToMs(timeUs), referenceTimesMs, referencePositions); timeUs = C.msToUs(timeMsAndPosition.first); long position = timeMsAndPosition.second; return new SeekPoints(new SeekPoint(timeUs, position)); }
@Override public Period getPeriod(int periodIndex, Period period, boolean setIdentifiers) { Assertions.checkIndex(periodIndex, 0, getPeriodCount()); Object id = setIdentifiers ? manifest.getPeriod(periodIndex).id : null; Object uid = setIdentifiers ? (firstPeriodId + periodIndex) : null; return period.set(id, uid, 0, manifest.getPeriodDurationUs(periodIndex), C.msToUs(manifest.getPeriod(periodIndex).startMs - manifest.getPeriod(0).startMs) - offsetInFirstPeriodUs); }
@Override public void detachPlayer() { if (adsManager != null && imaPausedContent) { adPlaybackState = adPlaybackState.withAdResumePositionUs( playingAd ? C.msToUs(player.getCurrentPosition()) : 0); adsManager.pause(); } lastVolumePercentage = getVolume(); lastAdProgress = getAdProgress(); lastContentProgress = getContentProgress(); player.removeListener(this); player = null; eventListener = null; }
@Test public void staleDisableRendererClock_shouldNotThrow() throws ExoPlaybackException { MediaClockRenderer mediaClockRenderer = new MediaClockRenderer(); mediaClockRenderer.positionUs = TEST_POSITION_US; mediaClock.onRendererDisabled(mediaClockRenderer); assertThat(mediaClock.syncAndGetPositionUs()).isEqualTo(C.msToUs(fakeClock.elapsedRealtime())); }
private boolean resolvePendingMessagePosition(PendingMessageInfo pendingMessageInfo) { if (pendingMessageInfo.resolvedPeriodUid == null) { // Position is still unresolved. Try to find window in current timeline. Pair<Object, Long> periodPosition = resolveSeekPosition( new SeekPosition( pendingMessageInfo.message.getTimeline(), pendingMessageInfo.message.getWindowIndex(), C.msToUs(pendingMessageInfo.message.getPositionMs())), /* trySubsequentPeriods= */ false); if (periodPosition == null) { return false; } pendingMessageInfo.setResolvedPosition( playbackInfo.timeline.getIndexOfPeriod(periodPosition.first), periodPosition.second, periodPosition.first); } else { // Position has been resolved for a previous timeline. Try to find the updated period index. int index = playbackInfo.timeline.getIndexOfPeriod(pendingMessageInfo.resolvedPeriodUid); if (index == C.INDEX_UNSET) { return false; } pendingMessageInfo.resolvedPeriodIndex = index; } return true; }
@Override public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { if (adsManager == null) { return; } if (!playingAd && !player.isPlayingAd()) { checkForContentComplete(); if (sentContentComplete) { for (int i = 0; i < adPlaybackState.adGroupCount; i++) { if (adPlaybackState.adGroupTimesUs[i] != C.TIME_END_OF_SOURCE) { adPlaybackState = adPlaybackState.withSkippedAdGroup(i); } } updateAdPlaybackState(); } else { long positionMs = player.getCurrentPosition(); timeline.getPeriod(0, period); int newAdGroupIndex = period.getAdGroupIndexForPositionUs(C.msToUs(positionMs)); if (newAdGroupIndex != C.INDEX_UNSET) { sentPendingContentPositionMs = false; pendingContentPositionMs = positionMs; if (newAdGroupIndex != adGroupIndex) { shouldNotifyAdPrepareError = false; } } } } else { updateImaStateForPlayerState(); } }
@Test public void disableRendererMediaClock_standaloneShouldBeSynced() throws ExoPlaybackException { MediaClockRenderer mediaClockRenderer = new MediaClockRenderer(); mediaClock.start(); mediaClock.onRendererEnabled(mediaClockRenderer); mediaClockRenderer.positionUs = TEST_POSITION_US; mediaClock.syncAndGetPositionUs(); mediaClock.onRendererDisabled(mediaClockRenderer); fakeClock.advanceTime(SLEEP_TIME_MS); assertThat(mediaClock.syncAndGetPositionUs()) .isEqualTo(TEST_POSITION_US + C.msToUs(SLEEP_TIME_MS)); assertClockIsRunning(); }