Removing widevine classic

bug:30505334
Change-Id: I312f83c0a13b28d0d98bb8003cabc09a63717ce3
This commit is contained in:
Jeff Tinker
2016-10-24 14:03:34 -07:00
parent f248bbb9ee
commit 97c2c74556
115 changed files with 0 additions and 9379 deletions

View File

@@ -1,7 +0,0 @@
# widevine prebuilts only available for ARM
# To build this dir you must define BOARD_WIDEVINE_OEMCRYPTO_LEVEL in the board config.
ifdef BOARD_WIDEVINE_OEMCRYPTO_LEVEL
include $(call all-subdir-makefiles)
endif # BOARD_WIDEVINE_OEMCRYPTO_LEVEL

View File

@@ -1,23 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
ifeq ($(BOARD_WIDEVINE_OEMCRYPTO_LEVEL),1)
LOCAL_CFLAGS := -DREQUIRE_SECURE_BUFFERS
endif
LOCAL_SRC_FILES := \
WVCryptoPlugin.cpp
LOCAL_C_INCLUDES := \
$(TOP)/vendor/widevine/proprietary/wvm/include \
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE:= libwvdecryptcommon
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_LIBRARIES := libcrypto_static
# Not 64-bit compatible, WVCryptoPlugin::decrypt stores a pointer in a uint32
LOCAL_MULTILIB := 32
include $(BUILD_STATIC_LIBRARY)

View File

@@ -1,254 +0,0 @@
/*
* Copyright (C) 2012 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 "wv_crypto_plugin"
#include <cutils/properties.h>
#include <utils/Log.h>
#include <string.h>
#include <openssl/md5.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
#include "WVCryptoPlugin.h"
#ifdef REQUIRE_SECURE_BUFFERS
#include <OEMCrypto_L1.h>
#endif
android::CryptoFactory *createCryptoFactory() {
return new android::WVCryptoFactory;
}
namespace android {
// static
const uint8_t WVCryptoFactory::kUUIDWidevine[16] = {
0xED,0xEF,0x8B,0xA9,0x79,0xD6,0x4A,0xCE,
0xA3,0xC8,0x27,0xDC,0xD5,0x1D,0x21,0xED
};
WVCryptoPlugin::WVCryptoPlugin(const void *data, size_t size)
: mInitCheck(NO_INIT)
{
memset(mEncKey, 0, sizeof(mEncKey));
// not using data at this time, require
// size to be zero.
if (size > 0) {
mInitCheck = -EINVAL;
} else {
mInitCheck = OK;
#ifdef REQUIRE_SECURE_BUFFERS
OEMCryptoResult res = OEMCrypto_Initialize();
if (res != OEMCrypto_SUCCESS) {
ALOGE("OEMCrypto_Initialize failed: %d", res);
mInitCheck = -EINVAL;
}
#endif
}
}
WVCryptoPlugin::~WVCryptoPlugin() {
#ifdef REQUIRE_SECURE_BUFFERS
if (mInitCheck == OK) {
OEMCryptoResult res = OEMCrypto_Terminate();
if (res != OEMCrypto_SUCCESS) {
ALOGW("OEMCrypto_Terminate failed: %d", res);
}
}
#endif
}
status_t WVCryptoPlugin::initCheck() const {
return mInitCheck;
}
bool WVCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const {
#ifdef REQUIRE_SECURE_BUFFERS
return !strncasecmp(mime, "video/", 6);
#else
return false;
#endif
}
// Returns negative values for error code and
// positive values for the size of decrypted data, which can be larger
// than the input length.
ssize_t WVCryptoPlugin::decrypt(
bool secure,
const uint8_t key[16],
const uint8_t iv[16],
Mode mode,
const Pattern &pattern,
const void *srcPtr,
const SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg) {
Mutex::Autolock autoLock(mLock);
CHECK(mode == kMode_Unencrypted || mode == kMode_AES_WV);
size_t srcOffset = 0;
size_t dstOffset = 0;
for (size_t i = 0; i < numSubSamples; ++i) {
const SubSample &ss = subSamples[i];
size_t srcSize;
if (mode == kMode_Unencrypted) {
srcSize = ss.mNumBytesOfClearData;
CHECK_EQ(ss.mNumBytesOfEncryptedData, 0u);
} else {
CHECK_EQ(ss.mNumBytesOfClearData, 0u);
srcSize = ss.mNumBytesOfEncryptedData;
}
//ALOGD("size[%d]=%d", i, srcSize);
if (srcSize == 0) {
continue; // segment size is zero, do not call decrypt
}
#ifdef REQUIRE_SECURE_BUFFERS
// decrypt using OEMCrypto API, used for L1 devices
OEMCrypto_UINT32 dstSize = srcSize;
OEMCryptoResult res;
OEMCrypto_UINT8 _iv[16];
const OEMCrypto_UINT8 *iv = NULL;
if (mode != kMode_Unencrypted) {
memset(_iv, 0, sizeof(_iv));
iv = _iv;
}
if (secure) {
//ALOGD("calling DecryptVideo, size=%d", srcSize);
res = OEMCrypto_DecryptVideo(
iv,
(const OEMCrypto_UINT8 *)srcPtr + srcOffset,
srcSize,
(OEMCrypto_UINT32)dstPtr,
dstOffset,
&dstSize);
} else {
//ALOGD("calling DecryptAudio: size=%d", srcSize);
res = OEMCrypto_DecryptAudio(
iv,
(const OEMCrypto_UINT8 *)srcPtr + srcOffset,
srcSize,
(OEMCrypto_UINT8 *)dstPtr + dstOffset,
&dstSize);
}
if (res != OEMCrypto_SUCCESS) {
ALOGE("decrypt result: %d", res);
return -EINVAL;
}
dstOffset += dstSize;
#else
if (mode == kMode_Unencrypted) {
memcpy((char *)dstPtr + dstOffset, (char *)srcPtr + srcOffset, srcSize);
} else {
status_t status = decryptSW(key, (uint8_t *)dstPtr + dstOffset,
(const uint8_t *)srcPtr + srcOffset, srcSize);
if (status != OK) {
ALOGE("decryptSW returned %d", status);
return status;
}
}
dstOffset += srcSize;
#endif
srcOffset += srcSize;
} // for each subsample
return static_cast<ssize_t>(dstOffset);
}
// SW AES CTS decrypt, used only for L3 devices
status_t WVCryptoPlugin::decryptSW(const uint8_t *key, uint8_t *out,
const uint8_t *in, size_t length)
{
#ifndef REQUIRE_SECURE_BUFFERS
unsigned char iv[kAES128BlockSize] = {0};
if (memcmp(key, mEncKey, sizeof(mEncKey)) != 0) {
// key has changed, recompute mAesKey from key
uint8_t hash[MD5_DIGEST_LENGTH];
char value[PROPERTY_VALUE_MAX] = {0};
char seed[] = "34985woeirsdlkfjxc";
property_get("ro.serialno", value, NULL);
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, (uint8_t *)seed, sizeof(seed));
MD5_Update(&ctx, (uint8_t *)value, strlen(value));
MD5_Final(hash, &ctx);
AES_KEY aesKey;
if (AES_set_decrypt_key(hash, sizeof(hash) * 8, &aesKey) == 0) {
uint8_t clearKey[kAES128BlockSize];
AES_ecb_encrypt(key, clearKey, &aesKey, 0);
if (AES_set_decrypt_key(clearKey, sizeof(hash) * 8, &mAesKey) == 0) {
memcpy(mEncKey, key, sizeof(mEncKey));
} else {
return -EINVAL;
}
} else {
return -EINVAL;
}
}
size_t k, r = length % kAES128BlockSize;
if (r) {
k = length - r - kAES128BlockSize;
} else {
k = length;
}
AES_cbc_encrypt(in, out, k, &mAesKey, iv, 0);
if (r) {
// cipher text stealing - Schneier Figure 9.5 p 196
unsigned char peniv[kAES128BlockSize] = {0};
memcpy(peniv, in + k + kAES128BlockSize, r);
AES_cbc_encrypt(in + k, out + k, kAES128BlockSize, &mAesKey, peniv, 0);
// exchange the final plaintext and ciphertext
for (size_t i = 0; i < r; i++) {
*(out + k + kAES128BlockSize + i) = *(out + k + i);
*(out + k + i) = *(in + k + kAES128BlockSize + i);
}
AES_cbc_encrypt(out + k, out + k, kAES128BlockSize, &mAesKey, iv, 0);
}
#endif
return OK;
}
} // namespace android

View File

@@ -1,97 +0,0 @@
/*
* Copyright (C) 2012 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 WV_CRYPTO_PLUGIN_H_
#define WV_CRYPTO_PLUGIN_H_
#include <media/hardware/CryptoAPI.h>
#include <utils/threads.h>
#include <openssl/aes.h>
namespace android {
struct WVCryptoPlugin : public CryptoPlugin {
WVCryptoPlugin(const void *data, size_t size);
virtual ~WVCryptoPlugin();
const static size_t kAES128BlockSize = 16;
status_t initCheck() const;
virtual bool requiresSecureDecoderComponent(const char *mime) const;
virtual ssize_t decrypt(
bool secure,
const uint8_t key[kAES128BlockSize],
const uint8_t iv[kAES128BlockSize],
Mode mode,
const Pattern &pattern,
const void *srcPtr,
const SubSample *subSamples, size_t numSubSamples,
void *dstPtr,
AString *errorDetailMsg);
private:
status_t decryptSW(const uint8_t *key, uint8_t *out, const uint8_t *in, size_t length);
Mutex mLock;
status_t mInitCheck;
AES_KEY mAesKey;
uint8_t mEncKey[kAES128BlockSize];
WVCryptoPlugin(const WVCryptoPlugin &);
WVCryptoPlugin &operator=(const WVCryptoPlugin &);
};
struct WVCryptoFactory : public CryptoFactory {
static const uint8_t kUUIDWidevine[16];
virtual bool isCryptoSchemeSupported(
const uint8_t uuid[16]) const {
return !memcmp(uuid, kUUIDWidevine, 16);
}
virtual status_t createPlugin(
const uint8_t uuid[16], const void *data, size_t size,
CryptoPlugin **out) {
*out = NULL;
if (memcmp(uuid, kUUIDWidevine, 16)) {
return -ENOENT;
}
WVCryptoPlugin *plugin = new WVCryptoPlugin(data, size);
status_t err;
if ((err = plugin->initCheck()) != OK) {
delete plugin;
plugin = NULL;
return err;
}
*out = plugin;
return OK;
}
};
} // namespace android
#endif // WV_CRYPTO_PLUGIN_H_

View File

@@ -1,16 +0,0 @@
#
# To be included by platform-specific vendor Android.mk to build
# Widevine wvm static library. Sets up includes and defines the core libraries
# required.
#
include $(TOP)/vendor/widevine/proprietary/wvm/common.mk
ifndef BOARD_WIDEVINE_OEMCRYPTO_LEVEL
$(error BOARD_WIDEVINE_OEMCRYPTO_LEVEL not defined!)
endif
LOCAL_WHOLE_STATIC_LIBRARIES := \
libwvdecryptcommon
LOCAL_STATIC_LIBRARIES := \
liboemcrypto

View File

@@ -1,51 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
########################
# Feature file for clients to look up widevine drm plug-in
include $(CLEAR_VARS)
LOCAL_MODULE := com.google.widevine.software.drm.xml
LOCAL_SRC_FILES := $(LOCAL_MODULE)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := widevine
LOCAL_MODULE_CLASS := ETC
# This will install the file in /system/etc/permissions
#
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
include $(BUILD_PREBUILT)
########################
# Dummy library used to indicate availability of widevine drm
include $(CLEAR_VARS)
LOCAL_MODULE := com.google.widevine.software.drm
LOCAL_SRC_FILES := src/StubLib.java
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := widevine
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
include $(BUILD_JAVA_LIBRARY)
########################
include $(CLEAR_VARS)
include $(TOP)/vendor/widevine/proprietary/drmwvmplugin/common.mk
LOCAL_SRC_FILES:= \
src/WVMDrmPlugin.cpp \
src/WVMLogging.cpp
LOCAL_CFLAGS += -Wno-unused-parameter -Werror
LOCAL_MODULE := libdrmwvmcommon
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := $(WIDEVINE_SUPPORTED_ARCH)
include $(BUILD_STATIC_LIBRARY)
# invoke Android.mk files in subdirs
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2011 Google, Inc. All Rights Reserved
-->
<permissions>
<library name="com.google.widevine.software.drm"
file="/system/framework/com.google.widevine.software.drm.jar"/>
</permissions>

View File

@@ -1,8 +0,0 @@
LOCAL_C_INCLUDES:= \
$(TOP)/vendor/widevine/proprietary/streamcontrol/include \
$(TOP)/vendor/widevine/proprietary/drmwvmplugin/include \
$(TOP)/frameworks/av/drm/libdrmframework/include \
$(TOP)/frameworks/av/drm/libdrmframework/plugins/common/include \
$(TOP)/frameworks/av/include
LOCAL_C_INCLUDES_x86 += $(TOP)/system/core/include/arch/linux-x86

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2011 Widevine Technologies, Inc., All Rights Reserved
*
* Declarations for Widevine DRM Plugin API
*/
#ifndef __WVMDRMPLUGIN_API_H__
#define __WVMDRMPLUGIN_API_H__
#include <string>
#include "WVStreamControlAPI.h"
class WVDRMPluginAPI {
public:
virtual ~WVDRMPluginAPI() {}
enum {
RIGHTS_VALID,
RIGHTS_INVALID,
RIGHTS_EXPIRED,
RIGHTS_NOT_ACQUIRED
};
enum {
PLAYBACK_START,
PLAYBACK_STOP,
PLAYBACK_PAUSE,
PLAYBACK_INVALID
};
// provisionedFlags
enum {
DEVICE_IS_PROVISIONED,
DEVICE_IS_NOT_PROVISIONED,
DEVICE_IS_PROVISIONED_SD_ONLY
};
static const int PlaybackMode_Default = 0;
static const int PlaybackMode_Streaming = 1;
static const int PlaybackMode_Offline = 2;
static const int PlaybackMode_Any = PlaybackMode_Streaming |
PlaybackMode_Offline;
static WVDRMPluginAPI *create();
static void destroy(WVDRMPluginAPI *plugin);
virtual bool OpenSession(const char *uri) = 0;
virtual void CloseSession() = 0;
virtual bool IsSupportedMediaType(const char *uri) = 0;
virtual bool RegisterDrmInfo(std::string &portal, std::string &dsPath) = 0;
virtual bool RegisterDrmInfo(std::string &portal, std::string &dsPath, uint32_t *status) = 0;
virtual bool UnregisterDrmInfo(std::string &portal, std::string &dsPath) = 0;
virtual bool AcquireDrmInfo(std::string &assetPath, int assetOpenFd, WVCredentials &credentials,
std::string &dsPath, const std::string &systemIdStr,
const std::string &assetIdStr,
const std::string &keyIdStr,
uint32_t *systemId, uint32_t *assetId,
uint32_t *keyId) = 0;
virtual bool ProcessDrmInfo(std::string &assetPath, int playbackMode) = 0;
virtual int CheckRightsStatus(std::string &path) = 0;
virtual bool GetConstraints(std::string &path, uint32_t *timeSincePlayback,
uint32_t *timeRemaining,
uint32_t *licenseDuration, std::string &lastError,
bool &allowOffline, bool &allowStreaming,
bool &denyHD) = 0;
virtual bool SetPlaybackStatus(int playbackStatus, off64_t position) = 0;
virtual bool RemoveRights(std::string &path) = 0;
virtual bool RemoveAllRights() = 0;
virtual bool Prepare(char *data, int len) = 0;
virtual int Operate(char *in, int inLength, char *out, int outLength, char *iv) = 0;
virtual std::string GetVersion() const = 0;
enum EventType {
EventType_AcquireDrmInfoFailed,
EventType_ProcessDrmInfoFailed,
EventType_RightsInstalled,
EventType_RightsRemoved,
EventType_HeartbeatServer,
EventType_HeartbeatPeriod,
EventType_AssetId,
EventType_DeviceId,
EventType_StreamId,
EventType_UserData
};
enum EventDestination {
EventDestination_JavaAPI,
EventDestination_MediaPlayer
};
// Returns true if event sent, false if no handler
typedef bool (*EventHandler)(EventType type, EventDestination destination,
const std::string &path);
virtual void SetEventHandler(EventHandler handler) = 0;
protected:
// use create factory method, don't construct directly
WVDRMPluginAPI() {}
};
#endif

View File

@@ -1,140 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
#ifndef __WVMDRMPLUGIN_H__
#define __WVMDRMPLUGIN_H__
#include <DrmEngineBase.h>
#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 fd);
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, int64_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) {
return DRM_ERROR_CANNOT_HANDLE;
}
status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle,
int fd, off64_t offset, off64_t length,
const char* mime);
status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle,
const char* uri) {
return DRM_ERROR_CANNOT_HANDLE;
}
status_t onOpenDecryptSession(int uniqueId, DecryptHandle *decryptHandle,
const char* uri,
const char* mime);
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);
class Listener {
public:
Listener() : mListener(NULL), mUniqueId(-1) {}
Listener(IDrmEngine::OnInfoListener *listener, int uniqueId)
: mListener(listener), mUniqueId(uniqueId) {};
IDrmEngine::OnInfoListener *GetListener() const {return mListener;}
int GetUniqueId() const {return mUniqueId;}
private:
IDrmEngine::OnInfoListener *mListener;
int mUniqueId;
};
enum MessageType {
MessageType_HeartbeatServer = 4000,
MessageType_HeartbeatPeriod = 4001,
MessageType_AssetId = 4002,
MessageType_DeviceId = 4003,
MessageType_StreamId = 4004,
MessageType_UserData = 4005
};
private:
bool isSupportedMimeType(const char* mime);
static bool SendEvent(WVDRMPluginAPI::EventType code, WVDRMPluginAPI::EventDestination dest,
const std::string &path);
static Vector<Listener> *sNativeListeners;
static Vector<Listener> *sJavaAPIListeners;
static const char *sFileExtensions[];
WVDRMPluginAPI *mDrmPluginImpl;
};
};
#endif /* __WVMDRMPLUGIN__ */

View File

@@ -1,10 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
#ifndef __WVMLOGGING_H__
#define __WVMLOGGING_H__
void android_printbuf(const char *buf);
#endif

View File

@@ -1 +0,0 @@
include $(call all-subdir-makefiles)

View File

@@ -1,38 +0,0 @@
LOCAL_PATH:= $(call my-dir)
ifneq ($(BOARD_USES_GENERIC_WIDEVINE),false)
#########################################################################
# libwvdrm_L?.so
include $(CLEAR_VARS)
LOCAL_MODULE := libwvdrm_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_STRIP_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := widevine
LOCAL_MODULE_TARGET_ARCH := arm
LOCAL_MULTILIB := 32
include $(BUILD_PREBUILT)
#########################################################################
# libwvocs_L?.a
include $(CLEAR_VARS)
LOCAL_MODULE := libwvocs_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE_SUFFIX := .a
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := arm
include $(BUILD_PREBUILT)
endif

View File

@@ -1,38 +0,0 @@
LOCAL_PATH:= $(call my-dir)
ifneq ($(BOARD_USES_GENERIC_WIDEVINE),false)
#########################################################################
# libwvdrm_L?.so
include $(CLEAR_VARS)
LOCAL_MODULE := libwvdrm_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_STRIP_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := widevine
LOCAL_MODULE_TARGET_ARCH := mips
LOCAL_MULTILIB := 32
include $(BUILD_PREBUILT)
#########################################################################
# libwvocs_L?.a
include $(CLEAR_VARS)
LOCAL_MODULE := libwvocs_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE_SUFFIX := .a
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := mips
include $(BUILD_PREBUILT)
endif

View File

@@ -1,31 +0,0 @@
LOCAL_PATH:= $(call my-dir)
#########################################################################
# libwvdrm_L?.so
include $(CLEAR_VARS)
LOCAL_MODULE := libwvdrm_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_STRIP_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := x86
include $(BUILD_PREBUILT)
#########################################################################
# libwvocs_L?.a
include $(CLEAR_VARS)
LOCAL_MODULE := libwvocs_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE_SUFFIX := .a
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := x86
include $(BUILD_PREBUILT)

View File

@@ -1,25 +0,0 @@
#
# To be included by platform-specific vendor Android.mk to build
# Widevine DRM plugin. Sets up includes and defines the core libraries
# required to build the plugin.
#
include $(TOP)/vendor/widevine/proprietary/drmwvmplugin/common.mk
ifndef BOARD_WIDEVINE_OEMCRYPTO_LEVEL
$(error BOARD_WIDEVINE_OEMCRYPTO_LEVEL not defined!)
endif
LOCAL_WHOLE_STATIC_LIBRARIES := \
libdrmframeworkcommon \
libdrmwvmcommon \
libwvocs_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_SHARED_LIBRARIES := \
libbinder \
libutils \
libcutils \
liblog \
libz \
libwvdrm_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL) \
libWVStreamControlAPI_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL) \
libdl

View File

@@ -1,4 +0,0 @@
package com.google.widevine.software.drm;
class StubLib {
}

View File

@@ -1,913 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "WVMDrmPlugIn"
#include <utils/Log.h>
#include <vector>
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
#include <drm/DrmInfo.h>
#include <drm/DrmInfoEvent.h>
#include <drm/DrmInfoStatus.h>
#include <drm/DrmConvertedStatus.h>
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmMetadata.h>
#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() {
_ah006(android_printbuf);
libocs_setup();
return new WVMDrmPlugin();
}
// This extern "C" is mandatory to be managed by TPlugInManager
extern "C" void destroy(IDrmEngine* pPlugIn) {
delete pPlugIn;
}
// Needed for event callout from implementation object
Vector<WVMDrmPlugin::Listener> *WVMDrmPlugin::sNativeListeners = NULL;
Vector<WVMDrmPlugin::Listener> *WVMDrmPlugin::sJavaAPIListeners = NULL;
// File extensions that Widevine can handle.
// Note: the empty extension is needed because some proxy servers will strip the extension.
const char *WVMDrmPlugin::sFileExtensions[] = {".wvm", ".m3u8", ".vob", ".smil", "", NULL};
WVMDrmPlugin::WVMDrmPlugin()
: DrmEngineBase(),
mDrmPluginImpl(WVDRMPluginAPI::create())
{
sNativeListeners = new Vector<WVMDrmPlugin::Listener>();
sJavaAPIListeners = new Vector<WVMDrmPlugin::Listener>();
mDrmPluginImpl->SetEventHandler(&SendEvent);
}
WVMDrmPlugin::~WVMDrmPlugin() {
delete sNativeListeners;
delete sJavaAPIListeners;
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) {
//ALOGD("WVMDrmPlugin::onInitialize : %d", uniqueId);
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) {
//ALOGD("WVMDrmPlugin::onTerminate : %d", uniqueId);
for (size_t i = 0; i < sNativeListeners->size(); i++) {
if ((*sNativeListeners)[i].GetUniqueId() == uniqueId) {
sNativeListeners->removeAt(i);
break;
}
}
for (size_t i = 0; i < sJavaAPIListeners->size(); i++) {
if ((*sJavaAPIListeners)[i].GetUniqueId() == uniqueId) {
sJavaAPIListeners->removeAt(i);
break;
}
}
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. uniqueId is a random
* number generated in the DRM service. If the DrmManagerClient
* is created in native code, uniqueId will be a number ranged
* from 0x1000 to 0x1fff. If it comes from Java code, the uniqueId
* will be a number ranged from 0x00 to 0xfff. So bit 0x1000 in
* uniqueId could be used in DRM plugins to differentiate native
* OnInfoListener and Java OnInfoListener.
* @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) {
//ALOGD("WVMDrmPlugin::onSetOnInfoListener : add %d", uniqueId);
Listener newListener = Listener(const_cast<IDrmEngine::OnInfoListener *>(infoListener), uniqueId);
bool found = false;
const int nativeUniqueIdFlag = 0x1000;
if (uniqueId & nativeUniqueIdFlag) {
// Replace old listener for this id if it exists
for (size_t i = 0; i < sNativeListeners->size(); i++) {
if ((*sNativeListeners)[i].GetUniqueId() == uniqueId) {
sNativeListeners->replaceAt(newListener, i);
found = true;
break;
}
}
if (!found)
sNativeListeners->push(newListener);
} else {
// Replace old listener for this id if it exists
for (size_t i = 0; i < sJavaAPIListeners->size(); i++) {
if ((*sJavaAPIListeners)[i].GetUniqueId() == uniqueId) {
sJavaAPIListeners->replaceAt(newListener, i);
found = true;
break;
}
}
if (!found)
sJavaAPIListeners->push(newListener);
}
return DRM_NO_ERROR;
}
bool WVMDrmPlugin::SendEvent(WVDRMPluginAPI::EventType type,
WVDRMPluginAPI::EventDestination destination,
const std::string &msg)
{
int code = -1;
bool result = false;
switch(type) {
case WVDRMPluginAPI::EventType_AcquireDrmInfoFailed:
code = DrmInfoEvent::TYPE_ACQUIRE_DRM_INFO_FAILED;
break;
case WVDRMPluginAPI::EventType_ProcessDrmInfoFailed:
code = DrmInfoEvent::TYPE_PROCESS_DRM_INFO_FAILED;
break;
case WVDRMPluginAPI::EventType_RightsInstalled:
code = DrmInfoEvent::TYPE_RIGHTS_INSTALLED;
break;
case WVDRMPluginAPI::EventType_RightsRemoved:
code = DrmInfoEvent::TYPE_RIGHTS_REMOVED;
break;
case WVDRMPluginAPI::EventType_HeartbeatServer:
code = MessageType_HeartbeatServer;
break;
case WVDRMPluginAPI::EventType_HeartbeatPeriod:
code = MessageType_HeartbeatPeriod;
break;
case WVDRMPluginAPI::EventType_AssetId:
code = MessageType_AssetId;
break;
case WVDRMPluginAPI::EventType_DeviceId:
code = MessageType_DeviceId;
break;
case WVDRMPluginAPI::EventType_StreamId:
code = MessageType_StreamId;
break;
case WVDRMPluginAPI::EventType_UserData:
code = MessageType_UserData;
break;
default:
break;
}
String8 message = String8(msg.c_str());
if (destination == WVDRMPluginAPI::EventDestination_JavaAPI) {
for (size_t i = 0; i < sJavaAPIListeners->size(); i++) {
DrmInfoEvent event((*sJavaAPIListeners)[i].GetUniqueId(), code, message);
//ALOGD("WVMDrmPlugin::SendEvent [Java]: uniqueId=%d type=%d, code=%d, msg=%s",
// (*sJavaAPIListeners)[i].GetUniqueId(), type, code, msg.c_str());
(*sJavaAPIListeners)[i].GetListener()->onInfo(event);
}
result = true;
} else if (destination == WVDRMPluginAPI::EventDestination_MediaPlayer) {
for (size_t i = 0; i < sNativeListeners->size(); i++) {
DrmInfoEvent event((*sNativeListeners)[i].GetUniqueId(), code, message);
//ALOGD("WVMDrmPlugin::SendEvent [Native]: uniqueId=%d type=%d, code=%d, msg=%s",
// (*sNativeListeners)[i].GetUniqueId(), type, code, msg.c_str());
(*sNativeListeners)[i].GetListener()->onInfo(event);
}
result = true;
}
return result;
}
/**
* 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) {
//ALOGD("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) ) {
ALOGE("onAcquireDrmInfo: Empty asset path or portal string, must specify both");
return NULL;
}
// for local files, app may provide the FD of the open file.
int assetOpenFd = atol(drmInfoRequest->get(String8("FileDescriptorKey")).string());
std::string assetDbPath = drmInfoRequest->get(String8("WVAssetDBPathKey")).string();
//ALOGV("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();
string licenseTypeStr = drmInfoRequest->get(String8("WVLicenseTypeKey")).string();
uint32_t systemId, assetId, keyId;
if (!mDrmPluginImpl->AcquireDrmInfo(assetPath, assetOpenFd, 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
// Do not propagate FileDescriptorKey into the newDrmInfo object
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()));
drmInfo->put(String8("WVLicenseTypeKey"), String8(licenseTypeStr.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: {
// creates a data store object per each portal
std::string assetDbPath = drmInfoRequest->get(String8("WVAssetDBPathKey")).string();
std::string portal = drmInfoRequest->get(String8("WVPortalKey")).string();
uint32_t drmInfoRequestStatus = 0;
if (portal.size() == 0) {
ALOGE("onAcquireDrmInfo: Must specify portal string for registration operations");
return NULL;
}
if (drmInfoRequest->getInfoType()==DrmInfoRequest::TYPE_REGISTRATION_INFO) {
if (!mDrmPluginImpl->RegisterDrmInfo(portal, assetDbPath, &drmInfoRequestStatus)) {
ALOGE("onAcquireDrmInfo: RegisterDrmInfo failed");
return NULL;
}
} else {
if (!mDrmPluginImpl->UnregisterDrmInfo(portal, assetDbPath)) {
ALOGE("onAcquireDrmInfo: UnregisterDrmInfo failed");
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());
if (drmInfoRequest->getInfoType()==DrmInfoRequest::TYPE_REGISTRATION_INFO) {
char buffer[16];
sprintf(buffer, "%lu", (unsigned long)drmInfoRequestStatus);
drmInfo->put(String8("WVDrmInfoRequestStatusKey"), String8(buffer));
drmInfo->put(String8("WVDrmInfoRequestVersionKey"),
String8(mDrmPluginImpl->GetVersion().c_str()));
}
break;
}
case DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO: {
ALOGE("onAcquireDrmInfo: Unsupported DrmInfoRequest type %d",
drmInfoRequest->getInfoType());
break;
}
default: {
ALOGE("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) {
//ALOGD("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();
int playbackMode = atol(drmInfo->get(String8("WVLicenseTypeKey")).string());
if (mDrmPluginImpl->ProcessDrmInfo(assetPath, playbackMode))
status = DrmInfoStatus::STATUS_OK;
} else if ((drmInfo->getInfoType() == DrmInfoRequest::TYPE_REGISTRATION_INFO) ||
(drmInfo->getInfoType() == DrmInfoRequest::TYPE_UNREGISTRATION_INFO)) {
status = DrmInfoStatus::STATUS_OK;
} else {
ALOGE("onProcessDrmInfo : drmInfo type %d not supported", drmInfo->getInfoType());
}
} else {
ALOGE("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)
{
//ALOGD("WVMDrmPlugin::onGetConstraints : %d", uniqueId);
if ( (Action::DEFAULT != action) && (Action::PLAY != action) ) {
ALOGE("onGetConstraints : action %d not supported", action);
return NULL;
}
uint32_t licenseDuration = 0;
uint32_t timeSincePlayback = 0;
uint32_t timeRemaining = 0;
std::string lastError;
bool allowOffline;
bool allowStreaming;
bool denyHD;
std::string assetPath(path->string());
bool isValid = mDrmPluginImpl->GetConstraints(assetPath, &timeSincePlayback, &timeRemaining,
&licenseDuration, lastError, allowOffline,
allowStreaming, denyHD);
DrmConstraints* drmConstraints = new DrmConstraints();
String8 key = String8("WVLastErrorKey");
drmConstraints->put(&key, lastError.c_str());
if (isValid) {
char charValue[16]; // max uint32 + 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);
key = String8("WVLicenseTypeKey");
sprintf(charValue, "%u", (allowStreaming ? 1 : 0) | (allowOffline ? 2 : 0));
drmConstraints->put(&key, charValue);
key = String8("WVLicensedResolutionKey");
sprintf(charValue, "%u", (denyHD ? 1 : 2));
drmConstraints->put(&key, 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) {
//ALOGD("WVMDrmPlugin::onGetSupportInfo : %d", uniqueId);
DrmSupportInfo* drmSupportInfo = new DrmSupportInfo();
// Add mimetype's
drmSupportInfo->addMimeType(String8("video/wvm"));
// Add File Suffixes
for (int i=0; sFileExtensions[i]; i++) {
drmSupportInfo->addFileSuffix(String8(sFileExtensions[i]));
}
// 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) {
//ALOGD("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) {
//ALOGD("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) {
//ALOGD("WVMDrmPlugin::canHandle('%s') ", path.string());
String8 extension = path.getPathExtension();
extension.toLower();
for (int i=0; sFileExtensions[i]; i++) {
if (String8(sFileExtensions[i]) == extension) {
return true;
}
}
return false;
}
/**
* 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, int fd) {
//ALOGD("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) {
//ALOGD("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) {
//ALOGD("WVMDrmPlugin::onCheckRightsStatus() : %d", uniqueId);
if ( (Action::DEFAULT != action) && (Action::PLAY != action) ) {
ALOGE("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) {
//ALOGD("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, int64_t position) {
//ALOGD("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) {
//ALOGD("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) {
//ALOGD("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) {
//ALOGD("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
* @param[in] mime Mime type 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, const char* mime)
{
ALOGV("onOpenDecryptSession: id=%d,fd=%d", uniqueId, fd);
if (!isSupportedMimeType(mime)) {
return DRM_ERROR_CANNOT_HANDLE;
}
// For efficiency, we rely on the WVMExtractor's sniff result instead of
// setting mimeType and decryptApiType here for the DRMExtractor's sniff.
// WVMExtractor's sniff uses the cached data source for the sniff.
decryptHandle->decryptInfo = NULL;
decryptHandle->status = DRM_NO_ERROR;
if (mDrmPluginImpl->OpenSession(NULL)) {
return DRM_NO_ERROR;
}
return DRM_ERROR_CANNOT_HANDLE;
}
bool WVMDrmPlugin::isSupportedMimeType(const char* mime) {
ALOGV("isSupportedMimeType: mime = %s", mime? mime: "NULL");
return strcasecmp("video/wvm", mime) == 0;
}
/**
* 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
* @param[in] mime Mime type 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,
const char* uri, const char* mime)
{
ALOGV("onOpenDecryptSession: id=%d,uri=%s",uniqueId,uri);
if (!isSupportedMimeType(mime)) {
return DRM_ERROR_CANNOT_HANDLE;
}
// For efficiency, we rely on the WVMExtractor's sniff result instead of
// setting mimeType and decryptApiType here for the DRMExtractor's sniff.
// WVMExtractor's sniff uses the cached data source for the sniff.
status_t result = DRM_ERROR_CANNOT_HANDLE;
if (!uri)
return result;
decryptHandle->decryptInfo = NULL;
decryptHandle->status = DRM_NO_ERROR;
if (mDrmPluginImpl->OpenSession(uri)) {
result = DRM_NO_ERROR;
} else {
//ALOGD("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) {
//ALOGD("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) {
//ALOGD("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)
{
//ALOGD("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;
int status;
status = mDrmPluginImpl->Operate(encBuffer->data, encBuffer->length, (*decBuffer)->data, (*decBuffer)->length, iv);
if (status != WVDRMPluginAPI::RIGHTS_VALID) {
(*decBuffer)->length = 0;
usleep(1000); // prevent spinning
if (status == WVDRMPluginAPI::RIGHTS_NOT_ACQUIRED) {
return DRM_ERROR_NO_LICENSE;
} else if (status == WVDRMPluginAPI::RIGHTS_EXPIRED) {
return DRM_ERROR_LICENSE_EXPIRED;
} else if (status == WVDRMPluginAPI::RIGHTS_INVALID) {
return DRM_ERROR_DECRYPT;
}
}
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) {
//ALOGD("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;
}

View File

@@ -1,14 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
#define LOG_TAG "WVMLogging"
#include <utils/Log.h>
#include "WVMLogging.h"
// Connect Widevine debug logging into Android logging
void android_printbuf(const char *buf)
{
ALOGD("%s", buf);
}

View File

@@ -1,32 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
TestPlugin.cpp \
../src/WVMLogging.cpp
LOCAL_C_INCLUDES+= \
vendor/widevine/proprietary/include \
vendor/widevine/proprietary/drmwvmplugin/include \
vendor/widevine/proprietary/streamcontrol/include \
frameworks/av/drm/libdrmframework/include \
frameworks/av/drm/libdrmframework/plugins/common/include
LOCAL_C_INCLUDES_x86 += $(TOP)/system/core/include/arch/linux-x86
LOCAL_SHARED_LIBRARIES := \
liblog \
libutils \
libz \
libdl
LOCAL_STATIC_LIBRARIES := \
libdrmframeworkcommon
LOCAL_MODULE:=test-wvdrmplugin
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_TARGET_ARCH := $(WIDEVINE_SUPPORTED_ARCH)
include $(BUILD_EXECUTABLE)

View File

@@ -1,332 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <iostream>
#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, bool useOpenFd = false);
void TestRegister(IDrmEngine *plugin);
void TestAcquireRights(IDrmEngine *plugin, String8 &url, int playbackMode,
bool useOpenFd = false);
void TestCheckRightsNotAcquired(IDrmEngine *plugin, String8 &url);
void TestCheckValidRights(IDrmEngine *plugin, String8 &url);
void TestGetConstraints(IDrmEngine *plugin, String8 &url, int playbackMode);
void TestRemoveRights(IDrmEngine *plugin, String8 &url);
void TestRemoveAllRights(IDrmEngine *plugin);
// Tests
void Run();
private:
static const int PlaybackMode_Default = 0;
static const int PlaybackMode_Streaming = 1;
static const int PlaybackMode_Offline = 2;
static const int PlaybackMode_Any = PlaybackMode_Streaming | PlaybackMode_Offline;
};
void WVMDrmPluginTest::Run()
{
cout << "WVDrmPluginTest::Run" << endl;
const char *path = "/vendor/lib/drm/libdrmwvmplugin.so";
void *handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
fprintf(stderr, "Can't open plugin: %s %s\n", path, dlerror());
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);
}
String8 url;
// Remote asset with widevine:// protocol
url = String8("widevine://commondatastorage.googleapis.com/wvmedia/bbb_base_480p_3br_tp.wvm");
TestAsset(plugin, url);
// Local asset using URL syntax
url = String8("file:///sdcard/Widevine/inception_base_360p_single.wvm");
TestAsset(plugin, url);
// Local asset using normal file path
url = String8("/sdcard/Widevine/inception_base_360p_single.wvm");
TestAsset(plugin, url);
// Local asset, but also supply open file descriptor
url = String8("/sdcard/Widevine/inception_base_360p_single.wvm");
TestAsset(plugin, url, true);
// 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::TestRegister(IDrmEngine *plugin)
{
cout << "WVDrmPluginTest::TestRegister" << endl;
String8 mimeType("video/wvm");
DrmInfoRequest registrationInfo(DrmInfoRequest::TYPE_REGISTRATION_INFO, mimeType);
registrationInfo.put(String8("WVPortalKey"), String8("OEM"));
DrmInfo *info = plugin->acquireDrmInfo(0, &registrationInfo);
if (info == NULL) {
fprintf(stderr, "acquireDrmInfo failed!\n");
exit(-1);
}
delete info;
}
void WVMDrmPluginTest::TestAcquireRights(IDrmEngine *plugin, String8 &url,
int playbackMode, bool useOpenFd)
{
cout << "WVDrmPluginTest::TestAcquireRights url=" << url << " mode=" <<
playbackMode << " useOpenFd=" << useOpenFd << endl;
int openFd = -1;
String8 mimeType("video/wvm");
DrmInfoRequest rightsAcquisitionInfo(DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, mimeType);
rightsAcquisitionInfo.put(String8("WVDRMServerKey"), String8(
"https://license.uat.widevine.com/getlicense/widevine"));
rightsAcquisitionInfo.put(String8("WVAssetURIKey"), url);
if (useOpenFd) {
char openFdStr[16];
openFd = open(url.string(), O_RDONLY);
if (openFd == -1) {
cout << "error opening " << url << ":" << endl;
fprintf(stderr, "Couldn't open local asset file\n");
exit(-1);
}
sprintf(openFdStr, "%lu", (unsigned long)openFd);
rightsAcquisitionInfo.put(String8("FileDescriptorKey"), String8(openFdStr));
}
rightsAcquisitionInfo.put(String8("WVDeviceIDKey"), String8("device1234"));
rightsAcquisitionInfo.put(String8("WVPortalKey"), String8("OEM"));
if (playbackMode) {
char num[4];
sprintf(num, "%d", playbackMode);
rightsAcquisitionInfo.put(String8("WVLicenseTypeKey"), String8(num));
cout << "WVLicenseTypeKey = " << num << endl;
}
DrmInfo *info = plugin->acquireDrmInfo(0, &rightsAcquisitionInfo);
if (info == NULL) {
fprintf(stderr, "acquireDrmInfo failed!\n");
exit(-1);
}
if (useOpenFd && (openFd != -1)) {
close(openFd);
}
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, int playbackMode)
{
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() != 6) {
fprintf(stderr, "getConstraints returned unexpected count: %d!\n", constraints->getCount());
exit(-1);
}
if (constraints->get(DrmConstraints::LICENSE_START_TIME) == "") {
fprintf(stderr, "getConstraints missing start time!\n");
exit(-1);
}
if (constraints->get(DrmConstraints::LICENSE_AVAILABLE_TIME) == "") {
fprintf(stderr, "getConstraints missing available time!\n");
exit(-1);
}
if (constraints->get(DrmConstraints::LICENSE_EXPIRY_TIME) == "") {
fprintf(stderr, "getConstraints missing expiry time!\n");
exit(-1);
}
if (constraints->get(String8("WVLicenseTypeKey")) == "") {
fprintf(stderr, "getConstraints missing license type key!\n");
exit(-1);
}
if (constraints->get(String8("WVLicensedResolutionKey")) == "") {
fprintf(stderr, "getConstraints missing resolution key!\n");
exit(-1);
}
if (constraints->get(String8("WVLastErrorKey")) == "") {
fprintf(stderr, "getConstraints missing last error key!\n");
exit(-1);
}
String8 licenseTypeStr = constraints->get(String8("WVLicenseTypeKey"));
int licenseType = atol(licenseTypeStr.string());
if (licenseType != playbackMode) {
fprintf(stderr, "license type mismatch, expected %d, found %d\n", playbackMode, licenseType);
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,
bool useOpenFd)
{
cout << "WVDrmPluginTest::TestAsset url=" << url <<
" useOpenFd=" << useOpenFd << endl;
TestRegister(plugin);
TestRemoveAllRights(plugin);
TestCheckRightsNotAcquired(plugin, url);
TestAcquireRights(plugin, url, PlaybackMode_Default, useOpenFd);
TestCheckValidRights(plugin, url);
TestGetConstraints(plugin, url, PlaybackMode_Any);
TestRemoveRights(plugin, url);
TestCheckRightsNotAcquired(plugin, url);
TestAcquireRights(plugin, url, PlaybackMode_Offline, useOpenFd);
TestCheckValidRights(plugin, url);
TestGetConstraints(plugin, url, PlaybackMode_Offline);
TestRemoveRights(plugin, url);
TestCheckRightsNotAcquired(plugin, url);
TestAcquireRights(plugin, url, PlaybackMode_Streaming, useOpenFd);
TestCheckValidRights(plugin, url);
TestGetConstraints(plugin, url, PlaybackMode_Streaming);
TestRemoveRights(plugin, url);
TestCheckRightsNotAcquired(plugin, url);
TestAcquireRights(plugin, url, PlaybackMode_Any, useOpenFd);
TestCheckValidRights(plugin, url);
TestGetConstraints(plugin, url, PlaybackMode_Any);
TestRemoveRights(plugin, url);
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();
}

View File

@@ -1,3 +0,0 @@
#!/bin/sh
PID=`adb shell ps | grep test-wvdrmplugin | awk '{print $2}'`
adb shell kill -9 $PID

View File

@@ -1,35 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_DEX_PREOPT := false
LOCAL_PACKAGE_NAME := WidevineSamplePlayer
LOCAL_JAVA_LIBRARIES := \
org.apache.http.legacy \
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-appcompat \
android-support-design
LOCAL_RESOURCE_DIR := \
$(addprefix $(LOCAL_PATH)/, res) \
frameworks/support/design/res \
frameworks/support/v7/appcompat/res \
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.design \
--extra-packages android.support.v7.appcompat \
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
# Use the following include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.widevine.demo">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.NoActionBar">
<activity android:name=".WidevineSamplePlayer"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".VideoPlayerView"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,2 +0,0 @@
# Project target.
target=android-IceCreamSandwich

View File

@@ -1,45 +0,0 @@
README: WidevineSamplePlayer.apk
[Install application:]
Attach a device
In a terminal window:
change directory to vendor/widevine/proprietary/samplePlayer
type the following commands:
mm
adb root
adb remount
adb sync
adb reboot
demo is installed to out/target/product/stingray/system/app/WidevineSamplePlayer.apk
[Download content for offline testing:]
View http://storage.googleapis.com/wvmedia/html/oem.html source for asset names.
For example:
curl -sL "http://seawwws001.cdn.shibboleth.tv/videos/content/starz_demo_ffmpeg_hc_single_720p.wvm" -O
adb shell mkdir -p /sdcard/Widevine
adb push starz_demo_ffmpeg_hc_single_720p.wvm /sdcard/Widevine/
[Running the application:]
On your device, open WidevineDemo.
The Streaming tab should be highlighted, with content available in 480p and 720p encodings.
Select an asset to play.
You should see a player page. To successfully play back content:
-Select Acquire Rights
-Select Play (or click the player window to start playback)
Selecting Show Rights will provide the license time left for playback in the log screen next to the player.
Selecting Remove Rights will stop further attempts to play back from succeeding for more than 10 seconds. You will need to then select Acquire Rights to allow playback again.
Stop video, and go back one screen to the video list.
Select the Downloads tab.
You should see the video we pushed to the device (starz_demo_ffmpeg_hc_single_720p.wvm).
Behavior on this page should be the same as for the Streaming page, with the exception that once you Acquire Rights, you should be able to continue playback of the Downloaded asset offline as long as the rights are valid. You can check the status of your rights with the Show Rights button.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/textView1"
android:text="@string/no_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textSize="24pt"
android:background="@drawable/background3"
android:gravity="center"></TextView>
</LinearLayout>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMaxWidth="0dp"
app:tabGravity="fill"
app:tabMode="fixed">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:id="@+id/textView1" android:text="@string/not_provisioned" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24pt" android:background="@drawable/background3" android:gravity="center"></TextView>
</LinearLayout>

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1"
android:background="@drawable/background3">
<TableRow >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface="serif"
android:text="@string/drm_server"/>
<EditText
android:layout_width="300sp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:id="@+id/drm_server"
/>
</TableRow>
<TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface="serif"
android:text="@string/device_id"/>
<EditText
android:layout_width="300sp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:id="@+id/device_id"
/>
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface="serif"
android:text="@string/portal_id"/>
<EditText
android:layout_width="300sp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:id="@+id/portal_id"
/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:typeface="serif"
android:text="@string/content_page"/>
<EditText
android:layout_width="300sp"
android:layout_height="wrap_content"
android:textSize="12sp"
android:id="@+id/content_page"
/>
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<Button
android:text="@string/save_settings"
android:id="@+id/save_settings"
/>
</TableRow>
</TableLayout>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Widevine Demo</string>
<string name="no_content">No Content Found</string>
<string name="not_provisioned">Device Not Provisioned</string>
<string name="play">Play</string>
<string name="stop">Stop</string>
<string name="mediacodec_mode">MediaCodec Mode</string>
<string name="normal_mode">Normal Mode</string>
<string name="constraints">Constraints</string>
<string name="acquire_rights">Acquire Rights</string>
<string name="remove_rights">Remove Rights</string>
<string name="show_rights">Show Rights</string>
<string name="streaming">Streaming</string>
<string name="downloads">Downloads</string>
<string name="settings">Settings</string>
<string name="content_page">Content Page</string>
<string name="device_id">Device Id</string>
<string name="drm_server">Drm Server</string>
<string name="portal_id">Portal Name</string>
<string name="update_button">Update Button</string>
<string name="no_classic">This device does not support Widevine classic playback."</string>
<string name="save_settings">Save Settings</string>
<string name="next_page">Next Page &gt;&gt;</string>
<string name="previous_page">&lt;&lt; Previous Page</string>
</resources>

View File

@@ -1,65 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
/**
* Describes one asset in the list view
*/
public class AssetDescriptor {
private String mThumbnail;
private String mTitle;
private String mDescription;
private String mUri;
private String mStatus;
public AssetDescriptor copy() {
AssetDescriptor ad = new AssetDescriptor();
ad.setTitle(mTitle);
ad.setThumbnail(mThumbnail);
ad.setDescription(mDescription);
ad.setUri(mUri);
return ad;
}
public String getThumbnail() {
return mThumbnail;
}
public void setThumbnail(String thumbnail) {
mThumbnail = thumbnail;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
public String getDescription() {
return mDescription;
}
public void setDescription(String description) {
mDescription = description;
}
public String getUri() {
return mUri;
}
public void setUri(String uri) {
mUri = uri;
}
public String getStatus() {
return mStatus;
}
public void setStatus(String status) {
mStatus = status;
}
}

View File

@@ -1,300 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public abstract class AssetFragment extends Fragment {
public static final String TAG = "WVM Sample Player";
private int currentPage;
protected ArrayList<AssetsPage> pages;
private View mView;
/** Called when the fragment is first created. */
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = inflater.inflate(R.layout.empty, container, false);
currentPage = 0;
pages = new ArrayList<AssetsPage>();
return mView;
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
if (setUpAssetPages()) {
createView();
}
}
protected abstract boolean setUpAssetPages();
private void createView() {
ViewGroup viewGroup = (ViewGroup)mView;
viewGroup.removeAllViews();
LinearLayout mainLayout = new LinearLayout(getActivity());
ImageView empty = new ImageView(getActivity());
empty.setBackground(getResources().getDrawable(R.drawable.empty,
getActivity().getTheme()));
View[] clips = new View[6];
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither = true;
AssetsPage page = pages.get(currentPage);
for (int i = 0; i < page.getPageCount(); i++) {
AssetItem assetItem = page.getPage(i);
clips[i] = createViewItem(getBitmapFromAssetItem(assetItem),
assetItem.getAssetPath(), assetItem.getTitle());
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT, 1);
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
LinearLayout.LayoutParams paramsMain = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT, 1);
paramsMain.gravity = Gravity.CENTER;
LinearLayout left = new LinearLayout(getActivity());
left.setOrientation(LinearLayout.VERTICAL);
if (clips[0] != null) {
left.addView(clips[0], params);
}
if (clips[3] != null) {
left.addView(clips[3], params);
} else {
left.addView(createEmptyView(), params);
}
LinearLayout middle = new LinearLayout(getActivity());
middle.setOrientation(LinearLayout.VERTICAL);
if (clips[1] != null) {
middle.addView(clips[1], params);
}
if (clips[4] != null) {
middle.addView(clips[4], params);
} else {
middle.addView(createEmptyView(), params);
}
LinearLayout right = new LinearLayout(getActivity());
right.setOrientation(LinearLayout.VERTICAL);
if (clips[2] != null) {
right.addView(clips[2], params);
}
params.gravity = Gravity.BOTTOM;
if (clips[5] != null) {
right.addView(clips[5], params);
} else {
right.addView(createEmptyView(), params);
}
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
LinearLayout body = new LinearLayout(getActivity());
body.addView(left, paramsMain);
body.addView(middle, paramsMain);
body.addView(right, paramsMain);
// Next button listener
View.OnClickListener nextButtonListener = new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click next page: " + currentPage);
if (currentPage < pages.size() - 1) {
currentPage++;
createView();
}
}
};
Button next = new Button(getActivity());
next.setText(R.string.next_page);
next.setTextSize(14);
next.setOnClickListener(nextButtonListener);
// Previous button listener
View.OnClickListener prevButtonListener = new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click prev page: " + currentPage);
if (currentPage > 0) {
currentPage--;
createView();
}
}
};
Button prev = new Button(getActivity());
prev.setText(R.string.previous_page);
prev.setTextSize(14);
prev.setOnClickListener(prevButtonListener);
LinearLayout buttons = new LinearLayout(getActivity());
buttons.addView(prev, params);
buttons.addView(next, params);
body.setBackground(getActivity().getResources().getDrawable(R.drawable.background3,
getActivity().getTheme()));
mainLayout.setOrientation(LinearLayout.VERTICAL);
mainLayout.addView(body, params);
LinearLayout.LayoutParams paramButtons = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
paramButtons.gravity = Gravity.CENTER;
mainLayout.addView(buttons, paramButtons);
viewGroup.addView(mainLayout, paramsMain);
}
private View createEmptyView() {
ImageView empty = new ImageView(getActivity());
empty.setBackground(getResources().getDrawable(R.drawable.empty, getActivity().getTheme()));
TextView emptyText = new TextView(getActivity());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
LinearLayout body = new LinearLayout(getActivity());
body.setOrientation(LinearLayout.VERTICAL);
body.addView(empty, params);
body.addView(emptyText, params);
return body;
}
private View createViewItem(Bitmap image, String path, String title) {
final String assetPath = path;
ClipImageView clip = new ClipImageView(getActivity());
clip.setImageBitmap(image);
clip.setBackgroundColor(0);
// Set the onClick listener for each image
clip.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click Asset path: " + assetPath);
Intent intent = new Intent(getActivity(), VideoPlayerView.class);
intent.putExtra("com.widevine.demo.Path", assetPath);
WidevineDrm drm = new WidevineDrm(getActivity());
if (drm.canPlayClassicWidevine()) {
getActivity().startActivity(intent);
} else {
Toast toast = Toast.makeText(
getActivity(), R.string.no_classic, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
});
TextView text = new TextView(getActivity());
text.setText((title == null) ? path : title);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
params.gravity = Gravity.CENTER;
LinearLayout body = new LinearLayout(getActivity());
body.setOrientation(LinearLayout.VERTICAL);
body.addView(clip, params);
body.addView(text, params);
return body;
}
private Bitmap getBitmapFromAssetItem(AssetItem assetItem) {
Bitmap clipImage = null;
String imageUrl = null;
if (assetItem.getImagePath() == null || assetItem.getImagePath().equals("")) {
if (!assetItem.getAssetPath().contains("http") && !assetItem.getAssetPath().contains("wvplay"))
clipImage = BitmapFactory.decodeResource(getResources(), R.drawable.download_clip);
else
clipImage = BitmapFactory.decodeResource(getResources(), R.drawable.streaming_clip);
} else {
InputStream bitmapStream = null;
if (assetItem.getImagePath().contains("http")) {
imageUrl = assetItem.getImagePath();
if (imageUrl != null) {
ImageHandler imageHandler = new ImageHandler(imageUrl);
imageHandler.start();
try {
imageHandler.join();
} catch (InterruptedException e) {
}
clipImage = imageHandler.getBitmap();
}
} else {
try {
bitmapStream = new FileInputStream(assetItem.getImagePath());
} catch (FileNotFoundException e) {
bitmapStream = null;
}
clipImage = BitmapFactory.decodeStream(bitmapStream);
}
if (clipImage == null) {
clipImage = BitmapFactory.decodeResource(getResources(), R.drawable.streaming_clip);
}
}
return clipImage;
}
}

View File

@@ -1,35 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
public class AssetItem {
private String assetPath;
private String imagePath;
private String title;
public AssetItem() {
assetPath = null;
imagePath = null;
title = null;
}
public AssetItem(String assetPath, String imagePath, String title) {
this.assetPath = assetPath;
this.imagePath = imagePath;
this.title = title;
}
public String getAssetPath() {
return assetPath;
}
public String getImagePath() {
return imagePath;
}
public String getTitle() {
return title;
}
}

View File

@@ -1,29 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.util.ArrayList;
public class AssetsPage {
public static final int MAX_ITEMS = 6;
private ArrayList<AssetItem> assets;
public AssetsPage() {
assets = new ArrayList<AssetItem>();
}
public void addPage(String assetPath, String imagePath, String title) {
assets.add(new AssetItem(assetPath, imagePath, title));
}
public AssetItem getPage(int pageNumber) {
return assets.get(pageNumber);
}
public int getPageCount() {
return assets.size();
}
}

View File

@@ -1,72 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import android.widget.ImageButton;
import android.view.MotionEvent;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
public class ClipImageView extends ImageButton {
private boolean touchedDown;
private float touchX, touchY, radius;
private boolean radiusInc;
private Bitmap selectCircle;
public ClipImageView(Context ctxt) {
super(ctxt);
touchedDown = false;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither = true;
selectCircle = BitmapFactory.decodeResource(getResources(), R.drawable.selection_circle,
options);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
touchX = e.getX();
touchY = e.getY();
radius = 60;
radiusInc = false;
touchedDown = true;
} else if (e.getAction() == MotionEvent.ACTION_UP) {
touchedDown = false;
}
// return true;
return super.onTouchEvent(e);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (touchedDown) {
canvas.drawBitmap(selectCircle, null, new Rect((int) (touchX - radius),
(int) (touchY - radius), (int) (touchX + radius),
(int) (touchY + radius)), null);
if (radiusInc) {
radius += 5;
}
else {
radius -= 5;
}
if (radius >= 60 || radius <= 0) {
radiusInc = !radiusInc;
}
}
invalidate();
}
}

View File

@@ -1,92 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import android.sax.Element;
import android.sax.EndElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.util.Xml;
/**
* Parser for the XML configuration file that defines the assets available to
* play
*/
public class ConfigXMLParser {
private URL mFeedUrl;
private InputStream inputStream;
protected InputStream getInputStream() {
try {
if (inputStream != null)
return inputStream;
else
return mFeedUrl.openConnection().getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public ConfigXMLParser(URL feedUrl) {
mFeedUrl = feedUrl;
}
public ConfigXMLParser(InputStream inputStream) {
this.inputStream = inputStream;
}
public List<AssetDescriptor> parse() {
final AssetDescriptor currentAssetDescriptor = new AssetDescriptor();
RootElement root = new RootElement("widevine");
final List<AssetDescriptor> assetDescriptors = new ArrayList<AssetDescriptor>();
Element assetlist = root.getChild("asset-list");
Element asset = assetlist.getChild("asset");
asset.setEndElementListener(new EndElementListener() {
public void end() {
if (currentAssetDescriptor.getUri().indexOf(".wvm") != -1
|| currentAssetDescriptor.getUri().indexOf(".ts") != -1
|| currentAssetDescriptor.getUri().indexOf(".m3u8") != -1
|| !currentAssetDescriptor.getUri().substring(currentAssetDescriptor
.getUri().lastIndexOf("/")).contains(".")) {
assetDescriptors.add(currentAssetDescriptor.copy());
}
}
});
asset.getChild("title").setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
currentAssetDescriptor.setTitle(body);
}
});
asset.getChild("uri").setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
currentAssetDescriptor.setUri(body);
}
});
asset.getChild("description").setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
currentAssetDescriptor.setDescription(body);
}
});
asset.getChild("thumbnail").setEndTextElementListener(new EndTextElementListener() {
public void end(String body) {
currentAssetDescriptor.setThumbnail(body);
}
});
try {
Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
} catch (Exception e) {
throw new RuntimeException(e);
}
return assetDescriptors;
}
}

View File

@@ -1,71 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import android.content.Context;
import android.widget.Toast;
public class DownloadFragment extends AssetFragment {
private static String TAG = "DownloadActivity";
protected boolean setUpAssetPages() {
pages = new ArrayList<AssetsPage>();
File[] assets = getDownloadedClips();
if (assets != null && assets.length > 0) {
for (int i = 0; i < assets.length;) {
AssetsPage page = new AssetsPage();
for (int j = 0; j < AssetsPage.MAX_ITEMS && i < assets.length; j++, i++) {
page.addPage(assets[i].getAbsolutePath(), null, null);
}
pages.add(page);
}
return true;
} else {
return false;
}
}
private File[] getDownloadedClips() {
File file = new File("/sdcard/Widevine");
String message = null;
if (!file.exists()) {
message = "Warning: /sdcard/Widevine does not exist, create it for downloads";
} else if (!file.isDirectory()) {
message = "Warning: /sdcard/Widevine is not a directory";
} else if (!file.canRead()) {
message = "Warning: unable to read /sdcard/Widevine, check settings->app->permissions->storage";
}
if (message != null) {
Context context = getActivity().getApplicationContext();
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, message, duration);
toast.show();
}
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
File file = new File(dir.getAbsolutePath() + File.separator + name);
if (!file.isDirectory()
&& !name.equals("curl")
&& (name.contains(".wvm") || name.contains(".ts") || name.contains(".mp4") ||
name.contains(".m3u8") | !name.contains(".")))
return true;
else
return false;
}
};
return file.listFiles(filter);
}
}

View File

@@ -1,79 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import android.content.Context;
import android.widget.VideoView;
public class FullScreenVideoView extends VideoView {
private boolean fullscreen;
private int rLeft, rRight, rTop, rBottom, regularHeight, regularWidth;
private int fullScreenWidth, fullScreenHeight;
public FullScreenVideoView(Context context) {
super(context);
fullscreen = false;
regularHeight = 0;
regularWidth = 0;
rBottom = 0;
rRight = 0;
rTop = 0;
rLeft = 0;
fullScreenWidth = 1280;
fullScreenHeight = 800;
}
public void setFullScreenDimensions(int width, int height) {
fullScreenWidth = width;
fullScreenHeight = height;
}
public void setFullScreen(boolean fullscreen) {
this.fullscreen = fullscreen;
this.requestLayout();
}
public boolean getFullScreen() {
return this.fullscreen;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (rLeft == 0 && rRight == 0 && rTop == 0 && rBottom == 0) {
rBottom = bottom;
rRight = right;
rTop = top;
rLeft = left;
}
if (fullscreen) {
super.onLayout(true, left, top, fullScreenWidth, fullScreenHeight);
} else {
if (rLeft == 0 && rRight == 0 && rTop == 0 && rBottom == 0) {
super.onLayout(changed, left, top, right, bottom);
} else {
super.onLayout(changed, rLeft, rTop, rRight, rBottom);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (regularHeight == 0 && regularWidth == 0) {
regularHeight = heightMeasureSpec;
regularWidth = widthMeasureSpec;
}
if (fullscreen) {
this.setMeasuredDimension(fullScreenWidth, fullScreenHeight);
} else {
if (regularHeight == 0 && regularWidth == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
super.onMeasure(regularWidth, regularHeight);
}
}
}
}

View File

@@ -1,112 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
public class HttpParser extends Thread {
private ArrayList<AssetItem> assets;
private String urlString;
private String rootUrl;
public HttpParser(String urlString) {
this.urlString = urlString;
this.assets = new ArrayList<AssetItem>();
this.rootUrl = this.urlString.substring(0, this.urlString.lastIndexOf("/") + 1);
}
public void run() {
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
InputStream reader = response.getEntity().getContent();
StringBuffer buffer = new StringBuffer();
int data = 0;
do {
data = reader.read();
if (data == -1) {
break;
}
buffer.append((char) data);
} while (true);
if (urlString.contains(".htm")) {
parseHtml(buffer.toString());
} else if (urlString.contains(".xml")) {
parseXML(buffer.toString());
}
} catch (MalformedURLException e) {
} catch (IOException e) {
}
}
private void parseXML(String xmlText) {
ConfigXMLParser parser = new ConfigXMLParser(new ByteArrayInputStream(xmlText.getBytes()));
ArrayList<AssetDescriptor> descrs = (ArrayList<AssetDescriptor>) parser.parse();
for (int i = 0; i < descrs.size(); i++) {
AssetDescriptor asset = descrs.get(i);
String imagePath = asset.getThumbnail();
if (!imagePath.contains("http")) {
imagePath = rootUrl + imagePath;
}
assets.add(new AssetItem(asset.getUri(), asset.getThumbnail(), asset.getTitle()));
}
}
private void parseHtml(String htmlText) {
int start = 0;
int end = 0;
while (true) {
String assetPath = null;
String title = null;
String imagePath = null;
start = htmlText.indexOf("href=\"", start);
if (start == -1) {
break;
} else {
start += "href=\"".length();
end = htmlText.indexOf("\"", start);
assetPath = htmlText.substring(start, end);
start = end + 1;
start = htmlText.indexOf("\"", start) + 1;
end = htmlText.indexOf("\"", start);
imagePath = htmlText.substring(start, end);
if (!imagePath.contains("http") && !imagePath.contains("wvplay")) {
imagePath = rootUrl + imagePath;
}
start = htmlText.indexOf("<p>", start) + "<p>".length();
end = htmlText.indexOf("</p>", start);
title = htmlText.substring(start, end);
start = end + 1;
assets.add(new AssetItem(assetPath, imagePath, title));
}
}
}
public ArrayList<AssetItem> getAssets() {
return this.assets;
}
}

View File

@@ -1,56 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.io.IOException;
import java.net.MalformedURLException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class ImageHandler extends Thread {
private boolean scale;
private String imageUrl;
private Bitmap clipImage = null;
public ImageHandler(String imageUrl) {
this.imageUrl = imageUrl;
this.clipImage = null;
}
public void setScale(boolean scale) {
this.scale = scale;
}
public void run() {
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(imageUrl);
HttpResponse response = httpClient.execute(request);
this.clipImage = BitmapFactory.decodeStream(response.getEntity().getContent());
if (scale) {
this.clipImage = Bitmap.createScaledBitmap(this.clipImage, 150, 200, false);
}
} catch (MalformedURLException e) {
this.clipImage = null;
} catch (IOException e) {
this.clipImage = null;
}
}
public Bitmap getBitmap() {
return this.clipImage;
}
}

View File

@@ -1,755 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widevine.demo;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaCrypto;
import android.media.MediaCryptoException;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.MediaController;
import java.io.IOException;
import java.lang.IllegalStateException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.UUID;
class CodecState {
private static final String TAG = "CodecState";
private MediaCodecView mView;
private MediaExtractor mExtractor;
private int mTrackIndex;
private MediaFormat mFormat;
private boolean mSawInputEOS, mSawOutputEOS;
private MediaCodec mCodec;
private MediaFormat mOutputFormat;
private ByteBuffer[] mCodecInputBuffers;
private ByteBuffer[] mCodecOutputBuffers;
private LinkedList<Integer> mAvailableInputBufferIndices;
private LinkedList<Integer> mAvailableOutputBufferIndices;
private LinkedList<MediaCodec.BufferInfo> mAvailableOutputBufferInfos;
private NonBlockingAudioTrack mAudioTrack;
private long mLastMediaTimeUs;
public CodecState(
MediaCodecView view,
MediaExtractor extractor,
int trackIndex,
MediaFormat format,
MediaCodec codec) {
mView = view;
mExtractor = extractor;
mTrackIndex = trackIndex;
mFormat = format;
mSawInputEOS = mSawOutputEOS = false;
mCodec = codec;
mCodec.start();
mCodecInputBuffers = mCodec.getInputBuffers();
mCodecOutputBuffers = mCodec.getOutputBuffers();
mAvailableInputBufferIndices = new LinkedList();
mAvailableOutputBufferIndices = new LinkedList();
mAvailableOutputBufferInfos = new LinkedList();
mLastMediaTimeUs = 0;
}
public void release() {
mCodec.stop();
mCodecInputBuffers = null;
mCodecOutputBuffers = null;
mOutputFormat = null;
mAvailableOutputBufferInfos = null;
mAvailableOutputBufferIndices = null;
mAvailableInputBufferIndices = null;
mCodec.release();
mCodec = null;
if (mAudioTrack != null) {
mAudioTrack.release();
mAudioTrack = null;
}
}
public void start() {
if (mAudioTrack != null) {
mAudioTrack.play();
}
}
public void pause() {
if (mAudioTrack != null) {
mAudioTrack.pause();
}
}
public long getCurrentPositionUs() {
return mLastMediaTimeUs;
}
public void flush() {
mAvailableInputBufferIndices.clear();
mAvailableOutputBufferIndices.clear();
mAvailableOutputBufferInfos.clear();
mSawInputEOS = false;
mSawOutputEOS = false;
if (mAudioTrack != null
&& mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
mAudioTrack.play();
}
mCodec.flush();
}
public void doSomeWork() {
int index = mCodec.dequeueInputBuffer(0 /* timeoutUs */);
if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
mAvailableInputBufferIndices.add(new Integer(index));
}
while (feedInputBuffer()) {}
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
index = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
mOutputFormat = mCodec.getOutputFormat();
onOutputFormatChanged();
} else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
mCodecOutputBuffers = mCodec.getOutputBuffers();
} else if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
mAvailableOutputBufferIndices.add(new Integer(index));
mAvailableOutputBufferInfos.add(info);
}
while (drainOutputBuffer()) {}
}
/** returns true if more input data could be fed */
private boolean feedInputBuffer() {
if (mSawInputEOS || mAvailableInputBufferIndices.isEmpty()) {
return false;
}
int index = mAvailableInputBufferIndices.peekFirst().intValue();
ByteBuffer codecData = mCodecInputBuffers[index];
int trackIndex = mExtractor.getSampleTrackIndex();
if (trackIndex == mTrackIndex) {
int sampleSize =
mExtractor.readSampleData(codecData, 0 /* offset */);
long sampleTime = mExtractor.getSampleTime();
int sampleFlags = mExtractor.getSampleFlags();
try {
if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
mExtractor.getSampleCryptoInfo(info);
mCodec.queueSecureInputBuffer(
index, 0 /* offset */, info, sampleTime, 0 /* flags */);
} else {
mCodec.queueInputBuffer(
index, 0 /* offset */, sampleSize, sampleTime,
0 /* flags */);
}
mAvailableInputBufferIndices.removeFirst();
mExtractor.advance();
} catch (MediaCodec.CryptoException e) {
Log.d(TAG, "CryptoException w/ errorCode "
+ e.getErrorCode() + ", '" + e.getMessage() + "'");
return false;
}
return true;
} else if (trackIndex < 0) {
Log.d(TAG, "saw input EOS on track " + mTrackIndex);
mSawInputEOS = true;
try {
mCodec.queueInputBuffer(
index, 0 /* offset */, 0 /* sampleSize */,
0 /* sampleTime */, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
mAvailableInputBufferIndices.removeFirst();
} catch (MediaCodec.CryptoException e) {
Log.d(TAG, "CryptoException w/ errorCode "
+ e.getErrorCode() + ", '" + e.getMessage() + "'");
}
}
return false;
}
private void onOutputFormatChanged() {
String mime = mOutputFormat.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("audio/")) {
int sampleRate =
mOutputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
int channelCount =
mOutputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount);
mAudioTrack.play();
}
}
/** returns true if more output data could be drained */
private boolean drainOutputBuffer() {
if (mSawOutputEOS || mAvailableOutputBufferIndices.isEmpty()) {
return false;
}
int index = mAvailableOutputBufferIndices.peekFirst().intValue();
MediaCodec.BufferInfo info = mAvailableOutputBufferInfos.peekFirst();
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "saw output EOS on track " + mTrackIndex);
mSawOutputEOS = true;
if (mAudioTrack != null) {
mAudioTrack.stop();
}
return false;
}
long realTimeUs =
mView.getRealTimeUsForMediaTime(info.presentationTimeUs);
long nowUs = mView.getNowUs();
long lateUs = nowUs - realTimeUs;
if (mAudioTrack != null) {
ByteBuffer buffer = mCodecOutputBuffers[index];
buffer.clear();
buffer.position(0 /* offset */);
byte[] audioCopy = new byte[info.size];
buffer.get(audioCopy, 0, info.size);
mAudioTrack.write(audioCopy, info.size);
mCodec.releaseOutputBuffer(index, false /* render */);
mLastMediaTimeUs = info.presentationTimeUs;
mAvailableOutputBufferIndices.removeFirst();
mAvailableOutputBufferInfos.removeFirst();
return true;
} else {
// video
boolean render;
if (lateUs < -10000) {
// too early;
return false;
} else if (lateUs > 30000) {
Log.d(TAG, "video late by " + lateUs + " us.");
render = false;
} else {
render = true;
mLastMediaTimeUs = info.presentationTimeUs;
}
mCodec.releaseOutputBuffer(index, render);
mAvailableOutputBufferIndices.removeFirst();
mAvailableOutputBufferInfos.removeFirst();
return true;
}
}
public long getAudioTimeUs() {
if (mAudioTrack == null) {
return 0;
}
return mAudioTrack.getAudioTimeUs();
}
}
class MediaCodecView extends SurfaceView
implements MediaController.MediaPlayerControl {
private static final String TAG = "MediaCodecView";
private Context mContext;
private Uri mUri;
private Map<String, String> mHeaders;
private boolean mEncrypted;
private MediaCrypto mCrypto;
private MediaExtractor mExtractor;
private Map<Integer, CodecState> mCodecStates;
CodecState mAudioTrackState;
private int mState;
private static final int STATE_IDLE = 1;
private static final int STATE_PREPARING = 2;
private static final int STATE_PLAYING = 3;
private static final int STATE_PAUSED = 4;
private Handler mHandler;
private static final int EVENT_PREPARE = 1;
private static final int EVENT_DO_SOME_WORK = 2;
private long mDeltaTimeUs;
private long mDurationUs;
private MediaController mMediaController;
public MediaCodecView(Context context) {
super(context);
initMediaCodecView();
}
public MediaCodecView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
initMediaCodecView();
}
public MediaCodecView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initMediaCodecView();
}
private void initMediaCodecView() {
mState = STATE_IDLE;
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_PREPARE:
{
try {
prepare();
start();
} catch (IOException e) {
Log.d(TAG, "prepare failed.");
} catch (MediaCryptoException e) {
Log.d(TAG, "failed to initialize crypto.");
}
break;
}
case EVENT_DO_SOME_WORK:
{
doSomeWork();
mHandler.sendMessageDelayed(
mHandler.obtainMessage(EVENT_DO_SOME_WORK), 5);
break;
}
default:
break;
}
}
};
}
public void setDataSource(
Context context, Uri uri, Map<String, String> headers,
boolean encrypted) {
reset();
mContext = context;
mUri = uri;
mHeaders = headers;
mEncrypted = encrypted;
}
private void prepare() throws IOException, MediaCryptoException {
if (mEncrypted) {
UUID uuid = new UUID(
(long)0xedef8ba979d64aceL, (long)0xa3c827dcd51d21edL);
try {
mCrypto = new MediaCrypto(uuid, null);
} catch (MediaCryptoException e) {
reset();
throw e;
}
}
try {
mExtractor = new MediaExtractor();
mExtractor.setDataSource(mContext, mUri, mHeaders);
} catch (IOException e) {
reset();
throw e;
}
mCodecStates = new HashMap();
boolean haveAudio = false;
boolean haveVideo = false;
for (int i = mExtractor.getTrackCount(); i-- > 0;) {
MediaFormat format = mExtractor.getTrackFormat(i);
Log.d(TAG, "track format #" + i + " is " + format);
String mime = format.getString(MediaFormat.KEY_MIME);
boolean isVideo = mime.startsWith("video/");
boolean isAudio = mime.startsWith("audio/");
if (!haveAudio && isAudio || !haveVideo && isVideo) {
mExtractor.selectTrack(i);
addTrack(i, format, mEncrypted);
if (isAudio) {
haveAudio = true;
} else {
haveVideo = true;
}
if (format.containsKey(MediaFormat.KEY_DURATION)) {
long durationUs = format.getLong(MediaFormat.KEY_DURATION);
if (durationUs > mDurationUs) {
mDurationUs = durationUs;
}
}
if (haveAudio && haveVideo) {
break;
}
}
}
mState = STATE_PAUSED;
}
private String getSecureDecoderNameForMime(String mime) {
MediaCodecInfo[] codecInfos =
new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos();
for (MediaCodecInfo codecInfo : codecInfos) {
if (codecInfo.isEncoder()) {
continue;
}
String[] supportedTypes = codecInfo.getSupportedTypes();
for (int i = 0; i < supportedTypes.length; ++i) {
if (supportedTypes[i].equalsIgnoreCase(mime)) {
return codecInfo.getName() + ".secure";
}
}
}
return null;
}
private void addTrack(
int trackIndex, MediaFormat format, boolean encrypted)
throws IOException {
String mime = format.getString(MediaFormat.KEY_MIME);
boolean isVideo = mime.startsWith("video/");
boolean isAudio = mime.startsWith("audio/");
MediaCodec codec;
if (encrypted && mCrypto.requiresSecureDecoderComponent(mime)) {
codec = MediaCodec.createByCodecName(
getSecureDecoderNameForMime(mime));
} else {
codec = MediaCodec.createDecoderByType(mime);
}
codec.configure(
format,
isVideo ? getHolder().getSurface() : null,
mCrypto,
0);
CodecState state =
new CodecState(this, mExtractor, trackIndex, format, codec);
mCodecStates.put(new Integer(trackIndex), state);
if (isAudio) {
mAudioTrackState = state;
}
// set video view size
invalidate();
}
public void start() {
Log.d(TAG, "start");
if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
return;
} else if (mState == STATE_IDLE) {
mState = STATE_PREPARING;
mHandler.sendMessage(mHandler.obtainMessage(EVENT_PREPARE));
return;
} else if (mState != STATE_PAUSED) {
throw new IllegalStateException();
}
for (CodecState state : mCodecStates.values()) {
state.start();
}
mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_SOME_WORK));
mDeltaTimeUs = -1;
mState = STATE_PLAYING;
if (mMediaController != null) {
mMediaController.show();
}
}
public void pause() {
Log.d(TAG, "pause");
if (mState == STATE_PAUSED) {
return;
} else if (mState != STATE_PLAYING) {
throw new IllegalStateException();
}
mHandler.removeMessages(EVENT_DO_SOME_WORK);
for (CodecState state : mCodecStates.values()) {
state.pause();
}
mState = STATE_PAUSED;
}
public void reset() {
if (mState == STATE_PLAYING) {
pause();
}
if (mMediaController != null) {
mMediaController.setEnabled(false);
}
if (mCodecStates != null) {
for (CodecState state : mCodecStates.values()) {
state.release();
}
mCodecStates = null;
}
if (mExtractor != null) {
mExtractor.release();
mExtractor = null;
}
if (mCrypto != null) {
mCrypto.release();
mCrypto = null;
}
mDurationUs = -1;
mState = STATE_IDLE;
}
public void setMediaController(MediaController ctrl) {
mMediaController = ctrl;
attachMediaController();
}
private void attachMediaController() {
View anchorView =
this.getParent() instanceof View ? (View)this.getParent() : this;
mMediaController.setMediaPlayer(this);
mMediaController.setAnchorView(anchorView);
mMediaController.setEnabled(true);
}
private void doSomeWork() {
for (CodecState state : mCodecStates.values()) {
state.doSomeWork();
}
}
public long getNowUs() {
if (mAudioTrackState == null) {
return System.currentTimeMillis() * 1000;
}
return mAudioTrackState.getAudioTimeUs();
}
public long getRealTimeUsForMediaTime(long mediaTimeUs) {
if (mDeltaTimeUs == -1) {
long nowUs = getNowUs();
mDeltaTimeUs = nowUs - mediaTimeUs;
}
return mDeltaTimeUs + mediaTimeUs;
}
public int getDuration() {
return (int)((mDurationUs + 500) / 1000);
}
public int getCurrentPosition() {
if (mCodecStates == null) {
return 0;
}
long positionUs = 0;
for (CodecState state : mCodecStates.values()) {
long trackPositionUs = state.getCurrentPositionUs();
if (trackPositionUs > positionUs) {
positionUs = trackPositionUs;
}
}
return (int)((positionUs + 500) / 1000);
}
public void seekTo(int timeMs) {
if (mState != STATE_PLAYING && mState != STATE_PAUSED) {
return;
}
mExtractor.seekTo(timeMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
for (CodecState state : mCodecStates.values()) {
state.flush();
}
Log.d(TAG, "seek to " + timeMs * 1000);
mDeltaTimeUs = -1;
}
public boolean isPlaying() {
return mState == STATE_PLAYING;
}
public int getBufferPercentage() {
if (mExtractor == null) {
return 0;
}
long cachedDurationUs = mExtractor.getCachedDuration();
if (cachedDurationUs < 0 || mDurationUs < 0) {
return 0;
}
int nowMs = getCurrentPosition();
int percentage =
100 * (nowMs + (int)(cachedDurationUs / 1000))
/ (int)(mDurationUs / 1000);
if (percentage > 100) {
percentage = 100;
}
return percentage;
}
public boolean canPause() {
return true;
}
public boolean canSeekBackward() {
return true;
}
public boolean canSeekForward() {
return true;
}
@Override
public int getAudioSessionId() {
return 0;
}
private void toggleMediaControlsVisiblity() {
if (mMediaController.isShowing()) {
mMediaController.hide();
} else {
mMediaController.show();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mState != STATE_IDLE && mMediaController != null) {
toggleMediaControlsVisiblity();
}
return false;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 720;
int height = 480;
Log.d(TAG, "setting size: " + width + 'x' + height);
setMeasuredDimension(width, height);
}
}

View File

@@ -1,207 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widevine.demo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.LinkedList;
class NonBlockingAudioTrack {
private static final String TAG = "NonBlockingAudioTrack";
private AudioTrack mAudioTrack;
private int mSampleRate;
private int mFrameSize;
private int mBufferSizeInFrames;
private int mNumFramesSubmitted = 0;
class QueueElem {
byte[] data;
int offset;
int size;
}
private LinkedList<QueueElem> mQueue = new LinkedList();
private Handler mHandler;
private boolean mWriteMorePending = false;
private static final int EVENT_WRITE_MORE = 1;
public NonBlockingAudioTrack(int sampleRate, int channelCount) {
int channelConfig;
switch (channelCount) {
case 1:
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
break;
case 2:
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
break;
case 6:
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
break;
default:
throw new IllegalArgumentException();
}
int minBufferSize =
AudioTrack.getMinBufferSize(
sampleRate,
channelConfig,
AudioFormat.ENCODING_PCM_16BIT);
int bufferSize = 2 * minBufferSize;
mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
sampleRate,
channelConfig,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
mSampleRate = sampleRate;
mFrameSize = 2 * channelCount;
mBufferSizeInFrames = bufferSize / mFrameSize;
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_WRITE_MORE:
mWriteMorePending = false;
writeMore();
break;
default:
break;
}
}
};
}
public long getAudioTimeUs() {
int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
return (numFramesPlayed * 1000000L) / mSampleRate;
}
public void play() {
mAudioTrack.play();
}
public void stop() {
cancelWriteMore();
mAudioTrack.stop();
mNumFramesSubmitted = 0;
}
public void pause() {
cancelWriteMore();
mAudioTrack.pause();
}
public void release() {
cancelWriteMore();
mAudioTrack.release();
mAudioTrack = null;
}
public int getPlayState() {
return mAudioTrack.getPlayState();
}
private void writeMore() {
if (mQueue.isEmpty()) {
return;
}
int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
int numFramesAvailableToWrite = mBufferSizeInFrames - numFramesPending;
int numBytesAvailableToWrite = numFramesAvailableToWrite * mFrameSize;
while (numBytesAvailableToWrite > 0) {
QueueElem elem = mQueue.peekFirst();
int numBytes = elem.size;
if (numBytes > numBytesAvailableToWrite) {
numBytes = numBytesAvailableToWrite;
}
int written = mAudioTrack.write(elem.data, elem.offset, numBytes);
assert(written == numBytes);
mNumFramesSubmitted += written / mFrameSize;
elem.size -= numBytes;
if (elem.size == 0) {
mQueue.removeFirst();
if (mQueue.isEmpty()) {
break;
}
} else {
elem.offset += numBytes;
break;
}
numBytesAvailableToWrite -= numBytes;
}
if (!mQueue.isEmpty()) {
scheduleWriteMore();
}
}
private void scheduleWriteMore() {
if (mWriteMorePending) {
return;
}
int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
int pendingDurationMs = 1000 * numFramesPending / mSampleRate;
mWriteMorePending = true;
mHandler.sendMessageDelayed(
mHandler.obtainMessage(EVENT_WRITE_MORE),
pendingDurationMs / 3);
}
private void cancelWriteMore() {
mHandler.removeMessages(EVENT_WRITE_MORE);
mWriteMorePending = false;
}
public void write(byte[] data, int size) {
QueueElem elem = new QueueElem();
elem.data = data;
elem.offset = 0;
elem.size = size;
mQueue.add(elem);
scheduleWriteMore();
}
}

View File

@@ -1,75 +0,0 @@
/*
* (c)Copyright 2011 Google Inc.
*/
package com.widevine.demo;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
public class SettingsFragment extends Fragment {
// public static String CONTENT_PAGE = "/sdcard/Widevine/config.xml";
public static String CONTENT_PAGE = "http://storage.googleapis.com/wvmedia/html/oem.html";
private Button updateButton;
private EditText drmServer, portalName, deviceId, contentPage;
public static final String TAG = "WVM Player Settings";
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.settings, container, false);
updateButton = (Button)rootView.findViewById(R.id.save_settings);
View.OnClickListener clickListener = new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click update settings");
WidevineDrm.Settings.DRM_SERVER_URI = drmServer.getText().toString();
WidevineDrm.Settings.DEVICE_ID = deviceId.getText().toString();
WidevineDrm.Settings.PORTAL_NAME = portalName.getText().toString();
SettingsFragment.CONTENT_PAGE = contentPage.getText().toString();
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setMessage("DRM Settings Updated").setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Log.d(TAG, "Click DRM Settings OK.");
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
};
updateButton.setOnClickListener(clickListener);
drmServer = (EditText)rootView.findViewById(R.id.drm_server);
drmServer.setText(WidevineDrm.Settings.DRM_SERVER_URI);
deviceId = (EditText)rootView.findViewById(R.id.device_id);
deviceId.setText(WidevineDrm.Settings.DEVICE_ID);
portalName = (EditText)rootView.findViewById(R.id.portal_id);
portalName.setText(WidevineDrm.Settings.PORTAL_NAME);
contentPage = (EditText)rootView.findViewById(R.id.content_page);
contentPage.setText(SettingsFragment.CONTENT_PAGE);
return rootView;
}
}

View File

@@ -1,86 +0,0 @@
/*
* (c)Copyright 2011 Google Inc.
*/
package com.widevine.demo;
import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import android.os.Bundle;
public class StreamingFragment extends AssetFragment {
private String contentPage;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
contentPage = SettingsFragment.CONTENT_PAGE;
}
protected boolean setUpAssetPages() {
pages = new ArrayList<AssetsPage>();
if (contentPage.contains(".htm")) {
ArrayList<AssetItem> assets = getStreamingClipsHttp();
if (assets == null || assets.size() == 0)
return false;
for (int i = 0; i < assets.size();) {
AssetsPage page = new AssetsPage();
for (int j = 0; j < AssetsPage.MAX_ITEMS && i < assets.size(); j++, i++) {
page.addPage(assets.get(i).getAssetPath(),
assets.get(i).getImagePath(), assets.get(i).getTitle());
}
pages.add(page);
}
} else {
ArrayList<AssetDescriptor> assets = getStreamingClipsXml();
if (assets == null || assets.size() == 0)
return false;
for (int i = 0; i < assets.size();) {
AssetsPage page = new AssetsPage();
for (int j = 0; j < AssetsPage.MAX_ITEMS && i < assets.size(); j++, i++) {
page.addPage(assets.get(i).getUri(), assets.get(i).getThumbnail(),
assets.get(i).getTitle());
}
pages.add(page);
}
}
return true;
}
private ArrayList<AssetDescriptor> getStreamingClipsXml() {
try {
File file = new File(contentPage);
if (file.exists()) {
ConfigXMLParser parser = new ConfigXMLParser(file.toURL());
ArrayList<AssetDescriptor> assets = (ArrayList<AssetDescriptor>) parser.parse();
return assets;
} else {
return new ArrayList<AssetDescriptor>();
}
} catch (MalformedURLException e) {
return new ArrayList<AssetDescriptor>();
}
}
private ArrayList<AssetItem> getStreamingClipsHttp() {
HttpParser parser = new HttpParser(contentPage);
parser.start();
try {
parser.join();
} catch (InterruptedException e) {
}
return parser.getAssets();
}
}

View File

@@ -1,437 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import android.support.v7.app.AppCompatActivity;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.MediaController;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Button;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.content.Context;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.media.MediaCrypto;
import android.media.MediaExtractor;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnInfoListener;
import android.util.Log;
import java.io.IOException;
public class VideoPlayerView extends AppCompatActivity {
private final static String TAG = "VideoPlayerView";
private final static float BUTTON_FONT_SIZE = 10;
private final static String EXIT_FULLSCREEN = "Exit Full Screen";
private final static String FULLSCREEN = "Enter Full Screen";
private final static String PLAY = "Play";
private final static int REFRESH = 1;
private WidevineDrm drm;
private FullScreenVideoView videoView;
private String assetUri;
private TextView logs;
private ScrollView scrollView;
private Context context;
private ClipImageView bgImage;
private Button playButton;
private Button fullScreen;
private Handler hRefresh;
private View contentView;
private LinearLayout main;
private LinearLayout sidePanel;
private boolean enteringFullScreen;
private int width, height;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display display = getWindowManager().getDefaultDisplay();
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
height = display.getHeight();
width = display.getWidth();
context = this;
contentView = createView();
if (drm.isProvisionedDevice()) {
setContentView(contentView);
} else {
setContentView(R.layout.notprovisioned);
}
drm.printPluginVersion();
}
@Override
protected void onStop() {
Log.d(TAG, "onStop.");
if (videoView != null) {
if (videoView.isPlaying()) {
stopPlayback();
}
}
super.onStop();
}
private View createView() {
enteringFullScreen = false;
assetUri = this.getIntent().getStringExtra("com.widevine.demo.Path").replaceAll("wvplay", "http");
drm = new WidevineDrm(this);
logMessage("Asset Uri: " + assetUri + "\n");
logMessage("Drm Server: " + WidevineDrm.Settings.DRM_SERVER_URI + "\n");
logMessage("Device Id: " + WidevineDrm.Settings.DEVICE_ID + "\n");
logMessage("Portal Name: " + WidevineDrm.Settings.PORTAL_NAME + "\n");
// Set log update listener
WidevineDrm.WidevineDrmLogEventListener drmLogListener =
new WidevineDrm.WidevineDrmLogEventListener() {
public void logUpdated() {
updateLogs();
}
};
logs = new TextView(this);
drm.setLogListener(drmLogListener);
drm.registerPortal(WidevineDrm.Settings.PORTAL_NAME);
scrollView = new ScrollView(this);
scrollView.addView(logs);
// Set message handler for log events
hRefresh = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case REFRESH:
/* Refresh UI */
logs.setText(drm.logBuffer.toString());
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
break;
}
}
};
updateLogs();
sidePanel = new LinearLayout(this);
sidePanel.setOrientation(LinearLayout.VERTICAL);
sidePanel.addView(scrollView, new LinearLayout.LayoutParams(
(int)(width * 0.35),
(int)(height * 0.5)));
LinearLayout.LayoutParams paramsSidePanel = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
paramsSidePanel.gravity = Gravity.CENTER;
sidePanel.addView(createButtons(), paramsSidePanel);
FrameLayout playerFrame = new FrameLayout(this);
videoView = new FullScreenVideoView(this);
videoView.setVisibility(View.INVISIBLE);
playerFrame.addView(videoView, new FrameLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.MATCH_PARENT));
bgImage = new ClipImageView(this);
bgImage.setBackground(getResources().getDrawable(R.drawable.play_shield,
context.getTheme()));
bgImage.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click play (start playback).");
startPlayback();
}
});
fullScreen = new Button(this);
fullScreen.setText(FULLSCREEN);
fullScreen.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click full screen");
int currentPosition = videoView.getCurrentPosition();
videoView.setVisibility(View.INVISIBLE);
if (fullScreen.getText().equals(FULLSCREEN)) {
videoView.setFullScreen(true);
fullScreen.setText(EXIT_FULLSCREEN);
enteringFullScreen = true;
} else {
videoView.setFullScreen(false);
fullScreen.setText(FULLSCREEN);
}
videoView.setVisibility(View.VISIBLE);
stopPlayback();
startPlayback();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
videoView.seekTo(currentPosition);
}
});
playerFrame.addView(fullScreen, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT));
fullScreen.setVisibility(View.INVISIBLE);
playerFrame.addView(bgImage, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
main = new LinearLayout(this);
main.addView(playerFrame, new LinearLayout.LayoutParams((int)(width * 0.65),
LinearLayout.LayoutParams.MATCH_PARENT, 1));
main.addView(sidePanel, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT, 3));
return main;
}
private void startPlayback() {
logMessage("Playback start.");
playButton.setText(R.string.stop);
bgImage.setVisibility(View.GONE);
videoView.setVisibility(View.VISIBLE);
videoView.setVideoPath(assetUri);
videoView.setMediaController(new MediaController(context));
videoView.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
String message = "Unknown error: " + what;
switch (what) {
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
message = "Unable to play media";
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
message = "Server failed";
break;
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
message = "Invalid media";
break;
}
logMessage(message + "\n");
updateLogs();
bgImage.setVisibility(View.VISIBLE);
videoView.setVisibility(View.INVISIBLE);
return false;
}
});
videoView.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
Log.d(TAG, "onCompletion.");
stopPlayback();
}
});
videoView.setOnInfoListener(new OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
String message = "Unknown info message";
switch (what) {
case MediaPlayer.MEDIA_INFO_UNKNOWN:
message = "Unknown info message 2";
break;
case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
message = "Video rendering start";
break;
case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
message = "Video track lagging";
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
message = "Buffering start";
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
message = "Buffering end";
break;
/*** TODO: Below needs to be added to MediaPlayer.java. Hard coded for now --Zan
case MediaPlayer.MEDIA_INFO_NETWORK_BANDWIDTH: ***/
case 703:
message = "Network bandwidth";
break;
case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
message = "Bad interleaving";
break;
case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
message = "Not seekable";
break;
case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:
message = "Metadata update";
break;
}
logMessage(message + "\n");
updateLogs();
return true;
}
});
videoView.requestFocus();
videoView.start();
if (videoView.getFullScreen()) {
sidePanel.setVisibility(View.GONE);
} else {
sidePanel.setVisibility(View.VISIBLE);
}
fullScreen.setVisibility(View.VISIBLE);
videoView.setFullScreenDimensions(contentView.getRight() - contentView.getLeft(),
contentView.getBottom() - contentView.getTop());
}
private void stopPlayback() {
logMessage("Stop Playback.");
playButton.setText(R.string.play);
bgImage.setVisibility(View.VISIBLE);
videoView.setVisibility(View.INVISIBLE);
videoView.stopPlayback();
fullScreen.setVisibility(View.INVISIBLE);
if (videoView.getFullScreen() && !enteringFullScreen) {
videoView.setVisibility(View.INVISIBLE);
videoView.setFullScreen(false);
sidePanel.setVisibility(View.VISIBLE);
fullScreen.setText(FULLSCREEN);
}
enteringFullScreen = false;
}
private View createButtons() {
playButton = new Button(this);
playButton.setText(R.string.play);
playButton.setTextSize(BUTTON_FONT_SIZE);
playButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click play");
Button b = (Button) v;
if (b.getText().equals(PLAY)) {
startPlayback();
} else {
stopPlayback();
}
}
});
Button rightsButton = new Button(this);
rightsButton.setText(R.string.acquire_rights);
rightsButton.setTextSize(BUTTON_FONT_SIZE);
rightsButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click rights");
drm.acquireRights(assetUri);
updateLogs();
}
});
Button removeButton = new Button(this);
removeButton.setText(R.string.remove_rights);
removeButton.setTextSize(BUTTON_FONT_SIZE);
removeButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click remove rights");
drm.removeRights(assetUri);
updateLogs();
}
});
Button checkButton = new Button(this);
checkButton.setText(R.string.show_rights);
checkButton.setTextSize(BUTTON_FONT_SIZE);
checkButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click check rights");
drm.showRights(assetUri);
updateLogs();
}
});
Button checkConstraints = new Button(this);
checkConstraints.setText(R.string.constraints);
checkConstraints.setTextSize(BUTTON_FONT_SIZE);
checkConstraints.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Click get constraints");
drm.getConstraints(assetUri);
updateLogs();
}
});
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
params.setMargins(0, 0, 0, 5);
LinearLayout buttonsLeft = new LinearLayout(this);
buttonsLeft.setOrientation(LinearLayout.VERTICAL);
buttonsLeft.addView(playButton, params);
buttonsLeft.addView(rightsButton, params);
buttonsLeft.addView(checkConstraints, params);
LinearLayout buttonsRight = new LinearLayout(this);
buttonsRight.setOrientation(LinearLayout.VERTICAL);
buttonsRight.addView(checkButton, params);
buttonsRight.addView(removeButton, params);
LinearLayout.LayoutParams paramsSides = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
paramsSides.gravity = Gravity.BOTTOM;
LinearLayout buttons = new LinearLayout(this);
buttons.addView(buttonsLeft, paramsSides);
buttons.addView(buttonsRight, paramsSides);
return buttons;
}
private void updateLogs() {
hRefresh.sendEmptyMessage(REFRESH);
}
@Override
protected void onPause() {
Log.v("VideoPlayerView", "------------------- onPause ----------------");
onStop();
}
private void logMessage(String message) {
Log.d(TAG, message);
drm.logBuffer.append(message);
}
}

View File

@@ -1,325 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import java.io.File;
import java.util.EventListener;
import java.util.Set;
import android.content.ContentValues;
import android.content.Context;
import android.drm.DrmErrorEvent;
import android.drm.DrmEvent;
import android.drm.DrmInfo;
import android.drm.DrmInfoEvent;
import android.drm.DrmInfoRequest;
import android.drm.DrmManagerClient;
import android.drm.DrmStore;
import android.os.ParcelFileDescriptor;
import android.util.Log;
public class WidevineDrm {
public static final String TAG = "WVM Sample Player";
interface WidevineDrmLogEventListener extends EventListener {
public void logUpdated();
}
private WidevineDrmLogEventListener logEventListener;
private final static long DEVICE_IS_PROVISIONED = 0;
private final static long DEVICE_IS_NOT_PROVISIONED = 1;
private final static long DEVICE_IS_PROVISIONED_SD_ONLY = 2;
private long mWVDrmInfoRequestStatusKey = DEVICE_IS_PROVISIONED;
private String mPluginVersion = "";
public StringBuffer logBuffer = new StringBuffer();
/**
* Drm Manager Configuration Methods
*/
public static class Settings {
public static String WIDEVINE_MIME_TYPE = "video/wvm";
public static String DRM_SERVER_URI = "https://license.uat.widevine.com/getlicense/widevine_test";
public static String DEVICE_ID = "device12345"; // use a unique device ID
public static String PORTAL_NAME = "widevine";
// test with a sizeable block of user data...
public static String USER_DATA = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789";
};
private DrmManagerClient mDrmManager;
// private Context mContext;
public WidevineDrm(Context context) {
// mContext = context;
mDrmManager = new DrmManagerClient(context);
mDrmManager.setOnInfoListener(new DrmManagerClient.OnInfoListener() {
// @Override
public void onInfo(DrmManagerClient client, DrmInfoEvent event) {
if (event.getType() == DrmInfoEvent.TYPE_RIGHTS_INSTALLED) {
logMessage("Rights installed\n");
}
}
});
mDrmManager.setOnEventListener(new DrmManagerClient.OnEventListener() {
public void onEvent(DrmManagerClient client, DrmEvent event) {
switch (event.getType()) {
case DrmEvent.TYPE_DRM_INFO_PROCESSED:
logMessage("Info Processed\n");
break;
case DrmEvent.TYPE_ALL_RIGHTS_REMOVED:
logMessage("All rights removed\n");
break;
}
}
});
mDrmManager.setOnErrorListener(new DrmManagerClient.OnErrorListener() {
public void onError(DrmManagerClient client, DrmErrorEvent event) {
switch (event.getType()) {
case DrmErrorEvent.TYPE_NO_INTERNET_CONNECTION:
logMessage("No Internet Connection\n");
break;
case DrmErrorEvent.TYPE_NOT_SUPPORTED:
logMessage("Not Supported\n");
break;
case DrmErrorEvent.TYPE_OUT_OF_MEMORY:
logMessage("Out of Memory\n");
break;
case DrmErrorEvent.TYPE_PROCESS_DRM_INFO_FAILED:
logMessage("Process DRM Info failed\n");
break;
case DrmErrorEvent.TYPE_REMOVE_ALL_RIGHTS_FAILED:
logMessage("Remove All Rights failed\n");
break;
case DrmErrorEvent.TYPE_RIGHTS_NOT_INSTALLED:
logMessage("Rights not installed\n");
break;
case DrmErrorEvent.TYPE_RIGHTS_RENEWAL_NOT_ALLOWED:
logMessage("Rights renewal not allowed\n");
break;
}
}
});
}
public DrmInfoRequest getDrmInfoRequest(String assetUri) {
DrmInfoRequest rightsAcquisitionInfo;
rightsAcquisitionInfo = new DrmInfoRequest(DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO,
Settings.WIDEVINE_MIME_TYPE);
rightsAcquisitionInfo.put("WVDRMServerKey", Settings.DRM_SERVER_URI);
rightsAcquisitionInfo.put("WVAssetURIKey", assetUri);
rightsAcquisitionInfo.put("WVDeviceIDKey", Settings.DEVICE_ID);
rightsAcquisitionInfo.put("WVPortalKey", Settings.PORTAL_NAME);
rightsAcquisitionInfo.put("WVCAUserDataKey", Settings.USER_DATA);
return rightsAcquisitionInfo;
}
public DrmInfoRequest getDrmInfoRequest(String assetUri, ParcelFileDescriptor pfd) {
DrmInfoRequest rightsAcquisitionInfo = getDrmInfoRequest(assetUri);
if (pfd.getFileDescriptor().valid()) {
rightsAcquisitionInfo.put("FileDescriptorKey", Integer.toString(pfd.getFd()));
}
return rightsAcquisitionInfo;
}
public boolean isProvisionedDevice() {
if (mWVDrmInfoRequestStatusKey == DEVICE_IS_PROVISIONED)
logMessage("Device is provisioned\n");
else if (mWVDrmInfoRequestStatusKey == DEVICE_IS_PROVISIONED_SD_ONLY)
logMessage("Device is provisioned SD only\n");
else if (mWVDrmInfoRequestStatusKey == DEVICE_IS_NOT_PROVISIONED)
logMessage("Device is not provisioned\n");
else
logMessage("Invalid provisioned status=" + mWVDrmInfoRequestStatusKey +"\n");
return ((mWVDrmInfoRequestStatusKey == DEVICE_IS_PROVISIONED) ||
(mWVDrmInfoRequestStatusKey == DEVICE_IS_PROVISIONED_SD_ONLY));
}
public void printPluginVersion() {
logMessage("plugin: " + mPluginVersion + "\n");
}
public boolean canPlayClassicWidevine() {
return registerPortal(WidevineDrm.Settings.PORTAL_NAME);
}
public boolean registerPortal(String portal) {
DrmInfoRequest request = new DrmInfoRequest(DrmInfoRequest.TYPE_REGISTRATION_INFO,
Settings.WIDEVINE_MIME_TYPE);
request.put("WVPortalKey", portal);
DrmInfo response = mDrmManager.acquireDrmInfo(request);
boolean canPlayClassicWidevine = true;
try {
String drmInfoRequestStatusKey = (String)response.get("WVDrmInfoRequestStatusKey");
if (null != drmInfoRequestStatusKey && !drmInfoRequestStatusKey.equals("")) {
mWVDrmInfoRequestStatusKey = Long.parseLong(drmInfoRequestStatusKey);
}
mPluginVersion = (String)response.get("WVDrmInfoRequestVersionKey");
} catch (NullPointerException e) {
canPlayClassicWidevine = false;
}
return canPlayClassicWidevine;
}
public int acquireRights(String assetUri) {
int rights = 0;
if (assetUri.startsWith("/sdcard")) {
try {
int mode = ParcelFileDescriptor.parseMode("r");
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(assetUri), mode);
rights = mDrmManager.acquireRights(getDrmInfoRequest(assetUri, pfd));
pfd.close();
}
catch (java.io.IOException e) {
logMessage("Unable to acquire rights for '" + assetUri + ": File I/O error'\n");
}
} else {
rights = mDrmManager.acquireRights(getDrmInfoRequest(assetUri));
}
logMessage("acquireRights = " + rights + "\n");
return rights;
}
public int checkRightsStatus(String assetUri) {
// Need to use acquireDrmInfo prior to calling checkRightsStatus
mDrmManager.acquireDrmInfo(getDrmInfoRequest(assetUri));
int status = mDrmManager.checkRightsStatus(assetUri);
logMessage("checkRightsStatus = " + status + "\n");
return status;
}
public void getConstraints(String assetUri) {
ContentValues values = mDrmManager.getConstraints(assetUri, DrmStore.Action.PLAY);
logContentValues(values, "No Contraints");
}
public void showRights(String assetUri) {
logMessage("showRights\n");
// Need to use acquireDrmInfo prior to calling getConstraints
mDrmManager.acquireDrmInfo(getDrmInfoRequest(assetUri));
ContentValues values = mDrmManager.getConstraints(assetUri, DrmStore.Action.PLAY);
logContentValues(values, "No Rights");
}
private void logContentValues(ContentValues values, String defaultMessage) {
if (values != null) {
Set<String> keys = values.keySet();
for (String key : keys) {
if (key.toLowerCase().contains("time")) {
logMessage(key + " = " + SecondsToDHMS(values.getAsLong(key)) + "\n");
} else if (key.toLowerCase().contains("licensetype")) {
logMessage(key + " = " + licenseType(values.getAsInteger(key)) + "\n");
} else if (key.toLowerCase().contains("licensedresolution")) {
logMessage(key + " = " + licenseResolution(values.getAsInteger(key)) + "\n");
} else {
logMessage(key + " = " + values.get(key) + "\n");
}
}
} else {
logMessage(defaultMessage + "\n");
}
}
private static final long seconds_per_minute = 60;
private static final long seconds_per_hour = 60 * seconds_per_minute;
private static final long seconds_per_day = 24 * seconds_per_hour;
private String SecondsToDHMS(long seconds) {
int days = (int) (seconds / seconds_per_day);
seconds -= days * seconds_per_day;
int hours = (int) (seconds / seconds_per_hour);
seconds -= hours * seconds_per_hour;
int minutes = (int) (seconds / seconds_per_minute);
seconds -= minutes * seconds_per_minute;
return Integer.toString(days) + "d " + Integer.toString(hours) + "h "
+ Integer.toString(minutes) + "m " + Long.toString(seconds)
+ "s";
}
private String licenseType(int code) {
switch (code) {
case 1:
return "Streaming";
case 2:
return "Offline";
case 3:
return "Both";
default:
return "Unknown";
}
}
private String licenseResolution(int code) {
switch (code) {
case 1:
return "SD only";
case 2:
return "HD or SD content";
default:
return "Unknown";
}
}
public int removeRights(String assetUri) {
// Need to use acquireDrmInfo prior to calling removeRights
mDrmManager.acquireDrmInfo(getDrmInfoRequest(assetUri));
int removeStatus = mDrmManager.removeRights(assetUri);
logMessage("removeRights = " + removeStatus + "\n");
return removeStatus;
}
public int removeAllRights() {
int removeAllStatus = mDrmManager.removeAllRights();
logMessage("removeAllRights = " + removeAllStatus + "\n");
return removeAllStatus;
}
public void setLogListener(WidevineDrmLogEventListener logEventListener) {
this.logEventListener = logEventListener;
}
private void logMessage(String message) {
Log.d(TAG, message);
logBuffer.append(message);
if (logEventListener != null) {
logEventListener.logUpdated();
}
}
}

View File

@@ -1,95 +0,0 @@
/*
* (c)Copyright 2011 Google, Inc
*/
package com.widevine.demo;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
public class WidevineSamplePlayer extends AppCompatActivity {
public static final String PREFS_NAME = "DrmPrefs";
private static final String TAG = "WidevineSamplePlayer";
DemoPagerAdapter mPagerAdapter;
ViewPager mViewPager;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// ViewPager and its adapters use support library
// fragments, so use getSupportFragmentManager.
mPagerAdapter = new DemoPagerAdapter(getSupportFragmentManager(), this);
mViewPager = (ViewPager)findViewById(R.id.pager);
mViewPager.setAdapter(mPagerAdapter);
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setupWithViewPager(mViewPager);
}
}
class DemoPagerAdapter extends FragmentPagerAdapter {
private Context context;
public DemoPagerAdapter(FragmentManager fm, Context c) {
super(fm);
context = c;
}
@Override
public Fragment getItem(int i) {
Fragment fragment = null;
switch (i) {
case 0: fragment = new StreamingFragment();
break;
case 1: fragment = new DownloadFragment();
break;
case 2: fragment = new SettingsFragment();
break;
}
return fragment;
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
int tabNameIds[] = {R.string.streaming, R.string.downloads, R.string.settings};
return context.getResources().getText(tabNameIds[position]);
}
}
// Instances of this class are fragments representing a single
// object in our collection.
class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// The last two arguments ensure LayoutParams are inflated
// properly.
View rootView = inflater.inflate(R.layout.empty, container, false);
return rootView;
}
}

View File

@@ -1 +0,0 @@
include $(call all-subdir-makefiles)

View File

@@ -1,13 +0,0 @@
typedef void (*_ah001)(char* , unsigned long );
void _ah002(_ah001 );
void _ah007(char* , unsigned long );
typedef int (*_ah003)(char * , char * , int , char * );
void _ah004(_ah003 );
void _ah008(char * , char * , int , char * );
bool _ah009();
typedef void (*_ah005)(const char * );
void _ah006(_ah005 );
void AndroidLogDebug(const char * );
typedef void (*_ah011)(int );
void _ah010(_ah011 );
void libocs_setup();

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
include $(call all-subdir-makefiles)

View File

@@ -1,20 +0,0 @@
LOCAL_PATH:= $(call my-dir)
ifneq ($(BOARD_USES_GENERIC_WIDEVINE),false)
include $(CLEAR_VARS)
LOCAL_MODULE := libWVStreamControlAPI_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_STRIP_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := widevine
LOCAL_MODULE_TARGET_ARCH := arm
LOCAL_MULTILIB := 32
include $(BUILD_PREBUILT)
endif

View File

@@ -1,20 +0,0 @@
LOCAL_PATH:= $(call my-dir)
ifneq ($(BOARD_USES_GENERIC_WIDEVINE),false)
include $(CLEAR_VARS)
LOCAL_MODULE := libWVStreamControlAPI_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_STRIP_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := widevine
LOCAL_MODULE_TARGET_ARCH := mips
LOCAL_MULTILIB := 32
include $(BUILD_PREBUILT)
endif

View File

@@ -1,14 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libWVStreamControlAPI_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_STRIP_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := x86
include $(BUILD_PREBUILT)

View File

@@ -1,34 +0,0 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
TestPlayer.cpp
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES += \
vendor/widevine/proprietary/include \
vendor/widevine/proprietary/streamcontrol/include \
vendor/widevine/proprietary/drmwvmplugin/include \
frameworks/av/drm/libdrmframework/include \
frameworks/av/drm/libdrmframework/plugins/common/include
LOCAL_C_INCLUDES_x86 += $(TOP)/system/core/include/arch/linux-x86
LOCAL_SHARED_LIBRARIES := \
libdrmframework \
liblog \
libutils \
libz \
libcutils \
libdl \
libWVStreamControlAPI_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL) \
libwvdrm_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE:=test-wvplayer_L$(BOARD_WIDEVINE_OEMCRYPTO_LEVEL)
LOCAL_MODULE_TARGET_ARCH := $(WIDEVINE_SUPPORTED_ARCH)
LOCAL_MULTILIB := 32
include $(BUILD_EXECUTABLE)

View File

@@ -1,689 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
#include <stdlib.h>
#include <unistd.h>
#include <termio.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <iostream>
#include <cstring>
#include <memory>
#include "WVStreamControlAPI.h"
#include "AndroidHooks.h"
#include "WVMDrmPlugin.h"
#include "drm/DrmInfoRequest.h"
#include "drm/DrmInfoStatus.h"
#include "drm/DrmConstraints.h"
#include "drm/DrmInfo.h"
#define AES_BLOCK_SIZE 16
using namespace std;
using namespace android;
#define DEFAULT_BLOCK_SIZE 16*1024
#define DEFAULT_PLAYBACK_BUFFER_SIZE 0*1024*1024
#define DEFAULT_START_TIME "now"
#define DEFAULT_DRM_URL "https://license.uat.widevine.com/getlicense/widevine"
#define SHOW_BITRATE 1
static void Terminate();
/**
* Print command line options
*/
void PrintUsage(char *prog)
{
printf("Usage: %s <options> url\n", prog);
printf(" %s <options> -L filename\n", prog);
printf(" -o output_file\n");
printf(" -b block_size (default: %d)\n", DEFAULT_BLOCK_SIZE);
printf(" -p playback_buffer_size (default: %d)\n", (int)DEFAULT_PLAYBACK_BUFFER_SIZE);
printf(" -m print PTS -> media time\n");
printf(" -s start_time (default: %s)\n", DEFAULT_START_TIME);
printf(" -d drm_url\n");
printf(" -L open filname on local file system\n");
exit(-1);
}
static IDrmEngine *sDrmPlugin = NULL;
static void *sSharedLibHandle = NULL;
static bool sWVInitialized = false;
static struct termios termattr, save_termattr;
static int ttysavefd = -1;
static enum
{
RESET, RAW, CBREAK
} ttystate = RESET;
/**
***************************************************************************
*
* set_tty_raw(), put the user's TTY in one-character-at-a-time mode.
*
* @returns 0 on success, -1 on failure.
*
*************************************************************************** */
int set_tty_raw(void)
{
int i;
i = tcgetattr(STDIN_FILENO, &termattr);
if (i < 0)
{
printf("tcgetattr() returned %d for fildes=%d\n",i,STDIN_FILENO);
perror("");
return -1;
}
save_termattr = termattr;
termattr.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
termattr.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON); /* | BRKINT */
termattr.c_cflag &= ~(CSIZE | PARENB);
termattr.c_cflag |= CS8;
/* termattr.c_oflag &= ~(OPOST); */
termattr.c_cc[VMIN] = 0; /* or 0 for some Unices; see note 1 */
termattr.c_cc[VTIME] = 0;
i = tcsetattr(STDIN_FILENO, TCSANOW, &termattr);
if (i < 0)
{
printf("tcsetattr() returned %d for fildes=%d\n",i,STDIN_FILENO);
perror("");
return -1;
}
ttystate = RAW;
ttysavefd = STDIN_FILENO;
return 0;
}
/**
* @return time of day in milliseconds
*/
static uint64_t get_time_in_ms()
{
uint64_t ms;
timeval t;
gettimeofday(&t, NULL);
ms = (uint64_t)(t.tv_sec) * 1000;
ms +=(uint64_t)(t.tv_usec) / 1000;
return ms;
}
/**
***************************************************************************
*
* set_tty_cooked(), restore normal TTY mode. Very important to call
* the function before exiting else the TTY won't be too usable.
*
* @returns 0 on success, -1 on failure.
*
*************************************************************************** */
int set_tty_cooked(void)
{
int i;
if (ttystate != CBREAK && ttystate != RAW)
{
return 0;
}
i = tcsetattr(STDIN_FILENO, TCSAFLUSH, &save_termattr);
if (i < 0)
{
return -1;
}
ttystate = RESET;
return 0;
}
/**
***************************************************************************
*
* kb_getc(), if there's a typed character waiting to be read,
*
* @return character; else return 0.
*
*************************************************************************** */
unsigned char kb_getc(void)
{
unsigned char ch;
ssize_t size;
size = read(STDIN_FILENO, &ch, 1);
if (size == 0)
{
return 0;
}
else
{
return ch;
}
}
static void PrintMessage(const char *msg)
{
printf("%s", msg);
}
static void OpenDrmPlugin()
{
const char *path = "/vendor/lib/drm/libdrmwvmplugin.so";
sSharedLibHandle = dlopen(path, RTLD_NOW);
if (sSharedLibHandle == NULL) {
fprintf(stderr, "Can't open plugin: %s\n", path);
Terminate();
}
typedef IDrmEngine *(*create_t)();
create_t creator = (create_t)dlsym(sSharedLibHandle, "create");
if (!creator) {
fprintf(stderr, "Can't find create method\n");
Terminate();
}
sDrmPlugin = (*creator)();
if (sDrmPlugin->initialize(0) != DRM_NO_ERROR) {
fprintf(stderr, "onInitialize failed!\n");
Terminate();
}
}
static void CloseDrmPlugin()
{
if (sSharedLibHandle) {
if (sDrmPlugin) {
typedef IDrmEngine *(*destroy_t)(IDrmEngine *plugin);
destroy_t destroyer = (destroy_t)dlsym(sSharedLibHandle, "destroy");
if (destroyer) {
(*destroyer(sDrmPlugin));
sDrmPlugin = NULL;
} else
fprintf(stderr, "Can't find destroy method\n");
}
dlclose(sSharedLibHandle);
sSharedLibHandle = NULL;
}
}
static void AcquireRights(IDrmEngine *plugin, string url, string drmUrl,
bool isLocal)
{
String8 mimeType("video/wvm");
DrmInfoRequest rightsAcquisitionInfo(DrmInfoRequest::TYPE_RIGHTS_ACQUISITION_INFO, mimeType);
int fdNum = -1;
rightsAcquisitionInfo.put(String8("WVDRMServerKey"), String8(drmUrl.c_str()));
rightsAcquisitionInfo.put(String8("WVAssetURIKey"), String8(url.c_str()));
if (isLocal) {
char fdStr[16];
fdNum = open(url.c_str(), O_RDONLY);
if (fdNum == -1) {
fprintf(stderr, "unable to open local movie file %s for reading.\n",
url.c_str());
close(fdNum);
Terminate();
}
sprintf(fdStr, "%lu", (unsigned long)fdNum);
rightsAcquisitionInfo.put(String8("FileDescriptorKey"), String8(fdStr));
}
rightsAcquisitionInfo.put(String8("WVDeviceIDKey"), String8("device1234"));
rightsAcquisitionInfo.put(String8("WVPortalKey"), String8("OEM"));
rightsAcquisitionInfo.put(String8("WVLicenseTypeKey"), String8("1"));
// Get asset rights from DRM. If it is necessary to
// read file metadata to get the asset ID, this can take several seconds.
DrmInfo *info = plugin->acquireDrmInfo(0, &rightsAcquisitionInfo);
if (info == NULL) {
fprintf(stderr, "acquireDrmInfo failed!\n");
if (isLocal) {
close(fdNum);
}
Terminate();
}
DrmInfoStatus *status = plugin->processDrmInfo(0, info);
if (status == NULL || status->statusCode != DrmInfoStatus::STATUS_OK) {
fprintf(stderr, "processDrmInfo failed!\n");
if (isLocal) {
close(fdNum);
}
Terminate();
}
if (plugin->checkRightsStatus(0, String8(url.c_str()), Action::DEFAULT) != RightsStatus::RIGHTS_VALID) {
fprintf(stderr, "checkValidRights default action failed!\n");
if (isLocal) {
close(fdNum);
}
Terminate();
}
if (isLocal) {
close(fdNum);
}
delete status;
delete info;
}
static void Terminate()
{
if (sWVInitialized)
WV_Terminate();
CloseDrmPlugin();
exit(-1);
}
static void _cb1(char *a, unsigned long b)
{
DrmBuffer buf(a, b);
sDrmPlugin->initializeDecryptUnit(0, NULL, 0, &buf);
}
/**
* Program entry pointer
*
* @return 0 for success, -1 for error
*/
int main( int argc, char *argv[] )
{
int option;
string url, outputFile, startTime = DEFAULT_START_TIME;
unsigned long blockSize = DEFAULT_BLOCK_SIZE;
unsigned long playbackBufferSize = DEFAULT_PLAYBACK_BUFFER_SIZE;
bool ptsToMediaTime = false;
string drmUrl = DEFAULT_DRM_URL;
bool isLocal = false;
_ah006(PrintMessage);
while ((option = getopt(argc, argv, "o:b:p:s:mD:d:L")) != -1) {
switch (option) {
case 'o':
outputFile = optarg;
break;
case 'b':
if (sscanf(optarg, "%lu", &blockSize) != 1)
PrintUsage(argv[0]);
break;
case 'p':
if (sscanf(optarg, "%lu", &playbackBufferSize) != 1)
PrintUsage(argv[0]);
break;
case 's':
startTime = optarg;
break;
case 'm':
ptsToMediaTime = true;
break;
case 'd':
drmUrl = optarg;
break;
case 'L':
isLocal = true;
break;
default:
printf("unknown option: '%c'\n", option);
PrintUsage(argv[0]);
}
}
if ((argc - optind) != 1)
PrintUsage(argv[0]);
url = argv[optind];
FILE *output = NULL;
if (outputFile.size()) {
output = fopen(outputFile.c_str(), "wb");
if (!output) {
fprintf(stderr, "unable to open output file %s for writing\n",
argv[2]);
Terminate();
}
}
// This turns off some verbose printing
setenv("WV_SILENT", "true", 1);
WVStatus status = WV_Initialize( NULL );
if (status != WV_Status_OK) {
fprintf(stderr, "ERROR: WV_Initialize returned status %d\n", (int)status);
Terminate();
} else
sWVInitialized = true;
// enable HTTP logging if you want to debug
WV_SetLogging(WV_Logging_HTTP);
OpenDrmPlugin();
// if isLocal is true, the url param holds a local filesystem filename
AcquireRights(sDrmPlugin, url, drmUrl, isLocal);
_ah002(_cb1);
/*
status = WV_StartBandwidthCheck( url.c_str() );
if (status != WV_Status_OK) {
fprintf(stderr, "ERROR: WV_CheckBandwidth returned status %d\n", (int)status);
Terminate();
}
unsigned long bandwidth;
do {
usleep(100000);
// The idea here is the bandwidth check is done in the background while the GUI/OSD is
// doing other things. In this example, we just wait for the result.
status = WV_GetBandwidthCheckStatus(&bandwidth);
} while (status == WV_Status_Checking_Bandwidth);
if (status == WV_Status_OK)
cout << "Bandwidth check " << bandwidth << endl;
else
cout << "Bandwidth check failed: " << status << endl;
*/
WVSession *session = 0;
WVCredentials credentials;
status = WV_Setup( session, url.c_str(), "RAW/RAW/RAW;destination=getdata", credentials);
if (status != WV_Status_OK) {
fprintf(stderr, "ERROR: WV_Setup returned status %d\n", (int)status);
if (status == 408)
fprintf(stderr, "TIMEOUT: Make sure your device is powered on and has a network connection\n");
else if (status == 404)
fprintf(stderr, "ASSET NOT FOUND: Make sure the URL you provided is correct\n");
Terminate();
}
WVMacrovision macrovision;
bool hdcp, cit;
status = WV_Info_GetCopyProtection(session, &macrovision, &hdcp, &cit);
switch (status) {
case WV_Status_OK:
printf("Copy protection: macrovison = %d, hdcp = %d, cit = %d\n", (int)macrovision, (int)hdcp, (int)cit);
break;
case WV_Status_Warning_Not_Available:
printf("Warning: Copy protection info not yet available\n");
status = WV_Status_OK;
break;
default:
fprintf(stderr, "ERROR: WV_Info_GetCopyProtection returned status %d\n", (int)status);
Terminate();
}
// Get audio and video config options
WVAudioType audioType;
unsigned short streamId;
unsigned short profile;
unsigned short numChannels;
unsigned long sampleFrequency;
unsigned long bitRate;
WVVideoType videoType;
unsigned short level;
unsigned short width;
unsigned short height;
float pixelAspectRatio;
float frameRate;
WV_Info_GetAudioConfiguration(session, &audioType, &streamId, &profile, &numChannels, &sampleFrequency, &bitRate);
printf("Audio type: %hu\n", audioType);
printf("Audio stream ID: %hu\n", streamId);
printf("Audio profile: %hu\n", profile);
printf("Audio channels: %hu\n", numChannels);
printf("Audio sampling freq: %lu\n", sampleFrequency);
printf("Audio bit rate: %lu\n", bitRate);
WV_Info_GetVideoConfiguration(session, &videoType, &streamId, &profile, &level, &width, &height, & pixelAspectRatio, &frameRate, &bitRate);
printf("Video type: %hu\n", videoType);
printf("Video stream ID: %hu\n", streamId);
printf("Video profile: %hu\n", profile);
printf("Video profile level: %hu\n", level);
printf("Video width: %hu\n", width);
printf("Video height: %hu\n", height);
printf("Video pixel aspect ratio: %f\n", pixelAspectRatio);
printf("Video frame rate: %f\n", frameRate);
printf("Video bit rate: %lu\n", bitRate);
float scale_used;
startTime += "-";
status = WV_Play( session, 1.0, &scale_used, startTime.c_str() );
if (status != WV_Status_OK) {
fprintf(stderr, "ERROR: WV_Play returned status %d\n", (int)status);
Terminate();
}
auto_ptr<uint8_t> buffer(new uint8_t[blockSize]);
size_t numBytes;
// fill playback buffer as quickly as possible
uint64_t bytesRead = 0;
while (bytesRead < playbackBufferSize) {
status = WV_GetData( session, buffer.get(), blockSize, &numBytes, 0 );
switch (status) {
case WV_Status_OK:
break;
case WV_Status_Warning_Download_Stalled:
case WV_Status_Warning_Need_Key:
fprintf(stderr, "WARNING: WV_GetData returned status %d\n", (int)status);
usleep(100000);
break;
default:
fprintf(stderr, "ERROR: WV_GetData returned status %d\n", (int)status);
Terminate();
break;
}
if (numBytes > 0) {
if (output)
fwrite(buffer.get(), numBytes, 1, output);
bytesRead += numBytes;
cout << "Read " << numBytes << "/" << bytesRead << " out of " << playbackBufferSize << endl;
}
}
set_tty_raw();
#if SHOW_BITRATE
unsigned long bitRates[32];
size_t numBitRates;
size_t curBitRate;
if (WV_Info_GetAdaptiveBitrates(session, bitRates, sizeof(bitRates)/sizeof(uint32_t),
&numBitRates, &curBitRate) == WV_Status_OK) {
printf("Bit Rates: ");
for (uint32_t idx = 0; idx < numBitRates; ++idx) {
if (idx == curBitRate)
printf("*%lu* ", bitRates[idx]);
else
printf("%lu ", bitRates[idx]);
}
printf("\n");
}
#endif
string nptTime = WV_Info_GetTime(session);
int hh, mm;
float ss;
sscanf(nptTime.c_str(), "%d:%d:%f", &hh, &mm, &ss);
uint64_t startMs = (uint64_t)((hh * 3600000) + (mm * 60000) + (ss * 1000));
uint64_t curMs = startMs;
uint64_t lastMs = curMs;
uint64_t baseTime = get_time_in_ms();
int trickPlayRate = 1;
bool quit = false;
while (!quit) {
uint64_t curTime = get_time_in_ms();
uint64_t streamTimeRef = (trickPlayRate >= 0) ? (curMs - startMs) : (startMs - curMs);
uint64_t clockRef = get_time_in_ms() - baseTime;
if (trickPlayRate)
clockRef *= trickPlayRate > 0 ? trickPlayRate : -trickPlayRate;
if (clockRef > streamTimeRef) {
// time for another pull
status = WV_GetData( session, buffer.get(), blockSize, &numBytes, 0 );
switch (status) {
case WV_Status_OK:
break;
case WV_Status_End_Of_Media:
printf("End of Media\n");
if (trickPlayRate < 0) {
WV_Play( session, 1.0, &scale_used, "00:00:00-" );
trickPlayRate = 1;
startMs = curMs;
baseTime = curTime;
} else
quit = true;
break;
case 1001:
fprintf(stderr, "ERROR: WV_GetData returned status %d\n", (int)status);
break;
case WV_Status_Warning_Download_Stalled:
case WV_Status_Warning_Need_Key:
fprintf(stderr, "WARNING: WV_GetData returned status %d\n", (int)status);
usleep(100000);
break;
default:
fprintf(stderr, "ERROR: WV_GetData returned status %d\n", (int)status);
Terminate();
break;
}
if (numBytes > 0) {
if (output)
fwrite(buffer.get(), numBytes, 1, output);
bytesRead += numBytes;
nptTime = WV_Info_GetTime(session);
sscanf(nptTime.c_str(), "%d:%d:%f", &hh, &mm, &ss);
curMs = (uint64_t)((hh * 3600000) + (mm * 60000) + (ss * 1000));
if (curMs != lastMs) {
int64_t msDif = (trickPlayRate >= 0) ? (curMs - lastMs) : (lastMs - curMs);
if ((msDif < 0) || (msDif > 60000)) {
// discontinuity
startMs = curMs;
baseTime = curTime;
printf("Current time (skip): %s\n", nptTime.c_str());
} else if ((curMs / 1000) != (lastMs / 1000)) {
printf("Current time: %s\n", nptTime.c_str());
#if SHOW_BITRATE
if (WV_Info_GetAdaptiveBitrates(session, bitRates, sizeof(bitRates)/sizeof(uint32_t),
&numBitRates, &curBitRate) == WV_Status_OK) {
printf("Bit Rates: ");
for (uint32_t idx = 0; idx < numBitRates; ++idx) {
if (idx == curBitRate)
printf("*%lu* ", bitRates[idx]);
else
printf("%lu ", bitRates[idx]);
}
printf("\n");
}
#endif
}
lastMs = curMs;
}
}
}
unsigned char kbhit = kb_getc();
switch (kbhit) {
case 'g':
char seekTime[256];
set_tty_cooked();
printf("Go to time: ");
if ((scanf("%s", seekTime) == 1) && (sscanf(seekTime, "%d:%d:%f", &hh, &mm, &ss) == 3)) {
status = WV_Play(session, 1, &scale_used, string(seekTime) + "-");
if (status != WV_Status_OK) {
fprintf(stderr, "ERROR: WV_Play returned status %d\n", (int)status);
Terminate();
}
startMs = curMs;
baseTime = curTime;
}
set_tty_raw();
break;
case 't':
set_tty_cooked();
printf("Trick-play rate (now): ");
if (scanf("%d", &trickPlayRate) == 1) {
printf( "Got a trick play value of %d\n", trickPlayRate );
if (trickPlayRate == 0)
trickPlayRate = 1;
status = WV_Play(session, trickPlayRate, &scale_used, "now-");
if (status != WV_Status_OK) {
fprintf(stderr, "ERROR: WV_Play returned status %d\n", (int)status);
Terminate();
}
startMs = curMs;
baseTime = curTime;
} else {
printf( "did not get a rate\n" );
}
set_tty_raw();
break;
case 'p':
set_tty_cooked();
printf("PTS: ");
uint64_t pts;
if (scanf("%llu", (long long unsigned int*)& pts) == 1) {
string mediaTime = WV_TimestampToMediaTime(session, pts, PTS);
printf("Media Time: \"%s\"\n", mediaTime.c_str());
}
set_tty_raw();
break;
case 'x':
quit = true;
break;
default:
break;
}
usleep(1000);
};
WV_Teardown( session );
WV_Terminate();
CloseDrmPlugin();
if (output)
fclose(output);
set_tty_cooked();
return(0);
}

View File

@@ -1,3 +0,0 @@
#!/bin/sh
PID=`adb shell ps | grep test-wvplayer | awk '{print $2}'`
adb shell kill -9 $PID

View File

@@ -1,27 +0,0 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(TOP)/vendor/widevine/proprietary/wvm/common.mk
ifeq ($(BOARD_WIDEVINE_OEMCRYPTO_LEVEL),1)
LOCAL_CFLAGS := -DREQUIRE_SECURE_BUFFERS
endif
LOCAL_SRC_FILES:= \
WVMLogging.cpp \
WVMExtractorImpl.cpp \
WVMFileSource.cpp \
WVMMediaSource.cpp \
WVMInfoListener.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright \
libstagefright_foundation
LOCAL_CFLAGS += -Wno-unused-parameter -Werror
LOCAL_MODULE := libwvmcommon
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := $(WIDEVINE_SUPPORTED_ARCH)
LOCAL_MULTILIB := 32
include $(BUILD_STATIC_LIBRARY)

View File

@@ -1,702 +0,0 @@
/*
* Copyright (C) 2011 Google, Inc. All Rights Reserved
*/
#define LOG_TAG "WVMExtractorImpl"
#include <utils/Log.h>
#include <cutils/qtaguid.h>
#include <cutils/properties.h>
#include "WVMExtractorImpl.h"
#include "WVMMediaSource.h"
#include "WVMFileSource.h"
#include "WVMInfoListener.h"
#include "WVMLogging.h"
#include "WVStreamControlAPI.h"
#include "media/stagefright/MediaErrors.h"
#include "media/stagefright/MediaDefs.h"
#include "drm/DrmManagerClient.h"
#include "drm/DrmConstraints.h"
#include "drm/DrmInfoEvent.h"
#include "AndroidHooks.h"
#define AES_BLOCK_SIZE 16
using namespace android;
static sp<DecryptHandle> sDecryptHandle;
static DrmManagerClient *sDrmManagerClient;
static void _cb1(char *data, unsigned long size)
{
DrmBuffer buf(data, size);
if (sDrmManagerClient != NULL) {
sDrmManagerClient->initializeDecryptUnit(sDecryptHandle, 0, &buf);
}
}
static int _cb2(char *in, char *out, int length, char *iv)
{
int status = -1;
if (sDrmManagerClient != NULL) {
DrmBuffer encryptedDrmBuffer(in, length);
DrmBuffer ivBuffer(iv, (iv? AES_BLOCK_SIZE: 0));
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
WVMLoadableExtractor *GetInstance(sp<DataSource> dataSource) {
return new WVMExtractorImpl(dataSource);
}
bool IsWidevineMedia(const sp<DataSource>& dataSource) {
String8 uri = dataSource->getUri();
if (uri.getPathExtension() == ".m3u8" || uri.find(".m3u8?") != -1) {
// can't sniff live streams - check for .m3u8 file extension
return true;
}
ssize_t kSniffSize = 128 * 1024;
char *buffer = new char[kSniffSize];
bool result = false;
if (buffer) {
ssize_t bytesRead = dataSource->readAt(0, buffer, kSniffSize);
if (bytesRead < kSniffSize) {
ALOGV("IsWidevineMedia - insufficient data: %d", (int)bytesRead);
} else {
setenv("WV_SILENT", "true", 1);
result = WV_IsWidevineMedia(buffer, kSniffSize);
}
delete[] buffer;
}
return result;
}
WVMExtractorImpl::WVMExtractorImpl(sp<DataSource> dataSource)
: mFileMetaData(new MetaData()),
mDataSource(dataSource),
mClientContext(new ClientContext()),
mHaveMetaData(false),
mUseAdaptiveStreaming(false),
mIsLiveStream(false),
mCryptoInitialized(false),
mSession(NULL),
mDuration(0),
mError(OK),
mSetupStatus(OK)
{
dataSource->getDrmInfo(sDecryptHandle, &sDrmManagerClient);
//ALOGD("WVMExtractorImpl::WVMExtractorImpl: uniqueId = %d", sDrmManagerClient->mUniqueId);
_ah006(android_printbuf);
_ah002(_cb1);
_ah004(_cb2);
if (sDecryptHandle != NULL) {
if (sDecryptHandle->status != RightsStatus::RIGHTS_VALID) {
mSetupStatus = ERROR_DRM_NO_LICENSE;
}
} else {
mSetupStatus = ERROR_DRM_NO_LICENSE;
}
// Set an info listener to handle messages from the drm plugin
mInfoListener = new WVMInfoListener();
sDrmManagerClient->setOnInfoListener(mInfoListener);
}
void WVMExtractorImpl::Initialize()
{
//ALOGD("WVMExtractorImpl::Initialize(%d)\n", getAdaptiveStreamingMode());
WVCredentials credentials;
WVStatus result;
if (mSetupStatus != OK) {
setError(mSetupStatus);
return;
}
if (mDataSource->getUri().size() > 0 && getAdaptiveStreamingMode()) {
mIsLiveStream = (mDataSource->getUri().getPathExtension().find(".m3u8") == 0);
}
WVCallbacks callbacks;
// The following memset is needed for 4.5.0 only, because WVCallbacks is a struct.
memset( &callbacks, 0, sizeof(callbacks));
callbacks.socketInfo = SocketInfoCallback;
#ifdef REQUIRE_SECURE_BUFFERS
if (!mClientContext->getCryptoPluginMode()) {
OEMCryptoResult res = OEMCrypto_Initialize();
if (res == OEMCrypto_SUCCESS) {
mCryptoInitialized = true;
} else {
ALOGE("Crypto initialize failed (%d)", res);
}
}
if (!mIsLiveStream) {
//ALOGD("WVMExtractorImpl::Initialize setting DecryptCallback\n");
callbacks.decrypt = WVMMediaSource::DecryptCallback;
}
#else
if (!mIsLiveStream && mClientContext->getCryptoPluginMode()) {
callbacks.decrypt = WVMMediaSource::DecryptCallback;
}
#endif
result = WV_Initialize(&callbacks);
if (result != WV_Status_OK) {
ALOGE("WV_Initialize returned status %d\n", result);
mSetupStatus = ERROR_IO;
} else {
// Enable for debugging HTTP messages
// WV_SetLogging(WV_Logging_HTTP);
if (mDataSource->getUri().size() > 0 && getAdaptiveStreamingMode()) {
// Use the URI - streaming case, only for widevine:// protocol
result = WV_Setup(mSession, mDataSource->getUri().string(),
"RAW/RAW/RAW;destination=getdata", credentials,
WV_OutputFormat_ES, getStreamCacheSize(), mClientContext.get());
} else {
// No URI supplied or not adaptive, pull data from the stagefright data source.
mFileSource = new WVMFileSource(mDataSource);
result = WV_Setup(mSession, mFileSource.get(),
"RAW/RAW/RAW;destination=getdata", credentials,
WV_OutputFormat_ES, getStreamCacheSize(), mClientContext.get());
}
if (result != WV_Status_OK) {
ALOGE("WV_Setup returned status %d in WVMMediaSource::start\n", result);
mSetupStatus = ERROR_IO;
WV_Teardown(mSession);
mSession = NULL;
} else {
mInfoListener->setSession(mSession);
}
}
if (mSetupStatus != OK) {
setError(mSetupStatus);
}
WV_SetWarningToErrorMS(10000);
}
WVMExtractorImpl::~WVMExtractorImpl() {
#ifdef REQUIRE_SECURE_BUFFERS
if (mCryptoInitialized) {
OEMCrypto_Terminate();
}
#endif
}
// Release decrypt handle when media sources are destroyed
void WVMExtractorImpl::cleanup()
{
if (sDecryptHandle.get()) {
sDecryptHandle.clear();
}
}
void WVMExtractorImpl::SocketInfoCallback(int fd, int closing, void *context)
{
//ALOGD("WVMExtractorImpl::SocketInfoCallback(%d, %d, %p)", fd, closing, context);
ClientContext *obj = (ClientContext *)context;
if (!obj) {
// Not an error, there are some cases where this is expected
return;
} else if (!obj->haveUID()) {
ALOGW("SocketInfoCallback: UID not set!");
return;
}
if (!closing) {
uint32_t kTag = *(uint32_t *)"WVEX";
int res = qtaguid_tagSocket(fd, kTag, obj->getUID());
if (res != 0) {
ALOGE("Failed tagging socket %d for uid %d (My UID=%d)", fd, obj->getUID(), geteuid());
}
} else {
int res = qtaguid_untagSocket(fd);
if (res != 0) {
ALOGE("Failed untagging socket %d (My UID=%d)", fd, geteuid());
}
}
}
//
// Configure metadata for video and audio sources
//
status_t WVMExtractorImpl::readMetaData()
{
if (mHaveMetaData) {
return OK;
}
Initialize();
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 videoBitRate;
WVStatus result = WV_Info_GetVideoConfiguration(mSession, &videoType, &videoStreamID,
&videoProfile, &level, &width, &height,
&aspect, &frameRate, &videoBitRate);
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;
unsigned long audioBitRate;
result = WV_Info_GetAudioConfiguration(mSession, &audioType, &audioStreamID, &audioProfile,
&numChannels, &sampleRate, &audioBitRate);
if (result != WV_Status_OK) {
return ERROR_MALFORMED;
}
if (numChannels == 0) {
ALOGD("numChannels is 0!");
return ERROR_MALFORMED;
}
std::string durationString = WV_Info_GetDuration(mSession, "sec");
if (durationString == "") {
// We won't have a duration for live streams, and Stagefright doesn't seem to
// have a way to represent that. Give a default duration of 1 hour for now.
if (mIsLiveStream) {
durationString = "3600";
} else {
return ERROR_MALFORMED;
}
}
mDuration = (int64_t)(strtod(durationString.c_str(), NULL) * 1000000);
sp<MetaData> audioMetaData = new MetaData();
sp<MetaData> videoMetaData = new MetaData();
audioMetaData->setInt64(kKeyDuration, mDuration);
videoMetaData->setInt64(kKeyDuration, mDuration);
audioMetaData->setInt32(kKeyBitRate, audioBitRate);
videoMetaData->setInt32(kKeyBitRate, videoBitRate);
switch(videoType) {
case WV_VideoType_H264:
videoMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
break;
default:
ALOGE("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:
ALOGE("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);
if (mIsLiveStream) {
float scaleUsed;
result = WV_Play(mSession, 1.0, &scaleUsed, "npt=now-");
if (result != WV_Status_OK) {
ALOGE("WV_Play for live stream setup failed: %d", result);
return ERROR_IO;
}
}
status_t status;
status = readESDSMetaData(audioMetaData);
if (status != OK) {
return status;
}
if (mIsLiveStream) {
result = WV_Pause(mSession, "");
if (result != WV_Status_OK) {
ALOGE("WV_Pause for live stream setup failed: %d", result);
}
}
bool cryptoPluginMode = mClientContext->getCryptoPluginMode();
mAudioSource = new WVMMediaSource(mSession, WV_EsSelector_Audio, audioMetaData,
mIsLiveStream, cryptoPluginMode, false);
mVideoSource = new WVMMediaSource(mSession, WV_EsSelector_Video, videoMetaData,
mIsLiveStream, cryptoPluginMode, mCryptoInitialized);
// responsibility for terminating has been delegated to WVMMediaSource
mCryptoInitialized = false;
// 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);
mClientContext->setAudioSource(mAudioSource);
mClientContext->setVideoSource(mVideoSource);
mVideoSource->delegateClientContext(mClientContext);
mHaveMetaData = true;
mInfoListener->configureHeartbeat();
if (cryptoPluginMode) {
// In crypto plugin mode, need to trigger the drm plugin to begin
// license use on playback since the media player isn't involved.
sDrmManagerClient->setPlaybackStatus(sDecryptHandle, Playback::START, 0);
#ifndef REQUIRE_SECURE_BUFFERS
if (!mIsLiveStream) {
// Get the content key for crypto plugin mode on L3 devices
char keyBuffer[AES_BLOCK_SIZE];
char nullBuffer[0];
DrmBuffer nullDrmBuffer(nullBuffer, 0);
DrmBuffer keyDrmBuffer(keyBuffer, sizeof(keyBuffer));
DrmBuffer *keyDrmBufferPtr = &keyDrmBuffer;
status_t status = sDrmManagerClient->decrypt(sDecryptHandle, 0, &nullDrmBuffer,
&keyDrmBufferPtr, &nullDrmBuffer);
if (status != OK) {
return status;
}
mVideoSource->SetCryptoPluginKey(keyBuffer);
mAudioSource->SetCryptoPluginKey(keyBuffer);
}
#endif
}
return OK;
}
status_t WVMExtractorImpl::readESDSMetaData(sp<MetaData> audioMetaData)
{
WVStatus result;
const unsigned char *config;
unsigned long size;
int limit = 500;
do {
size_t bytesRead;
bool auStart, sync;
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, sync);
result = WV_Info_GetCodecConfig(mSession, WV_CodecConfigType_ESDS, config, size);
if (result != WV_Status_OK) {
usleep(10000);
}
} while (result == WV_Status_Warning_Not_Available && limit-- > 0);
if (result != WV_Status_OK) {
ALOGE("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) {
ALOGD("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) {
setError(err);
return 0;
}
return 2; // 1 audio + 1 video
}
sp<IMediaSource> WVMExtractorImpl::getTrack(size_t index)
{
status_t err;
if ((err = readMetaData()) != OK) {
setError(err);
return NULL;
}
sp<MediaSource> result;
switch(index) {
case 0:
result = mVideoSource;
break;
case 1:
result = mAudioSource;
break;
default:
break;
}
return result;
}
sp<MetaData> WVMExtractorImpl::getTrackMetaData(size_t index, uint32_t flags)
{
status_t err;
if ((err = readMetaData()) != OK) {
setError(err);
return NULL;
}
sp<MetaData> 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<MetaData> WVMExtractorImpl::getMetaData() {
mFileMetaData->setCString(kKeyMIMEType, "video/wvm");
return mFileMetaData;
}
int64_t WVMExtractorImpl::getCachedDurationUs(status_t *finalStatus) {
const size_t kMaxEncodedRates = 32;
unsigned long encodedRates[kMaxEncodedRates];
size_t ratesReturned, currentTrack;
unsigned long minRate = ULONG_MAX;
unsigned long bandwidth = ULONG_MAX;
WVStatus status = WV_Info_GetAdaptiveBitrates(mSession, encodedRates, kMaxEncodedRates,
&ratesReturned, &currentTrack);
if (status == WV_Status_OK) {
if (currentTrack != kMaxEncodedRates && ratesReturned != 0) {
for (size_t i = 0; i < ratesReturned; i++) {
if (encodedRates[i] < minRate) {
minRate = encodedRates[i];
}
}
WV_Info_CurrentBandwidth(mSession, &bandwidth);
// Log the current adaptive rate every 5 seconds
time_t now = time(0);
static time_t lastLogTime = now;
if (now > lastLogTime + 5) {
lastLogTime = now;
ALOGI("using adaptive track #%d, rate=%ld, bandwidth=%ld\n",
currentTrack, encodedRates[currentTrack], bandwidth);
}
}
}
uint64_t durationUs = 0;
float secondsBuffered;
status = WV_Info_TimeBuffered(mSession, &secondsBuffered);
//
// As long as the bandwidth is greater than the min bitrate, don't
// need to consider rebuffering so return a buffered time greater
// than the low water mark. This provides fast startup when the
// network speed is good and rebuffering when it is not.
//
if (bandwidth != ULONG_MAX && minRate != ULONG_MAX && bandwidth >= minRate) {
//ALOGD("Bandwidth %ld is sufficient for lowest rate %ld, override buffered time", bandwidth, minRate);
durationUs = 10000000LL;
} else {
unsigned long kScaleFactor = 2; // scale to cached duration to tune watermark levels
durationUs = (uint64_t)(secondsBuffered * 1000000LL * kScaleFactor);
//ALOGD("Bandwidth %ld is too low for lowest rate %ld, use buffered time %lld", bandwidth, minRate, durationUs);
}
if (status == WV_Status_End_Of_Media) {
*finalStatus = ERROR_END_OF_STREAM;
} else if (status != WV_Status_OK) {
*finalStatus = ERROR_IO;
} else {
if (mIsLiveStream) {
*finalStatus = ERROR_END_OF_STREAM;
} else {
*finalStatus = OK;
int64_t current_time = 0; // usec.
if (mVideoSource != NULL) {
current_time = mVideoSource->getTime();
} else {
ALOGV("getCachedDurationUs: current_time not yet valid.");
}
// ALOGD("current_time=%.2f, duration %.2f, delta = %.2f, buffered=%.2f",
// current_time/1e6, mDuration/1e6,
// (mDuration - current_time )/1e6, time_buffered);
// If we are less than 10 seconds from end, report we are at the end.
if (mDuration > 0 && mDuration - current_time < 10000000) {
*finalStatus = ERROR_END_OF_STREAM;
}
}
}
return durationUs;
}
status_t WVMExtractorImpl::getEstimatedBandwidthKbps(int32_t *kbps)
{
status_t err = UNKNOWN_ERROR;
unsigned long bandwidth;
WVStatus status = WV_Info_CurrentBandwidth(mSession, &bandwidth);
if (status == WV_Status_OK) {
*kbps = bandwidth / 1024;
err = OK;
} else {
ALOGV("WV_Info_CurrentBandwidth failed: %d", status);
}
return err;
}
void WVMExtractorImpl::setAdaptiveStreamingMode(bool adaptive)
{
//ALOGD("WVMExtractorImpl::setAdaptiveStreamingMode(%d)", adaptive);
mUseAdaptiveStreaming = adaptive;
}
bool WVMExtractorImpl::getAdaptiveStreamingMode() const
{
//ALOGD("WVMExtractorImpl::getAdaptiveStreamingMode - %d", mUseAdaptiveStreaming);
return mUseAdaptiveStreaming;
}
void WVMExtractorImpl::setCryptoPluginMode(bool cryptoPluginMode)
{
//ALOGD("WVMExtractorImpl::setCryptoPluginMode(%d)", cryptoPluginMode);
mClientContext->setCryptoPluginMode(cryptoPluginMode);
}
void WVMExtractorImpl::setUID(uid_t uid)
{
//ALOGD("WVMExtractorImpl::setUID(%d)", (uint32_t)uid);
mClientContext->setUID(uid);
}
size_t WVMExtractorImpl::getStreamCacheSize() const
{
char value[PROPERTY_VALUE_MAX];
if (property_get("ro.com.widevine.cachesize", value, NULL) > 0) {
return atol(value);
} else {
return kDefaultStreamCacheSize;
}
}
status_t WVMExtractorImpl::getError() {
Mutex::Autolock autoLock(mLock);
status_t err = mError;
mError = OK;
//
// MediaPlayer.java documents these MEDIA_ERROR codes for applications:
// MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED, MEDIA_ERROR_IO,
// MEDIA_ERROR_MALFORMED, MEDIA_ERROR_UNSUPPORTED and MEDIA_ERROR_TIMED_OUT.
// In order to not change the behavior of WVMExtractorImpl.cpp,
// we return all ERROR_ codes used by the WVMExtractor in addition to
// those documented in MediaPlayer.java.
//
if (err == ERROR_DRM_NO_LICENSE || err == ERROR_END_OF_STREAM ||
err == ERROR_IO || err == ERROR_MALFORMED || err == OK ||
err == ERROR_UNSUPPORTED) {
return err;
} else {
return UNKNOWN_ERROR;
}
}
void WVMExtractorImpl::setError(status_t err) {
Mutex::Autolock autoLock(mLock);
mError = err;
}
} // namespace android

Some files were not shown because too many files have changed in this diff Show More