mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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.
This commit is contained in:
parent
d0ba265290
commit
4c4782c72d
@ -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) {
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user