免费版本
基于webRTC构建的新一代视频融合可视指挥调度平台WEB SDK restful接口文档
demo:https://github.com/besovideo/ewebdemo
npm包:https://www.npmjs.com/package/@besovideo/webrtc-player
安卓SDK在线文档
安卓MCP SDK(免费版本,只有MCP做CU,即手机客户端MCU的DEMO功能,用于集成到客户的行业APP中,没有MCP做PU,即做摄像头的功能部分
那个是完整代码里面的,属收费版本),https://github.com/besovideo/Android-BVCU-SDK
WEB client源代码(github),https://github.com/besovideo/webclientd.git
protocol SDK, BVCSP SDK for both embedded linux&android(github)
CUSDK for windows client(C++/C# demo,无JAVA接口), https://github.com/besovideo/bvcusdk.git
收费版本
带屏终端使用MCP,无屏终端例如智能安全帽使用MPU
提供的源代码就是安卓studio完整的工程打包的代码,编译出来直接就是标准发行的mcp.apk或mpu.apk,其中与平台通信的协议部分
(私有协议/28181)是以库的形式提供的,其它都是源代码。
平台源代码,目前提供的是WEB软件的代码,是基于javascript+VUE构建的WEB前端的代码,即我们看到的WEB软件的代码。
server源码费用就比较高,有兴趣可面议,不是JAVA语言编写的,而是底层采用C/C++,上层用GO语言构建的。
平台对接时调用WEB SDK开发时遇到的常见问题
一、环境准备
安装平台, 下载服务器安装包
二、开发前准备
开发前需要先获取对接的身份认证信息(AppId/AppKey), 平台会通过(AppId/AppKey)认证方式来验证请求发送者的身份。AppId和AppKey在安装后平台的运管中心中创建,如下图。
三、对接开发
1. token生成
randNum = randInt() % 900000 + 100000
sign = hmacsha256(AppKey, [AppId]+[timestamp]+[randNum])
token = base64(sv+[sign]+[AppId]+[0]+[randNum]+[timestamp]+[From])
示例(伪代码):
AppId = "31yOb7ulVXoBLYcRbmFesMu8"
AppKey = "dpFhBlov5zOLBqYJXimg6udabM9g30FDJNgt2g"
from = "zax-A37658F19E24"
timestamp = 1686023607
randValue = 156542
sign = hmacsha256("dpFhBlov5zOLBqYJXimg6udabM9g30FDJNgt2g", "31yOb7ulVXoBLYcRbmFesMu8+1686023607+156542")
// sign: "6a85345348c66c64c1f69765bb5f9de4305f4d12f4fc5a9c2550e25750d810e2"
token = base64("sv+6a85345348c66c64c1f69765bb5f9de4305f4d12f4fc5a9c2550e25750d810e2+31yOb7ulVXoBLYcRbmFesMu8+0+156542+1686023607+zax-A37658F19E24")
// token: "c3YrNmE4NTM0NTM0OGM2NmM2NGMxZjY5NzY1YmI1ZjlkZTQzMDVmNGQxMmY0ZmM1YTljMjU1MGUyNTc1MGQ4MTBlMiszMXlPYjd1bFZYb0JMWWNSYm1GZXNNdTgrMCsxNTY1NDIrMTY4NjAyMzYwNyt6YXgtQTM3NjU4RjE5RTI0"
2. 发送请求
发送请求前, 设置Headers中的Authorization值为上述中获取的token
附录
golang示例
package main
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"log"
"math/big"
"net/http"
"time"
)
func HmacSha256(key string, data string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(data))
return hex.EncodeToString(mac.Sum(nil))
}
func randNumber(min int64, max int64) int64 {
a := min
b := max - min
n, err := rand.Int(rand.Reader, big.NewInt(b))
if err != nil {
return time.Now().UnixNano()%b + a
}
return n.Int64() + a
}
type Token struct {
Type string // 类型
Sign string // 签名
AppId string //
Reserve int64 // 保留
Timestamp int64 // 时间戳(单位秒)
From string // 请求者
RandNum int64 // 随机数
appKey string //
}
func NewToken(appId, appKey string, from string) *Token {
t := &Token{
Type: "sv",
Sign: "",
AppId: appId,
Reserve: 0,
Timestamp: time.Now().Unix(),
From: from,
RandNum: randNumber(100000, 999999),
appKey: appKey,
}
return t
}
func (t *Token) sign() {
data := fmt.Sprintf("%v+%v+%v", t.AppId, t.Timestamp, t.RandNum)
t.Sign = HmacSha256(t.appKey, data)
}
func (t *Token) String() string {
t.sign()
data := fmt.Sprintf("%v+%v+%v+%v+%v+%v+%v", t.Type, t.Sign, t.AppId, t.Reserve, t.RandNum, t.Timestamp, t.From)
return base64.RawURLEncoding.EncodeToString([]byte(data))
}
func (t *Token) RealTimeString() string {
t.Timestamp = time.Now().Unix()
t.RandNum = randNumber(100000, 999999)
return t.String()
}
const (
AppId = "31yOb7ulVXoBLYcRbmFesMu8"
AppKey = "dpFhBlov5zOLBqYJXimg6udabM9g30FDJNgt2g"
from = "zax-A37658F19E24"
)
// SendRequest 发送请求
func SendRequest(request *http.Request) (*http.Response, error) {
request.Header.Set("Authorization", NewToken(AppId, AppKey, from).RealTimeString())
return http.DefaultClient.Do(request)
}
func main() {
token := NewToken(AppId, AppKey, from)
log.Println(token.RealTimeString())
request, _ := http.NewRequest(http.MethodPost, "https://domain/api", nil)
response, _ := SendRequest(request)
_ = response
}
java
package token_demo;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Token {
String appId;
String appKey;
String type; // 类型
String sign; // 签名
int reserve; // 保留
long timestamp; // 时间戳(单位秒)
String from; // 请求者
int randNum;// 随机数
public Token(String appId, String appKey, String from) {
this.appId = appId;
this.from = from;
this.appKey = appKey;
this.type = "sv";
this.reserve = 0;
this.timestamp = System.currentTimeMillis() / 1000;
this.randNum = new Random().nextInt(999999 - 100000) + 100000;
}
private void Sign() throws InvalidKeyException, NoSuchAlgorithmException {
String data = String.format("%s+%d+%d", this.appId, this.timestamp, this.randNum);
this.sign = this.HmacSha256(this.appKey, data);
}
public String String() throws InvalidKeyException, NoSuchAlgorithmException {
this.Sign();
String data = String.format("%s+%s+%s+%d+%d+%d+%s", this.type, this.sign, this.appId, this.reserve,
this.randNum, this.timestamp, this.from);
return Base64.getUrlEncoder().encodeToString(data.getBytes());
}
public String RealTimeString() throws InvalidKeyException, NoSuchAlgorithmException {
this.timestamp = System.currentTimeMillis() / 1000;
this.randNum = new Random().nextInt(999999 - 100000) + 100000;
return this.String();
}
String HmacSha256(String key, String data) throws NoSuchAlgorithmException, InvalidKeyException {
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
byte[] out = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return bytesToHex(out);
}
private String bytesToHex(byte[] hash) {
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String AppId = "31yOb7ulVXoBLYcRbmFesMu8";
String AppKey = "dpFhBlov5zOLBqYJXimg6udabM9g30FDJNgt2g";
String from = "zax-A37658F19E24";
Token t = new Token(AppId, AppKey, from);
for (int i = 0; i < 10; i++) {
System.out.println(t.RealTimeString());
}
}
}
在Iframe中集成WebApp页面遇到的问题
1.iframe中跨域页面无法写入cookie,导致依赖cookie传递token的接口失效;目前webapp中全部接口已经在url中携带token。参考MDN Cookie Security
2.iframe中访问麦克风需要向iframe标签添加allow属性,否则iframe中页面无法访问。
语法:
Permissions-Policy:<directive> <allowlist>allow='Permissions-Policy'
<iframe src="<https://example.com>" allow="microphone https://example.com;"></iframe>
参考文档
Deprecating Permissions in Cross-Origin Iframes
服务器部署到线上后如何解决跨域问题

免登录跳转地址格式
使用用户名密码跳转:
http://192.168.88.11:9780/client/webapps/safeProduction/#/login?&user=admin&password=123456&device=PU_55AA00
https://www.dunhun.cn/client/app/#/login?&user=test&password=123&device=PU_22060310device
参数用来指示跳转到对应的设备详情界面,如果不填,会跳转到登录后的首页。
使用token跳转:
好处:跳转时不会泄漏密码。
http://192.168.88.11:9780/client/webapps/safeProduction/#/login?&token=2CC73DBF37BF358F809FD435DDC1EB8C&device=PU_55AA00
第三方平台不调用登录接口,通过appid和key获取token。
参考第三方平台对接e接口说明
@besovideo/webrtc-player
https://www.npmjs.com/package/@besovideo/webrtc-player