在之后的开发项目中,极可能有作在线视频的,而在线视频就有个问题,由于在线播放,就颇有可能视频数据被抓包,若是这个在线视频平台有付费视频的话,这样就会有人作点倒卖视频的生意了,针对这个问题,目前国内有不少不错的加密视频平台,能够把你平台的视频放在他们那里,而后经过他们的机制进行加密,而后作一套机制,当用户使用平台播放时,实际上是平台去加密视频方请求过来的加密视频,这样就能够保证视频的安全性了php
鄙人据说的,有保利威,金盾,还有不少不少我叫不上名,搜索引擎一搜就有一大堆的,本次教程说的是保利威css
官网:传送门 有直播和点播的服务,直播是什么不用多说了吧,点播的意思就是我上面说的,把录制好的视频放到平台那种html
本教程只介绍使用云点播功能前端
点击使用手册的云点播:使用手册文档:传送门vue
选到h5视频播放:固然你也可使用flash,可是如今的平台基本都用的是h5了,因此本次选用h5的html5
复制这段代码放到一个html文档里,直接点那个浏览器标识打开测试:ios
这个页面整个都是保利威给我咱们提供的,能够加速播放,能够查看视频参数,能够看列表,而后还有个【test】这个就是跑马灯,相信有过购买视频的朋友都知道,你看你的付费视频时,都有本身的用户名啥的,假如你本身私自录制,传出去,就是用这个跑马灯就能够追踪到你的,他官方给的实例就是这样npm
但其实,由于我电脑装了IDM工具,且它这个视频只是展现,尚未真正的加密(看到后面的你就知道为何没有真正的加密了),因此个人IDM自动嗅探视频地址django
且还支持持断点播放json
可是这个因为是案例,因此利用IDM直接就能够下载,且播放时尚未水印:
好的,演示的视频就是这样了。
你须要注册一个保利威的帐号,而后拿到id和secret,点设置 - API接口:
注意id和secretkey以及token,这里的token有读的和写的,后期会用到
上传一个测试的视频,上传的时候设置加密参数
把加密设置打开,而后下面的移动端加密按你本身的需求来,我这里暂且不设置
相关的更多的视频配置参数:传送门
好的,我这里开始上传,我上传了一首歌的MV:
上传完以后平台自动解码,而后就会有一个视频加密的id了:
转码以后,审核:
在审核的时候你就能够作一些相关的视频设置:
这里还有不少的设置,不一一展现,本身体验了
等待大概一两分钟以后,显示已发布,表示能够用了:
这里还有一些设置,好比播放域名设置,这些就本身去体验了,我这里这些都直接用默认的
看文档得知有两个步骤:
- 服务端获取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>
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对应,最后有没有【/】要统一 ]
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()
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 response
# --------- 保利威视频注册用户id和key-------- POLYV_CONFIG = { 'userId': '您的id', # polyv 提供的服务器间的通信验证 'secretkey': '您的secret' # polyv 提供的接口调用签名访问的key }
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"])
以上步骤其实已经能够知足大部分用户了,可是有朋友发现了,这里仍是能够用IDM直接下载啊:
因此接下来就要设置跑马灯了
根据个人观察,好像默认就开启了跑马灯的
这个地址能够是本地的
这里的域名部分,官方建议这样设置,其实在后面我测试的时候,发现按照下面这个设置,兼容性很差,谷歌能够播放,IE浏览器放不了
指向test_bolyv视图函数
视图函数:
这个get方法是保利威后台自动调用的,不是咱们这边服务端要用的,也不是客户端要用的
user_name部分就是要显示的跑马灯数据
get_play_key和get_resp
get_play_key是获取sign的,注意这里的设置sign和上面获取加密视频的sign不太同样
get_resp是作跑马灯受权的:
这里他给的例子是用的不加密的方式
咱们要播放加密视频,固然仍是得使用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>
<cross-domain-policy> <allow-access-from domain="*.polyv.net"/> </cross-domain-policy>
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: 是否可播放, 一、可播放 二、禁播 :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()
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"]) def get(self, request, *args, **kwargs): vid = request.query_params.get("vid", "") code = request.query_params.get("code", "") t = request.query_params.get("t", "") callback = request.query_params.get("callback", "") user_name = "test|1234565" status = 1 # username, code, status, t sign = polyv_video.get_play_key(vid, user_name, code, status, t) print(sign) res_str = polyv_video.get_resp(int(status), user_name, sign) res_str = json.dumps(res_str, ensure_ascii=False) if callback != "": ret = callback + "(" + res_str + ")" else: ret = res_str print(ret) return HttpResponse(ret) # 解决跨域 def test_bolyv(request): return render(request, "bolyv_test.html")
from django.contrib import admin from django.urls import path, re_path from app.views import test_bolyv from app.views import Polyv urlpatterns = [ path('admin/', admin.site.urls), path('polyv', Polyv.as_view()), # 这里要与客户端url对应,最后有没有【/】要统一 re_path(r'^crossdomain.xml', test_bolyv) ]
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app.apps.AppConfig', 'corsheaders', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 注意顺序 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 'utils.middlewares.MyCorsMiddelware' # 这是咱们本身定义的那个中间件 ] # --------- 保利威视频注册用户id和key-------- POLYV_CONFIG = { 'userId': '您的id', # polyv 提供的服务器间的通信验证 'secretkey': '您的secret' # polyv 提供的接口调用签名访问的key } # 跨域增长忽略 CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = ( '*' ) CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', )
- 通过这先后几个接入第三方平台的案例,发现其实都不算难,可是都有一些坑,必需要本身去实践去研究了才能玩得会
- 火狐浏览器的安全级别默认很高,须要借助第三方库来配置
- 跑马灯视频设置get请求的url必须和主逻辑API接口是同一个
- 遇到问题仍是多研究,多理解,多分析