UNPKG

rrweb

Version:
3 lines (2 loc) 19.8 kB
var rrwebCanvasWebRTCReplay=function(g){"use strict";/*! simple-peer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */function f(h){const e=new Uint8Array(h);for(let t=0;t<h;t++)e[t]=Math.random()*256|0;return e}function p(){if(typeof globalThis>"u")return null;const h={RTCPeerConnection:globalThis.RTCPeerConnection||globalThis.mozRTCPeerConnection||globalThis.webkitRTCPeerConnection,RTCSessionDescription:globalThis.RTCSessionDescription||globalThis.mozRTCSessionDescription||globalThis.webkitRTCSessionDescription,RTCIceCandidate:globalThis.RTCIceCandidate||globalThis.mozRTCIceCandidate||globalThis.webkitRTCIceCandidate};return h.RTCPeerConnection?h:null}function r(h,e){return Object.defineProperty(h,"code",{value:e,enumerable:!0,configurable:!0}),h}function m(h){return h.replace(/a=ice-options:trickle\s\n/g,"")}function y(h){console.warn(h)}class _{constructor(e={}){if(this._map=new Map,this._id=f(4).toString("hex").slice(0,7),this._doDebug=e.debug,this._debug("new peer %o",e),this.channelName=e.initiator?e.channelName||f(20).toString("hex"):null,this.initiator=e.initiator||!1,this.channelConfig=e.channelConfig||_.channelConfig,this.channelNegotiated=this.channelConfig.negotiated,this.config=Object.assign({},_.config,e.config),this.offerOptions=e.offerOptions||{},this.answerOptions=e.answerOptions||{},this.sdpTransform=e.sdpTransform||(t=>t),this.streams=e.streams||(e.stream?[e.stream]:[]),this.trickle=e.trickle!==void 0?e.trickle:!0,this.allowHalfTrickle=e.allowHalfTrickle!==void 0?e.allowHalfTrickle:!1,this.iceCompleteTimeout=e.iceCompleteTimeout||5e3,this.destroyed=!1,this.destroying=!1,this._connected=!1,this.remoteAddress=void 0,this.remoteFamily=void 0,this.remotePort=void 0,this.localAddress=void 0,this.localFamily=void 0,this.localPort=void 0,this._wrtc=e.wrtc&&typeof e.wrtc=="object"?e.wrtc:p(),!this._wrtc)throw r(typeof window>"u"?new Error("No WebRTC support: Specify `opts.wrtc` option in this environment"):new Error("No WebRTC support: Not a supported browser"),"ERR_WEBRTC_SUPPORT");this._pcReady=!1,this._channelReady=!1,this._iceComplete=!1,this._iceCompleteTimer=null,this._channel=null,this._pendingCandidates=[],this._isNegotiating=!1,this._firstNegotiation=!0,this._batchedNegotiation=!1,this._queuedNegotiation=!1,this._sendersAwaitingStable=[],this._senderMap=new Map,this._closingInterval=null,this._remoteTracks=[],this._remoteStreams=[],this._chunk=null,this._cb=null,this._interval=null;try{this._pc=new this._wrtc.RTCPeerConnection(this.config)}catch(t){this.destroy(r(t,"ERR_PC_CONSTRUCTOR"));return}this._isReactNativeWebrtc=typeof this._pc._peerConnectionId=="number",this._pc.oniceconnectionstatechange=()=>{this._onIceStateChange()},this._pc.onicegatheringstatechange=()=>{this._onIceStateChange()},this._pc.onconnectionstatechange=()=>{this._onConnectionStateChange()},this._pc.onsignalingstatechange=()=>{this._onSignalingStateChange()},this._pc.onicecandidate=t=>{this._onIceCandidate(t)},typeof this._pc.peerIdentity=="object"&&this._pc.peerIdentity.catch(t=>{this.destroy(r(t,"ERR_PC_PEER_IDENTITY"))}),this.initiator||this.channelNegotiated?this._setupData({channel:this._pc.createDataChannel(this.channelName,this.channelConfig)}):this._pc.ondatachannel=t=>{this._setupData(t)},this.streams&&this.streams.forEach(t=>{this.addStream(t)}),this._pc.ontrack=t=>{this._onTrack(t)},this._debug("initial negotiation"),this._needsNegotiation()}get bufferSize(){return this._channel&&this._channel.bufferedAmount||0}get connected(){return this._connected&&this._channel.readyState==="open"}address(){return{port:this.localPort,family:this.localFamily,address:this.localAddress}}signal(e){if(!this.destroying){if(this.destroyed)throw r(new Error("cannot signal after peer is destroyed"),"ERR_DESTROYED");if(typeof e=="string")try{e=JSON.parse(e)}catch{e={}}this._debug("signal()"),e.renegotiate&&this.initiator&&(this._debug("got request to renegotiate"),this._needsNegotiation()),e.transceiverRequest&&this.initiator&&(this._debug("got request for transceiver"),this.addTransceiver(e.transceiverRequest.kind,e.transceiverRequest.init)),e.candidate&&(this._pc.remoteDescription&&this._pc.remoteDescription.type?this._addIceCandidate(e.candidate):this._pendingCandidates.push(e.candidate)),e.sdp&&this._pc.setRemoteDescription(new this._wrtc.RTCSessionDescription(e)).then(()=>{this.destroyed||(this._pendingCandidates.forEach(t=>{this._addIceCandidate(t)}),this._pendingCandidates=[],this._pc.remoteDescription.type==="offer"&&this._createAnswer())}).catch(t=>{this.destroy(r(t,"ERR_SET_REMOTE_DESCRIPTION"))}),!e.sdp&&!e.candidate&&!e.renegotiate&&!e.transceiverRequest&&this.destroy(r(new Error("signal() called with invalid signal data"),"ERR_SIGNALING"))}}_addIceCandidate(e){const t=new this._wrtc.RTCIceCandidate(e);this._pc.addIceCandidate(t).catch(i=>{!t.address||t.address.endsWith(".local")?y("Ignoring unsupported ICE candidate."):this.destroy(r(i,"ERR_ADD_ICE_CANDIDATE"))})}send(e){if(!this.destroying){if(this.destroyed)throw r(new Error("cannot send after peer is destroyed"),"ERR_DESTROYED");this._channel.send(e)}}addTransceiver(e,t){if(!this.destroying){if(this.destroyed)throw r(new Error("cannot addTransceiver after peer is destroyed"),"ERR_DESTROYED");if(this._debug("addTransceiver()"),this.initiator)try{this._pc.addTransceiver(e,t),this._needsNegotiation()}catch(i){this.destroy(r(i,"ERR_ADD_TRANSCEIVER"))}else this.emit("signal",{type:"transceiverRequest",transceiverRequest:{kind:e,init:t}})}}addStream(e){if(!this.destroying){if(this.destroyed)throw r(new Error("cannot addStream after peer is destroyed"),"ERR_DESTROYED");this._debug("addStream()"),e.getTracks().forEach(t=>{this.addTrack(t,e)})}}addTrack(e,t){if(this.destroying)return;if(this.destroyed)throw r(new Error("cannot addTrack after peer is destroyed"),"ERR_DESTROYED");this._debug("addTrack()");const i=this._senderMap.get(e)||new Map;let s=i.get(t);if(!s)s=this._pc.addTrack(e,t),i.set(t,s),this._senderMap.set(e,i),this._needsNegotiation();else throw s.removed?r(new Error("Track has been removed. You should enable/disable tracks that you want to re-add."),"ERR_SENDER_REMOVED"):r(new Error("Track has already been added to that stream."),"ERR_SENDER_ALREADY_ADDED")}replaceTrack(e,t,i){if(this.destroying)return;if(this.destroyed)throw r(new Error("cannot replaceTrack after peer is destroyed"),"ERR_DESTROYED");this._debug("replaceTrack()");const s=this._senderMap.get(e),a=s?s.get(i):null;if(!a)throw r(new Error("Cannot replace track that was never added."),"ERR_TRACK_NOT_ADDED");t&&this._senderMap.set(t,s),a.replaceTrack!=null?a.replaceTrack(t):this.destroy(r(new Error("replaceTrack is not supported in this browser"),"ERR_UNSUPPORTED_REPLACETRACK"))}removeTrack(e,t){if(this.destroying)return;if(this.destroyed)throw r(new Error("cannot removeTrack after peer is destroyed"),"ERR_DESTROYED");this._debug("removeSender()");const i=this._senderMap.get(e),s=i?i.get(t):null;if(!s)throw r(new Error("Cannot remove track that was never added."),"ERR_TRACK_NOT_ADDED");try{s.removed=!0,this._pc.removeTrack(s)}catch(a){a.name==="NS_ERROR_UNEXPECTED"?this._sendersAwaitingStable.push(s):this.destroy(r(a,"ERR_REMOVE_TRACK"))}this._needsNegotiation()}removeStream(e){if(!this.destroying){if(this.destroyed)throw r(new Error("cannot removeStream after peer is destroyed"),"ERR_DESTROYED");this._debug("removeSenders()"),e.getTracks().forEach(t=>{this.removeTrack(t,e)})}}_needsNegotiation(){this._debug("_needsNegotiation"),!this._batchedNegotiation&&(this._batchedNegotiation=!0,queueMicrotask(()=>{this._batchedNegotiation=!1,this.initiator||!this._firstNegotiation?(this._debug("starting batched negotiation"),this.negotiate()):this._debug("non-initiator initial negotiation request discarded"),this._firstNegotiation=!1}))}negotiate(){if(!this.destroying){if(this.destroyed)throw r(new Error("cannot negotiate after peer is destroyed"),"ERR_DESTROYED");this.initiator?this._isNegotiating?(this._queuedNegotiation=!0,this._debug("already negotiating, queueing")):(this._debug("start negotiation"),setTimeout(()=>{this._createOffer()},0)):this._isNegotiating?(this._queuedNegotiation=!0,this._debug("already negotiating, queueing")):(this._debug("requesting negotiation from initiator"),this.emit("signal",{type:"renegotiate",renegotiate:!0})),this._isNegotiating=!0}}destroy(e){this.destroyed||this.destroying||(this.destroying=!0,this._debug("destroying (error: %s)",e&&(e.message||e)),queueMicrotask(()=>{if(this.destroyed=!0,this.destroying=!1,this._debug("destroy (error: %s)",e&&(e.message||e)),this._connected=!1,this._pcReady=!1,this._channelReady=!1,this._remoteTracks=null,this._remoteStreams=null,this._senderMap=null,clearInterval(this._closingInterval),this._closingInterval=null,clearInterval(this._interval),this._interval=null,this._chunk=null,this._cb=null,this._channel){try{this._channel.close()}catch{}this._channel.onmessage=null,this._channel.onopen=null,this._channel.onclose=null,this._channel.onerror=null}if(this._pc){try{this._pc.close()}catch{}this._pc.oniceconnectionstatechange=null,this._pc.onicegatheringstatechange=null,this._pc.onsignalingstatechange=null,this._pc.onicecandidate=null,this._pc.ontrack=null,this._pc.ondatachannel=null}this._pc=null,this._channel=null,e&&this.emit("error",e),this.emit("close")}))}_setupData(e){if(!e.channel)return this.destroy(r(new Error("Data channel event is missing `channel` property"),"ERR_DATA_CHANNEL"));this._channel=e.channel,this._channel.binaryType="arraybuffer",typeof this._channel.bufferedAmountLowThreshold=="number"&&(this._channel.bufferedAmountLowThreshold=65536),this.channelName=this._channel.label,this._channel.onmessage=i=>{this._onChannelMessage(i)},this._channel.onbufferedamountlow=()=>{this._onChannelBufferedAmountLow()},this._channel.onopen=()=>{this._onChannelOpen()},this._channel.onclose=()=>{this._onChannelClose()},this._channel.onerror=i=>{this.destroy(r(i,"ERR_DATA_CHANNEL"))};let t=!1;this._closingInterval=setInterval(()=>{this._channel&&this._channel.readyState==="closing"?(t&&this._onChannelClose(),t=!0):t=!1},5e3)}_startIceCompleteTimeout(){this.destroyed||this._iceCompleteTimer||(this._debug("started iceComplete timeout"),this._iceCompleteTimer=setTimeout(()=>{this._iceComplete||(this._iceComplete=!0,this._debug("iceComplete timeout completed"),this.emit("iceTimeout"),this.emit("_iceComplete"))},this.iceCompleteTimeout))}_createOffer(){this.destroyed||this._pc.createOffer(this.offerOptions).then(e=>{if(this.destroyed)return;!this.trickle&&!this.allowHalfTrickle&&(e.sdp=m(e.sdp)),e.sdp=this.sdpTransform(e.sdp);const t=()=>{if(this.destroyed)return;const a=this._pc.localDescription||e;this._debug("signal"),this.emit("signal",{type:a.type,sdp:a.sdp})},i=()=>{this._debug("createOffer success"),!this.destroyed&&(this.trickle||this._iceComplete?t():this.once("_iceComplete",t))},s=a=>{this.destroy(r(a,"ERR_SET_LOCAL_DESCRIPTION"))};this._pc.setLocalDescription(e).then(i).catch(s)}).catch(e=>{this.destroy(r(e,"ERR_CREATE_OFFER"))})}_requestMissingTransceivers(){this._pc.getTransceivers&&this._pc.getTransceivers().forEach(e=>{!e.mid&&e.sender.track&&!e.requested&&(e.requested=!0,this.addTransceiver(e.sender.track.kind))})}_createAnswer(){this.destroyed||this._pc.createAnswer(this.answerOptions).then(e=>{if(this.destroyed)return;!this.trickle&&!this.allowHalfTrickle&&(e.sdp=m(e.sdp)),e.sdp=this.sdpTransform(e.sdp);const t=()=>{if(this.destroyed)return;const a=this._pc.localDescription||e;this._debug("signal"),this.emit("signal",{type:a.type,sdp:a.sdp}),this.initiator||this._requestMissingTransceivers()},i=()=>{this.destroyed||(this.trickle||this._iceComplete?t():this.once("_iceComplete",t))},s=a=>{this.destroy(r(a,"ERR_SET_LOCAL_DESCRIPTION"))};this._pc.setLocalDescription(e).then(i).catch(s)}).catch(e=>{this.destroy(r(e,"ERR_CREATE_ANSWER"))})}_onConnectionStateChange(){this.destroyed||this._pc.connectionState==="failed"&&this.destroy(r(new Error("Connection failed."),"ERR_CONNECTION_FAILURE"))}_onIceStateChange(){if(this.destroyed)return;const e=this._pc.iceConnectionState,t=this._pc.iceGatheringState;this._debug("iceStateChange (connection: %s) (gathering: %s)",e,t),this.emit("iceStateChange",e,t),(e==="connected"||e==="completed")&&(this._pcReady=!0,this._maybeReady()),e==="failed"&&this.destroy(r(new Error("Ice connection failed."),"ERR_ICE_CONNECTION_FAILURE")),e==="closed"&&this.destroy(r(new Error("Ice connection closed."),"ERR_ICE_CONNECTION_CLOSED"))}getStats(e){const t=i=>(Object.prototype.toString.call(i.values)==="[object Array]"&&i.values.forEach(s=>{Object.assign(i,s)}),i);this._pc.getStats.length===0||this._isReactNativeWebrtc?this._pc.getStats().then(i=>{const s=[];i.forEach(a=>{s.push(t(a))}),e(null,s)},i=>e(i)):this._pc.getStats.length>0?this._pc.getStats(i=>{if(this.destroyed)return;const s=[];i.result().forEach(a=>{const c={};a.names().forEach(l=>{c[l]=a.stat(l)}),c.id=a.id,c.type=a.type,c.timestamp=a.timestamp,s.push(t(c))}),e(null,s)},i=>e(i)):e(null,[])}_maybeReady(){if(this._debug("maybeReady pc %s channel %s",this._pcReady,this._channelReady),this._connected||this._connecting||!this._pcReady||!this._channelReady)return;this._connecting=!0;const e=()=>{this.destroyed||this.getStats((t,i)=>{if(this.destroyed)return;t&&(i=[]);const s={},a={},c={};let l=!1;i.forEach(n=>{(n.type==="remotecandidate"||n.type==="remote-candidate")&&(s[n.id]=n),(n.type==="localcandidate"||n.type==="local-candidate")&&(a[n.id]=n),(n.type==="candidatepair"||n.type==="candidate-pair")&&(c[n.id]=n)});const u=n=>{l=!0;let o=a[n.localCandidateId];o&&(o.ip||o.address)?(this.localAddress=o.ip||o.address,this.localPort=Number(o.port)):o&&o.ipAddress?(this.localAddress=o.ipAddress,this.localPort=Number(o.portNumber)):typeof n.googLocalAddress=="string"&&(o=n.googLocalAddress.split(":"),this.localAddress=o[0],this.localPort=Number(o[1])),this.localAddress&&(this.localFamily=this.localAddress.includes(":")?"IPv6":"IPv4");let d=s[n.remoteCandidateId];d&&(d.ip||d.address)?(this.remoteAddress=d.ip||d.address,this.remotePort=Number(d.port)):d&&d.ipAddress?(this.remoteAddress=d.ipAddress,this.remotePort=Number(d.portNumber)):typeof n.googRemoteAddress=="string"&&(d=n.googRemoteAddress.split(":"),this.remoteAddress=d[0],this.remotePort=Number(d[1])),this.remoteAddress&&(this.remoteFamily=this.remoteAddress.includes(":")?"IPv6":"IPv4"),this._debug("connect local: %s:%s remote: %s:%s",this.localAddress,this.localPort,this.remoteAddress,this.remotePort)};if(i.forEach(n=>{n.type==="transport"&&n.selectedCandidatePairId&&u(c[n.selectedCandidatePairId]),(n.type==="googCandidatePair"&&n.googActiveConnection==="true"||(n.type==="candidatepair"||n.type==="candidate-pair")&&n.selected)&&u(n)}),!l&&(!Object.keys(c).length||Object.keys(a).length)){setTimeout(e,100);return}else this._connecting=!1,this._connected=!0;if(this._chunk){try{this.send(this._chunk)}catch(o){return this.destroy(r(o,"ERR_DATA_CHANNEL"))}this._chunk=null,this._debug('sent chunk from "write before connect"');const n=this._cb;this._cb=null,n(null)}typeof this._channel.bufferedAmountLowThreshold!="number"&&(this._interval=setInterval(()=>this._onInterval(),150),this._interval.unref&&this._interval.unref()),this._debug("connect"),this.emit("connect")})};e()}_onInterval(){!this._cb||!this._channel||this._channel.bufferedAmount>65536||this._onChannelBufferedAmountLow()}_onSignalingStateChange(){this.destroyed||(this._pc.signalingState==="stable"&&(this._isNegotiating=!1,this._debug("flushing sender queue",this._sendersAwaitingStable),this._sendersAwaitingStable.forEach(e=>{this._pc.removeTrack(e),this._queuedNegotiation=!0}),this._sendersAwaitingStable=[],this._queuedNegotiation?(this._debug("flushing negotiation queue"),this._queuedNegotiation=!1,this._needsNegotiation()):(this._debug("negotiated"),this.emit("negotiated"))),this._debug("signalingStateChange %s",this._pc.signalingState),this.emit("signalingStateChange",this._pc.signalingState))}_onIceCandidate(e){this.destroyed||(e.candidate&&this.trickle?this.emit("signal",{type:"candidate",candidate:{candidate:e.candidate.candidate,sdpMLineIndex:e.candidate.sdpMLineIndex,sdpMid:e.candidate.sdpMid}}):!e.candidate&&!this._iceComplete&&(this._iceComplete=!0,this.emit("_iceComplete")),e.candidate&&this._startIceCompleteTimeout())}_onChannelMessage(e){if(this.destroyed)return;let t=e.data;t instanceof ArrayBuffer&&(t=new Uint8Array(t)),this.emit("data",t)}_onChannelBufferedAmountLow(){if(this.destroyed||!this._cb)return;this._debug("ending backpressure: bufferedAmount %d",this._channel.bufferedAmount);const e=this._cb;this._cb=null,e(null)}_onChannelOpen(){this._connected||this.destroyed||(this._debug("on channel open"),this._channelReady=!0,this._maybeReady())}_onChannelClose(){this.destroyed||(this._debug("on channel close"),this.destroy())}_onTrack(e){this.destroyed||e.streams.forEach(t=>{this._debug("on track"),this.emit("track",e.track,t),this._remoteTracks.push({track:e.track,stream:t}),!this._remoteStreams.some(i=>i.id===t.id)&&(this._remoteStreams.push(t),queueMicrotask(()=>{this._debug("on stream"),this.emit("stream",t)}))})}_debug(...e){!this._doDebug||(e[0]="["+this._id+"] "+e[0],console.log(...e))}on(e,t){const i=this._map;i.has(e)||i.set(e,new Set),i.get(e).add(t)}off(e,t){const i=this._map,s=i.get(e);!s||(s.delete(t),s.size===0&&i.delete(e))}once(e,t){const i=(...s)=>{this.off(e,i),t(...s)};this.on(e,i)}emit(e,...t){const i=this._map;if(!!i.has(e))for(const s of i.get(e))try{s(...t)}catch(a){console.error(a)}}}_.WEBRTC_SUPPORT=!!p(),_.config={iceServers:[{urls:["stun:stun.l.google.com:19302","stun:global.stun.twilio.com:3478"]}],sdpSemantics:"unified-plan"},_.channelConfig={};class R{constructor({canvasFoundCallback:e,signalSendCallback:t}){this.peer=null,this.streamNodeMap=new Map,this.streams=new Set,this.runningStreams=new WeakSet,this.canvasFoundCallback=e,this.signalSendCallback=t}initPlugin(){return{onBuild:(e,t)=>{e.nodeName==="CANVAS"&&this.canvasFoundCallback(e,t)},getMirror:e=>{this.mirror=e.nodeMirror}}}startStream(e,t){if(!this.runningStreams.has(t)){if(e.tagName==="VIDEO"){const i=e;i.srcObject=t,i.play(),this.runningStreams.add(t);return}if("MediaStreamTrackProcessor"in window){const i=e,s=i.getContext("2d");if(!s)throw new Error(`startStream: Could not get 2d canvas context for ${i.outerHTML}`);const a=t.getVideoTracks()[0],c=new MediaStreamTrackProcessor({track:a}).readable.getReader(),l=function(){c.read().then(({done:u,value:n})=>{!n||((i.width!==n.displayWidth||i.height!==n.displayHeight)&&(i.width=n.displayWidth,i.height=n.displayHeight),s.clearRect(0,0,i.width,i.height),s.drawImage(n,0,0),n.close(),u||l())})};l(),this.runningStreams.add(t)}else{const i=document.createElement("video");i.setAttribute("autoplay","true"),i.setAttribute("playsinline","true"),i.setAttribute("width",e.width.toString()),i.setAttribute("height",e.height.toString()),e.replaceWith(i),this.startStream(i,t)}}}signalReceive(e){this.peer||(this.peer=new _({initiator:!1}),this.peer.on("error",t=>{this.peer=null,console.log("error",t)}),this.peer.on("close",()=>{this.peer=null,console.log("closing")}),this.peer.on("signal",t=>{this.signalSendCallback(t)}),this.peer.on("connect",()=>{}),this.peer.on("data",t=>{try{const i=JSON.parse(t);this.streamNodeMap.set(i.streamId,i.nodeId)}catch(i){console.error("Could not parse data",i)}this.flushStreams()}),this.peer.on("stream",t=>{this.streams.add(t),this.flushStreams()})),this.peer.signal(e)}flushStreams(){this.streams.forEach(e=>{const t=this.streamNodeMap.get(e.id);if(!t)return;const i=this.mirror.getNode(t);i&&this.startStream(i,e)})}}return g.RRWebPluginCanvasWebRTCReplay=R,Object.defineProperty(g,"__esModule",{value:!0}),g}({}); //# sourceMappingURL=canvas-webrtc-replay.min.js.map