祗疼妳一个 发表于 2025-1-6 22:28:41

vue2使用rtsp视频流接入海康威视摄像头(纯前端)

一.获取海康威视rtsp视频流

海康威视官方的RTSP最新取流格式如下:
rtsp://用户名:密码@IP:554/Streaming/Channels/101
用户名和密码
https://i-blog.csdnimg.cn/blog_migrate/db1a05db005c218ba5b55d99b5ca23d0.pngIP就是登岸摄像头时候的IP(笔者这里IP是192.168.1.210)
https://i-blog.csdnimg.cn/blog_migrate/3aad1d28792cfb2438b0cb11c0b4b0fa.png
以是笔者的rtsp流地址就是rtsp://用户名:密码@192.168.1.210:554/Streaming/Channels/101
二. 测试rtsp流是否可以播放

1.实现RTSP协议推流需要做的配置

1.1关闭萤石云的接入

https://i-blog.csdnimg.cn/blog_migrate/28d71a5cf36fb35709259cf5abb61f20.png
1.2调整视频编码为H.264

https://i-blog.csdnimg.cn/blog_migrate/c0202556b241be5a7c4961e542d4552e.png
2.安装VLC播放器

在此下载 video mediaplay官网 即(VLC)
安装完成之后 打开VLC播放器https://img-blog.csdnimg.cn/direct/e63b792a9d4647b080826f2db8e26172.gif
在VLC播放器中打开网络串流 输入rtsp地址
乐成的话我们可以看到我们所表现的摄像头https://i-blog.csdnimg.cn/blog_migrate/e0ae48311053e0802098b1993bcfed9a.png
如果RTSP流地址正确且取流乐成,VLC的界面会表现监控画面。否则会报错,报错信息写在了日记里,在[工具]>[消息]里可以看到
三.在vue2中引用rtsp视频流情势的海康摄像头

1.新建webrtcstreamer.js文件

在public文件夹下新建webrtcstreamer.js 代码贴在下方,复制粘贴即可
var WebRtcStreamer = (function() {

/**
* Interface with WebRTC-streamer API
* @constructor
* @param {string} videoElement - id of the video element tag
* @param {string} srvurl -url of webrtc-streamer (default is current location)
*/
var WebRtcStreamer = function WebRtcStreamer (videoElement, srvurl) {
        if (typeof videoElement === "string") {
                this.videoElement = document.getElementById(videoElement);
        } else {
                this.videoElement = videoElement;
        }
        this.srvurl         = srvurl || location.protocol+"//"+window.location.hostname+":"+window.location.port;
        this.pc               = null;   

        this.mediaConstraints = { offerToReceiveAudio: true, offerToReceiveVideo: true };

        this.iceServers = null;
        this.earlyCandidates = [];
}

WebRtcStreamer.prototype._handleHttpErrors = function (response) {
    if (!response.ok) {
      throw Error(response.statusText);
    }
    return response;
}

/**
* Connect a WebRTC Stream to videoElement
* @param {string} videourl - id of WebRTC video stream
* @param {string} audiourl - id of WebRTC audio stream
* @param {string} options -options of WebRTC call
* @param {string} stream-local stream to send
*/
WebRtcStreamer.prototype.connect = function(videourl, audiourl, options, localstream) {
        this.disconnect();
       
        // getIceServers is not already received
        if (!this.iceServers) {
                console.log("Get IceServers");
               
                fetch(this.srvurl + "/api/getIceServers")
                        .then(this._handleHttpErrors)
                        .then( (response) => (response.json()) )
                        .then( (response) =>this.onReceiveGetIceServers(response, videourl, audiourl, options, localstream))
                        .catch( (error) => this.onError("getIceServers " + error ))
                               
        } else {
                this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream);
        }
}

/**
* Disconnect a WebRTC Stream and clear videoElement source
*/
WebRtcStreamer.prototype.disconnect = function() {               
        if (this.videoElement?.srcObject) {
                this.videoElement.srcObject.getTracks().forEach(track => {
                        track.stop()
                        this.videoElement.srcObject.removeTrack(track);
                });
        }
        if (this.pc) {
                fetch(this.srvurl + "/api/hangup?peerid=" + this.pc.peerid)
                        .then(this._handleHttpErrors)
                        .catch( (error) => this.onError("hangup " + error ))

               
                try {
                        this.pc.close();
                }
                catch (e) {
                        console.log ("Failure close peer connection:" + e);
                }
                this.pc = null;
        }
}   

/*
* GetIceServers callback
*/
WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl, audiourl, options, stream) {
        this.iceServers       = iceServers;
        this.pcConfig         = iceServers || {"iceServers": [] };
        try {            
                this.createPeerConnection();

                var callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl);
                if (audiourl) {
                        callurl += "&audiourl="+encodeURIComponent(audiourl);
                }
                if (options) {
                        callurl += "&options="+encodeURIComponent(options);
                }
               
                if (stream) {
                        this.pc.addStream(stream);
                }

                // clear early candidates
                this.earlyCandidates.length = 0;
               
                // create Offer
                this.pc.createOffer(this.mediaConstraints).then((sessionDescription) => {
                        console.log("Create offer:" + JSON.stringify(sessionDescription));
                       
                        this.pc.setLocalDescription(sessionDescription)
                                .then(() => {
                                        fetch(callurl, { method: "POST", body: JSON.stringify(sessionDescription) })
                                                .then(this._handleHttpErrors)
                                                .then( (response) => (response.json()) )
                                                .catch( (error) => this.onError("call " + error ))
                                                .then( (response) =>this.onReceiveCall(response) )
                                                .catch( (error) => this.onError("call " + error ))
                               
                                }, (error) => {
                                        console.log ("setLocalDescription error:" + JSON.stringify(error));
                                });
                       
                }, (error) => {
                        alert("Create offer error:" + JSON.stringify(error));
                });

        } catch (e) {
                this.disconnect();
                alert("connect error: " + e);
        }          
}


WebRtcStreamer.prototype.getIceCandidate = function() {
        fetch(this.srvurl + "/api/getIceCandidate?peerid=" + this.pc.peerid)
                .then(this._handleHttpErrors)
                .then( (response) => (response.json()) )
                .then( (response) =>this.onReceiveCandidate(response))
                .catch( (error) => this.onError("getIceCandidate " + error ))
}
                                       
/*
* create RTCPeerConnection
*/
WebRtcStreamer.prototype.createPeerConnection = function() {
        console.log("createPeerConnectionconfig: " + JSON.stringify(this.pcConfig));
        this.pc = new RTCPeerConnection(this.pcConfig);
        var pc = this.pc;
        pc.peerid = Math.random();               
       
        pc.onicecandidate = (evt) => this.onIceCandidate(evt);
        pc.onaddstream    = (evt) => this.onAddStream(evt);
        pc.oniceconnectionstatechange = (evt) => {
                console.log("oniceconnectionstatechangestate: " + pc.iceConnectionState);
                if (this.videoElement) {
                        if (pc.iceConnectionState === "connected") {
                                this.videoElement.style.opacity = "1.0";
                        }                       
                        else if (pc.iceConnectionState === "disconnected") {
                                this.videoElement.style.opacity = "0.25";
                        }                       
                        else if ( (pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed") ){
                                this.videoElement.style.opacity = "0.5";
                        } else if (pc.iceConnectionState === "new") {
                                this.getIceCandidate();
                        }
                }
        }
        pc.ondatachannel = function(evt) {
                console.log("remote datachannel created:"+JSON.stringify(evt));
               
                evt.channel.onopen = function () {
                        console.log("remote datachannel open");
                        this.send("remote channel openned");
                }
                evt.channel.onmessage = function (event) {
                        console.log("remote datachannel recv:"+JSON.stringify(event.data));
                }
        }
        pc.onicegatheringstatechange = function() {
                if (pc.iceGatheringState === "complete") {
                        const recvs = pc.getReceivers();
               
                        recvs.forEach((recv) => {
                          if (recv.track && recv.track.kind === "video") {
                                console.log("codecs:" + JSON.stringify(recv.getParameters().codecs))
                          }
                        });
                  }
        }

        try {
                var dataChannel = pc.createDataChannel("ClientDataChannel");
                dataChannel.onopen = function() {
                        console.log("local datachannel open");
                        this.send("local channel openned");
                }
                dataChannel.onmessage = function(evt) {
                        console.log("local datachannel recv:"+JSON.stringify(evt.data));
                }
        } catch (e) {
                console.log("Cannor create datachannel error: " + e);
        }       
       
        console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig) );
        return pc;
}


/*
* RTCPeerConnection IceCandidate callback
*/
WebRtcStreamer.prototype.onIceCandidate = function (event) {
        if (event.candidate) {
                if (this.pc.currentRemoteDescription){
                        this.addIceCandidate(this.pc.peerid, event.candidate);                                       
                } else {
                        this.earlyCandidates.push(event.candidate);
                }
        }
        else {
                console.log("End of candidates.");
        }
}


WebRtcStreamer.prototype.addIceCandidate = function(peerid, candidate) {
        fetch(this.srvurl + "/api/addIceCandidate?peerid="+peerid, { method: "POST", body: JSON.stringify(candidate) })
                .then(this._handleHttpErrors)
                .then( (response) => (response.json()) )
                .then( (response) =>{console.log("addIceCandidate ok:" + response)})
                .catch( (error) => this.onError("addIceCandidate " + error ))
}
                               
/*
* RTCPeerConnection AddTrack callback
*/
WebRtcStreamer.prototype.onAddStream = function(event) {
        console.log("Remote track added:" +JSON.stringify(event));
       
        this.videoElement.srcObject = event.stream;
        var promise = this.videoElement.play();
        if (promise !== undefined) {
          promise.catch((error) => {
                console.warn("error:"+error);
                this.videoElement.setAttribute("controls", true);
          });
        }
}
               
/*
* AJAX /call callback
*/
WebRtcStreamer.prototype.onReceiveCall = function(dataJson) {

        console.log("offer: " + JSON.stringify(dataJson));
        var descr = new RTCSessionDescription(dataJson);
        this.pc.setRemoteDescription(descr).then(() =>{
                        console.log ("setRemoteDescription ok");
                        while (this.earlyCandidates.length) {
                                var candidate = this.earlyCandidates.shift();
                                this.addIceCandidate(this.pc.peerid, candidate);                               
                        }
               
                        this.getIceCandidate()
                }
                , (error) => {
                        console.log ("setRemoteDescription error:" + JSON.stringify(error));
                });
}       

/*
* AJAX /getIceCandidate callback
*/
WebRtcStreamer.prototype.onReceiveCandidate = function(dataJson) {
        console.log("candidate: " + JSON.stringify(dataJson));
        if (dataJson) {
                for (var i=0; i<dataJson.length; i++) {
                        var candidate = new RTCIceCandidate(dataJson);
                       
                        console.log("Adding ICE candidate :" + JSON.stringify(candidate) );
                        this.pc.addIceCandidate(candidate).then( () =>      { console.log ("addIceCandidate OK"); }
                                , (error) => { console.log ("addIceCandidate error:" + JSON.stringify(error)); } );
                }
                this.pc.addIceCandidate();
        }
}


/*
* AJAX callback for Error
*/
WebRtcStreamer.prototype.onError = function(status) {
        console.log("onError:" + status);
}

return WebRtcStreamer;
})();

if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
        window.WebRtcStreamer = WebRtcStreamer;
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
        module.exports = WebRtcStreamer;
}
2.下载webrtc-streamer

资源在最上面
也可以去github上面下载:webrtc-streamer
下载完解压,打开文件夹,启动webrtc-streamer.exe
https://i-blog.csdnimg.cn/blog_migrate/f195951661a4ca5e70998d5ccbc7eb86.png
打开完会出现cmd一样的黑框框如下
https://i-blog.csdnimg.cn/blog_migrate/60316093c145da9e36bb140a4f915006.png
如果没有启动乐成可以在浏览器中输入http://127.0.0.1:8000/查看当地端口8000是否被其他应用程序占用,如果没有被占用打开窗口应该如下图所示(是可以看见自己的页面的)
https://i-blog.csdnimg.cn/blog_migrate/8680880d04ff3cd457c29e2a3418ce15.png
3.封装组件video.vue(名字随意)

代码如下(但是有需要留意的地方,请看下方)
<template>
<div id="video-contianer">
    <video
      class="video"
      ref="video"
      preload="auto"
      autoplay="autoplay"
      muted
      width="600"
      height="400"
    />
    <div
      class="mask"
      @click="handleClickVideo"
      :class="{ 'active-video-border': selectStatus }"
    ></div>
</div>
</template>

<script>
import WebRtcStreamer from '../../public/hk/webrtcstreamer'

export default {
name: 'videoCom',
props: {
    rtsp: {
      type: String,
      required: true,
    },
    isOn: {
      type: Boolean,
      default: false,
    },
    spareId: {
      type: Number,
    },
    selectStatus: {
      type: Boolean,
      default: false,
    },
},
data() {
    return {
      socket: null,
      result: null, // 返回值
      pic: null,
      webRtcServer: null,
      clickCount: 0, // 用来计数点击次数
    }
},
watch: {
    rtsp() {
      // do something
      console.log(this.rtsp)
      this.webRtcServer.disconnect()
      this.initVideo()
    },
},
destroyed() {
    this.webRtcServer.disconnect()
},
beforeCreate() {
    window.onbeforeunload = () => {
      this.webRtcServer.disconnect()
    }
},
created() {},
mounted() {
    this.initVideo()
},
methods: {
    initVideo() {
      try {
      //连接后端的IP地址和端口
      this.webRtcServer = new WebRtcStreamer(
          this.$refs.video,
          `http://192.168.1.102:8000`
      )
      //向后端发送rtsp地址
      this.webRtcServer.connect(this.rtsp)
      } catch (error) {
      console.log(error)
      }
    },
    /* 处理双击 单机 */
    dbClick() {
      this.clickCount++
      if (this.clickCount === 2) {
      this.btnFull() // 双击全屏
      this.clickCount = 0
      }
      setTimeout(() => {
      if (this.clickCount === 1) {
          this.clickCount = 0
      }
      }, 250)
    },
    /* 视频全屏 */
    btnFull() {
      const elVideo = this.$refs.video
      if (elVideo.webkitRequestFullScreen) {
      elVideo.webkitRequestFullScreen()
      } else if (elVideo.mozRequestFullScreen) {
      elVideo.mozRequestFullScreen()
      } else if (elVideo.requestFullscreen) {
      elVideo.requestFullscreen()
      }
    },
    /*
    ison用来判断是否需要更换视频流
    dbclick函数用来双击放大全屏方法
    */
    handleClickVideo() {
      if (this.isOn) {
      this.$emit('selectVideo', this.spareId)
      this.dbClick()
      } else {
      this.btnFull()
      }
    },
},
}
</script>

<style scoped lang="scss">
.active-video-border {
border: 2px salmon solid;
}
#video-contianer {
position: relative;
// width: 100%;
// height: 100%;
.video {
    // width: 100%;
    // height: 100%;
    // object-fit: cover;
}
.mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}
}
</style>
这里要留意两个地方
第一个是https://i-blog.csdnimg.cn/blog_migrate/aab4e966784e5d21cca7d61b852d90c9.png
第二个是
https://i-blog.csdnimg.cn/blog_migrate/0bf7bf05d54375fbb727aa2f49b997e5.png
不会查看本机端口的看这里(起首使用 Win + R打开运行 输入cmd)
https://i-blog.csdnimg.cn/blog_migrate/21482fcc21f4ee916934b3872cc30e9f.png
https://i-blog.csdnimg.cn/blog_migrate/d2c467f5e62b860d695c0697073ce51a.png
https://i-blog.csdnimg.cn/blog_migrate/6540f7fce024ff2b99710a9e7f9e6cf8.png
4.使用video封装组件播放rtsp视频流

起首我们在要使用video封装组件的地方引入并且注册video组件
https://i-blog.csdnimg.cn/blog_migrate/e6d01fcf77165e19b68a9b9a6e7f561e.png
之后在页面中使用video组件 并且定义了两个变量将rtsp流传给封装的video组件
https://i-blog.csdnimg.cn/blog_migrate/5bb5635f0360ddaf2f435f57ce0ff458.pnghttps://i-blog.csdnimg.cn/blog_migrate/243267b7403dea27e1f9e9b8a94836a1.png
效果图如下
https://img-blog.csdnimg.cn/direct/cd650fa1cf8946d0a4e32202896378ce.gif
5.使用此种方法播放的时候会默认带声音播放,如何取消(看这里)

https://i-blog.csdnimg.cn/blog_migrate/a36120e5dd352218f0fb1cfd2c80b5e6.png
之后声明一个方法
https://i-blog.csdnimg.cn/blog_migrate/0bed16bba4eefc8c327cfc6aa4b8f6b5.png
然后在created里面调用就静音了
https://i-blog.csdnimg.cn/blog_migrate/f2825986f36f333516dc746622acd829.png
四.其他功能

1.截图功能

rtsp流引入海康威视摄像头——截图功能-CSDN博客
到此为止海康摄像头引入vue的方法就完美完结了
如果同砚们有什么好的意见或者有什么题目可以私信我
最后祝各人事业蒸蒸日上,心想事成!
https://i-blog.csdnimg.cn/blog_migrate/365b21f3348e90694ac6b12b39eff00b.gif

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: vue2使用rtsp视频流接入海康威视摄像头(纯前端)