105 lines
3.3 KiB
Vue
105 lines
3.3 KiB
Vue
|
|
<template>
|
|||
|
|
<div>
|
|||
|
|
<video ref="videoPlayer" autoplay playsinline></video>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import axios from "axios";
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
videoPlayer: null,
|
|||
|
|
source: null, // 用于存储axios的CancelToken.source对象,以便后续取消请求
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
mounted() {
|
|||
|
|
this.videoPlayer = this.$refs.videoPlayer;
|
|||
|
|
this.startVideoStream();
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
startVideoStream() {
|
|||
|
|
//let url = "https://gep.ljsea.top/device/video_feed?" +"&device_id=" +localStorage.getItem("realvp_device_id") +"&token=" +localStorage.getItem("token"); //视频地址
|
|||
|
|
|
|||
|
|
const deviceId = localStorage.getItem("realvp_device_id"); // 替换为实际存储设备id的方式和键名
|
|||
|
|
const token = localStorage.getItem("token"); // 替换为实际存储token的方式和键名
|
|||
|
|
const url = `https://gep.ljsea.top/device/video_feed?device_id=${deviceId}&token=${token}`;
|
|||
|
|
this.source = axios.CancelToken.source();
|
|||
|
|
axios
|
|||
|
|
.get(url, {
|
|||
|
|
responseType: "arraybuffer",
|
|||
|
|
cancelToken: this.source.token,
|
|||
|
|
})
|
|||
|
|
.then((response) => {
|
|||
|
|
console.log("获取视频流成功");
|
|||
|
|
const reader = response.data
|
|||
|
|
.pipeThrough(new window.TextDecoderStream())
|
|||
|
|
.getReader();
|
|||
|
|
const processData = () => {
|
|||
|
|
reader.read().then(({ done, value }) => {
|
|||
|
|
if (done) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const dataString = new window.TextDecoder().decode(value);
|
|||
|
|
const parts = dataString.split("\r\n");
|
|||
|
|
|
|||
|
|
// 检查是否包含关键的视频流格式标识
|
|||
|
|
const hasFrameMarker = parts.some((part) =>
|
|||
|
|
part.startsWith("--frame")
|
|||
|
|
);
|
|||
|
|
const hasContentTypeMarker = parts.some(
|
|||
|
|
(part) => part === "Content-Type: image/jpeg"
|
|||
|
|
);
|
|||
|
|
if (!hasFrameMarker || !hasContentTypeMarker) {
|
|||
|
|
console.error("视频流数据格式不符合预期,无法解析");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
parts.forEach((part) => {
|
|||
|
|
if (part.startsWith("--frame")) {
|
|||
|
|
const imageData = [];
|
|||
|
|
let isImageData = false;
|
|||
|
|
parts.forEach((subPart) => {
|
|||
|
|
if (isImageData) {
|
|||
|
|
imageData.push(subPart);
|
|||
|
|
}
|
|||
|
|
if (subPart === "Content-Type: image/jpeg") {
|
|||
|
|
isImageData = true;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
const imageBlob = new Blob(imageData, { type: "image/jpeg" });
|
|||
|
|
const objectURL = URL.createObjectURL(imageBlob);
|
|||
|
|
this.videoPlayer.src = objectURL;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
processData();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
processData();
|
|||
|
|
})
|
|||
|
|
.catch((error) => {
|
|||
|
|
if (axios.isCancel(error)) {
|
|||
|
|
console.log("请求已取消");
|
|||
|
|
} else {
|
|||
|
|
console.error("获取视频流出错:", error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
beforeUnmount() {
|
|||
|
|
if (this.source) {
|
|||
|
|
this.source.cancel("组件即将销毁,取消视频流请求");
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
video {
|
|||
|
|
width: 100%;
|
|||
|
|
max-width: 800px;
|
|||
|
|
margin: 0 auto;
|
|||
|
|
}
|
|||
|
|
</style>
|