最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android drm 播放器,Android

运维笔记admin124浏览0评论

android drm 播放器,Android

android drm 播放器,Android

前言:

前面我们讲了在Mediacodec中如何实现DRM的(详见: ),本文我们将讲述如何在MediaPlayer中播放加密的视频文件。

APP中的调用过程:

首先我们看下,APP是怎么调用MediaPlayer java类的。

void playvideo{

MediaPlayer mplayer;

mplayer->setDataSource(.)

mplayer->setOnDrmConfigHelper(.) // setOnDrmConfigHelper

mplayer->prepare(.)

if(drmvideo){

mplayer->prepareDrm(.) // prepareDrm

mplayer->getKeyRequest(.)

mplayer->provideKeyResponse(.)

}

else{

mplayer->prepare(.)

}

// MediaPlayer is now ready to use

start(.)

// ...play/pause/resume...

stop()

releaseDrm(.)

}

和播放普通视频相比,播放加密视频需要调用: setOnDrmConfigHelper、prepareDrm、getKeyRequest、provideKeyResponse 等函数。

具体这些函数怎么实现的呢?我们继续往下看:

// frameworks/base/media/java/android/media/MediaPlayer.java

public void setOnDrmConfigHelper(OnDrmConfigHelper listener)

{

synchronized (mDrmLock) {

mOnDrmConfigHelper = listener;

// 这个函数很简单,就是把输入listener赋值非mOnDrmConfigHelper

}

}

public void prepareDrm(@NonNull UUID uuid) // 输入为uuid

throws UnsupportedSchemeException, ResourceBusyException,

ProvisioningNetworkErrorException, ProvisioningServerErrorException

{

Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);

boolean allDoneWithoutProvisioning = false;

// get a snapshot as we'll use them outside the lock

OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate = null;

synchronized (mDrmLock) {

// 很多异常处理,忽略

cleanDrmObj(); // 清理此前的对象

mPrepareDrmInProgress = true;

// local copy while the lock is held

onDrmPreparedHandlerDelegate = mOnDrmPreparedHandlerDelegate;

try {

// only creating the DRM object to allow pre-openSession configuration

prepareDrm_createDrmStep(uuid); // 此处会创建MediaDrm

} catch (Exception e) {

Log.w(TAG, "prepareDrm(): Exception ", e);

mPrepareDrmInProgress = false;

throw e;

}

mDrmConfigAllowed = true;

} // synchronized

// call the callback outside the lock

if (mOnDrmConfigHelper != null) {

mOnDrmConfigHelper.onDrmConfig(this);

}

synchronized (mDrmLock) {

mDrmConfigAllowed = false;

boolean earlyExit = false;

try {

prepareDrm_openSessionStep(uuid);

mDrmUUID = uuid;

mActiveDrmScheme = true;

allDoneWithoutProvisioning = true;

} catch (IllegalStateException e) {

final String msg = "prepareDrm(): Wrong usage: The player must be " +

"in the prepared state to call prepareDrm().";

Log.e(TAG, msg);

earlyExit = true;

throw new IllegalStateException(msg);

} catch (NotProvisionedException e) {

Log.w(TAG, "prepareDrm: NotProvisionedException");

// handle provisioning internally; it'll reset mPrepareDrmInProgress

int result = HandleProvisioninig(uuid);

// if blocking mode, we're already done;

// if non-blocking mode, we attempted to launch background provisioning

if (result != PREPARE_DRM_STATUS_SUCCESS) {

earlyExit = true;

String msg;

switch (result) {

case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:

msg = "prepareDrm: Provisioning was required but failed " +

"due to a network error.";

Log.e(TAG, msg);

throw new ProvisioningNetworkErrorException(msg);

case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:

msg = "prepareDrm: Provisioning was required but the request " +

"was denied by the server.";

Log.e(TAG, msg);

throw new ProvisioningServerErrorException(msg);

case PREPARE_DRM_STATUS_PREPARATION_ERROR:

default: // default for safeguard

msg = "prepareDrm: Post-provisioning preparation failed.";

Log.e(TAG, msg);

throw new IllegalStateException(msg);

}

}

// nothing else to do;

// if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup

} catch (Exception e) {

Log.e(TAG, "prepareDrm: Exception " + e);

earlyExit = true;

throw e;

} finally {

if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception

mPrepareDrmInProgress = false;

}

if (earlyExit) { // cleaning up object if didn't succeed

cleanDrmObj();

}

} // finally

} // synchronized

// if finished successfully without provisioning, call the callback outside the lock

if (allDoneWithoutProvisioning) {

if (onDrmPreparedHandlerDelegate != null)

onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS);

}

}

5.2 MediaPlayer API组的DRM处理

APP调用MediaPlayer的prepareDrm,传入了参数uuid

MediaPlayer.java 的prepareDrm 中首先调用prepareDrm_createDrmStep新建MediaDrm,然后调用prepareDrm_openSessionStep最终调用native的prepareDrm。

MediaPlayer.java 的 prepareDrm_createDrmStep 中新建MediaDrm: mDrmObj = new MediaDrm(uuid);

MediaPlayer.java 的prepareDrm_openSessionStep首先获取mDrmSessionId = mDrmObj.openSession(); 然后调用 _prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);

JNI层: android_media_MediaPlayer_prepareDrm 中获取 uuid = JByteArrayToVector(env, uuidObj); 和 drmSessionId = JByteArrayToVector(env, drmSessionIdObj);

然后调用native层 mp->prepareDrm(uuid.array(), drmSessionId);

MediaPlayer::prepareDrm

NuPlayer::prepareDrm 发送消息 kWhatPrepareDrm,

NuPlayer::onPrepareDrm 在此函数中调用NuPlayer::GenericSource::prepareDrm获取Crypto,并让mCrypto = crypto;此处的mCrypto就用于MediaCodec::configure。

NuPlayer::GenericSource::prepareDrm 调用 crypto = NuPlayerDrm::createCryptoAndPlugin(uuid, drmSessionId, status) 获取 crypto

NuPlayerDrm::createCryptoAndPlugin crypto = createCrypto(&status)

NuPlayerDrm::createCrypto 首先通过binder获取 "media.drm" service,然后调用service的makeCrypto获取crypto。

上面详细介绍了mCrypto由来,但是mCrypto有什么具体作用呢?

用于MediaCodec::configure。把mCrypto传入mediacodec。接下来就和5.1的处理类似。

NuPlayer::Decoder::onInputBufferFetched 首先通过NuPlayerDrm::getSampleCryptoInfo(meta_data)获取cryptInfo,然后调用MediaCodec的queueSecureInputBuffer,并把cryptInfo的信息传入。

在NuPlayer2Decoder.cpp中,当有input数据传来时,会判断cryptInfo是否为NULL,若为NULL表示当前数据不是加密的,直接调用MediaCodec的queueInputBuffer即可,否则调用queueSecureInputBuffer。在queueSecureInputBuffer中会设置解密秘钥给底层。后面的操作如下:

queueSecureInputBuffer发送消息kWhatQueueInputBuffer

MediaCodec::onQueueInputBuffer 响应消息kWhatQueueInputBuffer

ACodecBufferChannel::queueSecureInputBuffer 在此函数中会调用decrypt进行解密处理。然后发送ebd消息即可。

如果codec的componentname是以".secure"结束的,就会标记mFlags包含kFlagIsSecure。在后面的allocateBuffersOnPort中就会调用OMX的allocateSecureBuffer。

发布评论

评论列表(0)

  1. 暂无评论