提交 4c4782c7 编写于 作者: O Oliver Woodman

Pass whole PSSH box to MediaDrm (except in the WV+L case).

This fixes SmoothStreaming on AndroidTV, and also removes a
warning that gets logged when using Widevine/FMP4.
上级 d0ba2652
......@@ -19,6 +19,7 @@ import com.google.android.exoplayer.SampleSource.SampleSourceReader;
import com.google.android.exoplayer.drm.DrmInitData;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.extractor.mp4.Mp4Extractor;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.Util;
......@@ -266,7 +267,10 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe
return null;
}
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4);
drmInitData.putAll(psshInfo);
for (UUID uuid : psshInfo.keySet()) {
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid));
drmInitData.put(uuid, psshAtom);
}
return drmInitData;
}
......
......@@ -21,10 +21,10 @@ 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.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.upstream.UriLoadable;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util;
......@@ -258,22 +258,20 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
throws XmlPullParserException, IOException {
String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri");
UUID uuid = null;
byte[] data = null;
byte[] psshAtom = null;
do {
xpp.next();
// The cenc:pssh element is defined in 23001-7:2015
if (isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
byte[] decodedData = Base64.decode(xpp.getText(), Base64.DEFAULT);
ParsableByteArray psshAtom = new ParsableByteArray(decodedData);
psshAtom.skipBytes(12);
uuid = new UUID(psshAtom.readLong(), psshAtom.readLong());
int dataSize = psshAtom.readInt();
data = new byte[dataSize];
psshAtom.readBytes(data, 0, dataSize);
psshAtom = Base64.decode(xpp.getText(), Base64.DEFAULT);
uuid = PsshAtomUtil.parseUuid(psshAtom);
if (uuid == null) {
throw new ParserException("Invalid pssh atom in cenc:pssh element");
}
}
} while (!isEndTag(xpp, "ContentProtection"));
return buildContentProtection(schemeIdUri, uuid, data);
return buildContentProtection(schemeIdUri, uuid, psshAtom);
}
protected ContentProtection buildContentProtection(String schemeIdUri, UUID uuid, byte[] data) {
......
......@@ -70,15 +70,6 @@ public abstract class DrmInitData {
schemeData.put(schemeUuid, data);
}
/**
* Inserts scheme specific initialization data.
*
* @param data A mapping from scheme UUID to initialization data.
*/
public void putAll(Map<UUID, byte[]> data) {
schemeData.putAll(data);
}
}
/**
......
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer.drm;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.util.Util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.media.DeniedByServerException;
......@@ -96,7 +99,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
private MediaCrypto mediaCrypto;
private Exception lastException;
private String mimeType;
private byte[] schemePsshData;
private byte[] schemeData;
private byte[] sessionId;
/**
......@@ -265,13 +268,22 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
requestHandlerThread.start();
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
}
if (this.schemePsshData == null) {
if (schemeData == null) {
mimeType = drmInitData.mimeType;
schemePsshData = drmInitData.get(uuid);
if (schemePsshData == null) {
schemeData = drmInitData.get(uuid);
if (schemeData == 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);
if (psshData == null) {
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
} else {
schemeData = psshData;
}
}
}
state = STATE_OPENING;
openInternal(true);
......@@ -290,7 +302,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
postRequestHandler = null;
requestHandlerThread.quit();
requestHandlerThread = null;
schemePsshData = null;
schemeData = null;
mediaCrypto = null;
lastException = null;
if (sessionId != null) {
......@@ -352,7 +364,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
private void postKeyRequest() {
KeyRequest keyRequest;
try {
keyRequest = mediaDrm.getKeyRequest(sessionId, schemePsshData, mimeType,
keyRequest = mediaDrm.getKeyRequest(sessionId, schemeData, mimeType,
MediaDrm.KEY_TYPE_STREAMING, optionalKeyRequestParameters);
postRequestHandler.obtainMessage(MSG_KEYS, keyRequest).sendToTarget();
} catch (NotProvisionedException e) {
......
......@@ -35,7 +35,6 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import java.util.UUID;
/**
* Facilitates the extraction of data from the fragmented mp4 container format.
......@@ -250,16 +249,11 @@ public final class FragmentedMp4Extractor implements Extractor {
for (int i = 0; i < moovChildrenSize; i++) {
LeafAtom child = moovChildren.get(i);
if (child.type == Atom.TYPE_pssh) {
ParsableByteArray psshAtom = child.data;
psshAtom.setPosition(Atom.FULL_HEADER_SIZE);
UUID uuid = new UUID(psshAtom.readLong(), psshAtom.readLong());
int dataSize = psshAtom.readInt();
byte[] data = new byte[dataSize];
psshAtom.readBytes(data, 0, dataSize);
if (drmInitData == null) {
drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4);
}
drmInitData.put(uuid, data);
byte[] psshData = child.data.data;
drmInitData.put(PsshAtomUtil.parseUuid(psshData), psshData);
}
}
if (drmInitData != null) {
......
/*
* Copyright (C) 2014 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.exoplayer.extractor.mp4;
import com.google.android.exoplayer.util.ParsableByteArray;
import java.nio.ByteBuffer;
import java.util.UUID;
/**
* Utility methods for handling PSSH atoms.
*/
public final class PsshAtomUtil {
private PsshAtomUtil() {}
/**
* Builds a PSSH atom for a given {@link UUID} containing the given scheme specific data.
*
* @param uuid The UUID of the scheme.
* @param data The scheme specific data.
* @return The PSSH atom.
*/
public static byte[] buildPsshAtom(UUID uuid, byte[] data) {
int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */ + data.length;
ByteBuffer psshBox = ByteBuffer.allocate(psshBoxLength);
psshBox.putInt(psshBoxLength);
psshBox.putInt(Atom.TYPE_pssh);
psshBox.putInt(0 /* version=0, flags=0 */);
psshBox.putLong(uuid.getMostSignificantBits());
psshBox.putLong(uuid.getLeastSignificantBits());
psshBox.putInt(data.length);
psshBox.put(data);
return psshBox.array();
}
/**
* Parses the UUID from a PSSH atom.
* <p>
* The UUID is only parsed if the data is a valid PSSH atom.
*
* @param atom The atom to parse.
* @return The parsed UUID. Null if the data is not a valid PSSH atom.
*/
public static UUID parseUuid(byte[] atom) {
ParsableByteArray atomData = new ParsableByteArray(atom);
if (!isPsshAtom(atomData, null)) {
return null;
}
atomData.setPosition(Atom.FULL_HEADER_SIZE);
return new UUID(atomData.readLong(), atomData.readLong());
}
/**
* Parses the scheme specific data from a PSSH atom.
* <p>
* The scheme specific data is only parsed if the data is a valid PSSH atom matching the given
* UUID, or if the data is a valid PSSH atom of any type in the case that the passed UUID is null.
*
* @param atom The atom to parse.
* @param uuid The required UUID of the PSSH atom, or null to accept any UUID.
* @return The parsed scheme specific data. Null if the data is not a valid PSSH atom or if its
* UUID does not match the one provided.
*/
public static byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) {
ParsableByteArray atomData = new ParsableByteArray(atom);
if (!isPsshAtom(atomData, uuid)) {
return null;
}
atomData.setPosition(Atom.FULL_HEADER_SIZE + 16 /* UUID */);
int dataSize = atomData.readInt();
byte[] data = new byte[dataSize];
atomData.readBytes(data, 0, dataSize);
return data;
}
private static boolean isPsshAtom(ParsableByteArray atomData, UUID uuid) {
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
// Data too short.
return false;
}
atomData.setPosition(0);
int atomSize = atomData.readInt();
if (atomSize != atomData.bytesLeft() + 4) {
// Not an atom, or incorrect atom size.
return false;
}
int atomType = atomData.readInt();
if (atomType != Atom.TYPE_pssh) {
// Not an atom, or incorrect atom type.
return false;
}
atomData.setPosition(Atom.FULL_HEADER_SIZE);
if (uuid == null) {
atomData.skipBytes(16);
} else if (atomData.readLong() != uuid.getMostSignificantBits()
|| atomData.readLong() != uuid.getLeastSignificantBits()) {
// UUID doesn't match.
return false;
}
int dataSize = atomData.readInt();
if (dataSize != atomData.bytesLeft()) {
// Incorrect dataSize.
return false;
}
return true;
}
}
......@@ -16,6 +16,7 @@
package com.google.android.exoplayer.smoothstreaming;
import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement;
......@@ -423,7 +424,7 @@ public class SmoothStreamingManifestParser implements UriLoadable.Parser<SmoothS
@Override
public Object build() {
return new ProtectionElement(uuid, initData);
return new ProtectionElement(uuid, PsshAtomUtil.buildPsshAtom(uuid, initData));
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册