mirror of
https://github.com/samsonjs/media.git
synced 2026-04-02 10:45:51 +00:00
DASH: Fix detection of end of live events
The remaining work is to split Window.isDynamic so that it's possible to represent a window that wont be appended to, but may still be removed from. Issue: #4780 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=221439220
This commit is contained in:
parent
177f3883e9
commit
84fc24492f
5 changed files with 34 additions and 77 deletions
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
### 2.9.2 ###
|
||||
|
||||
* DASH: Fix detecting the end of live events
|
||||
([#4780](https://github.com/google/ExoPlayer/issues/4780)).
|
||||
* Include channel count in audio capabilities check
|
||||
([#4690](https://github.com/google/ExoPlayer/issues/4690)).
|
||||
|
||||
|
|
|
|||
|
|
@ -139,9 +139,12 @@ public abstract class Timeline {
|
|||
*/
|
||||
public boolean isSeekable;
|
||||
|
||||
/**
|
||||
* Whether this window may change when the timeline is updated.
|
||||
*/
|
||||
// TODO: Split this to better describe which parts of the window might change. For example it
|
||||
// should be possible to individually determine whether the start and end positions of the
|
||||
// window may change relative to the underlying periods. For an example of where it's useful to
|
||||
// know that the end position is fixed whilst the start position may still change, see:
|
||||
// https://github.com/google/ExoPlayer/issues/4780.
|
||||
/** Whether this window may change when the timeline is updated. */
|
||||
public boolean isDynamic;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -376,7 +376,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
|
||||
private int staleManifestReloadAttempt;
|
||||
private long expiredManifestPublishTimeUs;
|
||||
private boolean dynamicMediaPresentationEnded;
|
||||
|
||||
private int firstPeriodId;
|
||||
|
||||
|
|
@ -679,7 +678,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
elapsedRealtimeOffsetMs = 0;
|
||||
staleManifestReloadAttempt = 0;
|
||||
expiredManifestPublishTimeUs = C.TIME_UNSET;
|
||||
dynamicMediaPresentationEnded = false;
|
||||
firstPeriodId = 0;
|
||||
periodsById.clear();
|
||||
}
|
||||
|
|
@ -691,10 +689,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
startLoadingManifest();
|
||||
}
|
||||
|
||||
/* package */ void onDashLiveMediaPresentationEndSignalEncountered() {
|
||||
this.dynamicMediaPresentationEnded = true;
|
||||
}
|
||||
|
||||
/* package */ void onDashManifestPublishTimeExpired(long expiredManifestPublishTimeUs) {
|
||||
if (this.expiredManifestPublishTimeUs == C.TIME_UNSET
|
||||
|| this.expiredManifestPublishTimeUs < expiredManifestPublishTimeUs) {
|
||||
|
|
@ -734,9 +728,8 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
// behind.
|
||||
Log.w(TAG, "Loaded out of sync manifest");
|
||||
isManifestStale = true;
|
||||
} else if (dynamicMediaPresentationEnded
|
||||
|| (expiredManifestPublishTimeUs != C.TIME_UNSET
|
||||
&& newManifest.publishTimeMs * 1000 <= expiredManifestPublishTimeUs)) {
|
||||
} else if (expiredManifestPublishTimeUs != C.TIME_UNSET
|
||||
&& newManifest.publishTimeMs * 1000 <= expiredManifestPublishTimeUs) {
|
||||
// If we receive a dynamic manifest that's older than expected (i.e. its publish time has
|
||||
// expired, or it's dynamic and we know the presentation has ended), then this manifest is
|
||||
// stale.
|
||||
|
|
@ -745,8 +738,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
"Loaded stale dynamic manifest: "
|
||||
+ newManifest.publishTimeMs
|
||||
+ ", "
|
||||
+ dynamicMediaPresentationEnded
|
||||
+ ", "
|
||||
+ expiredManifestPublishTimeUs);
|
||||
isManifestStale = true;
|
||||
}
|
||||
|
|
@ -763,7 +754,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
staleManifestReloadAttempt = 0;
|
||||
}
|
||||
|
||||
|
||||
manifest = newManifest;
|
||||
manifestLoadPending &= manifest.dynamic;
|
||||
manifestLoadStartTimestampMs = elapsedRealtimeMs - loadDurationMs;
|
||||
|
|
@ -1170,12 +1160,16 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
long windowDefaultStartPositionUs = getAdjustedWindowDefaultStartPositionUs(
|
||||
defaultPositionProjectionUs);
|
||||
Object tag = setTag ? windowTag : null;
|
||||
boolean isDynamic =
|
||||
manifest.dynamic
|
||||
&& manifest.minUpdatePeriodMs != C.TIME_UNSET
|
||||
&& manifest.durationMs == C.TIME_UNSET;
|
||||
return window.set(
|
||||
tag,
|
||||
presentationStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
/* isSeekable= */ true,
|
||||
manifest.dynamic,
|
||||
isDynamic,
|
||||
windowDefaultStartPositionUs,
|
||||
windowDurationUs,
|
||||
/* firstPeriodIndex= */ 0,
|
||||
|
|
@ -1253,11 +1247,6 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||
public void onDashManifestPublishTimeExpired(long expiredManifestPublishTimeUs) {
|
||||
DashMediaSource.this.onDashManifestPublishTimeExpired(expiredManifestPublishTimeUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDashLiveMediaPresentationEndSignalEncountered() {
|
||||
DashMediaSource.this.onDashLiveMediaPresentationEndSignalEncountered();
|
||||
}
|
||||
}
|
||||
|
||||
private final class ManifestCallback implements Loader.Callback<ParsingLoadable<DashManifest>> {
|
||||
|
|
|
|||
|
|
@ -318,9 +318,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
}
|
||||
}
|
||||
|
||||
long periodDurationUs = representationHolder.periodDurationUs;
|
||||
boolean periodEnded = periodDurationUs != C.TIME_UNSET;
|
||||
|
||||
if (representationHolder.getSegmentCount() == 0) {
|
||||
// The index doesn't define any segments.
|
||||
out.endOfStream = !manifest.dynamic || (periodIndex < manifest.getPeriodCount() - 1);
|
||||
out.endOfStream = periodEnded;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -343,17 +346,15 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
fatalError = new BehindLiveWindowException();
|
||||
return;
|
||||
}
|
||||
|
||||
if (segmentNum > lastAvailableSegmentNum
|
||||
|| (missingLastSegment && segmentNum >= lastAvailableSegmentNum)) {
|
||||
// The segment is beyond the end of the period. We know the period will not be extended if the
|
||||
// manifest is static, or if there's a period after this one.
|
||||
out.endOfStream = !manifest.dynamic || (periodIndex < manifest.getPeriodCount() - 1);
|
||||
// The segment is beyond the end of the period.
|
||||
out.endOfStream = periodEnded;
|
||||
return;
|
||||
}
|
||||
|
||||
long periodDurationUs = representationHolder.periodDurationUs;
|
||||
if (periodDurationUs != C.TIME_UNSET
|
||||
&& representationHolder.getSegmentStartTimeUs(segmentNum) >= periodDurationUs) {
|
||||
if (periodEnded && representationHolder.getSegmentStartTimeUs(segmentNum) >= periodDurationUs) {
|
||||
// The period duration clips the period to a position before the segment.
|
||||
out.endOfStream = true;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -60,8 +60,7 @@ import java.util.TreeMap;
|
|||
*/
|
||||
public final class PlayerEmsgHandler implements Handler.Callback {
|
||||
|
||||
private static final int EMSG_MEDIA_PRESENTATION_ENDED = 1;
|
||||
private static final int EMSG_MANIFEST_EXPIRED = 2;
|
||||
private static final int EMSG_MANIFEST_EXPIRED = 1;
|
||||
|
||||
/** Callbacks for player emsg events encountered during DASH live stream. */
|
||||
public interface PlayerEmsgCallback {
|
||||
|
|
@ -75,9 +74,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
* @param expiredManifestPublishTimeUs The manifest publish time that has been expired.
|
||||
*/
|
||||
void onDashManifestPublishTimeExpired(long expiredManifestPublishTimeUs);
|
||||
|
||||
/** Called when a media presentation end signal is encountered during live stream. * */
|
||||
void onDashLiveMediaPresentationEndSignalEncountered();
|
||||
}
|
||||
|
||||
private final Allocator allocator;
|
||||
|
|
@ -88,7 +84,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
|
||||
private DashManifest manifest;
|
||||
|
||||
private boolean dynamicMediaPresentationEnded;
|
||||
private long expiredManifestPublishTimeUs;
|
||||
private long lastLoadedChunkEndTimeUs;
|
||||
private long lastLoadedChunkEndTimeBeforeRefreshUs;
|
||||
|
|
@ -134,21 +129,15 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
return true;
|
||||
}
|
||||
boolean manifestRefreshNeeded = false;
|
||||
if (dynamicMediaPresentationEnded) {
|
||||
// The manifest we have is dynamic, but we know a non-dynamic one representing the final state
|
||||
// should be available.
|
||||
manifestRefreshNeeded = true;
|
||||
} else {
|
||||
// Find the smallest publishTime (greater than or equal to the current manifest's publish
|
||||
// time) that has a corresponding expiry time.
|
||||
Map.Entry<Long, Long> expiredEntry = ceilingExpiryEntryForPublishTime(manifest.publishTimeMs);
|
||||
if (expiredEntry != null) {
|
||||
long expiredPointUs = expiredEntry.getValue();
|
||||
if (expiredPointUs < presentationPositionUs) {
|
||||
expiredManifestPublishTimeUs = expiredEntry.getKey();
|
||||
notifyManifestPublishTimeExpired();
|
||||
manifestRefreshNeeded = true;
|
||||
}
|
||||
// Find the smallest publishTime (greater than or equal to the current manifest's publish time)
|
||||
// that has a corresponding expiry time.
|
||||
Map.Entry<Long, Long> expiredEntry = ceilingExpiryEntryForPublishTime(manifest.publishTimeMs);
|
||||
if (expiredEntry != null) {
|
||||
long expiredPointUs = expiredEntry.getValue();
|
||||
if (expiredPointUs < presentationPositionUs) {
|
||||
expiredManifestPublishTimeUs = expiredEntry.getKey();
|
||||
notifyManifestPublishTimeExpired();
|
||||
manifestRefreshNeeded = true;
|
||||
}
|
||||
}
|
||||
if (manifestRefreshNeeded) {
|
||||
|
|
@ -221,9 +210,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
return true;
|
||||
}
|
||||
switch (message.what) {
|
||||
case (EMSG_MEDIA_PRESENTATION_ENDED):
|
||||
handleMediaPresentationEndedMessageEncountered();
|
||||
return true;
|
||||
case (EMSG_MANIFEST_EXPIRED):
|
||||
ManifestExpiryEventInfo messageObj = (ManifestExpiryEventInfo) message.obj;
|
||||
handleManifestExpiredMessage(
|
||||
|
|
@ -248,11 +234,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleMediaPresentationEndedMessageEncountered() {
|
||||
dynamicMediaPresentationEnded = true;
|
||||
notifySourceMediaPresentationEnded();
|
||||
}
|
||||
|
||||
private @Nullable Map.Entry<Long, Long> ceilingExpiryEntryForPublishTime(long publishTimeMs) {
|
||||
return manifestPublishTimeToExpiryTimeUs.ceilingEntry(publishTimeMs);
|
||||
}
|
||||
|
|
@ -273,10 +254,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
playerEmsgCallback.onDashManifestPublishTimeExpired(expiredManifestPublishTimeUs);
|
||||
}
|
||||
|
||||
private void notifySourceMediaPresentationEnded() {
|
||||
playerEmsgCallback.onDashLiveMediaPresentationEndSignalEncountered();
|
||||
}
|
||||
|
||||
/** Requests DASH media manifest to be refreshed if necessary. */
|
||||
private void maybeNotifyDashManifestRefreshNeeded() {
|
||||
if (lastLoadedChunkEndTimeBeforeRefreshUs != C.TIME_UNSET
|
||||
|
|
@ -298,12 +275,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean isMessageSignalingMediaPresentationEnded(EventMessage eventMessage) {
|
||||
// According to section 4.5.2.1 DASH-IF IOP, if both presentation time delta and event duration
|
||||
// are zero, the media presentation is ended.
|
||||
return eventMessage.presentationTimeUs == 0 && eventMessage.durationMs == 0;
|
||||
}
|
||||
|
||||
/** Handles emsg messages for a specific track for the player. */
|
||||
public final class PlayerTrackEmsgHandler implements TrackOutput {
|
||||
|
||||
|
|
@ -413,16 +384,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
|
|||
if (manifestPublishTimeMsInEmsg == C.TIME_UNSET) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isMessageSignalingMediaPresentationEnded(eventMessage)) {
|
||||
onMediaPresentationEndedMessageEncountered();
|
||||
} else {
|
||||
onManifestExpiredMessageEncountered(eventTimeUs, manifestPublishTimeMsInEmsg);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMediaPresentationEndedMessageEncountered() {
|
||||
handler.sendMessage(handler.obtainMessage(EMSG_MEDIA_PRESENTATION_ENDED));
|
||||
onManifestExpiredMessageEncountered(eventTimeUs, manifestPublishTimeMsInEmsg);
|
||||
}
|
||||
|
||||
private void onManifestExpiredMessageEncountered(
|
||||
|
|
|
|||
Loading…
Reference in a new issue