提交 7e2fffe7 编写于 作者: O Oliver Woodman

Make sure we use the correct mimeType with DRM initialization data.

We were previously using the container format of the media being
played as the mimeType generating key requests, but this is not
always correct. As an example, where a manifest contains webm streams
but specifies initialization data using cenc:pssh elements in the
manifest, the media has a webm mimeType, but the DRM initialization
data has an mp4 mimeType.
上级 ad7237b5
......@@ -21,6 +21,7 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.webm.StreamBuilder.ContentEncodingSettings;
......@@ -237,8 +238,12 @@ public final class WebmExtractorTest extends InstrumentationTestCase {
assertIndex(DEFAULT_TIMECODE_SCALE, 1);
DrmInitData drmInitData = extractorOutput.drmInitData;
assertNotNull(drmInitData);
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(WIDEVINE_UUID));
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(ZERO_UUID));
SchemeInitData widevineInitData = drmInitData.get(WIDEVINE_UUID);
assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType);
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, widevineInitData.data);
SchemeInitData zeroInitData = drmInitData.get(ZERO_UUID);
assertEquals(MimeTypes.VIDEO_WEBM, zeroInitData.mimeType);
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data);
}
public void testPrepareThreeCuePoints() throws IOException, InterruptedException {
......
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer;
import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.util.Assertions;
......@@ -273,10 +274,10 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
if (psshInfo == null || psshInfo.isEmpty()) {
return null;
}
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4);
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped();
for (UUID uuid : psshInfo.keySet()) {
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid));
drmInitData.put(uuid, psshAtom);
drmInitData.put(uuid, new SchemeInitData(MimeTypes.VIDEO_MP4, psshAtom));
}
return drmInitData;
}
......
......@@ -1054,8 +1054,6 @@ public class DashChunkSource implements ChunkSource, Output {
}
private static DrmInitData getDrmInitData(AdaptationSet adaptationSet) {
String drmInitMimeType = mimeTypeIsWebm(adaptationSet.representations.get(0).format.mimeType)
? MimeTypes.VIDEO_WEBM : MimeTypes.VIDEO_MP4;
if (adaptationSet.contentProtections.isEmpty()) {
return null;
} else {
......@@ -1064,7 +1062,7 @@ public class DashChunkSource implements ChunkSource, Output {
ContentProtection contentProtection = adaptationSet.contentProtections.get(i);
if (contentProtection.uuid != null && contentProtection.data != null) {
if (drmInitData == null) {
drmInitData = new DrmInitData.Mapped(drmInitMimeType);
drmInitData = new DrmInitData.Mapped();
}
drmInitData.put(contentProtection.uuid, contentProtection.data);
}
......
......@@ -15,10 +15,10 @@
*/
package com.google.android.exoplayer.dash.mpd;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util;
import java.util.Arrays;
import java.util.UUID;
/**
......@@ -37,16 +37,16 @@ public class ContentProtection {
public final UUID uuid;
/**
* Protection scheme specific data. May be null.
* Protection scheme specific initialization data. May be null.
*/
public final byte[] data;
public final SchemeInitData data;
/**
* @param schemeUriId Identifies the content protection scheme.
* @param uuid The UUID of the protection scheme, if known. May be null.
* @param data Protection scheme specific initialization data. May be null.
*/
public ContentProtection(String schemeUriId, UUID uuid, byte[] data) {
public ContentProtection(String schemeUriId, UUID uuid, SchemeInitData data) {
this.schemeUriId = Assertions.checkNotNull(schemeUriId);
this.uuid = uuid;
this.data = data;
......@@ -64,20 +64,14 @@ public class ContentProtection {
ContentProtection other = (ContentProtection) obj;
return schemeUriId.equals(other.schemeUriId)
&& Util.areEqual(uuid, other.uuid)
&& Arrays.equals(data, other.data);
&& Util.areEqual(data, other.data);
}
@Override
public int hashCode() {
int hashCode = 1;
hashCode = hashCode * 37 + schemeUriId.hashCode();
if (uuid != null) {
hashCode = hashCode * 37 + uuid.hashCode();
}
if (data != null) {
hashCode = hashCode * 37 + Arrays.hashCode(data);
}
int hashCode = schemeUriId.hashCode();
hashCode = (37 * hashCode) + (uuid != null ? uuid.hashCode() : 0);
hashCode = (37 * hashCode) + (data != null ? data.hashCode() : 0);
return hashCode;
}
......
......@@ -21,6 +21,7 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentList;
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate;
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement;
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.upstream.UriLoadable;
import com.google.android.exoplayer.util.Assertions;
......@@ -317,23 +318,24 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
throws XmlPullParserException, IOException {
String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri");
UUID uuid = null;
byte[] psshAtom = null;
SchemeInitData data = null;
do {
xpp.next();
// The cenc:pssh element is defined in 23001-7:2015
if (ParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
psshAtom = Base64.decode(xpp.getText(), Base64.DEFAULT);
uuid = PsshAtomUtil.parseUuid(psshAtom);
data = new SchemeInitData(MimeTypes.VIDEO_MP4,
Base64.decode(xpp.getText(), Base64.DEFAULT));
uuid = PsshAtomUtil.parseUuid(data.data);
if (uuid == null) {
throw new ParserException("Invalid pssh atom in cenc:pssh element");
}
}
} while (!ParserUtil.isEndTag(xpp, "ContentProtection"));
return buildContentProtection(schemeIdUri, uuid, psshAtom);
return buildContentProtection(schemeIdUri, uuid, data);
}
protected ContentProtection buildContentProtection(String schemeIdUri, UUID uuid, byte[] data) {
protected ContentProtection buildContentProtection(String schemeIdUri, UUID uuid,
SchemeInitData data) {
return new ContentProtection(schemeIdUri, uuid, data);
}
......
......@@ -15,25 +15,19 @@
*/
package com.google.android.exoplayer.drm;
import com.google.android.exoplayer.util.Assertions;
import android.media.MediaDrm;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Encapsulates initialization data required by a {@link MediaDrm} instance.
* Encapsulates initialization data required by a {@link MediaDrm} instances.
*/
public abstract class DrmInitData {
/**
* The container mime type.
*/
public final String mimeType;
public DrmInitData(String mimeType) {
this.mimeType = mimeType;
}
public interface DrmInitData {
/**
* Retrieves initialization data for a given DRM scheme, specified by its UUID.
......@@ -41,22 +35,21 @@ public abstract class DrmInitData {
* @param schemeUuid The DRM scheme's UUID.
* @return The initialization data for the scheme, or null if the scheme is not supported.
*/
public abstract byte[] get(UUID schemeUuid);
public abstract SchemeInitData get(UUID schemeUuid);
/**
* A {@link DrmInitData} implementation that maps UUID onto scheme specific data.
*/
public static final class Mapped extends DrmInitData {
public static final class Mapped implements DrmInitData {
private final Map<UUID, byte[]> schemeData;
private final Map<UUID, SchemeInitData> schemeData;
public Mapped(String mimeType) {
super(mimeType);
public Mapped() {
schemeData = new HashMap<>();
}
@Override
public byte[] get(UUID schemeUuid) {
public SchemeInitData get(UUID schemeUuid) {
return schemeData.get(schemeUuid);
}
......@@ -64,10 +57,10 @@ public abstract class DrmInitData {
* Inserts scheme specific initialization data.
*
* @param schemeUuid The scheme UUID.
* @param data The corresponding initialization data.
* @param schemeInitData The corresponding initialization data.
*/
public void put(UUID schemeUuid, byte[] data) {
schemeData.put(schemeUuid, data);
public void put(UUID schemeUuid, SchemeInitData schemeInitData) {
schemeData.put(schemeUuid, schemeInitData);
}
}
......@@ -75,20 +68,62 @@ public abstract class DrmInitData {
/**
* A {@link DrmInitData} implementation that returns the same initialization data for all schemes.
*/
public static final class Universal extends DrmInitData {
public static final class Universal implements DrmInitData {
private byte[] data;
private SchemeInitData data;
public Universal(String mimeType, byte[] data) {
super(mimeType);
public Universal(SchemeInitData data) {
this.data = data;
}
@Override
public byte[] get(UUID schemeUuid) {
public SchemeInitData get(UUID schemeUuid) {
return data;
}
}
/**
* Scheme initialization data.
*/
public static final class SchemeInitData {
/**
* The mimeType of {@link #data}.
*/
public final String mimeType;
/**
* The initialization data.
*/
public final byte[] data;
/**
* @param mimeType The mimeType of the initialization data.
* @param data The initialization data.
*/
public SchemeInitData(String mimeType, byte[] data) {
this.mimeType = Assertions.checkNotNull(mimeType);
this.data = Assertions.checkNotNull(data);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SchemeInitData)) {
return false;
}
if (obj == this) {
return true;
}
SchemeInitData other = (SchemeInitData) obj;
return mimeType.equals(other.mimeType) && Arrays.equals(data, other.data);
}
@Override
public int hashCode() {
return mimeType.hashCode() + 31 * Arrays.hashCode(data);
}
}
}
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.drm;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.util.Util;
......@@ -103,8 +104,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
private int state;
private MediaCrypto mediaCrypto;
private Exception lastException;
private String mimeType;
private byte[] schemeData;
private SchemeInitData schemeInitData;
private byte[] sessionId;
/**
......@@ -273,20 +273,19 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
requestHandlerThread.start();
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
}
if (schemeData == null) {
mimeType = drmInitData.mimeType;
schemeData = drmInitData.get(uuid);
if (schemeData == null) {
if (schemeInitData == null) {
schemeInitData = drmInitData.get(uuid);
if (schemeInitData == null) {
onError(new IllegalStateException("Media does not support uuid: " + uuid));
return;
}
if (Util.SDK_INT < 21) {
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeData, WIDEVINE_UUID);
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData.data, WIDEVINE_UUID);
if (psshData == null) {
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
} else {
schemeData = psshData;
schemeInitData = new SchemeInitData(schemeInitData.mimeType, psshData);
}
}
}
......@@ -307,7 +306,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
postRequestHandler = null;
requestHandlerThread.quit();
requestHandlerThread = null;
schemeData = null;
schemeInitData = null;
mediaCrypto = null;
lastException = null;
if (sessionId != null) {
......@@ -369,7 +368,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
private void postKeyRequest() {
KeyRequest keyRequest;
try {
keyRequest = mediaDrm.getKeyRequest(sessionId, schemeData, mimeType,
keyRequest = mediaDrm.getKeyRequest(sessionId, schemeInitData.data, schemeInitData.mimeType,
MediaDrm.KEY_TYPE_STREAMING, optionalKeyRequestParameters);
postRequestHandler.obtainMessage(MSG_KEYS, keyRequest).sendToTarget();
} catch (NotProvisionedException e) {
......
......@@ -18,6 +18,7 @@ package com.google.android.exoplayer.extractor.mp4;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
......@@ -292,10 +293,11 @@ public final class FragmentedMp4Extractor implements Extractor {
LeafAtom child = moovChildren.get(i);
if (child.type == Atom.TYPE_pssh) {
if (drmInitData == null) {
drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4);
drmInitData = new DrmInitData.Mapped();
}
byte[] psshData = child.data.data;
drmInitData.put(PsshAtomUtil.parseUuid(psshData), psshData);
drmInitData.put(PsshAtomUtil.parseUuid(psshData),
new SchemeInitData(MimeTypes.VIDEO_MP4, psshData));
}
}
if (drmInitData != null) {
......
......@@ -19,6 +19,7 @@ import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.ChunkIndex;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
......@@ -450,8 +451,8 @@ public final class WebmExtractor implements Extractor {
throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
}
if (!sentDrmInitData) {
extractorOutput.drmInitData(
new DrmInitData.Universal(MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId));
extractorOutput.drmInitData(new DrmInitData.Universal(
new SchemeInitData(MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId)));
sentDrmInitData = true;
}
}
......
......@@ -29,6 +29,7 @@ import com.google.android.exoplayer.chunk.FormatEvaluator;
import com.google.android.exoplayer.chunk.FormatEvaluator.Evaluation;
import com.google.android.exoplayer.chunk.MediaChunk;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor;
import com.google.android.exoplayer.extractor.mp4.Track;
import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox;
......@@ -144,8 +145,9 @@ public class SmoothStreamingChunkSource implements ChunkSource,
byte[] keyId = getProtectionElementKeyId(protectionElement.data);
trackEncryptionBoxes = new TrackEncryptionBox[1];
trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId);
drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4);
drmInitData.put(protectionElement.uuid, protectionElement.data);
drmInitData = new DrmInitData.Mapped();
drmInitData.put(protectionElement.uuid,
new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data));
} else {
trackEncryptionBoxes = null;
drmInitData = null;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册