diff --git a/doc/Widevine Java API for Android OS DRM Framework.pdf b/doc/Widevine Java API for Android OS DRM Framework.pdf deleted file mode 100644 index b2cba381..00000000 Binary files a/doc/Widevine Java API for Android OS DRM Framework.pdf and /dev/null differ diff --git a/libdrmwvmplugin.so b/libdrmwvmplugin.so deleted file mode 100644 index 2bd26e06..00000000 Binary files a/libdrmwvmplugin.so and /dev/null differ diff --git a/libwvm.so b/libwvm.so deleted file mode 100644 index 0fd26f52..00000000 Binary files a/libwvm.so and /dev/null differ diff --git a/proprietary/drmwvmplugin/Android.mk b/proprietary/drmwvmplugin/Android.mk new file mode 100644 index 00000000..33eb5d54 --- /dev/null +++ b/proprietary/drmwvmplugin/Android.mk @@ -0,0 +1,48 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifeq ($(TARGET_ARCH),arm) + +LOCAL_SRC_FILES:= \ + src/WVMDrmPlugin.cpp \ + src/WVMLogging.cpp + +LOCAL_C_INCLUDES:= \ + bionic \ + bionic/libstdc++/include \ + external/stlport/stlport \ + vendor/widevine/proprietary/streamcontrol/include \ + vendor/widevine/proprietary/drmwvmplugin/include + +LOCAL_MODULE := libdrmwvmplugin +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm + +LOCAL_STATIC_LIBRARIES := \ + libdrmframeworkcommon + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libcutils \ + libstlport \ + libz \ + libwvdrm \ + libWVStreamControlAPI + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_PRELINK_MODULE := false + +LOCAL_C_INCLUDES += \ + $(TOP)/frameworks/base/drm/libdrmframework/include \ + $(TOP)/frameworks/base/drm/libdrmframework/plugins/common/include \ + $(TOP)/frameworks/base/include + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/proprietary/drmwvmplugin/include/WVMDrmPlugin.h b/proprietary/drmwvmplugin/include/WVMDrmPlugin.h new file mode 100644 index 00000000..efce025d --- /dev/null +++ b/proprietary/drmwvmplugin/include/WVMDrmPlugin.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2011 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 __WVMDRMPLUGIN_H__ +#define __WVMDRMPLUGIN_H__ + + +#include +#include + +#include "WVDRMPluginAPI.h" + + +namespace android { + + class WVMDrmPlugin : public DrmEngineBase + { + +public: + WVMDrmPlugin(); + virtual ~WVMDrmPlugin(); + +protected: + DrmConstraints* onGetConstraints(int uniqueId, const String8* path, int action); + + DrmMetadata* onGetMetadata(int uniqueId, const String8* path); + + status_t onInitialize(int uniqueId); + + status_t onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener); + + status_t onTerminate(int uniqueId); + + bool onCanHandle(int uniqueId, const String8& path); + + DrmInfoStatus* onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo); + + status_t onSaveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + + DrmInfo* onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + + String8 onGetOriginalMimeType(int uniqueId, const String8& path); + + int onGetDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + + int onCheckRightsStatus(int uniqueId, const String8& path, int action); + + status_t onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve); + + status_t onSetPlaybackStatus( + int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, off64_t position); + + bool onValidateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + + status_t onRemoveRights(int uniqueId, const String8& path); + + status_t onRemoveAllRights(int uniqueId); + + status_t onOpenConvertSession(int uniqueId, int convertId); + + DrmConvertedStatus* onConvertData(int uniqueId, int convertId, const DrmBuffer* inputData); + + DrmConvertedStatus* onCloseConvertSession(int uniqueId, int convertId); + + DrmSupportInfo* onGetSupportInfo(int uniqueId); + + status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle, + int fd, off64_t offset, off64_t length); + + status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle, + const char *uri); + + status_t onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle); + + status_t onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + + status_t onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, + DrmBuffer *ivBuffer); + + + status_t onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId); + + ssize_t onPread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off64_t offset); + +private: + const IDrmEngine::OnInfoListener *mOnInfoListener; + WVDRMPluginAPI *mDrmPluginImpl; +}; + +}; + +#endif /* __WVMDRMPLUGIN__ */ + diff --git a/proprietary/drmwvmplugin/include/WVMLogging.h b/proprietary/drmwvmplugin/include/WVMLogging.h new file mode 100644 index 00000000..1e46c7ad --- /dev/null +++ b/proprietary/drmwvmplugin/include/WVMLogging.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 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 __WVMLOGGING_H__ +#define __WVMLOGGING_H__ + +void android_printbuf(const char *buf); + +#endif diff --git a/proprietary/drmwvmplugin/src/WVMDrmPlugin.cpp b/proprietary/drmwvmplugin/src/WVMDrmPlugin.cpp new file mode 100644 index 00000000..b39599cf --- /dev/null +++ b/proprietary/drmwvmplugin/src/WVMDrmPlugin.cpp @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2011 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 "WVMDrmPlugIn" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "WVMDrmPlugin.h" +#include "WVMLogging.h" +#include "AndroidHooks.h" + +using namespace std; +using namespace android; + + +// This extern "C" is mandatory to be managed by TPlugInManager +extern "C" IDrmEngine* create() { + return new WVMDrmPlugin(); +} + +// This extern "C" is mandatory to be managed by TPlugInManager +extern "C" void destroy(IDrmEngine* pPlugIn) { + delete pPlugIn; +} + +WVMDrmPlugin::WVMDrmPlugin() + : DrmEngineBase(), + mOnInfoListener(NULL), + mDrmPluginImpl(WVDRMPluginAPI::create()) +{ +} + +WVMDrmPlugin::~WVMDrmPlugin() { + WVDRMPluginAPI::destroy(mDrmPluginImpl); +} + + +/** + * Initialize plug-in + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onInitialize(int uniqueId) { + //LOGD("WVMDrmPlugin::onInitialize : %d", uniqueId); + AndroidSetLogCallout(android_printbuf); + return DRM_NO_ERROR; +} + +/** + * Terminate the plug-in + * and release resource bound to plug-in + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onTerminate(int uniqueId) { + //LOGD("WVMDrmPlugin::onTerminate : %d", uniqueId); + return DRM_NO_ERROR; +} + +/** + * Register a callback to be invoked when the caller required to + * receive necessary information + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] infoListener Listener + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onSetOnInfoListener( + int uniqueId, const IDrmEngine::OnInfoListener* infoListener) { + //LOGD("WVMDrmPlugin::onSetOnInfoListener : %d", uniqueId); + mOnInfoListener = infoListener; + return DRM_NO_ERROR; +} + +/** + * Retrieves necessary information for registration, unregistration or rights + * acquisition information. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfoRequest Request information to retrieve drmInfo + * @return DrmInfo + * instance as a result of processing given input + */ +DrmInfo* WVMDrmPlugin::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + //LOGD("WVMDrmPlugin::onAcquireDrmInfo : %d", uniqueId); + DrmInfo* drmInfo = NULL; + + std::string assetPath; + + if (NULL != drmInfoRequest) { + switch(drmInfoRequest->getInfoType()) { + case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO: { + + assetPath = drmInfoRequest->get(String8("WVAssetURIKey")).string(); + + WVCredentials credentials; + + // creates a data store object per each portal + credentials.portal = drmInfoRequest->get(String8("WVPortalKey")).string(); + if ( (assetPath.size() == 0) || (credentials.portal.size() == 0) ) { + LOGE("onAcquireDrmInfo: Empty asset path or portal string, must specify both"); + return NULL; + } + + std::string assetDbPath = drmInfoRequest->get(String8("WVAssetDBPathKey")).string(); + //LOGV("onAcquireDrmInfo: portal=%s, dsPath=%s", credentials.portal.c_str(), assetDbPath.c_str()); + + credentials.drmServerURL = drmInfoRequest->get(String8("WVDRMServerKey")).string(); + credentials.userData = drmInfoRequest->get(String8("WVCAUserDataKey")).string(); + credentials.deviceID = drmInfoRequest->get(String8("WVDeviceIDKey")).string(); + credentials.streamID = drmInfoRequest->get(String8("WVStreamIDKey")).string(); + + string systemIdStr = drmInfoRequest->get(String8("WVSystemIDKey")).string(); + string assetIdStr = drmInfoRequest->get(String8("WVAssetIDKey")).string(); + string keyIdStr = drmInfoRequest->get(String8("WVKeyIDKey")).string(); + + uint32_t systemId, assetId, keyId; + + if (!mDrmPluginImpl->AcquireDrmInfo(assetPath, credentials, assetDbPath, + systemIdStr, assetIdStr, keyIdStr, + &systemId, &assetId, &keyId)) + return NULL; + + + String8 dataString("dummy_acquistion_string"); + int length = dataString.length(); + char* data = NULL; + data = new char[length]; + memcpy(data, dataString.string(), length); + drmInfo = new DrmInfo(drmInfoRequest->getInfoType(), + DrmBuffer(data, length), drmInfoRequest->getMimeType()); + + // Sets additional drmInfo attributes + drmInfo->put(String8("WVAssetURIKey"), String8(assetPath.c_str())); + drmInfo->put(String8("WVDRMServerKey"), String8(credentials.drmServerURL.c_str())); + drmInfo->put(String8("WVAssetDbPathKey"), String8(assetDbPath.c_str())); + drmInfo->put(String8("WVPortalKey"), String8(credentials.portal.c_str())); + drmInfo->put(String8("WVCAUserDataKey"), String8(credentials.userData.c_str())); + drmInfo->put(String8("WVDeviceIDKey"), String8(credentials.deviceID.c_str())); + drmInfo->put(String8("WVStreamIDKey"), String8(credentials.streamID.c_str())); + + char buffer[16]; + sprintf(buffer, "%lu", (unsigned long)systemId); + drmInfo->put(String8("WVSystemIDKey"), String8(buffer)); + sprintf(buffer, "%lu", (unsigned long)assetId); + drmInfo->put(String8("WVAssetIDKey"), String8(buffer)); + sprintf(buffer, "%lu", (unsigned long)keyId); + drmInfo->put(String8("WVKeyIDKey"), String8(buffer)); + break; + } + case DrmInfoRequest::TYPE_REGISTRATION_INFO: + case DrmInfoRequest::TYPE_UNREGISTRATION_INFO: + case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO: { + LOGE("onAcquireDrmInfo: Unsupported DrmInfoRequest type %d", + drmInfoRequest->getInfoType()); + break; + } + default: { + LOGE("onAcquireDrmInfo: Unknown info type %d", drmInfoRequest->getInfoType()); + break; + } + } + } + return drmInfo; +} + +/** + * Executes given drm information based on its type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmInfo Information needs to be processed + * @return DrmInfoStatus + * instance as a result of processing given input + */ +DrmInfoStatus* WVMDrmPlugin::onProcessDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + //LOGD("WVMDrmPlugin::onProcessDrmInfo: %d", uniqueId); + + int status = DrmInfoStatus::STATUS_ERROR; + + if (NULL != drmInfo) { + if (drmInfo->getInfoType() == DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO) { + std::string assetPath = drmInfo->get(String8("WVAssetURIKey")).string(); + + if (mDrmPluginImpl->ProcessDrmInfo(assetPath)) + status = DrmInfoStatus::STATUS_OK; + } else { + LOGE("onProcessDrmInfo : drmInfo type %d not supported", drmInfo->getInfoType()); + } + } else { + LOGE("onProcessDrmInfo : drmInfo cannot be NULL"); + } + + String8 licenseString("dummy_license_string"); + const int bufferSize = licenseString.size(); + char* data = NULL; + data = new char[bufferSize]; + memcpy(data, licenseString.string(), bufferSize); + const DrmBuffer* buffer = new DrmBuffer(data, bufferSize); + DrmInfoStatus* drmInfoStatus = + new DrmInfoStatus(status, drmInfo->getInfoType(), buffer, drmInfo->getMimeType()); + + return drmInfoStatus; +} + +/** + * Get constraint information associated with input content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Actions defined such as, + * Action::DEFAULT, Action::PLAY, etc + * @return DrmConstraints + * key-value pairs of constraint are embedded in it + * @note + * In case of error, return NULL + */ +DrmConstraints* WVMDrmPlugin::onGetConstraints(int uniqueId, const String8* path, int action) +{ + //LOGD("WVMDrmPlugin::onGetConstraints : %d", uniqueId); + + if ( (Action::DEFAULT != action) && (Action::PLAY != action) ) { + LOGE("onGetConstraints : action %d not supported", action); + return NULL; + } + + uint32_t licenseDuration = 0; + uint32_t timeSincePlayback = 0; + uint32_t timeRemaining = 0; + + std::string assetPath(path->string()); + if (!mDrmPluginImpl->GetConstraints(assetPath, &timeSincePlayback, &timeRemaining, &licenseDuration)) + return NULL; + + DrmConstraints* drmConstraints = new DrmConstraints(); + char charValue[16]; // max uint32 = 0xffffffff + terminating char + + memset(charValue, 0, 16); + sprintf(charValue, "%lu", (unsigned long)timeSincePlayback); + drmConstraints->put(&(DrmConstraints::LICENSE_START_TIME), charValue); + + memset(charValue, 0, 16); + sprintf(charValue, "%lu", (unsigned long)timeRemaining); + drmConstraints->put(&(DrmConstraints::LICENSE_EXPIRY_TIME), charValue); + + memset(charValue, 0, 16); + sprintf(charValue, "%lu", (unsigned long)licenseDuration); + drmConstraints->put(&(DrmConstraints::LICENSE_AVAILABLE_TIME), charValue); + + return drmConstraints; +} + + +/** + * Returns the information about the Drm Engine capabilities which includes + * supported MimeTypes and file suffixes. + * + * @param[in] uniqueId Unique identifier for a session + * @return DrmSupportInfo + * instance which holds the capabilities of a plug-in + */ +DrmSupportInfo* WVMDrmPlugin::onGetSupportInfo(int uniqueId) { + //LOGD("WVMDrmPlugin::onGetSupportInfo : %d", uniqueId); + DrmSupportInfo* drmSupportInfo = new DrmSupportInfo(); + // Add mimetype's + drmSupportInfo->addMimeType(String8("video/wvm")); + // Add File Suffixes + drmSupportInfo->addFileSuffix(String8(".wvm")); + // Add plug-in description + drmSupportInfo->setDescription(String8("Widevine DRM plug-in")); + return drmSupportInfo; +} + +/** + * Get meta data from protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * + * @return DrmMetadata + * key-value pairs of meta data; NULL if failed + */ +DrmMetadata* WVMDrmPlugin::onGetMetadata(int uniqueId, const String8* path) { + //LOGD("WVDrmPlugin::onGetMetadata returns NULL\n"); + return NULL; +} + +/** + * Save DRM rights to specified rights path + * and make association with content path + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] drmRights DrmRights to be saved + * @param[in] rightsPath File path where rights to be saved + * @param[in] contentPath File path where content was saved + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onSaveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + //LOGD("WVMDrmPlugin::onSaveRights : %d", uniqueId); + return DRM_NO_ERROR; +} + +/** + * Get whether the given content can be handled by this plugin or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path the protected object + * @return bool + * Returns true if this plugin can handle , false in case of not able to handle + */ +bool WVMDrmPlugin::onCanHandle(int uniqueId, const String8& path) { + //LOGD("WVMDrmPlugin::canHandle('%s') ", path.string()); + String8 extension = path.getPathExtension(); + extension.toLower(); + return (String8(".wvm") == extension); +} + +/** + * Retrieves the mime type embedded inside the original content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return String8 + * Returns mime-type of the original content, such as "video/mpeg" + */ +String8 WVMDrmPlugin::onGetOriginalMimeType(int uniqueId, const String8& path) { + //LOGD("WVMDrmPlugin::onGetOriginalMimeType() : %d", uniqueId); + return String8("video/wvm"); +} + +/** + * Retrieves the type of the protected object (content, rights, etc..) + * using specified path or mimetype. At least one parameter should be non null + * to retrieve DRM object type + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the content or null. + * @param[in] mimeType Mime type of the content or null. + * @return type of the DRM content, + * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT + */ +int WVMDrmPlugin::onGetDrmObjectType( + int uniqueId, const String8& path, const String8& mimeType) { + //LOGD("WVMDrmPlugin::onGetDrmObjectType() : %d", uniqueId); + return DrmObjectType::UNKNOWN; +} + +/** + * Check whether the given content has valid rights or not + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to perform (Action::DEFAULT, Action::PLAY, etc) + * @return the status of the rights for the protected content, + * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc. + */ +int WVMDrmPlugin::onCheckRightsStatus(int uniqueId, const String8& path, int action) { + //LOGD("WVMDrmPlugin::onCheckRightsStatus() : %d", uniqueId); + + if ( (Action::DEFAULT != action) && (Action::PLAY != action) ) { + LOGE("onCheckRightsStatus : action %d not supported", action); + return RightsStatus::RIGHTS_INVALID; + } + + std::string assetPath(path.string()); + int rightsStatus = mDrmPluginImpl->CheckRightsStatus(assetPath); + + switch(rightsStatus) { + case WVDRMPluginAPI::RIGHTS_INVALID: + return RightsStatus::RIGHTS_INVALID; + break; + case WVDRMPluginAPI::RIGHTS_EXPIRED: + return RightsStatus::RIGHTS_EXPIRED; + break; + case WVDRMPluginAPI::RIGHTS_VALID: + return RightsStatus::RIGHTS_VALID; + break; + case WVDRMPluginAPI::RIGHTS_NOT_ACQUIRED: + return RightsStatus::RIGHTS_NOT_ACQUIRED; + break; + } + return RightsStatus::RIGHTS_INVALID; +} + +/** + * Consumes the rights for a content. + * If the reserve parameter is true the rights is reserved until the same + * application calls this api again with the reserve parameter set to false. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] action Action to perform. (Action::DEFAULT, Action::PLAY, etc) + * @param[in] reserve True if the rights should be reserved. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onConsumeRights(int uniqueId, DecryptHandle* decryptHandle, + int action, bool reserve) { + //LOGD("WVMDrmPlugin::onConsumeRights() : %d", uniqueId); + return DRM_NO_ERROR; +} + +/** + * Informs the DRM Engine about the playback actions performed on the DRM files. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] playbackStatus Playback action (Playback::START, Playback::STOP, Playback::PAUSE) + * @param[in] position Position in the file (in milliseconds) where the start occurs. + * Only valid together with Playback::START. + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onSetPlaybackStatus(int uniqueId, DecryptHandle* decryptHandle, + int playbackStatus, off64_t position) { + //LOGD("WVMDrmPlugin::onSetPlaybackStatus"); + + int op; + + switch(playbackStatus) { + case Playback::START: + op = WVDRMPluginAPI::PLAYBACK_START; + break; + case Playback::STOP: + op = WVDRMPluginAPI::PLAYBACK_STOP; + break; + case Playback::PAUSE: + op = WVDRMPluginAPI::PLAYBACK_PAUSE; + break; + default: + op = WVDRMPluginAPI::PLAYBACK_INVALID; + break; + } + + if (mDrmPluginImpl->SetPlaybackStatus(op, position)) + return DRM_NO_ERROR; + + return DRM_ERROR_UNKNOWN; +} + +/** + * Validates whether an action on the DRM content is allowed or not. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @param[in] action Action to validate (Action::PLAY, Action::TRANSFER, etc) + * @param[in] description Detailed description of the action + * @return true if the action is allowed. + */ +bool WVMDrmPlugin::onValidateAction(int uniqueId, const String8& path, + int action, const ActionDescription& description) { + //LOGD("WVMDrmPlugin::onValidateAction() : %d", uniqueId); + return true; +} + +/** + * Removes the rights associated with the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] path Path of the protected content + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onRemoveRights(int uniqueId, const String8& path) { + //LOGD("WVMDrmPlugin::onRemoveRights() : %d", uniqueId); + + std::string assetPath(path.string()); + if (mDrmPluginImpl->RemoveRights(assetPath)) + return DRM_NO_ERROR; + + return DRM_ERROR_UNKNOWN; +} + +/** + * Removes all the rights information of each plug-in associated with + * DRM framework. Will be used in master reset + * + * @param[in] uniqueId Unique identifier for a session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onRemoveAllRights(int uniqueId) { + //LOGD("WVMDrmPlugin::onRemoveAllRights() : %d", uniqueId); + + if (mDrmPluginImpl->RemoveAllRights()) + return DRM_NO_ERROR; + + return DRM_ERROR_UNKNOWN; +} + +/** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the current decryption session + * @param[in] fd File descriptor of the protected content to be decrypted + * @param[in] offset Start position of the content + * @param[in] length The length of the protected content + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ +status_t WVMDrmPlugin::onOpenDecryptSession( + int uniqueId, DecryptHandle *decryptHandle, int fd, off64_t offset, off64_t length) +{ + status_t result = DRM_ERROR_CANNOT_HANDLE; + + //LOGD("onOpenDecryptSession: fd=%d, offset=%lld, length=%lld", fd, offset, length); + + char buffer[64 * 1024]; + int dupfd = dup(fd); + if (dupfd == -1) + return result; + + FILE *f = fdopen(dupfd, "rb"); + if (f) { + fseek(f, 0, SEEK_SET); + if (fread(buffer, 1, sizeof(buffer), f) != sizeof(buffer)) { + fclose(f); + return DRM_ERROR_CANNOT_HANDLE; + } + fclose(f); + } else { + close(dupfd); + return result; + } + + if (WV_IsWidevineMedia(buffer, sizeof(buffer))) { + //LOGD("WVMDrmPlugin::onOpenDecryptSession - WV_IsWidevineMedia: true"); + decryptHandle->mimeType = String8("video/wvm"); + decryptHandle->decryptApiType = DecryptApiType::WV_BASED; + decryptHandle->status = DRM_NO_ERROR; + decryptHandle->decryptInfo = NULL; + result = DRM_NO_ERROR; + } else { + //LOGD("WVMDrmPlugin::onOpenDecryptSession - not Widevine media"); + } + + return result; +} + +/** + * Open the decrypt session to decrypt the given protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the current decryption session + * @param[in] uri Path of the protected content to be decrypted + * @return + * DRM_ERROR_CANNOT_HANDLE for failure and DRM_NO_ERROR for success + */ +status_t WVMDrmPlugin::onOpenDecryptSession( + int uniqueId, DecryptHandle* decryptHandle, const char* uri) +{ + status_t result = DRM_ERROR_CANNOT_HANDLE; + + if (!uri) + return result; + + size_t len = strlen(uri); + + if ((len >= 4 && !strncmp(&uri[len - 4], ".wvm", 4)) || + (strstr(uri, ".wvm?") != NULL) || + (len >= 5 && !strncmp(&uri[len - 5], ".m3u8", 5)) || + (strstr(uri, ".m3u8?") != NULL)) + { + //LOGD("WVMDrmPlugin::onOpenDecryptSession(uri) : %d - match", uniqueId); + decryptHandle->mimeType = String8("video/wvm"); + decryptHandle->decryptApiType = DecryptApiType::WV_BASED; + decryptHandle->status = DRM_NO_ERROR; + decryptHandle->decryptInfo = NULL; + + mDrmPluginImpl->OpenSession(); + result = DRM_NO_ERROR; + } else { + //LOGD("WVMDrmPlugin::onOpenDecryptSession(uri) - not Widevine media"); + } + + + return result; +} + + +/** + * Close the decrypt session for the given handle + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { + //LOGD("WVMDrmPlugin::onCloseDecryptSession() : %d", uniqueId); + if (NULL != decryptHandle) { + if (NULL != decryptHandle->decryptInfo) { + delete decryptHandle->decryptInfo; decryptHandle->decryptInfo = NULL; + } + delete decryptHandle; decryptHandle = NULL; + } + mDrmPluginImpl->CloseSession(); + return DRM_NO_ERROR; +} + +/** + * Initialize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptId Handle for the decryption session + * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID + * @param[in] headerInfo Information for initializing decryption of this decrypUnit + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + //LOGD("WVMDrmPlugin::onInitializeDecryptUnit(): %d", uniqueId); + if (!mDrmPluginImpl->Prepare(headerInfo->data, headerInfo->length)) + return DRM_ERROR_CANNOT_HANDLE; + + return DRM_NO_ERROR; +} + +/** + * Decrypt the protected content buffers for the given unit + * This method will be called any number of times, based on number of + * encrypted streams received from application. + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptId Handle for the decryption session + * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID + * @param[in] encBuffer Encrypted data block + * @param[out] decBuffer Decrypted data block + * @param[in] IV Optional buffer + * @return status_t + * Returns the error code for this API + * DRM_NO_ERROR for success, and one of DRM_ERROR_UNKNOWN, DRM_ERROR_LICENSE_EXPIRED + * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, + * DRM_ERROR_DECRYPT for failure. + */ +status_t WVMDrmPlugin::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, + DrmBuffer *ivBuffer) +{ + //LOGD("WVMDrmPlugin::onDecrypt\n"); +#define AES_BLOCK_SIZE 16 + char iv[AES_BLOCK_SIZE]; + memcpy(iv, ivBuffer->data, sizeof(iv)); + + if (*decBuffer == NULL) + return DRM_ERROR_DECRYPT; + + (*decBuffer)->length = encBuffer->length; + + if (!mDrmPluginImpl->Operate(encBuffer->data, (*decBuffer)->data, encBuffer->length, iv)) { + (*decBuffer)->length = 0; + usleep(1000000); // prevent spinning + return DRM_ERROR_LICENSE_EXPIRED; + } + + return DRM_NO_ERROR; +} + +/** + * Finalize decryption for the given unit of the protected content + * + * @param[in] uniqueId Unique identifier for a session + * @param[in] decryptHandle Handle for the decryption session + * @param[in] decryptUnitId ID Specifies decryption unit, such as track ID + * @return status_t + * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure + */ +status_t WVMDrmPlugin::onFinalizeDecryptUnit( + int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { + //LOGD("WVMDrmPlugin::onFinalizeDecryptUnit() : %d", uniqueId); + return DRM_NO_ERROR; +} + +/** + * The following methods are not required for the Widevine DRM plugin + */ +ssize_t WVMDrmPlugin::onPread(int uniqueId, DecryptHandle* decryptHandle, + void* buffer, ssize_t numBytes, off64_t offset) { + return DRM_ERROR_UNKNOWN; +} + + +status_t WVMDrmPlugin::onOpenConvertSession(int uniqueId, int convertId) { + return DRM_ERROR_UNKNOWN; +} + +DrmConvertedStatus* WVMDrmPlugin::onConvertData( + int uniqueId, int convertId, const DrmBuffer* inputData) { + return NULL; +} + +DrmConvertedStatus* WVMDrmPlugin::onCloseConvertSession(int uniqueId, int convertId) { + return NULL; +} + + diff --git a/proprietary/drmwvmplugin/src/WVMLogging.cpp b/proprietary/drmwvmplugin/src/WVMLogging.cpp new file mode 100644 index 00000000..a3b9bd7a --- /dev/null +++ b/proprietary/drmwvmplugin/src/WVMLogging.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 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_TAG "WVMLogging" +#include +#include "WVMLogging.h" + +// Connect Widevine debug logging into Android logging + +void android_printbuf(const char *buf) +{ + LOGD("%s", buf); +} diff --git a/proprietary/drmwvmplugin/test/Android.mk b/proprietary/drmwvmplugin/test/Android.mk new file mode 100644 index 00000000..d3fa57cc --- /dev/null +++ b/proprietary/drmwvmplugin/test/Android.mk @@ -0,0 +1,32 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + TestPlugin.cpp \ + ../src/WVMLogging.cpp + +LOCAL_C_INCLUDES+= \ + bionic \ + vendor/widevine/proprietary/include \ + external/stlport/stlport \ + frameworks/base/drm/libdrmframework/include \ + frameworks/base/drm/libdrmframework/plugins/common/include \ + frameworks/base/drm/libdrmframework/plugins/widevine/include + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + libwvdrm \ + liblog \ + libutils \ + libz \ + libdl + +LOCAL_STATIC_LIBRARIES := \ + libdrmframeworkcommon + +LOCAL_MODULE:=test-wvdrmplugin + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + diff --git a/proprietary/drmwvmplugin/test/TestPlugin.cpp b/proprietary/drmwvmplugin/test/TestPlugin.cpp new file mode 100644 index 00000000..19422573 --- /dev/null +++ b/proprietary/drmwvmplugin/test/TestPlugin.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2011 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. + */ + +#include + +#include + +#include "WVMDrmPlugin.h" +#include "drm/DrmInfoRequest.h" +#include "drm/DrmInfoStatus.h" +#include "drm/DrmConstraints.h" +#include "drm/DrmInfo.h" + +using namespace android; +using namespace std; + +class WVMDrmPluginTest +{ +public: + WVMDrmPluginTest() {} + ~WVMDrmPluginTest() {} + + void TestAsset(IDrmEngine *plugin, String8 &url); + + void TestRemoveAllRights(IDrmEngine *plugin); + void TestAcquireRights(IDrmEngine *plugin, String8 &url); + void TestCheckRightsNotAcquired(IDrmEngine *plugin, String8 &url); + void TestCheckValidRights(IDrmEngine *plugin, String8 &url); + void TestGetConstraints(IDrmEngine *plugin, String8 &url); + void TestRemoveRights(IDrmEngine *plugin, String8 &url); + + // Tests + void Run(); +}; + +void WVMDrmPluginTest::Run() +{ + cout << "WVDrmPluginTest::Run" << endl; + const char *path = "/system/lib/drm/libdrmwvmplugin.so"; + void *handle = dlopen(path, RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "Can't open plugin: %s\n", path); + exit(-1); + } + + typedef IDrmEngine *(*create_t)(); + create_t creator = (create_t)dlsym(handle, "create"); + if (!creator) { + fprintf(stderr, "Can't find create method\n"); + exit(-1); + } + + typedef void (*destroy_t)(IDrmEngine *); + destroy_t destroyer = (destroy_t)dlsym(handle, "destroy"); + if (!destroyer) { + fprintf(stderr, "Can't find destroy method\n"); + exit(-1); + } + + // Basic test - see if we can instantiate the object and call a method + IDrmEngine *plugin = (*creator)(); + if (plugin->initialize(0) != DRM_NO_ERROR) { + fprintf(stderr, "onInitialize failed!\n"); + exit(-1); + } + + // Remote asset + String8 url; + url = String8("http://seawwws001.cdn.shibboleth.tv/videos/qa/adventures_d_ch_444169.wvm"); + TestAsset(plugin, url); + + // Local asset + url = String8("file:///sdcard/Widevine/trailers_d_ch_444169.wvm"); + TestAsset(plugin, url); + + // Remote asset with query parameters + url = String8("http://seawwws001.cdn.shibboleth.tv/videos/qa/adventures_d_ch_444169.wvm?a=b"); + TestAsset(plugin, url); + + // Shut down and clean up + if (plugin->terminate(0) != DRM_NO_ERROR) { + fprintf(stderr, "onTerminate failed!\n"); + exit(-1); + } + destroyer(plugin); + dlclose(handle); + printf("Test successful!\n"); + exit(0); +} + +void WVMDrmPluginTest::TestAcquireRights(IDrmEngine *plugin, String8 &url) +{ + cout << "WVDrmPluginTest::TestAcquireRights url=" << url << endl; + + String8 mimeType("video/wvm"); + DrmInfoRequest rightsAcquisitionInfo(DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, mimeType); + rightsAcquisitionInfo.put(String8("WVDRMServerKey"), String8("http://wstfcps005.shibboleth.tv/widevine/cypherpc/cgi-bin/GetEMMs.cgi")); + rightsAcquisitionInfo.put(String8("WVAssetURIKey"), url); + rightsAcquisitionInfo.put(String8("WVDeviceIDKey"), String8("device1234")); + rightsAcquisitionInfo.put(String8("WVPortalKey"), String8("YouTube")); + + DrmInfo *info = plugin->acquireDrmInfo(0, &rightsAcquisitionInfo); + if (info == NULL) { + fprintf(stderr, "acquireDrmInfo failed!\n"); + exit(-1); + } + + DrmInfoStatus *status = plugin->processDrmInfo(0, info); + if (status == NULL || status->statusCode != DrmInfoStatus::STATUS_OK) { + fprintf(stderr, "processDrmInfo failed!\n"); + exit(-1); + } + + delete status; + delete info; +} + +void WVMDrmPluginTest::TestCheckRightsNotAcquired(IDrmEngine *plugin, String8 &url) +{ + cout << "WVDrmPluginTest::TestCheckRightsNotAcquired url=" << url << endl; + + if (plugin->checkRightsStatus(0, url, Action::DEFAULT) != RightsStatus::RIGHTS_NOT_ACQUIRED) { + fprintf(stderr, "checkRightsNotAcquired default action failed!\n"); + exit(-1); + } + + if (plugin->checkRightsStatus(0, url, Action::PLAY) != RightsStatus::RIGHTS_NOT_ACQUIRED) { + fprintf(stderr, "checkRightsNotAcquired failed!\n"); + exit(-1); + } +} + +void WVMDrmPluginTest::TestCheckValidRights(IDrmEngine *plugin, String8 &url) +{ + cout << "WVDrmPluginTest::TestCheckValidRights url=" << url << endl; + + if (plugin->checkRightsStatus(0, url, Action::DEFAULT) != RightsStatus::RIGHTS_VALID) { + fprintf(stderr, "checkValidRights default action failed!\n"); + exit(-1); + } + + if (plugin->checkRightsStatus(0, url, Action::PLAY) != RightsStatus::RIGHTS_VALID) { + fprintf(stderr, "checkValidRights play action failed!\n"); + exit(-1); + } +} + +void WVMDrmPluginTest::TestGetConstraints(IDrmEngine *plugin, String8 &url) +{ + cout << "WVDrmPluginTest::TestGetConstraints url=" << url << endl; + + DrmConstraints *constraints; + constraints = plugin->getConstraints(0, &url, Action::PLAY); + if (constraints == NULL) { + fprintf(stderr, "getConstraints returned NULL constraints!\n"); + exit(-1); + } + + if (constraints->getCount() != 3) { + fprintf(stderr, "getConstraints returned unexpected count!\n"); + exit(-1); + } + + if (constraints->get(DrmConstraints::LICENSE_START_TIME) == "") { + fprintf(stderr, "getConstraints returned unexpected count!\n"); + exit(-1); + } + + if (constraints->get(DrmConstraints::LICENSE_AVAILABLE_TIME) == "") { + fprintf(stderr, "getConstraints returned unexpected count!\n"); + exit(-1); + } + + if (constraints->get(DrmConstraints::LICENSE_EXPIRY_TIME) == "") { + fprintf(stderr, "getConstraints returned unexpected count!\n"); + exit(-1); + } + + delete constraints; +} + +void WVMDrmPluginTest::TestRemoveRights(IDrmEngine *plugin, String8 &url) +{ + cout << "WVDrmPluginTest::TestRemoveRights url=" << url << endl; + + status_t status = plugin->removeRights(0, url); + if (status != DRM_NO_ERROR) { + fprintf(stderr, "removeRights returned error: %d!\n", (int)status); + exit(-1); + } +} + +void WVMDrmPluginTest::TestRemoveAllRights(IDrmEngine *plugin) +{ + cout << "WVDrmPluginTest::TestRemoveAllRights" << endl; + + status_t status = plugin->removeAllRights(0); + if (status != DRM_NO_ERROR) { + fprintf(stderr, "removeAllRights returned error: %d!\n", (int)status); + exit(-1); + } +} + +void WVMDrmPluginTest::TestAsset(IDrmEngine *plugin, String8 &url) +{ + cout << "WVDrmPluginTest::TestAsset url=" << url << endl; + + TestAcquireRights(plugin, url); + TestCheckValidRights(plugin, url); + TestGetConstraints(plugin, url); + TestRemoveRights(plugin, url); + TestCheckRightsNotAcquired(plugin, url); + TestAcquireRights(plugin, url); + TestRemoveAllRights(plugin); + TestCheckRightsNotAcquired(plugin, url); +} + +int main(int argc, char **argv) +{ + // turn off some noisy printing in WVStreamControl + setenv("WV_SILENT", "true", 1); + WVMDrmPluginTest test; + test.Run(); +} diff --git a/proprietary/drmwvmplugin/test/kill.sh b/proprietary/drmwvmplugin/test/kill.sh new file mode 100755 index 00000000..acc7151c --- /dev/null +++ b/proprietary/drmwvmplugin/test/kill.sh @@ -0,0 +1,3 @@ +#!/bin/sh +PID=`adb shell ps | grep test-wvdrmplugin | awk '{print $2}'` +adb shell kill -9 $PID diff --git a/proprietary/streamcontrol/Android.mk b/proprietary/streamcontrol/Android.mk new file mode 100644 index 00000000..5053e7d6 --- /dev/null +++ b/proprietary/streamcontrol/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/proprietary/include/AndroidHooks.h b/proprietary/streamcontrol/include/AndroidHooks.h similarity index 100% rename from proprietary/include/AndroidHooks.h rename to proprietary/streamcontrol/include/AndroidHooks.h diff --git a/proprietary/include/WVDRMPluginAPI.h b/proprietary/streamcontrol/include/WVDRMPluginAPI.h similarity index 100% rename from proprietary/include/WVDRMPluginAPI.h rename to proprietary/streamcontrol/include/WVDRMPluginAPI.h diff --git a/proprietary/include/WVStreamControlAPI.h b/proprietary/streamcontrol/include/WVStreamControlAPI.h similarity index 100% rename from proprietary/include/WVStreamControlAPI.h rename to proprietary/streamcontrol/include/WVStreamControlAPI.h diff --git a/proprietary/lib/Android.mk b/proprietary/streamcontrol/lib/Android.mk similarity index 100% rename from proprietary/lib/Android.mk rename to proprietary/streamcontrol/lib/Android.mk diff --git a/proprietary/lib/libWVStreamControlAPI.so b/proprietary/streamcontrol/lib/libWVStreamControlAPI.so similarity index 100% rename from proprietary/lib/libWVStreamControlAPI.so rename to proprietary/streamcontrol/lib/libWVStreamControlAPI.so diff --git a/proprietary/lib/libwvdrm.so b/proprietary/streamcontrol/lib/libwvdrm.so similarity index 100% rename from proprietary/lib/libwvdrm.so rename to proprietary/streamcontrol/lib/libwvdrm.so diff --git a/proprietary/test/Android.mk b/proprietary/streamcontrol/test/Android.mk similarity index 90% rename from proprietary/test/Android.mk rename to proprietary/streamcontrol/test/Android.mk index 5dd11b29..5f36472f 100644 --- a/proprietary/test/Android.mk +++ b/proprietary/streamcontrol/test/Android.mk @@ -8,7 +8,7 @@ LOCAL_C_INCLUDES+= \ bionic \ vendor/widevine/proprietary/include \ external/stlport/stlport \ - frameworks/base/media/libwvm/include + vendor/widevine/proprietary/streamcontrol/include LOCAL_SHARED_LIBRARIES := \ libstlport \ diff --git a/proprietary/test/TestPlayer.cpp b/proprietary/streamcontrol/test/TestPlayer.cpp similarity index 100% rename from proprietary/test/TestPlayer.cpp rename to proprietary/streamcontrol/test/TestPlayer.cpp diff --git a/proprietary/test/kill.sh b/proprietary/streamcontrol/test/kill.sh similarity index 100% rename from proprietary/test/kill.sh rename to proprietary/streamcontrol/test/kill.sh diff --git a/proprietary/wvm/Android.mk b/proprietary/wvm/Android.mk new file mode 100644 index 00000000..68f8259b --- /dev/null +++ b/proprietary/wvm/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +ifeq ($(TARGET_ARCH),arm) + +LOCAL_SRC_FILES:= \ + WVMLogging.cpp \ + WVMExtractorImpl.cpp \ + WVMFileSource.cpp \ + WVMMediaSource.cpp + +LOCAL_C_INCLUDES:= \ + bionic \ + bionic/libstdc++ \ + external/stlport/stlport \ + vendor/widevine/proprietary/streamcontrol/include \ + vendor/widevine/proprietary/wvm/include + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + libstagefright \ + libWVStreamControlAPI \ + libdrmframework \ + libcutils \ + liblog \ + libutils \ + libz + +LOCAL_MODULE := libwvm + +LOCAL_MODULE_TAGS := optional + +LOCAL_PRELINK_MODULE := false + +include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/proprietary/wvm/WVMExtractorImpl.cpp b/proprietary/wvm/WVMExtractorImpl.cpp new file mode 100644 index 00000000..c97d8600 --- /dev/null +++ b/proprietary/wvm/WVMExtractorImpl.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2011 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_TAG "WVMExtractorImpl" +#include + +#include "WVMExtractorImpl.h" +#include "WVMMediaSource.h" +#include "WVMFileSource.h" +#include "WVMLogging.h" +#include "WVStreamControlAPI.h" +#include "media/stagefright/MediaErrors.h" +#include "media/stagefright/MediaDefs.h" +#include "drm/DrmManagerClient.h" +#include "AndroidHooks.h" + +#define AES_BLOCK_SIZE 16 + + +using namespace android; + +static DecryptHandle *sDecryptHandle; +static DrmManagerClient *sDrmManagerClient; + + +// Android integration callout hooks +static void HandleEcmCallout(char *ecm, unsigned long size) +{ + DrmBuffer buf(ecm, size); + if (sDrmManagerClient != NULL) { + sDrmManagerClient->initializeDecryptUnit(sDecryptHandle, 0, &buf); + } +} + +static int HandleDecryptCallout(char *in, char *out, int length, char *iv) +{ + int status = -1; + + if (sDrmManagerClient != NULL) { + DrmBuffer encryptedDrmBuffer(in, length); + DrmBuffer ivBuffer(iv, length); + + DrmBuffer decryptedDrmBuffer(out, length); + DrmBuffer *decryptedDrmBufferPtr = &decryptedDrmBuffer; + + char ivout[AES_BLOCK_SIZE]; + if (in && length) + memcpy(ivout, in + length - AES_BLOCK_SIZE, AES_BLOCK_SIZE); + + status = sDrmManagerClient->decrypt(sDecryptHandle, 0, + &encryptedDrmBuffer, &decryptedDrmBufferPtr, + &ivBuffer); + + if (iv) + memcpy(iv, ivout, AES_BLOCK_SIZE); + } + + return status; +} + + +namespace android { + +// DLL entry - construct an extractor and return it +MediaExtractor *GetInstance(sp dataSource) { + return new WVMExtractorImpl(dataSource); +} + +WVMExtractorImpl::WVMExtractorImpl(sp dataSource) + : mFileMetaData(new MetaData()), + mDataSource(dataSource), + mHaveMetaData(false), + mSession(NULL), + mSetupStatus(OK) +{ + dataSource->getDrmInfo(&sDecryptHandle, &sDrmManagerClient); + + // Set up callouts + AndroidSetLogCallout(android_printbuf); + AndroidSetEcmCallout(HandleEcmCallout); + AndroidSetDecryptCallout(HandleDecryptCallout); + + if (sDecryptHandle != NULL) { + if (sDecryptHandle->status != RightsStatus::RIGHTS_VALID) { + mSetupStatus = ERROR_NO_LICENSE; + } + } else + mSetupStatus = ERROR_NO_LICENSE; + + WVCredentials credentials; + + WVStatus result = WV_Initialize(NULL); + if (result != WV_Status_OK) { + LOGE("WV_Initialize returned status %d\n", result); + mSetupStatus = ERROR_IO; + } else { + // Enable for debugging HTTP messages + // WV_SetLogging(WV_Logging_HTTP); + + if (dataSource->getUri().size() == 0) { + // No URI supplied, pull data from the data source + mFileSource = new WVMFileSource(dataSource); + result = WV_Setup(mSession, mFileSource.get(), + "RAW/RAW/RAW;destination=getdata", credentials, + WV_OutputFormat_ES, kStreamCacheSize); + } else { + // Use the URI + result = WV_Setup(mSession, dataSource->getUri().string(), + "RAW/RAW/RAW;destination=getdata", credentials, + WV_OutputFormat_ES, kStreamCacheSize); + } + if (result != WV_Status_OK) { + LOGE("WV_Setup returned status %d in WVMMediaSource::start\n", result); + mSetupStatus = ERROR_IO; + } + } + + WV_SetWarningToErrorMS(5000); +} + +WVMExtractorImpl::~WVMExtractorImpl() { +} + +// +// Configure metadata for video and audio sources +// +status_t WVMExtractorImpl::readMetaData() +{ + if (mHaveMetaData) + return OK; + + if (mSetupStatus != OK) + return mSetupStatus; + + // Get Video Configuration + WVVideoType videoType; + unsigned short videoStreamID; + unsigned short videoProfile; + unsigned short level; + unsigned short width, height; + float aspect, frameRate; + unsigned long bitRate; + + WVStatus result = WV_Info_GetVideoConfiguration(mSession, &videoType, &videoStreamID, + &videoProfile, &level, &width, &height, + &aspect, &frameRate, &bitRate); + if (result != WV_Status_OK) + return ERROR_MALFORMED; + + // Get Audio Configuration + WVAudioType audioType; + unsigned short audioStreamID; + unsigned short audioProfile; + unsigned short numChannels; + unsigned long sampleRate; + + result = WV_Info_GetAudioConfiguration(mSession, &audioType, &audioStreamID, &audioProfile, + &numChannels, &sampleRate, &bitRate); + if (result != WV_Status_OK) + return ERROR_MALFORMED; + + std::string durationString = WV_Info_GetDuration(mSession, "sec"); + if (durationString == "") { + return ERROR_MALFORMED; + } + + int64_t duration = (int64_t)(strtod(durationString.c_str(), NULL) * 1000000); + + sp audioMetaData = new MetaData(); + sp videoMetaData = new MetaData(); + + audioMetaData->setInt64(kKeyDuration, duration); + videoMetaData->setInt64(kKeyDuration, duration); + + switch(videoType) { + case WV_VideoType_H264: + videoMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + break; + default: + LOGE("Invalid WV video type %d, expected H264C\n", audioType); + break; + } + + switch(audioType) { + case WV_AudioType_AAC: + audioMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + break; + default: + LOGE("Invalid WV audio type %d, expected AAC\n", audioType); + break; + } + + audioMetaData->setInt32(kKeyTrackID, audioStreamID); + videoMetaData->setInt32(kKeyTrackID, videoStreamID); + + audioMetaData->setInt32(kKeyChannelCount, numChannels); + audioMetaData->setInt32(kKeySampleRate, sampleRate); + + videoMetaData->setInt32(kKeyWidth, width); + videoMetaData->setInt32(kKeyHeight, height); + + status_t status; + + status = readAVCCMetaData(videoMetaData); + if (status != OK) + return status; + + status = readESDSMetaData(audioMetaData); + if (status != OK) + return status; + + mAudioSource = new WVMMediaSource(mSession, WV_EsSelector_Audio, audioMetaData); + mVideoSource = new WVMMediaSource(mSession, WV_EsSelector_Video, videoMetaData); + + // Since the WVExtractor goes away soon after this, we delegate ownership of some resources + // to the constructed media source + if (mFileSource.get()) + mVideoSource->delegateFileSource(mFileSource); + + mVideoSource->delegateDataSource(mDataSource); + + mFileMetaData->setCString(kKeyMIMEType, "video/mp4"); + + mHaveMetaData = true; + + return OK; +} + +status_t WVMExtractorImpl::readAVCCMetaData(sp videoMetaData) +{ + WVStatus result; + + const unsigned char *config; + unsigned long size; + int limit = 50; + do { + size_t bytesRead; + bool auStart; + unsigned long long dts, pts; + unsigned char buf[1]; + size_t bufSize = 0; + + // + // In order to get the codec config data, we need to have the WVMK + // pull some video data. But we can't use it yet, so just request 0 bytes. + // + (void)WV_GetEsData(mSession, WV_EsSelector_Video, buf, bufSize, + bytesRead, auStart, dts, pts); + + result = WV_Info_GetCodecConfig(mSession, WV_CodecConfigType_AVCC, config, size); + if (result != WV_Status_OK) + usleep(100); + } while (result == WV_Status_Warning_Not_Available && limit-- > 0); + + if (result != WV_Status_OK) { + LOGE("WV_Info_GetCodecConfig AVCC returned error %d\n", result); + return ERROR_IO; + } + +#if 0 + char *filename = "/data/wvm/avcc"; + FILE *f = fopen(filename, "w"); + if (!f) + LOGD("Failed to open %s", filename); + else { + fwrite(config, size, 1, f); + fclose(f); + } +#endif + +#if 0 + unsigned char mp4_force[] = + { 0x01, 0x42, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40, + 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda, + 0x14, 0x2a, 0x48, 0x01, 0x00, 0x04, 0x68, 0xcb, 0x8d, 0x48 } ; + unsigned char wvm_force[] = + { 0x01, 0x42, 0x80, 0x1e, 0xff, 0xe1, 0x00, 0x1c, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40, + 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda, + 0x14, 0x2a, 0x48, 0x00, 0x01, 0x00, 0x05, 0x68, 0xcb, 0x8d, 0x48, 0x00 } ; + unsigned char wvm_force_no_zero[] = + { 0x01, 0x42, 0x80, 0x1e, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40, + 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda, + 0x14, 0x2a, 0x48, 0x01, 0x00, 0x04, 0x68, 0xcb, 0x8d, 0x48 } ; + unsigned char wvm_force_modprof[] = + { 0x01, 0x42, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x1c, 0x67, 0x42, 0x80, 0x1e, 0x96, 0x52, 0x01, 0x40, + 0x5f, 0xf3, 0x60, 0x2a, 0x10, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x09, 0xda, + 0x14, 0x2a, 0x48, 0x00, 0x01, 0x00, 0x05, 0x68, 0xcb, 0x8d, 0x48, 0x00 } ; + + videoMetaData->setData(kKeyAVCC, kTypeAVCC, wvm_force_no_zero, sizeof(wvm_force_no_zero)); +#else + videoMetaData->setData(kKeyAVCC, kTypeAVCC, config, size); +#endif + return OK; +} + +status_t WVMExtractorImpl::readESDSMetaData(sp audioMetaData) +{ + WVStatus result; + + const unsigned char *config; + unsigned long size; + int limit = 50; + do { + size_t bytesRead; + bool auStart; + unsigned long long dts, pts; + unsigned char buf[1]; + size_t bufSize = 0; + + // + // In order to get the codec config data, we need to have the WVMK + // pull some audio data. But we can't use it yet, so just request 0 bytes. + // + (void)WV_GetEsData(mSession, WV_EsSelector_Audio, buf, bufSize, + bytesRead, auStart, dts, pts); + + result = WV_Info_GetCodecConfig(mSession, WV_CodecConfigType_ESDS, config, size); + if (result != WV_Status_OK) + usleep(100); + } while (result == WV_Status_Warning_Not_Available && limit-- > 0); + + if (result != WV_Status_OK) { + LOGE("WV_Info_GetCodecConfig ESDS returned error %d\n", result); + return ERROR_IO; + } + +#if 0 + char *filename = "/data/wvm/esds"; + FILE *f = fopen(filename, "w"); + if (!f) + LOGD("Failed to open %s", filename); + else { + fwrite(config, size, 1, f); + fclose(f); + } +#endif + audioMetaData->setData(kKeyESDS, kTypeESDS, config, size); + return OK; +} + +size_t WVMExtractorImpl::countTracks() { + status_t err; + if ((err = readMetaData()) != OK) { + return 0; + } + + return 2; // 1 audio + 1 video +} + +sp WVMExtractorImpl::getTrack(size_t index) +{ + status_t err; + if ((err = readMetaData()) != OK) { + return NULL; + } + + sp result; + + switch(index) { + case 0: + result = mVideoSource; + break; + case 1: + result = mAudioSource; + break; + default: + break; + } + return result; +} + + +sp WVMExtractorImpl::getTrackMetaData(size_t index, uint32_t flags) +{ + status_t err; + if ((err = readMetaData()) != OK) { + return NULL; + } + + sp result; + switch(index) { + case 0: + if (mVideoSource != NULL) + result = mVideoSource->getFormat(); + break; + case 1: + if (mAudioSource != NULL) + result = mAudioSource->getFormat(); + break; + default: + break; + } + return result; +} + +sp WVMExtractorImpl::getMetaData() { + status_t err; + if ((err = readMetaData()) != OK) { + return new MetaData; + } + + return mFileMetaData; +} + +} // namespace android + diff --git a/proprietary/wvm/WVMFileSource.cpp b/proprietary/wvm/WVMFileSource.cpp new file mode 100644 index 00000000..3da21ff5 --- /dev/null +++ b/proprietary/wvm/WVMFileSource.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 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_TAG "WVMFileSource" +#include + +#include "WVMFileSource.h" +#include "media/stagefright/MediaErrors.h" +#include "media/stagefright/MediaDefs.h" +#include "media/stagefright/MediaDebug.h" + +namespace android { + + +WVMFileSource::WVMFileSource(sp &dataSource) + : mDataSource(dataSource), + mOffset(0) +{ +} + +unsigned long long WVMFileSource::GetSize() +{ + off64_t size; + mDataSource->getSize(&size); + return size; +} + +unsigned long long WVMFileSource::GetOffset() +{ + return mOffset; +} + +void WVMFileSource::Seek(unsigned long long offset) +{ + mOffset = offset; +} + +size_t WVMFileSource::Read(size_t amount, unsigned char *buffer) +{ + size_t result = mDataSource->readAt(mOffset, buffer, amount); + +#if 0 + // debug code - log packets to files + char filename[32]; + static int counter = 0; + sprintf(filename, "/data/wv/buf%d", counter++); + FILE *f = fopen(filename, "w"); + if (!f) + LOGE("WVMFileSource: can't open %s", filename); + else { + fwrite(buffer, amount, 1, f); + fclose(f); + } + LOGD("WVMFileSource::Read(%d bytes to buf=%p)", amount, buffer); +#endif + + mOffset += result; + return result; +} + +} // namespace android diff --git a/proprietary/wvm/WVMLogging.cpp b/proprietary/wvm/WVMLogging.cpp new file mode 100644 index 00000000..fd478abc --- /dev/null +++ b/proprietary/wvm/WVMLogging.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2011 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_TAG "WVMLogging" +#include +#include "WVMLogging.h" + +// Connect Widevine debug logging into Android logging + +void android_printbuf(const char *buf) +{ + LOGD("%s", buf); +} diff --git a/proprietary/wvm/WVMMediaSource.cpp b/proprietary/wvm/WVMMediaSource.cpp new file mode 100644 index 00000000..71ea2c0c --- /dev/null +++ b/proprietary/wvm/WVMMediaSource.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2011 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_TAG "WVMMediaSource" +#include + +#include "WVMMediaSource.h" +#include "WVMFileSource.h" +#include "media/stagefright/MediaErrors.h" +#include "media/stagefright/MediaDefs.h" +#include "media/stagefright/MediaDebug.h" + +namespace android { + +extern DrmManagerClient *gDrmManagerClient; + +WVMMediaSource::WVMMediaSource(WVSession *session, WVEsSelector esSelector, + const sp &metaData) + : mSession(session), + mESSelector(esSelector), + mTrackMetaData(metaData), + mStarted(false), + mGroup(NULL), + mDts(0), + mPts(0) +{ +} + +// Since the WVMExtractor lifetime is short, we delegate ownership of some resources +// to the media source, which cleans them up after when the media source is destroyed + +void WVMMediaSource::delegateFileSource(sp fileSource) +{ + mFileSource = fileSource; +} + +void WVMMediaSource::delegateDataSource(sp dataSource) +{ + mDataSource = dataSource; +} + +void WVMMediaSource::allocBufferGroup() +{ + if (mGroup) + delete mGroup; + + mGroup = new MediaBufferGroup; + + size_t size; + if (mESSelector == WV_EsSelector_Video) + size = 256 * 1024; + else + size = 64 * 1024; + + mGroup->add_buffer(new MediaBuffer(size)); +} + + + +status_t WVMMediaSource::start(MetaData *) +{ + //LOGD("WVMMediaSource::start()"); + Mutex::Autolock autoLock(mLock); + + CHECK(!mStarted); + + allocBufferGroup(); + + mStarted = true; + + // Let video stream control play/pause + if (mESSelector == WV_EsSelector_Video) { + float speed; + WVStatus result = WV_Play(mSession, 1.0, &speed, "now-"); + if (result != WV_Status_OK) { + LOGE("WV_Play returned status %d in WVMMediaSource::start\n", result); + } + } + return OK; +} + + +status_t WVMMediaSource::stop() +{ + //LOGD("WVMMediaSource::stop()"); + Mutex::Autolock autoLock(mLock); + + CHECK(mStarted); + + // Let video stream control play/pause + if (mESSelector == WV_EsSelector_Video) { + WVStatus result = WV_Pause(mSession, "now"); + if (result != WV_Status_OK) { + LOGE("WV_Pause returned status %d in WVMMediaSource::stop\n", result); + } + } + + delete mGroup; + mGroup = NULL; + + mStarted = false; + + return OK; +} + +sp WVMMediaSource::getFormat() +{ + Mutex::Autolock autoLock(mLock); + return mTrackMetaData; +} + +std::string usecToNPT(int64_t time) +{ + unsigned hours = (unsigned)(time / (60LL * 60 * 1000000)); + time -= (int64_t)hours * 60 * 60 * 1000000; + unsigned mins = (unsigned)(time / (60 * 1000000)); + time -= (int64_t)mins * 60 * 1000000; + float secs = (float)time / 1000000; + char buf[32]; + sprintf(buf, "%d:%d:%f", hours, mins, secs); + return std::string(buf); +} + +status_t WVMMediaSource::read(MediaBuffer **buffer, const ReadOptions *options) +{ + Mutex::Autolock autoLock(mLock); + + CHECK(mStarted); + + *buffer = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + // Let video stream control seek + if (mESSelector == WV_EsSelector_Video) { + float scaleUsed; + std::string when = usecToNPT(seekTimeUs) + std::string("-"); + WVStatus result = WV_Play(mSession, 1.0, &scaleUsed, when ); + if (result != WV_Status_OK) { + LOGE("WV_Play returned status %d in WVMMediaSource::read\n", result); + return ERROR_IO; + } + } + } + + MediaBuffer *mediaBuf; + + status_t err = mGroup->acquire_buffer(&mediaBuf); + + if (err != OK) { + CHECK_EQ(mediaBuf, NULL); + return err; + } + + size_t bytesRead; + bool auStart; + size_t offset = 0; + + // Pull full access units. Since we aren't sure how big they might be, + // start with initial buffer size, then allocate a larger buffer if we + // get more a number of bytes equal to the full buffer size and go back + // for the rest. Only loop in this case, usually it's one pass through. + + while (true) { + size_t size = mediaBuf->size() - offset; + + WVStatus result = WV_GetEsData(mSession, mESSelector, (uint8_t *)mediaBuf->data() + offset, + size, bytesRead, auStart, mDts, mPts); + if (result == WV_Status_End_Of_Media) { + mediaBuf->release(); + return ERROR_END_OF_STREAM; + } else if (result != WV_Status_OK) { + if (result != WV_Status_Warning_Need_Key && + result != WV_Status_Warning_Download_Stalled) + { + LOGE("WV_GetEsData returned ERROR %d in WVMMediaSource::read\n", result); + mediaBuf->release(); + return ERROR_IO; + } else + LOGW("WV_GetEsData returned WARNING %d in WVMMediaSource::read\n", result); + } + + + if (bytesRead == 0) { + // Didn't get anything, sleep a bit so we don't hog the CPU then + // try again. + usleep(10000); + continue; + } + + if (offset + bytesRead < mediaBuf->size()) + break; + + //LOGD("Resizing..."); + + // This buffer is too small, allocate a larger buffer twice the size + // and copy the data from the current buffer into the first part of + // the new buffer, then set offset to where the next read should go. + + MediaBuffer *newBuffer = new MediaBuffer(mediaBuf->size() * 2); + newBuffer->add_ref(); + + memcpy(newBuffer->data(), mediaBuf->data(), mediaBuf->size()); + offset = mediaBuf->size(); + + mGroup->add_buffer(newBuffer); + + mediaBuf->release(); + mediaBuf = newBuffer; + } + +#define PCR_HZ 90000 + int64_t keyTime = (int64_t)mDts * 1000000 / PCR_HZ; + + mediaBuf->meta_data()->clear(); + mediaBuf->meta_data()->setInt64(kKeyTime, keyTime); + + mediaBuf->set_range(0, bytesRead + offset); + +#if 0 + // debug code - log packets to files + char filename[32]; + static int acounter = 0, vcounter = 0; + if (mESSelector == WV_EsSelector_Video) + sprintf(filename, "/data/wvm/v%d", vcounter++); + else + sprintf(filename, "/data/wvm/a%d", acounter++); + + FILE *f = fopen(filename, "w"); + if (!f) + LOGE("WVMFileSource: can't open %s", filename); + else { + fwrite(mediaBuf->data(), bytesRead + offset, 1, f); + fclose(f); + } + LOGD("WVMMediaSource::read writing (%d bytes to %s)", bytesRead + offset, filename); +#endif + +#if 0 + LOGD("[%p] %s set range_length=%d, get range_length=%d kKeyTime=%lld\n", mediaBuf, + (mESSelector == WV_EsSelector_Video ? "video" : "audio"), + bytesRead + offset, mediaBuf->range_length(), keyTime); +#endif + + *buffer = mediaBuf; + + return OK; +} + +WVMMediaSource::~WVMMediaSource() +{ + //LOGD("WVMMediaSource::~WVMMediaSource()"); + + if (mStarted) { + stop(); + } + + if (mESSelector == WV_EsSelector_Video) { + if (mSession != NULL) + WV_Teardown(mSession); + } +} + +} // namespace android diff --git a/proprietary/wvm/include/WVMExtractorImpl.h b/proprietary/wvm/include/WVMExtractorImpl.h new file mode 100644 index 00000000..ba04c1d1 --- /dev/null +++ b/proprietary/wvm/include/WVMExtractorImpl.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 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 WVMEXTRACTOR_H_ + +#define WVMEXTRACTOR_H_ + +#include "AndroidConfig.h" +#include "WVStreamControlAPI.h" +#include +#include +#include + + +// DLL entry - given a data source, instantiate a WVMExtractor object + +namespace android { + +MediaExtractor *GetInstance(sp dataSource); + + +class WVMMediaSource; +class WVMFileSource; + +class WVMExtractorImpl : public MediaExtractor { +public: + WVMExtractorImpl(sp dataSource); + + virtual size_t countTracks(); + virtual sp getTrack(size_t index); + virtual sp getTrackMetaData(size_t index, uint32_t flags); + + virtual sp getMetaData(); + +protected: + virtual ~WVMExtractorImpl(); + +private: + status_t readAVCCMetaData(sp videoMetaData); + status_t readESDSMetaData(sp audioMetaData); + + sp mAudioSource; + sp mVideoSource; + sp mFileMetaData; + sp mFileSource; + sp mDataSource; + + bool mHaveMetaData; + + WVSession *mSession; + + status_t mSetupStatus; + + status_t readMetaData(); + + const static size_t kStreamCacheSize = 10 * 1024 * 1024; + + WVMExtractorImpl(const WVMExtractorImpl &); + WVMExtractorImpl &operator=(const WVMExtractorImpl &); +}; + +} // namespace android + +#endif // WVMEXTRACTOR_H_ diff --git a/proprietary/wvm/include/WVMFileSource.h b/proprietary/wvm/include/WVMFileSource.h new file mode 100644 index 00000000..da51da2a --- /dev/null +++ b/proprietary/wvm/include/WVMFileSource.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 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 WVFILE_SOURCE_H_ +#define WVFILE_SOURCE_H_ + +#include "AndroidConfig.h" +#include "WVStreamControlAPI.h" +#include +#include + +// +// Supports reading data from local file descriptor instead of URI-based streaming +// as we normally do. +// + +namespace android { + +class WVMFileSource : public WVFileSource, public RefBase { +public: + WVMFileSource(sp &dataSource); + virtual ~WVMFileSource() {} + + virtual unsigned long long GetSize(); + virtual unsigned long long GetOffset(); + + virtual void Seek(unsigned long long offset); + virtual size_t Read(size_t amount, unsigned char *buffer); + +private: + sp mDataSource; + unsigned long long mOffset; +}; + +}; + + +#endif diff --git a/proprietary/wvm/include/WVMLogging.h b/proprietary/wvm/include/WVMLogging.h new file mode 100644 index 00000000..1e46c7ad --- /dev/null +++ b/proprietary/wvm/include/WVMLogging.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 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 __WVMLOGGING_H__ +#define __WVMLOGGING_H__ + +void android_printbuf(const char *buf); + +#endif diff --git a/proprietary/wvm/include/WVMMediaSource.h b/proprietary/wvm/include/WVMMediaSource.h new file mode 100644 index 00000000..5f311b48 --- /dev/null +++ b/proprietary/wvm/include/WVMMediaSource.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 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 WVMMEDIA_SOURCE_H_ +#define WVMMEDIA_SOURCE_H_ + +#include "AndroidConfig.h" +#include "WVStreamControlAPI.h" +#include +#include +#include +#include +#include + + +namespace android { + +class WVMFileSource; + +class WVMMediaSource : public MediaSource { +public: + WVMMediaSource(WVSession *session, WVEsSelector esSelector, + const sp &metaData); + + void delegateFileSource(sp fileSource); + void delegateDataSource(sp dataSource); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + + virtual sp getFormat(); + + virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL); + +protected: + virtual ~WVMMediaSource(); + +private: + Mutex mLock; + + WVSession *mSession; + WVEsSelector mESSelector; // indicates audio vs. video + + sp mTrackMetaData; + + bool mStarted; + + MediaBufferGroup *mGroup; + + unsigned long long mDts; + unsigned long long mPts; + + sp mFileSource; + sp mDataSource; + + void allocBufferGroup(); + + WVMMediaSource(const WVMMediaSource &); + WVMMediaSource &operator=(const WVMMediaSource &); +}; + +} // namespace android + +#endif // WVMMEDIA_SOURCE_H_ diff --git a/proprietary/wvm/test/Android.mk b/proprietary/wvm/test/Android.mk new file mode 100644 index 00000000..dc237def --- /dev/null +++ b/proprietary/wvm/test/Android.mk @@ -0,0 +1,27 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Testlibwvm.cpp + +LOCAL_C_INCLUDES+= \ + bionic \ + vendor/widevine/proprietary/include \ + external/stlport/stlport \ + frameworks/base/media/libstagefright + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + libdrmframework \ + libstagefright \ + liblog \ + libutils \ + libz \ + libdl + +LOCAL_MODULE:=test-libwvm + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + diff --git a/proprietary/wvm/test/Testlibwvm.cpp b/proprietary/wvm/test/Testlibwvm.cpp new file mode 100644 index 00000000..2cd6040a --- /dev/null +++ b/proprietary/wvm/test/Testlibwvm.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 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. + */ + +#include +#include + +#include "include/WVMExtractor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace android; +using namespace std; + +class TestLibWVM +{ +public: + TestLibWVM() {} + ~TestLibWVM() {} + + // Tests + void Load(); +}; + +DrmManagerClient* gDrmManagerClient; + +// This test just confirms that there are no unresolved symbols in libwvm and we +// can locate the entry point. + +void TestLibWVM::Load() +{ + cout << "TestLibWVM::Load" << endl; + + const char *path = "/system/lib/libwvm.so"; + void *handle = dlopen(path, RTLD_NOW); + if (handle == NULL) { + fprintf(stderr, "Can't open plugin: %s\n", path); + exit(-1); + } + + typedef MediaExtractor *(*GetInstanceFunc)(sp); + GetInstanceFunc getInstanceFunc = + (GetInstanceFunc) dlsym(handle, + "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE"); + + // Basic test - just see if we can instantiate the object and call a method + if (getInstanceFunc) { + LOGD("Found GetInstanceFunc"); + } else { + LOGE("Failed to locate GetInstance in libwvm.so"); + } + +// dlclose(handle); + printf("Test successful!\n"); + exit(0); +} + +int main(int argc, char **argv) +{ + TestLibWVM test; + test.Load(); +}