mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix NPE when reading from a SampleQueue from a loading thread
Issue: #7273 PiperOrigin-RevId: 308238035
This commit is contained in:
parent
4df7470da5
commit
30c55d117e
7 changed files with 64 additions and 12 deletions
|
|
@ -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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
* 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 {
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
// 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 @@
|
||||||
*/
|
*/
|
||||||
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
|
||||||
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;
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue