field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
- public final class MediaCas {
+ public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
- method public void closeSession(byte[]);
+ method public void close();
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method public static boolean isSystemIdSupported(int);
- method public byte[] openSession(int) throws android.media.MediaCasException;
- method public byte[] openSession(int, int) throws android.media.MediaCasException;
- method public void processEcm(byte[], byte[], int, int) throws android.media.MediaCasException;
- method public void processEcm(byte[], byte[]) throws android.media.MediaCasException;
+ method public android.media.MediaCas.Session openSession(int) throws android.media.MediaCasException;
+ method public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException;
method public void processEmm(byte[], int, int) throws android.media.MediaCasException;
method public void processEmm(byte[]) throws android.media.MediaCasException;
method public void provision(java.lang.String) throws android.media.MediaCasException;
method public void refreshEntitlements(int, byte[]) throws android.media.MediaCasException;
- method public void release();
method public void sendEvent(int, int, byte[]) throws android.media.MediaCasException;
method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
- method public void setSessionPrivateData(byte[], byte[]) throws android.media.MediaCasException;
}
public static abstract interface MediaCas.EventListener {
method public int getSystemId();
}
+ public final class MediaCas.Session implements java.lang.AutoCloseable {
+ method public void close();
+ method public void processEcm(byte[], int, int) throws android.media.MediaCasException;
+ method public void processEcm(byte[]) throws android.media.MediaCasException;
+ method public void setPrivateData(byte[]) throws android.media.MediaCasException;
+ }
+
public class MediaCasException extends java.lang.Exception {
}
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
}
- public final class MediaDescrambler {
+ public final class MediaDescrambler implements java.lang.AutoCloseable {
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
- method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
- method public final void release();
+ method public void close();
+ method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaCasSession(byte[]);
+ method public final void setMediaCasSession(android.media.MediaCas.Session);
}
public class MediaDescription implements android.os.Parcelable {
ctor public MediaExtractor();
method public boolean advance();
method public long getCachedDuration();
+ method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
method public android.media.MediaMetricsSet getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0
}
+ public static final class MediaExtractor.CasInfo {
+ method public android.media.MediaCas.Session getSession();
+ method public int getSystemId();
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
method public final boolean containsKey(java.lang.String);
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
- public final class MediaCas {
+ public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
- method public void closeSession(byte[]);
+ method public void close();
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method public static boolean isSystemIdSupported(int);
- method public byte[] openSession(int) throws android.media.MediaCasException;
- method public byte[] openSession(int, int) throws android.media.MediaCasException;
- method public void processEcm(byte[], byte[], int, int) throws android.media.MediaCasException;
- method public void processEcm(byte[], byte[]) throws android.media.MediaCasException;
+ method public android.media.MediaCas.Session openSession(int) throws android.media.MediaCasException;
+ method public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException;
method public void processEmm(byte[], int, int) throws android.media.MediaCasException;
method public void processEmm(byte[]) throws android.media.MediaCasException;
method public void provision(java.lang.String) throws android.media.MediaCasException;
method public void refreshEntitlements(int, byte[]) throws android.media.MediaCasException;
- method public void release();
method public void sendEvent(int, int, byte[]) throws android.media.MediaCasException;
method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
- method public void setSessionPrivateData(byte[], byte[]) throws android.media.MediaCasException;
}
public static abstract interface MediaCas.EventListener {
method public int getSystemId();
}
+ public final class MediaCas.Session implements java.lang.AutoCloseable {
+ method public void close();
+ method public void processEcm(byte[], int, int) throws android.media.MediaCasException;
+ method public void processEcm(byte[]) throws android.media.MediaCasException;
+ method public void setPrivateData(byte[]) throws android.media.MediaCasException;
+ }
+
public class MediaCasException extends java.lang.Exception {
}
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
}
- public final class MediaDescrambler {
+ public final class MediaDescrambler implements java.lang.AutoCloseable {
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
- method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
- method public final void release();
+ method public void close();
+ method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaCasSession(byte[]);
+ method public final void setMediaCasSession(android.media.MediaCas.Session);
}
public class MediaDescription implements android.os.Parcelable {
ctor public MediaExtractor();
method public boolean advance();
method public long getCachedDuration();
+ method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
method public android.media.MediaMetricsSet getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0
}
+ public static final class MediaExtractor.CasInfo {
+ method public android.media.MediaCas.Session getSession();
+ method public int getSystemId();
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
method public final boolean containsKey(java.lang.String);
field public static final int STOP_VIDEO_RECORDING = 3; // 0x3
}
- public final class MediaCas {
+ public final class MediaCas implements java.lang.AutoCloseable {
ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException;
- method public void closeSession(byte[]);
+ method public void close();
method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins();
method public static boolean isSystemIdSupported(int);
- method public byte[] openSession(int) throws android.media.MediaCasException;
- method public byte[] openSession(int, int) throws android.media.MediaCasException;
- method public void processEcm(byte[], byte[], int, int) throws android.media.MediaCasException;
- method public void processEcm(byte[], byte[]) throws android.media.MediaCasException;
+ method public android.media.MediaCas.Session openSession(int) throws android.media.MediaCasException;
+ method public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException;
method public void processEmm(byte[], int, int) throws android.media.MediaCasException;
method public void processEmm(byte[]) throws android.media.MediaCasException;
method public void provision(java.lang.String) throws android.media.MediaCasException;
method public void refreshEntitlements(int, byte[]) throws android.media.MediaCasException;
- method public void release();
method public void sendEvent(int, int, byte[]) throws android.media.MediaCasException;
method public void setEventListener(android.media.MediaCas.EventListener, android.os.Handler);
method public void setPrivateData(byte[]) throws android.media.MediaCasException;
- method public void setSessionPrivateData(byte[], byte[]) throws android.media.MediaCasException;
}
public static abstract interface MediaCas.EventListener {
method public int getSystemId();
}
+ public final class MediaCas.Session implements java.lang.AutoCloseable {
+ method public void close();
+ method public void processEcm(byte[], int, int) throws android.media.MediaCasException;
+ method public void processEcm(byte[]) throws android.media.MediaCasException;
+ method public void setPrivateData(byte[]) throws android.media.MediaCasException;
+ }
+
public class MediaCasException extends java.lang.Exception {
}
method public abstract int readAt(long, byte[], int, int) throws java.io.IOException;
}
- public final class MediaDescrambler {
+ public final class MediaDescrambler implements java.lang.AutoCloseable {
ctor public MediaDescrambler(int) throws android.media.MediaCasException.UnsupportedCasException;
- method public final int descramble(java.nio.ByteBuffer, int, java.nio.ByteBuffer, int, android.media.MediaCodec.CryptoInfo);
- method public final void release();
+ method public void close();
+ method public final int descramble(java.nio.ByteBuffer, java.nio.ByteBuffer, android.media.MediaCodec.CryptoInfo);
method public final boolean requiresSecureDecoderComponent(java.lang.String);
- method public final void setMediaCasSession(byte[]);
+ method public final void setMediaCasSession(android.media.MediaCas.Session);
}
public class MediaDescription implements android.os.Parcelable {
ctor public MediaExtractor();
method public boolean advance();
method public long getCachedDuration();
+ method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
method public android.media.MediaMetricsSet getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
field public static final int SEEK_TO_PREVIOUS_SYNC = 0; // 0x0
}
+ public static final class MediaExtractor.CasInfo {
+ method public android.media.MediaCas.Session getSession();
+ method public int getSystemId();
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
method public final boolean containsKey(java.lang.String);
* management messages) can be distributed out-of-band, or in-band with the stream.
* <p>
* To descramble elementary streams, the app first calls {@link #openSession} to
- * generate a sessionId that will uniquely identify a session. A session provides
- * a context for subsequent key updates and descrambling activities. The ECMs
- * (Entitlement control messages) are sent to the session via method {@link #processEcm}.
+ * generate a {@link Session} object that will uniquely identify a session. A session
+ * provides a context for subsequent key updates and descrambling activities. The ECMs
+ * (Entitlement control messages) are sent to the session via method
+ * {@link Session#processEcm}.
* <p>
* The app next constructs a MediaDescrambler object, and initializes it with the
- * sessionId using {@link MediaDescrambler#setMediaCasSession}. This ties the
+ * session using {@link MediaDescrambler#setMediaCasSession}. This ties the
* descrambler to the session, and the descrambler can then be used to descramble
* content secured with the session's key, either during extraction, or during decoding
* with {@link android.media.MediaCodec}.
* If the app uses {@link MediaExtractor}, it can delegate the CAS session
* management to MediaExtractor by calling {@link MediaExtractor#setMediaCas}.
* MediaExtractor will take over and call {@link #openSession}, {@link #processEmm}
- * and/or {@link #processEcm}, etc.. if necessary.
+ * and/or {@link Session#processEcm}, etc.. if necessary.
* <p>
* When using {@link MediaExtractor}, the app would still need a MediaDescrambler
* to use with {@link MediaCodec} if the licensing requires a secure decoder. The
- * sessionId of the descrambler can be retrieved by {@link MediaExtractor#getDrmInitData}
- * and used to initialize a MediaDescrambler object for MediaCodec.
+ * session associated with the descrambler of a track can be retrieved by calling
+ * {@link MediaExtractor#getCasInfo}, and used to initialize a MediaDescrambler
+ * object for MediaCodec.
* <p>
* <h3>Listeners</h3>
* <p>The app may register a listener to receive events from the CA system using
* method {@link #setEventListener}. The exact format of the event is scheme-specific
* and is not specified by this API.
*/
-public final class MediaCas {
+public final class MediaCas implements AutoCloseable {
private static final String TAG = "MediaCas";
private final ParcelableCasData mCasData = new ParcelableCasData();
private ICas mICas;
}
/**
+ * Class for an open session with the CA system.
+ */
+ public final class Session implements AutoCloseable {
+ final byte[] mSessionId;
+
+ Session(@NonNull byte[] sessionId) {
+ mSessionId = sessionId;
+ }
+
+ /**
+ * Set the private data for a session.
+ *
+ * @param data byte array of the private data.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ * @throws MediaCasException for CAS-specific errors.
+ * @throws MediaCasStateException for CAS-specific state exceptions.
+ */
+ public void setPrivateData(@NonNull byte[] data)
+ throws MediaCasException {
+ validateInternalStates();
+
+ try {
+ mICas.setSessionPrivateData(mSessionId, data);
+ } catch (ServiceSpecificException e) {
+ MediaCasException.throwExceptions(e);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+
+ /**
+ * Send a received ECM packet to the specified session of the CA system.
+ *
+ * @param data byte array of the ECM data.
+ * @param offset position within data where the ECM data begins.
+ * @param length length of the data (starting from offset).
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ * @throws MediaCasException for CAS-specific errors.
+ * @throws MediaCasStateException for CAS-specific state exceptions.
+ */
+ public void processEcm(@NonNull byte[] data, int offset, int length)
+ throws MediaCasException {
+ validateInternalStates();
+
+ try {
+ mCasData.set(data, offset, length);
+ mICas.processEcm(mSessionId, mCasData);
+ } catch (ServiceSpecificException e) {
+ MediaCasException.throwExceptions(e);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+
+ /**
+ * Send a received ECM packet to the specified session of the CA system.
+ * This is similar to {@link Session#processEcm(byte[], int, int)}
+ * except that the entire byte array is sent.
+ *
+ * @param data byte array of the ECM data.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ * @throws MediaCasException for CAS-specific errors.
+ * @throws MediaCasStateException for CAS-specific state exceptions.
+ */
+ public void processEcm(@NonNull byte[] data) throws MediaCasException {
+ processEcm(data, 0, data.length);
+ }
+
+ /**
+ * Close the session.
+ *
+ * @throws IllegalStateException if the MediaCas instance is not valid.
+ * @throws MediaCasStateException for CAS-specific state exceptions.
+ */
+ @Override
+ public void close() {
+ validateInternalStates();
+
+ try {
+ mICas.closeSession(mSessionId);
+ } catch (ServiceSpecificException e) {
+ MediaCasStateException.throwExceptions(e);
+ } catch (RemoteException e) {
+ cleanupAndRethrowIllegalState();
+ }
+ }
+ }
+
+ Session createFromSessionId(byte[] sessionId) {
+ if (sessionId == null || sessionId.length == 0) {
+ return null;
+ }
+ return new Session(sessionId);
+ }
+
+ /**
* Class for parceling CAS plugin descriptors over IMediaCasService binder.
*/
static class ParcelableCasPluginDescriptor
*
* @param programNumber program_number of the program (as in ISO/IEC13818-1).
*
- * @return session id of the newly opened session.
+ * @return session the newly opened session.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
- public byte[] openSession(int programNumber) throws MediaCasException {
+ public Session openSession(int programNumber) throws MediaCasException {
validateInternalStates();
try {
- return mICas.openSession(programNumber);
+ return createFromSessionId(mICas.openSession(programNumber));
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
* @param programNumber program_number of the stream (as in ISO/IEC13818-1).
* @param elementaryPID elementary_PID of the stream (as in ISO/IEC13818-1).
*
- * @return session id of the newly opened session.
+ * @return session the newly opened session.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
* @throws MediaCasException for CAS-specific errors.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
- public byte[] openSession(int programNumber, int elementaryPID)
+ public Session openSession(int programNumber, int elementaryPID)
throws MediaCasException {
validateInternalStates();
try {
- return mICas.openSessionForStream(programNumber, elementaryPID);
+ return createFromSessionId(mICas.openSessionForStream(programNumber, elementaryPID));
} catch (ServiceSpecificException e) {
MediaCasException.throwExceptions(e);
} catch (RemoteException e) {
}
/**
- * Close the specified session.
- *
- * @param sessionId the session to be closed.
- *
- * @throws IllegalStateException if the MediaCas instance is not valid.
- * @throws MediaCasStateException for CAS-specific state exceptions.
- */
- public void closeSession(@NonNull byte[] sessionId) {
- validateInternalStates();
-
- try {
- mICas.closeSession(sessionId);
- } catch (ServiceSpecificException e) {
- MediaCasStateException.throwExceptions(e);
- } catch (RemoteException e) {
- cleanupAndRethrowIllegalState();
- }
- }
-
- /**
- * Set the private data for a session.
- *
- * @param sessionId the session for which the private data is intended.
- * @param data byte array of the private data.
- *
- * @throws IllegalStateException if the MediaCas instance is not valid.
- * @throws MediaCasException for CAS-specific errors.
- * @throws MediaCasStateException for CAS-specific state exceptions.
- */
- public void setSessionPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data)
- throws MediaCasException {
- validateInternalStates();
-
- try {
- mICas.setSessionPrivateData(sessionId, data);
- } catch (ServiceSpecificException e) {
- MediaCasException.throwExceptions(e);
- } catch (RemoteException e) {
- cleanupAndRethrowIllegalState();
- }
- }
-
- /**
- * Send a received ECM packet to the specified session of the CA system.
- *
- * @param sessionId the session for which the ECM is intended.
- * @param data byte array of the ECM data.
- * @param offset position within data where the ECM data begins.
- * @param length length of the data (starting from offset).
- *
- * @throws IllegalStateException if the MediaCas instance is not valid.
- * @throws MediaCasException for CAS-specific errors.
- * @throws MediaCasStateException for CAS-specific state exceptions.
- */
- public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data,
- int offset, int length) throws MediaCasException {
- validateInternalStates();
-
- try {
- mCasData.set(data, offset, length);
- mICas.processEcm(sessionId, mCasData);
- } catch (ServiceSpecificException e) {
- MediaCasException.throwExceptions(e);
- } catch (RemoteException e) {
- cleanupAndRethrowIllegalState();
- }
- }
-
- /**
- * Send a received ECM packet to the specified session of the CA system.
- * This is similar to {@link #processEcm(byte[], byte[], int, int)}
- * except that the entire byte array is sent.
- *
- * @param sessionId the session for which the ECM is intended.
- * @param data byte array of the ECM data.
- *
- * @throws IllegalStateException if the MediaCas instance is not valid.
- * @throws MediaCasException for CAS-specific errors.
- * @throws MediaCasStateException for CAS-specific state exceptions.
- */
- public void processEcm(@NonNull byte[] sessionId, @NonNull byte[] data)
- throws MediaCasException {
- processEcm(sessionId, data, 0, data.length);
- }
-
- /**
* Send a received EMM packet to the CA system.
*
* @param data byte array of the EMM data.
}
}
- /**
- * Release the MediaCas instance.
- */
- public void release() {
+ @Override
+ public void close() {
if (mICas != null) {
try {
mICas.release();
@Override
protected void finalize() {
- release();
+ close();
}
}
\ No newline at end of file
* Scrambling schemes are identified by 16-bit unsigned integer as in CA_system_id.
*
*/
-public final class MediaDescrambler {
+public final class MediaDescrambler implements AutoCloseable {
private static final String TAG = "MediaDescrambler";
private IDescrambler mIDescrambler;
* android.media.MediaCodec#queueSecureInputBuffer} by specifying even
* or odd key in the {@link android.media.MediaCodec.CryptoInfo#key} field.
*
- * @param sessionId the MediaCas sessionId to associate with this
+ * @param session the MediaCas session to associate with this
* MediaDescrambler instance.
*
* @throws IllegalStateException if the descrambler instance is not valid.
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
- public final void setMediaCasSession(@NonNull byte[] sessionId) {
+ public final void setMediaCasSession(@NonNull MediaCas.Session session) {
validateInternalStates();
try {
- mIDescrambler.setMediaCasSession(sessionId);
+ mIDescrambler.setMediaCasSession(session.mSessionId);
} catch (ServiceSpecificException e) {
MediaCasStateException.throwExceptions(e);
} catch (RemoteException e) {
* Descramble a ByteBuffer of data described by a
* {@link android.media.MediaCodec.CryptoInfo} structure.
*
- * @param srcBuf ByteBuffer containing the scrambled data.
- * @param srcPos position within src where the scrambled data starts.
- * @param dstBuf ByteBuffer to descramble into. If null, descrambling will happen
- * in-place and src will be used as dst.
- * @param dstPos position within dst to put the descrambled data.
+ * @param srcBuf ByteBuffer containing the scrambled data, which starts at
+ * srcBuf.position().
+ * @param dstBuf ByteBuffer to hold the descrambled data, which starts at
+ * dstBuf.position().
* @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure
* describing the subsamples contained in src.
*
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public final int descramble(
- @NonNull ByteBuffer srcBuf, int srcPos, ByteBuffer dstBuf, int dstPos,
+ @NonNull ByteBuffer srcBuf, @NonNull ByteBuffer dstBuf,
@NonNull MediaCodec.CryptoInfo cryptoInfo) {
validateInternalStates();
cryptoInfo.numSubSamples,
cryptoInfo.numBytesOfClearData,
cryptoInfo.numBytesOfEncryptedData,
- srcBuf, srcPos, dstBuf, dstPos);
+ srcBuf, srcBuf.position(), srcBuf.limit(),
+ dstBuf, dstBuf.position(), dstBuf.limit());
} catch (ServiceSpecificException e) {
MediaCasStateException.throwExceptions(e);
}
return -1;
}
- public final void release() {
+ @Override
+ public void close() {
if (mIDescrambler != null) {
try {
mIDescrambler.release();
@Override
protected void finalize() {
- release();
+ close();
}
private static native final void native_init();
private native final void native_release();
private native final int native_descramble(
byte key, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
- @NonNull ByteBuffer srcBuf, int srcOffset, ByteBuffer dstBuf, int dstOffset);
+ @NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit,
+ ByteBuffer dstBuf, int dstOffset, int dstLimit);
static {
System.loadLibrary("media_jni");
* @param mediaCas the MediaCas object to use.
*/
public final void setMediaCas(@NonNull MediaCas mediaCas) {
+ mMediaCas = mediaCas;
nativeSetMediaCas(mediaCas.getBinder());
}
private native final void nativeSetMediaCas(@NonNull IBinder casBinder);
+ /**
+ * Describes the conditional access system used to scramble a track.
+ */
+ public static final class CasInfo {
+ private final int mSystemId;
+ private final MediaCas.Session mSession;
+
+ CasInfo(int systemId, @Nullable MediaCas.Session session) {
+ mSystemId = systemId;
+ mSession = session;
+ }
+
+ /**
+ * Retrieves the system id of the conditional access system.
+ *
+ * @return CA system id of the CAS used to scramble the track.
+ */
+ public int getSystemId() {
+ return mSystemId;
+ }
+
+ /**
+ * Retrieves the {@link MediaCas.Session} associated with a track. The
+ * session is needed to initialize a descrambler in order to decode the
+ * scrambled track.
+ * <p>
+ * @see MediaDescrambler#setMediaCasSession
+ * <p>
+ * @return a {@link MediaCas.Session} object associated with a track.
+ */
+ public MediaCas.Session getSession() {
+ return mSession;
+ }
+ }
+
+ /**
+ * Retrieves the information about the conditional access system used to scramble
+ * a track.
+ *
+ * @param index of the track.
+ * @return an {@link CasInfo} object describing the conditional access system.
+ */
+ public CasInfo getCasInfo(int index) {
+ Map<String, Object> formatMap = getTrackFormatNative(index);
+ if (formatMap.containsKey(MediaFormat.KEY_CA_SYSTEM_ID)) {
+ int systemId = ((Integer)formatMap.get(MediaFormat.KEY_CA_SYSTEM_ID)).intValue();
+ MediaCas.Session session = null;
+ if (mMediaCas != null && formatMap.containsKey(MediaFormat.KEY_CA_SESSION_ID)) {
+ ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_SESSION_ID);
+ buf.rewind();
+ final byte[] sessionId = new byte[buf.remaining()];
+ buf.get(sessionId);
+ session = mMediaCas.createFromSessionId(sessionId);
+ }
+ return new CasInfo(systemId, session);
+ }
+ return null;
+ }
+
@Override
protected void finalize() {
native_finalize();
return initDataMap.get(schemeUuid);
}
};
- } else if (formatMap.containsKey("mime")
- && "video/mp2ts".equals(formatMap.get("mime"))) {
- final Map<UUID, DrmInitData.SchemeInitData> initDataMap =
- new HashMap<UUID, DrmInitData.SchemeInitData>();
-
- int numTracks = getTrackCount();
- for (int i = 0; i < numTracks; ++i) {
- Map<String, Object> trackFormatMap = getTrackFormatNative(i);
- if (!trackFormatMap.containsKey("cas")) {
- continue;
- }
- ByteBuffer buf = (ByteBuffer) trackFormatMap.get("cas");
- buf.rewind();
- final byte[] data = new byte[buf.remaining()];
- buf.get(data);
- initDataMap.put(new UUID(0, i), new DrmInitData.SchemeInitData("cas", data));
- }
- if (initDataMap.isEmpty()) {
- return null;
- }
- return new DrmInitData() {
- public SchemeInitData get(UUID schemeUuid) {
- return initDataMap.get(schemeUuid);
- }
- };
} else {
int numTracks = getTrackCount();
for (int i = 0; i < numTracks; ++i) {
}
};
}
- return null;
}
+ return null;
}
/**
native_init();
}
+ private MediaCas mMediaCas;
+
private long mNativeContext;
}
*/
public static final String KEY_TRACK_ID = "track-id";
+ /**
+ * A key describing the system id of the conditional access system used to scramble
+ * a media track.
+ * <p>
+ * This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
+ * access system.
+ * <p>
+ * The associated value is an integer.
+ * @hide
+ */
+ public static final String KEY_CA_SYSTEM_ID = "ca-system-id";
+
+ /**
+ * A key describing the {@link MediaCas.Session} object associated with a media track.
+ * <p>
+ * This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
+ * access system.
+ * <p>
+ * The associated value is a ByteBuffer.
+ * @hide
+ */
+ public static final String KEY_CA_SESSION_ID = "ca-session-id";
+
/* package private */ MediaFormat(Map<String, Object> map) {
mMap = map;
}
}
static status_t getBufferAndSize(
- JNIEnv *env, jobject byteBuf, jint offset, size_t length,
+ JNIEnv *env, jobject byteBuf, jint offset, jint limit, size_t length,
void **outPtr, jbyteArray *outByteArray) {
void *ptr = env->GetDirectBufferAddress(byteBuf);
- size_t bufSize;
jbyteArray byteArray = NULL;
ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
jboolean isCopy;
ptr = env->GetByteArrayElements(byteArray, &isCopy);
-
- bufSize = (size_t) env->GetArrayLength(byteArray);
- } else {
- bufSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
}
- if (length + offset > bufSize) {
+ if ((jint)length + offset > limit) {
if (byteArray != NULL) {
env->ReleaseByteArrayElements(byteArray, (jbyte *)ptr, 0);
}
static jint android_media_MediaDescrambler_native_descramble(
JNIEnv *env, jobject thiz, jbyte key, jint numSubSamples,
jintArray numBytesOfClearDataObj, jintArray numBytesOfEncryptedDataObj,
- jobject srcBuf, jint srcOffset, jobject dstBuf, jint dstOffset) {
+ jobject srcBuf, jint srcOffset, jint srcLimit,
+ jobject dstBuf, jint dstOffset, jint dstLimit) {
sp<JDescrambler> descrambler = getDescrambler(env, thiz);
if (descrambler == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
numBytesOfEncryptedDataObj, &subSamples);
if (totalLength < 0) {
jniThrowException(env, "java/lang/IllegalArgumentException",
- "Invalid sub sample info!");
+ "Invalid subsample info!");
return -1;
}
void *srcPtr = NULL, *dstPtr = NULL;
jbyteArray srcArray = NULL, dstArray = NULL;
status_t err = getBufferAndSize(
- env, srcBuf, srcOffset, totalLength, &srcPtr, &srcArray);
+ env, srcBuf, srcOffset, srcLimit, totalLength, &srcPtr, &srcArray);
if (err == OK) {
if (dstBuf == NULL) {
dstPtr = srcPtr;
} else {
err = getBufferAndSize(
- env, dstBuf, dstOffset, totalLength, &dstPtr, &dstArray);
+ env, dstBuf, dstOffset, dstLimit, totalLength, &dstPtr, &dstArray);
}
}
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Invalid buffer offset and/or size for subsamples!");
+ return -1;
+ }
+
Status status;
if (err == OK) {
status = descrambler->descramble(
(void *)android_media_MediaDescrambler_native_init },
{ "native_setup", "(Landroid/os/IBinder;)V",
(void *)android_media_MediaDescrambler_native_setup },
- { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;ILjava/nio/ByteBuffer;I)I",
+ { "native_descramble", "(BI[I[ILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)I",
(void *)android_media_MediaDescrambler_native_descramble },
};