mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
RtspMediaPeriod
: Use a new ExtractorOutput
for each SampleQueue
This removes concurrent access from `rtspLoaderWrappers`. Previously there was a race between the playback thread clearing & re-adding entries to this list in `retryWithRtpTcp()`, and the loading thread accessing the entries in `InternalListener.track()` (implemented from `ExtractorOutput`). This change means each `ExtractorOutputImpl` uses exactly one `SampleQueue` for its one `TrackOutput`. When the `RtspLoaderWrapper` instances are replaced in `retryWithRtpTcp()`, any stale instances will only be able to access their own (also stale) `SampleQueue` instances (vs before, where the stale `ExtractorOutput` could accidentally access 'new' `SampleQueue` instances via the `rtspLoaderWrappers` field). As well as fixing a race condition in the prod code, this also de-flakes `RtspPlaybackTest`. #minor-release PiperOrigin-RevId: 559130479
This commit is contained in:
parent
398809e4e2
commit
9533f5cd1c
@ -36,6 +36,8 @@
|
|||||||
* HLS Extension:
|
* HLS Extension:
|
||||||
* Smooth Streaming Extension:
|
* Smooth Streaming Extension:
|
||||||
* RTSP Extension:
|
* RTSP Extension:
|
||||||
|
* Fix a race condition that could lead to `IndexOutOfBoundsException` when
|
||||||
|
falling back to TCP, or playback hanging in some situations.
|
||||||
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
|
||||||
* MIDI extension:
|
* MIDI extension:
|
||||||
* Cast Extension:
|
* Cast Extension:
|
||||||
|
@ -500,18 +500,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return listBuilder.build();
|
return listBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class InternalListener
|
// All interactions are on the loading thread
|
||||||
implements ExtractorOutput,
|
private final class ExtractorOutputImpl implements ExtractorOutput {
|
||||||
Loader.Callback<RtpDataLoadable>,
|
|
||||||
UpstreamFormatChangedListener,
|
|
||||||
SessionInfoListener,
|
|
||||||
PlaybackEventListener {
|
|
||||||
|
|
||||||
// ExtractorOutput implementation.
|
private final TrackOutput trackOutput;
|
||||||
|
|
||||||
|
private ExtractorOutputImpl(TrackOutput trackOutput) {
|
||||||
|
this.trackOutput = trackOutput;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackOutput track(int id, int type) {
|
public TrackOutput track(int id, int type) {
|
||||||
return checkNotNull(rtspLoaderWrappers.get(id)).sampleQueue;
|
return trackOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -523,6 +523,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public void seekMap(SeekMap seekMap) {
|
public void seekMap(SeekMap seekMap) {
|
||||||
// RTSP does not support seek map.
|
// RTSP does not support seek map.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class InternalListener
|
||||||
|
implements Loader.Callback<RtpDataLoadable>,
|
||||||
|
UpstreamFormatChangedListener,
|
||||||
|
SessionInfoListener,
|
||||||
|
PlaybackEventListener {
|
||||||
|
|
||||||
// Loadable.Callback implementation.
|
// Loadable.Callback implementation.
|
||||||
|
|
||||||
@ -790,9 +797,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
*/
|
*/
|
||||||
public RtspLoaderWrapper(
|
public RtspLoaderWrapper(
|
||||||
RtspMediaTrack mediaTrack, int trackId, RtpDataChannel.Factory rtpDataChannelFactory) {
|
RtspMediaTrack mediaTrack, int trackId, RtpDataChannel.Factory rtpDataChannelFactory) {
|
||||||
loadInfo = new RtpLoadInfo(mediaTrack, trackId, rtpDataChannelFactory);
|
|
||||||
loader = new Loader("ExoPlayer:RtspMediaPeriod:RtspLoaderWrapper " + trackId);
|
loader = new Loader("ExoPlayer:RtspMediaPeriod:RtspLoaderWrapper " + trackId);
|
||||||
sampleQueue = SampleQueue.createWithoutDrm(allocator);
|
sampleQueue = SampleQueue.createWithoutDrm(allocator);
|
||||||
|
loadInfo = new RtpLoadInfo(mediaTrack, trackId, sampleQueue, rtpDataChannelFactory);
|
||||||
sampleQueue.setUpstreamFormatChangeListener(internalListener);
|
sampleQueue.setUpstreamFormatChangeListener(internalListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -875,7 +882,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
/** Creates a new instance. */
|
/** Creates a new instance. */
|
||||||
public RtpLoadInfo(
|
public RtpLoadInfo(
|
||||||
RtspMediaTrack mediaTrack, int trackId, RtpDataChannel.Factory rtpDataChannelFactory) {
|
RtspMediaTrack mediaTrack,
|
||||||
|
int trackId,
|
||||||
|
TrackOutput trackOutput,
|
||||||
|
RtpDataChannel.Factory rtpDataChannelFactory) {
|
||||||
this.mediaTrack = mediaTrack;
|
this.mediaTrack = mediaTrack;
|
||||||
|
|
||||||
// This listener runs on the playback thread, posted by the Loader thread.
|
// This listener runs on the playback thread, posted by the Loader thread.
|
||||||
@ -899,7 +909,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
trackId,
|
trackId,
|
||||||
mediaTrack,
|
mediaTrack,
|
||||||
/* eventListener= */ transportEventListener,
|
/* eventListener= */ transportEventListener,
|
||||||
/* output= */ internalListener,
|
/* output= */ new ExtractorOutputImpl(trackOutput),
|
||||||
rtpDataChannelFactory);
|
rtpDataChannelFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user