Android视频开发进阶(part5-安卓的DRM,视频版权保护)

以前由于种种缘由很久没有更新视频开发的文章了。今天刚刚从国内飞回来,趁着周末更新一下.不过关于DRM这块首先通常的开发者不多用到,并且DRM的开发须要先后台的密切合做,能够说后台的工做占了一大半,安卓前端这块DRM的API封装其实已经很到位了,只是由于接触的人少,因此文档并无多么而已。因此这篇文章只是给你们过一遍概念,细节我就少讲一些。有具体的问题能够留言,html

今天主要开始讲解一下安卓视频开发的DRM这个问题,DRM是英文Digital rights management的缩写,能够理解为版权保护。众所周知,视频或者音频的盗版问题是困扰发行商的一个大麻烦,由于盗版的横行会直接致使发行商利润的减小。那么像在PC端或者移动端的在线/离线多媒体内容的播放上,发行商又能怎么解决呢?前端

好比最近优酷很火的《白夜追凶》这种电视剧,java

附上我超级喜欢的潘老师。。。

vip的会员能够享受离线观看。假如说这种类型的文件没有进行版权保护,或者说加密,那么会员机制就会轻易做废.(我能够申请一个会员,而后把文件从SD卡中复制粘贴而且发送到网上)android

因此通常来讲,对这种premiere content(由于咱们公司也是作电视剧,老板都这么叫,中文翻译应该能够说是付费内容???),都须要对当前文件,例如MP4文件的audio或者video track部分的内容进行加密,可是metadata部分不加密。只有在用户登陆以后,进行身份验证了才传一个密钥用来对该视频进行解密。git

那么问题来了算法

在安卓平台上的视频怎么作DRM的解析?

在回答这个问题以前咱们先了解一下当前移动端的DRM的一些概念。数据库


1.DRM platform

DRM 平台能够理解为DRM服务的提供商,它提供了一整套DRM的服务方案,从前端到后端,这里你们能够把这种服务理解为一套带引号的SDK,不一样的提供商在服务器端和客户端会要求不一样的数据传输格式。因平台而异,安卓的设备广泛拥有Widevine这个服务(在framework层),是近几年才被google收购的。后端

从上图能够看出,现有的成熟的DRM平台并很少,安卓端的话通常使用的是第一个。api


2.DRM 是怎么工做的?

简单点来说,DRM的后台,即服务器端的工做其实和大部分视频内容分发处理后台没有太大的区别,惟一的不一样就是它须要对视频数据部分进行适当的加密(在这里咱们不讨论加密算法)。服务器

而客户端呢就须要相应的得到解密的秘钥对视频内容解密,值得注意的是DRM里面秘钥通常被称license而不是key。整个过程能够用如下的流程图来解释。


3.通常的DRM平台提供商的任务

咱们这一部分来详细的了解一下DRM的平台提供商的任务(固然平台提供商并非必需的,若是企业本身有能力作一整套解决方案那也ok,不过这整篇文章你也不用看了😁)。

Widevine的后台为例,widevine自从被google收购以后就将其后台发展为相似云平台的PASS服务了,企业的后台能够购买Widevine的服务,服务提供加密的API组还有数据库容量,用来保存企业视频的license,即解密秘钥。至于身份验证啊等等功能就留给企业后台本身完成。如下示意图能够大概解释一下Widevine的DRM流程

在第四步以后的流程里面,企业后台会根据播放器的请求去返回一个向Widevine数据库请求license 的URL,播放器在得到视频URL和license URL以后就能够开始播放了。

这是最naive的实现。缘由是如今的视频服务出现了不少中间商。。。。 :cat:

由于中间涉及不少步骤,因此不少小型的企业决定不要本身去和google的Widevine服务器打交道,而是交给一些视频服务的中间商去作这个事情,而后和这些中间商作身份验证,这样免去了不少麻烦,api相对简单,弊端固然就是要多付钱了。。。

不过不管怎么样,安卓平台上播放DRM视频的宗旨就是获取视频url加上解密用的license。就是这么简单


4.Android的DRM实例代码

说了这么多,终于到安卓的重点了,代码怎么写?????

安卓端的DRM比较蛋疼,由于对于使用MediaCodec API组的和使用原生的MediaPlayer API的开发者来讲,二者的代码彻底不相同。由于DRM开发者比较少,谷歌的官方文档也不完整,并且很难被理解,我也是研究了好久文档才整明白。

先说MediaCodec API的DRM:

4.1 MediaCodec API组的DRM处理

在这个官方文档里面已经讲的很详细了,若是使用MediaCodec进行decode的时候,configure()方法须要传进一个MediaCrypto

首先咱们须要建立一个MediaDrm对象而且调用其openSession方法,该方法会返回一个sessionID,标识该次解码工做。

第二步咱们须要建立一个MediaCrypto对象给MediaCodec 对象。 它须要一个UUID和initdata,UUID是Widevine的Scheme ID,在Exoplayer的源码中能够看到,在C.java里面。而initData就是上面说到的sessionID.

public static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);

复制代码

最后咱们还须要对license server作license的call,获得的reponse就是咱们须要的license了,此时只须要调用MediaDrm的provideKeyResponse()方法,视频就能够自动开始播放了。

因此其实总结一下,MediaCodec负责解码,它须要一个MediaCrypto对象,同时须要一个MediaDrm对象,前者获取后者的sessionId让framework去寻找对应的license,后者负责保存从服务器下载下来的license而且提供一个惟一的sessionId给前者。附上伪代码

public static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
//获取sessionId
MediaDrm mediaDrm = new MediaDrm();
String sessionId = mediaDrm.openSession();
//建立MediaCrypto
/** sessionId 是串联 MediaDrm和MediaCrypto的关键 **/
MediaCrypto ctypto = new MediaCrypto(WIDEVINE_UUID, sessionId)
//用cypto对象来进行解密
MediaCodec codec = new MediaCodec("xxxx")
codec.configure(..,...,ctypto)
/** 注意的是license并不须要在configure以前获取,能够稍后再进行 **/

//网络链接
byte[] license = HttpUrlConnection.connect().......
mediaDrm.provideKeyResponse(xxx,license);

/** 全部工做结束,视频能够正常播放了。 **/


复制代码

MediaCodec的drm处理文档比较齐全,因此问题不大,具体源码仍是又不懂的能够参考ExoPlayer的代码, StreamingDrmSessionManager.java里面整个流程都有。


4.2 MediaPlayer API组的DRM处理

最后一个难点来了,就是原生的播放器MediaPlayer的DRM处理,这部分着实让我苦恼了好久,由于网上资料不多文档极其不齐全。若是你直接谷歌搜索MediaPlayer DRM,是没有任何结果的。。。你也不会知道MediaCodec和MediaPlayer处理DRM有任何不一样。

不过功夫不负有心人,费劲千辛万苦以后我终于找到了一丢丢线索。先看官方文档。

developer.android.com/reference/a…

重点在这个地方:

这么重要的事情。。。。谷歌就这么轻描淡写的在这个文档里面随便一提。

也就是说,其实MediaPlayer播放视频的时候,是不须要传任何相似MediaCrypto之类的对象的,直接用DrmManagerClient进行相关操做,framework层会自动处理解密工做了。

下面附上伪代码:

public static final String WV_DRM_SERVER_KEY = "WVDRMServerKey";
    public static final String WV_ASSET_URI_KEY = "WVAssetURIKey";
    public static final String WV_DEVICE_ID_KEY = "WVDeviceIDKey";
    public static final String WV_PORTAL_KEY = "WVPortalKey";


/** 调用该方法进行解密,执行成功就ok了 **/

  public void acquireKey(){
        DrmInfoRequest drmInfoRequest = createDrmInfoRequest(assetUrl, infoHolder.getDrmLisenceUrl());
        DrmInfo drmInfo = mDrmManager.acquireDrmInfo(drmInfoRequest);
        int rights = mDrmManager.acquireRights(drmInfoRequest);
    }
    
  
  
  /** licenseServerUri 就是 对license server进行http通讯的Url **/
  
   private DrmInfoRequest createDrmInfoRequest(String assetUri, String licenseServerUri) {
        DrmInfoRequest rightsAcquisitionInfo;
        rightsAcquisitionInfo = new DrmInfoRequest(DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_INFO,
                WIDEVINE_MIME_TYPE);

        if (licenseServerUri != null) {
            rightsAcquisitionInfo.put(WV_DRM_SERVER_KEY, licenseServerUri);
        }
        rightsAcquisitionInfo.put(WV_ASSET_URI_KEY, assetUri);
        rightsAcquisitionInfo.put(WV_DEVICE_ID_KEY, mDeviceId);
        rightsAcquisitionInfo.put(WV_PORTAL_KEY, PORTAL_NAME);

        return rightsAcquisitionInfo;
    }


复制代码

因此说MediaPlayer的DRM处理更加简单暴力。。。。固然DrmManagerClient还有其余的一些操做,好比说callback的注册等等。。。

这期的文章就到这,由于Drm涉及到不少细节的处理,还有和后台沟通的问题,这里我只是大概介绍一下Drm的概念和不一样类型的api的用法,就不详细展开我工做中具体遇到的麻烦了,有问题能够直接留言或者私信~

刚刚从国内回新就感冒了,这几天昏昏沉沉的。。。。要再点睡觉好好休息了。

参考文档

相关文章
相关标签/搜索