Plumb DrmSessionManager into HlsMediaSource

PiperOrigin-RevId: 259520431
This commit is contained in:
aquilescanta 2019-07-23 13:53:59 +01:00 committed by Oliver Woodman
parent e6bafec418
commit 2a8cf2f5ef
6 changed files with 87 additions and 18 deletions

View File

@ -484,7 +484,9 @@ public class PlayerActivity extends AppCompatActivity
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
case C.TYPE_HLS:
return new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri);
return new HlsMediaSource.Factory(dataSourceFactory)
.setDrmSessionManager(drmSessionManager)
.createMediaSource(uri);
case C.TYPE_OTHER:
return new ProgressiveMediaSource.Factory(dataSourceFactory)
.setDrmSessionManager(drmSessionManager)

View File

@ -22,6 +22,8 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.SeekParameters;
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.extractor.Extractor;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
@ -63,6 +65,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private final HlsPlaylistTracker playlistTracker;
private final HlsDataSourceFactory dataSourceFactory;
@Nullable private final TransferListener mediaTransferListener;
private final DrmSessionManager<?> drmSessionManager;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final EventDispatcher eventDispatcher;
private final Allocator allocator;
@ -91,6 +94,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
* and keys.
* @param mediaTransferListener The transfer listener to inform of any media data transfers. May
* be null if no listener is available.
* @param drmSessionManager The {@link DrmSessionManager} to acquire {@link DrmSession
* DrmSessions} with.
* @param loadErrorHandlingPolicy A {@link LoadErrorHandlingPolicy}.
* @param eventDispatcher A dispatcher to notify of events.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
@ -104,6 +109,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
HlsPlaylistTracker playlistTracker,
HlsDataSourceFactory dataSourceFactory,
@Nullable TransferListener mediaTransferListener,
DrmSessionManager<?> drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher,
Allocator allocator,
@ -114,6 +120,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
this.playlistTracker = playlistTracker;
this.dataSourceFactory = dataSourceFactory;
this.mediaTransferListener = mediaTransferListener;
this.drmSessionManager = drmSessionManager;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher;
this.allocator = allocator;
@ -735,6 +742,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
allocator,
positionUs,
muxedAudioFormat,
drmSessionManager,
loadErrorHandlingPolicy,
eventDispatcher);
}

View File

@ -20,6 +20,8 @@ import android.os.Handler;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.BaseMediaSource;
@ -65,6 +67,7 @@ public final class HlsMediaSource extends BaseMediaSource
@Nullable private List<StreamKey> streamKeys;
private HlsPlaylistTracker.Factory playlistTrackerFactory;
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private DrmSessionManager<?> drmSessionManager;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private boolean allowChunklessPreparation;
private boolean useSessionKeys;
@ -93,6 +96,7 @@ public final class HlsMediaSource extends BaseMediaSource
playlistParserFactory = new DefaultHlsPlaylistParserFactory();
playlistTrackerFactory = DefaultHlsPlaylistTracker.FACTORY;
extractorFactory = HlsExtractorFactory.DEFAULT;
drmSessionManager = DrmSessionManager.getDummyDrmSessionManager();
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
}
@ -127,6 +131,20 @@ public final class HlsMediaSource extends BaseMediaSource
return this;
}
/**
* Sets the {@link DrmSessionManager} to use for acquiring {@link DrmSession DrmSessions}. The
* default value is {@link DrmSessionManager#DUMMY}.
*
* @param drmSessionManager The {@link DrmSessionManager}.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public Factory setDrmSessionManager(DrmSessionManager<?> drmSessionManager) {
Assertions.checkState(!isCreateCalled);
this.drmSessionManager = drmSessionManager;
return this;
}
/**
* Sets the {@link LoadErrorHandlingPolicy}. The default value is created by calling {@link
* DefaultLoadErrorHandlingPolicy#DefaultLoadErrorHandlingPolicy()}.
@ -271,6 +289,7 @@ public final class HlsMediaSource extends BaseMediaSource
hlsDataSourceFactory,
extractorFactory,
compositeSequenceableLoaderFactory,
drmSessionManager,
loadErrorHandlingPolicy,
playlistTrackerFactory.createTracker(
hlsDataSourceFactory, loadErrorHandlingPolicy, playlistParserFactory),
@ -297,6 +316,7 @@ public final class HlsMediaSource extends BaseMediaSource
private final Uri manifestUri;
private final HlsDataSourceFactory dataSourceFactory;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final DrmSessionManager<?> drmSessionManager;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final boolean allowChunklessPreparation;
private final boolean useSessionKeys;
@ -310,6 +330,7 @@ public final class HlsMediaSource extends BaseMediaSource
HlsDataSourceFactory dataSourceFactory,
HlsExtractorFactory extractorFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
DrmSessionManager<?> drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
HlsPlaylistTracker playlistTracker,
boolean allowChunklessPreparation,
@ -319,6 +340,7 @@ public final class HlsMediaSource extends BaseMediaSource
this.dataSourceFactory = dataSourceFactory;
this.extractorFactory = extractorFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
this.drmSessionManager = drmSessionManager;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.playlistTracker = playlistTracker;
this.allowChunklessPreparation = allowChunklessPreparation;
@ -352,6 +374,7 @@ public final class HlsMediaSource extends BaseMediaSource
playlistTracker,
dataSourceFactory,
mediaTransferListener,
drmSessionManager,
loadErrorHandlingPolicy,
eventDispatcher,
allocator,

View File

@ -62,8 +62,11 @@ import java.io.IOException;
if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL) {
throw new SampleQueueMappingException(
sampleStreamWrapper.getTrackGroups().get(trackGroupIndex).getFormat(0).sampleMimeType);
} else if (sampleQueueIndex == HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_PENDING) {
sampleStreamWrapper.maybeThrowError();
} else if (sampleQueueIndex != HlsSampleStreamWrapper.SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL) {
sampleStreamWrapper.maybeThrowError(sampleQueueIndex);
}
sampleStreamWrapper.maybeThrowError();
}
@Override

View File

@ -23,12 +23,15 @@ 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.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
import com.google.android.exoplayer2.source.DecryptableSampleQueueReader;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.SampleQueue;
import com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener;
@ -94,6 +97,7 @@ import java.util.Set;
private final HlsChunkSource chunkSource;
private final Allocator allocator;
private final Format muxedAudioFormat;
private final DrmSessionManager<?> drmSessionManager;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final Loader loader;
private final EventDispatcher eventDispatcher;
@ -107,6 +111,7 @@ import java.util.Set;
private final Map<String, DrmInitData> overridingDrmInitData;
private SampleQueue[] sampleQueues;
private DecryptableSampleQueueReader[] sampleQueueReaders;
private int[] sampleQueueTrackIds;
private boolean audioSampleQueueMappingDone;
private int audioSampleQueueIndex;
@ -154,6 +159,8 @@ import java.util.Set;
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param positionUs The position from which to start loading media.
* @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist.
* @param drmSessionManager The {@link DrmSessionManager} to acquire {@link DrmSession
* DrmSessions} with.
* @param loadErrorHandlingPolicy A {@link LoadErrorHandlingPolicy}.
* @param eventDispatcher A dispatcher to notify of events.
*/
@ -165,6 +172,7 @@ import java.util.Set;
Allocator allocator,
long positionUs,
Format muxedAudioFormat,
DrmSessionManager<?> drmSessionManager,
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
EventDispatcher eventDispatcher) {
this.trackType = trackType;
@ -173,6 +181,7 @@ import java.util.Set;
this.overridingDrmInitData = overridingDrmInitData;
this.allocator = allocator;
this.muxedAudioFormat = muxedAudioFormat;
this.drmSessionManager = drmSessionManager;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
this.eventDispatcher = eventDispatcher;
loader = new Loader("Loader:HlsSampleStreamWrapper");
@ -181,6 +190,7 @@ import java.util.Set;
audioSampleQueueIndex = C.INDEX_UNSET;
videoSampleQueueIndex = C.INDEX_UNSET;
sampleQueues = new SampleQueue[0];
sampleQueueReaders = new DecryptableSampleQueueReader[0];
sampleQueueIsAudioVideoFlags = new boolean[0];
sampleQueuesEnabledStates = new boolean[0];
mediaChunks = new ArrayList<>();
@ -211,7 +221,7 @@ import java.util.Set;
public void prepareWithMasterPlaylistInfo(
TrackGroup[] trackGroups, int primaryTrackGroupIndex, int... optionalTrackGroupsIndices) {
prepared = true;
this.trackGroups = new TrackGroupArray(trackGroups);
this.trackGroups = createTrackGroupArrayWithDrmInfo(trackGroups);
optionalTrackGroups = new HashSet<>();
for (int optionalTrackGroupIndex : optionalTrackGroupsIndices) {
optionalTrackGroups.add(this.trackGroups.get(optionalTrackGroupIndex));
@ -438,6 +448,9 @@ import java.util.Set;
for (SampleQueue sampleQueue : sampleQueues) {
sampleQueue.discardToEnd();
}
for (DecryptableSampleQueueReader reader : sampleQueueReaders) {
reader.release();
}
}
loader.release(this);
handler.removeCallbacksAndMessages(null);
@ -448,6 +461,9 @@ import java.util.Set;
@Override
public void onLoaderReleased() {
resetSampleQueues();
for (DecryptableSampleQueueReader reader : sampleQueueReaders) {
reader.release();
}
}
public void setIsTimestampMaster(boolean isTimestampMaster) {
@ -461,7 +477,12 @@ import java.util.Set;
// SampleStream implementation.
public boolean isReady(int sampleQueueIndex) {
return loadingFinished || (!isPendingReset() && sampleQueues[sampleQueueIndex].hasNextSample());
return !isPendingReset() && sampleQueueReaders[sampleQueueIndex].isReady(loadingFinished);
}
public void maybeThrowError(int sampleQueueIndex) throws IOException {
maybeThrowError();
sampleQueueReaders[sampleQueueIndex].maybeThrowError();
}
public void maybeThrowError() throws IOException {
@ -494,13 +515,8 @@ import java.util.Set;
}
int result =
sampleQueues[sampleQueueIndex].read(
formatHolder,
buffer,
requireFormat,
/* allowOnlyClearBuffers= */ false,
loadingFinished,
lastSeekPositionUs);
sampleQueueReaders[sampleQueueIndex].read(
formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs);
if (result == C.RESULT_FORMAT_READ) {
Format format = formatHolder.format;
if (sampleQueueIndex == primarySampleQueueIndex) {
@ -516,12 +532,6 @@ import java.util.Set;
: upstreamTrackFormat;
format = format.copyWithManifestFormatInfo(trackFormat);
}
if (format.drmInitData != null) {
DrmInitData drmInitData = overridingDrmInitData.get(format.drmInitData.schemeType);
if (drmInitData != null) {
format = format.copyWithDrmInitData(drmInitData);
}
}
formatHolder.format = format;
}
return result;
@ -836,6 +846,9 @@ import java.util.Set;
sampleQueueTrackIds[trackCount] = id;
sampleQueues = Arrays.copyOf(sampleQueues, trackCount + 1);
sampleQueues[trackCount] = trackOutput;
sampleQueueReaders = Arrays.copyOf(sampleQueueReaders, trackCount + 1);
sampleQueueReaders[trackCount] =
new DecryptableSampleQueueReader(sampleQueues[trackCount], drmSessionManager);
sampleQueueIsAudioVideoFlags = Arrays.copyOf(sampleQueueIsAudioVideoFlags, trackCount + 1);
sampleQueueIsAudioVideoFlags[trackCount] = type == C.TRACK_TYPE_AUDIO
|| type == C.TRACK_TYPE_VIDEO;
@ -1048,11 +1061,29 @@ import java.util.Set;
trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat, false));
}
}
this.trackGroups = new TrackGroupArray(trackGroups);
this.trackGroups = createTrackGroupArrayWithDrmInfo(trackGroups);
Assertions.checkState(optionalTrackGroups == null);
optionalTrackGroups = Collections.emptySet();
}
private TrackGroupArray createTrackGroupArrayWithDrmInfo(TrackGroup[] trackGroups) {
for (int i = 0; i < trackGroups.length; i++) {
TrackGroup trackGroup = trackGroups[i];
Format[] exposedFormats = new Format[trackGroup.length];
for (int j = 0; j < trackGroup.length; j++) {
Format format = trackGroup.getFormat(j);
if (format.drmInitData != null) {
format =
format.copyWithExoMediaCryptoType(
drmSessionManager.getExoMediaCryptoType(format.drmInitData));
}
exposedFormats[j] = format;
}
trackGroups[i] = new TrackGroup(exposedFormats);
}
return new TrackGroupArray(trackGroups);
}
private HlsMediaChunk getLastMediaChunk() {
return mediaChunks.get(mediaChunks.size() - 1);
}

View File

@ -22,6 +22,7 @@ import static org.mockito.Mockito.when;
import android.net.Uri;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
@ -81,6 +82,7 @@ public final class HlsMediaPeriodTest {
mockPlaylistTracker,
mockDataSourceFactory,
mock(TransferListener.class),
mock(DrmSessionManager.class),
mock(LoadErrorHandlingPolicy.class),
new EventDispatcher()
.withParameters(