|
@ -1,10 +1,26 @@ |
|
|
import flvJs from "flv.js"; |
|
|
import flvJs from "flv.js"; |
|
|
|
|
|
import mpegTsJs from "mpegts.js"; |
|
|
|
|
|
import { Message } from "element-ui"; |
|
|
// mpegts.js
|
|
|
// mpegts.js
|
|
|
import { |
|
|
import { |
|
|
getCameraStream, |
|
|
getCameraStream, |
|
|
getNearCamera, |
|
|
getNearCamera, |
|
|
} from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js"; |
|
|
} from "@screen/pages/Home/components/RoadAndEvents/utils/httpList.js"; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* flv 视频测试 |
|
|
|
|
|
* https://bilibili.github.io/flv.js/demo/
|
|
|
|
|
|
* https://www.zngg.net/tool/detail/FlvPlayer
|
|
|
|
|
|
* https://xqq.im/mpegts.js/demo/arib.html
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* flv 视频流 |
|
|
|
|
|
*/ |
|
|
|
|
|
const testFlvUrl = |
|
|
|
|
|
"https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-720p.flv"; |
|
|
|
|
|
// "https://v17.dogevideo.com/vcloud/17/v/20190424/1556036075_818c4125ec9c8cbc7a7a8a7cc1601512/1057/e51d88e79732fb952ebdbb4a57aa628a.flv?vkey=D9EAC2&tkey=17062414799ec7c466dc&auth_key=1706255879-zQK6JYToEeRiUark-0-6e363fb7709e783e64efc919d2267bdc";
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* |
|
|
* |
|
|
* @param {HTMLElement} container 容器 |
|
|
* @param {HTMLElement} container 容器 |
|
@ -42,6 +58,34 @@ export async function openVideoStream(container, { camId, url } = {}) { |
|
|
return player; |
|
|
return player; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async function getUrl({ camId, url, pileNum } = {}) { |
|
|
|
|
|
// return testFlvUrl;
|
|
|
|
|
|
if (url) return url; |
|
|
|
|
|
|
|
|
|
|
|
if (pileNum) { |
|
|
|
|
|
const { code, data } = await getNearCamera(pileNum).catch(() => ({})); |
|
|
|
|
|
if (code != 200 || !data?.length) { |
|
|
|
|
|
Message.warning("未获取到附近的相机信息"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
camId = data[0].camId; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (camId) { |
|
|
|
|
|
const { code, data } = await getCameraStream(camId).catch(() => ({})); |
|
|
|
|
|
if (code != 200) { |
|
|
|
|
|
Message.warning("未获取到当前相机的播放地址"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
url = data.liveUrl; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!url) return Promise.reject("获取 url 失败!"); |
|
|
|
|
|
|
|
|
|
|
|
return url; |
|
|
|
|
|
} |
|
|
export class HttpLivePlayer { |
|
|
export class HttpLivePlayer { |
|
|
/** |
|
|
/** |
|
|
* @type { flvJs.Player } |
|
|
* @type { flvJs.Player } |
|
@ -60,33 +104,37 @@ export class HttpLivePlayer { |
|
|
|
|
|
|
|
|
constructor(container, options) { |
|
|
constructor(container, options) { |
|
|
this.container = container; |
|
|
this.container = container; |
|
|
|
|
|
if (!flvJs.getFeatureList().mseLiveFlvPlayback) |
|
|
|
|
|
return Message.error("浏览器不支持播放 flv 视频流"); |
|
|
|
|
|
|
|
|
this.getUrl(options).then(() => { |
|
|
this.initLiveVideo(); |
|
|
|
|
|
getUrl(options).then((url) => { |
|
|
|
|
|
this.url = url; |
|
|
this.initLiveVideo(); |
|
|
this.initLiveVideo(); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
async getUrl({ camId, url, pileNum } = {}) { |
|
|
// async getUrl({ camId, url, pileNum } = {}) {
|
|
|
this.url = url; |
|
|
// this.url = url;
|
|
|
|
|
|
|
|
|
if (pileNum) { |
|
|
// if (pileNum) {
|
|
|
const { code, data } = await getNearCamera(pileNum).catch(() => ({})); |
|
|
// const { code, data } = await getNearCamera(pileNum).catch(() => ({}));
|
|
|
if (code != 200 || !data?.length) return; |
|
|
// if (code != 200 || !data?.length) return;
|
|
|
|
|
|
|
|
|
camId = data[0].camId; |
|
|
// camId = data[0].camId;
|
|
|
} |
|
|
// }
|
|
|
|
|
|
|
|
|
if (camId) { |
|
|
// if (camId) {
|
|
|
const { code, data } = await getCameraStream(camId).catch(() => ({})); |
|
|
// const { code, data } = await getCameraStream(camId).catch(() => ({}));
|
|
|
if (code != 200) return; |
|
|
// if (code != 200) return;
|
|
|
|
|
|
|
|
|
url = data.liveUrl; |
|
|
// url = data.liveUrl;
|
|
|
} |
|
|
// }
|
|
|
|
|
|
|
|
|
if (!url) return Promise.reject("获取 url 失败!"); |
|
|
// if (!url) return Promise.reject("获取 url 失败!");
|
|
|
|
|
|
|
|
|
this.url = url; |
|
|
// this.url = url;
|
|
|
} |
|
|
// }
|
|
|
|
|
|
|
|
|
destroy() { |
|
|
destroy() { |
|
|
if (!this.player) return; |
|
|
if (!this.player) return; |
|
@ -117,10 +165,11 @@ export class HttpLivePlayer { |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
autoCleanupSourceBuffer: true, |
|
|
autoCleanupSourceBuffer: true, |
|
|
// enableWorker: false, //不启用分离线程
|
|
|
enableWorker: true, // 启用分离的线程进行转换
|
|
|
// enableStashBuffer: true, //关闭IO隐藏缓冲区
|
|
|
enableStashBuffer: false, // 关闭IO隐藏缓冲区 如果您需要实时(最小延迟)来进行实时流播放,则设置为false
|
|
|
|
|
|
stashInitialSize: 128, |
|
|
isLive: true, |
|
|
isLive: true, |
|
|
lazyLoad: false, |
|
|
lazyLoad: true, |
|
|
} |
|
|
} |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
@ -129,13 +178,35 @@ export class HttpLivePlayer { |
|
|
this.player.load(); |
|
|
this.player.load(); |
|
|
this.player.play(); |
|
|
this.player.play(); |
|
|
|
|
|
|
|
|
|
|
|
this.container.addEventListener("progress", () => { |
|
|
|
|
|
let end = player.buffered.end(0); //获取当前buffered值(缓冲区末尾)
|
|
|
|
|
|
let delta = end - player.currentTime; //获取buffered与当前播放位置的差值
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟过大,通过跳帧的方式更新视频
|
|
|
|
|
|
if (delta > 10 || delta < 0) { |
|
|
|
|
|
this.player.currentTime = this.player.buffered.end(0) - 1; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 追帧
|
|
|
|
|
|
if (delta > 1) { |
|
|
|
|
|
this.container.playbackRate = 1.1; |
|
|
|
|
|
} else { |
|
|
|
|
|
this.container.playbackRate = 1; |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
this.player.on(flvJs.Events.ERROR, (errorType, errorDetail, errorInfo) => { |
|
|
this.player.on(flvJs.Events.ERROR, (errorType, errorDetail, errorInfo) => { |
|
|
console.log("errorType", errorType); |
|
|
console.log("errorType", errorType); |
|
|
console.log("errorDetail", errorDetail); |
|
|
console.log("errorDetail", errorDetail); |
|
|
console.log("errorInfo", errorInfo); |
|
|
console.log("errorInfo", errorInfo); |
|
|
|
|
|
|
|
|
// 视频出错后销毁重建
|
|
|
// 视频出错后销毁重建
|
|
|
|
|
|
this.player.pause(); |
|
|
|
|
|
this.player.unload(); |
|
|
|
|
|
this.player.detachMediaElement(); |
|
|
this.destroy(); |
|
|
this.destroy(); |
|
|
|
|
|
this.player = null; |
|
|
|
|
|
|
|
|
this.initLiveVideo(); |
|
|
this.initLiveVideo(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
@ -145,9 +216,61 @@ export class HttpLivePlayer { |
|
|
this.lastDecodedFrames = res.decodedFrames; |
|
|
this.lastDecodedFrames = res.decodedFrames; |
|
|
} else { |
|
|
} else { |
|
|
this.lastDecodedFrames = 0; |
|
|
this.lastDecodedFrames = 0; |
|
|
this.destroy(); |
|
|
// this.destroy();
|
|
|
this.initLiveVideo(); |
|
|
// this.initLiveVideo();
|
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* https://juejin.cn/post/6855577308271476743
|
|
|
|
|
|
* https://www.cnblogs.com/xiahj/p/flvExtend.html
|
|
|
|
|
|
* 使用 mpegTsJs |
|
|
|
|
|
* @param {*} container |
|
|
|
|
|
* @param {*} options |
|
|
|
|
|
*/ |
|
|
|
|
|
export async function openLiveVideo(container, options) { |
|
|
|
|
|
if (!mpegTsJs.getFeatureList().mseLivePlayback) |
|
|
|
|
|
return Message.error("浏览器不支持播放 flv 视频流"); |
|
|
|
|
|
|
|
|
|
|
|
const url = await getUrl(options).catch(() => {}); |
|
|
|
|
|
console.log( |
|
|
|
|
|
"%c [ url ]-212-「videoStream.js」", |
|
|
|
|
|
"font-size:15px; background:#6f87e8; color:#b3cbff;", |
|
|
|
|
|
url |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (!url) return; |
|
|
|
|
|
|
|
|
|
|
|
const player = mpegTsJs.createPlayer( |
|
|
|
|
|
{ |
|
|
|
|
|
type: "flv", |
|
|
|
|
|
url, |
|
|
|
|
|
isLive: true, |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
autoCleanupSourceBuffer: true, |
|
|
|
|
|
enableWorker: true, // 启用分离的线程进行转换
|
|
|
|
|
|
enableStashBuffer: false, // 关闭IO隐藏缓冲区 如果您需要实时(最小延迟)来进行实时流播放,则设置为false
|
|
|
|
|
|
stashInitialSize: 128, |
|
|
|
|
|
isLive: true, |
|
|
|
|
|
lazyLoad: true, |
|
|
|
|
|
} |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
player.attachMediaElement(container); |
|
|
|
|
|
|
|
|
|
|
|
container.addEventListener("play", () => { |
|
|
|
|
|
try { |
|
|
|
|
|
let end = player.buffered.end(0) - 1; |
|
|
|
|
|
|
|
|
|
|
|
player.currentTime = end; |
|
|
|
|
|
} catch (error) {} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
player.load(); |
|
|
|
|
|
player.play(); |
|
|
|
|
|
|
|
|
|
|
|
return player; |
|
|
|
|
|
} |
|
|