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的用法,就不詳細展開我工做中具體遇到的麻煩了,有問題能夠直接留言或者私信~

剛剛從國內回新就感冒了,這幾天昏昏沉沉的。。。。要再點睡覺好好休息了。

參考文檔

相關文章
相關標籤/搜索