Commit 30c55d11 by aquilescanta Committed by Ian Baker

Fix NPE when reading from a SampleQueue from a loading thread

Issue: #7273
PiperOrigin-RevId: 308238035
parent 4df7470d
...@@ -122,6 +122,8 @@ ...@@ -122,6 +122,8 @@
`http://dashif.org/guidelines/trickmode`) into the same `TrackGroup` as `http://dashif.org/guidelines/trickmode`) into the same `TrackGroup` as
the main adaptation sets to which they refer. Trick play tracks are the main adaptation sets to which they refer. Trick play tracks are
marked with the `C.ROLE_FLAG_TRICK_PLAY` flag. marked with the `C.ROLE_FLAG_TRICK_PLAY` flag.
* Fix assertion failure in `SampleQueue` when playing DASH streams with
EMSG tracks ([#7273](https://github.com/google/ExoPlayer/issues/7273)).
* MP3: Add `IndexSeeker` for accurate seeks in VBR streams * MP3: Add `IndexSeeker` for accurate seeks in VBR streams
([#6787](https://github.com/google/ExoPlayer/issues/6787)). This seeker is ([#6787](https://github.com/google/ExoPlayer/issues/6787)). This seeker is
enabled by passing `FLAG_ENABLE_INDEX_SEEKING` to the `Mp3Extractor`. It may enabled by passing `FLAG_ENABLE_INDEX_SEEKING` to the `Mp3Extractor`. It may
......
...@@ -686,7 +686,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -686,7 +686,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return sampleQueues[i]; return sampleQueues[i];
} }
} }
SampleQueue trackOutput = new SampleQueue(allocator, drmSessionManager, eventDispatcher); SampleQueue trackOutput =
new SampleQueue(
allocator,
/* playbackLooper= */ handler.getLooper(),
drmSessionManager,
eventDispatcher);
trackOutput.setUpstreamFormatChangeListener(this); trackOutput.setUpstreamFormatChangeListener(this);
@NullableType @NullableType
TrackId[] sampleQueueTrackIds = Arrays.copyOf(this.sampleQueueTrackIds, trackCount + 1); TrackId[] sampleQueueTrackIds = Arrays.copyOf(this.sampleQueueTrackIds, trackCount + 1);
......
...@@ -55,6 +55,7 @@ public class SampleQueue implements TrackOutput { ...@@ -55,6 +55,7 @@ public class SampleQueue implements TrackOutput {
private final SampleDataQueue sampleDataQueue; private final SampleDataQueue sampleDataQueue;
private final SampleExtrasHolder extrasHolder; private final SampleExtrasHolder extrasHolder;
private final Looper playbackLooper;
private final DrmSessionManager drmSessionManager; private final DrmSessionManager drmSessionManager;
private final MediaSourceEventDispatcher eventDispatcher; private final MediaSourceEventDispatcher eventDispatcher;
@Nullable private UpstreamFormatChangedListener upstreamFormatChangeListener; @Nullable private UpstreamFormatChangedListener upstreamFormatChangeListener;
...@@ -94,6 +95,7 @@ public class SampleQueue implements TrackOutput { ...@@ -94,6 +95,7 @@ public class SampleQueue implements TrackOutput {
* Creates a sample queue. * Creates a sample queue.
* *
* @param allocator An {@link Allocator} from which allocations for sample data can be obtained. * @param allocator An {@link Allocator} from which allocations for sample data can be obtained.
* @param playbackLooper The looper associated with the media playback thread.
* @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions} * @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions}
* from. The created instance does not take ownership of this {@link DrmSessionManager}. * from. The created instance does not take ownership of this {@link DrmSessionManager}.
* @param eventDispatcher A {@link MediaSourceEventDispatcher} to notify of events related to this * @param eventDispatcher A {@link MediaSourceEventDispatcher} to notify of events related to this
...@@ -101,11 +103,13 @@ public class SampleQueue implements TrackOutput { ...@@ -101,11 +103,13 @@ public class SampleQueue implements TrackOutput {
*/ */
public SampleQueue( public SampleQueue(
Allocator allocator, Allocator allocator,
Looper playbackLooper,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
MediaSourceEventDispatcher eventDispatcher) { MediaSourceEventDispatcher eventDispatcher) {
sampleDataQueue = new SampleDataQueue(allocator); this.playbackLooper = playbackLooper;
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
sampleDataQueue = new SampleDataQueue(allocator);
extrasHolder = new SampleExtrasHolder(); extrasHolder = new SampleExtrasHolder();
capacity = SAMPLE_CAPACITY_INCREMENT; capacity = SAMPLE_CAPACITY_INCREMENT;
sourceIds = new int[capacity]; sourceIds = new int[capacity];
...@@ -799,7 +803,6 @@ public class SampleQueue implements TrackOutput { ...@@ -799,7 +803,6 @@ public class SampleQueue implements TrackOutput {
// Ensure we acquire the new session before releasing the previous one in case the same session // Ensure we acquire the new session before releasing the previous one in case the same session
// is being used for both DrmInitData. // is being used for both DrmInitData.
@Nullable DrmSession previousSession = currentDrmSession; @Nullable DrmSession previousSession = currentDrmSession;
Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper());
currentDrmSession = currentDrmSession =
newDrmInitData != null newDrmInitData != null
? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData) ? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.source.chunk; package com.google.android.exoplayer2.source.chunk;
import android.os.Looper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -132,14 +133,22 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S ...@@ -132,14 +133,22 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
int[] trackTypes = new int[1 + embeddedTrackCount]; int[] trackTypes = new int[1 + embeddedTrackCount];
SampleQueue[] sampleQueues = new SampleQueue[1 + embeddedTrackCount]; SampleQueue[] sampleQueues = new SampleQueue[1 + embeddedTrackCount];
primarySampleQueue = new SampleQueue(allocator, drmSessionManager, eventDispatcher); primarySampleQueue =
new SampleQueue(
allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
drmSessionManager,
eventDispatcher);
trackTypes[0] = primaryTrackType; trackTypes[0] = primaryTrackType;
sampleQueues[0] = primarySampleQueue; sampleQueues[0] = primarySampleQueue;
for (int i = 0; i < embeddedTrackCount; i++) { for (int i = 0; i < embeddedTrackCount; i++) {
SampleQueue sampleQueue = SampleQueue sampleQueue =
new SampleQueue( new SampleQueue(
allocator, DrmSessionManager.getDummyDrmSessionManager(), eventDispatcher); allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
DrmSessionManager.getDummyDrmSessionManager(),
eventDispatcher);
embeddedSampleQueues[i] = sampleQueue; embeddedSampleQueues[i] = sampleQueue;
sampleQueues[i + 1] = sampleQueue; sampleQueues[i + 1] = sampleQueue;
trackTypes[i + 1] = this.embeddedTrackTypes[i]; trackTypes[i + 1] = this.embeddedTrackTypes[i];
......
...@@ -26,6 +26,7 @@ import static java.util.Arrays.copyOfRange; ...@@ -26,6 +26,7 @@ import static java.util.Arrays.copyOfRange;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.os.Looper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -39,6 +40,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput; ...@@ -39,6 +40,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
...@@ -139,7 +141,12 @@ public final class SampleQueueTest { ...@@ -139,7 +141,12 @@ public final class SampleQueueTest {
ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())) ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(mockDrmSession); .thenReturn(mockDrmSession);
eventDispatcher = new MediaSourceEventDispatcher(); eventDispatcher = new MediaSourceEventDispatcher();
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher); sampleQueue =
new SampleQueue(
allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
mockDrmSessionManager,
eventDispatcher);
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
inputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); inputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
} }
...@@ -356,7 +363,12 @@ public final class SampleQueueTest { ...@@ -356,7 +363,12 @@ public final class SampleQueueTest {
public void isReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() { public void isReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() {
when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true); when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true);
// We recreate the queue to ensure the mock DRM session manager flags are taken into account. // We recreate the queue to ensure the mock DRM session manager flags are taken into account.
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher); sampleQueue =
new SampleQueue(
allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
mockDrmSessionManager,
eventDispatcher);
writeTestDataWithEncryptedSections(); writeTestDataWithEncryptedSections();
assertThat(sampleQueue.isReady(/* loadingFinished= */ false)).isTrue(); assertThat(sampleQueue.isReady(/* loadingFinished= */ false)).isTrue();
} }
...@@ -534,7 +546,12 @@ public final class SampleQueueTest { ...@@ -534,7 +546,12 @@ public final class SampleQueueTest {
public void allowPlayClearSamplesWithoutKeysReadsClearSamples() { public void allowPlayClearSamplesWithoutKeysReadsClearSamples() {
when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true); when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true);
// We recreate the queue to ensure the mock DRM session manager flags are taken into account. // We recreate the queue to ensure the mock DRM session manager flags are taken into account.
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher); sampleQueue =
new SampleQueue(
allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
mockDrmSessionManager,
eventDispatcher);
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED); when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);
writeTestDataWithEncryptedSections(); writeTestDataWithEncryptedSections();
...@@ -924,7 +941,11 @@ public final class SampleQueueTest { ...@@ -924,7 +941,11 @@ public final class SampleQueueTest {
public void adjustUpstreamFormat() { public void adjustUpstreamFormat() {
String label = "label"; String label = "label";
sampleQueue = sampleQueue =
new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher) { new SampleQueue(
allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
mockDrmSessionManager,
eventDispatcher) {
@Override @Override
public Format getAdjustedUpstreamFormat(Format format) { public Format getAdjustedUpstreamFormat(Format format) {
return super.getAdjustedUpstreamFormat(copyWithLabel(format, label)); return super.getAdjustedUpstreamFormat(copyWithLabel(format, label));
...@@ -940,7 +961,11 @@ public final class SampleQueueTest { ...@@ -940,7 +961,11 @@ public final class SampleQueueTest {
public void invalidateUpstreamFormatAdjustment() { public void invalidateUpstreamFormatAdjustment() {
AtomicReference<String> label = new AtomicReference<>("label1"); AtomicReference<String> label = new AtomicReference<>("label1");
sampleQueue = sampleQueue =
new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher) { new SampleQueue(
allocator,
/* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()),
mockDrmSessionManager,
eventDispatcher) {
@Override @Override
public Format getAdjustedUpstreamFormat(Format format) { public Format getAdjustedUpstreamFormat(Format format) {
return super.getAdjustedUpstreamFormat(copyWithLabel(format, label.get())); return super.getAdjustedUpstreamFormat(copyWithLabel(format, label.get()));
......
...@@ -288,6 +288,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { ...@@ -288,6 +288,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
this.sampleQueue = this.sampleQueue =
new SampleQueue( new SampleQueue(
allocator, allocator,
/* playbackLooper= */ handler.getLooper(),
DrmSessionManager.getDummyDrmSessionManager(), DrmSessionManager.getDummyDrmSessionManager(),
new MediaSourceEventDispatcher()); new MediaSourceEventDispatcher());
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -910,7 +911,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -910,7 +911,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
boolean isAudioVideo = type == C.TRACK_TYPE_AUDIO || type == C.TRACK_TYPE_VIDEO; boolean isAudioVideo = type == C.TRACK_TYPE_AUDIO || type == C.TRACK_TYPE_VIDEO;
HlsSampleQueue sampleQueue = HlsSampleQueue sampleQueue =
new HlsSampleQueue(allocator, drmSessionManager, eventDispatcher, overridingDrmInitData); new HlsSampleQueue(
allocator,
/* playbackLooper= */ handler.getLooper(),
drmSessionManager,
eventDispatcher,
overridingDrmInitData);
if (isAudioVideo) { if (isAudioVideo) {
sampleQueue.setDrmInitData(drmInitData); sampleQueue.setDrmInitData(drmInitData);
} }
...@@ -1380,10 +1386,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -1380,10 +1386,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private HlsSampleQueue( private HlsSampleQueue(
Allocator allocator, Allocator allocator,
Looper playbackLooper,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
MediaSourceEventDispatcher eventDispatcher, MediaSourceEventDispatcher eventDispatcher,
Map<String, DrmInitData> overridingDrmInitData) { Map<String, DrmInitData> overridingDrmInitData) {
super(allocator, drmSessionManager, eventDispatcher); super(allocator, playbackLooper, drmSessionManager, eventDispatcher);
this.overridingDrmInitData = overridingDrmInitData; this.overridingDrmInitData = overridingDrmInitData;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment