基於HLS流媒體協議視頻加密的解決方案

本文只討論應用於瀏覽器環境的流媒體協議的加密。javascript

背景

付費觀看視頻的模式是不少平臺的核心業務,若是視頻被錄製並不是法傳播,付費業務將受到嚴重威脅。所以對視頻服務進行加密的技術變得尤其重要。css

本文所指的視頻加密是爲了讓要保護的視頻不能輕易被下載,即便下載到了也是加密後的內容,其它人解開加密後的內容須要付出很是大的代價。html

沒法作到嚴格的讓要保護的視頻不被錄製,緣由在於你須要在客戶端播放出視頻的原內容,解密的流程在客戶端的話不法分子就能模擬整個流程,最保守也能用屏幕錄製軟件錄製到視頻的原內容(能夠經過加水印的方法緩解下)。咱們的目標是讓他獲取原內容的代價更大。前端

本文爲你們提供了一套簡單的基於HLS流媒體協議,使用video.js + NodeJS + FFmpeg等相關技術實現的m3u8+ts+aes128視頻加密及播放的解決方案示例。java

目錄

項目簡介

  起初是爲了將工做中已有的基於Flash的視頻播放器替換爲不依賴Flash的HTML5視頻播放器,主要使用了現有的video.js開源播放器作的定製化開發。當完成視頻播放器的製做後,在進一步延伸Web端視頻加密的相關內容時,開始瞭解並逐漸深刻的研究了相關視頻加密內容。最終經過整理概括,以及自身的理解,作了這個簡單的Demo。目的是爲了可以給在視頻加密這方面有相同目的的道友提供微薄的幫助,要是能起到拋磚引玉的效果,天然是再好不過了。node

項目啓動

1.安裝項目環境git

  • 安裝node、npm環境
  • 根據app目錄下的package.json安裝對應的npm包
  • 安裝ffmpeg

2.啓動項目github

  • 在app目錄下,輸入npm start,啓動項目
  • 在瀏覽器中訪問 http://localhost:3000
  • 按照頁面中的順序進行相關操做

3.權限登陸web

  • 用戶名:admin
  • 密碼:admin

項目原理

  本項目的核心原理其實就是講解了一個視頻源從正常的mp4格式如何變爲加密後的m3u8文件+ts文件+key祕鑰文件,以後又如何在服務端被限制訪問,最終可以在客戶端正常播放的視頻加密、解密並播放的流程。express

項目原理圖示

項目原理圖

技術棧

  • NodeJS + Express 實現服務器開發
  • FFmpeg + fluent-ffmpeg 實現node環境下的視頻轉碼、加密
  • socket.io 經過websocket相關的類庫,實現實時輸出FFmpeg進行的視頻轉碼、加密操做
  • video.js + videojs-contrib-hls.js 實現客戶端的視頻解密及播放
  • html + css + js 實現簡單的前端開發

源碼簡析

項目目錄說明

video-hls-encrypt/                   .............................. hls視頻加密項目根目錄
├── app/                             .............................. express框架默認的app根目錄
│   ├── bin/                         .............................. express框架啓動的bin目錄
│   │   └── www                      .............................. express框架啓動的www文件
│   ├── controllers/                 .............................. 項目控制器目錄,服務器相關的邏輯代碼
│   │   ├── encrypt.js               .............................. 加密邏輯代碼
│   │   └── upload.js                .............................. 上傳邏輯代碼
│   ├── node_modules/                .............................. express框架須要的相關npm依賴包,即package.json文件相對應的依賴包
│   │   └── ...
│   ├── public/                      .............................. express框架靜態文件目錄,客戶端請求的相關靜態文件
│   │   ├── javascripts              .............................. 客戶端的js文件目錄
│   │   │   ├── encrypt.js           .............................. 加密功能相關邏輯代碼
│   │   │   ├── index.js             .............................. 主頁相關邏輯代碼
│   │   │   ├── player.js            .............................. 播放器相關邏輯代碼
│   │   │   ├── socket.io.js         .............................. socket.io.js 類庫源文件
│   │   │   └── utils.js             .............................. 工具類
│   │   ├── key/                     .............................. 祕鑰相關目錄
│   │   │   ├── encrypt.key          .............................. 祕鑰文件
│   │   │   └── key_info.key         .............................. ffmpeg加密視頻轉換相關文件
│   │   ├── libs/                    .............................. 第三方類庫目錄
│   │   │   ├── videojs/             .............................. videojs 相關代碼
│   │   │   └── videojs-contrib-hls/ .............................. videojs-contrib-hls 相關代碼
│   │   ├── stylesheets/             .............................. css樣式目錄
│   │   │   └── common.css           .............................. 通用樣式表
│   │   └── videos/                  .............................. 視頻資源目錄
│   │       ├── encrypt/             .............................. 加密後的視頻資源目錄
│   │       └── noencrypt/           .............................. 加密前的視頻資源目錄
│   ├── routes/                      .............................. express框架路由目錄
│   │   └── router.js                .............................. express路由
│   ├── views/                       .............................. express框架ejs模板目錄
│   │   ├── encrypt.ejs              .............................. 視頻加密頁面
│   │   ├── error.ejs                .............................. 錯誤頁面
│   │   ├── index.ejs                .............................. 主頁
│   │   ├── login.ejs                .............................. 登陸頁面
│   │   ├── player.ejs               .............................. 播放器頁面
│   │   └── upload.ejs               .............................. 上傳視頻頁面
│   ├── app.js                       .............................. express程序入口
│   ├── nodemon.json                 .............................. node服務器熱更新插件nodemon對應的配置文件
│   └── package.json                 .............................. express框架須要的第三方依賴包配置文件
├── .gitignore
├── README.md                        .............................. 項目說明文檔
└── TODO-List.md                     .............................. 項目開發計劃文檔
複製代碼

源碼簡析

  • 簡單的權限判斷,app.js中:
    • express的中間件
    • 判斷請求的後綴
    • 判斷session中是否有用戶名,有則容許訪問 .key文件;沒有則禁止訪問
    • 主要是保護.key文件,能夠加入其它的權限手段,好比token、session有效時長等等
//靜態資源訪問限制
app.use(function (req, res, next) {
    var suffix = /(\.key)$/g;//後綴格式指定
    if ( suffix.test(req.path)) {
        console.log(req.session.username,'++++請求key文件了');
        if((req.session.username != 'admin')){
            return res.send('請求非法');
        }else{
            console.log('+++++請求key文件了,而且已經登陸,登陸名爲:',req.session.username);
            next();
        }
    }
    else {
        next();
    }
});
複製代碼
  • 利用FFmpeg對視頻進行加密、切片處理,在encrypt.js中:
    • 利用了FFmpeg的切片和加密方法
    • 建議能夠深刻研究FFmpeg框架的相關api
    • 能夠根據實際業務來對視頻進行更符合要求的切片處理
/**
 * 加密處理方法
 * @param options 加密數據的相關參數
 * @param socket socket輸出
 * @param callback 回調函數
 */
function encryptFun(options,socket, callback) {
    var _name = options.fileName.split('.')[0];
    var _type = options.fileName.split('.')[1];
    var _encryptPath = options.encryptPath + '/' + _name;
    var _videoPath = options.noencryptPath + '/' + options.fileName;
    var _keyInfoPath = './public/key/key_info.key';
    var _outputPath = _encryptPath + '/playlist.m3u8';
    console.log('begin encrypt Fun');
    if (_type == 'mp4') {
        ffmpegCommand(_videoPath)
            .addOption('-hls_time', '10')   //設置每一個片斷的長度
            .addOption('-hls_key_info_file', _keyInfoPath)
            .save(_outputPath)
            .on('end', function () {
                socket.emit('encrypt-event',{msg:'Encrypt the ' + options.fileName + ' file OK!',type:1});
                callback(null, 'Encrypt the ' + options.fileName + ' file OK!');
            })
            .on('stderr', function (stderrLine) {
                console.log('Stderr output: ' + stderrLine);
                socket.emit('encrypt-event',{msg:stderrLine});
            })
            .on('error', function (err, stdout, stderr) {
                console.log('Cannot process video: ' + err.message);
                socket.emit('encrypt-event',{msg:err.message});
                callback(err, err.message);
            });
    }
    else{
        callback('type err','file type is not mp4.');
    }
}
複製代碼
  • 視頻播放相關邏輯,player.ejs中:
    • 使用了videojs做爲播放器插件
    • 使用了videojs-contrib-hls做爲切片流解碼插件
    • 具體的邏輯代碼在player.js中
<script src="javascripts/utils.js"></script>
<script src="libs/videojs/video.min.js"></script>
<script src="libs/videojs-contrib-hls/videojs-contrib-hls.js"></script>
<script src="javascripts/player.js"></script>
複製代碼

建議

  • 本項目更多的價值在於展現出一整套的加密原理,同時爲了證實這套原理的可行性,作的比較簡單的示例。
  • 本項目不會提供相關技術棧的使用教程。
  • 若是須要在實際應用中使用相關原理或技術棧,建議根據實際項目對部分或總體解決方案進行完善和擴展。

雜談

如下的內容均爲我的觀點,僅供參考

  因爲本人自身是作前端開發的,因此不少相關的示例都是基於前端考慮,對於後端的相應的策略並非很專業。好比後端服務器,也採用的是偏前端的NodeJS。我想表達的是,在整套解決方案中,我主要作了3件事:

  • 第一,把mp4的視頻源經過FFmpeg轉換爲加密後的m3u8文件和ts文件以及關鍵的加密密鑰key文件;
  • 第二,經過最簡單的權限訪問,保護加密密鑰key文件;
  • 第三,利用video.js及相關的videojs-contrib-hls.js實現客戶端的視頻文件解密,並播放。

  所以能夠看出關於視頻加密的解決方案中,最重要的實際上是如何保護加密密鑰key文件,而這部分工做更多的是在於服務器端的相關策略,好比可使用cookiesession相關技術、添加自定義token校驗、有效時長機制等等方法保證祕鑰key文件的相對安全性、可靠性。  

  而如何將視頻源文件轉化爲對應的加密後的文件,能夠更多的研究開源庫FFmpeg的使用,甚至若是沒有迫切需求,能夠考慮使用第三方視頻雲服務商的相關解決方案。至於客戶端的視頻解密,也能夠研究video.js相關的內容。

完整項目

github.com/hauk0101/vi…

參考資料

瀏覽知識共享許可協議

知識共享許可協議
本做品採用知識共享署名-相同方式共享 4.0 國際許可協議進行許可。

相關文章
相關標籤/搜索