mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Add DRM data to AnalyticsCollectorTest
This requires lots of new DRM plumbing in FakeMedia{Period,Source} and
FakeSampleStream.
Part of issue:#6765
PiperOrigin-RevId: 312072332
This commit is contained in:
parent
5b0e971f0e
commit
9c8cd4b575
14 changed files with 611 additions and 46 deletions
|
|
@ -42,6 +42,7 @@ import com.google.android.exoplayer2.Player.EventListener;
|
|||
import com.google.android.exoplayer2.Timeline.Window;
|
||||
import com.google.android.exoplayer2.analytics.AnalyticsListener;
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.ClippingMediaSource;
|
||||
import com.google.android.exoplayer2.source.CompositeMediaSource;
|
||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
|
|
@ -600,6 +601,7 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
|
|
@ -635,6 +637,7 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
|
|
@ -661,6 +664,7 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
|
|
@ -904,11 +908,16 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
// Defer completing preparation of the period until playback parameters have been set.
|
||||
fakeMediaPeriodHolder[0] =
|
||||
new FakeMediaPeriod(trackGroupArray, eventDispatcher, /* deferOnPrepared= */ true);
|
||||
new FakeMediaPeriod(
|
||||
trackGroupArray,
|
||||
drmSessionManager,
|
||||
eventDispatcher,
|
||||
/* deferOnPrepared= */ true);
|
||||
createPeriodCalledCountDownLatch.countDown();
|
||||
return fakeMediaPeriodHolder[0];
|
||||
}
|
||||
|
|
@ -950,11 +959,16 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
// Defer completing preparation of the period until seek has been sent.
|
||||
fakeMediaPeriodHolder[0] =
|
||||
new FakeMediaPeriod(trackGroupArray, eventDispatcher, /* deferOnPrepared= */ true);
|
||||
new FakeMediaPeriod(
|
||||
trackGroupArray,
|
||||
drmSessionManager,
|
||||
eventDispatcher,
|
||||
/* deferOnPrepared= */ true);
|
||||
createPeriodCalledCountDownLatch.countDown();
|
||||
return fakeMediaPeriodHolder[0];
|
||||
}
|
||||
|
|
@ -3666,6 +3680,7 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
return new FakeMediaPeriod(trackGroupArray, eventDispatcher) {
|
||||
|
|
@ -6367,6 +6382,7 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
return new FakeMediaPeriod(trackGroupArray, eventDispatcher) {
|
||||
|
|
@ -6442,9 +6458,10 @@ public final class ExoPlayerTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
return new FakeMediaPeriod(trackGroupArray, eventDispatcher) {
|
||||
return new FakeMediaPeriod(trackGroupArray, drmSessionManager, eventDispatcher) {
|
||||
private Loader loader = new Loader("oomLoader");
|
||||
|
||||
@Override
|
||||
|
|
@ -6456,11 +6473,15 @@ public final class ExoPlayerTest {
|
|||
|
||||
@Override
|
||||
protected SampleStream createSampleStream(
|
||||
long positionUs, TrackSelection selection, EventDispatcher eventDispatcher) {
|
||||
long positionUs,
|
||||
TrackSelection selection,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher) {
|
||||
// Create 3 samples without end of stream signal to test that all 3 samples are
|
||||
// still played before the exception is thrown.
|
||||
return new FakeSampleStream(
|
||||
selection.getSelectedFormat(),
|
||||
drmSessionManager,
|
||||
eventDispatcher,
|
||||
positionUs,
|
||||
/* timeUsIncrement= */ 0,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
|
|||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Window;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.ExoMediaDrm;
|
||||
import com.google.android.exoplayer2.drm.MediaDrmCallback;
|
||||
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||
|
|
@ -43,16 +49,19 @@ import com.google.android.exoplayer2.testutil.ActionSchedule;
|
|||
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
|
||||
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
|
||||
import com.google.android.exoplayer2.testutil.FakeAudioRenderer;
|
||||
import com.google.android.exoplayer2.testutil.FakeExoMediaDrm;
|
||||
import com.google.android.exoplayer2.testutil.FakeMediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Test;
|
||||
|
|
@ -107,6 +116,24 @@ public final class AnalyticsCollectorTest {
|
|||
private static final int EVENT_DRM_SESSION_RELEASED = 38;
|
||||
private static final int EVENT_VIDEO_FRAME_PROCESSING_OFFSET = 39;
|
||||
|
||||
private static final UUID DRM_SCHEME_UUID =
|
||||
UUID.nameUUIDFromBytes(TestUtil.createByteArray(7, 8, 9));
|
||||
|
||||
public static final DrmInitData DRM_DATA_1 =
|
||||
new DrmInitData(
|
||||
new DrmInitData.SchemeData(
|
||||
DRM_SCHEME_UUID,
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT.sampleMimeType,
|
||||
/* data= */ TestUtil.createByteArray(1, 2, 3)));
|
||||
public static final DrmInitData DRM_DATA_2 =
|
||||
new DrmInitData(
|
||||
new DrmInitData.SchemeData(
|
||||
DRM_SCHEME_UUID,
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT.sampleMimeType,
|
||||
/* data= */ TestUtil.createByteArray(4, 5, 6)));
|
||||
private static final Format VIDEO_FORMAT_DRM_1 =
|
||||
ExoPlayerTestRunner.VIDEO_FORMAT.buildUpon().setDrmInitData(DRM_DATA_1).build();
|
||||
|
||||
private static final int TIMEOUT_MS = 10000;
|
||||
private static final Timeline SINGLE_PERIOD_TIMELINE = new FakeTimeline(/* windowCount= */ 1);
|
||||
private static final EventWindowAndPeriodId WINDOW_0 =
|
||||
|
|
@ -114,6 +141,12 @@ public final class AnalyticsCollectorTest {
|
|||
private static final EventWindowAndPeriodId WINDOW_1 =
|
||||
new EventWindowAndPeriodId(/* windowIndex= */ 1, /* mediaPeriodId= */ null);
|
||||
|
||||
private final DrmSessionManager drmSessionManager =
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(DRM_SCHEME_UUID, uuid -> new FakeExoMediaDrm())
|
||||
.setMultiSession(true)
|
||||
.build(new EmptyDrmCallback());
|
||||
|
||||
private EventWindowAndPeriodId period0;
|
||||
private EventWindowAndPeriodId period1;
|
||||
private EventWindowAndPeriodId period0Seq0;
|
||||
|
|
@ -1158,6 +1191,71 @@ public final class AnalyticsCollectorTest {
|
|||
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drmEvents_singlePeriod() throws Exception {
|
||||
MediaSource mediaSource =
|
||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, drmSessionManager, VIDEO_FORMAT_DRM_1);
|
||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
||||
|
||||
populateEventIds(listener.lastReportedTimeline);
|
||||
assertThat(listener.getEvents(EVENT_DRM_ERROR)).isEmpty();
|
||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_ACQUIRED)).containsExactly(period0);
|
||||
assertThat(listener.getEvents(EVENT_DRM_KEYS_LOADED)).containsExactly(period0);
|
||||
// The release event is lost because it's posted to "ExoPlayerTest thread" after that thread
|
||||
// has been quit during clean-up.
|
||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_RELEASED)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drmEvents_periodWithSameDrmData_keysReused() throws Exception {
|
||||
MediaSource mediaSource =
|
||||
new ConcatenatingMediaSource(
|
||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, drmSessionManager, VIDEO_FORMAT_DRM_1),
|
||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, drmSessionManager, VIDEO_FORMAT_DRM_1));
|
||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
||||
|
||||
populateEventIds(listener.lastReportedTimeline);
|
||||
assertThat(listener.getEvents(EVENT_DRM_ERROR)).isEmpty();
|
||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_ACQUIRED)).containsExactly(period0, period1);
|
||||
assertThat(listener.getEvents(EVENT_DRM_KEYS_LOADED)).containsExactly(period0);
|
||||
// The period1 release event is lost because it's posted to "ExoPlayerTest thread" after that
|
||||
// thread has been quit during clean-up.
|
||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_RELEASED)).containsExactly(period0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drmEvents_periodWithDifferentDrmData_keysLoadedAgain() throws Exception {
|
||||
MediaSource mediaSource =
|
||||
new ConcatenatingMediaSource(
|
||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, drmSessionManager, VIDEO_FORMAT_DRM_1),
|
||||
new FakeMediaSource(
|
||||
SINGLE_PERIOD_TIMELINE,
|
||||
drmSessionManager,
|
||||
VIDEO_FORMAT_DRM_1.buildUpon().setDrmInitData(DRM_DATA_2).build()));
|
||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
||||
|
||||
populateEventIds(listener.lastReportedTimeline);
|
||||
assertThat(listener.getEvents(EVENT_DRM_ERROR)).isEmpty();
|
||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_ACQUIRED)).containsExactly(period0, period1);
|
||||
assertThat(listener.getEvents(EVENT_DRM_KEYS_LOADED)).containsExactly(period0, period1);
|
||||
// The period1 release event is lost because it's posted to "ExoPlayerTest thread" after that
|
||||
// thread has been quit during clean-up.
|
||||
assertThat(listener.getEvents(EVENT_DRM_SESSION_RELEASED)).containsExactly(period0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drmEvents_errorHandling() throws Exception {
|
||||
DrmSessionManager failingDrmSessionManager =
|
||||
new DefaultDrmSessionManager.Builder().build(new FailingDrmCallback());
|
||||
MediaSource mediaSource =
|
||||
new FakeMediaSource(SINGLE_PERIOD_TIMELINE, failingDrmSessionManager, VIDEO_FORMAT_DRM_1);
|
||||
TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
|
||||
|
||||
populateEventIds(listener.lastReportedTimeline);
|
||||
assertThat(listener.getEvents(EVENT_DRM_ERROR)).containsExactly(period0);
|
||||
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(period0);
|
||||
}
|
||||
|
||||
private void populateEventIds(Timeline timeline) {
|
||||
period0 =
|
||||
new EventWindowAndPeriodId(
|
||||
|
|
@ -1544,4 +1642,42 @@ public final class AnalyticsCollectorTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MediaDrmCallback} that returns empty byte arrays for both {@link
|
||||
* #executeProvisionRequest(UUID, ExoMediaDrm.ProvisionRequest)} and {@link
|
||||
* #executeKeyRequest(UUID, ExoMediaDrm.KeyRequest)}.
|
||||
*/
|
||||
private static final class EmptyDrmCallback implements MediaDrmCallback {
|
||||
@Override
|
||||
public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request)
|
||||
throws MediaDrmCallbackException {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request)
|
||||
throws MediaDrmCallbackException {
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link MediaDrmCallback} that throws exceptions for both {@link
|
||||
* #executeProvisionRequest(UUID, ExoMediaDrm.ProvisionRequest)} and {@link
|
||||
* #executeKeyRequest(UUID, ExoMediaDrm.KeyRequest)}.
|
||||
*/
|
||||
private static final class FailingDrmCallback implements MediaDrmCallback {
|
||||
@Override
|
||||
public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request)
|
||||
throws MediaDrmCallbackException {
|
||||
throw new RuntimeException("executeProvision failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request)
|
||||
throws MediaDrmCallbackException {
|
||||
throw new RuntimeException("executeKey failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import com.google.android.exoplayer2.C;
|
|||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.RendererCapabilities;
|
||||
import com.google.android.exoplayer2.RendererConfiguration;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
|
|
@ -111,6 +112,7 @@ public class MediaCodecAudioRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ AUDIO_AAC,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
||||
import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder;
|
||||
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
|
||||
|
|
@ -144,6 +145,7 @@ public class MetadataRendererTest {
|
|||
new Format[] {EMSG_FORMAT},
|
||||
new FakeSampleStream(
|
||||
EMSG_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 0,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.Player;
|
|||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Period;
|
||||
import com.google.android.exoplayer2.Timeline.Window;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.ClippingMediaSource.IllegalClippingException;
|
||||
import com.google.android.exoplayer2.source.MaskingMediaSource.DummyTimeline;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
|
|
@ -566,6 +567,7 @@ public final class ClippingMediaSourceTest {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
eventDispatcher.downstreamFormatChanged(
|
||||
|
|
@ -578,7 +580,12 @@ public final class ClippingMediaSourceTest {
|
|||
C.usToMs(eventStartUs),
|
||||
C.usToMs(eventEndUs)));
|
||||
return super.createFakeMediaPeriod(
|
||||
id, trackGroupArray, allocator, eventDispatcher, transferListener);
|
||||
id,
|
||||
trackGroupArray,
|
||||
allocator,
|
||||
drmSessionManager,
|
||||
eventDispatcher,
|
||||
transferListener);
|
||||
}
|
||||
};
|
||||
final ClippingMediaSource clippingMediaSource =
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import com.google.android.exoplayer2.RendererConfiguration;
|
|||
import com.google.android.exoplayer2.decoder.DecoderException;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||
import com.google.android.exoplayer2.testutil.FakeSampleStream;
|
||||
import com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem;
|
||||
|
|
@ -185,6 +186,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -213,6 +215,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -240,6 +243,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -270,6 +274,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream1 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -278,6 +283,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream2 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -314,6 +320,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream1 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -322,6 +329,7 @@ public final class DecoderVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream2 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ H264_FORMAT,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.google.android.exoplayer2.Format;
|
|||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.RendererCapabilities;
|
||||
import com.google.android.exoplayer2.RendererConfiguration;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
|
||||
|
|
@ -126,6 +127,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50_000,
|
||||
|
|
@ -162,6 +164,7 @@ public class MediaCodecVideoRendererTest {
|
|||
new Format[] {VIDEO_H264},
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 0,
|
||||
|
|
@ -199,6 +202,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ pAsp1,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 5000,
|
||||
|
|
@ -248,6 +252,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -282,6 +287,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -308,6 +314,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -333,6 +340,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -359,6 +367,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream1 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -367,6 +376,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream2 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -401,6 +411,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream1 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -409,6 +420,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream2 =
|
||||
new FakeSampleStream(
|
||||
/* format= */ VIDEO_H264,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
@ -450,6 +462,7 @@ public class MediaCodecVideoRendererTest {
|
|||
FakeSampleStream fakeSampleStream =
|
||||
new FakeSampleStream(
|
||||
/* format= */ mp4Uhd,
|
||||
DrmSessionManager.DUMMY,
|
||||
/* eventDispatcher= */ null,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 50,
|
||||
|
|
|
|||
|
|
@ -67,14 +67,6 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
|||
this.sequenceableLoader = new CompositeSequenceableLoader(new SequenceableLoader[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
for (ChunkSampleStream<FakeChunkSource> sampleStream : sampleStreams) {
|
||||
sampleStream.release();
|
||||
}
|
||||
super.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void prepare(Callback callback, long positionUs) {
|
||||
super.prepare(callback, positionUs);
|
||||
|
|
@ -141,8 +133,11 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
|||
}
|
||||
|
||||
@Override
|
||||
protected SampleStream createSampleStream(
|
||||
long positionUs, TrackSelection trackSelection, EventDispatcher eventDispatcher) {
|
||||
protected final SampleStream createSampleStream(
|
||||
long positionUs,
|
||||
TrackSelection trackSelection,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher) {
|
||||
FakeChunkSource chunkSource =
|
||||
chunkSourceFactory.createChunkSource(trackSelection, durationUs, transferListener);
|
||||
return new ChunkSampleStream<>(
|
||||
|
|
@ -159,11 +154,19 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
|||
}
|
||||
|
||||
@Override
|
||||
// sampleStream is created by createSampleStream() above.
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void seekSampleStream(SampleStream sampleStream, long positionUs) {
|
||||
((ChunkSampleStream<FakeChunkSource>) sampleStream).seekToUs(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
// sampleStream is created by createSampleStream() above.
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void releaseSampleStream(SampleStream sampleStream) {
|
||||
((ChunkSampleStream<FakeChunkSource>) sampleStream).release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContinueLoadingRequested(ChunkSampleStream<FakeChunkSource> source) {
|
||||
Assertions.checkStateNotNull(callback).onContinueLoadingRequested(this);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.testutil;
|
|||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Period;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
|
|
@ -37,7 +38,7 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource {
|
|||
Timeline timeline,
|
||||
TrackGroupArray trackGroupArray,
|
||||
FakeChunkSource.Factory chunkSourceFactory) {
|
||||
super(timeline, trackGroupArray);
|
||||
super(timeline, DrmSessionManager.DUMMY, trackGroupArray);
|
||||
this.chunkSourceFactory = chunkSourceFactory;
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
Period period = Util.castNonNull(getTimeline()).getPeriodByUid(id.periodUid, new Period());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.exoplayer2.testutil;
|
||||
|
||||
import android.media.DeniedByServerException;
|
||||
import android.media.MediaCryptoException;
|
||||
import android.media.MediaDrmException;
|
||||
import android.media.NotProvisionedException;
|
||||
import android.os.PersistableBundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||
import com.google.android.exoplayer2.drm.ExoMediaDrm;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** A fake implementation of {@link ExoMediaDrm} for use in tests. */
|
||||
@RequiresApi(18)
|
||||
public class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
|
||||
private static final KeyRequest DUMMY_KEY_REQUEST =
|
||||
new KeyRequest(TestUtil.createByteArray(4, 5, 6), "foo.test");
|
||||
|
||||
private static final ProvisionRequest DUMMY_PROVISION_REQUEST =
|
||||
new ProvisionRequest(TestUtil.createByteArray(7, 8, 9), "bar.test");
|
||||
|
||||
private final Map<String, byte[]> byteProperties;
|
||||
private final Map<String, String> stringProperties;
|
||||
private final Set<List<Byte>> openSessionIds;
|
||||
private final AtomicInteger sessionIdGenerator;
|
||||
|
||||
private int referenceCount;
|
||||
|
||||
/**
|
||||
* Constructs an instance that returns random and unique {@code sessionIds} for subsequent calls
|
||||
* to {@link #openSession()}.
|
||||
*/
|
||||
public FakeExoMediaDrm() {
|
||||
byteProperties = new HashMap<>();
|
||||
stringProperties = new HashMap<>();
|
||||
openSessionIds = new HashSet<>();
|
||||
sessionIdGenerator = new AtomicInteger();
|
||||
|
||||
referenceCount = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnEventListener(@Nullable OnEventListener listener) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnKeyStatusChangeListener(@Nullable OnKeyStatusChangeListener listener) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnExpirationUpdateListener(@Nullable OnExpirationUpdateListener listener) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] openSession() throws MediaDrmException {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
byte[] sessionId =
|
||||
TestUtil.buildTestData(/* length= */ 10, sessionIdGenerator.incrementAndGet());
|
||||
if (!openSessionIds.add(toByteList(sessionId))) {
|
||||
throw new MediaDrmException(
|
||||
Util.formatInvariant(
|
||||
"Generated sessionId[%s] clashes with already-open session",
|
||||
sessionIdGenerator.get()));
|
||||
}
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeSession(byte[] sessionId) {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
Assertions.checkState(openSessionIds.remove(toByteList(sessionId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyRequest getKeyRequest(
|
||||
byte[] scope,
|
||||
@Nullable List<DrmInitData.SchemeData> schemeDatas,
|
||||
int keyType,
|
||||
@Nullable HashMap<String, String> optionalParameters)
|
||||
throws NotProvisionedException {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
return DUMMY_KEY_REQUEST;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public byte[] provideKeyResponse(byte[] scope, byte[] response)
|
||||
throws NotProvisionedException, DeniedByServerException {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProvisionRequest getProvisionRequest() {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
return DUMMY_PROVISION_REQUEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideProvisionResponse(byte[] response) throws DeniedByServerException {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> queryKeyStatus(byte[] sessionId) {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acquire() {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
referenceCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
referenceCount--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreKeys(byte[] sessionId, byte[] keySetId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PersistableBundle getMetrics() {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPropertyString(String propertyName) {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
@Nullable String value = stringProperties.get(propertyName);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Unrecognized propertyName: " + propertyName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPropertyByteArray(String propertyName) {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
@Nullable byte[] value = byteProperties.get(propertyName);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Unrecognized propertyName: " + propertyName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyString(String propertyName, String value) {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
stringProperties.put(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyByteArray(String propertyName, byte[] value) {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
byteProperties.put(propertyName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExoMediaCrypto createMediaCrypto(byte[] sessionId) throws MediaCryptoException {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
Assertions.checkState(openSessionIds.contains(toByteList(sessionId)));
|
||||
return new FakeExoMediaCrypto();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends ExoMediaCrypto> getExoMediaCryptoType() {
|
||||
return FakeExoMediaCrypto.class;
|
||||
}
|
||||
|
||||
private static List<Byte> toByteList(byte[] byteArray) {
|
||||
List<Byte> result = new ArrayList<>(byteArray.length);
|
||||
for (byte b : byteArray) {
|
||||
result.add(b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class FakeExoMediaCrypto implements ExoMediaCrypto {}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import android.os.SystemClock;
|
|||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.SeekParameters;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||
|
|
@ -49,6 +50,7 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
|
||||
private final TrackGroupArray trackGroupArray;
|
||||
private final List<SampleStream> sampleStreams;
|
||||
private final DrmSessionManager drmSessionManager;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final long fakePreparationLoadTaskId;
|
||||
|
||||
|
|
@ -62,23 +64,46 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
private long discontinuityPositionUs;
|
||||
|
||||
/**
|
||||
* Constructs a FakeMediaPeriod.
|
||||
*
|
||||
* @param trackGroupArray The track group array.
|
||||
* @param eventDispatcher A dispatcher for media source events.
|
||||
*/
|
||||
public FakeMediaPeriod(TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher) {
|
||||
this(trackGroupArray, eventDispatcher, /* deferOnPrepared */ false);
|
||||
this(trackGroupArray, DrmSessionManager.DUMMY, eventDispatcher, /* deferOnPrepared */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a FakeMediaPeriod.
|
||||
*
|
||||
* @param trackGroupArray The track group array.
|
||||
* @param drmSessionManager The {@link DrmSessionManager} used for DRM interactions.
|
||||
* @param eventDispatcher A dispatcher for media source events.
|
||||
*/
|
||||
public FakeMediaPeriod(
|
||||
TrackGroupArray trackGroupArray,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher) {
|
||||
this(trackGroupArray, drmSessionManager, eventDispatcher, /* deferOnPrepared */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a FakeMediaPeriod.
|
||||
*
|
||||
* @param trackGroupArray The track group array.
|
||||
* @param drmSessionManager The DrmSessionManager used for DRM interactions.
|
||||
* @param eventDispatcher A dispatcher for media source events.
|
||||
* @param deferOnPrepared Whether {@link MediaPeriod.Callback#onPrepared(MediaPeriod)} should be
|
||||
* called only after {@link #setPreparationComplete()} has been called. If {@code false}
|
||||
* preparation completes immediately.
|
||||
*/
|
||||
public FakeMediaPeriod(
|
||||
TrackGroupArray trackGroupArray, EventDispatcher eventDispatcher, boolean deferOnPrepared) {
|
||||
TrackGroupArray trackGroupArray,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
boolean deferOnPrepared) {
|
||||
this.trackGroupArray = trackGroupArray;
|
||||
this.drmSessionManager = drmSessionManager;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
this.deferOnPrepared = deferOnPrepared;
|
||||
discontinuityPositionUs = C.TIME_UNSET;
|
||||
|
|
@ -118,6 +143,9 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
|
||||
public void release() {
|
||||
prepared = false;
|
||||
for (int i = 0; i < sampleStreams.size(); i++) {
|
||||
releaseSampleStream(sampleStreams.get(i));
|
||||
}
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +201,7 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
int indexInTrackGroup = selection.getIndexInTrackGroup(selection.getSelectedIndex());
|
||||
assertThat(indexInTrackGroup).isAtLeast(0);
|
||||
assertThat(indexInTrackGroup).isLessThan(trackGroup.length);
|
||||
streams[i] = createSampleStream(positionUs, selection, eventDispatcher);
|
||||
streams[i] = createSampleStream(positionUs, selection, drmSessionManager, eventDispatcher);
|
||||
sampleStreams.add(streams[i]);
|
||||
streamResetFlags[i] = true;
|
||||
}
|
||||
|
|
@ -245,13 +273,18 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
*
|
||||
* @param positionUs The position at which the tracks were selected, in microseconds.
|
||||
* @param selection A selection of tracks.
|
||||
* @param drmSessionManager The DRM session manager.
|
||||
* @param eventDispatcher A dispatcher for events that should be used by the sample stream.
|
||||
* @return A {@link SampleStream} for this selection.
|
||||
*/
|
||||
protected SampleStream createSampleStream(
|
||||
long positionUs, TrackSelection selection, EventDispatcher eventDispatcher) {
|
||||
long positionUs,
|
||||
TrackSelection selection,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher) {
|
||||
return new FakeSampleStream(
|
||||
selection.getSelectedFormat(),
|
||||
drmSessionManager,
|
||||
eventDispatcher,
|
||||
positionUs,
|
||||
/* timeUsIncrement= */ 0,
|
||||
|
|
@ -262,7 +295,7 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
* Seeks inside the given sample stream.
|
||||
*
|
||||
* @param sampleStream A sample stream that was created by a call to {@link
|
||||
* #createSampleStream(long, TrackSelection, EventDispatcher)}.
|
||||
* #createSampleStream(long, TrackSelection, DrmSessionManager, EventDispatcher)}.
|
||||
* @param positionUs The position to seek to, in microseconds.
|
||||
*/
|
||||
protected void seekSampleStream(SampleStream sampleStream, long positionUs) {
|
||||
|
|
@ -271,6 +304,16 @@ public class FakeMediaPeriod implements MediaPeriod {
|
|||
.resetSampleStreamItems(positionUs, FakeSampleStream.SINGLE_SAMPLE_THEN_END_OF_STREAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the given sample stream.
|
||||
*
|
||||
* @param sampleStream A sample stream that was created by a call to {@link
|
||||
* #createSampleStream(long, TrackSelection, DrmSessionManager, EventDispatcher)}.
|
||||
*/
|
||||
protected void releaseSampleStream(SampleStream sampleStream) {
|
||||
((FakeSampleStream) sampleStream).release();
|
||||
}
|
||||
|
||||
private void finishPreparation() {
|
||||
prepared = true;
|
||||
Util.castNonNull(prepareCallback).onPrepared(this);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.C;
|
|||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Period;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.BaseMediaSource;
|
||||
import com.google.android.exoplayer2.source.ForwardingTimeline;
|
||||
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||
|
|
@ -73,6 +74,7 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
private final TrackGroupArray trackGroupArray;
|
||||
private final ArrayList<FakeMediaPeriod> activeMediaPeriods;
|
||||
private final ArrayList<MediaPeriodId> createdMediaPeriods;
|
||||
private final DrmSessionManager drmSessionManager;
|
||||
|
||||
private @MonotonicNonNull Timeline timeline;
|
||||
private boolean preparedSource;
|
||||
|
|
@ -87,7 +89,19 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
* can be manually set later using {@link #setNewSourceInfo(Timeline)}.
|
||||
*/
|
||||
public FakeMediaSource(@Nullable Timeline timeline, Format... formats) {
|
||||
this(timeline, buildTrackGroupArray(formats));
|
||||
this(timeline, DrmSessionManager.DUMMY, formats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link FakeMediaSource}. This media source creates {@link FakeMediaPeriod}s with a
|
||||
* {@link TrackGroupArray} using the given {@link Format}s. It passes {@code drmSessionManager}
|
||||
* into the created periods. The provided {@link Timeline} may be null to prevent an immediate
|
||||
* source info refresh message when preparing the media source. It can be manually set later using
|
||||
* {@link #setNewSourceInfo(Timeline)}.
|
||||
*/
|
||||
public FakeMediaSource(
|
||||
@Nullable Timeline timeline, DrmSessionManager drmSessionManager, Format... formats) {
|
||||
this(timeline, drmSessionManager, buildTrackGroupArray(formats));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,13 +110,17 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
* immediate source info refresh message when preparing the media source. It can be manually set
|
||||
* later using {@link #setNewSourceInfo(Timeline)}.
|
||||
*/
|
||||
public FakeMediaSource(@Nullable Timeline timeline, TrackGroupArray trackGroupArray) {
|
||||
public FakeMediaSource(
|
||||
@Nullable Timeline timeline,
|
||||
DrmSessionManager drmSessionManager,
|
||||
TrackGroupArray trackGroupArray) {
|
||||
if (timeline != null) {
|
||||
this.timeline = timeline;
|
||||
}
|
||||
this.trackGroupArray = trackGroupArray;
|
||||
this.activeMediaPeriods = new ArrayList<>();
|
||||
this.createdMediaPeriods = new ArrayList<>();
|
||||
this.drmSessionManager = drmSessionManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
@ -136,6 +154,7 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
public synchronized void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
||||
assertThat(preparedSource).isFalse();
|
||||
transferListener = mediaTransferListener;
|
||||
drmSessionManager.prepare();
|
||||
preparedSource = true;
|
||||
releasedSource = false;
|
||||
sourceInfoRefreshHandler = Util.createHandler();
|
||||
|
|
@ -159,7 +178,8 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
EventDispatcher eventDispatcher =
|
||||
createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs());
|
||||
FakeMediaPeriod mediaPeriod =
|
||||
createFakeMediaPeriod(id, trackGroupArray, allocator, eventDispatcher, transferListener);
|
||||
createFakeMediaPeriod(
|
||||
id, trackGroupArray, allocator, drmSessionManager, eventDispatcher, transferListener);
|
||||
activeMediaPeriods.add(mediaPeriod);
|
||||
createdMediaPeriods.add(id);
|
||||
return mediaPeriod;
|
||||
|
|
@ -179,6 +199,7 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
assertThat(preparedSource).isTrue();
|
||||
assertThat(releasedSource).isFalse();
|
||||
assertThat(activeMediaPeriods.isEmpty()).isTrue();
|
||||
drmSessionManager.release();
|
||||
releasedSource = true;
|
||||
preparedSource = false;
|
||||
Util.castNonNull(sourceInfoRefreshHandler).removeCallbacksAndMessages(null);
|
||||
|
|
@ -242,9 +263,11 @@ public class FakeMediaSource extends BaseMediaSource {
|
|||
MediaPeriodId id,
|
||||
TrackGroupArray trackGroupArray,
|
||||
Allocator allocator,
|
||||
DrmSessionManager drmSessionManager,
|
||||
EventDispatcher eventDispatcher,
|
||||
@Nullable TransferListener transferListener) {
|
||||
return new FakeMediaPeriod(trackGroupArray, eventDispatcher);
|
||||
return new FakeMediaPeriod(
|
||||
trackGroupArray, drmSessionManager, eventDispatcher, /* deferOnPrepared= */ false);
|
||||
}
|
||||
|
||||
private void finishSourcePreparation() {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.testutil;
|
||||
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.BaseRenderer;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
|
|
@ -24,6 +24,7 @@ import com.google.android.exoplayer2.FormatHolder;
|
|||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.RendererCapabilities;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.drm.DrmSession;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
|
@ -49,6 +50,8 @@ public class FakeRenderer extends BaseRenderer {
|
|||
|
||||
private final DecoderInputBuffer buffer;
|
||||
|
||||
@Nullable private DrmSession currentDrmSession;
|
||||
|
||||
private long playbackPositionUs;
|
||||
private long lastSamplePositionUs;
|
||||
private boolean hasPendingBuffer;
|
||||
|
|
@ -91,7 +94,10 @@ public class FakeRenderer extends BaseRenderer {
|
|||
buffer.clear();
|
||||
@SampleStream.ReadDataResult
|
||||
int result = readSource(formatHolder, buffer, /* formatRequired= */ false);
|
||||
|
||||
if (result == C.RESULT_FORMAT_READ) {
|
||||
DrmSession.replaceSession(currentDrmSession, formatHolder.drmSession);
|
||||
currentDrmSession = formatHolder.drmSession;
|
||||
Format format = Assertions.checkNotNull(formatHolder.format);
|
||||
if (MimeTypes.getTrackType(format.sampleMimeType) != getTrackType()) {
|
||||
throw ExoPlaybackException.createForRenderer(
|
||||
|
|
@ -147,6 +153,14 @@ public class FakeRenderer extends BaseRenderer {
|
|||
: RendererCapabilities.create(FORMAT_UNSUPPORTED_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
if (currentDrmSession != null) {
|
||||
currentDrmSession.release(/* eventDispatcher= */ null);
|
||||
currentDrmSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the renderer reads a new format. */
|
||||
protected void onFormatChanged(Format format) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,16 +15,24 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.testutil;
|
||||
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.drm.DrmSession;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* Fake {@link SampleStream} that outputs a given {@link Format}, any amount of {@link
|
||||
|
|
@ -85,15 +93,16 @@ public class FakeSampleStream implements SampleStream {
|
|||
new FakeSampleStreamItem(new byte[] {0}), FakeSampleStreamItem.END_OF_STREAM_ITEM
|
||||
};
|
||||
|
||||
private final Format initialFormat;
|
||||
private final ArrayDeque<FakeSampleStreamItem> fakeSampleStreamItems;
|
||||
private final int timeUsIncrement;
|
||||
|
||||
private final DrmSessionManager drmSessionManager;
|
||||
@Nullable private final EventDispatcher eventDispatcher;
|
||||
|
||||
private Format format;
|
||||
private @MonotonicNonNull Format downstreamFormat;
|
||||
private long timeUs;
|
||||
private boolean readFormat;
|
||||
private boolean readEOSBuffer;
|
||||
@Nullable private DrmSession currentDrmSession;
|
||||
|
||||
/**
|
||||
* Creates fake sample stream which outputs the given {@link Format}, optionally one sample with
|
||||
|
|
@ -107,6 +116,7 @@ public class FakeSampleStream implements SampleStream {
|
|||
Format format, @Nullable EventDispatcher eventDispatcher, boolean shouldOutputSample) {
|
||||
this(
|
||||
format,
|
||||
DrmSessionManager.DUMMY,
|
||||
eventDispatcher,
|
||||
/* firstSampleTimeUs= */ 0,
|
||||
/* timeUsIncrement= */ 0,
|
||||
|
|
@ -120,6 +130,7 @@ public class FakeSampleStream implements SampleStream {
|
|||
* FakeSampleStreamItem items}, then end of stream.
|
||||
*
|
||||
* @param format The {@link Format} to output.
|
||||
* @param drmSessionManager A {@link DrmSessionManager} for DRM interactions.
|
||||
* @param eventDispatcher An {@link EventDispatcher} to notify of read events.
|
||||
* @param firstSampleTimeUs The time at which samples will start being output, in microseconds.
|
||||
* @param timeUsIncrement The time each sample should increase by, in microseconds.
|
||||
|
|
@ -129,11 +140,13 @@ public class FakeSampleStream implements SampleStream {
|
|||
*/
|
||||
public FakeSampleStream(
|
||||
Format format,
|
||||
DrmSessionManager drmSessionManager,
|
||||
@Nullable EventDispatcher eventDispatcher,
|
||||
long firstSampleTimeUs,
|
||||
int timeUsIncrement,
|
||||
FakeSampleStreamItem... fakeSampleStreamItems) {
|
||||
this.format = format;
|
||||
this.initialFormat = format;
|
||||
this.drmSessionManager = drmSessionManager;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
this.fakeSampleStreamItems = new ArrayDeque<>(Arrays.asList(fakeSampleStreamItems));
|
||||
this.timeUs = firstSampleTimeUs;
|
||||
|
|
@ -164,16 +177,21 @@ public class FakeSampleStream implements SampleStream {
|
|||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return !readFormat || readEOSBuffer || !fakeSampleStreamItems.isEmpty();
|
||||
if (fakeSampleStreamItems.isEmpty()) {
|
||||
return readEOSBuffer || downstreamFormat == null;
|
||||
}
|
||||
if (fakeSampleStreamItems.peek().format != null) {
|
||||
// A format can be read.
|
||||
return true;
|
||||
}
|
||||
return mayReadSample();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(
|
||||
FormatHolder formatHolder, DecoderInputBuffer buffer, boolean formatRequired) {
|
||||
if (!readFormat || formatRequired) {
|
||||
readFormat = true;
|
||||
formatHolder.format = format;
|
||||
notifyEventDispatcher(formatHolder);
|
||||
if (downstreamFormat == null || formatRequired) {
|
||||
onFormatResult(downstreamFormat == null ? initialFormat : downstreamFormat, formatHolder);
|
||||
return C.RESULT_FORMAT_READ;
|
||||
}
|
||||
// Once an EOS buffer has been read, send EOS every time.
|
||||
|
|
@ -181,33 +199,79 @@ public class FakeSampleStream implements SampleStream {
|
|||
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||
return C.RESULT_BUFFER_READ;
|
||||
}
|
||||
|
||||
if (!fakeSampleStreamItems.isEmpty()) {
|
||||
FakeSampleStreamItem fakeSampleStreamItem = fakeSampleStreamItems.remove();
|
||||
if (fakeSampleStreamItem.format != null) {
|
||||
format = fakeSampleStreamItem.format;
|
||||
formatHolder.format = format;
|
||||
notifyEventDispatcher(formatHolder);
|
||||
onFormatResult(fakeSampleStreamItem.format, formatHolder);
|
||||
return C.RESULT_FORMAT_READ;
|
||||
}
|
||||
if (fakeSampleStreamItem.sampleData != null) {
|
||||
byte[] sampleData = fakeSampleStreamItem.sampleData;
|
||||
} else {
|
||||
byte[] sampleData = Assertions.checkNotNull(fakeSampleStreamItem.sampleData);
|
||||
if (fakeSampleStreamItem.flags != 0) {
|
||||
buffer.setFlags(fakeSampleStreamItem.flags);
|
||||
if (buffer.isEndOfStream()) {
|
||||
readEOSBuffer = true;
|
||||
return C.RESULT_BUFFER_READ;
|
||||
}
|
||||
}
|
||||
if (!mayReadSample()) {
|
||||
// Put the item back so we can consume it next time.
|
||||
fakeSampleStreamItems.addFirst(fakeSampleStreamItem);
|
||||
return C.RESULT_NOTHING_READ;
|
||||
}
|
||||
buffer.timeUs = timeUs;
|
||||
timeUs += timeUsIncrement;
|
||||
buffer.ensureSpaceForWrite(sampleData.length);
|
||||
buffer.data.put(sampleData);
|
||||
if (fakeSampleStreamItem.flags != 0) {
|
||||
buffer.setFlags(fakeSampleStreamItem.flags);
|
||||
readEOSBuffer = buffer.isEndOfStream();
|
||||
}
|
||||
return C.RESULT_BUFFER_READ;
|
||||
}
|
||||
}
|
||||
return C.RESULT_NOTHING_READ;
|
||||
}
|
||||
|
||||
private void onFormatResult(Format newFormat, FormatHolder outputFormatHolder) {
|
||||
outputFormatHolder.format = newFormat;
|
||||
@Nullable
|
||||
DrmInitData oldDrmInitData = downstreamFormat == null ? null : downstreamFormat.drmInitData;
|
||||
boolean isFirstFormat = downstreamFormat == null;
|
||||
downstreamFormat = newFormat;
|
||||
@Nullable DrmInitData newDrmInitData = newFormat.drmInitData;
|
||||
outputFormatHolder.drmSession = currentDrmSession;
|
||||
notifyEventDispatcher(outputFormatHolder);
|
||||
if (!isFirstFormat && Util.areEqual(oldDrmInitData, newDrmInitData)) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
// Ensure we acquire the new session before releasing the previous one in case the same session
|
||||
// is being used for both DrmInitData.
|
||||
@Nullable DrmSession previousSession = currentDrmSession;
|
||||
Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper());
|
||||
currentDrmSession =
|
||||
newDrmInitData != null
|
||||
? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData)
|
||||
: drmSessionManager.acquirePlaceholderSession(
|
||||
playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType));
|
||||
outputFormatHolder.drmSession = currentDrmSession;
|
||||
|
||||
if (previousSession != null) {
|
||||
previousSession.release(eventDispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean mayReadSample() {
|
||||
@Nullable DrmSession drmSession = this.currentDrmSession;
|
||||
return drmSession == null
|
||||
|| drmSession.getState() == DrmSession.STATE_OPENED_WITH_KEYS
|
||||
|| (!fakeSampleStreamItems.isEmpty()
|
||||
&& (fakeSampleStreamItems.peek().flags & C.BUFFER_FLAG_ENCRYPTED) == 0
|
||||
&& drmSession.playClearSamplesWithoutKeys());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowError() throws IOException {
|
||||
// Do nothing.
|
||||
if (currentDrmSession != null && currentDrmSession.getState() == DrmSession.STATE_ERROR) {
|
||||
throw Assertions.checkNotNull(currentDrmSession.getError());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -215,6 +279,14 @@ public class FakeSampleStream implements SampleStream {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/** Release this SampleStream and all underlying resources. */
|
||||
public void release() {
|
||||
if (currentDrmSession != null) {
|
||||
currentDrmSession.release(eventDispatcher);
|
||||
currentDrmSession = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyEventDispatcher(FormatHolder formatHolder) {
|
||||
if (eventDispatcher != null) {
|
||||
eventDispatcher.downstreamFormatChanged(
|
||||
|
|
|
|||
Loading…
Reference in a new issue