Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

qing的世界發表於2018-03-19

之前因為種種原因好久沒有更新視訊開發的文章了。今天剛剛從國內飛回來,趁著週末更新一下.不過關於DRM這塊首先一般的開發者很少用到,而且DRM的開發需要前後臺的密切合作,可以說後臺的工作佔了一大半,安卓前端這塊DRM的API封裝其實已經很到位了,只是因為接觸的人少,所以文件並沒有多麼而已。所以這篇文章只是給大家過一遍概念,細節我就少講一些。有具體的問題可以留言,

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

今天主要開始講解一下安卓視訊開發的DRM這個問題,DRM是英文Digital rights management的縮寫,可以理解為版權保護。眾所周知,視訊或者音訊的盜版問題是困擾發行商的一個大麻煩,因為盜版的橫行會直接導致發行商利潤的減少。那麼像在PC端或者移動端的線上/離線多媒體內容的播放上,發行商又能怎麼解決呢?

比如最近優酷很火的《白夜追凶》這種電視劇,

附上我超級喜歡的潘老師。。。

vip的會員可以享受離線觀看。假如說這種型別的檔案沒有進行版權保護,或者說加密,那麼會員機制就會輕易作廢.(我可以申請一個會員,然後把檔案從SD卡中複製貼上並且傳送到網上)

所以一般來說,對這種premiere content(因為我們公司也是做電視劇,老闆都這麼叫,中文翻譯應該可以說是付費內容???),都需要對當前檔案,例如MP4檔案的audio或者video track部分的內容進行加密,但是metadata部分不加密。只有在使用者登入之後,進行身份驗證了才傳一個金鑰用來對該視訊進行解密。

那麼問題來了

在安卓平臺上的視訊怎麼做DRM的解析?

在回答這個問題之前我們先了解一下當前移動端的DRM的一些概念。


1.DRM platform

DRM 平臺可以理解為DRM服務的提供商,它提供了一整套DRM的服務方案,從前端到後端,這裡大家可以把這種服務理解為一套帶引號的SDK,不同的提供商在伺服器端和客戶端會要求不同的資料傳輸格式。因平臺而異,安卓的裝置普遍擁有Widevine這個服務(在framework層),是近幾年才被google收購的。

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

從上圖可以看出,現有的成熟的DRM平臺並不多,安卓端的話一般使用的是第一個。


2.DRM 是怎麼工作的?

簡單點來講,DRM的後臺,即伺服器端的工作其實和大部分視訊內容分發處理後臺沒有太大的區別,唯一的不同就是它需要對視訊資料部分進行適當的加密(在這裡我們不討論加密演算法)。

而客戶端呢就需要相應的獲得解密的祕鑰對視訊內容解密,值得注意的是DRM裡面祕鑰一般被稱license而不是key。整個過程可以用以下的流程圖來解釋。

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)


3.一般的DRM平臺提供商的任務

我們這一部分來詳細的瞭解一下DRM的平臺提供商的任務(當然平臺提供商並不是必需的,如果企業自己有能力做一整套解決方案那也ok,不過這整篇文章你也不用看了?)。

Widevine的後臺為例,widevine自從被google收購之後就將其後臺發展為類似雲平臺的PASS服務了,企業的後臺可以購買Widevine的服務,服務提供加密的API組還有資料庫容量,用來儲存企業視訊的license,即解密祕鑰。至於身份驗證啊等等功能就留給企業後臺自己完成。以下示意圖可以大概解釋一下Widevine的DRM流程

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

在第四步之後的流程裡面,企業後臺會根據播放器的請求去返回一個向Widevine資料庫請求license 的URL,播放器在獲得視訊URL和license URL之後就可以開始播放了。

這是最naive的實現。原因是現在的視訊服務出現了很多中間商。。。。 :cat:

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

因為中間涉及很多步驟,所以很多小型的企業決定不要自己去和google的Widevine伺服器打交道,而是交給一些視訊服務的中間商去做這個事情,然後和這些中間商做身份驗證,這樣免去了很多麻煩,api相對簡單,弊端當然就是要多付錢了。。。

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

不過無論怎麼樣,安卓平臺上播放DRM視訊的宗旨就是獲取視訊url加上解密用的license。就是這麼簡單


4.Android的DRM例項程式碼

說了這麼多,終於到安卓的重點了,程式碼怎麼寫?????

安卓端的DRM比較蛋疼,因為對於使用MediaCodec API組的和使用原生的MediaPlayer API的開發者來說,兩者的程式碼完全不相同。因為DRM開發者比較少,谷歌的官方文件也不完整,而且很難被理解,我也是研究了很久文件才整明白。

先說MediaCodec API的DRM:

4.1 MediaCodec API組的DRM處理

在這個官方文件裡面已經講的很詳細了,如果使用MediaCodec進行decode的時候,configure()方法需要傳進一個MediaCrypto

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

首先我們需要建立一個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…

重點在這個地方:

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

這麼重要的事情。。。。谷歌就這麼輕描淡寫的在這個文件裡面隨便一提。

Android視訊開發進階(part5-安卓的DRM,視訊版權保護)

也就是說,其實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的用法,就不詳細展開我工作中具體遇到的麻煩了,有問題可以直接留言或者私信~

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

參考文件

相關文章