2 lines
13 KiB
JavaScript
2 lines
13 KiB
JavaScript
import{dn as e,dp as t,dq as s,dr as i,a as o,dj as n,l as a,dw as c,dx as r,a4 as d,x as h,r as l,_ as p,n as m,b as u,t as v}from"./card-b98d578d.js";import{d as y}from"./dispatch-live-error-5bb8d032.js";import{c as g,a as b,s as w,h as f,M as C,b as S,V as O}from"./audio-13475a81.js";import{g as E}from"./get-technology-for-video-rtc-778a0c05.js";class T extends HTMLElement{constructor(){super(),this.DISCONNECT_TIMEOUT=5e3,this.RECONNECT_TIMEOUT=15e3,this.CODECS=["avc1.640029","avc1.64002A","avc1.640033","hvc1.1.6.L153.B0","mp4a.40.2","mp4a.40.5","flac","opus"],this.mode="webrtc,mse,hls,mjpeg",this.media="video,audio",this.background=!1,this.visibilityThreshold=0,this.visibilityCheck=!0,this.pcConfig={bundlePolicy:"max-bundle",iceServers:[{urls:"stun:stun.l.google.com:19302"}],sdpSemantics:"unified-plan"},this.wsState=WebSocket.CLOSED,this.pcState=WebSocket.CLOSED,this.video=null,this.ws=null,this.wsURL="",this.pc=null,this.connectTS=0,this.mseCodecs="",this.disconnectTID=0,this.reconnectTID=0,this.ondata=null,this.onmessage=null,this.microphoneStream=null,this.mediaPlayerController=null,this.controls=!0,this._audioTracksMuteStateCleanup=null}_dispatchMediaLoadedEvent(){e(this,this.video,{...this.mediaPlayerController&&{mediaPlayerController:this.mediaPlayerController},capabilities:{has2WayAudio:g(this.pc),hasAudio:b(this.video,this.pc,this.mseCodecs),supportsPause:!0},technology:E(this)})}reconnect(){this.wsState!==WebSocket.CLOSED?(this.ws?.addEventListener("close",(()=>this.onconnect())),this.ondisconnect()):(this.ondisconnect(),this.onconnect())}setControls(e){this.controls=e,this.video&&w(this.video,e)}set src(e){"string"!=typeof e&&(e=e.toString()),e.startsWith("http")?e="ws"+e.substring(4):e.startsWith("/")&&(e="ws"+location.origin.substring(4)+e),this.wsURL=e,this.onconnect()}play(){}send(e){this.ws&&this.ws.send(JSON.stringify(e))}codecs(e){return this.CODECS.filter((e=>this.media.indexOf(e.indexOf("vc1")>0?"video":"audio")>=0)).filter((t=>e(`video/mp4; codecs="${t}"`))).join()}connectedCallback(){if(this.disconnectTID&&(clearTimeout(this.disconnectTID),this.disconnectTID=0),this.video){const e=this.video.seekable;e.length>0&&(this.video.currentTime=e.end(e.length-1)),this.play()}else this.oninit();this.onconnect()}disconnectedCallback(){this.background||this.disconnectTID||this.wsState===WebSocket.CLOSED&&this.pcState===WebSocket.CLOSED||(this.disconnectTID=setTimeout((()=>{this.reconnectTID&&(clearTimeout(this.reconnectTID),this.reconnectTID=0),this.disconnectTID=0,this.ondisconnect()}),this.DISCONNECT_TIMEOUT))}oninit(){this.video=document.createElement("video"),w(this.video,this.controls),this.video.playsInline=!0,this.video.preload="auto",this.video.style.display="block",this.video.style.width="100%",this.video.style.height="100%",this.appendChild(this.video),this.video.addEventListener("error",(e=>{this.ws&&this.wsState===WebSocket.OPEN&&this.ws.close()}));const e=window.navigator.userAgent.match(/Version\/(\d+).+Safari/);if(e){const t=e[1]<"13"?"mp4a.40.2":e[1]<"14"?"flac":"opus";this.CODECS.splice(this.CODECS.indexOf(t))}if(!this.background){if("hidden"in document&&this.visibilityCheck&&document.addEventListener("visibilitychange",(()=>{document.hidden?this.disconnectedCallback():this.isConnected&&this.connectedCallback()})),"IntersectionObserver"in window&&this.visibilityThreshold){new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting?this.isConnected&&this.connectedCallback():this.disconnectedCallback()}))}),{threshold:this.visibilityThreshold}).observe(this)}this.video.onloadeddata=()=>{this.controls&&f(this.video,C),this._dispatchMediaLoadedEvent(),this._audioTracksMuteStateCleanup?.(),this._audioTracksMuteStateCleanup=S(this.pc,(()=>this._dispatchMediaLoadedEvent()))},this.video.onvolumechange=()=>t(this),this.video.onplay=()=>s(this),this.video.onpause=()=>i(this),this.video.muted=!0}}onconnect(){return!(!this.isConnected||!this.wsURL||this.ws||this.pc)&&(this.wsState=WebSocket.CONNECTING,this.connectTS=Date.now(),this.ws=new WebSocket(this.wsURL),this.ws.binaryType="arraybuffer",this.ws.addEventListener("open",(()=>this.onopen())),this.ws.addEventListener("close",(()=>this.onclose())),!0)}ondisconnect(){this.wsState=WebSocket.CLOSED,this.ws&&(this.ws.close(),this.ws=null),this.pcState=WebSocket.CLOSED,this.pc&&(this.pc.close(),this.pc=null),this.video.src="",this.video.srcObject=null,this._audioTracksMuteStateCleanup?.(),this._audioTracksMuteStateCleanup=null}onopen(){this.wsState=WebSocket.OPEN,this.ws.addEventListener("message",(e=>{if("string"==typeof e.data){const t=JSON.parse(e.data);for(const e in this.onmessage)this.onmessage[e](t)}else this.ondata(e.data)})),this.ondata=null,this.onmessage={};const e=[];return this.mode.indexOf("mse")>=0&&("MediaSource"in window||"ManagedMediaSource"in window)?(e.push("mse"),this.onmse()):this.mode.indexOf("hls")>=0&&this.video.canPlayType("application/vnd.apple.mpegurl")?(e.push("hls"),this.onhls()):this.mode.indexOf("mp4")>=0&&(e.push("mp4"),this.onmp4()),this.mode.indexOf("webrtc")>=0&&"RTCPeerConnection"in window&&(e.push("webrtc"),this.onwebrtc()),this.mode.indexOf("mjpeg")>=0&&(e.length?this.onmessage.mjpeg=t=>{"error"===t.type&&0===t.value.indexOf(e[0])&&this.onmjpeg()}:(e.push("mjpeg"),this.onmjpeg())),e}onclose(){if(this.wsState===WebSocket.CLOSED)return!1;this.wsState=WebSocket.CONNECTING,this.ws=null;const e=Math.max(this.RECONNECT_TIMEOUT-(Date.now()-this.connectTS),0);return this.reconnectTID=setTimeout((()=>{this.reconnectTID=0,this.onconnect()}),e),!0}onmse(){let e;if("ManagedMediaSource"in window){const t=window.ManagedMediaSource;e=new t,e.addEventListener("sourceopen",(()=>{this.send({type:"mse",value:this.codecs(t.isTypeSupported)})}),{once:!0}),this.video.disableRemotePlayback=!0,this.video.srcObject=e}else e=new MediaSource,e.addEventListener("sourceopen",(()=>{URL.revokeObjectURL(this.video.src),this.send({type:"mse",value:this.codecs(MediaSource.isTypeSupported)})}),{once:!0}),this.video.src=URL.createObjectURL(e),this.video.srcObject=null;this.play(),this.mseCodecs="",this.onmessage.mse=t=>{if("mse"!==t.type)return;this.mseCodecs=t.value;const s=e.addSourceBuffer(t.value);s.mode="segments",s.addEventListener("updateend",(()=>{if(!s.updating&&o>0)try{const e=i.slice(0,o);s.appendBuffer(e),o=0}catch(e){}if(!s.updating&&s.buffered&&s.buffered.length){const t=s.buffered.end(s.buffered.length-1),i=t-5,o=s.buffered.start(0);i>o&&(s.remove(o,i),e.setLiveSeekableRange(i,t)),this.video.currentTime<i&&(this.video.currentTime=i);const n=t-this.video.currentTime;this.video.playbackRate=n>.1?n:.1}}));const i=new Uint8Array(2097152);let o=0;this.ondata=e=>{if(s.updating||o>0){const t=new Uint8Array(e);i.set(t,o),o+=t.byteLength}else try{s.appendBuffer(e)}catch(e){}}}}onwebrtc(){const e=new RTCPeerConnection(this.pcConfig);e.addEventListener("icecandidate",(e=>{if(e.candidate&&this.mode.indexOf("webrtc/tcp")>=0&&"udp"===e.candidate.protocol)return;const t=e.candidate?e.candidate.toJSON().candidate:"";this.send({type:"webrtc/candidate",value:t})})),e.addEventListener("connectionstatechange",(()=>{if("connected"===e.connectionState){const t=e.getTransceivers().filter((e=>"recvonly"===e.currentDirection)).map((e=>e.receiver.track)),s=document.createElement("video");s.addEventListener("loadeddata",(()=>this.onpcvideo(s)),{once:!0}),s.srcObject=new MediaStream(t)}else"failed"!==e.connectionState&&"disconnected"!==e.connectionState||(e.close(),this.pcState=WebSocket.CLOSED,this.pc=null,this.onconnect())})),this.onmessage.webrtc=t=>{switch(t.type){case"webrtc/candidate":if(this.mode.indexOf("webrtc/tcp")>=0&&t.value.indexOf(" udp ")>0)return;e.addIceCandidate({candidate:t.value,sdpMid:"0"}).catch((e=>{console.warn(e)}));break;case"webrtc/answer":e.setRemoteDescription({type:"answer",sdp:t.value}).catch((e=>{console.warn(e)}));break;case"error":if(t.value.indexOf("webrtc/offer")<0)return;e.close()}},this.createOffer(e).then((e=>{this.send({type:"webrtc/offer",value:e.sdp})})),this.pcState=WebSocket.CONNECTING,this.pc=e}async createOffer(e){this.microphoneStream?.getTracks().forEach((t=>{e.addTransceiver(t,{direction:"sendonly"})}));try{if(this.media.indexOf("microphone")>=0){(await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach((t=>{e.addTransceiver(t,{direction:"sendonly"})}))}}catch(e){console.warn(e)}for(const t of["video","audio"])this.media.indexOf(t)>=0&&e.addTransceiver(t,{direction:"recvonly"});const t=await e.createOffer();return await e.setLocalDescription(t),t}onpcvideo(e){if(this.pc){let t=0,s=0;const i=e.srcObject;if(i.getVideoTracks().length>0){t+=this.pc.remoteDescription.sdp.includes("H265/90000")?576:544}i.getAudioTracks().length>0&&(t+=258),this.mseCodecs.indexOf("hvc1.")>=0&&(s+=560),this.mseCodecs.indexOf("avc1.")>=0&&(s+=528),this.mseCodecs.indexOf("mp4a.")>=0&&(s+=257),t>=s?(this.video.srcObject=i,this.play(),this.pcState=WebSocket.OPEN,this.wsState=WebSocket.CLOSED,this.ws&&(this.ws.close(),this.ws=null)):(this.pcState=WebSocket.CLOSED,this.pc&&(this.pc.close(),this.pc=null))}e.srcObject=null}onmjpeg(){let t=!1;this.ondata=s=>{w(this.video,!1),this.video.poster="data:image/jpeg;base64,"+T.btoa(s),t||(t=!0,e(this,this.video,{...this.mediaPlayerController&&{mediaPlayerController:this.mediaPlayerController},technology:["mjpeg"]}))},this.send({type:"mjpeg"})}onhls(){this.onmessage.hls=e=>{if("hls"!==e.type)return;const t="http"+this.wsURL.substring(2,this.wsURL.indexOf("/ws"))+"/hls/",s=e.value.replace("hls/",t);this.video.src="data:application/vnd.apple.mpegurl;base64,"+btoa(s),this.play()},this.send({type:"hls",value:this.codecs((e=>this.video.canPlayType(e)))})}onmp4(){const t=document.createElement("canvas");let s;const i=document.createElement("video");i.autoplay=!0,i.playsInline=!0,i.muted=!0,i.addEventListener("loadeddata",(o=>{s||(t.width=i.videoWidth,t.height=i.videoHeight,s=t.getContext("2d"),e(this,i,{...this.mediaPlayerController&&{mediaPlayerController:this.mediaPlayerController},technology:["mp4"]})),s.drawImage(i,0,0,t.width,t.height),w(this.video,!1),this.video.poster=t.toDataURL("image/jpeg")})),this.ondata=e=>{i.src="data:video/mp4;base64,"+T.btoa(e)},this.send({type:"mp4",value:this.codecs(this.video.canPlayType)})}static btoa(e){const t=new Uint8Array(e),s=t.byteLength;let i="";for(let e=0;e<s;e++)i+=String.fromCharCode(t[e]);return window.btoa(i)}}customElements.define("advanced-camera-card-live-go2rtc-player",T);let k=class extends o{constructor(){super(...arguments),this.controls=!1,this._message=null,this._mediaPlayerController=new O(this,(()=>this._player?.video??null),(()=>this.controls))}async getMediaPlayerController(){return this._mediaPlayerController}disconnectedCallback(){this._player=void 0,this._message=null,super.disconnectedCallback()}connectedCallback(){super.connectedCallback(),this.requestUpdate()}_handleError(e,t){t&&n(t),this._message={type:"error",...e},y(this)}async _getPlayerSource(){const e=this.camera?.getConfig(),t=this.camera?.getProxyConfig();if(!this.hass||!e)return null;const s=this.cameraEndpoints?.go2rtc;if(!s)return this._handleError({message:a("error.live_camera_no_endpoint"),context:e}),null;let i=null;try{const o=await c(this.hass,s,t,{context:"live",ttl:86400,websocket:!0,openLimit:0});o.sign?(i=await r(this.hass,o.endpoint,86400),i||this._handleError({message:a("error.failed_sign"),context:e})):i=o.endpoint}catch(t){this._handleError({message:a("error.failed_proxy"),context:e},t)}return i}async _createPlayer(){const e=await this._getPlayerSource();if(!e)return;this._player=new T,this._player.mediaPlayerController=this._mediaPlayerController,this._player.microphoneStream=this.microphoneState?.stream??null,this._player.src=e,this._player.visibilityCheck=!1,this._player.setControls(this.controls);const t=this.camera?.getConfig();t?.go2rtc?.modes&&t.go2rtc.modes.length&&(this._player.mode=t.go2rtc.modes.join(",")),this.requestUpdate()}willUpdate(e){e.has("cameraEndpoints")&&(this._message=null),this._message||this._player&&!e.has("cameraEndpoints")||this._createPlayer(),e.has("controls")&&this._player&&this._player.setControls(this.controls),this._player&&e.has("microphoneState")&&this._player.microphoneStream!==(this.microphoneState?.stream??null)&&(this._player.microphoneStream=this.microphoneState?.stream??null,this._player.reconnect())}render(){return this._message?d(this._message):h`${this._player}`}static get styles(){return l(":host {\n width: 100%;\n height: 100%;\n display: block;\n}\n\nvideo {\n object-fit: var(--advanced-camera-card-media-layout-fit, contain);\n object-position: var(--advanced-camera-card-media-layout-position-x, 50%) var(--advanced-camera-card-media-layout-position-y, 50%);\n object-view-box: inset(var(--advanced-camera-card-media-layout-view-box-top, 0%) var(--advanced-camera-card-media-layout-view-box-right, 0%) var(--advanced-camera-card-media-layout-view-box-bottom, 0%) var(--advanced-camera-card-media-layout-view-box-left, 0%));\n width: 100%;\n height: 100%;\n display: block;\n}")}};p([m({attribute:!1})],k.prototype,"camera",void 0),p([m({attribute:!1})],k.prototype,"cameraEndpoints",void 0),p([m({attribute:!1})],k.prototype,"microphoneState",void 0),p([m({attribute:!1})],k.prototype,"microphoneConfig",void 0),p([m({attribute:!0,type:Boolean})],k.prototype,"controls",void 0),p([u()],k.prototype,"_message",void 0),k=p([v("advanced-camera-card-live-go2rtc")],k);export{k as AdvancedCameraCardGo2RTC};
|