企业官网建站 小程序开发 云逸学习园地
技术文章 许愿墙
技术文章 PHP学习 Javascript学习 DIV+CSS学习 uniapp学习 HTML学习 微信小程序、公众号知识点 VUE知识学习
您所在的位置>云逸小栈>VUE知识学习>正文

vue播放rtsp流的操作方法,webrtc方案

2025-01-22 16:01:52 浏览 1067

vue播放rtsp流的视频,需要准备两个东西,一个是webrtc服务和对应的js库文件

1、js库文件 (新建个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("createPeerConnection  config: " + 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("oniceconnectionstatechange  state: " + 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[i]);

             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;
})();

export default WebRtcStreamer

js的调用方法,需要创建一个视频播放的组件video.vue并且引入上面的库文件

<template>
    <div id="video-contianer">
       <video
          class="video"
          ref="video"
          preload="auto"
          autoplay="autoplay"
          muted
          :width="width"
          :height="height"
       />
       <div
          class="mask"
          @click="handleClickVideo"
          :class="{ 'active-video-border': selectStatus }"></div>
    </div>
</template>

<script>
import WebRtcStreamer from '@/assets/js/webrtcstreamer'

export default {
    name: 'videoCom',
    props: {
       rtsp: {
          type: String,
          required: true,
       },
       isOn: {
          type: Boolean,
          default: false,
       },
       spareId: {
          type: Number,
       },
       selectStatus: {
          type: Boolean,
          default: false,
       },
       width:{
          type:String
       },
       height:{
          type:String
       }
    },
    data() {
       return {
          socket: null,
          result: null, // 返回值
          pic: null,
          webRtcServer: null,
          clickCount: 0, // 用来计数点击次数
       }
    },
    watch: {
       rtsp() {
          this.webRtcServer.disconnect()
          this.initVideo()
       },
    },
    destroyed() {
       this.webRtcServer.disconnect()
    },
    beforeCreate() {
       window.onbeforeunload = () => {
          this.webRtcServer.disconnect()
       }
    },
    created() {},
    mounted() {
       this.initVideo()
       this.controlVolime() //静音
    },
    methods: {
       //控制
       yeshi(str) {
          let video = this.$refs.video;
          video.style.filter = str;
       },
       zoomChild(scale) {
          let video = this.$refs.video;
          video.style.transform = `scale(${scale})`;
       },
       initVideo() {
          try {
             //连接后端的IP地址和端口
             this.webRtcServer = new WebRtcStreamer(
                this.$refs.video,
                // `http://192.3.3.30:8000`
                `http://192.168.20.8:8000`
             )
             //向后端发送rtsp地址
             // this.webRtcServer.connect(this.rtsp)
             this.webRtcServer.connect('rtsp://176.139.87.16/axis-media/media.amp')
          } 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()
          }
       },
       controlVolime() {
          this.$nextTick(()=>{
             this.$refs.video.volume = 0
          })
       }
    },
}
</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>

最后 是webrtc

webrtc-streamer.zip


开启webrtc服务,把本机ip地址+端口 写进video组件内即可

例如:http://192.168.20.8:8000


网站开发者电话

18066742510

个人微信号
个人公众号
个人小程序
个人抖音