当前位置:   article > 正文

vue2-rtsp视频流播放

vue2-rtsp视频流播放

 <Videos :rtsp="item.rtspUrl"></Videos>
  1. swiperList: [
  2. {
  3. id: 1,
  4. title: "",
  5. rtspUrl: 'rtsp://rtspstream:cb50b0754546ed4cbfe327e6d9f34d93@zephyr.rtsp.stream/movie',
  6. },
  7. {
  8. id: 2,
  9. title: "",
  10. rtspUrl: 'rtsp://rtspstream:cb50b0754546ed4cbfe327e6d9f34d93@zephyr.rtsp.stream/movie',
  11. },
  12. {
  13. id: 3,
  14. title: "",
  15. rtspUrl: 'rtsp://rtspstream:cb50b0754546ed4cbfe327e6d9f34d93@zephyr.rtsp.stream/movie',
  16. },
  17. ],
import Videos from "./webrtc/webrtcstreamer.vue";

 

  1. <template>
  2. <div id="video-contianer">
  3. <video
  4. class="video"
  5. ref="video"
  6. preload="auto"
  7. autoplay="autoplay"
  8. width="100%"
  9. height="100%"
  10. muted="muted"
  11. />
  12. <!-- muted -->
  13. <div
  14. class="mask"
  15. @click="handleClickVideo"
  16. :class="{ 'active-video-border': selectStatus }"
  17. ></div>
  18. </div>
  19. </template>
  20. <script>
  21. import WebRtcStreamer from "./webrtcstreamer11.js";
  22. export default {
  23. name: "videoCom",
  24. props: {
  25. rtsp: {
  26. type: String,
  27. required: true,
  28. },
  29. isOn: {
  30. type: Boolean,
  31. default: false,
  32. },
  33. spareId: {
  34. type: Number,
  35. },
  36. selectStatus: {
  37. type: Boolean,
  38. default: false,
  39. },
  40. },
  41. data() {
  42. return {
  43. socket: null,
  44. result: null, // 返回值
  45. pic: null,
  46. webRtcServer: null,
  47. clickCount: 0, // 用来计数点击次数
  48. //rtsp:'rtsp://rtspstream:cb50b0754546ed4cbfe327e6d9f34d93@zephyr.rtsp.stream/movie'
  49. };
  50. },
  51. watch: {
  52. rtsp() {
  53. // do something
  54. console.log(this.rtsp);
  55. this.webRtcServer.disconnect();
  56. //this.initVideo();
  57. },
  58. },
  59. destroyed() {
  60. this.webRtcServer.disconnect();
  61. },
  62. beforeCreate() {
  63. window.onbeforeunload = () => {
  64. this.webRtcServer.disconnect();
  65. };
  66. },
  67. created() {
  68. this.$nextTick(() => {
  69. this.init();
  70. });
  71. },
  72. mounted() {
  73. this.initVideo();
  74. },
  75. methods: {
  76. initVideo() {
  77. try {
  78. //连接后端的IP地址和端口
  79. this.webRtcServer = new WebRtcStreamer(
  80. this.$refs.video,
  81. `http://127.0.0.1:8000`
  82. // `http://192.168.18.104:8080`
  83. );
  84. //向后端发送rtsp地址
  85. this.webRtcServer.connect(this.rtsp);
  86. } catch (error) {
  87. console.log(error);
  88. }
  89. },
  90. init() {
  91. var video = document.querySelector("video");
  92. video.addEventListener("click", function () {
  93. video.muted = true;
  94. video.autoplay = true;
  95. });
  96. },
  97. /* 处理双击 单机 */
  98. dbClick() {
  99. this.clickCount++;
  100. if (this.clickCount === 2) {
  101. this.btnFull(); // 双击全屏
  102. this.clickCount = 0;
  103. }
  104. setTimeout(() => {
  105. if (this.clickCount === 1) {
  106. this.clickCount = 0;
  107. }
  108. }, 250);
  109. },
  110. /* 视频全屏 */
  111. btnFull() {
  112. const elVideo = this.$refs.video;
  113. if (elVideo.webkitRequestFullScreen) {
  114. elVideo.webkitRequestFullScreen();
  115. } else if (elVideo.mozRequestFullScreen) {
  116. elVideo.mozRequestFullScreen();
  117. } else if (elVideo.requestFullscreen) {
  118. elVideo.requestFullscreen();
  119. }
  120. },
  121. /*
  122. ison用来判断是否需要更换视频流
  123. dbclick函数用来双击放大全屏方法
  124. */
  125. handleClickVideo() {
  126. if (this.isOn) {
  127. this.$emit("selectVideo", this.spareId);
  128. this.dbClick();
  129. } else {
  130. this.btnFull();
  131. }
  132. },
  133. },
  134. };
  135. </script>
  136. <style scoped lang="scss">
  137. .active-video-border {
  138. border: 2px salmon solid;
  139. }
  140. #video-contianer {
  141. position: relative;
  142. // width: 100%;
  143. // height: 100%;
  144. .video {
  145. // width: 100%;
  146. // height: 100%;
  147. // object-fit: cover;
  148. }
  149. .mask {
  150. position: absolute;
  151. top: 0;
  152. left: 0;
  153. width: 100%;
  154. height: 100%;
  155. cursor: pointer;
  156. }
  157. }
  158. </style>

 

  1. var WebRtcStreamer = (function() {
  2. /**
  3. * Interface with WebRTC-streamer API
  4. * @constructor
  5. * @param {string} videoElement - id of the video element tag
  6. * @param {string} srvurl - url of webrtc-streamer (default is current location)
  7. */
  8. var WebRtcStreamer = function WebRtcStreamer (videoElement, srvurl) {
  9. if (typeof videoElement === "string") {
  10. this.videoElement = document.getElementById(videoElement);
  11. } else {
  12. this.videoElement = videoElement;
  13. }
  14. this.srvurl = srvurl || location.protocol+"//"+window.location.hostname+":"+window.location.port;
  15. this.pc = null;
  16. this.mediaConstraints = { offerToReceiveAudio: true, offerToReceiveVideo: true };
  17. this.iceServers = null;
  18. this.earlyCandidates = [];
  19. }
  20. WebRtcStreamer.prototype._handleHttpErrors = function (response) {
  21. if (!response.ok) {
  22. throw Error(response.statusText);
  23. }
  24. return response;
  25. }
  26. /**
  27. * Connect a WebRTC Stream to videoElement
  28. * @param {string} videourl - id of WebRTC video stream
  29. * @param {string} audiourl - id of WebRTC audio stream
  30. * @param {string} options - options of WebRTC call
  31. * @param {string} stream - local stream to send
  32. */
  33. WebRtcStreamer.prototype.connect = function(videourl, audiourl, options, localstream) {
  34. this.disconnect();
  35. // getIceServers is not already received
  36. if (!this.iceServers) {
  37. console.log("Get IceServers");
  38. fetch(this.srvurl + "/api/getIceServers")
  39. .then(this._handleHttpErrors)
  40. .then( (response) => (response.json()) )
  41. .then( (response) => this.onReceiveGetIceServers(response, videourl, audiourl, options, localstream))
  42. .catch( (error) => this.onError("getIceServers " + error ))
  43. } else {
  44. this.onReceiveGetIceServers(this.iceServers, videourl, audiourl, options, localstream);
  45. }
  46. }
  47. /**
  48. * Disconnect a WebRTC Stream and clear videoElement source
  49. */
  50. WebRtcStreamer.prototype.disconnect = function() {
  51. if (this.videoElement?.srcObject) {
  52. this.videoElement.srcObject.getTracks().forEach(track => {
  53. track.stop()
  54. this.videoElement.srcObject.removeTrack(track);
  55. });
  56. }
  57. if (this.pc) {
  58. fetch(this.srvurl + "/api/hangup?peerid=" + this.pc.peerid)
  59. .then(this._handleHttpErrors)
  60. .catch( (error) => this.onError("hangup " + error ))
  61. try {
  62. this.pc.close();
  63. }
  64. catch (e) {
  65. console.log ("Failure close peer connection:" + e);
  66. }
  67. this.pc = null;
  68. }
  69. }
  70. /*
  71. * GetIceServers callback
  72. */
  73. WebRtcStreamer.prototype.onReceiveGetIceServers = function(iceServers, videourl, audiourl, options, stream) {
  74. this.iceServers = iceServers;
  75. this.pcConfig = iceServers || {"iceServers": [] };
  76. try {
  77. this.createPeerConnection();
  78. var callurl = this.srvurl + "/api/call?peerid=" + this.pc.peerid + "&url=" + encodeURIComponent(videourl);
  79. if (audiourl) {
  80. callurl += "&audiourl="+encodeURIComponent(audiourl);
  81. }
  82. if (options) {
  83. callurl += "&options="+encodeURIComponent(options);
  84. }
  85. if (stream) {
  86. this.pc.addStream(stream);
  87. }
  88. // clear early candidates
  89. this.earlyCandidates.length = 0;
  90. // create Offer
  91. this.pc.createOffer(this.mediaConstraints).then((sessionDescription) => {
  92. console.log("Create offer:" + JSON.stringify(sessionDescription));
  93. this.pc.setLocalDescription(sessionDescription)
  94. .then(() => {
  95. fetch(callurl, { method: "POST", body: JSON.stringify(sessionDescription) })
  96. .then(this._handleHttpErrors)
  97. .then( (response) => (response.json()) )
  98. .catch( (error) => this.onError("call " + error ))
  99. .then( (response) => this.onReceiveCall(response) )
  100. .catch( (error) => this.onError("call " + error ))
  101. }, (error) => {
  102. console.log ("setLocalDescription error:" + JSON.stringify(error));
  103. });
  104. }, (error) => {
  105. alert("Create offer error:" + JSON.stringify(error));
  106. });
  107. } catch (e) {
  108. this.disconnect();
  109. alert("connect error: " + e);
  110. }
  111. }
  112. WebRtcStreamer.prototype.getIceCandidate = function() {
  113. fetch(this.srvurl + "/api/getIceCandidate?peerid=" + this.pc.peerid)
  114. .then(this._handleHttpErrors)
  115. .then( (response) => (response.json()) )
  116. .then( (response) => this.onReceiveCandidate(response))
  117. .catch( (error) => this.onError("getIceCandidate " + error ))
  118. }
  119. /*
  120. * create RTCPeerConnection
  121. */
  122. WebRtcStreamer.prototype.createPeerConnection = function() {
  123. console.log("createPeerConnection config: " + JSON.stringify(this.pcConfig));
  124. this.pc = new RTCPeerConnection(this.pcConfig);
  125. var pc = this.pc;
  126. pc.peerid = Math.random();
  127. pc.onicecandidate = (evt) => this.onIceCandidate(evt);
  128. pc.onaddstream = (evt) => this.onAddStream(evt);
  129. pc.oniceconnectionstatechange = (evt) => {
  130. console.log("oniceconnectionstatechange state: " + pc.iceConnectionState);
  131. if (this.videoElement) {
  132. if (pc.iceConnectionState === "connected") {
  133. this.videoElement.style.opacity = "1.0";
  134. }
  135. else if (pc.iceConnectionState === "disconnected") {
  136. this.videoElement.style.opacity = "0.25";
  137. }
  138. else if ( (pc.iceConnectionState === "failed") || (pc.iceConnectionState === "closed") ) {
  139. this.videoElement.style.opacity = "0.5";
  140. } else if (pc.iceConnectionState === "new") {
  141. this.getIceCandidate();
  142. }
  143. }
  144. }
  145. pc.ondatachannel = function(evt) {
  146. console.log("remote datachannel created:"+JSON.stringify(evt));
  147. evt.channel.onopen = function () {
  148. console.log("remote datachannel open");
  149. this.send("remote channel openned");
  150. }
  151. evt.channel.onmessage = function (event) {
  152. console.log("remote datachannel recv:"+JSON.stringify(event.data));
  153. }
  154. }
  155. pc.onicegatheringstatechange = function() {
  156. if (pc.iceGatheringState === "complete") {
  157. const recvs = pc.getReceivers();
  158. recvs.forEach((recv) => {
  159. if (recv.track && recv.track.kind === "video") {
  160. console.log("codecs:" + JSON.stringify(recv.getParameters().codecs))
  161. }
  162. });
  163. }
  164. }
  165. try {
  166. var dataChannel = pc.createDataChannel("ClientDataChannel");
  167. dataChannel.onopen = function() {
  168. console.log("local datachannel open");
  169. this.send("local channel openned");
  170. }
  171. dataChannel.onmessage = function(evt) {
  172. console.log("local datachannel recv:"+JSON.stringify(evt.data));
  173. }
  174. } catch (e) {
  175. console.log("Cannor create datachannel error: " + e);
  176. }
  177. console.log("Created RTCPeerConnnection with config: " + JSON.stringify(this.pcConfig) );
  178. return pc;
  179. }
  180. /*
  181. * RTCPeerConnection IceCandidate callback
  182. */
  183. WebRtcStreamer.prototype.onIceCandidate = function (event) {
  184. if (event.candidate) {
  185. if (this.pc.currentRemoteDescription) {
  186. this.addIceCandidate(this.pc.peerid, event.candidate);
  187. } else {
  188. this.earlyCandidates.push(event.candidate);
  189. }
  190. }
  191. else {
  192. console.log("End of candidates.");
  193. }
  194. }
  195. WebRtcStreamer.prototype.addIceCandidate = function(peerid, candidate) {
  196. fetch(this.srvurl + "/api/addIceCandidate?peerid="+peerid, { method: "POST", body: JSON.stringify(candidate) })
  197. .then(this._handleHttpErrors)
  198. .then( (response) => (response.json()) )
  199. .then( (response) => {console.log("addIceCandidate ok:" + response)})
  200. .catch( (error) => this.onError("addIceCandidate " + error ))
  201. }
  202. /*
  203. * RTCPeerConnection AddTrack callback
  204. */
  205. WebRtcStreamer.prototype.onAddStream = function(event) {
  206. console.log("Remote track added:" + JSON.stringify(event));
  207. this.videoElement.srcObject = event.stream;
  208. var promise = this.videoElement.play();
  209. if (promise !== undefined) {
  210. promise.catch((error) => {
  211. console.warn("error:"+error);
  212. this.videoElement.setAttribute("controls", true);
  213. });
  214. }
  215. }
  216. /*
  217. * AJAX /call callback
  218. */
  219. WebRtcStreamer.prototype.onReceiveCall = function(dataJson) {
  220. console.log("offer: " + JSON.stringify(dataJson));
  221. var descr = new RTCSessionDescription(dataJson);
  222. this.pc.setRemoteDescription(descr).then(() => {
  223. console.log ("setRemoteDescription ok");
  224. while (this.earlyCandidates.length) {
  225. var candidate = this.earlyCandidates.shift();
  226. this.addIceCandidate(this.pc.peerid, candidate);
  227. }
  228. this.getIceCandidate()
  229. }
  230. , (error) => {
  231. console.log ("setRemoteDescription error:" + JSON.stringify(error));
  232. });
  233. }
  234. /*
  235. * AJAX /getIceCandidate callback
  236. */
  237. WebRtcStreamer.prototype.onReceiveCandidate = function(dataJson) {
  238. console.log("candidate: " + JSON.stringify(dataJson));
  239. if (dataJson) {
  240. for (var i=0; i<dataJson.length; i++) {
  241. var candidate = new RTCIceCandidate(dataJson[i]);
  242. console.log("Adding ICE candidate :" + JSON.stringify(candidate) );
  243. this.pc.addIceCandidate(candidate).then( () => { console.log ("addIceCandidate OK"); }
  244. , (error) => { console.log ("addIceCandidate error:" + JSON.stringify(error)); } );
  245. }
  246. this.pc.addIceCandidate();
  247. }
  248. }
  249. /*
  250. * AJAX callback for Error
  251. */
  252. WebRtcStreamer.prototype.onError = function(status) {
  253. console.log("onError:" + status);
  254. }
  255. return WebRtcStreamer;
  256. })();
  257. if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
  258. window.WebRtcStreamer = WebRtcStreamer;
  259. }
  260. if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
  261. module.exports = WebRtcStreamer;
  262. }

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/449335
推荐阅读
相关标签
  

闽ICP备14008679号