Modular DRM for MediaPlayer
Bug: 34559906
Test: Manual through the test app
Change-Id: I286f9ff199c34563b7b8643de725f8d1534ea06c
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index a4cc152..e5a98dd 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -24,7 +24,6 @@
#include <system/audio.h>
#include <media/IMediaSource.h>
-#include <media/drm/DrmAPI.h> // for DrmPlugin::* enum
#include <media/VolumeShaper.h>
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
@@ -97,22 +96,10 @@
const sp<VolumeShaper::Operation>& operation) = 0;
virtual sp<VolumeShaper::State> getVolumeShaperState(int id) = 0;
- // ModDrm
- virtual status_t prepareDrm(const uint8_t uuid[16], const int mode) = 0;
+ // Modular DRM
+ virtual status_t prepareDrm(const uint8_t uuid[16],
+ const Vector<uint8_t>& drmSessionId) = 0;
virtual status_t releaseDrm() = 0;
- virtual status_t getKeyRequest(Vector<uint8_t> const& scope,
- String8 const &mimeType,
- DrmPlugin::KeyType keyType,
- KeyedVector<String8, String8>& optionalParameters,
- Vector<uint8_t>& request,
- String8& defaultUrl,
- DrmPlugin::KeyRequestType& keyRequestType) = 0;
- virtual status_t provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
- Vector<uint8_t>& response,
- Vector<uint8_t>& keySetId) = 0;
- virtual status_t restoreKeys(Vector<uint8_t> const& keySetId) = 0;
- virtual status_t getDrmPropertyString(String8 const& name, String8& value) = 0;
- virtual status_t setDrmPropertyString(String8 const& name, String8 const& value) = 0;
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index b3e53fc..a01f7f2 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -285,33 +285,13 @@
return INVALID_OPERATION;
}
- // ModDrm
- virtual status_t prepareDrm(const uint8_t uuid[16], const int mode) {
+ // Modular DRM
+ virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId) {
return INVALID_OPERATION;
}
virtual status_t releaseDrm() {
return INVALID_OPERATION;
}
- virtual status_t getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
- DrmPlugin::KeyType keyType,
- KeyedVector<String8, String8>& optionalParameters,
- Vector<uint8_t>& request, String8& defaultUrl,
- DrmPlugin::KeyRequestType& keyRequestType) {
- return INVALID_OPERATION;
- }
- virtual status_t provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
- Vector<uint8_t>& response, Vector<uint8_t>& keySetId) {
- return INVALID_OPERATION;
- }
- virtual status_t restoreKeys(Vector<uint8_t> const& keySetId) {
- return INVALID_OPERATION;
- }
- virtual status_t getDrmPropertyString(String8 const& name, String8& value) {
- return INVALID_OPERATION;
- }
- virtual status_t setDrmPropertyString(String8 const& name, String8 const& value) {
- return INVALID_OPERATION;
- }
private:
friend class MediaPlayerService;
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index fbe3926..18d69a7 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -266,19 +266,9 @@
const sp<VolumeShaper::Configuration>& configuration,
const sp<VolumeShaper::Operation>& operation);
sp<VolumeShaper::State> getVolumeShaperState(int id);
- // ModDrm
- status_t prepareDrm(const uint8_t uuid[16], const int mode);
+ // Modular DRM
+ status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
status_t releaseDrm();
- status_t getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
- DrmPlugin::KeyType keyType,
- KeyedVector<String8, String8>& optionalParameters,
- Vector<uint8_t>& request, String8& defaultUrl,
- DrmPlugin::KeyRequestType& keyRequestType);
- status_t provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
- Vector<uint8_t>& response, Vector<uint8_t>& keySetId);
- status_t restoreKeys(Vector<uint8_t> const& keySetId);
- status_t getDrmPropertyString(String8 const& name, String8& value);
- status_t setDrmPropertyString(String8 const& name, String8 const& value);
private:
void clear_l();
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 699ae48..20b26e2 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -91,6 +91,8 @@
const sp<ICrypto> &crypto,
uint32_t flags);
+ status_t releaseCrypto();
+
status_t setCallback(const sp<AMessage> &callback);
status_t setOnFrameRenderedNotification(const sp<AMessage> ¬ify);
@@ -239,6 +241,7 @@
kWhatSetParameters = 'setP',
kWhatSetCallback = 'setC',
kWhatSetNotification = 'setN',
+ kWhatDrmReleaseCrypto = 'rDrm',
};
enum {
@@ -416,6 +419,8 @@
mStickyError = err;
}
+ void onReleaseCrypto(const sp<AMessage>& msg);
+
DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
};
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 5222a42..3996227 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -72,14 +72,9 @@
SET_NEXT_PLAYER,
APPLY_VOLUME_SHAPER,
GET_VOLUME_SHAPER_STATE,
- // ModDrm
+ // Modular DRM
PREPARE_DRM,
RELEASE_DRM,
- GET_KEY_REQUEST,
- PROVIDE_KEY_RESPONSE,
- RESTORE_KEYS,
- GET_DRM_PROPERTY_STRING,
- SET_DRM_PROPERTY_STRING,
};
// ModDrm helpers
@@ -521,14 +516,14 @@
return state;
}
- // ModDrm
- status_t prepareDrm(const uint8_t uuid[16], const int mode)
+ // Modular DRM
+ status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
{
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.write(uuid, 16);
- data.writeInt32(mode);
+ writeVector(data, drmSessionId);
status_t status = remote()->transact(PREPARE_DRM, data, &reply);
if (status != OK) {
@@ -552,105 +547,6 @@
return reply.readInt32();
}
-
- status_t getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
- DrmPlugin::KeyType keyType, KeyedVector<String8, String8>& optionalParameters,
- Vector<uint8_t>& request, String8& defaultUrl,
- DrmPlugin::KeyRequestType& keyRequestType)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
- writeVector(data, scope);
- data.writeString8(mimeType);
- data.writeInt32((int32_t)keyType);
-
- data.writeUint32(optionalParameters.size());
- for (size_t i = 0; i < optionalParameters.size(); ++i) {
- data.writeString8(optionalParameters.keyAt(i));
- data.writeString8(optionalParameters.valueAt(i));
- }
-
- status_t status = remote()->transact(GET_KEY_REQUEST, data, &reply);
- if (status != OK) {
- ALOGE("getKeyRequest: binder call failed: %d", status);
- return status;
- }
-
- readVector(reply, request);
- defaultUrl = reply.readString8();
- keyRequestType = (DrmPlugin::KeyRequestType)reply.readInt32();
-
- return reply.readInt32();
- }
-
- status_t provideKeyResponse(Vector<uint8_t>& releaseKeySetId, Vector<uint8_t>& response,
- Vector<uint8_t> &keySetId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
- writeVector(data, releaseKeySetId);
- writeVector(data, response);
-
- status_t status = remote()->transact(PROVIDE_KEY_RESPONSE, data, &reply);
- if (status != OK) {
- ALOGE("provideKeyResponse: binder call failed: %d", status);
- return status;
- }
-
- readVector(reply, keySetId);
-
- return reply.readInt32();
- }
-
- status_t restoreKeys(Vector<uint8_t> const& keySetId)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
- writeVector(data, keySetId);
-
- status_t status = remote()->transact(RESTORE_KEYS, data, &reply);
- if (status != OK) {
- ALOGE("restoreKeys: binder call failed: %d", status);
- return status;
- }
-
- return reply.readInt32();
- }
-
- status_t getDrmPropertyString(String8 const& name, String8& value)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
- data.writeString8(name);
- status_t status = remote()->transact(GET_DRM_PROPERTY_STRING, data, &reply);
- if (status != OK) {
- ALOGE("getDrmPropertyString: binder call failed: %d", status);
- return status;
- }
-
- value = reply.readString8();
- return reply.readInt32();
- }
-
- status_t setDrmPropertyString(String8 const& name, String8 const& value)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
- data.writeString8(name);
- data.writeString8(value);
- status_t status = remote()->transact(SET_DRM_PROPERTY_STRING, data, &reply);
- if (status != OK) {
- ALOGE("setDrmPropertyString: binder call failed: %d", status);
- return status;
- }
-
- return reply.readInt32();
- }
};
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -983,15 +879,16 @@
return NO_ERROR;
} break;
- // ModDrm
+ // Modular DRM
case PREPARE_DRM: {
CHECK_INTERFACE(IMediaPlayer, data, reply);
+
uint8_t uuid[16];
data.read(uuid, sizeof(uuid));
+ Vector<uint8_t> drmSessionId;
+ readVector(data, drmSessionId);
- int mode = data.readInt32();
-
- uint32_t result = prepareDrm(uuid, mode);
+ uint32_t result = prepareDrm(uuid, drmSessionId);
reply->writeInt32(result);
return OK;
}
@@ -1002,73 +899,6 @@
reply->writeInt32(result);
return OK;
}
- case GET_KEY_REQUEST: {
- CHECK_INTERFACE(IMediaPlayer, data, reply);
-
- Vector<uint8_t> scope;
- readVector(data, scope);
- String8 mimeType = data.readString8();
- DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)data.readInt32();
-
- KeyedVector<String8, String8> optionalParameters;
- uint32_t count = data.readUint32();
- for (size_t i = 0; i < count; ++i) {
- String8 key, value;
- key = data.readString8();
- value = data.readString8();
- optionalParameters.add(key, value);
- }
-
- Vector<uint8_t> request;
- String8 defaultUrl;
- DrmPlugin::KeyRequestType keyRequestType = DrmPlugin::kKeyRequestType_Unknown;
-
- status_t result = getKeyRequest(scope, mimeType, keyType, optionalParameters,
- request, defaultUrl, keyRequestType);
-
- writeVector(*reply, request);
- reply->writeString8(defaultUrl);
- reply->writeInt32(keyRequestType);
- reply->writeInt32(result);
- return OK;
- }
- case PROVIDE_KEY_RESPONSE: {
- CHECK_INTERFACE(IMediaPlayer, data, reply);
- Vector<uint8_t> releaseKeySetId, response, keySetId;
- readVector(data, releaseKeySetId);
- readVector(data, response);
- uint32_t result = provideKeyResponse(releaseKeySetId, response, keySetId);
- writeVector(*reply, keySetId);
- reply->writeInt32(result);
- return OK;
- }
- case RESTORE_KEYS: {
- CHECK_INTERFACE(IMediaPlayer, data, reply);
-
- Vector<uint8_t> keySetId;
- readVector(data, keySetId);
- uint32_t result = restoreKeys(keySetId);
- reply->writeInt32(result);
- return OK;
- }
- case GET_DRM_PROPERTY_STRING: {
- CHECK_INTERFACE(IMediaPlayer, data, reply);
- String8 name, value;
- name = data.readString8();
- uint32_t result = getDrmPropertyString(name, value);
- reply->writeString8(value);
- reply->writeInt32(result);
- return OK;
- }
- case SET_DRM_PROPERTY_STRING: {
- CHECK_INTERFACE(IMediaPlayer, data, reply);
- String8 name, value;
- name = data.readString8();
- value = data.readString8();
- uint32_t result = setDrmPropertyString(name, value);
- reply->writeInt32(result);
- return OK;
- }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index dfc2e1b..685065a 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -16,7 +16,7 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer"
+#define LOG_TAG "MediaPlayerNative"
#include <fcntl.h>
#include <inttypes.h>
@@ -1012,9 +1012,12 @@
return mPlayer->getVolumeShaperState(id);
}
-// ModDrm
-status_t MediaPlayer::prepareDrm(const uint8_t uuid[16], const int mode)
+// Modular DRM
+status_t MediaPlayer::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId)
{
+ // TODO change to ALOGV
+ ALOGD("prepareDrm: uuid: %p drmSessionId: %p(%zu)", uuid,
+ drmSessionId.array(), drmSessionId.size());
Mutex::Autolock _l(mLock);
if (mPlayer == NULL) {
return NO_INIT;
@@ -1026,10 +1029,19 @@
return INVALID_OPERATION;
}
- status_t ret = mPlayer->prepareDrm(uuid, mode);
- ALOGV("prepareDrm: ret=%d", ret);
+ if (drmSessionId.isEmpty()) {
+ ALOGE("prepareDrm: Unexpected. Can't proceed with crypto. Empty drmSessionId.");
+ return INVALID_OPERATION;
+ }
- return ret;
+ // Passing down to mediaserver mainly for creating the crypto
+ status_t status = mPlayer->prepareDrm(uuid, drmSessionId);
+ ALOGE_IF(status != OK, "prepareDrm: Failed at mediaserver with ret: %d", status);
+
+ // TODO change to ALOGV
+ ALOGD("prepareDrm: mediaserver::prepareDrm ret=%d", status);
+
+ return status;
}
status_t MediaPlayer::releaseDrm()
@@ -1039,96 +1051,26 @@
return NO_INIT;
}
- // Not allowing releaseDrm in an active state
- if (mCurrentState & (MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED)) {
- ALOGE("releaseDrm can not be called in the started/paused state.");
+ // Not allowing releaseDrm in an active/resumable state
+ if (mCurrentState & (MEDIA_PLAYER_STARTED |
+ MEDIA_PLAYER_PAUSED |
+ MEDIA_PLAYER_PLAYBACK_COMPLETE |
+ MEDIA_PLAYER_STATE_ERROR)) {
+ ALOGE("releaseDrm Unexpected state %d. Can only be called in stopped/idle.", mCurrentState);
return INVALID_OPERATION;
}
- status_t ret = mPlayer->releaseDrm();
- ALOGV("releaseDrm: ret=%d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer::getKeyRequest(Vector<uint8_t> const& scope, String8 const& mimeType,
- DrmPlugin::KeyType keyType,
- KeyedVector<String8, String8>& optionalParameters,
- Vector<uint8_t>& request, String8& defaultUrl,
- DrmPlugin::KeyRequestType& keyRequestType)
-{
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
+ status_t status = mPlayer->releaseDrm();
+ // TODO change to ALOGV
+ ALOGD("releaseDrm: mediaserver::releaseDrm ret: %d", status);
+ if (status != OK) {
+ ALOGE("releaseDrm: Failed at mediaserver with ret: %d", status);
+ // Overriding to OK so the client proceed with its own cleanup
+ // Client can't do more cleanup. mediaserver release its crypto at end of session anyway.
+ status = OK;
}
- // Not enforcing a particular state beyond the checks enforced by the Java layer
- // Key exchange can happen after the start.
- status_t ret = mPlayer->getKeyRequest(scope, mimeType, keyType, optionalParameters,
- request, defaultUrl, keyRequestType);
- ALOGV("getKeyRequest ret=%d %d %s %d ", ret,
- (int)request.size(), defaultUrl.string(), (int)keyRequestType);
-
- return ret;
-}
-
-status_t MediaPlayer::provideKeyResponse(Vector<uint8_t>& releaseKeySetId,
- Vector<uint8_t>& response, Vector<uint8_t>& keySetId)
-{
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- // Not enforcing a particular state beyond the checks enforced by the Java layer
- // Key exchange can happen after the start.
- status_t ret = mPlayer->provideKeyResponse(releaseKeySetId, response, keySetId);
- ALOGV("provideKeyResponse: ret=%d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer::restoreKeys(Vector<uint8_t> const& keySetId)
-{
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- // Not enforcing a particular state beyond the checks enforced by the Java layer
- // Key exchange can happen after the start.
- status_t ret = mPlayer->restoreKeys(keySetId);
- ALOGV("restoreKeys: ret=%d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer::getDrmPropertyString(String8 const& name, String8& value)
-{
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- // Not enforcing a particular state beyond the checks enforced by the Java layer
- status_t ret = mPlayer->getDrmPropertyString(name, value);
- ALOGV("getDrmPropertyString: ret=%d", ret);
-
- return ret;
-}
-
-status_t MediaPlayer::setDrmPropertyString(String8 const& name, String8 const& value)
-{
- Mutex::Autolock _l(mLock);
- if (mPlayer == NULL) {
- return NO_INIT;
- }
-
- // Not enforcing a particular state beyond the checks enforced by the Java layer
- status_t ret = mPlayer->setDrmPropertyString(name, value);
- ALOGV("setDrmPropertyString: ret=%d", ret);
-
- return ret;
+ return status;
}
} // namespace android
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 1786e6b..64627fb 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -25,8 +25,9 @@
liblog \
libdl \
libgui \
- libmedia \
libaudioclient \
+ libmedia \
+ libmediadrm \
libmediautils \
libmemunreachable \
libstagefright \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index cdae456..62e5ec8 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1428,6 +1428,32 @@
}
}
+// Modular DRM
+status_t MediaPlayerService::Client::prepareDrm(const uint8_t uuid[16],
+ const Vector<uint8_t>& drmSessionId)
+{
+ ALOGV("[%d] prepareDrm", mConnId);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+
+ status_t ret = p->prepareDrm(uuid, drmSessionId);
+ ALOGV("prepareDrm ret: %d", ret);
+
+ return ret;
+}
+
+status_t MediaPlayerService::Client::releaseDrm()
+{
+ ALOGV("[%d] releaseDrm", mConnId);
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+
+ status_t ret = p->releaseDrm();
+ ALOGV("releaseDrm ret: %d", ret);
+
+ return ret;
+}
+
#if CALLBACK_ANTAGONIZER
const int Antagonizer::interval = 10000; // 10 msecs
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index cbaf21c..f5a540b 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -357,32 +357,9 @@
virtual status_t dump(int fd, const Vector<String16>& args);
audio_session_t getAudioSessionId() { return mAudioSessionId; }
- // ModDrm
- virtual status_t prepareDrm(const uint8_t /*uuid*/[16], const int /*mode*/)
- { return INVALID_OPERATION; }
- virtual status_t releaseDrm()
- { return INVALID_OPERATION; }
- virtual status_t getKeyRequest(Vector<uint8_t> const& /*scope*/,
- String8 const& /*mimeType*/,
- DrmPlugin::KeyType /*keyType*/,
- KeyedVector<String8, String8>& /*optionalParameters*/,
- Vector<uint8_t>& /*request*/,
- String8& /*defaultUrl*/,
- DrmPlugin::KeyRequestType& /*keyRequestType*/)
- { return INVALID_OPERATION; }
- virtual status_t provideKeyResponse(Vector<uint8_t>& /*releaseKeySetId*/,
- Vector<uint8_t>& /*response*/,
- Vector<uint8_t>& /*keySetId*/)
- { return INVALID_OPERATION; }
- virtual status_t restoreKeys(Vector<uint8_t> const& /*keySetId*/)
- { return INVALID_OPERATION; }
- virtual status_t getDrmPropertyString(String8 const& /*name*/,
- String8& /*value*/)
- { return INVALID_OPERATION; }
- virtual status_t setDrmPropertyString(String8 const& /*name*/,
- String8 const& /*value*/)
- { return INVALID_OPERATION; }
-
+ // Modular DRM
+ virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t>& drmSessionId);
+ virtual status_t releaseDrm();
private:
class ServiceDeathNotifier: public IBinder::DeathRecipient
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index a0e633c..8686560 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -10,6 +10,7 @@
NuPlayerDecoderBase.cpp \
NuPlayerDecoderPassThrough.cpp \
NuPlayerDriver.cpp \
+ NuPlayerDrm.cpp \
NuPlayerRenderer.cpp \
NuPlayerStreamListener.cpp \
RTSPSource.cpp \
@@ -32,7 +33,10 @@
LOCAL_CFLAGS += -DENABLE_STAGEFRIGHT_EXPERIMENTS
endif
-LOCAL_SHARED_LIBRARIES := libmedia
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libmediadrm \
LOCAL_MODULE:= libstagefright_nuplayer
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 91a2b7b..7986856 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "GenericSource"
#include "GenericSource.h"
+#include "NuPlayerDrm.h"
#include "AnotherPacketSource.h"
@@ -63,14 +64,17 @@
mUIDValid(uidValid),
mUID(uid),
mFd(-1),
- mDrmManagerClient(NULL),
mBitrate(-1ll),
mPendingReadBufferTypes(0) {
+ ALOGV("GenericSource");
+
mBufferingMonitor = new BufferingMonitor(notify);
resetDataSource();
}
void NuPlayer::GenericSource::resetDataSource() {
+ ALOGV("resetDataSource");
+
mHTTPService.clear();
mHttpSource.clear();
mUri.clear();
@@ -81,9 +85,6 @@
}
mOffset = 0;
mLength = 0;
- setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
- mDecryptHandle = NULL;
- mDrmManagerClient = NULL;
mStarted = false;
mStopRead = true;
@@ -93,12 +94,18 @@
mBufferingMonitorLooper = NULL;
}
mBufferingMonitor->stop();
+
+ mIsDrmProtected = false;
+ mIsSecure = false;
+ mMimes.clear();
}
status_t NuPlayer::GenericSource::setDataSource(
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers) {
+ ALOGV("setDataSource url: %s", url);
+
resetDataSource();
mHTTPService = httpService;
@@ -115,6 +122,8 @@
status_t NuPlayer::GenericSource::setDataSource(
int fd, int64_t offset, int64_t length) {
+ ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);
+
resetDataSource();
mFd = dup(fd);
@@ -127,6 +136,8 @@
}
status_t NuPlayer::GenericSource::setDataSource(const sp<DataSource>& source) {
+ ALOGV("setDataSource (source: %p)", source.get());
+
resetDataSource();
mDataSource = source;
return OK;
@@ -161,6 +172,8 @@
return UNKNOWN_ERROR;
}
+ mMimes.clear();
+
for (size_t i = 0; i < numtracks; ++i) {
sp<IMediaSource> track = extractor->getTrack(i);
if (track == NULL) {
@@ -176,6 +189,8 @@
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
+ ALOGV("initFromDataSource track[%d]: %s", i, mime);
+
// Do the string compare immediately with "mime",
// we can't assume "mime" would stay valid after another
// extractor operation, some extractors might modify meta
@@ -192,6 +207,8 @@
} else {
mAudioIsVorbis = false;
}
+
+ mMimes.add(String8(mime));
}
} else if (!strncasecmp(mime, "video/", 6)) {
if (mVideoTrack.mSource == NULL) {
@@ -200,15 +217,8 @@
mVideoTrack.mPackets =
new AnotherPacketSource(mVideoTrack.mSource->getFormat());
- // check if the source requires secure buffers
- int32_t secure;
- if (meta->findInt32(kKeyRequiresSecureBuffers, &secure)
- && secure) {
- mIsSecure = true;
- if (mUIDValid) {
- extractor->setUID(mUID);
- }
- }
+ // video always at the beginning
+ mMimes.insertAt(String8(mime), 0);
}
}
@@ -228,11 +238,17 @@
}
}
+ ALOGV("initFromDataSource mSources.size(): %zu mIsSecure: %d mime[0]: %s", mSources.size(),
+ mIsSecure, (mMimes.isEmpty() ? "NONE" : mMimes[0].string()));
+
if (mSources.size() == 0) {
ALOGE("b/23705695");
return UNKNOWN_ERROR;
}
+ // Modular DRM: The return value doesn't affect source initialization.
+ (void)checkDrmInfo();
+
mBitrate = totalBitrate;
return OK;
@@ -296,6 +312,7 @@
}
NuPlayer::GenericSource::~GenericSource() {
+ ALOGV("~GenericSource");
if (mLooper != NULL) {
mLooper->unregisterHandler(id());
mLooper->stop();
@@ -304,6 +321,8 @@
}
void NuPlayer::GenericSource::prepareAsync() {
+ ALOGV("prepareAsync: (looper: %d)", (mLooper != NULL));
+
if (mLooper == NULL) {
mLooper = new ALooper;
mLooper->setName("generic");
@@ -317,6 +336,8 @@
}
void NuPlayer::GenericSource::onPrepareAsync() {
+ ALOGV("onPrepareAsync: mDataSource: %d", (mDataSource != NULL));
+
// delayed data source creation
if (mDataSource == NULL) {
// set to false first, if the extractor
@@ -380,35 +401,21 @@
}
notifyFlagsChanged(
- (mIsSecure ? FLAG_SECURE : 0)
- | (mDecryptHandle != NULL ? FLAG_PROTECTED : 0)
- | FLAG_CAN_PAUSE
- | FLAG_CAN_SEEK_BACKWARD
- | FLAG_CAN_SEEK_FORWARD
- | FLAG_CAN_SEEK);
+ // FLAG_SECURE will be known if/when prepareDrm is called by the app
+ // FLAG_PROTECTED will be known if/when prepareDrm is called by the app
+ FLAG_CAN_PAUSE |
+ FLAG_CAN_SEEK_BACKWARD |
+ FLAG_CAN_SEEK_FORWARD |
+ FLAG_CAN_SEEK);
- if (mIsSecure) {
- // secure decoders must be instantiated before starting widevine source
- //
- // TODO: mIsSecure and FLAG_SECURE may be obsolete, revisit after
- // removing widevine
- sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);
- notifyInstantiateSecureDecoders(reply);
- } else {
- finishPrepareAsync();
- }
-}
-
-void NuPlayer::GenericSource::onSecureDecodersInstantiated(status_t err) {
- if (err != OK) {
- ALOGE("Failed to instantiate secure decoders!");
- notifyPreparedAndCleanup(err);
- return;
- }
finishPrepareAsync();
+
+ ALOGV("onPrepareAsync: Done");
}
void NuPlayer::GenericSource::finishPrepareAsync() {
+ ALOGV("finishPrepareAsync");
+
status_t err = startSources();
if (err != OK) {
ALOGE("Failed to init start data source!");
@@ -443,8 +450,6 @@
{
Mutex::Autolock _l(mDisconnectLock);
mDataSource.clear();
- mDecryptHandle = NULL;
- mDrmManagerClient = NULL;
mCachedSource.clear();
mHttpSource.clear();
}
@@ -468,27 +473,20 @@
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
- setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
(new AMessage(kWhatStart, this))->post();
}
void NuPlayer::GenericSource::stop() {
- // nothing to do, just account for DRM playback status
- setDrmPlaybackStatusIfNeeded(Playback::STOP, 0);
mStarted = false;
}
void NuPlayer::GenericSource::pause() {
- // nothing to do, just account for DRM playback status
- setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0);
mStarted = false;
}
void NuPlayer::GenericSource::resume() {
- // nothing to do, just account for DRM playback status
- setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
(new AMessage(kWhatResume, this))->post();
@@ -512,14 +510,6 @@
}
}
-void NuPlayer::GenericSource::setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position) {
- if (mDecryptHandle != NULL) {
- mDrmManagerClient->setPlaybackStatus(mDecryptHandle, playbackStatus, position);
- }
- mSubtitleTrack.mPackets = new AnotherPacketSource(NULL);
- mTimedTextTrack.mPackets = new AnotherPacketSource(NULL);
-}
-
status_t NuPlayer::GenericSource::feedMoreTSData() {
return OK;
}
@@ -653,11 +643,14 @@
break;
}
- case kWhatSecureDecodersInstantiated:
+ case kWhatPrepareDrm:
{
- int32_t err;
- CHECK(msg->findInt32("err", &err));
- onSecureDecodersInstantiated(err);
+ status_t status = onPrepareDrm(msg);
+ sp<AMessage> response = new AMessage;
+ response->setInt32("status", status);
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
break;
}
@@ -1194,11 +1187,6 @@
mAudioLastDequeueTimeUs = seekTimeUs;
}
- setDrmPlaybackStatusIfNeeded(Playback::START, seekTimeUs / 1000);
- if (!mStarted) {
- setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0);
- }
-
// If currently buffering, post kWhatBufferingEnd first, so that
// NuPlayer resumes. Otherwise, if cache hits high watermark
// before new polling happens, no one will resume the playback.
@@ -1219,11 +1207,26 @@
}
sp<ABuffer> ab;
- if (mIsSecure && !audio) {
+
+ if (mIsDrmProtected) {
+ // Modular DRM
+ // Enabled for both video/audio so 1) media buffer is reused without extra copying
+ // 2) meta data can be retrieved in onInputBufferFetched for calling queueSecureInputBuffer.
+
// data is already provided in the buffer
ab = new ABuffer(NULL, mb->range_length());
mb->add_ref();
ab->setMediaBufferBase(mb);
+
+ // Modular DRM: Required b/c of the above add_ref.
+ // If ref>0, there must be an observer, or it'll crash at release().
+ // TODO: MediaBuffer might need to be revised to ease such need.
+ mb->setObserver(this);
+ // setMediaBufferBase() interestingly doesn't increment the ref count on its own.
+ // Extra increment (since we want to keep mb alive and attached to ab beyond this function
+ // call. This is to counter the effect of mb->release() towards the end.
+ mb->add_ref();
+
} else {
ab = new ABuffer(outLength);
memcpy(ab->data(),
@@ -1828,4 +1831,128 @@
}
}
+// Modular DRM
+status_t NuPlayer::GenericSource::prepareDrm(
+ const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *crypto)
+{
+ ALOGV("prepareDrm");
+
+ sp<AMessage> msg = new AMessage(kWhatPrepareDrm, this);
+ // synchronous call so just passing the address but with local copies of "const" args
+ uint8_t UUID[16];
+ memcpy(UUID, uuid, sizeof(UUID));
+ Vector<uint8_t> sessionId = drmSessionId;
+ msg->setPointer("uuid", (void*)UUID);
+ msg->setPointer("drmSessionId", (void*)&sessionId);
+ msg->setPointer("crypto", (void*)crypto);
+
+ sp<AMessage> response;
+ status_t status = msg->postAndAwaitResponse(&response);
+
+ if (status == OK && response != NULL) {
+ CHECK(response->findInt32("status", &status));
+ ALOGV_IF(status == OK, "prepareDrm: mCrypto: %p (%d)", crypto->get(),
+ (*crypto != NULL ? (*crypto)->getStrongCount() : 0));
+ ALOGD("prepareDrm ret: %d ", status);
+ } else {
+ ALOGE("prepareDrm err: %d", status);
+ }
+
+ return status;
+}
+
+status_t NuPlayer::GenericSource::onPrepareDrm(const sp<AMessage> &msg)
+{
+ ALOGV("onPrepareDrm ");
+
+ mIsDrmProtected = false;
+ mIsSecure = false;
+
+ uint8_t *uuid;
+ Vector<uint8_t> *drmSessionId;
+ sp<ICrypto> *outCrypto;
+ CHECK(msg->findPointer("uuid", (void**)&uuid));
+ CHECK(msg->findPointer("drmSessionId", (void**)&drmSessionId));
+ CHECK(msg->findPointer("crypto", (void**)&outCrypto));
+
+ status_t status = OK;
+ sp<ICrypto> crypto = NuPlayerDrm::createCryptoAndPlugin(uuid, *drmSessionId, status);
+ if (crypto == NULL) {
+ ALOGE("onPrepareDrm: createCrypto failed. status: %d", status);
+ return status;
+ }
+ ALOGV("onPrepareDrm: createCryptoAndPlugin succeeded for uuid: %s",
+ DrmUUID::toHexString(uuid).string());
+
+ *outCrypto = crypto;
+ // as long a there is an active crypto
+ mIsDrmProtected = true;
+
+ if (mMimes.size() == 0) {
+ status = UNKNOWN_ERROR;
+ ALOGE("onPrepareDrm: Unexpected. Must have at least one track. status: %d", status);
+ return status;
+ }
+
+ // first mime in this list is either the video track, or the first audio track
+ const char *mime = mMimes[0].string();
+ mIsSecure = crypto->requiresSecureDecoderComponent(mime);
+ ALOGV("onPrepareDrm: requiresSecureDecoderComponent mime: %s isSecure: %d",
+ mime, mIsSecure);
+
+ // Checking the member flags while in the looper to send out the notification.
+ // The legacy mDecryptHandle!=NULL check (for FLAG_PROTECTED) is equivalent to mIsDrmProtected.
+ notifyFlagsChanged(
+ (mIsSecure ? FLAG_SECURE : 0) |
+ (mIsDrmProtected ? FLAG_PROTECTED : 0) |
+ FLAG_CAN_PAUSE |
+ FLAG_CAN_SEEK_BACKWARD |
+ FLAG_CAN_SEEK_FORWARD |
+ FLAG_CAN_SEEK);
+
+ return status;
+}
+
+status_t NuPlayer::GenericSource::checkDrmInfo()
+{
+ if (mFileMeta == NULL) {
+ ALOGE("checkDrmInfo: No metadata");
+ return OK; // letting the caller responds accordingly
+ }
+
+ uint32_t type;
+ const void *pssh;
+ size_t psshsize;
+
+ if (!mFileMeta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
+ ALOGE("checkDrmInfo: No PSSH");
+ return OK; // source without DRM info
+ }
+
+ Parcel parcel;
+ NuPlayerDrm::retrieveDrmInfo(pssh, psshsize, mMimes, &parcel);
+ ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH size: %d Parcel size: %d objects#: %d",
+ psshsize, (int)parcel.dataSize(), (int)parcel.objectsCount());
+
+ if (parcel.dataSize() == 0) {
+ ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+ return UNKNOWN_ERROR;
+ }
+
+ // Can't pass parcel as a message to the player. Converting Parcel->ABuffer to pass it
+ // to the Player's onSourceNotify then back to Parcel for calling driver's notifyListener.
+ sp<ABuffer> drmInfoBuffer = ABuffer::CreateAsCopy(parcel.data(), parcel.dataSize());
+ notifyDrmInfo(drmInfoBuffer);
+
+ return OK;
+}
+
+void NuPlayer::GenericSource::signalBufferReturned(MediaBuffer *buffer)
+{
+ //ALOGV("signalBufferReturned %p refCount: %d", buffer, buffer->localRefcount());
+
+ buffer->setObserver(NULL);
+ buffer->release(); // this leads to delete since that there is no observor
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index e1949f3..64f21a6 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -28,7 +28,6 @@
namespace android {
class DecryptHandle;
-class DrmManagerClient;
struct AnotherPacketSource;
struct ARTSPController;
class DataSource;
@@ -38,7 +37,9 @@
class MediaBuffer;
struct NuCachedSource2;
-struct NuPlayer::GenericSource : public NuPlayer::Source {
+struct NuPlayer::GenericSource : public NuPlayer::Source,
+ public MediaBufferObserver // Modular DRM
+{
GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid);
status_t setDataSource(
@@ -84,6 +85,13 @@
virtual void setOffloadAudio(bool offload);
+ // Modular DRM
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+ virtual status_t prepareDrm(
+ const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *crypto);
+
+
protected:
virtual ~GenericSource();
@@ -109,6 +117,8 @@
kWhatStart,
kWhatResume,
kWhatSecureDecodersInstantiated,
+ // Modular DRM
+ kWhatPrepareDrm,
};
struct Track {
@@ -224,8 +234,6 @@
sp<NuCachedSource2> mCachedSource;
sp<DataSource> mHttpSource;
sp<MetaData> mFileMeta;
- DrmManagerClient *mDrmManagerClient;
- sp<DecryptHandle> mDecryptHandle;
bool mStarted;
bool mStopRead;
int64_t mBitrate;
@@ -243,7 +251,6 @@
status_t initFromDataSource();
int64_t getLastReadPosition();
- void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position);
void notifyPreparedAndCleanup(status_t err);
void onSecureDecodersInstantiated(status_t err);
@@ -299,6 +306,13 @@
void queueDiscontinuityIfNeeded(
bool seeking, bool formatChange, media_track_type trackType, Track *track);
+ // Modular DRM
+ bool mIsDrmProtected;
+ Vector<String8> mMimes;
+
+ status_t checkDrmInfo();
+ status_t onPrepareDrm(const sp<AMessage> &msg);
+
DISALLOW_EVIL_CONSTRUCTORS(GenericSource);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 6593fcd..e800d13 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -254,16 +254,21 @@
sp<Source> source;
if (IsHTTPLiveURL(url)) {
source = new HTTPLiveSource(notify, httpService, url, headers);
+ ALOGV("setDataSourceAsync HTTPLiveSource %s", url);
} else if (!strncasecmp(url, "rtsp://", 7)) {
source = new RTSPSource(
notify, httpService, url, headers, mUIDValid, mUID);
+ ALOGV("setDataSourceAsync RTSPSource %s", url);
} else if ((!strncasecmp(url, "http://", 7)
|| !strncasecmp(url, "https://", 8))
&& ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
|| strstr(url, ".sdp?"))) {
source = new RTSPSource(
notify, httpService, url, headers, mUIDValid, mUID, true);
+ ALOGV("setDataSourceAsync RTSPSource http/https/.sdp %s", url);
} else {
+ ALOGV("setDataSourceAsync GenericSource %s", url);
+
sp<GenericSource> genericSource =
new GenericSource(notify, mUIDValid, mUID);
@@ -287,6 +292,9 @@
sp<GenericSource> source =
new GenericSource(notify, mUIDValid, mUID);
+ ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
+ fd, (long long)offset, (long long)length, source.get());
+
status_t err = source->setDataSource(fd, offset, length);
if (err != OK) {
@@ -340,6 +348,8 @@
}
void NuPlayer::prepareAsync() {
+ ALOGV("prepareAsync");
+
(new AMessage(kWhatPrepare, this))->post();
}
@@ -577,6 +587,8 @@
case kWhatPrepare:
{
+ ALOGV("onMessageReceived kWhatPrepare");
+
mSource->prepareAsync();
break;
}
@@ -1133,8 +1145,9 @@
case SHUTTING_DOWN_DECODER:
break; // Wait for shutdown to complete.
case FLUSHED:
+ // Both secure audio/video now. Legacy Widevine did it for secure video.
// Widevine source reads must stop before releasing the video decoder.
- if (!audio && mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
+ if (mSource != NULL && mIsDrmProtected) {
mSource->stop();
mSourceStarted = false;
}
@@ -1330,6 +1343,30 @@
break;
}
+ case kWhatPrepareDrm:
+ {
+ status_t status = onPrepareDrm(msg);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("status", status);
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatReleaseDrm:
+ {
+ status_t status = onReleaseDrm();
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("status", status);
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+ break;
+ }
+
default:
TRESPASS();
break;
@@ -1391,6 +1428,9 @@
}
void NuPlayer::onStart(int64_t startPositionUs, MediaPlayerSeekMode mode) {
+ ALOGV("onStart: mCrypto: %p (%d)", mCrypto.get(),
+ (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
if (!mSourceStarted) {
mSourceStarted = true;
mSource->start();
@@ -1435,6 +1475,13 @@
mOffloadAudio =
canOffloadStream(audioMeta, hasVideo, mSource->isStreaming(), streamType)
&& (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
+
+ // Modular DRM: Disabling audio offload if the source is protected
+ if (mOffloadAudio && mIsDrmProtected) {
+ mOffloadAudio = false;
+ ALOGV("onStart: Disabling mOffloadAudio now that the source is protected.");
+ }
+
if (mOffloadAudio) {
flags |= Renderer::FLAG_OFFLOAD_AUDIO;
}
@@ -1527,12 +1574,11 @@
*state = SHUTTING_DOWN_DECODER;
ALOGV("initiating %s decoder shutdown", audio ? "audio" : "video");
- if (!audio) {
- // Widevine source reads must stop before releasing the video decoder.
- if (mSource != NULL && mSourceFlags & Source::FLAG_SECURE) {
- mSource->stop();
- mSourceStarted = false;
- }
+ // Both secure audio/video now. Legacy Widevine did it for secure video only.
+ // Widevine source reads must stop before releasing the video decoder.
+ if (mSource != NULL && mIsDrmProtected) {
+ mSource->stop();
+ mSourceStarted = false;
}
getDecoder(audio)->initiateShutdown();
break;
@@ -1650,9 +1696,16 @@
sp<AMessage> videoFormat = mSource->getFormat(false /* audio */);
audio_stream_type_t streamType = mAudioSink->getAudioStreamType();
const bool hasVideo = (videoFormat != NULL);
- const bool canOffload = canOffloadStream(
+ bool canOffload = canOffloadStream(
audioMeta, hasVideo, mSource->isStreaming(), streamType)
&& (mPlaybackSettings.mSpeed == 1.f && mPlaybackSettings.mPitch == 1.f);
+
+ // Modular DRM: Disabling audio offload if the source is protected
+ if (canOffload && mIsDrmProtected) {
+ canOffload = false;
+ ALOGV("determineAudioModeChange: Disabling mOffloadAudio b/c the source is protected.");
+ }
+
if (canOffload) {
if (!mOffloadAudio) {
mRenderer->signalEnableOffloadAudio();
@@ -1725,10 +1778,12 @@
const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL);
format->setInt32("has-video", hasVideo);
*decoder = new DecoderPassThrough(notify, mSource, mRenderer);
+ ALOGV("instantiateDecoder audio DecoderPassThrough hasVideo: %d", hasVideo);
} else {
mSource->setOffloadAudio(false /* offload */);
*decoder = new Decoder(notify, mSource, mPID, mUID, mRenderer);
+ ALOGV("instantiateDecoder audio Decoder");
}
} else {
sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
@@ -1748,6 +1803,15 @@
}
}
(*decoder)->init();
+
+ // Modular DRM
+ if (mIsDrmProtected) {
+ format->setPointer("crypto", mCrypto.get());
+ ALOGV("instantiateDecoder: mCrypto: %p (%d) isSecure: %d", mCrypto.get(),
+ (mCrypto != NULL ? mCrypto->getStrongCount() : 0),
+ (mSourceFlags & Source::FLAG_SECURE) != 0);
+ }
+
(*decoder)->configure(format);
if (!audio) {
@@ -2142,6 +2206,16 @@
mPrepared = false;
mResetting = false;
mSourceStarted = false;
+
+ // Modular DRM
+ if (mCrypto != NULL) {
+ // decoders will be flushed before this so their mCrypto would go away on their own
+ // TODO change to ALOGV
+ ALOGD("performReset mCrypto: %p (%d)", mCrypto.get(),
+ (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+ mCrypto.clear();
+ }
+ mIsDrmProtected = false;
}
void NuPlayer::performScanSources() {
@@ -2236,6 +2310,7 @@
case Source::kWhatPrepared:
{
+ ALOGV("NuPlayer::onSourceNotify Source::kWhatPrepared source: %p", mSource.get());
if (mSource == NULL) {
// This is a stale notification from a source that was
// asynchronously preparing when the client called reset().
@@ -2270,6 +2345,22 @@
break;
}
+ // Modular DRM
+ case Source::kWhatDrmInfo:
+ {
+ Parcel parcel;
+ sp<ABuffer> drmInfo;
+ CHECK(msg->findBuffer("drmInfo", &drmInfo));
+ parcel.setData(drmInfo->data(), drmInfo->size());
+
+ ALOGV("onSourceNotify() kWhatDrmInfo MEDIA_DRM_INFO drmInfo: %p parcel size: %zu",
+ drmInfo.get(), parcel.dataSize());
+
+ notifyListener(MEDIA_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+
+ break;
+ }
+
case Source::kWhatFlagsChanged:
{
uint32_t flags;
@@ -2277,6 +2368,19 @@
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
+
+ ALOGV("onSourceNotify() kWhatFlagsChanged FLAG_CAN_PAUSE: %d "
+ "FLAG_CAN_SEEK_BACKWARD: %d \n\t\t\t\t FLAG_CAN_SEEK_FORWARD: %d "
+ "FLAG_CAN_SEEK: %d FLAG_DYNAMIC_DURATION: %d \n"
+ "\t\t\t\t FLAG_SECURE: %d FLAG_PROTECTED: %d",
+ (flags & Source::FLAG_CAN_PAUSE) != 0,
+ (flags & Source::FLAG_CAN_SEEK_BACKWARD) != 0,
+ (flags & Source::FLAG_CAN_SEEK_FORWARD) != 0,
+ (flags & Source::FLAG_CAN_SEEK) != 0,
+ (flags & Source::FLAG_DYNAMIC_DURATION) != 0,
+ (flags & Source::FLAG_SECURE) != 0,
+ (flags & Source::FLAG_PROTECTED) != 0);
+
if ((flags & NuPlayer::Source::FLAG_CAN_SEEK) == 0) {
driver->notifyListener(
MEDIA_INFO, MEDIA_INFO_NOT_SEEKABLE, 0);
@@ -2527,6 +2631,132 @@
notifyListener(MEDIA_TIMED_TEXT, 0, 0);
}
}
+
+// Modular DRM begin
+status_t NuPlayer::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
+{
+ ALOGV("prepareDrm ");
+
+ // Passing to the looper anyway; called in a pre-config prepared state so no race on mCrypto
+ sp<AMessage> msg = new AMessage(kWhatPrepareDrm, this);
+ // synchronous call so just passing the address but with local copies of "const" args
+ uint8_t UUID[16];
+ memcpy(UUID, uuid, sizeof(UUID));
+ Vector<uint8_t> sessionId = drmSessionId;
+ msg->setPointer("uuid", (void*)UUID);
+ msg->setPointer("drmSessionId", (void*)&sessionId);
+
+ sp<AMessage> response;
+ status_t status = msg->postAndAwaitResponse(&response);
+
+ if (status == OK && response != NULL) {
+ CHECK(response->findInt32("status", &status));
+ ALOGV("prepareDrm ret: %d ", status);
+ } else {
+ ALOGE("prepareDrm err: %d", status);
+ }
+
+ return status;
+}
+
+status_t NuPlayer::releaseDrm()
+{
+ ALOGV("releaseDrm ");
+
+ sp<AMessage> msg = new AMessage(kWhatReleaseDrm, this);
+
+ sp<AMessage> response;
+ status_t status = msg->postAndAwaitResponse(&response);
+
+ if (status == OK && response != NULL) {
+ CHECK(response->findInt32("status", &status));
+ ALOGV("releaseDrm ret: %d ", status);
+ } else {
+ ALOGE("releaseDrm err: %d", status);
+ }
+
+ return status;
+}
+
+status_t NuPlayer::onPrepareDrm(const sp<AMessage> &msg)
+{
+ // TODO change to ALOGV
+ ALOGD("onPrepareDrm ");
+
+ status_t status = INVALID_OPERATION;
+ if (mSource == NULL) {
+ ALOGE("onPrepareDrm: No source. onPrepareDrm failed with %d.", status);
+ return status;
+ }
+
+ uint8_t *uuid;
+ Vector<uint8_t> *drmSessionId;
+ CHECK(msg->findPointer("uuid", (void**)&uuid));
+ CHECK(msg->findPointer("drmSessionId", (void**)&drmSessionId));
+
+ status = OK;
+ sp<ICrypto> crypto = NULL;
+
+ status = mSource->prepareDrm(uuid, *drmSessionId, &crypto);
+ if (crypto == NULL) {
+ ALOGE("onPrepareDrm: mSource->prepareDrm failed. status: %d", status);
+ return status;
+ }
+ ALOGV("onPrepareDrm: mSource->prepareDrm succeeded");
+
+ if (mCrypto != NULL) {
+ ALOGE("onPrepareDrm: Unexpected. Already having mCrypto: %p (%d)",
+ mCrypto.get(), mCrypto->getStrongCount());
+ mCrypto.clear();
+ }
+
+ mCrypto = crypto;
+ mIsDrmProtected = true;
+ // TODO change to ALOGV
+ ALOGD("onPrepareDrm: mCrypto: %p (%d)", mCrypto.get(),
+ (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
+ return status;
+}
+
+status_t NuPlayer::onReleaseDrm()
+{
+ // TODO change to ALOGV
+ ALOGD("onReleaseDrm ");
+
+ mIsDrmProtected = true;
+
+ status_t status;
+ if (mCrypto != NULL) {
+ status=OK;
+ // first making sure the codecs have released their crypto reference
+ const sp<DecoderBase> &videoDecoder = getDecoder(false/*audio*/);
+ if (videoDecoder != NULL) {
+ status = videoDecoder->releaseCrypto();
+ ALOGV("onReleaseDrm: video decoder ret: %d", status);
+ }
+
+ const sp<DecoderBase> &audioDecoder = getDecoder(true/*audio*/);
+ if (audioDecoder != NULL) {
+ status_t status_audio = audioDecoder->releaseCrypto();
+ if (status == OK) { // otherwise, returning the first error
+ status = status_audio;
+ }
+ ALOGV("onReleaseDrm: audio decoder ret: %d", status_audio);
+ }
+
+ // TODO change to ALOGV
+ ALOGD("onReleaseDrm: mCrypto: %p (%d)", mCrypto.get(),
+ (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+ mCrypto.clear();
+ } else { // mCrypto == NULL
+ ALOGE("onReleaseDrm: Unexpected. There is no crypto.");
+ status = INVALID_OPERATION;
+ }
+
+ return status;
+}
+// Modular DRM end
////////////////////////////////////////////////////////////////////////////////
sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
@@ -2559,12 +2789,24 @@
}
void NuPlayer::Source::notifyPrepared(status_t err) {
+ ALOGV("Source::notifyPrepared %d", err);
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatPrepared);
notify->setInt32("err", err);
notify->post();
}
+void NuPlayer::Source::notifyDrmInfo(const sp<ABuffer> &drmInfoBuffer)
+{
+ ALOGV("Source::notifyDrmInfo");
+
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatDrmInfo);
+ notify->setBuffer("drmInfo", drmInfoBuffer);
+
+ notify->post();
+}
+
void NuPlayer::Source::notifyInstantiateSecureDecoders(const sp<AMessage> &reply) {
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatInstantiateSecureDecoders);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index cc8c97a..d3cb7c1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -19,6 +19,7 @@
#define NU_PLAYER_H_
#include <media/AudioResamplerPublic.h>
+#include <media/ICrypto.h>
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/foundation/AHandler.h>
@@ -88,6 +89,10 @@
sp<MetaData> getFileMeta();
float getFrameRate();
+ // Modular DRM
+ status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
+ status_t releaseDrm();
+
protected:
virtual ~NuPlayer();
@@ -142,6 +147,8 @@
kWhatSelectTrack = 'selT',
kWhatGetDefaultBufferingSettings = 'gDBS',
kWhatSetBufferingSettings = 'sBuS',
+ kWhatPrepareDrm = 'pDrm',
+ kWhatReleaseDrm = 'rDrm',
};
wp<NuPlayerDriver> mDriver;
@@ -223,6 +230,10 @@
// Pause state as requested by source (internally) due to buffering
bool mPausedForBuffering;
+ // Modular DRM
+ sp<ICrypto> mCrypto;
+ bool mIsDrmProtected;
+
inline const sp<DecoderBase> &getDecoder(bool audio) {
return audio ? mAudioDecoder : mVideoDecoder;
}
@@ -294,6 +305,9 @@
void writeTrackInfo(Parcel* reply, const sp<AMessage>& format) const;
+ status_t onPrepareDrm(const sp<AMessage> &msg);
+ status_t onReleaseDrm();
+
DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 0a0a8aa..5689e95 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -23,6 +23,7 @@
#include "NuPlayerCCDecoder.h"
#include "NuPlayerDecoder.h"
+#include "NuPlayerDrm.h"
#include "NuPlayerRenderer.h"
#include "NuPlayerSource.h"
@@ -255,6 +256,13 @@
break;
}
+ case kWhatDrmReleaseCrypto:
+ {
+ ALOGV("kWhatDrmReleaseCrypto");
+ onReleaseCrypto(msg);
+ break;
+ }
+
default:
DecoderBase::onMessageReceived(msg);
break;
@@ -312,8 +320,19 @@
// any error signaling will occur.
ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err);
}
+
+ // Modular DRM
+ void *pCrypto;
+ if (!format->findPointer("crypto", &pCrypto)) {
+ pCrypto = NULL;
+ }
+ sp<ICrypto> crypto = (ICrypto*)pCrypto;
+ ALOGV("onConfigure mCrypto: %p (%d) mIsSecure: %d",
+ crypto.get(), (crypto != NULL ? crypto->getStrongCount() : 0), mIsSecure);
+
err = mCodec->configure(
- format, mSurface, NULL /* crypto */, 0 /* flags */);
+ format, mSurface, crypto, 0 /* flags */);
+
if (err != OK) {
ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
mCodec->release();
@@ -559,6 +578,43 @@
notify->post();
}
+status_t NuPlayer::Decoder::releaseCrypto()
+{
+ ALOGV("releaseCrypto");
+
+ sp<AMessage> msg = new AMessage(kWhatDrmReleaseCrypto, this);
+
+ sp<AMessage> response;
+ status_t status = msg->postAndAwaitResponse(&response);
+ if (status == OK && response != NULL) {
+ CHECK(response->findInt32("status", &status));
+ ALOGV("releaseCrypto ret: %d ", status);
+ } else {
+ ALOGE("releaseCrypto err: %d", status);
+ }
+
+ return status;
+}
+
+void NuPlayer::Decoder::onReleaseCrypto(const sp<AMessage>& msg)
+{
+ status_t status = INVALID_OPERATION;
+ if (mCodec != NULL) {
+ status = mCodec->releaseCrypto();
+ } else {
+ // returning OK if the codec has been already released
+ status = OK;
+ ALOGE("onReleaseCrypto No mCodec. err: %d", status);
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("status", status);
+
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+}
+
bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
if (isDiscontinuityPending()) {
return false;
@@ -929,6 +985,10 @@
flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
}
+ // Modular DRM
+ MediaBuffer *mediaBuf = NULL;
+ NuPlayerDrm::CryptoInfo *cryptInfo = NULL;
+
// copy into codec buffer
if (needsCopy) {
if (buffer->size() > codecBuffer->capacity()) {
@@ -936,24 +996,68 @@
mDequeuedInputBuffers.push_back(bufferIx);
return false;
}
- codecBuffer->setRange(0, buffer->size());
- memcpy(codecBuffer->data(), buffer->data(), buffer->size());
- }
- status_t err = mCodec->queueInputBuffer(
- bufferIx,
- codecBuffer->offset(),
- codecBuffer->size(),
- timeUs,
- flags);
+ if (buffer->data() != NULL) {
+ codecBuffer->setRange(0, buffer->size());
+ memcpy(codecBuffer->data(), buffer->data(), buffer->size());
+ } else { // No buffer->data()
+ //Modular DRM
+ mediaBuf = (MediaBuffer*)buffer->getMediaBufferBase();
+ if (mediaBuf != NULL) {
+ codecBuffer->setRange(0, mediaBuf->size());
+ memcpy(codecBuffer->data(), mediaBuf->data(), mediaBuf->size());
+
+ sp<MetaData> meta_data = mediaBuf->meta_data();
+ cryptInfo = NuPlayerDrm::getSampleCryptoInfo(meta_data);
+
+ // since getMediaBuffer() has incremented the refCount
+ mediaBuf->release();
+ } else { // No mediaBuf
+ ALOGE("onInputBufferFetched: buffer->data()/mediaBuf are NULL for %p",
+ buffer.get());
+ handleError(UNKNOWN_ERROR);
+ return false;
+ }
+ } // buffer->data()
+ } // needsCopy
+
+ status_t err;
+ AString errorDetailMsg;
+ if (cryptInfo != NULL) {
+ err = mCodec->queueSecureInputBuffer(
+ bufferIx,
+ codecBuffer->offset(),
+ cryptInfo->subSamples,
+ cryptInfo->numSubSamples,
+ cryptInfo->key,
+ cryptInfo->iv,
+ cryptInfo->mode,
+ cryptInfo->pattern,
+ timeUs,
+ flags,
+ &errorDetailMsg);
+ // synchronous call so done with cryptInfo here
+ free(cryptInfo);
+ } else {
+ err = mCodec->queueInputBuffer(
+ bufferIx,
+ codecBuffer->offset(),
+ codecBuffer->size(),
+ timeUs,
+ flags,
+ &errorDetailMsg);
+ } // no cryptInfo
+
if (err != OK) {
- ALOGE("Failed to queue input buffer for %s (err=%d)",
- mComponentName.c_str(), err);
+ ALOGE("onInputBufferFetched: queue%sInputBuffer failed for %s (err=%d, %s)",
+ (cryptInfo != NULL ? "Secure" : ""),
+ mComponentName.c_str(), err, errorDetailMsg.c_str());
handleError(err);
} else {
mInputBufferIsDequeued.editItemAt(bufferIx) = false;
}
- }
+
+ } // buffer != NULL
return true;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 82db59c..de21379 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -39,6 +39,8 @@
// sets the output surface of video decoders.
virtual status_t setVideoSurface(const sp<Surface> &surface);
+ virtual status_t releaseCrypto();
+
protected:
virtual ~Decoder();
@@ -57,7 +59,8 @@
kWhatCodecNotify = 'cdcN',
kWhatRenderBuffer = 'rndr',
kWhatSetVideoSurface = 'sSur',
- kWhatAudioOutputFormatChanged = 'aofc'
+ kWhatAudioOutputFormatChanged = 'aofc',
+ kWhatDrmReleaseCrypto = 'rDrm',
};
enum {
@@ -135,6 +138,8 @@
void notifyResumeCompleteIfNecessary();
+ void onReleaseCrypto(const sp<AMessage>& msg);
+
DISALLOW_EVIL_CONSTRUCTORS(Decoder);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 6811903..dcdfcaf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -51,6 +51,10 @@
return mStats;
}
+ virtual status_t releaseCrypto() {
+ return INVALID_OPERATION;
+ }
+
enum {
kWhatInputDiscontinuity = 'inDi',
kWhatVideoSizeChanged = 'viSC',
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 0ddbd63..abea5bc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -884,8 +884,8 @@
void NuPlayerDriver::notifyListener_l(
int msg, int ext1, int ext2, const Parcel *in) {
- ALOGD("notifyListener_l(%p), (%d, %d, %d), loop setting(%d, %d)",
- this, msg, ext1, ext2, mAutoLoop, mLooping);
+ ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
+ this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
switch (msg) {
case MEDIA_PLAYBACK_COMPLETE:
{
@@ -943,6 +943,8 @@
}
void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
+ ALOGV("notifyPrepareCompleted %d", err);
+
Mutex::Autolock autoLock(mLock);
if (mState != STATE_PREPARING) {
@@ -987,4 +989,33 @@
mPlayerFlags = flags;
}
+// Modular DRM
+status_t NuPlayerDriver::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
+{
+ ALOGV("prepareDrm(%p) state: %d", this, mState);
+
+ Mutex::Autolock autoLock(mLock);
+
+ // leaving the state verification for mediaplayer.cpp
+ status_t ret = mPlayer->prepareDrm(uuid, drmSessionId);
+
+ ALOGV("prepareDrm ret: %d", ret);
+
+ return ret;
+}
+
+status_t NuPlayerDriver::releaseDrm()
+{
+ ALOGV("releaseDrm(%p) state: %d", this, mState);
+
+ Mutex::Autolock autoLock(mLock);
+
+ // leaving the state verification for mediaplayer.cpp
+ status_t ret = mPlayer->releaseDrm();
+
+ ALOGV("releaseDrm ret: %d", ret);
+
+ return ret;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 5bfc539..972a348 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -87,6 +87,10 @@
void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
void notifyFlagsChanged(uint32_t flags);
+ // Modular DRM
+ virtual status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
+ virtual status_t releaseDrm();
+
protected:
virtual ~NuPlayerDriver();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
new file mode 100644
index 0000000..cb9a5f8
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -0,0 +1,366 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuPlayerDrm"
+
+#include "NuPlayerDrm.h"
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaDrmService.h>
+#include <utils/Log.h>
+
+
+namespace android {
+
+// static helpers - internal
+
+sp<IDrm> NuPlayerDrm::CreateDrm(status_t *pstatus)
+{
+ status_t &status = *pstatus;
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.drm"));
+ ALOGV("CreateDrm binder %p", (binder != NULL ? binder.get() : 0));
+
+ sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
+ if (service == NULL) {
+ ALOGE("CreateDrm failed at IMediaDrmService");
+ return NULL;
+ }
+
+ sp<IDrm> drm = service->makeDrm();
+ if (drm == NULL) {
+ ALOGE("CreateDrm failed at makeDrm");
+ return NULL;
+ }
+
+ // this is before plugin creation so NO_INIT is fine
+ status = drm->initCheck();
+ if (status != OK && status != NO_INIT) {
+ ALOGE("CreateDrm failed drm->initCheck(): %d", status);
+ return NULL;
+ }
+ return drm;
+}
+
+sp<ICrypto> NuPlayerDrm::createCrypto(status_t *pstatus)
+{
+ status_t &status = *pstatus;
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.drm"));
+
+ sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
+ if (service == NULL) {
+ status = UNKNOWN_ERROR;
+ ALOGE("CreateCrypto failed at IMediaDrmService");
+ return NULL;
+ }
+
+ sp<ICrypto> crypto = service->makeCrypto();
+ if (crypto == NULL) {
+ status = UNKNOWN_ERROR;
+ ALOGE("createCrypto failed");
+ return NULL;
+ }
+
+ // this is before plugin creation so NO_INIT is fine
+ status = crypto->initCheck();
+ if (status != OK && status != NO_INIT) {
+ ALOGE("createCrypto failed crypto->initCheck(): %d", status);
+ return NULL;
+ }
+
+ return crypto;
+}
+
+Vector<DrmUUID> NuPlayerDrm::parsePSSH(const void *pssh, size_t psshsize)
+{
+ Vector<DrmUUID> drmSchemes, empty;
+ const int DATALEN_SIZE = 4;
+
+ // the format of the buffer is 1 or more of:
+ // {
+ // 16 byte uuid
+ // 4 byte data length N
+ // N bytes of data
+ // }
+ // Determine the number of entries in the source data.
+ // Since we got the data from stagefright, we trust it is valid and properly formatted.
+
+ const uint8_t *data = (const uint8_t*)pssh;
+ size_t len = psshsize;
+ size_t numentries = 0;
+ while (len > 0) {
+ if (len < DrmUUID::UUID_SIZE) {
+ ALOGE("ParsePSSH: invalid PSSH data");
+ return empty;
+ }
+
+ const uint8_t *uuidPtr = data;
+
+ // skip uuid
+ data += DrmUUID::UUID_SIZE;
+ len -= DrmUUID::UUID_SIZE;
+
+ // get data length
+ if (len < DATALEN_SIZE) {
+ ALOGE("ParsePSSH: invalid PSSH data");
+ return empty;
+ }
+
+ uint32_t datalen = *((uint32_t*)data);
+ data += DATALEN_SIZE;
+ len -= DATALEN_SIZE;
+
+ if (len < datalen) {
+ ALOGE("ParsePSSH: invalid PSSH data");
+ return empty;
+ }
+
+ // skip the data
+ data += datalen;
+ len -= datalen;
+
+ DrmUUID _uuid(uuidPtr);
+ drmSchemes.add(_uuid);
+
+ ALOGV("ParsePSSH[%zu]: %s: %s", numentries,
+ _uuid.toHexString().string(),
+ DrmUUID::arrayToHex(data, datalen).string()
+ );
+
+ numentries++;
+ }
+
+ return drmSchemes;
+}
+
+Vector<DrmUUID> NuPlayerDrm::getSupportedDrmSchemes(const void *pssh, size_t psshsize)
+{
+ Vector<DrmUUID> psshDRMs = parsePSSH(pssh, psshsize);
+
+ Vector<DrmUUID> supportedDRMs;
+ // temporary DRM object for crypto Scheme enquiry (without creating a plugin)
+ status_t status = OK;
+ sp<IDrm> drm = CreateDrm(&status);
+ if (drm != NULL) {
+ for (size_t i = 0; i < psshDRMs.size(); i++) {
+ DrmUUID uuid = psshDRMs[i];
+ if (drm->isCryptoSchemeSupported(uuid.ptr(), String8()))
+ supportedDRMs.add(uuid);
+ }
+
+ drm.clear();
+ } else {
+ ALOGE("getSupportedDrmSchemes: Can't create Drm obj: %d", status);
+ }
+
+ ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu",
+ psshDRMs.size(), supportedDRMs.size());
+
+ return supportedDRMs;
+}
+
+// static helpers - public
+
+sp<ICrypto> NuPlayerDrm::createCryptoAndPlugin(const uint8_t uuid[16],
+ const Vector<uint8_t> &drmSessionId, status_t &status)
+{
+ // Extra check
+ if (drmSessionId.isEmpty()) {
+ status = INVALID_OPERATION;
+ ALOGE("createCryptoAndPlugin: Failed. Empty drmSessionId. status: %d", status);
+ return NULL;
+ }
+
+ status = OK;
+ sp<ICrypto> crypto = createCrypto(&status);
+ if (crypto == NULL) {
+ ALOGE("createCryptoAndPlugin: createCrypto failed. status: %d", status);
+ return NULL;
+ }
+ ALOGV("createCryptoAndPlugin: createCrypto succeeded");
+
+ status = crypto->createPlugin(uuid, drmSessionId.array(), drmSessionId.size());
+ if (status != OK) {
+ ALOGE("createCryptoAndPlugin: createCryptoPlugin failed. status: %d", status);
+ // crypto will clean itself when leaving the current scope
+ return NULL;
+ }
+
+ return crypto;
+}
+
+// Parcel has only private copy constructor so passing it in rather than returning
+void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize,
+ const Vector<String8> &mimes_in, Parcel *parcel)
+{
+ // 0) Make mimes a vector of unique items while keeping the original order; video first
+ Vector<String8> mimes;
+ for (size_t j = 0; j < mimes_in.size(); j++) {
+ String8 mime = mimes_in[j];
+ bool exists = false;
+ for (size_t i = 0; i < mimes.size() && !exists; i++) {
+ if (mimes[i] == mime) {
+ exists = true;
+ }
+ } // for i
+
+ if (!exists) {
+ mimes.add(mime);
+ }
+ } // for j
+
+
+ // 1) PSSH bytes
+ parcel->writeUint32(psshsize);
+ parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
+
+ ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %zu %s", psshsize,
+ DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
+
+ // 2) supportedDRMs
+ Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
+ parcel->writeUint32(supportedDRMs.size());
+ for (size_t i = 0; i < supportedDRMs.size(); i++) {
+ DrmUUID uuid = supportedDRMs[i];
+ parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
+
+ ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
+ uuid.toHexString().string());
+ }
+
+ // TODO: remove mimes after it's removed from Java DrmInfo
+ // 3) mimes
+ parcel->writeUint32(mimes.size());
+ for (size_t i = 0; i < mimes.size(); i++) {
+ // writing as String16 so the Java framework side can unpack it to Java String
+ String16 mime(mimes[i]);
+ parcel->writeString16(mime);
+
+ ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO MIME[%zu] %s",
+ i, mimes[i].string());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+/// Helpers for NuPlayerDecoder
+////////////////////////////////////////////////////////////////////////////////////////////
+
+NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo(
+ int numSubSamples,
+ uint8_t key[kBlockSize],
+ uint8_t iv[kBlockSize],
+ CryptoPlugin::Mode mode,
+ size_t *clearbytes,
+ size_t *encryptedbytes)
+{
+ // size needed to store all the crypto data
+ size_t cryptosize = sizeof(CryptoInfo) +
+ sizeof(CryptoPlugin::SubSample) * numSubSamples;
+ CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
+ if (ret == NULL) {
+ ALOGE("couldn't allocate %zu bytes", cryptosize);
+ return NULL;
+ }
+ ret->numSubSamples = numSubSamples;
+ memcpy(ret->key, key, kBlockSize);
+ memcpy(ret->iv, iv, kBlockSize);
+ ret->mode = mode;
+ ret->pattern.mEncryptBlocks = 0;
+ ret->pattern.mSkipBlocks = 0;
+ ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
+ CryptoPlugin::SubSample *subSamples = ret->subSamples;
+
+ for (int i = 0; i < numSubSamples; i++) {
+ subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
+ subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
+ 0 :
+ encryptedbytes[i];
+ }
+
+ return ret;
+}
+
+NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(sp<MetaData> meta)
+{
+ uint32_t type;
+ const void *crypteddata;
+ size_t cryptedsize;
+
+ if (meta == NULL) {
+ ALOGE("getSampleCryptoInfo: Unexpected. No meta data for sample.");
+ return NULL;
+ }
+
+ if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
+ return NULL;
+ }
+ size_t numSubSamples = cryptedsize / sizeof(size_t);
+
+ if (numSubSamples <= 0) {
+ ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
+ return NULL;
+ }
+
+ const void *cleardata;
+ size_t clearsize;
+ if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
+ if (clearsize != cryptedsize) {
+ // The two must be of the same length.
+ ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
+ cryptedsize, clearsize);
+ return NULL;
+ }
+ }
+
+ const void *key;
+ size_t keysize;
+ if (meta->findData(kKeyCryptoKey, &type, &key, &keysize)) {
+ if (keysize != kBlockSize) {
+ ALOGE("getSampleCryptoInfo Keys must be %zu bytes in length: %zu",
+ kBlockSize, keysize);
+ // Keys must be 16 bytes in length.
+ return NULL;
+ }
+ }
+
+ const void *iv;
+ size_t ivsize;
+ if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
+ if (ivsize != kBlockSize) {
+ ALOGE("getSampleCryptoInfo IV must be %zu bytes in length: %zu",
+ kBlockSize, ivsize);
+ // IVs must be 16 bytes in length.
+ return NULL;
+ }
+ }
+
+ int32_t mode;
+ if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+ mode = CryptoPlugin::kMode_AES_CTR;
+ }
+
+ return makeCryptoInfo(numSubSamples,
+ (uint8_t*) key,
+ (uint8_t*) iv,
+ (CryptoPlugin::Mode)mode,
+ (size_t*) cleardata,
+ (size_t*) crypteddata);
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
new file mode 100644
index 0000000..6704bd1
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef NUPLAYER_DRM_H_
+#define NUPLAYER_DRM_H_
+
+#include <binder/Parcel.h>
+#include <media/ICrypto.h>
+#include <media/IDrm.h>
+#include <media/stagefright/MetaData.h> // for CryptInfo
+
+
+namespace android {
+
+ struct DrmUUID {
+ static const int UUID_SIZE = 16;
+
+ DrmUUID() {
+ memset(this->uuid, 0, sizeof(uuid));
+ }
+
+ // to allow defining Vector/KeyedVector of UUID type
+ DrmUUID(const DrmUUID &a) {
+ memcpy(this->uuid, a.uuid, sizeof(uuid));
+ }
+
+ // to allow defining Vector/KeyedVector of UUID type
+ DrmUUID(const uint8_t uuid_in[UUID_SIZE]) {
+ memcpy(this->uuid, uuid_in, sizeof(uuid));
+ }
+
+ const uint8_t *ptr() const {
+ return uuid;
+ }
+
+ String8 toHexString() const {
+ return arrayToHex(uuid, UUID_SIZE);
+ }
+
+ static String8 toHexString(const uint8_t uuid_in[UUID_SIZE]) {
+ return arrayToHex(uuid_in, UUID_SIZE);
+ }
+
+ static String8 arrayToHex(const uint8_t *array, int bytes) {
+ String8 result;
+ for (int i = 0; i < bytes; i++) {
+ result.appendFormat("%02x", array[i]);
+ }
+
+ return result;
+ }
+
+ protected:
+ uint8_t uuid[UUID_SIZE];
+ };
+
+
+ struct NuPlayerDrm {
+
+ // static helpers - internal
+
+ protected:
+ static sp<IDrm> CreateDrm(status_t *pstatus);
+ static sp<ICrypto> createCrypto(status_t *pstatus);
+ static Vector<DrmUUID> parsePSSH(const void *pssh, size_t psshsize);
+ static Vector<DrmUUID> getSupportedDrmSchemes(const void *pssh, size_t psshsize);
+
+ // static helpers - public
+
+ public:
+ static sp<ICrypto> createCryptoAndPlugin(const uint8_t uuid[16],
+ const Vector<uint8_t> &drmSessionId, status_t &status);
+ // Parcel has only private copy constructor so passing it in rather than returning
+ static void retrieveDrmInfo(const void *pssh, size_t psshsize,
+ const Vector<String8> &mimes_in, Parcel *parcel);
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ /// Helpers for NuPlayerDecoder
+ ////////////////////////////////////////////////////////////////////////////////////////////
+
+ static const uint8_t kBlockSize = 16; // AES_BLOCK_SIZE
+
+ struct CryptoInfo {
+ int numSubSamples;
+ uint8_t key[kBlockSize];
+ uint8_t iv[kBlockSize];
+ CryptoPlugin::Mode mode;
+ CryptoPlugin::Pattern pattern;
+ CryptoPlugin::SubSample *subSamples;
+ };
+
+ static CryptoInfo *makeCryptoInfo(
+ int numSubSamples,
+ uint8_t key[kBlockSize],
+ uint8_t iv[kBlockSize],
+ CryptoPlugin::Mode mode,
+ size_t *clearbytes,
+ size_t *encryptedbytes);
+
+ static CryptoInfo *getSampleCryptoInfo(sp<MetaData> meta);
+
+ }; // NuPlayerDrm
+
+} // android
+
+#endif //NUPLAYER_DRM_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 0429ef1..e7cca27 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -20,9 +20,10 @@
#include "NuPlayer.h"
+#include <media/ICrypto.h>
+#include <media/mediaplayer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MetaData.h>
-#include <media/mediaplayer.h>
#include <utils/Vector.h>
namespace android {
@@ -55,6 +56,8 @@
kWhatQueueDecoderShutdown,
kWhatDrmNoLicense,
kWhatInstantiateSecureDecoders,
+ // Modular DRM
+ kWhatDrmInfo,
};
// The provides message is used to notify the player about various
@@ -132,6 +135,17 @@
virtual void setOffloadAudio(bool /* offload */) {}
+ // Modular DRM
+ virtual status_t prepareDrm(
+ const uint8_t /*uuid*/[16], const Vector<uint8_t> &/*drmSessionId*/,
+ sp<ICrypto> */*crypto*/) {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t releaseDrm() {
+ return INVALID_OPERATION;
+ }
+
protected:
virtual ~Source() {}
@@ -143,6 +157,8 @@
void notifyVideoSizeChanged(const sp<AMessage> &format = NULL);
void notifyInstantiateSecureDecoders(const sp<AMessage> &reply);
void notifyPrepared(status_t err = OK);
+ // Modular DRM
+ void notifyDrmInfo(const sp<ABuffer> &buffer);
private:
sp<AMessage> mNotify;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a332cce..cbd5802 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -758,6 +758,51 @@
return err;
}
+status_t MediaCodec::releaseCrypto()
+{
+ ALOGV("releaseCrypto");
+
+ sp<AMessage> msg = new AMessage(kWhatDrmReleaseCrypto, this);
+
+ sp<AMessage> response;
+ status_t status = msg->postAndAwaitResponse(&response);
+
+ if (status == OK && response != NULL) {
+ CHECK(response->findInt32("status", &status));
+ ALOGV("releaseCrypto ret: %d ", status);
+ }
+ else {
+ ALOGE("releaseCrypto err: %d", status);
+ }
+
+ return status;
+}
+
+void MediaCodec::onReleaseCrypto(const sp<AMessage>& msg)
+{
+ status_t status = INVALID_OPERATION;
+ if (mCrypto != NULL) {
+ ALOGV("onReleaseCrypto: mCrypto: %p (%d)", mCrypto.get(), mCrypto->getStrongCount());
+ mBufferChannel->setCrypto(NULL);
+ // TODO change to ALOGV
+ ALOGD("onReleaseCrypto: [before clear] mCrypto: %p (%d)",
+ mCrypto.get(), mCrypto->getStrongCount());
+ mCrypto.clear();
+
+ status = OK;
+ }
+ else {
+ ALOGW("onReleaseCrypto: No mCrypto. err: %d", status);
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("status", status);
+
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ response->postReply(replyID);
+}
+
status_t MediaCodec::setInputSurface(
const sp<PersistentSurface> &surface) {
sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
@@ -1938,9 +1983,15 @@
crypto = NULL;
}
+ ALOGV("kWhatConfigure: Old mCrypto: %p (%d)",
+ mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
mCrypto = static_cast<ICrypto *>(crypto);
mBufferChannel->setCrypto(mCrypto);
+ ALOGV("kWhatConfigure: New mCrypto: %p (%d)",
+ mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -2471,6 +2522,12 @@
break;
}
+ case kWhatDrmReleaseCrypto:
+ {
+ onReleaseCrypto(msg);
+ break;
+ }
+
default:
TRESPASS();
}
@@ -2530,6 +2587,10 @@
delete mSoftRenderer;
mSoftRenderer = NULL;
+ if ( mCrypto != NULL ) {
+ ALOGV("setState: ~mCrypto: %p (%d)",
+ mCrypto.get(), (mCrypto != NULL ? mCrypto->getStrongCount() : 0));
+ }
mCrypto.clear();
handleSetSurface(NULL);