加密視頻 在以後的開發項目中,很可能有做線上視頻的,而線上視頻就有個問題,因為線上播放,就很有可能視頻數據被抓包,如果這個線上視頻平臺有付費視頻的話,這樣就會有人做點倒賣視頻的生意了,針對這個問題,目前國內有很多不錯的加密視頻平臺,可以把你平臺的視頻放在他們那裡,然後通過他們的機制進行加密,然後做一... ...
加密視頻
在以後的開發項目中,很可能有做線上視頻的,而線上視頻就有個問題,因為線上播放,就很有可能視頻數據被抓包,如果這個線上視頻平臺有付費視頻的話,這樣就會有人做點倒賣視頻的生意了,針對這個問題,目前國內有很多不錯的加密視頻平臺,可以把你平臺的視頻放在他們那裡,然後通過他們的機制進行加密,然後做一套機制,當用戶使用平臺播放時,其實是平臺去加密視頻方請求過來的加密視頻,這樣就可以保證視頻的安全性了
常用加密視頻平臺
鄙人聽說的,有保利威,金盾,還有很多很多我叫不上名,搜索引擎一搜就有一大堆的,本次教程說的是保利威
保利威
官網:傳送門 有直播和點播的服務,直播是什麼不用多說了吧,點播的意思就是我上面說的,把錄製好的視頻放到平臺那種
本教程只介紹使用雲點播功能
文檔手冊
點擊使用手冊的雲點播:使用手冊文檔:傳送門
選到h5視頻播放:當然你也可以使用flash,但是現在的平臺基本都用的是h5了,所以本次選用h5的
官方文檔案例演示
複製這段代碼放到一個html文檔里,直接點那個瀏覽器標識打開測試:
這個頁面整個都是保利威給我我們提供的,可以加速播放,可以查看視頻參數,可以看列表,然後還有個【test】這個就是跑馬燈,相信有過購買視頻的朋友都知道,你看你的付費視頻時,都有自己的用戶名啥的,假如你自己私自錄製,傳出去,就是用這個跑馬燈就可以追蹤到你的,他官方給的實例就是這樣
但其實,因為我電腦裝了IDM工具,且它這個視頻只是展示,還沒有真正的加密(看到後面的你就知道為什麼沒有真正的加密了),所以我的IDM自動嗅探視頻地址
且還支持持斷點播放
但是這個由於是案例,所以利用IDM直接就可以下載,且播放時還沒有水印:
好的,演示的視頻就是這樣了。
代碼實現
前期準備:
你需要註冊一個保利威的賬號,然後拿到id和secret,點設置 - API介面:
註意id和secretkey以及token,這裡的token有讀的和寫的,後期會用到
上傳視頻
上傳一個測試的視頻,上傳的時候設置加密參數
上傳之前開啟加密設置
把加密設置打開,然後下麵的移動端加密按你自己的需求來,我這裡暫且不設置
相關的更多的視頻配置參數:傳送門
好的,我這裡開始上傳,我上傳了一首歌的MV:
上傳完之後平臺自動解碼,然後就會有一個視頻加密的id了:
轉碼之後,審核:
在審核的時候你就可以做一些相關的視頻設置:
這裡還有很多的設置,不一一展示,自己體驗了
等待大概一兩分鐘之後,顯示已發佈,表示可以用了:
其他參數配置
這裡還有一些設置,比如播放功能變數名稱設置,這些就自己去體驗了,我這裡這些都直接用預設的
查看官方文檔:
看文檔得知有兩個步驟:
- 服務端獲取token,將token給客戶端
- 客戶端拿到token,開始播放視頻
獲取token
他官方文檔給的是php的,不存在,後面重構成Python的就行了
播放:
播放有兩種播放形式,一種是直接在playsafe里傳一個token,一種是給一個函數,函數必須帶視頻id和next,next是一個函數,在獲取token之後將token傳到next里即可,而這官方給的文檔是next(playsafe),這裡有個坑,不是傳playsafe,而是傳token,我在這卡了很久
再次強調:視頻如果是加密的,需要設置加密參數 playsafe,playsafe有兩種形式,一個是傳token,一個是傳函數+next回調函數,且函數必須把token作為值傳進去
下麵還有更多的配置,切換視頻之類的,不一一展示了,自己研究
代碼實現:
創建一個djangorestframework的項目,項目名為EncryptVideo,app名為app:
url:
polyv對象,在項目根目錄創建utils文件夾,該文件夾下創建polyv文件,定義一個Polyv對象
利用了設計模式里的單例模式返回一個polyv_video對象
view:request.META.get('REMOTE_ADDR') 可以獲取客戶端的IP地址 導入那個polyv對象
html,註意返回數據的層級,這個得根據你返回的數據來定
在這之前,我建議最好寫一個解決跨域請求的中間件,也放在utils目錄下:
啟動項目:Python manage.py runserver localhost:8000
那個html文件利用pycharm的功能從瀏覽器打開:
展示結果,朋友們,如果你遇到了這些坑,可以按我的方法試試
第一個坑
(為了不浪費大家時間,我上面給的截圖其實已經是我修改過並且正確的了)
發現返回的結果,sign params invild,意思就是說sign參數無效,那麼再看官方文檔:
sign的計算規則根本由這個concated生成的:
但是這句話,有歧義,按照ASCII升序拼接,到底是先拼接了之後再按ASCII升序還是先按ASCII升序之後再拼接,而且他這句,按照ASCKII升序 key + value + key + value ... +value 拼接,確實不知道到底怎麼拼接,所以我是這樣的:
當然你也可以用列表生成式,反正怎麼舒服怎麼來,反正拼接順序就是先按key排序之後再key+value組合
第二個坑,這個坑我個人認為操作的問題,其實不算坑,如果你遇到跟我一樣的問題,那恭喜你 嘻嘻
(上面的截圖也是正確的了,不浪費大家時間)
可以顯示,但是點擊播放放不了,打開控制台,報錯了:
(ip地址可以忽略,這是我之前測試的時候遇到的問題 )
這個問題,我跟你說,看似是同源策略的問題,其實並不是,就是我前面標註的那裡:
就是因為兩個url沒有統一導致的,所以必須要統一,要嘛都在結束符【/】,要嘛都不帶
第三個坑,錯誤的以為保利威方給你報同源策略錯誤
(上面截圖也是已經是正確的了)
展示結果還是不能播放,並且連視頻縮略圖都沒了,而且如圖:
這個問題我是耗在這耗時最久的,報錯的意思就是跨域請求了,瀏覽器同源策略的問題,但是我把本地的啟動ip改成了我區域網的ip【192.168.0.8:8000】,html部分axios非同步請求那裡的也是【192.168.0.8:8000】,然後在django配置文件的這裡,我添加了這個
中間價對response的設置前面也設置了,啟動還是不行,後面突然醒悟過來,列印看token是否有拿到,確實有拿到
而且在我們這個平臺,保利威,客戶三者之間的關係,其實是這樣的:
也就是說,這個問題就是因為第2步之後的第3步上卡住,產生了跨域請求,所以這跟我們沒多大關係了,是保利威視頻那邊的問題。前面這句話前半句是對的(“第2步之後的第3步上卡住了”),後面的分析都是錯的,但是當時的我不知道啊,按著錯誤的思路,我想了想,我在保利威後臺設置了一個視頻功能變數名稱白名單:localhost:8000
有朋友回問,保利威那邊預設不就是對任何功能變數名稱都沒有限制的嗎?是啊,但是我還是設置了,設置之後,果然還是不行,我還溯源,準備從這個next參數的開始:
我還去分析了他們的那個js文件,想找找next到底是什麼:
發現簡直無從下手的。最後我就真的以為是保利威那邊的問題功能變數名稱問題,就是要設置那個功能變數名稱才行,但是我這裡改下,那裡改下還成功了:
可以播放了,可以調播放速度啥的,註意,預設打開沒有聲,是因為預設音量按鈕沒開,自己點那個喇叭圖標打開音量
那按著這個錯誤的思路,有朋友會說,我設置功能變數名稱是127.0.0.1:8000,把項目的啟動ip也啟動為本地地址看看:
訪問:
所以還是不能直接是ip地址。
但是!但是,根據我的經驗,我還是不太放心,我又新建了一個django項目:
邏輯一樣,然後啟動的就是127.0.0.1:8001,然後保利威視頻功能變數名稱限制我也刪了:
發現照樣能播放:
最後經我的研究發現,還是獲取token那裡有問題,我把代碼重新寫的非常淺顯明瞭,什麼列表生成式的寫法都棄了,就為了讀代碼順暢(上面的代碼截圖給的已經是正確的了)
結果這樣確實可以正常獲取token,然後next函數傳入token就直接播放了。
所以我錯誤的以為保利威那邊的問題,饒了很久才發現。
以上是我個人的從分析問題,走錯分析的路掉進坑了,然後得到的總結,如果你們沒有遇到,一氣呵成,那麼你很棒,反正我是遇到了這些坑,最後不放心又測試了一次才找到根本問題的。當然以上都是我個人推斷,不代表絕對正確
好的,怎麼代表我們真的配置好了呢?再上傳一個視頻,然後播放看看,如果真的沒問題,那以後就沒問題了,我開始上傳,並修改html上的id
瀏覽器打開:
相關代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>保利威視頻測試</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <body> <div id="player"></div> <script src="//player.polyv.net/script/player.js"></script> <script> var player = polyvPlayer({ wrap: '#player', width: 800, height: 533, vid: '2f57a436189b03930638e752c9a3761e_2', // 視頻id,對應賬號里的視頻id playsafe: function (vid, next) { axios.request({ url: "http://localhost:8000/polyv", method: "POST", data: { vid: vid } }).then(function (data) { console.log(data.data); next(data.data.token); }) } }); </script> </body> </html>html
from django.contrib import admin from django.urls import path from app.views import Polyv urlpatterns = [ path('admin/', admin.site.urls), path('polyv', Polyv.as_view()), # 這裡要與客戶端url對應,最後有沒有【/】要統一 ]urls
from django.conf import settings import time import requests import json import hashlib class PolyvPlayer(object): userId = settings.POLYV_CONFIG['userId'] secretkey = settings.POLYV_CONFIG['secretkey'] def tomd5(self, value): """取md5值""" return hashlib.md5(value.encode()).hexdigest() # 獲取視頻數據的token def get_video_token(self, videoId, viewerIp, viewerId=None, viewerName='', extraParams='HTML5'): """ :param videoId: 視頻id :param viewerId: 看視頻用戶id :param viewerIp: 看視頻用戶ip :param viewerName: 看視頻用戶昵稱 :param extraParams: 擴展參數 :param sign: 加密的sign :return: 返回點播的視頻的token """ ts = int(time.time() * 1000) # 時間戳 plain = { "userId": self.userId, 'videoId': videoId, 'ts': ts, 'viewerId': viewerId, 'viewerIp': viewerIp, 'viewerName': viewerName, 'extraParams': extraParams } # 按照官方文檔,將參數 按照ASCKII升序 key + value + key + value... + value 拼接 plain_sorted = {} key_temp = sorted(plain) for key in key_temp: plain_sorted[key] = plain[key] print(plain_sorted) plain_string = '' for k, v in plain_sorted.items(): plain_string += str(k) + str(v) print(plain_string) sign_data = self.secretkey + plain_string + self.secretkey # 取sign_data的md5的大寫 sign = self.tomd5(sign_data).upper() # 新的帶有sign的字典 plain.update({'sign': sign}) print('plain', plain) result = requests.post( url='https://hls.videocc.net/service/v1/token', headers={"Content-type": "application/x-www-form-urlencoded"}, # 一定要帶上這個請求頭 data=plain ).json() data = {} if isinstance(result, str) else result.get("data", {}) return {"token": data} polyv_video = PolyvPlayer()polyv.py
from django.utils.deprecation import MiddlewareMixin class MyCorsMiddelware(MiddlewareMixin): def process_response(self, request, response): response["Access-Control-Allow-Origin"] = "*" if request.method == "OPTIONS": response["Access-Control-Allow-Headers"] = "*" return responsemiddlewares
# --------- 保利威視頻註冊用戶id和key-------- POLYV_CONFIG = { 'userId': '您的id', # polyv 提供的伺服器間的通訊驗證 'secretkey': '您的secret' # polyv 提供的介面調用簽名訪問的key }settings部分
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from django.http import HttpResponse from utils.polyv import polyv_video import json class Polyv(APIView): def post(self, request): vid = request.data.get("vid") remote_addr = request.META.get("REMOTE_ADDR") user_id = 1 user_name = "test" verify_data = polyv_video.get_video_token(vid, remote_addr, user_id, user_name) return Response(verify_data["token"])views
播放跑馬燈
以上步驟其實已經可以滿足大部分用戶了,但是有朋友發現了,這裡還是可以用IDM直接下載啊:
所以接下來就要設置跑馬燈了
1.設置視頻授權跑馬燈
根據我的觀察,好像預設就開啟了跑馬燈的
2.設置一個授權地址
這個地址可以是本地的
3.創建一個html文件,名字隨意,存入如下代碼:
這裡的功能變數名稱部分,官方建議這樣設置,其實在後面我測試的時候,發現按照下麵這個設置,相容性不好,谷歌可以播放,IE瀏覽器放不了
4.url部分添加一個crossdomain.xml
指向test_bolyv視圖函數
視圖函數:
5.視頻介面/polyv添加get方法
這個get方法是保利威後臺自動調用的,不是我們這邊服務端要用的,也不是客戶端要用的
user_name部分就是要顯示的跑馬燈數據
6.在定義的polyv類里添加方法:
get_play_key和get_resp
get_play_key是獲取sign的,註意這裡的設置sign和上面獲取加密視頻的sign不太一樣
get_resp是做跑馬燈授權的:
7.引入新的js
這裡他給的例子是用的不加密的方式
我們要播放加密視頻,當然還是得使用playsafe參數播放,在templates目錄下新建一個tests.html,代碼如下,我標註出來的就是添加的參數,同樣的,註意返回數據的層級
啟動項目:
通過pycharm虛擬一個客戶端出來,點擊那些瀏覽器圖標打開,我的電腦是windows,所以試了谷歌,火狐和IE
谷歌:谷歌預設打開是沒有聲音的,這是谷歌瀏覽器的策略問題,不是大問題,正常播放,且正常顯示跑馬燈
IE:
再次強調,如果在那個xml文件里,你如果按官方的建議設置成這樣:
然後你打開IE是播放不了的:
所以別按它建議的設置,就用預設的
火狐:
問題來了,就是這個問題,我折騰老久了,這個根本原因還是跨域請求問題,因為火狐瀏覽器預設安全性比較高,所以谷歌和IE可以,就是火狐不行,我查閱了很多,比如關閉火狐的跨域請求的,設置跨域請求的,設置了很多,還是沒用,最後使用了第三方庫django-cors-headers解決了,相關介紹安裝文章: 前後端分離djangorestframework——解決跨域請求 第5個方法
根據操作,重啟項目,火狐立馬播放:
然後現在你看我用IDM點下載:
我隨便選了一個:
提示:
最後再來個輔助測試,換個視頻id播放看看,換回這個視頻
火狐,谷歌,IE:
確實沒有問題了,這樣的操作是不是很6啊,我反正感覺很6
詳細的參數配置步驟就沒展開了,具體看官方文檔吧:傳送門
相關代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src='https://player.polyv.net/script/polyvplayer.min.js'></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <body> <div id="player"></div> <script> var player = polyvObject('#player').videoPlayer({ wrap: '#player', width: 800, height: 533, forceH5: true, vid: '2f57a43618b400b4f9c84fcea9b103a8_2', code: 'myRandomCodeValue', playsafe: function (vid, next) { // 向後端發送請求獲取加密的token console.log(vid); axios.request({ url: "http://localhost:8000/polyv", method: "POST", data: { vid: vid } }).then(function (data) { console.log(data); next(data.data.token) }) } }); </script> </body> </html>前端html
<cross-domain-policy> <allow-access-from domain="*.polyv.net"/> </cross-domain-policy>bolyv_test.html
from django.conf import settings import time import requests import json import hashlib class PolyvPlayer(object): userId = settings.POLYV_CONFIG['userId'] secretkey = settings.POLYV_CONFIG['secretkey'] def tomd5(self, value): """取md5值""" return hashlib.md5(value.encode()).hexdigest() # 獲取視頻數據的token def get_video_token(self, videoId, viewerIp, viewerId=None, viewerName='', extraParams='HTML5'): """ :param videoId: 視頻id :param viewerId: 看視頻用戶id :param viewerIp: 看視頻用戶ip :param viewerName: 看視頻用戶昵稱 :param extraParams: 擴展參數 :param sign: 加密的sign :return: 返回點播的視頻的token """ ts = int(time.time() * 1000) # 時間戳 plain = { "userId": self.userId, 'videoId': videoId, 'ts': ts, 'viewerId': viewerId, 'viewerIp': viewerIp, 'viewerName': viewerName, 'extraParams': extraParams } # 按照官方文檔,將參數 按照ASCKII升序 key + value + key + value... + value 拼接 plain_sorted = {} key_temp = sorted(plain) for key in key_temp: plain_sorted[key] = plain[key] print(plain_sorted) plain_string = '' for k, v in plain_sorted.items(): plain_string += str(k) + str(v) print(plain_string) sign_data = self.secretkey + plain_string + self.secretkey # 取sign_data的md5的大寫 sign = self.tomd5(sign_data).upper() # 新的帶有sign的字典 plain.update({'sign': sign}) print('plain', plain) result = requests.post( url='https://hls.videocc.net/service/v1/token', headers={"Content-type": "application/x-www-form-urlencoded"}, # 一定要帶上這個請求頭 data=plain ).json() data = {} if isinstance(result, str) else result.get("data", {}) return {"token": data} def get_play_key(self, vid, username, code, status, ts): """ :param vid: 視頻 vid :param username: 響應跑馬燈展示 :param code: 自定義參數 :param status: 是否可播放, 1、可播放 2、禁播 :param ts: 時間戳 :return: 返回跑馬燈視頻的key """ return self.tomd5("vid={}&secretkey={}&username={}&code={}&status={}&t={}".format( vid, self.secretkey, username, code, status, ts)).lower() @staticmethod def get_resp(status, username, sign, msg="授權暫未通過"): res_str = { "status": status, "username": username, "sign": sign, "msg": msg, "fontSize": "18", "fontColor": "0xFF0000", "speed": "50", "filter": "on", "setting": "2", "alpha": "0.7", "filterAlpha": "1", "filterColor": "0x3914AF", "blurX": "2", "blurY": "2", "tweenTime": "1", "interval": "3", "lifeTime": "3", "strength": "4", "show": "on" } return res_str polyv_video = PolyvPlayer()polyv.py - 自定義的polyv類