本次项目使用Aliplayer 播放器 播放rtmp直播流(户外摄像头的直播流),视频监控是用的阿里云的视频监控。
前端框架:layui
需求:
1、使用阿里播放器插件Aliplayer,实现rtmp流的直播播放;
2、实现PTZ云台控制。
3、调用PTZ云台控制接口的时候,公共参数中的签名结果串,是经过计算获得的,签名方式HMAC-SHA1。
踩坑点:
1、签名串算的时候踩坑了,先下载各sha1算法的插件,调试的时候,对着官方文档的例子先算一下,是否输出正确,如果不正确报错,阿里云也会给返回值,可根据返回结果查看一下哪里的问题。
https://help.aliyun.com/document_detail/109568.html?spm=a2c4g.11186623.2.12.26d9200frMF1JS
2、Aliplayer播放rtmp格式,加载直播流之后画面不动,花了很多时间去查资料。开始以为是因为配置参数写法格式错误,后来发现加引号和不加引号其实都可以。
最后提的阿里工单,解决办法:
给播放器参数加上
rtmpBufferTime: 0,
showBuffer: 0,
(rmtp流是纯视频流,没有音频流,参数设置是对于纯视频流的播放兼容参数)
这次是在有参考的情况下还用了这么长时间调试,实在是不应该啊。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<title>Aliplayer在线配置</title>
<link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.8.1/skins/default/aliplayer-min.css" />
<script type="text/javascript" charset="utf-8" src="https://g.alicdn.com/de/prismplayer/2.8.1/aliplayer-min.js"></script>
<link rel="stylesheet" href="layuiadmin/layui/css/layui.css" media="all">
<style>
#set {
position: absolute;
right: 15px;
top: 15px;
z-index: 9999
}
#control{width: 200px;height: 200px;}
#control ul li{width: 33.33%;float: left;margin-bottom: 10px;text-align: center;}
.bgcon{
padding: 10px 5px 5px;
width: 210px;
height: 132px;
display: table;
background-color: rgba(0,142,240,.1);
border-radius: 5px;
border: 1px solid rgba(0,142,240,.2);
margin-bottom: 10px;
}
</style>
</head>
<body>
<button class="layui-btn layui-btn-normal" id="set">设置</button>
<div class="prism-player" id="player-con">
</div>
<div id="control" style="display: none;padding: 10px">
<ul class="bgcon">
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0.5,0.5,0)" onmouseup="stop_move()"><span>↖ </span></button>
</li>
<li><button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0,0.5,0)" onmouseup="stop_move()"><span>↑ </span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(-0.5,0.5,0)" onmouseup="stop_move()"><span>↗ </span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0.5,0,0)" onmouseup="stop_move()"><span>← </span></button>
</li>
<li style="text-align: center;line-height: 38px;position: relative; ">
方向调节 </button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(-0.5,0,0)" onmouseup="stop_move()"><span>→ </span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0.5,-0.5,0)" onmouseup="stop_move()"><span>↙ </span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0,-0.5,0)" onmouseup="stop_move()"><span>↓ </span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(-0.5,-0.5,0)" onmouseup="stop_move()"><span>↘ </span></button>
</li>
</ul>
<ul class="bgcon">
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_adjust(0,-0.5)" onmouseup="stop_adjust()"><span>-</span></button>
</li>
<li style="line-height: 38px;"> 变焦
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_adjust(0,0.5)" onmouseup="stop_adjust()"><span>+</span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0, 0, -0.5)" onmouseup="stop_move()"><span>-</span></button>
</li>
<li style="line-height: 38px;">
变倍 </button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_move(0, 0, 0.5)" onmouseup="stop_move()"><span>+</span></button>
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_adjust(-0.5,0)" onmouseup="stop_adjust()"><span>-</span></button>
</li>
<li style="line-height: 38px;">
光圈
</li>
<li>
<button type="button" class="layui-btn layui-btn-normal" onmousedown="start_adjust(0.5,0)" onmouseup="stop_adjust()"><span>+</span></button>
</li>
</ul>
</div>
<script src="layuiadmin/layui/layui.js"></script>
<script>
layui.config({
version: true, //一般用于更新模块缓存,设为 true 即让浏览器不缓存。
base: 'layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['index', 'layer', 'commonUtils', 'request'], function() {
var $ = layui.$,
commonUtils = layui.commonUtils,
request = layui.request,
layer = layui.layer;
var id = 'xxxx'; //摄像头设备id
var AccessKeyId = 'xxx';//阿里云控制台的密钥
var AccessKeySecret = 'xxxx';
var player = null;
if (player) {
player.dispose(); //销毁
$('#player-con').empty();
}
player = new Aliplayer({
id: "player-con",
width: "100%",
height: "400px",
autoplay: true, //自动播放
rePlay: false,//重播视频
isLive: true,//是否直播
useH5Prism: true, //指定使用H5播放器。
useFlashPrism: false, //指定使用Flash播放器
rtmpBufferTime: 0,
showBuffer: 0,
stashInitialSizeForFlv: 30, //H5播放flv时,初始缓存大小,只在直播下起作用。
source: 'rtmp://....' //rtmp格式的地址
}, function(player) {
// player.on("ready", function(e) {});
});
$("#set").click(function() {
layer.open({
type: 1,
anim: -1,
title: 'control',
closeBtn: 1,
// shade: 0.1,
shadeClose: true,
content: $('#control'),
area: ['240px', '420px']
})
})
//摄像头移动
window.start_move = function(pan, tilt, zoom) {
var params = {
Action: 'ContinuousMove',
Id: id,
Pan: pan,
Tilt: tilt,
Zoom: zoom,
Version: '2018-12-12',
AccessKeyId: AccessKeyId,
Signature: '',
SignatureMethod: 'HMAC-SHA1',
Timestamp: Timestamp(),
SignatureVersion: '1.0',
SignatureNonce: commonUtils.UUID(),
Format: 'JSON'
};
params.Signature = Signature(params);
ali_api(params)
}
//停止转动
window.stop_move = function() {
const params = {
Action: 'StopMove',
Id: id,
Pan: true,
Tilt: true,
Zoom: true,
Version: '2018-12-12',
AccessKeyId: AccessKeyId,
Signature: '',
SignatureMethod: 'HMAC-SHA1',
Timestamp: Timestamp(),
SignatureVersion: '1.0',
SignatureNonce: commonUtils.UUID(),
Format: 'JSON'
};
params.Signature = Signature(params);
ali_api(params)
}
//设置光圈,焦点
window.start_adjust = function(Iris, Focus) {
var params = {
Action: 'ContinuousAdjust',
Id: id,
Iris: Iris,
Focus: Focus,
Version: '2018-12-12',
AccessKeyId: AccessKeyId,
Signature: '',
SignatureMethod: 'HMAC-SHA1',
Timestamp: Timestamp(),
SignatureVersion: '1.0',
SignatureNonce: commonUtils.UUID(),
Format: 'JSON'
};
params.Signature = Signature(params);
ali_api(params)
}
//停止设置光圈焦点
window.stop_adjust = function() {
const params = {
Action: 'StopAdjust',
Id: id,
Iris: true,
Focus: true,
Version: '2018-12-12',
AccessKeyId: AccessKeyId,
Signature: '',
SignatureMethod: 'HMAC-SHA1',
Timestamp: Timestamp(),
SignatureVersion: '1.0',
SignatureNonce: commonUtils.UUID(),
Format: 'JSON'
};
params.Signature = Signature(params);
ali_api(params)
}
//发送请求
function ali_api(params) {
$.ajax({
url: 'http://vs.cn-qingdao.aliyuncs.com',
type: 'GET',
async: true,
data: params,
success: function(res, status, xhr) {
},
error: function(jqXHR, textStatus, errorThrown) {
layer.msg(errorThrown);
}
})
}
/* 签名结果串 计算。
关于签名的计算方法,参见签名机制。
https://help.aliyun.com/document_detail/109568.html?spm=a2c4g.11186623.2.12.58c1200f7NANPp
*/
function Signature(params) {
var data = [];
var keys = Object.keys(params).sort();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === 'Signature') {
continue;
}
data.push(key + '=' + params[key]);
}
var CanonicalizedQueryString = escape(data.join('&'));
var StringToSign = ('GET&%2F&' + CanonicalizedQueryString).replace(/%3A/g, '%253A');
return commonUtils.hmacsha1(AccessKeySecret + '&', StringToSign).toString(); //第一个参数为Key ,第二个参数为 StringToSign
}
//时间戳
function Timestamp() {
var timestamp = (new Date().getTime()) - (60 * 60 * 8 * 1000);
var date = new Date(timestamp);
return date.getFullYear() +
'-' +
('00' + (date.getMonth() + 1)).substr(-2) +
'-' +
('00' + date.getDate()).substr(-2) +
'T' +
('00' + date.getHours()).substr(-2) +
':' +
('00' + date.getMinutes()).substr(-2) +
':' +
('00' + date.getSeconds()).substr(-2) +
'Z';
}
});
</script>
</body>
调用的commonUtils.hmacsha1 方法
//调用的commonUtils.hmacsha1
hmacsha1:function(k, d, _p, _z) {
// heavily optimized and compressed version of http://pajhome.org.uk/crypt/md5/sha1.js
// _p = b64pad, _z = character size; not used here but I left them available just in case
if (!_p) { _p = '='; }
if (!_z) { _z = 8; }
function _f(t, b, c, d) { if (t < 20) { return (b & c) | ((~b) & d); } if (t < 40) { return b ^ c ^ d; } if (t < 60) { return (b & c) | (b & d) | (c & d); } return b ^ c ^ d; }
function _k(t) { return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; }
function _s(x, y) { var l = (x & 0xFFFF) + (y & 0xFFFF),
m = (x >> 16) + (y >> 16) + (l >> 16); return (m << 16) | (l & 0xFFFF); }
function _r(n, c) { return (n << c) | (n >>> (32 - c)); }
function _c(x, l) { x[l >> 5] |= 0x80 << (24 - l % 32);
x[((l + 64 >> 9) << 4) + 15] = l; var w = [80],
a = 1732584193,
b = -271733879,
c = -1732584194,
d = 271733878,
e = -1009589776; for (var i = 0; i < x.length; i += 16) { var o = a,
p = b,
q = c,
r = d,
s = e; for (var j = 0; j < 80; j++) { if (j < 16) { w[j] = x[i + j]; } else { w[j] = _r(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); } var t = _s(_s(_r(a, 5), _f(j, b, c, d)), _s(_s(e, w[j]), _k(j)));
e = d;
d = c;
c = _r(b, 30);
b = a;
a = t; } a = _s(a, o);
b = _s(b, p);
c = _s(c, q);
d = _s(d, r);
e = _s(e, s); } return [a, b, c, d, e]; }
function _b(s) { var b = [],
m = (1 << _z) - 1; for (var i = 0; i < s.length * _z; i += _z) { b[i >> 5] |= (s.charCodeAt(i / 8) & m) << (32 - _z - i % 32); } return b; }
function _h(k, d) { var b = _b(k); if (b.length > 16) { b = _c(b, k.length * _z); } var p = [16],
o = [16]; for (var i = 0; i < 16; i++) { p[i] = b[i] ^ 0x36363636;
o[i] = b[i] ^ 0x5C5C5C5C; } var h = _c(p.concat(_b(d)), 512 + d.length * _z); return _c(o.concat(h), 512 + 160); }
function _n(b) { var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
s = ''; for (var i = 0; i < b.length * 4; i += 3) { var r = (((b[i >> 2] >> 8 * (3 - i % 4)) & 0xFF) << 16) | (((b[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | ((b[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4)) & 0xFF); for (var j = 0; j < 4; j++) { if (i * 8 + j * 6 > b.length * 32) { s += _p; } else { s += t.charAt((r >> 6 * (3 - j)) & 0x3F); } } } return s; }
function _x(k, d) { return _n(_h(k, d)); }
return _x(k, d);
}
调用的UUID() 随机数的函数
/**
* 生成UUID
*/
random4: function() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
UUID: function() {
return (commonUtils.random4() + commonUtils.random4() + "-" + commonUtils.random4() + "-" + commonUtils.random4() + "-" + commonUtils.random4() + "-" + commonUtils.random4() + commonUtils.random4() + commonUtils.random4());
}