Remove mediaTimeOffsetMs from EventDispatcher

The `DashMediaSource` wrongly added an offset to the media times set
to the `MediaLoadData`. With this the `startTimeMS` and `endTimeMs`
don't represent the positions in the period but in the stream.

`DashMediaSource` was the only call site that was setting the offset
to a non-zero value. So if we are using 0 for the `DashMediaSource`
as well, the offset is redundant and we can remove it everywhere.

PiperOrigin-RevId: 520682026
This commit is contained in:
bachinger 2023-03-30 18:41:09 +01:00 committed by Marc Baechinger
parent 104cfc322c
commit d7010da614
12 changed files with 74 additions and 61 deletions

View File

@ -21,6 +21,13 @@
AdPlaybackState>)` by adding a timeline parameter that contains the AdPlaybackState>)` by adding a timeline parameter that contains the
periods with the UIDs used as keys in the map. This is required to avoid periods with the UIDs used as keys in the map. This is required to avoid
concurrency issues with multi-period live streams. concurrency issues with multi-period live streams.
* Deprecate `EventDispatcher.withParameters(int windowIndex, @Nullable
MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs)` and
`BaseMediaSource.createEventDispatcher(..., long mediaTimeOffsetMs)`.
The variant of the methods without the `mediaTimeOffsetUs` can be called
instead. Note that even for the deprecated variants, the offset is not
anymore added to `startTimeUs` and `endTimeUs` of the `MediaLoadData`
objects that are dispatched by the dispatcher.
* Audio: * Audio:
* Fix bug where some playbacks fail when tunneling is enabled and * Fix bug where some playbacks fail when tunneling is enabled and
`AudioProcessors` are active, e.g. for gapless trimming `AudioProcessors` are active, e.g. for gapless trimming
@ -52,6 +59,8 @@
* DASH: * DASH:
* Fix handling of empty segment timelines * Fix handling of empty segment timelines
([#11014](https://github.com/google/ExoPlayer/issues/11014)). ([#11014](https://github.com/google/ExoPlayer/issues/11014)).
* Remove the media time offset from `MediaLoadData.startTimeMs` and
`MediaLoadData.endTimeMs` for multi period DASH streams.
* RTSP: * RTSP:
* Retry with TCP if RTSP Setup with UDP fails with RTSP Error 461 * Retry with TCP if RTSP Setup with UDP fails with RTSP Error 461
UnsupportedTransport UnsupportedTransport

View File

@ -101,37 +101,46 @@ public abstract class BaseMediaSource implements MediaSource {
*/ */
protected final MediaSourceEventListener.EventDispatcher createEventDispatcher( protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
@Nullable MediaPeriodId mediaPeriodId) { @Nullable MediaPeriodId mediaPeriodId) {
return eventDispatcher.withParameters( return eventDispatcher.withParameters(/* windowIndex= */ 0, mediaPeriodId);
/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0);
} }
/** /**
* Returns a {@link MediaSourceEventListener.EventDispatcher} which dispatches all events to the * Returns a {@link MediaSourceEventListener.EventDispatcher} which dispatches all events to the
* registered listeners with the specified {@link MediaPeriodId} and time offset. * registered listeners with the specified window index and {@link MediaPeriodId}.
*
* @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events.
* @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds.
* @return An event dispatcher with pre-configured media period id and time offset.
*/
protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
Assertions.checkNotNull(mediaPeriodId);
return eventDispatcher.withParameters(/* windowIndex= */ 0, mediaPeriodId, mediaTimeOffsetMs);
}
/**
* Returns a {@link MediaSourceEventListener.EventDispatcher} which dispatches all events to the
* registered listeners with the specified window index, {@link MediaPeriodId} and time offset.
* *
* @param windowIndex The timeline window index to be reported with the events. * @param windowIndex The timeline window index to be reported with the events.
* @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. May be null, if * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. May be null, if
* the events do not belong to a specific media period. * the events do not belong to a specific media period.
* @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds. * @return An event dispatcher with pre-configured media period id.
* @return An event dispatcher with pre-configured media period id and time offset.
*/ */
protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
return eventDispatcher.withParameters(windowIndex, mediaPeriodId);
}
/**
* Note: The {@code mediaTimeOffsetMs} passed to this method is ignored and not added to media
* times in any way.
*
* @deprecated Use {@link #createEventDispatcher(MediaPeriodId)} instead.
*/
@Deprecated
protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
Assertions.checkNotNull(mediaPeriodId);
return eventDispatcher.withParameters(/* windowIndex= */ 0, mediaPeriodId);
}
/**
* Note: The {@code mediaTimeOffsetMs} passed to this method is ignored and not added to media
* times in any way.
*
* @deprecated Use {@link #createEventDispatcher(int, MediaPeriodId)} instead.
*/
@Deprecated
protected final MediaSourceEventListener.EventDispatcher createEventDispatcher( protected final MediaSourceEventListener.EventDispatcher createEventDispatcher(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
return eventDispatcher.withParameters(windowIndex, mediaPeriodId, mediaTimeOffsetMs); return eventDispatcher.withParameters(windowIndex, mediaPeriodId);
} }
/** /**

View File

@ -357,8 +357,7 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex); int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex);
if (mediaSourceEventDispatcher.windowIndex != windowIndex if (mediaSourceEventDispatcher.windowIndex != windowIndex
|| !Util.areEqual(mediaSourceEventDispatcher.mediaPeriodId, mediaPeriodId)) { || !Util.areEqual(mediaSourceEventDispatcher.mediaPeriodId, mediaPeriodId)) {
mediaSourceEventDispatcher = mediaSourceEventDispatcher = createEventDispatcher(windowIndex, mediaPeriodId);
createEventDispatcher(windowIndex, mediaPeriodId, /* mediaTimeOffsetMs= */ 0);
} }
if (drmEventDispatcher.windowIndex != windowIndex if (drmEventDispatcher.windowIndex != windowIndex
|| !Util.areEqual(drmEventDispatcher.mediaPeriodId, mediaPeriodId)) { || !Util.areEqual(drmEventDispatcher.mediaPeriodId, mediaPeriodId)) {

View File

@ -16,6 +16,7 @@
package androidx.media3.exoplayer.source; package androidx.media3.exoplayer.source;
import static androidx.media3.common.util.Util.postOrRun; import static androidx.media3.common.util.Util.postOrRun;
import static androidx.media3.common.util.Util.usToMs;
import android.os.Handler; import android.os.Handler;
import androidx.annotation.CheckResult; import androidx.annotation.CheckResult;
@ -26,7 +27,6 @@ import androidx.media3.common.Format;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@ -152,26 +152,22 @@ public interface MediaSourceEventListener {
@Nullable public final MediaPeriodId mediaPeriodId; @Nullable public final MediaPeriodId mediaPeriodId;
private final CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers; private final CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers;
private final long mediaTimeOffsetMs;
/** Creates an event dispatcher. */ /** Creates an event dispatcher. */
public EventDispatcher() { public EventDispatcher() {
this( this(
/* listenerAndHandlers= */ new CopyOnWriteArrayList<>(), /* listenerAndHandlers= */ new CopyOnWriteArrayList<>(),
/* windowIndex= */ 0, /* windowIndex= */ 0,
/* mediaPeriodId= */ null, /* mediaPeriodId= */ null);
/* mediaTimeOffsetMs= */ 0);
} }
private EventDispatcher( private EventDispatcher(
CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers, CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers,
int windowIndex, int windowIndex,
@Nullable MediaPeriodId mediaPeriodId, @Nullable MediaPeriodId mediaPeriodId) {
long mediaTimeOffsetMs) {
this.listenerAndHandlers = listenerAndHandlers; this.listenerAndHandlers = listenerAndHandlers;
this.windowIndex = windowIndex; this.windowIndex = windowIndex;
this.mediaPeriodId = mediaPeriodId; this.mediaPeriodId = mediaPeriodId;
this.mediaTimeOffsetMs = mediaTimeOffsetMs;
} }
/** /**
@ -180,14 +176,24 @@ public interface MediaSourceEventListener {
* *
* @param windowIndex The timeline window index to be reported with the events. * @param windowIndex The timeline window index to be reported with the events.
* @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events.
* @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds.
* @return A view of the event dispatcher with the pre-configured parameters. * @return A view of the event dispatcher with the pre-configured parameters.
*/ */
@CheckResult @CheckResult
public EventDispatcher withParameters(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {
return new EventDispatcher(listenerAndHandlers, windowIndex, mediaPeriodId);
}
/**
* Note: The {@code mediaTimeOffsetMs} passed to this method is ignored and not added to media
* times in any way.
*
* @deprecated Use {@link #withParameters(int, MediaPeriodId)} instead.
*/
@Deprecated
@CheckResult
public EventDispatcher withParameters( public EventDispatcher withParameters(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
return new EventDispatcher( return new EventDispatcher(listenerAndHandlers, windowIndex, mediaPeriodId);
listenerAndHandlers, windowIndex, mediaPeriodId, mediaTimeOffsetMs);
} }
/** /**
@ -246,8 +252,8 @@ public interface MediaSourceEventListener {
trackFormat, trackFormat,
trackSelectionReason, trackSelectionReason,
trackSelectionData, trackSelectionData,
adjustMediaTime(mediaStartTimeUs), usToMs(mediaStartTimeUs),
adjustMediaTime(mediaEndTimeUs))); usToMs(mediaEndTimeUs)));
} }
/** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
@ -291,8 +297,8 @@ public interface MediaSourceEventListener {
trackFormat, trackFormat,
trackSelectionReason, trackSelectionReason,
trackSelectionData, trackSelectionData,
adjustMediaTime(mediaStartTimeUs), usToMs(mediaStartTimeUs),
adjustMediaTime(mediaEndTimeUs))); usToMs(mediaEndTimeUs)));
} }
/** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ /** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
@ -337,8 +343,8 @@ public interface MediaSourceEventListener {
trackFormat, trackFormat,
trackSelectionReason, trackSelectionReason,
trackSelectionData, trackSelectionData,
adjustMediaTime(mediaStartTimeUs), usToMs(mediaStartTimeUs),
adjustMediaTime(mediaEndTimeUs))); usToMs(mediaEndTimeUs)));
} }
/** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ /** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
@ -397,8 +403,8 @@ public interface MediaSourceEventListener {
trackFormat, trackFormat,
trackSelectionReason, trackSelectionReason,
trackSelectionData, trackSelectionData,
adjustMediaTime(mediaStartTimeUs), usToMs(mediaStartTimeUs),
adjustMediaTime(mediaEndTimeUs)), usToMs(mediaEndTimeUs)),
error, error,
wasCanceled); wasCanceled);
} }
@ -431,8 +437,8 @@ public interface MediaSourceEventListener {
/* trackFormat= */ null, /* trackFormat= */ null,
C.SELECTION_REASON_ADAPTIVE, C.SELECTION_REASON_ADAPTIVE,
/* trackSelectionData= */ null, /* trackSelectionData= */ null,
adjustMediaTime(mediaStartTimeUs), usToMs(mediaStartTimeUs),
adjustMediaTime(mediaEndTimeUs))); usToMs(mediaEndTimeUs)));
} }
/** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */ /** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */
@ -460,7 +466,7 @@ public interface MediaSourceEventListener {
trackFormat, trackFormat,
trackSelectionReason, trackSelectionReason,
trackSelectionData, trackSelectionData,
adjustMediaTime(mediaTimeUs), usToMs(mediaTimeUs),
/* mediaEndTimeMs= */ C.TIME_UNSET)); /* mediaEndTimeMs= */ C.TIME_UNSET));
} }
@ -474,11 +480,6 @@ public interface MediaSourceEventListener {
} }
} }
private long adjustMediaTime(long mediaTimeUs) {
long mediaTimeMs = Util.usToMs(mediaTimeUs);
return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs;
}
private static final class ListenerAndHandler { private static final class ListenerAndHandler {
public Handler handler; public Handler handler;

View File

@ -517,8 +517,7 @@ public class DownloadHelperTest {
trackGroupArrays[periodIndex], trackGroupArrays[periodIndex],
allocator, allocator,
TEST_TIMELINE.getWindow(0, new Timeline.Window()).positionInFirstPeriodUs, TEST_TIMELINE.getWindow(0, new Timeline.Window()).positionInFirstPeriodUs,
new EventDispatcher() new EventDispatcher().withParameters(/* windowIndex= */ 0, id)) {
.withParameters(/* windowIndex= */ 0, id, /* mediaTimeOffsetMs= */ 0)) {
@Override @Override
public List<StreamKey> getStreamKeys(List<ExoTrackSelection> trackSelections) { public List<StreamKey> getStreamKeys(List<ExoTrackSelection> trackSelections) {
List<StreamKey> result = new ArrayList<>(); List<StreamKey> result = new ArrayList<>();

View File

@ -246,10 +246,7 @@ public final class MergingMediaPeriodTest {
new FakeMediaPeriodWithSelectionParameters( new FakeMediaPeriodWithSelectionParameters(
new TrackGroupArray(trackGroups), new TrackGroupArray(trackGroups),
new EventDispatcher() new EventDispatcher()
.withParameters( .withParameters(/* windowIndex= */ i, new MediaPeriodId(/* periodUid= */ i)),
/* windowIndex= */ i,
new MediaPeriodId(/* periodUid= */ i),
/* mediaTimeOffsetMs= */ 0),
/* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) -> /* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) ->
ImmutableList.of( ImmutableList.of(
oneByteSample(definition.singleSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME), oneByteSample(definition.singleSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME),

View File

@ -69,7 +69,7 @@ public final class ProgressiveMediaPeriodTest {
.withParameters(/* windowIndex= */ 0, mediaPeriodId), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
new DefaultLoadErrorHandlingPolicy(), new DefaultLoadErrorHandlingPolicy(),
new MediaSourceEventListener.EventDispatcher() new MediaSourceEventListener.EventDispatcher()
.withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
sourceInfoRefreshListener, sourceInfoRefreshListener,
new DefaultAllocator(/* trimOnReset= */ true, C.DEFAULT_BUFFER_SEGMENT_SIZE), new DefaultAllocator(/* trimOnReset= */ true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
/* customCacheKey= */ null, /* customCacheKey= */ null,

View File

@ -469,8 +469,7 @@ public final class DashMediaSource extends BaseMediaSource {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
int periodIndex = (Integer) id.periodUid - firstPeriodId; int periodIndex = (Integer) id.periodUid - firstPeriodId;
MediaSourceEventListener.EventDispatcher periodEventDispatcher = MediaSourceEventListener.EventDispatcher periodEventDispatcher = createEventDispatcher(id);
createEventDispatcher(id, manifest.getPeriod(periodIndex).startMs);
DrmSessionEventListener.EventDispatcher drmEventDispatcher = createDrmEventDispatcher(id); DrmSessionEventListener.EventDispatcher drmEventDispatcher = createDrmEventDispatcher(id);
DashMediaPeriod mediaPeriod = DashMediaPeriod mediaPeriod =
new DashMediaPeriod( new DashMediaPeriod(

View File

@ -216,7 +216,7 @@ public final class DashMediaPeriodTest {
.withParameters(/* windowIndex= */ 0, mediaPeriodId), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
mock(LoadErrorHandlingPolicy.class), mock(LoadErrorHandlingPolicy.class),
new MediaSourceEventListener.EventDispatcher() new MediaSourceEventListener.EventDispatcher()
.withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
/* elapsedRealtimeOffsetMs= */ 0, /* elapsedRealtimeOffsetMs= */ 0,
mock(LoaderErrorThrower.class), mock(LoaderErrorThrower.class),
mock(Allocator.class), mock(Allocator.class),

View File

@ -89,7 +89,7 @@ public final class HlsMediaPeriodTest {
.withParameters(/* windowIndex= */ 0, mediaPeriodId), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
mock(LoadErrorHandlingPolicy.class), mock(LoadErrorHandlingPolicy.class),
new MediaSourceEventListener.EventDispatcher() new MediaSourceEventListener.EventDispatcher()
.withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
mock(Allocator.class), mock(Allocator.class),
mock(CompositeSequenceableLoaderFactory.class), mock(CompositeSequenceableLoaderFactory.class),
/* allowChunklessPreparation= */ true, /* allowChunklessPreparation= */ true,

View File

@ -72,7 +72,7 @@ public class SsMediaPeriodTest {
.withParameters(/* windowIndex= */ 0, mediaPeriodId), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
mock(LoadErrorHandlingPolicy.class), mock(LoadErrorHandlingPolicy.class),
new MediaSourceEventListener.EventDispatcher() new MediaSourceEventListener.EventDispatcher()
.withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), .withParameters(/* windowIndex= */ 0, mediaPeriodId),
mock(LoaderErrorThrower.class), mock(LoaderErrorThrower.class),
mock(Allocator.class)); mock(Allocator.class));
}; };

View File

@ -239,7 +239,7 @@ public class FakeMediaSource extends BaseMediaSource {
Assertions.checkArgument(periodIndex != C.INDEX_UNSET); Assertions.checkArgument(periodIndex != C.INDEX_UNSET);
Period period = timeline.getPeriod(periodIndex, new Period()); Period period = timeline.getPeriod(periodIndex, new Period());
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher = MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher =
createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs()); createEventDispatcher(period.windowIndex, id);
DrmSessionEventListener.EventDispatcher drmEventDispatcher = DrmSessionEventListener.EventDispatcher drmEventDispatcher =
createDrmEventDispatcher(period.windowIndex, id); createDrmEventDispatcher(period.windowIndex, id);
MediaPeriod mediaPeriod = MediaPeriod mediaPeriod =