Browse Source

更换流媒体

master
chendingwei 2 years ago
parent
commit
0f5e0897ac
  1. 4
      index.html
  2. 0
      public/decoder.js
  3. 0
      public/decoder.wasm
  4. 0
      public/jessibuca.js
  5. 20
      src/App.vue
  6. 637
      src/utils/jessibuca-demo/jessibuca.d.ts
  7. 65
      src/views/page/cameraCenter.vue

4
index.html

@ -2,13 +2,13 @@
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="./jessibuca.js"></script>
</body>
</html>

0
src/utils/jessibuca-demo/decoder.js → public/decoder.js

0
src/utils/jessibuca-demo/decoder.wasm → public/decoder.wasm

0
src/utils/jessibuca-demo/jessibuca.js → public/jessibuca.js

20
src/App.vue

@ -15,28 +15,30 @@ import { storeToRefs } from 'pinia';
import MqttUnit from "@/utils/mqttunit";
import { onMounted } from 'vue';
import {WebRtcStreamer} from '@/utils/webrtc-streamer/webrtcstreamer.js'
let pinia = useStore()
onMounted(() => {
//
document.oncontextmenu = new Function("event.returnValue=false");
let client = new MqttUnit().mqttInit();
// let client = new MqttUnit().mqttInit();
let WebRtcStreamerRt=WebRtcStreamer()
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
window.WebRtcStreamer = WebRtcStreamerRt;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = WebRtcStreamerRt;
// window.Jessibuca=Jessibuca
}
client.on('message', (topic: string, message: Uint8Array) => {
const utf8decoder = new TextDecoder();
const msgString = utf8decoder.decode(message);
if (topic.startsWith("/server/warning")) {
// $mitt.emit("warnInfo", msgString);
console.log(msgString);
// client.on('message', (topic: string, message: Uint8Array) => {
// const utf8decoder = new TextDecoder();
// const msgString = utf8decoder.decode(message);
// if (topic.startsWith("/server/warning")) {
// // $mitt.emit("warnInfo", msgString);
// console.log(msgString);
}
})
// }
// })
})
function timeupdatefn(e:any) {
let {audioCount,audioCurrentCount}=storeToRefs(pinia)

637
src/utils/jessibuca-demo/jessibuca.d.ts

@ -1,637 +0,0 @@
declare namespace Jessibuca {
/** 超时信息 */
enum TIMEOUT {
/** 当play()的时候,如果没有数据返回 */
loadingTimeout = 'loadingTimeout',
/** 当播放过程中,如果超过timeout之后没有数据渲染 */
delayTimeout = 'delayTimeout',
}
/** 错误信息 */
enum ERROR {
/** 播放错误,url 为空的时候,调用 play 方法 */
playError = 'playError',
/** http 请求失败 */
fetchError = 'fetchError',
/** websocket 请求失败 */
websocketError = 'websocketError',
/** webcodecs 解码 h265 失败 */
webcodecsH265NotSupport = 'webcodecsH265NotSupport',
/** mediaSource 解码 h265 失败 */
mediaSourceH265NotSupport = 'mediaSourceH265NotSupport',
/** wasm 解码失败 */
wasmDecodeError = 'wasmDecodeError',
}
interface Config {
/**
*
* * string document.getElementById('id')
* */
container: HTMLElement | string;
/**
*
*/
videoBuffer?: number;
/**
* worker地址
* * decoder.js文件 decoder.js decoder.wasm文件必须是放在同一个目录下面 */
decoder?: string;
/**
* 使
*/
forceNoOffscreen?: boolean;
/**
* 'visibilityState''hidden'
*/
hiddenAutoPause?: boolean;
/**
* `false`
*/
hasAudio?: boolean;
/**
* 0()180270
*/
rotate?: boolean;
/**
* 1. `true`,canvas区域,, `setScaleMode(1)`
* 2. `false`canvas区域, `setScaleMode(0)`
*/
isResize?: boolean;
/**
* 1. `true`,canvas区域,,, `setScaleMode(2)`
*/
isFullResize?: boolean;
/**
* 1. `true`ws协议不检验是否以.flv为依据
*/
isFlv?: boolean;
/**
*
*/
debug?: boolean;
/**
* 1. ,
* 2. (loading)(heart),,timeout事件
*/
timeout?: number;
/**
* 1. ,
* 2. ,,timeout事件
*/
heartTimeout?: number;
/**
* 1. ,
* 2. ,,timeout事件
*/
loadingTimeout?: number;
/**
*
*/
supportDblclickFullscreen?: boolean;
/**
*
*/
showBandwidth?: boolean;
/**
*
*/
operateBtns?: {
/** 是否显示全屏按钮 */
fullscreen?: boolean;
/** 是否显示截图按钮 */
screenshot?: boolean;
/** 是否显示播放暂停按钮 */
play?: boolean;
/** 是否显示声音按钮 */
audio?: boolean;
/** 是否显示录制按 */
record?: boolean;
};
/**
* , canvas标签渲染视频并不会像video标签那样保持屏幕常亮
*/
keepScreenOn?: boolean;
/**
*
*/
isNotMute?: boolean;
/**
*
*/
loadingText?: string;
/**
*
*/
background?: string;
/**
* MediaSource硬解码
* * H.264Safari on iOS不支持
* * forceNoOffscreen false ()
*/
useMSE?: boolean;
/**
* Webcodecs硬解码
* * H.264 (chrome 94https或者localhost环境)
* * forceNoOffscreen false )
* */
useWCS?: boolean;
/**
*
* esc -> 退arrowUp -> arrowDown ->
*/
hotKey?: boolean;
/**
* 使MSE或者Webcodecs H265的时候wasm模式
* false Error true wasm模式播放
*/
autoWasm?: boolean;
/**
* heartTimeout ,
*/
heartTimeoutReplay?: boolean,
/**
* heartTimeoutReplay
*/
heartTimeoutReplayTimes?: number,
/**
* loadingTimeout loading之后自动再播放,
*/
loadingTimeoutReplay?: boolean,
/**
* heartTimeoutReplay
*/
loadingTimeoutReplayTimes?: number
/**
* wasm解码报错之后
*/
wasmDecodeErrorReplay?: boolean,
/**
* https://github.com/langhuihui/jessibuca/issues/152 解决方案
* WebGL图像预处理默认每次取4字节的数据540x960分辨率下的UV分量宽度是540/2=2704绿
*/
openWebglAlignment?: boolean
}
}
declare class Jessibuca {
constructor(config?: Jessibuca.Config);
/**
*
@example
// 开启
jessibuca.setDebug(true)
// 关闭
jessibuca.setDebug(false)
*/
setDebug(flag: boolean): void;
/**
*
@example
jessibuca.mute()
*/
mute(): void;
/**
*
@example
jessibuca.cancelMute()
*/
cancelMute(): void;
/**
*
*
* iPhonechrome等要求自动播放时使
*
* https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
*/
audioResume(): void;
/**
*
* ,
* ,,timeout事件
@example
jessibuca.setTimeout(10)
jessibuca.on('timeout',function(){
//
});
*/
setTimeout(): void;
/**
* @param mode
* 0 canvas区域, `isResize` false
*
* 1 ,canvas区域,, `isResize` true
*
* 2 ,canvas区域,,, `isFullResize` true
@example
jessibuca.setScaleMode(0)
jessibuca.setScaleMode(1)
jessibuca.setScaleMode(2)
*/
setScaleMode(mode: number): void;
/**
*
*
* pause `play()`
@example
jessibuca.pause().then(()=>{
console.log('pause success')
jessibuca.play().then(()=>{
}).catch((e)=>{
})
}).catch((e)=>{
console.log('pause error',e);
})
*/
pause(): Promise<void>;
/**
* ,
@example
jessibuca.close();
*/
close(): void;
/**
*
@example
jessibuca.destroy()
*/
destroy(): void;
/**
*
@example
jessibuca.clearView()
*/
clearView(): void;
/**
*
@example
jessibuca.play('url').then(()=>{
console.log('play success')
}).catch((e)=>{
console.log('play error',e)
})
//
jessibuca.play()
*/
play(url?: string): Promise<void>;
/**
*
*/
resize(): void;
/**
*
*
* `videoBuffer`
*
@example
// 设置 200ms 缓冲
jessibuca.setBufferTime(0.2)
*/
setBufferTime(time: number): void;
/**
* 0() 180270
*
* > iOS没有全屏API *
@example
jessibuca.setRotate(0)
jessibuca.setRotate(90)
jessibuca.setRotate(270)
*/
setRotate(deg: number): void;
/**
*
* 0 1
*
* > mute cancelMute setVolume(0) mute方法mute setVolume(0)0
* @param volume 0;1
@example
jessibuca.setVolume(0.2)
jessibuca.setVolume(0)
jessibuca.setVolume(1)
*/
setVolume(volume: number): void;
/**
*
@example
var result = jessibuca.hasLoaded()
console.log(result) // true
*/
hasLoaded(): boolean;
/**
* , canvas标签渲染视频并不会像video标签那样保持屏幕常亮
* H5目前在chrome\edge 84, android chrome 84API, https页面
*
@example
jessibuca.setKeepScreenOn()
*/
setKeepScreenOn(): boolean;
/**
* ()
@example
jessibuca.setFullscreen(true)
//
jessibuca.setFullscreen(false)
*/
setFullscreen(flag: boolean): void;
/**
*
*
* @param filename , , `时间戳`
* @param format , png或jpeg或者webp , `png`
* @param quality , jpeg或者webp时0 ~ 1 , `0.92`
* @param type , download或者base64或者blob`download`
@example
jessibuca.screenshot("test","png",0.5)
const base64 = jessibuca.screenshot("test","png",0.5,'base64')
const fileBlob = jessibuca.screenshot("test",'blob')
*/
screenshot(filename?: string, format?: string, quality?: number, type?: string): void;
/**
*
* @param fileName
* @param fileType webmwebm mp4
@example
jessibuca.startRecord('xxx','webm')
*/
startRecord(fileName: string, fileType: string): void;
/**
*
@example
jessibuca.stopRecordAndSave()
*/
stopRecordAndSave(): void;
/**
*
@example
var result = jessibuca.isPlaying()
console.log(result) // true
*/
isPlaying(): boolean;
/**
*
@example
var result = jessibuca.isMute()
console.log(result) // true
*/
isMute(): boolean;
/**
*
@example
var result = jessibuca.isRecording()
console.log(result) // true
*/
isRecording(): boolean;
/**
* jessibuca
* @example
* jessibuca.on("load",function(){console.log('load')})
*/
on(event: 'load', callback: () => void): void;
/**
* ms
* @example
* jessibuca.on('timeUpdate',function (ts) {console.log('timeUpdate',ts);})
*/
on(event: 'timeUpdate', callback: () => void): void;
/**
* 2
* @example
* jessibuca.on("videoInfo",function(data){console.log('width:',data.width,'height:',data.width)})
*/
on(event: 'videoInfo', callback: (data: {
/** 视频宽 */
width: number;
/** 视频高 */
height: number;
}) => void): void;
/**
* 2
* @example
* jessibuca.on("audioInfo",function(data){console.log('numOfChannels:',data.numOfChannels,'sampleRate',data.sampleRate)})
*/
on(event: 'audioInfo', callback: (data: {
/** 声频通道 */
numOfChannels: number;
/** 采样率 */
sampleRate: number;
}) => void): void;
/**
*
* @example
* jessibuca.on("log",function(data){console.log('data:',data)})
*/
on(event: 'log', callback: () => void): void;
/**
*
* @example
* jessibuca.on("error",function(error){
if(error === Jessibuca.ERROR.fetchError){
//
}
else if(error === Jessibuca.ERROR.webcodecsH265NotSupport){
//
}
console.log('error:',error)
})
*/
on(event: 'error', callback: (err: Jessibuca.ERROR) => void): void;
/**
* KB 1,
* @example
* jessibuca.on("kBps",function(data){console.log('kBps:',data)})
*/
on(event: 'kBps', callback: (value: number) => void): void;
/**
*
* @example
* jessibuca.on("start",function(){console.log('start render')})
*/
on(event: 'start', callback: () => void): void;
/**
* ,
* @example
* jessibuca.on("timeout",function(error){console.log('timeout:',error)})
*/
on(event: 'timeout', callback: (error: Jessibuca.TIMEOUT) => void): void;
/**
* play()
* @example
* jessibuca.on("loadingTimeout",function(){console.log('timeout')})
*/
on(event: 'loadingTimeout', callback: () => void): void;
/**
* timeout之后没有数据渲染
* @example
* jessibuca.on("delayTimeout",function(){console.log('timeout')})
*/
on(event: 'delayTimeout', callback: () => void): void;
/**
*
* @example
* jessibuca.on("fullscreen",function(flag){console.log('is fullscreen',flag)})
*/
on(event: 'fullscreen', callback: () => void): void;
/**
*
* @example
* jessibuca.on("play",function(flag){console.log('play')})
*/
on(event: 'play', callback: () => void): void;
/**
*
* @example
* jessibuca.on("pause",function(flag){console.log('pause')})
*/
on(event: 'pause', callback: () => void): void;
/**
* boolean值
* @example
* jessibuca.on("mute",function(flag){console.log('is mute',flag)})
*/
on(event: 'mute', callback: () => void): void;
/**
* 1
* @example
* jessibuca.on("stats",function(s){console.log("stats is",s)})
*/
on(event: 'stats', callback: (stats: {
/** 当前缓冲区时长,单位毫秒 */
buf: number;
/** 当前视频帧率 */
fps: number;
/** 当前音频码率,单位bit */
abps: number;
/** 当前视频码率,单位bit */
vbps: number;
/** 当前视频帧pts,单位毫秒 */
ts: number;
}) => void): void;
/**
* 1
* @param performance 0: 表示卡顿,1: 表示流畅,2: 表示非常流程
* @example
* jessibuca.on("performance",function(performance){console.log("performance is",performance)})
*/
on(event: 'performance', callback: (performance: 0 | 1 | 2) => void): void;
/**
*
* @example
* jessibuca.on("recordStart",function(){console.log("record start")})
*/
on(event: 'recordStart', callback: () => void): void;
/**
*
* @example
* jessibuca.on("recordEnd",function(){console.log("record end")})
*/
on(event: 'recordEnd', callback: () => void): void;
/**
* 1s一次
* @example
* jessibuca.on("recordingTimestamp",function(timestamp){console.log("recordingTimestamp is",timestamp)})
*/
on(event: 'recordingTimestamp', callback: (timestamp: number) => void): void;
/**
* play方法 -> -> -> ->
* @param event
* @param callback
*/
on(event: 'playToRenderTimes', callback: (times: {
playInitStart: number, // 1 初始化
playStart: number, // 2 初始化
streamStart: number, // 3 网络请求
streamResponse: number, // 4 网络请求
demuxStart: number, // 5 解封装
decodeStart: number, // 6 解码
videoStart: number, // 7 渲染
playTimestamp: number,// playStart- playInitStart
streamTimestamp: number,// streamStart - playStart
streamResponseTimestamp: number,// streamResponse - streamStart
demuxTimestamp: number, // demuxStart - streamResponse
decodeTimestamp: number, // decodeStart - demuxStart
videoTimestamp: number,// videoStart - decodeStart
allTimestamp: number // videoStart - playInitStart
}) => void): void
/**
*
*
@example
jessibuca.on("load",function(){console.log('load')})
*/
on(event: string, callback: Function): void;
}
export default Jessibuca;

65
src/views/page/cameraCenter.vue

@ -3,10 +3,11 @@
<div id="videoWrapper">
<canvas ref="videoCanvas" id="videoCanvas" :width="canvasWidth" :height="canvasHeight">
</canvas>
<video ref="videoPlayer" id="videoPlayer" disablePictureInPicture autoplay muted
<!-- <video ref="videoPlayer" id="videoPlayer" disablePictureInPicture autoplay muted
@loadedmetadata="changeVideoCanvasSize" @mouseover="mouseOverVideo" @mouseout="mouseOutVideo"
@mousedown="mouseDownVideo">
</video>
</video> -->
<div id="videoPlayer" @mousedown="mouseDownVideo"></div>
<div v-for="item in labelList" :class="`labels ${item.groupName == '视频标签' ? 'lavels-video' : 'lavels-build'}`"
:key="item.id"
:style="`top:${canvasHeight * item.canvasTopRatio}px;left:${canvasWidth * item.canvasLeftRatio}px`">
@ -22,7 +23,7 @@
<img src="@/assets/images/buildLabelLine.png">
<div>
<img src="@/assets/images/buildLabelBg.png">
<div class="label-content">{{ item.name }}</div>
<div class="label-content">{{ item.name }}</div>
</div>
</div>
<template #overlay>
@ -92,14 +93,44 @@ let labelList = ref<any[]>([])
let addLabel = storeToRefs(piniaStore).addLabel
let addLabelLeft = ref(0);
let addLabelTop = ref(0);
var jessibuca = ref();
onMounted(() => {
{
player = <HTMLVideoElement>document.querySelector('#videoPlayer');
canvas = <HTMLCanvasElement>document.querySelector('#videoCanvas');
loadVideoPlayer();
loadVideoCanvas();
changeVideoCanvasSize();
var showOperateBtns = false; //
var forceNoOffscreen = true; //
jessibuca.value = new Jessibuca({
container: player,
videoBuffer: 1, //
isResize: false,
text: "",
loadingText: "",
useMSE: false,
debug: false,
showBandwidth: showOperateBtns, //
operateBtns: {
fullscreen: showOperateBtns,
screenshot: showOperateBtns,
play: showOperateBtns,
audio: false,
recorder: false
},
forceNoOffscreen: forceNoOffscreen,
isNotMute: false,
},);
jessibuca.value.onLog = (msg:any) => console.error(msg);
jessibuca.value.onRecord = (status:any) => console.log('onRecord', status);
jessibuca.value.onPause = () => console.log('onPause');
jessibuca.value.onPlay = () => console.log('onPlay');
jessibuca.value.onFullscreen = (msg:any) => console.log('onFullscreen', msg);
jessibuca.value.onMute = (msg:any) => console.log('onMute', msg);
}
})
@ -108,6 +139,7 @@ onMounted(() => {
watch(curSelectKey, (newVal, oldVal) => {
labelList.value = []
switchCamera(newVal)
jessibuca.value.play("ws://192.168.1.119:8080/jessica/live/test.flv")
})
watch(addLabel, (newVal, oldVal) => {
if (newVal.isAddLabel) {
@ -115,6 +147,7 @@ watch(addLabel, (newVal, oldVal) => {
document.body.onmousemove = (e) => {
addLabelTop.value = e.clientY
addLabelLeft.value = e.clientX
}
}
})
@ -141,7 +174,7 @@ function deleteLabel(id: number) {
function loadVideoPlayer() {
let elmId = 'videoPlayer';
let url = apiUrl.WebRtcUrl;
webRtcServer.value = new WebRtcStreamer(elmId, url);
// webRtcServer.value = new WebRtcStreamer(elmId, url);
}
function loadVideoCanvas() {
window.addEventListener('resize', () => {
@ -205,8 +238,7 @@ function mouseOutVideo() {
player.classList.remove('activeChoose');
}
function mouseDownVideo(e: MouseEvent) {
console.log('mouseDownVideo', e.buttons);
if (!isActiveChoose.value) return;
// e.buttons == 1
if (e.buttons == 1) {
@ -216,8 +248,8 @@ function mouseDownVideo(e: MouseEvent) {
let videoHeight = player.videoHeight | 1080;
let canvasWidth = player.clientWidth;
let canvasHeight = player.clientHeight;
let canvasLeft = e.offsetX;
let canvasTop = e.offsetY;
let canvasLeft = addLabelLeft.value;
let canvasTop = addLabelTop.value;
let canvasLeftRatio = canvasLeft / canvasWidth;
let canvasTopRatio = canvasTop / canvasHeight;
let entity = {
@ -254,11 +286,12 @@ function mouseDownVideo(e: MouseEvent) {
isDelete: false,
inFlag: true,
name: name,
groupName:name
groupName: name
}
labelList.value.push(obj)
isActiveChoose.value = false
piniaStore.updateIsAddLabel({ cmMarkGroupId: 0, labelType: "", isAddLabel: false })
}
});
})
@ -290,8 +323,8 @@ function switchCamera(cameraId: string) {
// step3, connect webrtc-steamer.
console.log('connect webrtc-steamer.');
webRtcServer.value.disconnect();
webRtcServer.value.connect(rtspUrl);
// webRtcServer.value.disconnect();
// webRtcServer.value.connect(rtspUrl);
// step4, active camera searcher.
markSearchApi.IsExistsSearcher({
@ -403,8 +436,8 @@ onUnmounted(() => {
// webRtcServer
if (!webRtcServer.value) return;
console.log(123);
webRtcServer.value.disconnect();
webRtcServer.value = null;
// webRtcServer.value.disconnect();
// webRtcServer.value = null;
}
})
</script>
@ -463,7 +496,7 @@ onUnmounted(() => {
}
.lavels-build {
transform: translate(-10px, -10px);
position: fixed;
}

Loading…
Cancel
Save