Remove DRM->DASH dependency in prep for DASH module split
Also renamed releaseResources->release to be consistent with the rest of the library, and added some synchronization to ensure correct usage. Issue: #2139 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150753414
This commit is contained in:
parent
065d3dc523
commit
b1a2ae1856
@ -23,17 +23,9 @@ import android.test.InstrumentationTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import org.mockito.Mock;
|
||||
|
||||
@ -50,89 +42,57 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
TestUtil.setUpMockito(this);
|
||||
|
||||
when(mediaDrm.openSession()).thenReturn(new byte[] {1, 2, 3});
|
||||
|
||||
offlineLicenseHelper = new OfflineLicenseHelper<>(mediaDrm, mediaDrmCallback, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
offlineLicenseHelper.releaseResources();
|
||||
offlineLicenseHelper.release();
|
||||
offlineLicenseHelper = null;
|
||||
}
|
||||
|
||||
public void testDownloadRenewReleaseKey() throws Exception {
|
||||
DashManifest manifest = newDashManifestWithAllElements();
|
||||
setStubLicenseAndPlaybackDurationValues(1000, 200);
|
||||
|
||||
byte[] keySetId = {2, 5, 8};
|
||||
setStubKeySetId(keySetId);
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(newDrmInitData());
|
||||
|
||||
assertOfflineLicenseKeySetIdEqual(keySetId, offlineLicenseKeySetId);
|
||||
|
||||
byte[] keySetId2 = {6, 7, 0, 1, 4};
|
||||
setStubKeySetId(keySetId2);
|
||||
|
||||
byte[] offlineLicenseKeySetId2 = offlineLicenseHelper.renew(offlineLicenseKeySetId);
|
||||
byte[] offlineLicenseKeySetId2 = offlineLicenseHelper.renewLicense(offlineLicenseKeySetId);
|
||||
|
||||
assertOfflineLicenseKeySetIdEqual(keySetId2, offlineLicenseKeySetId2);
|
||||
|
||||
offlineLicenseHelper.release(offlineLicenseKeySetId2);
|
||||
offlineLicenseHelper.releaseLicense(offlineLicenseKeySetId2);
|
||||
}
|
||||
|
||||
public void testDownloadFailsIfThereIsNoInitData() throws Exception {
|
||||
setDefaultStubValues();
|
||||
DashManifest manifest =
|
||||
newDashManifest(newPeriods(newAdaptationSets(newRepresentations(null /*no init data*/))));
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
|
||||
assertNull(offlineLicenseKeySetId);
|
||||
public void testDownloadLicenseFailsIfNullInitData() throws Exception {
|
||||
try {
|
||||
offlineLicenseHelper.downloadLicense(null);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testDownloadFailsIfThereIsNoRepresentation() throws Exception {
|
||||
setDefaultStubValues();
|
||||
DashManifest manifest = newDashManifest(newPeriods(newAdaptationSets(/*no representation*/)));
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
|
||||
assertNull(offlineLicenseKeySetId);
|
||||
}
|
||||
|
||||
public void testDownloadFailsIfThereIsNoAdaptationSet() throws Exception {
|
||||
setDefaultStubValues();
|
||||
DashManifest manifest = newDashManifest(newPeriods(/*no adaptation set*/));
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
|
||||
assertNull(offlineLicenseKeySetId);
|
||||
}
|
||||
|
||||
public void testDownloadFailsIfThereIsNoPeriod() throws Exception {
|
||||
setDefaultStubValues();
|
||||
DashManifest manifest = newDashManifest(/*no periods*/);
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
|
||||
assertNull(offlineLicenseKeySetId);
|
||||
}
|
||||
|
||||
public void testDownloadFailsIfNoKeySetIdIsReturned() throws Exception {
|
||||
public void testDownloadLicenseFailsIfNoKeySetIdIsReturned() throws Exception {
|
||||
setStubLicenseAndPlaybackDurationValues(1000, 200);
|
||||
DashManifest manifest = newDashManifestWithAllElements();
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(newDrmInitData());
|
||||
|
||||
assertNull(offlineLicenseKeySetId);
|
||||
}
|
||||
|
||||
public void testDownloadDoesNotFailIfDurationNotAvailable() throws Exception {
|
||||
public void testDownloadLicenseDoesNotFailIfDurationNotAvailable() throws Exception {
|
||||
setDefaultStubKeySetId();
|
||||
DashManifest manifest = newDashManifestWithAllElements();
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(newDrmInitData());
|
||||
|
||||
assertNotNull(offlineLicenseKeySetId);
|
||||
}
|
||||
@ -142,9 +102,8 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
|
||||
int playbackDuration = 200;
|
||||
setStubLicenseAndPlaybackDurationValues(licenseDuration, playbackDuration);
|
||||
setDefaultStubKeySetId();
|
||||
DashManifest manifest = newDashManifestWithAllElements();
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(newDrmInitData());
|
||||
|
||||
Pair<Long, Long> licenseDurationRemainingSec = offlineLicenseHelper
|
||||
.getLicenseDurationRemainingSec(offlineLicenseKeySetId);
|
||||
@ -158,9 +117,8 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
|
||||
int playbackDuration = 0;
|
||||
setStubLicenseAndPlaybackDurationValues(licenseDuration, playbackDuration);
|
||||
setDefaultStubKeySetId();
|
||||
DashManifest manifest = newDashManifestWithAllElements();
|
||||
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.download(httpDataSource, manifest);
|
||||
byte[] offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(newDrmInitData());
|
||||
|
||||
Pair<Long, Long> licenseDurationRemainingSec = offlineLicenseHelper
|
||||
.getLicenseDurationRemainingSec(offlineLicenseKeySetId);
|
||||
@ -169,12 +127,6 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
|
||||
assertEquals(playbackDuration, (long) licenseDurationRemainingSec.second);
|
||||
}
|
||||
|
||||
private void setDefaultStubValues()
|
||||
throws android.media.NotProvisionedException, android.media.DeniedByServerException {
|
||||
setDefaultStubKeySetId();
|
||||
setStubLicenseAndPlaybackDurationValues(1000, 200);
|
||||
}
|
||||
|
||||
private void setDefaultStubKeySetId()
|
||||
throws android.media.NotProvisionedException, android.media.DeniedByServerException {
|
||||
setStubKeySetId(new byte[] {2, 5, 8});
|
||||
@ -201,34 +153,9 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
|
||||
when(mediaDrm.queryKeyStatus(any(byte[].class))).thenReturn(keyStatus);
|
||||
}
|
||||
|
||||
private static DashManifest newDashManifestWithAllElements() {
|
||||
return newDashManifest(newPeriods(newAdaptationSets(newRepresentations(newDrmInitData()))));
|
||||
}
|
||||
|
||||
private static DashManifest newDashManifest(Period... periods) {
|
||||
return new DashManifest(0, 0, 0, false, 0, 0, 0, null, null, Arrays.asList(periods));
|
||||
}
|
||||
|
||||
private static Period newPeriods(AdaptationSet... adaptationSets) {
|
||||
return new Period("", 0, Arrays.asList(adaptationSets));
|
||||
}
|
||||
|
||||
private static AdaptationSet newAdaptationSets(Representation... representations) {
|
||||
return new AdaptationSet(0, C.TRACK_TYPE_VIDEO, Arrays.asList(representations), null);
|
||||
}
|
||||
|
||||
private static Representation newRepresentations(DrmInitData drmInitData) {
|
||||
Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4,
|
||||
MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0);
|
||||
if (drmInitData != null) {
|
||||
format = format.copyWithDrmInitData(drmInitData);
|
||||
}
|
||||
return Representation.newInstance("", 0, format, "", new SingleSegmentBase());
|
||||
}
|
||||
|
||||
private static DrmInitData newDrmInitData() {
|
||||
return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType",
|
||||
new byte[]{1, 4, 7, 0, 3, 6}));
|
||||
new byte[] {1, 4, 7, 0, 3, 6}));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.source.dash;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DashUtil}.
|
||||
*/
|
||||
public final class DashUtilTest extends TestCase {
|
||||
|
||||
public void testLoadDrmInitDataFromManifest() throws Exception {
|
||||
Period period = newPeriod(newAdaptationSets(newRepresentations(newDrmInitData())));
|
||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(newDataSource(), period);
|
||||
assertEquals(newDrmInitData(), drmInitData);
|
||||
}
|
||||
|
||||
public void testLoadDrmInitDataMissing() throws Exception {
|
||||
Period period = newPeriod(newAdaptationSets(newRepresentations(null /* no init data */)));
|
||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(newDataSource(), period);
|
||||
assertNull(drmInitData);
|
||||
}
|
||||
|
||||
public void testLoadDrmInitDataNoRepresentations() throws Exception {
|
||||
Period period = newPeriod(newAdaptationSets(/* no representation */));
|
||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(newDataSource(), period);
|
||||
assertNull(drmInitData);
|
||||
}
|
||||
|
||||
public void testLoadDrmInitDataNoAdaptationSets() throws Exception {
|
||||
Period period = newPeriod(/* no adaptation set */);
|
||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(newDataSource(), period);
|
||||
assertNull(drmInitData);
|
||||
}
|
||||
|
||||
private static Period newPeriod(AdaptationSet... adaptationSets) {
|
||||
return new Period("", 0, Arrays.asList(adaptationSets));
|
||||
}
|
||||
|
||||
private static AdaptationSet newAdaptationSets(Representation... representations) {
|
||||
return new AdaptationSet(0, C.TRACK_TYPE_VIDEO, Arrays.asList(representations), null);
|
||||
}
|
||||
|
||||
private static Representation newRepresentations(DrmInitData drmInitData) {
|
||||
Format format = Format.createVideoContainerFormat("id", MimeTypes.VIDEO_MP4,
|
||||
MimeTypes.VIDEO_H264, "", Format.NO_VALUE, 1024, 768, Format.NO_VALUE, null, 0);
|
||||
if (drmInitData != null) {
|
||||
format = format.copyWithDrmInitData(drmInitData);
|
||||
}
|
||||
return Representation.newInstance("", 0, format, "", new SingleSegmentBase());
|
||||
}
|
||||
|
||||
private static DrmInitData newDrmInitData() {
|
||||
return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType",
|
||||
new byte[]{1, 4, 7, 0, 3, 6}));
|
||||
}
|
||||
|
||||
private static DataSource newDataSource() {
|
||||
// TODO: Use DummyDataSource when available.
|
||||
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||
fakeDataSource.getDataSet().newDefaultData().appendReadError(new IOException("Unexpected"));
|
||||
return fakeDataSource;
|
||||
}
|
||||
|
||||
}
|
@ -22,15 +22,9 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.EventListener;
|
||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.Mode;
|
||||
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
|
||||
import com.google.android.exoplayer2.source.dash.DashUtil;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
@ -38,8 +32,7 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Helper class to download, renew and release offline licenses. It utilizes {@link
|
||||
* DefaultDrmSessionManager}.
|
||||
* Helper class to download, renew and release offline licenses.
|
||||
*/
|
||||
public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
|
||||
@ -48,8 +41,8 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
private final HandlerThread handlerThread;
|
||||
|
||||
/**
|
||||
* Instantiates a new instance which uses Widevine CDM. Call {@link #releaseResources()} when
|
||||
* you're done with the helper instance.
|
||||
* Instantiates a new instance which uses Widevine CDM. Call {@link #release()} when the instance
|
||||
* is no longer required.
|
||||
*
|
||||
* @param licenseUrl The default license URL.
|
||||
* @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
|
||||
@ -64,8 +57,8 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new instance which uses Widevine CDM. Call {@link #releaseResources()} when
|
||||
* you're done with the helper instance.
|
||||
* Instantiates a new instance which uses Widevine CDM. Call {@link #release()} when the instance
|
||||
* is no longer required.
|
||||
*
|
||||
* @param callback Performs key and provisioning requests.
|
||||
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
|
||||
@ -84,7 +77,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance. Call {@link #releaseResources()} when you're done with it.
|
||||
* Constructs an instance. Call {@link #release()} when the instance is no longer required.
|
||||
*
|
||||
* @param mediaDrm An underlying {@link ExoMediaDrm} for use by the manager.
|
||||
* @param callback Performs key and provisioning requests.
|
||||
@ -97,7 +90,6 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
HashMap<String, String> optionalKeyRequestParameters) {
|
||||
handlerThread = new HandlerThread("OfflineLicenseHelper");
|
||||
handlerThread.start();
|
||||
|
||||
conditionVariable = new ConditionVariable();
|
||||
EventListener eventListener = new EventListener() {
|
||||
@Override
|
||||
@ -124,67 +116,23 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
optionalKeyRequestParameters, new Handler(handlerThread.getLooper()), eventListener);
|
||||
}
|
||||
|
||||
/** Releases the used resources. */
|
||||
public void releaseResources() {
|
||||
/** Releases the helper. Should be called when the helper is no longer required. */
|
||||
public void release() {
|
||||
handlerThread.quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an offline license.
|
||||
*
|
||||
* @param dataSource The {@link HttpDataSource} to be used for download.
|
||||
* @param manifestUriString The URI of the manifest to be read.
|
||||
* @return The downloaded offline license key set id.
|
||||
* @param drmInitData The {@link DrmInitData} for the content whose license is to be downloaded.
|
||||
* @return The key set id for the downloaded license.
|
||||
* @throws IOException If an error occurs reading data from the stream.
|
||||
* @throws InterruptedException If the thread has been interrupted.
|
||||
* @throws DrmSessionException Thrown when there is an error during DRM session.
|
||||
* @throws DrmSessionException Thrown when a DRM session error occurs.
|
||||
*/
|
||||
public byte[] download(HttpDataSource dataSource, String manifestUriString)
|
||||
throws IOException, InterruptedException, DrmSessionException {
|
||||
return download(dataSource, DashUtil.loadManifest(dataSource, manifestUriString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an offline license.
|
||||
*
|
||||
* @param dataSource The {@link HttpDataSource} to be used for download.
|
||||
* @param dashManifest The {@link DashManifest} of the DASH content.
|
||||
* @return The downloaded offline license key set id.
|
||||
* @throws IOException If an error occurs reading data from the stream.
|
||||
* @throws InterruptedException If the thread has been interrupted.
|
||||
* @throws DrmSessionException Thrown when there is an error during DRM session.
|
||||
*/
|
||||
public byte[] download(HttpDataSource dataSource, DashManifest dashManifest)
|
||||
throws IOException, InterruptedException, DrmSessionException {
|
||||
// Get DrmInitData
|
||||
// Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream,
|
||||
// as per DASH IF Interoperability Recommendations V3.0, 7.5.3.
|
||||
if (dashManifest.getPeriodCount() < 1) {
|
||||
return null;
|
||||
}
|
||||
Period period = dashManifest.getPeriod(0);
|
||||
int adaptationSetIndex = period.getAdaptationSetIndex(C.TRACK_TYPE_VIDEO);
|
||||
if (adaptationSetIndex == C.INDEX_UNSET) {
|
||||
adaptationSetIndex = period.getAdaptationSetIndex(C.TRACK_TYPE_AUDIO);
|
||||
if (adaptationSetIndex == C.INDEX_UNSET) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex);
|
||||
if (adaptationSet.representations.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Representation representation = adaptationSet.representations.get(0);
|
||||
DrmInitData drmInitData = representation.format.drmInitData;
|
||||
if (drmInitData == null) {
|
||||
Format sampleFormat = DashUtil.loadSampleFormat(dataSource, representation);
|
||||
if (sampleFormat != null) {
|
||||
drmInitData = sampleFormat.drmInitData;
|
||||
}
|
||||
if (drmInitData == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public synchronized byte[] downloadLicense(DrmInitData drmInitData) throws IOException,
|
||||
InterruptedException, DrmSessionException {
|
||||
Assertions.checkArgument(drmInitData != null);
|
||||
blockingKeyRequest(DefaultDrmSessionManager.MODE_DOWNLOAD, null, drmInitData);
|
||||
return drmSessionManager.getOfflineLicenseKeySetId();
|
||||
}
|
||||
@ -193,10 +141,11 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
* Renews an offline license.
|
||||
*
|
||||
* @param offlineLicenseKeySetId The key set id of the license to be renewed.
|
||||
* @return Renewed offline license key set id.
|
||||
* @throws DrmSessionException Thrown when there is an error during DRM session.
|
||||
* @return The renewed offline license key set id.
|
||||
* @throws DrmSessionException Thrown when a DRM session error occurs.
|
||||
*/
|
||||
public byte[] renew(byte[] offlineLicenseKeySetId) throws DrmSessionException {
|
||||
public synchronized byte[] renewLicense(byte[] offlineLicenseKeySetId)
|
||||
throws DrmSessionException {
|
||||
Assertions.checkNotNull(offlineLicenseKeySetId);
|
||||
blockingKeyRequest(DefaultDrmSessionManager.MODE_DOWNLOAD, offlineLicenseKeySetId, null);
|
||||
return drmSessionManager.getOfflineLicenseKeySetId();
|
||||
@ -206,19 +155,22 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
|
||||
* Releases an offline license.
|
||||
*
|
||||
* @param offlineLicenseKeySetId The key set id of the license to be released.
|
||||
* @throws DrmSessionException Thrown when there is an error during DRM session.
|
||||
* @throws DrmSessionException Thrown when a DRM session error occurs.
|
||||
*/
|
||||
public void release(byte[] offlineLicenseKeySetId) throws DrmSessionException {
|
||||
public synchronized void releaseLicense(byte[] offlineLicenseKeySetId)
|
||||
throws DrmSessionException {
|
||||
Assertions.checkNotNull(offlineLicenseKeySetId);
|
||||
blockingKeyRequest(DefaultDrmSessionManager.MODE_RELEASE, offlineLicenseKeySetId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns license and playback durations remaining in seconds of the given offline license.
|
||||
* Returns the remaining license and playback durations in seconds, for an offline license.
|
||||
*
|
||||
* @param offlineLicenseKeySetId The key set id of the license.
|
||||
* @return The remaining license and playback durations, in seconds.
|
||||
* @throws DrmSessionException Thrown when a DRM session error occurs.
|
||||
*/
|
||||
public Pair<Long, Long> getLicenseDurationRemainingSec(byte[] offlineLicenseKeySetId)
|
||||
public synchronized Pair<Long, Long> getLicenseDurationRemainingSec(byte[] offlineLicenseKeySetId)
|
||||
throws DrmSessionException {
|
||||
Assertions.checkNotNull(offlineLicenseKeySetId);
|
||||
DrmSession<T> session = openBlockingKeyRequest(DefaultDrmSessionManager.MODE_QUERY,
|
||||
|
@ -38,7 +38,7 @@ public final class WidevineUtil {
|
||||
* @throws IllegalStateException If called when a session isn't opened.
|
||||
* @param drmSession
|
||||
*/
|
||||
public static Pair<Long, Long> getLicenseDurationRemainingSec(DrmSession drmSession) {
|
||||
public static Pair<Long, Long> getLicenseDurationRemainingSec(DrmSession<?> drmSession) {
|
||||
Map<String, String> keyStatus = drmSession.queryKeyStatus();
|
||||
return new Pair<>(
|
||||
getDurationRemainingSec(keyStatus, PROPERTY_LICENSE_DURATION_REMAINING),
|
||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.dash;
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer2.extractor.Extractor;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
@ -26,6 +27,7 @@ import com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper;
|
||||
import com.google.android.exoplayer2.source.chunk.InitializationChunk;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
@ -34,6 +36,7 @@ import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility methods for DASH streams.
|
||||
@ -46,8 +49,7 @@ public final class DashUtil {
|
||||
* @param dataSource The {@link HttpDataSource} from which the manifest should be read.
|
||||
* @param manifestUriString The URI of the manifest to be read.
|
||||
* @return An instance of {@link DashManifest}.
|
||||
* @throws IOException If an error occurs reading data from the stream.
|
||||
* @see DashManifestParser
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
*/
|
||||
public static DashManifest loadManifest(DataSource dataSource, String manifestUriString)
|
||||
throws IOException {
|
||||
@ -63,8 +65,35 @@ public final class DashUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initialization data for the {@code representation} and returns the sample {@link
|
||||
* Format}.
|
||||
* Loads {@link DrmInitData} for a given period in a DASH manifest.
|
||||
*
|
||||
* @param dataSource The {@link HttpDataSource} from which data should be loaded.
|
||||
* @param period The {@link Period}.
|
||||
* @return The loaded {@link DrmInitData}, or null if none is defined.
|
||||
* @throws IOException Thrown when there is an error while loading.
|
||||
* @throws InterruptedException Thrown if the thread was interrupted.
|
||||
*/
|
||||
public static DrmInitData loadDrmInitData(DataSource dataSource, Period period)
|
||||
throws IOException, InterruptedException {
|
||||
Representation representation = getFirstRepresentation(period, C.TRACK_TYPE_VIDEO);
|
||||
if (representation == null) {
|
||||
representation = getFirstRepresentation(period, C.TRACK_TYPE_AUDIO);
|
||||
if (representation == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
DrmInitData drmInitData = representation.format.drmInitData;
|
||||
if (drmInitData != null) {
|
||||
// Prefer drmInitData obtained from the manifest over drmInitData obtained from the stream,
|
||||
// as per DASH IF Interoperability Recommendations V3.0, 7.5.3.
|
||||
return drmInitData;
|
||||
}
|
||||
Format sampleFormat = DashUtil.loadSampleFormat(dataSource, representation);
|
||||
return sampleFormat == null ? null : sampleFormat.drmInitData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads initialization data for the {@code representation} and returns the sample {@link Format}.
|
||||
*
|
||||
* @param dataSource The source from which the data should be loaded.
|
||||
* @param representation The representation which initialization chunk belongs to.
|
||||
@ -155,6 +184,15 @@ public final class DashUtil {
|
||||
return new ChunkExtractorWrapper(extractor, format);
|
||||
}
|
||||
|
||||
private static Representation getFirstRepresentation(Period period, int type) {
|
||||
int index = period.getAdaptationSetIndex(type);
|
||||
if (index == C.INDEX_UNSET) {
|
||||
return null;
|
||||
}
|
||||
List<Representation> representations = period.adaptationSets.get(index).representations;
|
||||
return representations.isEmpty() ? null : representations.get(0);
|
||||
}
|
||||
|
||||
private DashUtil() {}
|
||||
|
||||
}
|
||||
|
@ -18,11 +18,15 @@ package com.google.android.exoplayer2.playbacktests.gts;
|
||||
import android.media.MediaDrm.MediaDrmStateException;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||
import com.google.android.exoplayer2.drm.OfflineLicenseHelper;
|
||||
import com.google.android.exoplayer2.playbacktests.util.ActionSchedule;
|
||||
import com.google.android.exoplayer2.playbacktests.util.HostActivity;
|
||||
import com.google.android.exoplayer2.source.dash.DashUtil;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
@ -72,7 +76,7 @@ public final class DashWidevineOfflineTest extends ActivityInstrumentationTestCa
|
||||
releaseLicense();
|
||||
}
|
||||
if (offlineLicenseHelper != null) {
|
||||
offlineLicenseHelper.releaseResources();
|
||||
offlineLicenseHelper.release();
|
||||
}
|
||||
offlineLicenseHelper = null;
|
||||
httpDataSourceFactory = null;
|
||||
@ -89,7 +93,7 @@ public final class DashWidevineOfflineTest extends ActivityInstrumentationTestCa
|
||||
testRunner.run();
|
||||
|
||||
// Renew license after playback should still work
|
||||
offlineLicenseKeySetId = offlineLicenseHelper.renew(offlineLicenseKeySetId);
|
||||
offlineLicenseKeySetId = offlineLicenseHelper.renewLicense(offlineLicenseKeySetId);
|
||||
Assert.assertNotNull(offlineLicenseKeySetId);
|
||||
}
|
||||
|
||||
@ -164,15 +168,18 @@ public final class DashWidevineOfflineTest extends ActivityInstrumentationTestCa
|
||||
}
|
||||
|
||||
private void downloadLicense() throws InterruptedException, DrmSessionException, IOException {
|
||||
offlineLicenseKeySetId = offlineLicenseHelper.download(
|
||||
httpDataSourceFactory.createDataSource(), DashTestData.WIDEVINE_H264_MANIFEST);
|
||||
DataSource dataSource = httpDataSourceFactory.createDataSource();
|
||||
DashManifest dashManifest = DashUtil.loadManifest(dataSource,
|
||||
DashTestData.WIDEVINE_H264_MANIFEST);
|
||||
DrmInitData drmInitData = DashUtil.loadDrmInitData(dataSource, dashManifest.getPeriod(0));
|
||||
offlineLicenseKeySetId = offlineLicenseHelper.downloadLicense(drmInitData);
|
||||
Assert.assertNotNull(offlineLicenseKeySetId);
|
||||
Assert.assertTrue(offlineLicenseKeySetId.length > 0);
|
||||
testRunner.setOfflineLicenseKeySetId(offlineLicenseKeySetId);
|
||||
}
|
||||
|
||||
private void releaseLicense() throws DrmSessionException {
|
||||
offlineLicenseHelper.release(offlineLicenseKeySetId);
|
||||
offlineLicenseHelper.releaseLicense(offlineLicenseKeySetId);
|
||||
offlineLicenseKeySetId = null;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user