@scrypted/prebuffer-mixin 0.1.160 → 0.1.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +1 -1
- package/dist/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/main.ts +29 -12
package/.vscode/settings.json
CHANGED
package/dist/main.nodejs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(()=>{var e={454:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{systemManager:o}=i.default,s="v4";class AutoenableMixinProvider extends i.ScryptedDeviceBase{constructor(e){var t,r,n;super(e),n={},(r="hasEnabledMixin")in(t=this)?Object.defineProperty(t,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[r]=n;try{this.hasEnabledMixin=JSON.parse(this.storage.getItem("hasEnabledMixin"))}catch(e){this.hasEnabledMixin={}}this.pluginsComponent=o.getComponent("plugins"),o.listen((async(e,t,r)=>{t.eventInterface!==i.ScryptedInterface.ScryptedDevice||t.property||this.maybeEnableMixin(e)}));for(const e of Object.keys(o.getSystemState())){const t=o.getDeviceById(e);this.maybeEnableMixin(t)}}async shouldEnableMixin(e){return!0}async maybeEnableMixin(e){var t;if(!e||null!==(t=e.mixins)&&void 0!==t&&t.includes(this.id))return;if(this.hasEnabledMixin[e.id]===s)return;if(!await this.canMixin(e.type,e.interfaces))return;if(!await this.shouldEnableMixin(e))return;this.log.i("auto enabling mixin for "+e.name);const r=(e.mixins||[]).slice();r.push(this.id);const i=await this.pluginsComponent;await i.setMixins(e.id,r),this.setHasEnabledMixin(e.id)}setHasEnabledMixin(e){this.hasEnabledMixin[e]!==s&&(this.hasEnabledMixin[e]=s,this.storage.setItem("hasEnabledMixin",JSON.stringify(this.hasEnabledMixin)))}}t.AutoenableMixinProvider=AutoenableMixinProvider},201:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createParserRebroadcaster=async function(e,t,r){return await g(Object.assign({},r,{connect:(r,i)=>(e.on(t,r),()=>{i()})}))},t.createRebroadcaster=g,t.handleRebroadcasterClient=v,t.parseAudioCodec=f,t.parseResolution=p,t.parseVideoCodec=h,t.startParserSession=async function(e,t){const{console:r}=t;let i,a=!0;const u=new s.EventEmitter;let m,g,v,y,S;u.on("error",(e=>r.error("rebroadcast error",e)));const b=new Promise(((e,t)=>{y=e,S=t}));function P(){var e;a&&(u.emit("killed"),u.emit("error",new Error("killed"))),a=!1,null==D||D.kill(),setTimeout((()=>null==D?void 0:D.kill("SIGKILL")),1e3),null===(e=S)||void 0===e||e(new Error("ffmpeg was killed before connecting to the rebroadcast session")),clearTimeout(i)}const M=e.inputArguments.slice();i=setTimeout(P,3e4);const w=e=>{let i;function n(){r.error("timeout waiting for data, killing parser session",e),P()}function o(){t.timeout&&(clearTimeout(i),i=setTimeout(n,t.timeout))}return u.once("killed",(()=>clearTimeout(i))),o(),{resetActivityTimer:o}};let I=!1;const O=["pipe","pipe","pipe"];let x=3;for(const e of Object.keys(t.parsers)){const i=t.parsers[e];if(i.parseDatagram){I=!0;const t=d.default.createSocket("udp4"),r=await(0,o.bindZero)(t),n=d.default.createSocket("udp4");await(0,o.bind)(n,r.port+1),u.once("killed",(()=>{t.close(),n.close()})),M.push(...i.outputArguments,r.url.replace("udp://","rtp://"));const{resetActivityTimer:s}=w(e);(async()=>{for await(const a of i.parseDatagram(t,parseInt(null===(r=v)||void 0===r?void 0:r[2]),parseInt(null===(n=v)||void 0===n?void 0:n[3]))){var r,n,o;null===(o=y)||void 0===o||o(void 0),u.emit(e,a),s()}})(),(async()=>{for await(const a of i.parseDatagram(n,parseInt(null===(t=v)||void 0===t?void 0:t[2]),parseInt(null===(r=v)||void 0===r?void 0:r[3]),"rtcp")){var t,r,o;null===(o=y)||void 0===o||o(void 0),u.emit(e,a),s()}})()}else if(i.tcpProtocol){const t=await(0,o.listenZeroSingleClient)(),n=new URL(i.tcpProtocol);n.port=t.port.toString(),M.push(...i.outputArguments,n.toString());const{resetActivityTimer:s}=w(e);(async()=>{const n=await t.clientPromise;try{for await(const t of i.parse(n,parseInt(null===(o=v)||void 0===o?void 0:o[2]),parseInt(null===(a=v)||void 0===a?void 0:a[3]))){var o,a,c;null===(c=y)||void 0===c||c(void 0),u.emit(e,t),s()}}catch(e){r.error("rebroadcast parse error",e),P()}})()}else M.push(...i.outputArguments,"pipe:"+x++),O.push("pipe")}I&&(M.push("-sdp_file","pipe:"+x++),O.push("pipe"));M.unshift("-hide_banner"),(0,c.safePrintFFmpegArguments)(r,M);const D=n.default.spawn(await l.getFFmpegPath(),M,{stdio:O});let C;(0,c.ffmpegLogInitialOutput)(r,D,void 0,null==t?void 0:t.storage),D.on("exit",P),C=I?new Promise((e=>{const t=[];D.stdio[x-1].on("data",(r=>{t.push(r),e(t)}))})):Promise.resolve([]);let A=0;return Object.keys(t.parsers).forEach((async e=>{const i=t.parsers[e];if(!i.parse||i.tcpProtocol)return;const n=D.stdio[3+A];A++;try{const{resetActivityTimer:t}=w(e);for await(const r of i.parse(n,parseInt(null===(o=v)||void 0===o?void 0:o[2]),parseInt(null===(s=v)||void 0===s?void 0:s[3]))){var o,s,a;null===(a=y)||void 0===a||a(void 0),u.emit(e,r),t()}}catch(e){r.error("rebroadcast parse error",e),P()}})),f(D).then((e=>m=e)),h(D).then((e=>g=e)),p(D).then((e=>v=e)),await b,y=void 0,S=void 0,clearTimeout(i),{sdp:C,inputAudioCodec:m,inputVideoCodec:g,inputVideoResolution:v,isActive:()=>a,kill:P,mediaStreamOptions:e.mediaStreamOptions||{id:void 0,name:void 0},on(e,t){return u.on(e,t),this},once(e,t){return u.once(e,t),this},removeListener(e,t){return u.removeListener(e,t),this}}};var i=r(808),n=u(r(81)),o=r(769),s=r(361),a=u(r(510)),c=r(833),d=u(r(891));function u(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:l}=a.default;async function p(e){return new Promise((t=>{const r=i=>{const n=i.toString(),o=/(([0-9]{2,5})x([0-9]{2,5}))/.exec(n);o&&(e.stdout.removeListener("data",r),e.stderr.removeListener("data",r),t(o))};e.stdout.on("data",r),e.stderr.on("data",r)}))}async function m(e,t){return new Promise((r=>{const i=n=>{const o=n.toString(),s=o.indexOf(`${t}: `);if(-1!==s){const n=o.substring(s+t.length+1).trim();let a=n.indexOf(" ");const c=n.indexOf(",");-1!==a&&c<a&&(a=c),-1!==a&&(e.stdout.removeListener("data",i),e.stderr.removeListener("data",i),r(n.substring(0,a)))}};e.stdout.on("data",i),e.stderr.on("data",i)}))}async function h(e){return m(e,"Video")}async function f(e){return m(e,"Audio")}async function g(e){let t,r=0;const n=()=>{null!=e&&e.idle&&(clearTimeout(t),t=setTimeout((()=>{0!==r?n():e.idle.callback()}),e.idle.timeout))};n();const s=(0,i.createServer)((t=>{v(t,e),t.once("close",(()=>{n(),r--})),n(),r++})),a=await(0,o.listenZero)(s);return{server:s,port:a,url:`tcp://127.0.0.1:${a}`,get clients(){return r}}}async function v(e,t){const r=await e;let i=!0;const n=()=>{r.removeAllListeners(),r.destroy();const e=o;o=void 0,null==e||e()};let o=null==t?void 0:t.connect((e=>{i&&(i=!1,e.startStream&&r.write(e.startStream));for(const t of e.chunks)r.write(t);return r.writableLength}),n);r.once("close",(()=>{n()})),r.on("error",(e=>{var r;return null==t||null===(r=t.console)||void 0===r?void 0:r.log("client stream ended")}))}},769:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.bind=async function(e,t){return e.bind(t),await(0,n.once)(e,"listening"),{port:t,url:`udp://127.0.0.1:${t}`}},t.bindUdp=c,t.bindZero=async function(e){return c(e,0)},t.createBindUdp=d,t.createBindZero=async function(){return d(0)},t.listenZero=a,t.listenZeroSingleClient=async function(){const e=new i.default.Server,t=await a(e),r=new Promise(((t,r)=>{const i=setTimeout((()=>{r(new Error("timeout waiting for client"))}),3e4);e.on("connection",(r=>{e.close(),clearTimeout(i),t(r)}))}));return{url:`tcp://127.0.0.1:${t}`,port:t,clientPromise:r}};var i=s(r(808)),n=r(361),o=s(r(891));function s(e){return e&&e.__esModule?e:{default:e}}async function a(e){return e.listen(0),await(0,n.once)(e,"listening"),e.address().port}async function c(e,t){e.bind(t),await(0,n.once)(e,"listening");const r=e.address().port;return{port:r,url:`udp://127.0.0.1:${r}`}}async function d(e){const t=o.default.createSocket("udp4"),{port:r,url:i}=await c(t,e);return{server:t,port:r,url:i}}},833:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(568);Object.keys(i).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===i[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return i[e]}}))}))},961:(e,t)=>{"use strict";async function r(e,t){if(!t)return Buffer.alloc(0);{const r=e.read(t);if(r)return r}return new Promise(((r,i)=>{const n=()=>{const i=e.read(t);i&&(s(),r(i))},o=()=>{s(),i(new Error(`stream ended during read for minimum ${t} bytes`))},s=()=>{e.removeListener("readable",n),e.removeListener("end",o)};e.on("readable",n),e.on("end",o)}))}Object.defineProperty(t,"__esModule",{value:!0}),t.readLength=r,t.readLine=async function(e){return n(e,i)},t.readUntil=n;const i="\n".charCodeAt(0);async function n(e,t){const i=[];let n=0;for(;;){const o=await r(e,1);if(!o)throw new Error("end of stream");if(o[0]===t)break;i[n++]=o[0]}return Buffer.from(i).toString()}},175:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RtspServer=t.RTSP_FRAME_MAGIC=void 0,t.createRtspParser=function(){let e;return{container:"rtsp",tcpProtocol:"rtsp://127.0.0.1/"+(0,n.randomBytes)(8).toString("hex"),inputArguments:["-rtsp_transport","tcp"],outputArguments:["-rtsp_transport","tcp","-vcodec","copy","-acodec","copy","-f","rtsp"],findSyncFrame:s,sdp:new Promise((t=>e=t)),async*parse(t,r,i){const n=new RtspServer(t);await n.handleSetup(),e(n.sdp);for await(const{type:e,rtcp:t,header:o,packet:s}of n.handleRecord())yield{chunks:[o,s],type:e,width:r,height:i}}}};var i=r(961),n=r(113);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function s(e){return e}t.RTSP_FRAME_MAGIC=36;class RtspServer{constructor(e,t,r){o(this,"videoChannel",0),o(this,"audioChannel",2),o(this,"udpPorts",{video:0,audio:0}),this.client=e,this.sdp=t,this.udp=r,this.session=(0,n.randomBytes)(4).toString("hex"),t&&(t=t.trim())}async handleSetup(){let e=[];for(;;){let t=await(0,i.readLine)(this.client);if(t=t.trim(),t)e.push(t);else{if(!await this.headers(e))break;e=[]}}}async handlePlayback(){return this.handleSetup()}async*handleRecord(){for(;;){const e=await(0,i.readLength)(this.client,4);if(36!==e[0])throw new Error("RTSP frame magic expected, but got "+e[0]);const t=e.readUInt16BE(2),r=await(0,i.readLength)(this.client,t),n=e.readUInt8(1);yield{type:n-n%2===this.videoChannel?"video":"audio",rtcp:n%2==1,header:e,packet:r}}}send(e,t){const r=Buffer.alloc(4);r.writeUInt8(36,0),r.writeUInt8(t,1),r.writeUInt16BE(e.length,2),this.client.write(r),this.client.write(Buffer.from(e))}sendUdp(e,t,r){this.udp.send(t,r?e+1:e,"127.0.0.1")}sendVideo(e,t){this.udp?this.sendUdp(this.udpPorts.video,e,t):this.send(e,t?this.videoChannel+1:this.videoChannel)}sendAudio(e,t){this.udp?this.sendUdp(this.udpPorts.audio,e,t):this.send(e,t?this.audioChannel+1:this.audioChannel)}options(e,t){const r={Public:"DESCRIBE, OPTIONS, PAUSE, PLAY, SETUP, TEARDOWN, ANNOUNCE, RECORD"};this.respond(200,"OK",t,r)}describe(e,t){const r={};r["Content-Base"]=e,r["Content-Type"]="application/sdp",this.respond(200,"OK",t,r,Buffer.from(this.sdp))}setup(e,t){const r={},i=t.transport;if(r.Transport=t.transport,r.Session=this.session,i.includes("UDP")){const t=i.match(/.*?client_port=([0-9]+)-([0-9]+)/),[r,n,o]=t;e.includes("audio")?this.udpPorts.audio=parseInt(n):this.udpPorts.video=parseInt(n)}else if(i.includes("TCP")){const t=i.match(/.*?interleaved=([0-9]+)-([0-9]+)/);if(t){const r=parseInt(t[1]);parseInt(t[2]);e.includes("audio")?this.audioChannel=r:this.videoChannel=r}}this.respond(200,"OK",t,r)}play(e,t){const r={};r["RTP-Info"]=`url=${e}/trackID=0;seq=0;rtptime=0,url=${e}/trackID=1;seq=0;rtptime=0`,r.Range="npt=now-",r.Session=this.session,this.respond(200,"OK",t,r)}async announce(e,t){const r=parseInt(t["content-length"]),n=await(0,i.readLength)(this.client,r);this.sdp=n.toString();const o={};o.Session=this.session,this.respond(200,"OK",t,o)}async record(e,t){const r={};r.Session=this.session,this.respond(200,"OK",t,r)}async teardown(e,t){const r={};r.Session=this.session,this.respond(200,"OK",t,r)}async headers(e){var t,r;null===(t=this.console)||void 0===t||t.log("request header",e.join("\n"));let[i,n]=e[0].split(" ",2);i=i.toLowerCase();const o=function(e){const t={};for(const r of e.slice(1)){const e=r.indexOf(":");let i="";-1!==e&&(i=r.substring(e+1).trim()),t[r.substring(0,e).toLowerCase()]=i}return t}(e);if(null===(r=this.console)||void 0===r||r.log("request headers",o),this[i])return await this[i](n,o),"play"!==i&&"record"!==i;this.respond(400,"Bad Request",o,{})}respond(e,t,r,i,n){var o;let s=`RTSP/1.0 ${e} ${t}\r\n`;r.cseq&&(i.CSeq=r.cseq),n&&(i["Content-Length"]=n.length.toString());for(const[e,t]of Object.entries(i))s+=`${e}: ${t}\r\n`;null===(o=this.console)||void 0===o||o.log("response header",s),s+="\r\n",this.client.write(s),n&&this.client.write(n)}}t.RtspServer=RtspServer},567:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{deviceManager:o}=i.default;class SettingsMixinDeviceBase extends i.MixinDeviceBase{constructor(e,t,r){super(e,r.mixinDeviceInterfaces,t,r.providerNativeId,r.mixinStorageSuffix),this.settingsGroup=r.group,this.settingsGroupKey=r.groupKey,process.nextTick((()=>o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)))}async getSettings(){const e=this.mixinDeviceInterfaces.includes(i.ScryptedInterface.Settings)?this.mixinDevice.getSettings():void 0,t=this.getMixinSettings(),r=[];try{const t=await e||[];r.push(...t)}catch(e){const t=this.name,i=`${t} Extension settings failed to load.`;this.console.error(i,e),r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:i,readonly:!0})}try{const e=await t||[];for(const t of e)t.group=t.group||this.settingsGroup,t.key=this.settingsGroupKey+":"+t.key;r.push(...e)}catch(e){const t=o.getDeviceState(this.mixinProviderNativeId).name,i=`${t} Extension settings failed to load.`;this.console.error(i,e),r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:i,readonly:!0})}return r}async putSetting(e,t){const r=this.settingsGroupKey+":";if(null==e||!e.startsWith(r))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(r.length),t),o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}release(){o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=SettingsMixinDeviceBase},129:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PIXEL_FORMAT_YUV420P=t.PIXEL_FORMAT_RGB24=void 0,t.createDgramParser=a,t.createFragmentedMp4Parser=function(e){return{container:"mp4",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-movflags","frag_keyframe+empty_moov+default_base_moof","-f","mp4"],async*parse(e){const t=c(e);let r,i,n;for await(const e of t)r?i||(i=e):r=e,yield{startStream:n,chunks:[e.header,e.data],type:e.type},r&&i&&!n&&(n=Buffer.concat([r.header,r.data,i.header,i.data]))},findSyncFrame:o}},t.createMpegTsParser=function(e){return{container:"mpegts",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-f","mpegts"],parse:s(188,(e=>{if(71!=e[0])throw new Error("Invalid sync byte in mpeg-ts packet. Terminating stream.")})),findSyncFrame(e){for(let t=0;t<e.length;t++){const r=e[t];for(let i=0;i<r.chunks.length;i++){const n=r.chunks[i];let o=0;for(;o+188<n.length;){const r=n.subarray(o,o+188);if(256==((31&r[1])<<8|r[2])&&32&r[3]&&r[4]>0&&64&r[5])return e.slice(t);o+=188}}}return e}}},t.createRawVideoParser=function(e){var t;const r=(null===(t=e)||void 0===t?void 0:t.pixelFormat)||d;let i;e=e||{};const{size:s,everyNFrames:a}=e;s&&(i=`scale=${s.width}:${s.height}`);a&&a>1&&(i?i+=",":i="",i+=`select=not(mod(n\\,${a}))`);return{container:"rawvideo",outputArguments:[...i?["-vf",i]:[],"-an","-vcodec","rawvideo","-pix_fmt",r.name,"-f","rawvideo"],async*parse(e,t,i){if(!t||!i)throw new Error("error parsing rawvideo, unknown width and height");t=(null==s?void 0:s.width)||t,i=(null==s?void 0:s.height)||i;const o=r.computeLength(t,i);for(;;){const r=await(0,n.readLength)(e,o);yield{chunks:[r],width:t,height:i}}},findSyncFrame:o}},t.createRtpParser=function(...e){return{container:"rtsp",inputArguments:["-v","verbose","-rtsp_transport","tcp"],outputArguments:[...e,"-f","rtp"],parseDatagram:a(),findSyncFrame:o}},t.parseFragmentedMP4=c;var i=r(361),n=r(961);function o(e){return e}function s(e,t){return async function*(r){let n=[],o=0;for(;;){const s=r.read();if(!s){await(0,i.once)(r,"readable");continue}if(n.push(s),o+=s.length,o<e)continue;const a=Buffer.concat(n);null==t||t(a);const c=a.length%e,d=a.slice(0,a.length-c),u=a.slice(a.length-c);n=[u],o=u.length,yield{chunks:[d]}}}}function a(){return async function*(e,t,r,n){for(;;){const[t]=await(0,i.once)(e,"message");yield{chunks:[t],type:n}}}}async function*c(e){for(;;){const t=await(0,n.readLength)(e,8),r=t.readInt32BE(0)-8,i=t.slice(4).toString(),o=await(0,n.readLength)(e,r);yield{header:t,length:r,type:i,data:o}}}const d={name:"yuv420p",computeLength:(e,t)=>e*t*1.5};t.PIXEL_FORMAT_YUV420P=d;t.PIXEL_FORMAT_RGB24={name:"rgb24",computeLength:(e,t)=>e*t*3}},653:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.startRFC4571Parser=async function(e,t){var r,n;let c=!0;const u=new o.EventEmitter,l=await d.convertMediaObjectToJSON(e,"x-scrypted/x-rfc4571"),{url:p,sdp:m}=l,h=parseInt(null===(r=m.match(/m=audio.* ([0-9]+)/))||void 0===r?void 0:r[1]),f=parseInt(null===(n=m.match(/m=video.* ([0-9]+)/))||void 0===n?void 0:n[1]),g=new URL(p);if(!g.protocol.startsWith("tcp"))throw new Error("rfc4751 url must be tcp");const v=s.default.connect(parseInt(g.port),g.hostname),y=()=>{c&&(u.emit("killed"),u.emit("error",new Error("killed"))),c=!1,v.destroy()};return v.on("close",y),v.on("error",y),(async()=>{for(;;){const e=await(0,i.readLength)(v,2),t=e.readInt16BE(0),r=await(0,i.readLength)(v,t),n=127&r[1],o=Buffer.alloc(2);o[0]=a.RTSP_FRAME_MAGIC,n===h?o[1]=0:n===f&&(o[1]=2);const s={chunks:[o,e,r]};u.emit("rtsp",s)}})().finally(y),{sdp:Promise.resolve([Buffer.from(m)]),inputAudioCodec:"pcm",inputVideoCodec:"h264",inputVideoResolution:void 0,isActive:()=>c,kill:y,mediaStreamOptions:l.mediaStreamOptions,on(e,t){return u.on(e,t),this},once(e,t){return u.once(e,t),this},removeListener(e,t){return u.removeListener(e,t),this}}};var i=r(961),n=c(r(510)),o=r(781),s=c(r(808)),a=r(175);function c(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:d}=n.default},510:(e,t,r)=>{"use strict";var i=Object.create?function(e,t,r,i){void 0===i&&(i=r),Object.defineProperty(e,i,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]},n=function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.MixinDeviceBase=t.ScryptedDeviceBase=void 0,n(r(632),t);const o=r(632);class ScryptedDeviceBase extends o.DeviceBase{constructor(e){super(),this.nativeId=e}get storage(){return this._storage||(this._storage=deviceManager.getDeviceStorage(this.nativeId)),this._storage}get log(){return this._log||(this._log=deviceManager.getDeviceLogger(this.nativeId)),this._log}get console(){return this._console||(this._console=deviceManager.getDeviceConsole(this.nativeId)),this._console}_lazyLoadDeviceState(){this._deviceState||(this.nativeId?this._deviceState=deviceManager.getDeviceState(this.nativeId):this._deviceState=deviceManager.getDeviceState())}onDeviceEvent(e,t){return deviceManager.onDeviceEvent(this.nativeId,e,t)}}t.ScryptedDeviceBase=ScryptedDeviceBase;class MixinDeviceBase extends o.DeviceBase{constructor(e,t,r,i,n){super(),this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=i,this._mixinStorageSuffix=n,this._listeners=new Set,this._deviceState=r}get storage(){if(!this._storage){const e=this._mixinStorageSuffix,t=this.id+(e?":"+e:"");this._storage=deviceManager.getMixinStorage(t,this.mixinProviderNativeId)}return this._storage}get console(){return this._console||(deviceManager.getMixinConsole?this._console=deviceManager.getMixinConsole(this.id,this.mixinProviderNativeId):this._console=deviceManager.getDeviceConsole(this.mixinProviderNativeId)),this._console}onDeviceEvent(e,t){return deviceManager.onMixinEvent(this.id,this,e,t)}_lazyLoadDeviceState(){}manageListener(e){this._listeners.add(e)}release(){for(const e of this._listeners)e.removeListener()}}t.MixinDeviceBase=MixinDeviceBase,function(){function e(e){return function(){return this._lazyLoadDeviceState(),this._deviceState[e]}}function t(e){return function(t){this._lazyLoadDeviceState(),this._deviceState[e]=t}}for(var r of Object.values(o.ScryptedInterfaceProperty))Object.defineProperty(ScryptedDeviceBase.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(MixinDeviceBase.prototype,r,{set:t(r),get:e(r)})}();let s={};try{s=Object.assign(s,{log:deviceManager.getDeviceLogger(void 0),deviceManager,endpointManager,mediaManager,systemManager,pluginHostAPI})}catch(e){console.error("sdk initialization error, import @scrypted/sdk/types instead",e)}t.default=s},632:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ScryptedMimeTypes=t.ScryptedInterface=t.MediaPlayerState=t.LockState=t.ThermostatMode=t.TemperatureUnit=t.FanMode=t.HumidityMode=t.ScryptedDeviceType=t.ScryptedInterfaceDescriptors=t.ScryptedInterfaceProperty=t.DeviceBase=void 0;t.DeviceBase=class DeviceBase{},function(e){e.id="id",e.info="info",e.interfaces="interfaces",e.mixins="mixins",e.name="name",e.providedInterfaces="providedInterfaces",e.providedName="providedName",e.providedRoom="providedRoom",e.providedType="providedType",e.providerId="providerId",e.room="room",e.type="type",e.on="on",e.brightness="brightness",e.colorTemperature="colorTemperature",e.rgb="rgb",e.hsv="hsv",e.running="running",e.paused="paused",e.docked="docked",e.thermostatActiveMode="thermostatActiveMode",e.thermostatAvailableModes="thermostatAvailableModes",e.thermostatMode="thermostatMode",e.thermostatSetpoint="thermostatSetpoint",e.thermostatSetpointHigh="thermostatSetpointHigh",e.thermostatSetpointLow="thermostatSetpointLow",e.temperature="temperature",e.temperatureUnit="temperatureUnit",e.humidity="humidity",e.lockState="lockState",e.entryOpen="entryOpen",e.batteryLevel="batteryLevel",e.online="online",e.updateAvailable="updateAvailable",e.fromMimeType="fromMimeType",e.toMimeType="toMimeType",e.binaryState="binaryState",e.intrusionDetected="intrusionDetected",e.powerDetected="powerDetected",e.audioDetected="audioDetected",e.motionDetected="motionDetected",e.ambientLight="ambientLight",e.occupied="occupied",e.flooded="flooded",e.ultraviolet="ultraviolet",e.luminance="luminance",e.position="position",e.humiditySetting="humiditySetting",e.fan="fan"}(t.ScryptedInterfaceProperty||(t.ScryptedInterfaceProperty={})),t.ScryptedInterfaceDescriptors={ScryptedDevice:{name:"ScryptedDevice",methods:["listen","probe","setName","setRoom","setType"],properties:["id","info","interfaces","mixins","name","providedInterfaces","providedName","providedRoom","providedType","providerId","room","type"]},ScryptedPlugin:{name:"ScryptedPlugin",methods:["getPluginJson"],properties:[]},OnOff:{name:"OnOff",methods:["turnOff","turnOn"],properties:["on"]},Brightness:{name:"Brightness",methods:["setBrightness"],properties:["brightness"]},ColorSettingTemperature:{name:"ColorSettingTemperature",methods:["getTemperatureMaxK","getTemperatureMinK","setColorTemperature"],properties:["colorTemperature"]},ColorSettingRgb:{name:"ColorSettingRgb",methods:["setRgb"],properties:["rgb"]},ColorSettingHsv:{name:"ColorSettingHsv",methods:["setHsv"],properties:["hsv"]},Notifier:{name:"Notifier",methods:["sendNotification"],properties:[]},StartStop:{name:"StartStop",methods:["start","stop"],properties:["running"]},Pause:{name:"Pause",methods:["pause","resume"],properties:["paused"]},Dock:{name:"Dock",methods:["dock"],properties:["docked"]},TemperatureSetting:{name:"TemperatureSetting",methods:["setThermostatMode","setThermostatSetpoint","setThermostatSetpointHigh","setThermostatSetpointLow"],properties:["thermostatActiveMode","thermostatAvailableModes","thermostatMode","thermostatSetpoint","thermostatSetpointHigh","thermostatSetpointLow"]},Thermometer:{name:"Thermometer",methods:["setTemperatureUnit"],properties:["temperature","temperatureUnit"]},HumiditySensor:{name:"HumiditySensor",methods:[],properties:["humidity"]},Camera:{name:"Camera",methods:["getPictureOptions","takePicture"],properties:[]},VideoCamera:{name:"VideoCamera",methods:["getVideoStream","getVideoStreamOptions"],properties:[]},VideoCameraConfiguration:{name:"VideoCameraConfiguration",methods:["setVideoStreamOptions"],properties:[]},Intercom:{name:"Intercom",methods:["startIntercom","stopIntercom"],properties:[]},Lock:{name:"Lock",methods:["lock","unlock"],properties:["lockState"]},PasswordStore:{name:"PasswordStore",methods:["addPassword","getPasswords","removePassword"],properties:[]},Authenticator:{name:"Authenticator",methods:["checkPassword"],properties:[]},Scene:{name:"Scene",methods:["activate","deactivate","isReversible"],properties:[]},Entry:{name:"Entry",methods:["closeEntry","openEntry"],properties:[]},EntrySensor:{name:"EntrySensor",methods:[],properties:["entryOpen"]},DeviceProvider:{name:"DeviceProvider",methods:["getDevice"],properties:[]},DeviceDiscovery:{name:"DeviceDiscovery",methods:["discoverDevices"],properties:[]},DeviceCreator:{name:"DeviceCreator",methods:["createDevice","getCreateDeviceSettings"],properties:[]},Battery:{name:"Battery",methods:[],properties:["batteryLevel"]},Refresh:{name:"Refresh",methods:["getRefreshFrequency","refresh"],properties:[]},MediaPlayer:{name:"MediaPlayer",methods:["getMediaStatus","load","seek","skipNext","skipPrevious"],properties:[]},Online:{name:"Online",methods:[],properties:["online"]},SoftwareUpdate:{name:"SoftwareUpdate",methods:["checkForUpdate","installUpdate"],properties:["updateAvailable"]},BufferConverter:{name:"BufferConverter",methods:["convert"],properties:["fromMimeType","toMimeType"]},Settings:{name:"Settings",methods:["getSettings","putSetting"],properties:[]},BinarySensor:{name:"BinarySensor",methods:[],properties:["binaryState"]},IntrusionSensor:{name:"IntrusionSensor",methods:[],properties:["intrusionDetected"]},PowerSensor:{name:"PowerSensor",methods:[],properties:["powerDetected"]},AudioSensor:{name:"AudioSensor",methods:[],properties:["audioDetected"]},MotionSensor:{name:"MotionSensor",methods:[],properties:["motionDetected"]},AmbientLightSensor:{name:"AmbientLightSensor",methods:[],properties:["ambientLight"]},OccupancySensor:{name:"OccupancySensor",methods:[],properties:["occupied"]},FloodSensor:{name:"FloodSensor",methods:[],properties:["flooded"]},UltravioletSensor:{name:"UltravioletSensor",methods:[],properties:["ultraviolet"]},LuminanceSensor:{name:"LuminanceSensor",methods:[],properties:["luminance"]},PositionSensor:{name:"PositionSensor",methods:[],properties:["position"]},Readme:{name:"Readme",methods:["getReadmeMarkdown"],properties:[]},OauthClient:{name:"OauthClient",methods:["getOauthUrl","onOauthCallback"],properties:[]},MixinProvider:{name:"MixinProvider",methods:["canMixin","getMixin","releaseMixin"],properties:[]},HttpRequestHandler:{name:"HttpRequestHandler",methods:["onRequest"],properties:[]},EngineIOHandler:{name:"EngineIOHandler",methods:["onConnection"],properties:[]},PushHandler:{name:"PushHandler",methods:["onPush"],properties:[]},Program:{name:"Program",methods:["run"],properties:[]},Scriptable:{name:"Scriptable",methods:["eval","loadScripts","saveScript"],properties:[]},ObjectDetector:{name:"ObjectDetector",methods:["getDetectionInput","getObjectTypes"],properties:[]},ObjectDetection:{name:"ObjectDetection",methods:["detectObjects","getDetectionModel"],properties:[]},HumiditySetting:{name:"HumiditySetting",methods:["setHumidity"],properties:["humiditySetting"]},Fan:{name:"Fan",methods:["setFan"],properties:["fan"]}},function(e){e.Builtin="Builtin",e.Camera="Camera",e.Fan="Fan",e.Light="Light",e.Switch="Switch",e.Outlet="Outlet",e.Sensor="Sensor",e.Scene="Scene",e.Program="Program",e.Automation="Automation",e.Vacuum="Vacuum",e.Notifier="Notifier",e.Thermostat="Thermostat",e.Lock="Lock",e.PasswordControl="PasswordControl",e.Display="Display",e.Speaker="Speaker",e.Event="Event",e.Entry="Entry",e.Garage="Garage",e.DeviceProvider="DeviceProvider",e.DataSource="DataSource",e.API="API",e.Doorbell="Doorbell",e.Irrigation="Irrigation",e.Valve="Valve",e.Person="Person",e.Unknown="Unknown"}(t.ScryptedDeviceType||(t.ScryptedDeviceType={})),function(e){e.Humidify="Humidify",e.Dehumidify="Dehumidify",e.Auto="Auto",e.Off="Off"}(t.HumidityMode||(t.HumidityMode={})),function(e){e.Auto="Auto",e.Manual="Manual"}(t.FanMode||(t.FanMode={})),function(e){e.C="C",e.F="F"}(t.TemperatureUnit||(t.TemperatureUnit={})),function(e){e.Off="Off",e.Cool="Cool",e.Heat="Heat",e.HeatCool="HeatCool",e.Auto="Auto",e.FanOnly="FanOnly",e.Purifier="Purifier",e.Eco="Eco",e.Dry="Dry",e.On="On"}(t.ThermostatMode||(t.ThermostatMode={})),function(e){e.Locked="Locked",e.Unlocked="Unlocked",e.Jammed="Jammed"}(t.LockState||(t.LockState={})),function(e){e.Idle="Idle",e.Playing="Playing",e.Paused="Paused",e.Buffering="Buffering"}(t.MediaPlayerState||(t.MediaPlayerState={})),function(e){e.ScryptedDevice="ScryptedDevice",e.ScryptedPlugin="ScryptedPlugin",e.OnOff="OnOff",e.Brightness="Brightness",e.ColorSettingTemperature="ColorSettingTemperature",e.ColorSettingRgb="ColorSettingRgb",e.ColorSettingHsv="ColorSettingHsv",e.Notifier="Notifier",e.StartStop="StartStop",e.Pause="Pause",e.Dock="Dock",e.TemperatureSetting="TemperatureSetting",e.Thermometer="Thermometer",e.HumiditySensor="HumiditySensor",e.Camera="Camera",e.VideoCamera="VideoCamera",e.VideoCameraConfiguration="VideoCameraConfiguration",e.Intercom="Intercom",e.Lock="Lock",e.PasswordStore="PasswordStore",e.Authenticator="Authenticator",e.Scene="Scene",e.Entry="Entry",e.EntrySensor="EntrySensor",e.DeviceProvider="DeviceProvider",e.DeviceDiscovery="DeviceDiscovery",e.DeviceCreator="DeviceCreator",e.Battery="Battery",e.Refresh="Refresh",e.MediaPlayer="MediaPlayer",e.Online="Online",e.SoftwareUpdate="SoftwareUpdate",e.BufferConverter="BufferConverter",e.Settings="Settings",e.BinarySensor="BinarySensor",e.IntrusionSensor="IntrusionSensor",e.PowerSensor="PowerSensor",e.AudioSensor="AudioSensor",e.MotionSensor="MotionSensor",e.AmbientLightSensor="AmbientLightSensor",e.OccupancySensor="OccupancySensor",e.FloodSensor="FloodSensor",e.UltravioletSensor="UltravioletSensor",e.LuminanceSensor="LuminanceSensor",e.PositionSensor="PositionSensor",e.Readme="Readme",e.OauthClient="OauthClient",e.MixinProvider="MixinProvider",e.HttpRequestHandler="HttpRequestHandler",e.EngineIOHandler="EngineIOHandler",e.PushHandler="PushHandler",e.Program="Program",e.Scriptable="Scriptable",e.ObjectDetector="ObjectDetector",e.ObjectDetection="ObjectDetection",e.HumiditySetting="HumiditySetting",e.Fan="Fan"}(t.ScryptedInterface||(t.ScryptedInterface={})),function(e){e.AcceptUrlParameter="accept-url",e.Url="text/x-uri",e.InsecureLocalUrl="text/x-insecure-local-uri",e.LocalUrl="text/x-local-uri",e.PushEndpoint="text/x-push-endpoint",e.MediaStreamUrl="text/x-media-url",e.FFmpegInput="x-scrypted/x-ffmpeg-input",e.RTCAVSignalingPrefix="x-scrypted-rtc-signaling-",e.RTCAVOffer="x-scrypted/x-rtc-av-offer",e.RTCAVAnswer="x-scrypted/x-rtc-av-answer",e.RTCAVSignalingOfferSetup="x-scrypted/x-rtc-av-signalling-offer-setup",e.SchemePrefix="x-scrypted/x-scrypted-scheme-",e.MediaObject="x-scrypted/x-scrypted-media-object"}(t.ScryptedMimeTypes||(t.ScryptedMimeTypes={}))},568:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,r,i){var s,a;const c=!!n.default.env.SCRYPTED_FFMPEG_NOISY||!(null==i||!i.getItem("SCRYPTED_FFMPEG_NOISY"));function d(e){const i=n=>{const s=n.toString();for(const e of o)if(-1!==s.indexOf(e))return;if(!(c||r||-1===s.indexOf("frame=")&&-1===s.indexOf("size=")))return e(s),e("video/audio detected, discarding further input"),t.stdout.removeListener("data",i),void t.stderr.removeListener("data",i);e(s)};return i}null===(s=t.stdout)||void 0===s||s.on("data",d(e.log)),null===(a=t.stderr)||void 0===a||a.on("data",d(e.error)),t.on("exit",(()=>e.log("ffmpeg exited")))},t.safePrintFFmpegArguments=function(e,t){const r=[];for(const e of t)try{const t=new URL(e);r.push(`${t.protocol}[REDACTED]`)}catch(t){r.push(e)}e.log(r.join(" "))};var i,n=(i=r(282))&&i.__esModule?i:{default:i};const o=["decode_slice_header error","no frame!","non-existing PPS"]},81:e=>{"use strict";e.exports=require("child_process")},113:e=>{"use strict";e.exports=require("crypto")},891:e=>{"use strict";e.exports=require("dgram")},361:e=>{"use strict";e.exports=require("events")},808:e=>{"use strict";e.exports=require("net")},282:e=>{"use strict";e.exports=require("process")},781:e=>{"use strict";e.exports=require("stream")}},t={};function r(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,r),o.exports}var i={};(()=>{"use strict";var e=i;Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t,n=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=f(t);if(r&&r.has(e))return r.get(e);var i={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var s=n?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(i,o,s):i[o]=e[o]}i.default=e,r&&r.set(e,i);return i}(r(510)),o=r(361),s=r(567),a=r(201),c=r(129),d=r(454),u=r(769),l=r(175),p=(t=r(808))&&t.__esModule?t:{default:t},m=r(961),h=r(653);function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(f=function(e){return e?r:t})(e)}function g(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}const{mediaManager:v,log:y,systemManager:S,deviceManager:b}=n.default,P=1e4,M="prebufferDuration",w="sendKeyframe",I="Default",O="AAC or No Audio",x=`${O} (Copy)`,D="Compatible Audio",C="Other Audio",A=["aac","mp3","mp2","opus"],k="-fflags +genpts",T=[O,D,C],_=["mpegts","mp4","rtsp"];class PrebufferSession{constructor(e,t,r,i){g(this,"prebuffers",{mp4:[],mpegts:[],rtsp:[]}),g(this,"detectedIdrInterval",0),g(this,"prevIdr",0),g(this,"audioDisabled",!1),g(this,"activeClients",0),this.mixin=e,this.streamName=t,this.streamId=r,this.stopInactive=i,this.storage=e.storage,this.console=e.console,this.mixinDevice=e.mixinDevice,this.audioConfigurationKey="audioConfiguration-"+this.streamId,this.ffmpegInputArgumentsKey="ffmpegInputArguments-"+this.streamId,this.rebroadcastModeKey="rebroadcastMode-"+this.streamId,this.lastDetectedAudioCodecKey="lastDetectedAudioCodec-"+this.streamId}clearPrebuffers(){this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.rtsp=[]}ensurePrebufferSession(){this.parserSessionPromise||this.mixin.released||(this.console.log(this.streamName,"prebuffer session started"),this.parserSessionPromise=this.startPrebufferSession(),this.parserSessionPromise.catch((()=>this.parserSessionPromise=void 0)))}getAudioConfig(){let e=this.storage.getItem(this.audioConfigurationKey)||"";T.find((t=>e.startsWith(t)))||(e="");const t=-1!==e.indexOf(O),r=-1!==e.indexOf(D),i=-1!==e.indexOf(C);return{isUsingDefaultAudioConfig:!(t||r||i),aacAudio:t,compatibleAudio:r,reencodeAudio:i}}async getMixinSettings(){const e=[],t=this.parserSession;let r=0,i=0;for(const e of this.prebuffers.mp4){i=i||e.time;for(const t of e.chunk.chunks)r+=t.byteLength}const n=Date.now()-i,o=Math.round(r/n*8),s=this.streamName?`Rebroadcast: ${this.streamName}`:"Rebroadcast";var a,c,d;(e.push({title:"Audio Codec Transcoding",group:s,description:"Configuring your camera to output AAC, MP3, MP2, or Opus is recommended. PCM/G711 cameras should set this to Transcode.",type:"string",key:this.audioConfigurationKey,value:this.storage.getItem(this.audioConfigurationKey)||I,choices:[I,x,"Compatible Audio (Copy)","Other Audio (Transcode)"]},{title:"FFmpeg Input Arguments Prefix",group:s,description:"Optional/Advanced: Additional input arguments to pass to the ffmpeg command. These will be placed before the input arguments.",key:this.ffmpegInputArgumentsKey,value:this.storage.getItem(this.ffmpegInputArgumentsKey),placeholder:k,choices:[k,"-use_wallclock_as_timestamps 1","-v verbose"],combobox:!0},{title:"Rebroadcast Mode",group:s,description:"THIS FEATURE IS IN TESTING. DO NOT CHANGE THIS FROM MPEG-TS. The stream format to use when rebroadcasting.",placeholder:"MPEG-TS",choices:["MPEG-TS","RTSP"],key:this.rebroadcastModeKey,value:this.storage.getItem(this.rebroadcastModeKey)||"MPEG-TS"}),t)?e.push({key:"detectedResolution",group:s,title:"Detected Resolution and Bitrate",readonly:!0,value:`${(null==t||null===(a=t.inputVideoResolution)||void 0===a?void 0:a[0])||"unknown"} @ ${o||"unknown"} Kb/s`,description:"Configuring your camera to 1920x1080, 2000Kb/S, Variable Bit Rate, is recommended."},{key:"detectedCodec",group:s,title:"Detected Video/Audio Codecs",readonly:!0,value:((null==t||null===(c=t.inputVideoCodec)||void 0===c?void 0:c.toString())||"unknown")+"/"+((null==t||null===(d=t.inputAudioCodec)||void 0===d?void 0:d.toString())||"unknown"),description:"Configuring your camera to H264 video and AAC/MP3/MP2/Opus audio is recommended."},{key:"detectedKeyframe",group:s,title:"Detected Keyframe Interval",description:"Configuring your camera to 4 seconds is recommended (IDR aka Frame Interval = FPS * 4 seconds).",readonly:!0,value:((this.detectedIdrInterval||0)/1e3).toString()||"none"}):e.push({title:"Status",group:s,key:"status",description:"Rebroadcast is currently idle and will be started automatically on demand.",value:"Idle",readonly:!0});return e}async startPrebufferSession(){var e,t,r,i,o,s,d;this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.rtsp=[];const u=parseInt(this.storage.getItem(M))||P;let p;try{p=(await this.mixinDevice.getVideoStreamOptions()).find((e=>e.id===this.streamId))}catch(e){}const m=null===(null===(e=p)||void 0===e?void 0:e.audio),f=null===(t=p)||void 0===t||null===(r=t.audio)||void 0===r?void 0:r.codec,{isUsingDefaultAudioConfig:g,aacAudio:S,compatibleAudio:b,reencodeAudio:w}=this.getAudioConfig();let I=this.storage.getItem(this.lastDetectedAudioCodecKey)||void 0;"null"===I&&(I=null);let O=!1;m||f||!g||void 0!==I||(this.console.warn("Camera did not report an audio codec, muting the audio stream and probing the codec."),O=!0),!m&&f&&void 0!==I&&I!==f&&this.console.warn("Audio codec plugin reported vs detected mismatch",f,I);const x=void 0===I?null==f?void 0:f.toLowerCase():null===(i=I)||void 0===i?void 0:i.toLowerCase(),D=!A.includes(x);O||!1===(null===(o=p)||void 0===o?void 0:o.userConfigurable)||m||D&&(g&&y.a(`${this.mixin.name} is using the ${x} audio codec. Configuring your Camera to use AAC, MP3, MP2, or Opus audio is recommended. If this is not possible, Select 'Transcode Audio' in the camera stream's Rebroadcast settings to suppress this alert.`),this.console.warn("Configure your camera to output AAC, MP3, MP2, or Opus audio. Suboptimal audio codec in use:",x));const C=["-bsf:a","aac_adtstoasc"],T=[];let E;this.audioDisabled=!1;const R=null===I;let L=!1;var B;!O&&g&&D&&(!1===(null===(B=p)||void 0===B?void 0:B.userConfigurable)?this.console.log("camera reports it is not user configurable. transcoding due to incompatible codec",x):this.console.log("camera audio transcoding due to incompatible codec. configure the camera to use a compatible codec if possible."),L=!0);if(m||O)E=["-an"],this.audioDisabled=!0;else if(w||L)E=["-bsf:a","aac_adtstoasc","-acodec","libfdk_aac","-ar","8k","-b:a","100k","-bufsize","400k","-ac","1","-profile:a","aac_low","-flags","+global_header"];else if(S||R)E=["-acodec","copy"],E.push(...C);else if(b)E=["-acodec","copy"],E.push(...T);else{E=["-acodec","copy"];const e="aac"===x?C:T;E.push(...e)}const j=["-vcodec","copy"],F={console:this.console,timeout:6e4,parsers:{}},V="RTSP"===this.storage.getItem(this.rebroadcastModeKey);if(this.console.log("rebroadcast mode:",V?"rtsp":"mpegts"),V){const e=(0,l.createRtspParser)();this.sdp=e.sdp,F.parsers.rtsp=e}else F.parsers.mpegts=(0,c.createMpegTsParser)({vcodec:j,acodec:E}),F.parsers.mp4=(0,c.createFragmentedMp4Parser)({vcodec:j,acodec:E});this.parsers=F.parsers;const H=await this.mixinDevice.getVideoStream(p);let N,U;if(this.storage.removeItem(this.lastDetectedAudioCodecKey),"x-scrypted/x-rfc4571"===H.mimeType)N=await(0,h.startRFC4571Parser)(H,F),this.sdp=N.sdp.then((e=>Buffer.concat(e).toString()));else{const e=await v.convertMediaObjectToBuffer(H,n.ScryptedMimeTypes.FFmpegInput),t=JSON.parse(e.toString());U=t.mediaStreamOptions;const r=this.storage.getItem(this.ffmpegInputArgumentsKey)||k;t.inputArguments.unshift(...r.split(" ")),N=await(0,a.startParserSession)(t,F)}if(N.inputAudioCodec?A.includes(null===(s=N.inputAudioCodec)||void 0===s?void 0:s.toLowerCase())?this.console.log("Detected audio codec is mp4/mpegts compatible.",N.inputAudioCodec):this.console.log("Detected audio codec is not mp4/mpegts compatible.",N.inputAudioCodec):this.console.log("No audio stream detected."),this.storage.setItem(this.lastDetectedAudioCodecKey,N.inputAudioCodec||"null"),"h264"!==N.inputVideoCodec&&this.console.error("Video codec is not h264. If there are errors, try changing your camera's encoder output."),O)return this.console.warn("Audio probe complete, ending rebroadcast session and restarting with detected codecs."),N.kill(),this.startPrebufferSession();if(this.parserSession=N,null!==(d=U)&&void 0!==d&&d.refreshAt){let e,t=U;const r=async()=>{if(!N.isActive)return;const e=await this.mixinDevice.getVideoStream(t),r=await v.convertMediaObjectToBuffer(e,n.ScryptedMimeTypes.FFmpegInput),o=JSON.parse(r.toString());t=o.mediaStreamOptions,i(t)},i=t=>{const i=t.refreshAt-Date.now()-3e4;this.console.log("refreshing media stream in",i),e=setTimeout(r,i)};i(t),N.once("killed",(()=>clearTimeout(e)))}N.once("killed",(()=>{this.parserSessionPromise=void 0,this.parserSession===N&&(this.parserSession=void 0)}));for(const e of _){let t=0;N.on(e,(r=>{const i=this.prebuffers[e],n=Date.now();for("mdat"===r.type&&(this.prevIdr&&(this.detectedIdrInterval=n-this.prevIdr),this.prevIdr=n),i.push({time:n,chunk:r});i.length&&i[0].time<n-u;)i.shift(),t++;t>1e3&&(this.prebuffers[e]=i.slice(),t=0)}))}return N}printActiveClients(){this.console.log(this.streamName,"active rebroadcast clients:",this.activeClients)}inactivityCheck(e,t){this.printActiveClients(),this.stopInactive&&(this.activeClients||this.inactivityTimeout&&!t||(clearTimeout(this.inactivityTimeout),this.inactivityTimeout=setTimeout((()=>{this.activeClients||(this.console.log(this.streamName,"terminating rebroadcast due to inactivity"),e.kill())}),3e4)))}async getVideoStream(e){var t,r;this.ensurePrebufferSession();const i=await this.parserSessionPromise,n="false"!==this.storage.getItem(w),o=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0);this.console.log(this.streamName,"prebuffer request started");const s="RTSP"===this.storage.getItem(this.rebroadcastModeKey),c=s?"rtsp":"mpegts",d=this.parsers[null==e?void 0:e.container]?null==e?void 0:e.container:c,p=Object.assign({},i.mediaStreamOptions);p.prebuffer=o;const{reencodeAudio:m}=this.getAudioConfig();s?p.audio={codec:null==i?void 0:i.inputAudioCodec}:this.audioDisabled?p.audio=null:p.audio=m?{codec:"aac",encoder:"libfdk_aac",profile:"aac_low"}:{codec:null==i?void 0:i.inputAudioCodec},p.video&&null!==(t=i.inputVideoResolution)&&void 0!==t&&t[2]&&null!==(r=i.inputVideoResolution)&&void 0!==r&&r[3]&&Object.assign(p.video,{width:parseInt(i.inputVideoResolution[2]),height:parseInt(i.inputVideoResolution[3])});const h=Date.now();let f=0;const g=this.prebuffers[d];for(const e of g)if(!(e.time<h-o))for(const t of e.chunk.chunks)f+=t.length;const y=Math.max(5e5,f).toString(),S=await(async t=>{const r=this.prebuffers[t];let n,s;if("rtsp"===t){this.sdp.then((e=>console.log(e)));const e=await(0,u.listenZeroSingleClient)();n=e.clientPromise.then((async e=>{let t=await this.sdp;const r=new l.RtspServer(e,t);return r.console=this.console,await r.handlePlayback(),e})),s=e.url.replace("tcp://","rtsp://")}else{const e=await(0,u.listenZeroSingleClient)();n=e.clientPromise,s=`tcp://127.0.0.1:${e.port}`}return(0,a.handleRebroadcasterClient)(n,{console:this.console,connect:(n,s)=>{this.activeClients++,this.printActiveClients();const a=Date.now(),c=e=>{n(e)>1e8&&(this.console.log("more than 100MB has been buffered, did downstream die? killing connection.",this.streamName),d())},d=()=>{s(),this.console.log(this.streamName,"prebuffer request ended"),i.removeListener(t,c),i.removeListener("killed",d)};i.on(t,c),i.once("killed",d);for(const e of r)e.time<a-o||c(e.chunk);return()=>{this.activeClients--,this.inactivityCheck(i,!1!==(null==e?void 0:e.refresh)),d()}}}),s})(d),b={url:S,container:d,inputArguments:["-analyzeduration","0","-probesize",y,...this.parsers[d].inputArguments||[],"-f",this.parsers[d].container,"-i",S],mediaStreamOptions:p};return v.createFFmpegMediaObject(b)}}class PrebufferMixin extends s.SettingsMixinDeviceBase{constructor(e,t,r,i){super(e,r,{providerNativeId:i,mixinDeviceInterfaces:t,group:"Prebuffer Settings",groupKey:"prebuffer"}),g(this,"released",!1),g(this,"sessions",new Map),this.delayStart()}delayStart(){this.console.log("prebuffer sessions starting in 5 seconds"),setTimeout((()=>this.ensurePrebufferSessions()),5e3)}async getVideoStream(e){await this.ensurePrebufferSessions();const t=null==e?void 0:e.id;let r=this.sessions.get(t);return!r||null!=e&&e.directMediaStream?this.mixinDevice.getVideoStream(e):(r.ensurePrebufferSession(),await r.parserSessionPromise,r=this.sessions.get(t),r?r.getVideoStream(e):this.mixinDevice.getVideoStream(e))}async ensurePrebufferSessions(){const e=await this.mixinDevice.getVideoStreamOptions(),t=this.getEnabledMediaStreamOptions(e),r=t?t.map((e=>e.id)):[void 0],i=(null==e?void 0:e.map((e=>e.id)))||[void 0];if("true"!==this.storage.getItem("warnedCloud")){(null==e?void 0:e.find((e=>"cloud"===e.source)))&&(this.storage.setItem("warnedCloud","true"),y.a(`${this.name} is a cloud camera. Prebuffering maintains a persistent stream and will not enabled by default. You must enable the Prebuffer stream manually.`))}const s=this.mixinDeviceInterfaces.includes(n.ScryptedInterface.Battery);let a=0;const c=i.length;for(const t of i){let i=this.sessions.get(t);if(!i){var d;const n=null==e?void 0:e.find((e=>e.id===t));null!=n&&n.prebuffer&&y.a(`Prebuffer is already available on ${this.name}. If this is a grouped device, disable the Rebroadcast extension.`);const u=null==n?void 0:n.name,l=!r.includes(t);if(i=new PrebufferSession(this,u,t,s||l),this.sessions.set(t,i),t===(null==e||null===(d=e[0])||void 0===d?void 0:d.id)&&this.sessions.set(void 0,i),s){this.console.log("camera is battery powered, prebuffering and rebroadcasting will only work on demand.");continue}if(l){this.console.log("stream",u,"will be rebroadcast on demand.");continue}(async()=>{for(;this.sessions.get(t)===i&&!this.released;){i.ensurePrebufferSession();try{const e=await i.parserSessionPromise;a++,this.online=a==c,await(0,o.once)(e,"killed"),this.console.error("prebuffer session ended")}catch(e){this.console.error("prebuffer session ended with error",e)}finally{a--,this.online=a==c}this.console.log("restarting prebuffer session in 5 seconds"),await new Promise((e=>setTimeout(e,5e3)))}this.console.log("exiting prebuffer session (released or restarted with new configuration)")})()}}b.onMixinEvent(this.id,this.mixinProviderNativeId,n.ScryptedInterface.Settings,void 0)}async getMixinSettings(){const e=[];try{const t=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(t);(null==t?void 0:t.length)>0&&e.push({title:"Prebuffered Streams",description:"The streams to prebuffer. Enable only as necessary to reduce traffic.",key:"enabledStreams",value:r.map((e=>e.name||"")),choices:t.map((e=>e.name)),multiple:!0})}catch(e){throw this.console.error("error in getVideoStreamOptions",e),e}e.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:M,value:this.storage.getItem(M)||P.toString()},{title:"Start at Previous Keyframe",description:"Start live streams from the previous key frame. Improves startup time.",type:"boolean",key:w,value:("false"!==this.storage.getItem(w)).toString()});for(const t of new Set([...this.sessions.values()]))if(t)try{e.push(...await t.getMixinSettings())}catch(e){throw this.console.error("error in prebuffer session getMixinSettings",e),e}return e}async putMixinSetting(e,t){const r=this.sessions;this.sessions=new Map,"enabledStreams"===e?this.storage.setItem(e,JSON.stringify(t)):this.storage.setItem(e,t.toString());for(const e of r.values()){var i;null==e||null===(i=e.parserSessionPromise)||void 0===i||i.then((e=>e.kill()))}this.ensurePrebufferSessions()}getEnabledMediaStreamOptions(e){if(!e)return;try{const t=JSON.parse(this.storage.getItem("enabledStreams"));return e.filter((e=>t.includes(e.name)))}catch(e){}const t=e.find((e=>"cloud"!==e.source));return t?[t]:[]}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=this.getEnabledMediaStreamOptions(e);const r=parseInt(this.storage.getItem(M))||P;for(const n of e){var i;(null!==(i=this.sessions.get(n.id))&&void 0!==i&&i.parserSession||t.includes(n))&&(n.prebuffer=r)}return e}release(){this.console.log("prebuffer releasing if started"),this.released=!0;for(const t of this.sessions.values()){var e;t&&(t.clearPrebuffers(),null===(e=t.parserSessionPromise)||void 0===e||e.then((e=>{this.console.log("prebuffer released"),e.kill(),t.clearPrebuffers()})))}}}class PrebufferProvider extends d.AutoenableMixinProvider{constructor(e){super(e),this.fromMimeType="x-scrypted/x-rfc4571",this.toMimeType=n.ScryptedMimeTypes.FFmpegInput;for(const e of Object.keys(S.getSystemState())){var t;const r=S.getDeviceById(e);null!==(t=r.mixins)&&void 0!==t&&t.includes(this.id)&&r.getVideoStreamOptions()}const r=function(){var e=new Date;return e.setHours(24),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0),e.getTime()-(new Date).getTime()}()+72e5;this.log.i(`Rebroadcaster scheduled for restart at 2AM: ${Math.round(r/1e3/60)} minutes`),setTimeout((()=>b.requestRestart()),r)}async convert(e,t,r){var i,n;const o=JSON.parse(e.toString()),{url:s,sdp:a}=o,c=parseInt(null===(i=a.match(/m=audio.* ([0-9]+)/))||void 0===i?void 0:i[1]),d=parseInt(null===(n=a.match(/m=video.* ([0-9]+)/))||void 0===n?void 0:n[1]),h=new URL(s);if(!h.protocol.startsWith("tcp"))throw new Error("rfc4751 url must be tcp");const{clientPromise:f,url:g}=await(0,u.listenZeroSingleClient)(),v={url:g,inputArguments:["-rtsp_transport","tcp","-max_delay","1000000","-i",g.replace("tcp","rtsp")]};return f.then((async e=>{const t=new l.RtspServer(e,a);t.console=this.console,await t.handlePlayback();const r=p.default.connect(parseInt(h.port),h.hostname);for(e.on("close",(()=>{r.destroy()})),r.on("close",(()=>{e.destroy()}));;){const i=(await(0,m.readLength)(r,2)).readInt16BE(0),n=await(0,m.readLength)(r,i),o=127&n[1];if(o===c)t.sendAudio(n,!1);else{if(o!==d)throw e.destroy(),r.destroy(),new Error("unknown payload type "+o);t.sendVideo(n,!1)}}})),Buffer.from(JSON.stringify(v))}async canMixin(e,t){return t.includes(n.ScryptedInterface.VideoCamera)?[n.ScryptedInterface.VideoCamera,n.ScryptedInterface.Settings,n.ScryptedInterface.Online]:null}async getMixin(e,t,r){return this.setHasEnabledMixin(r.id),new PrebufferMixin(e,t,r,this.nativeId)}async releaseMixin(e,t){t.online=!0,t.release()}}var E=new PrebufferProvider;e.default=E})();var n=exports="undefined"==typeof exports?{}:exports;for(var o in i)n[o]=i[o];i.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})})();
|
|
1
|
+
(()=>{var e={454:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{systemManager:o}=i.default,s="v4";class AutoenableMixinProvider extends i.ScryptedDeviceBase{constructor(e){var t,r,n;super(e),n={},(r="hasEnabledMixin")in(t=this)?Object.defineProperty(t,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[r]=n;try{this.hasEnabledMixin=JSON.parse(this.storage.getItem("hasEnabledMixin"))}catch(e){this.hasEnabledMixin={}}this.pluginsComponent=o.getComponent("plugins"),o.listen((async(e,t,r)=>{t.eventInterface!==i.ScryptedInterface.ScryptedDevice||t.property||this.maybeEnableMixin(e)}));for(const e of Object.keys(o.getSystemState())){const t=o.getDeviceById(e);this.maybeEnableMixin(t)}}async shouldEnableMixin(e){return!0}async maybeEnableMixin(e){var t;if(!e||null!==(t=e.mixins)&&void 0!==t&&t.includes(this.id))return;if(this.hasEnabledMixin[e.id]===s)return;if(!await this.canMixin(e.type,e.interfaces))return;if(!await this.shouldEnableMixin(e))return;this.log.i("auto enabling mixin for "+e.name);const r=(e.mixins||[]).slice();r.push(this.id);const i=await this.pluginsComponent;await i.setMixins(e.id,r),this.setHasEnabledMixin(e.id)}setHasEnabledMixin(e){this.hasEnabledMixin[e]!==s&&(this.hasEnabledMixin[e]=s,this.storage.setItem("hasEnabledMixin",JSON.stringify(this.hasEnabledMixin)))}}t.AutoenableMixinProvider=AutoenableMixinProvider},201:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createParserRebroadcaster=async function(e,t,r){return await g(Object.assign({},r,{connect:(r,i)=>(e.on(t,r),()=>{i()})}))},t.createRebroadcaster=g,t.handleRebroadcasterClient=v,t.parseAudioCodec=f,t.parseResolution=p,t.parseVideoCodec=h,t.startParserSession=async function(e,t){const{console:r}=t;let i,a=!0;const u=new s.EventEmitter;let m,g,v,y,S;u.on("error",(e=>r.error("rebroadcast error",e)));const b=new Promise(((e,t)=>{y=e,S=t}));function P(){var e;a&&(u.emit("killed"),u.emit("error",new Error("killed"))),a=!1,null==D||D.kill(),setTimeout((()=>null==D?void 0:D.kill("SIGKILL")),1e3),null===(e=S)||void 0===e||e(new Error("ffmpeg was killed before connecting to the rebroadcast session")),clearTimeout(i)}const M=e.inputArguments.slice();i=setTimeout(P,3e4);const w=e=>{let i;function n(){r.error("timeout waiting for data, killing parser session",e),P()}function o(){t.timeout&&(clearTimeout(i),i=setTimeout(n,t.timeout))}return u.once("killed",(()=>clearTimeout(i))),o(),{resetActivityTimer:o}};let I=!1;const O=["pipe","pipe","pipe"];let x=3;for(const e of Object.keys(t.parsers)){const i=t.parsers[e];if(i.parseDatagram){I=!0;const t=d.default.createSocket("udp4"),r=await(0,o.bindZero)(t),n=d.default.createSocket("udp4");await(0,o.bind)(n,r.port+1),u.once("killed",(()=>{t.close(),n.close()})),M.push(...i.outputArguments,r.url.replace("udp://","rtp://"));const{resetActivityTimer:s}=w(e);(async()=>{for await(const a of i.parseDatagram(t,parseInt(null===(r=v)||void 0===r?void 0:r[2]),parseInt(null===(n=v)||void 0===n?void 0:n[3]))){var r,n,o;null===(o=y)||void 0===o||o(void 0),u.emit(e,a),s()}})(),(async()=>{for await(const a of i.parseDatagram(n,parseInt(null===(t=v)||void 0===t?void 0:t[2]),parseInt(null===(r=v)||void 0===r?void 0:r[3]),"rtcp")){var t,r,o;null===(o=y)||void 0===o||o(void 0),u.emit(e,a),s()}})()}else if(i.tcpProtocol){const t=await(0,o.listenZeroSingleClient)(),n=new URL(i.tcpProtocol);n.port=t.port.toString(),M.push(...i.outputArguments,n.toString());const{resetActivityTimer:s}=w(e);(async()=>{const n=await t.clientPromise;try{for await(const t of i.parse(n,parseInt(null===(o=v)||void 0===o?void 0:o[2]),parseInt(null===(a=v)||void 0===a?void 0:a[3]))){var o,a,c;null===(c=y)||void 0===c||c(void 0),u.emit(e,t),s()}}catch(e){r.error("rebroadcast parse error",e),P()}})()}else M.push(...i.outputArguments,"pipe:"+x++),O.push("pipe")}I&&(M.push("-sdp_file","pipe:"+x++),O.push("pipe"));M.unshift("-hide_banner"),(0,c.safePrintFFmpegArguments)(r,M);const D=n.default.spawn(await l.getFFmpegPath(),M,{stdio:O});let C;(0,c.ffmpegLogInitialOutput)(r,D,void 0,null==t?void 0:t.storage),D.on("exit",P),C=I?new Promise((e=>{const t=[];D.stdio[x-1].on("data",(r=>{t.push(r),e(t)}))})):Promise.resolve([]);let A=0;return Object.keys(t.parsers).forEach((async e=>{const i=t.parsers[e];if(!i.parse||i.tcpProtocol)return;const n=D.stdio[3+A];A++;try{const{resetActivityTimer:t}=w(e);for await(const r of i.parse(n,parseInt(null===(o=v)||void 0===o?void 0:o[2]),parseInt(null===(s=v)||void 0===s?void 0:s[3]))){var o,s,a;null===(a=y)||void 0===a||a(void 0),u.emit(e,r),t()}}catch(e){r.error("rebroadcast parse error",e),P()}})),f(D).then((e=>m=e)),h(D).then((e=>g=e)),p(D).then((e=>v=e)),await b,y=void 0,S=void 0,clearTimeout(i),{sdp:C,inputAudioCodec:m,inputVideoCodec:g,inputVideoResolution:v,isActive:()=>a,kill:P,mediaStreamOptions:e.mediaStreamOptions||{id:void 0,name:void 0},on(e,t){return u.on(e,t),this},once(e,t){return u.once(e,t),this},removeListener(e,t){return u.removeListener(e,t),this}}};var i=r(808),n=u(r(81)),o=r(769),s=r(361),a=u(r(510)),c=r(833),d=u(r(891));function u(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:l}=a.default;async function p(e){return new Promise((t=>{const r=i=>{const n=i.toString(),o=/(([0-9]{2,5})x([0-9]{2,5}))/.exec(n);o&&(e.stdout.removeListener("data",r),e.stderr.removeListener("data",r),t(o))};e.stdout.on("data",r),e.stderr.on("data",r)}))}async function m(e,t){return new Promise((r=>{const i=n=>{const o=n.toString(),s=o.indexOf(`${t}: `);if(-1!==s){const n=o.substring(s+t.length+1).trim();let a=n.indexOf(" ");const c=n.indexOf(",");-1!==a&&c<a&&(a=c),-1!==a&&(e.stdout.removeListener("data",i),e.stderr.removeListener("data",i),r(n.substring(0,a)))}};e.stdout.on("data",i),e.stderr.on("data",i)}))}async function h(e){return m(e,"Video")}async function f(e){return m(e,"Audio")}async function g(e){let t,r=0;const n=()=>{null!=e&&e.idle&&(clearTimeout(t),t=setTimeout((()=>{0!==r?n():e.idle.callback()}),e.idle.timeout))};n();const s=(0,i.createServer)((t=>{v(t,e),t.once("close",(()=>{n(),r--})),n(),r++})),a=await(0,o.listenZero)(s);return{server:s,port:a,url:`tcp://127.0.0.1:${a}`,get clients(){return r}}}async function v(e,t){const r=await e;let i=!0;const n=()=>{r.removeAllListeners(),r.destroy();const e=o;o=void 0,null==e||e()};let o=null==t?void 0:t.connect((e=>{i&&(i=!1,e.startStream&&r.write(e.startStream));for(const t of e.chunks)r.write(t);return r.writableLength}),n);r.once("close",(()=>{n()})),r.on("error",(e=>{var r;return null==t||null===(r=t.console)||void 0===r?void 0:r.log("client stream ended")}))}},769:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.bind=async function(e,t){return e.bind(t),await(0,n.once)(e,"listening"),{port:t,url:`udp://127.0.0.1:${t}`}},t.bindUdp=c,t.bindZero=async function(e){return c(e,0)},t.createBindUdp=d,t.createBindZero=async function(){return d(0)},t.listenZero=a,t.listenZeroSingleClient=async function(){const e=new i.default.Server,t=await a(e),r=new Promise(((t,r)=>{const i=setTimeout((()=>{r(new Error("timeout waiting for client"))}),3e4);e.on("connection",(r=>{e.close(),clearTimeout(i),t(r)}))}));return{url:`tcp://127.0.0.1:${t}`,port:t,clientPromise:r}};var i=s(r(808)),n=r(361),o=s(r(891));function s(e){return e&&e.__esModule?e:{default:e}}async function a(e){return e.listen(0),await(0,n.once)(e,"listening"),e.address().port}async function c(e,t){e.bind(t),await(0,n.once)(e,"listening");const r=e.address().port;return{port:r,url:`udp://127.0.0.1:${r}`}}async function d(e){const t=o.default.createSocket("udp4"),{port:r,url:i}=await c(t,e);return{server:t,port:r,url:i}}},833:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(568);Object.keys(i).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===i[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return i[e]}}))}))},961:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.readLength=n,t.readLine=async function(e){return s(e,o)},t.readString=async function(e){let t="";return(e=await e).on("data",(e=>{t+=e.toString()})),e.resume(),await(0,i.once)(e,"end"),t},t.readUntil=s;var i=r(361);async function n(e,t){if(!t)return Buffer.alloc(0);{const r=e.read(t);if(r)return r}return new Promise(((r,i)=>{const n=()=>{const i=e.read(t);i&&(s(),r(i))},o=()=>{s(),i(new Error(`stream ended during read for minimum ${t} bytes`))},s=()=>{e.removeListener("readable",n),e.removeListener("end",o)};e.on("readable",n),e.on("end",o)}))}const o="\n".charCodeAt(0);async function s(e,t){const r=[];let i=0;for(;;){const o=await n(e,1);if(!o)throw new Error("end of stream");if(o[0]===t)break;r[i++]=o[0]}return Buffer.from(r).toString()}},175:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RtspServer=t.RTSP_FRAME_MAGIC=void 0,t.createRtspParser=function(){let e;return{container:"rtsp",tcpProtocol:"rtsp://127.0.0.1/"+(0,n.randomBytes)(8).toString("hex"),inputArguments:["-rtsp_transport","tcp"],outputArguments:["-rtsp_transport","tcp","-vcodec","copy","-acodec","copy","-f","rtsp"],findSyncFrame:s,sdp:new Promise((t=>e=t)),async*parse(t,r,i){const n=new RtspServer(t);await n.handleSetup(),e(n.sdp);for await(const{type:e,rtcp:t,header:o,packet:s}of n.handleRecord())yield{chunks:[o,s],type:e,width:r,height:i}}}};var i=r(961),n=r(113);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function s(e){return e}t.RTSP_FRAME_MAGIC=36;class RtspServer{constructor(e,t,r){o(this,"videoChannel",0),o(this,"audioChannel",2),o(this,"udpPorts",{video:0,audio:0}),this.client=e,this.sdp=t,this.udp=r,this.session=(0,n.randomBytes)(4).toString("hex"),t&&(t=t.trim())}async handleSetup(){let e=[];for(;;){let t=await(0,i.readLine)(this.client);if(t=t.trim(),t)e.push(t);else{if(!await this.headers(e))break;e=[]}}}async handlePlayback(){return this.handleSetup()}async*handleRecord(){for(;;){const e=await(0,i.readLength)(this.client,4),t=e.readUInt16BE(2),r=await(0,i.readLength)(this.client,t),n=e.readUInt8(1);yield{type:n-n%2===this.videoChannel?"video":"audio",rtcp:n%2==1,header:e,packet:r}}}send(e,t){const r=Buffer.alloc(4);r.writeUInt8(36,0),r.writeUInt8(t,1),r.writeUInt16BE(e.length,2),this.client.write(r),this.client.write(Buffer.from(e))}sendUdp(e,t,r){this.udp.send(t,r?e+1:e,"127.0.0.1")}sendVideo(e,t){this.udp?this.sendUdp(this.udpPorts.video,e,t):this.send(e,t?this.videoChannel+1:this.videoChannel)}sendAudio(e,t){this.udp?this.sendUdp(this.udpPorts.audio,e,t):this.send(e,t?this.audioChannel+1:this.audioChannel)}options(e,t){const r={Public:"DESCRIBE, OPTIONS, PAUSE, PLAY, SETUP, TEARDOWN, ANNOUNCE, RECORD"};this.respond(200,"OK",t,r)}describe(e,t){const r={};r["Content-Base"]=e,r["Content-Type"]="application/sdp",this.respond(200,"OK",t,r,Buffer.from(this.sdp))}setup(e,t){const r={},i=t.transport;if(r.Transport=t.transport,r.Session=this.session,i.includes("UDP")){const t=i.match(/.*?client_port=([0-9]+)-([0-9]+)/),[r,n,o]=t;e.includes("audio")?this.udpPorts.audio=parseInt(n):this.udpPorts.video=parseInt(n)}else if(i.includes("TCP")){const t=i.match(/.*?interleaved=([0-9]+)-([0-9]+)/);if(t){const r=parseInt(t[1]);parseInt(t[2]);e.includes("audio")?this.audioChannel=r:this.videoChannel=r}}this.respond(200,"OK",t,r)}play(e,t){const r={};r["RTP-Info"]=`url=${e}/trackID=0;seq=0;rtptime=0,url=${e}/trackID=1;seq=0;rtptime=0`,r.Range="npt=now-",r.Session=this.session,this.respond(200,"OK",t,r)}async announce(e,t){const r=parseInt(t["content-length"]),n=await(0,i.readLength)(this.client,r);this.sdp=n.toString();const o={};o.Session=this.session,this.respond(200,"OK",t,o)}async record(e,t){const r={};r.Session=this.session,this.respond(200,"OK",t,r)}async teardown(e,t){const r={};r.Session=this.session,this.respond(200,"OK",t,r)}async headers(e){var t,r;null===(t=this.console)||void 0===t||t.log("request header",e.join("\n"));let[i,n]=e[0].split(" ",2);i=i.toLowerCase();const o=function(e){const t={};for(const r of e.slice(1)){const e=r.indexOf(":");let i="";-1!==e&&(i=r.substring(e+1).trim()),t[r.substring(0,e).toLowerCase()]=i}return t}(e);if(null===(r=this.console)||void 0===r||r.log("request headers",o),this[i])return await this[i](n,o),"play"!==i&&"record"!==i;this.respond(400,"Bad Request",o,{})}respond(e,t,r,i,n){var o;let s=`RTSP/1.0 ${e} ${t}\r\n`;r.cseq&&(i.CSeq=r.cseq),n&&(i["Content-Length"]=n.length.toString());for(const[e,t]of Object.entries(i))s+=`${e}: ${t}\r\n`;null===(o=this.console)||void 0===o||o.log("response header",s),s+="\r\n",this.client.write(s),n&&this.client.write(n)}}t.RtspServer=RtspServer},567:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=n(t);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var a=o?Object.getOwnPropertyDescriptor(e,s):null;a&&(a.get||a.set)?Object.defineProperty(i,s,a):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(r(510));function n(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(n=function(e){return e?r:t})(e)}const{deviceManager:o}=i.default;class SettingsMixinDeviceBase extends i.MixinDeviceBase{constructor(e,t,r){super(e,r.mixinDeviceInterfaces,t,r.providerNativeId,r.mixinStorageSuffix),this.settingsGroup=r.group,this.settingsGroupKey=r.groupKey,process.nextTick((()=>o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)))}async getSettings(){const e=this.mixinDeviceInterfaces.includes(i.ScryptedInterface.Settings)?this.mixinDevice.getSettings():void 0,t=this.getMixinSettings(),r=[];try{const t=await e||[];r.push(...t)}catch(e){const t=this.name,i=`${t} Extension settings failed to load.`;this.console.error(i,e),r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:i,readonly:!0})}try{const e=await t||[];for(const t of e)t.group=t.group||this.settingsGroup,t.key=this.settingsGroupKey+":"+t.key;r.push(...e)}catch(e){const t=o.getDeviceState(this.mixinProviderNativeId).name,i=`${t} Extension settings failed to load.`;this.console.error(i,e),r.push({key:Math.random().toString(),title:t,value:"Settings Error",group:"Errors",description:i,readonly:!0})}return r}async putSetting(e,t){const r=this.settingsGroupKey+":";if(null==e||!e.startsWith(r))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(r.length),t),o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}release(){o.onMixinEvent(this.id,this,i.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=SettingsMixinDeviceBase},129:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PIXEL_FORMAT_YUV420P=t.PIXEL_FORMAT_RGB24=void 0,t.createDgramParser=a,t.createFragmentedMp4Parser=function(e){return{container:"mp4",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-movflags","frag_keyframe+empty_moov+default_base_moof","-f","mp4"],async*parse(e){const t=c(e);let r,i,n;for await(const e of t)r?i||(i=e):r=e,yield{startStream:n,chunks:[e.header,e.data],type:e.type},r&&i&&!n&&(n=Buffer.concat([r.header,r.data,i.header,i.data]))},findSyncFrame:o}},t.createMpegTsParser=function(e){return{container:"mpegts",outputArguments:[...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-f","mpegts"],parse:s(188,(e=>{if(71!=e[0])throw new Error("Invalid sync byte in mpeg-ts packet. Terminating stream.")})),findSyncFrame(e){for(let t=0;t<e.length;t++){const r=e[t];for(let i=0;i<r.chunks.length;i++){const n=r.chunks[i];let o=0;for(;o+188<n.length;){const r=n.subarray(o,o+188);if(256==((31&r[1])<<8|r[2])&&32&r[3]&&r[4]>0&&64&r[5])return e.slice(t);o+=188}}}return e}}},t.createRawVideoParser=function(e){var t;const r=(null===(t=e)||void 0===t?void 0:t.pixelFormat)||d;let i;e=e||{};const{size:s,everyNFrames:a}=e;s&&(i=`scale=${s.width}:${s.height}`);a&&a>1&&(i?i+=",":i="",i+=`select=not(mod(n\\,${a}))`);return{container:"rawvideo",outputArguments:[...i?["-vf",i]:[],"-an","-vcodec","rawvideo","-pix_fmt",r.name,"-f","rawvideo"],async*parse(e,t,i){if(!t||!i)throw new Error("error parsing rawvideo, unknown width and height");t=(null==s?void 0:s.width)||t,i=(null==s?void 0:s.height)||i;const o=r.computeLength(t,i);for(;;){const r=await(0,n.readLength)(e,o);yield{chunks:[r],width:t,height:i}}},findSyncFrame:o}},t.createRtpParser=function(...e){return{container:"rtsp",inputArguments:["-v","verbose","-rtsp_transport","tcp"],outputArguments:[...e,"-f","rtp"],parseDatagram:a(),findSyncFrame:o}},t.parseFragmentedMP4=c;var i=r(361),n=r(961);function o(e){return e}function s(e,t){return async function*(r){let n=[],o=0;for(;;){const s=r.read();if(!s){await(0,i.once)(r,"readable");continue}if(n.push(s),o+=s.length,o<e)continue;const a=Buffer.concat(n);null==t||t(a);const c=a.length%e,d=a.slice(0,a.length-c),u=a.slice(a.length-c);n=[u],o=u.length,yield{chunks:[d]}}}}function a(){return async function*(e,t,r,n){for(;;){const[t]=await(0,i.once)(e,"message");yield{chunks:[t],type:n}}}}async function*c(e){for(;;){const t=await(0,n.readLength)(e,8),r=t.readInt32BE(0)-8,i=t.slice(4).toString(),o=await(0,n.readLength)(e,r);yield{header:t,length:r,type:i,data:o}}}const d={name:"yuv420p",computeLength:(e,t)=>e*t*1.5};t.PIXEL_FORMAT_YUV420P=d;t.PIXEL_FORMAT_RGB24={name:"rgb24",computeLength:(e,t)=>e*t*3}},653:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.startRFC4571Parser=async function(e,t){var r,n;let c=!0;const u=new o.EventEmitter,l=await d.convertMediaObjectToJSON(e,"x-scrypted/x-rfc4571"),{url:p,sdp:m}=l,h=parseInt(null===(r=m.match(/m=audio.* ([0-9]+)/))||void 0===r?void 0:r[1]),f=parseInt(null===(n=m.match(/m=video.* ([0-9]+)/))||void 0===n?void 0:n[1]),g=new URL(p);if(!g.protocol.startsWith("tcp"))throw new Error("rfc4751 url must be tcp");const v=s.default.connect(parseInt(g.port),g.hostname),y=()=>{c&&(u.emit("killed"),u.emit("error",new Error("killed"))),c=!1,v.destroy()};return v.on("close",y),v.on("error",y),(async()=>{for(;;){const e=await(0,i.readLength)(v,2),t=e.readInt16BE(0),r=await(0,i.readLength)(v,t),n=127&r[1],o=Buffer.alloc(2);o[0]=a.RTSP_FRAME_MAGIC,n===h?o[1]=0:n===f&&(o[1]=2);const s={chunks:[o,e,r]};u.emit("rtsp",s)}})().finally(y),{sdp:Promise.resolve([Buffer.from(m)]),inputAudioCodec:"pcm",inputVideoCodec:"h264",inputVideoResolution:void 0,isActive:()=>c,kill:y,mediaStreamOptions:l.mediaStreamOptions,on(e,t){return u.on(e,t),this},once(e,t){return u.once(e,t),this},removeListener(e,t){return u.removeListener(e,t),this}}};var i=r(961),n=c(r(510)),o=r(781),s=c(r(808)),a=r(175);function c(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:d}=n.default},510:(e,t,r)=>{"use strict";var i=Object.create?function(e,t,r,i){void 0===i&&(i=r),Object.defineProperty(e,i,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]},n=function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.MixinDeviceBase=t.ScryptedDeviceBase=void 0,n(r(632),t);const o=r(632);class ScryptedDeviceBase extends o.DeviceBase{constructor(e){super(),this.nativeId=e}get storage(){return this._storage||(this._storage=deviceManager.getDeviceStorage(this.nativeId)),this._storage}get log(){return this._log||(this._log=deviceManager.getDeviceLogger(this.nativeId)),this._log}get console(){return this._console||(this._console=deviceManager.getDeviceConsole(this.nativeId)),this._console}_lazyLoadDeviceState(){this._deviceState||(this.nativeId?this._deviceState=deviceManager.getDeviceState(this.nativeId):this._deviceState=deviceManager.getDeviceState())}onDeviceEvent(e,t){return deviceManager.onDeviceEvent(this.nativeId,e,t)}}t.ScryptedDeviceBase=ScryptedDeviceBase;class MixinDeviceBase extends o.DeviceBase{constructor(e,t,r,i,n){super(),this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=i,this._mixinStorageSuffix=n,this._listeners=new Set,this._deviceState=r}get storage(){if(!this._storage){const e=this._mixinStorageSuffix,t=this.id+(e?":"+e:"");this._storage=deviceManager.getMixinStorage(t,this.mixinProviderNativeId)}return this._storage}get console(){return this._console||(deviceManager.getMixinConsole?this._console=deviceManager.getMixinConsole(this.id,this.mixinProviderNativeId):this._console=deviceManager.getDeviceConsole(this.mixinProviderNativeId)),this._console}onDeviceEvent(e,t){return deviceManager.onMixinEvent(this.id,this,e,t)}_lazyLoadDeviceState(){}manageListener(e){this._listeners.add(e)}release(){for(const e of this._listeners)e.removeListener()}}t.MixinDeviceBase=MixinDeviceBase,function(){function e(e){return function(){return this._lazyLoadDeviceState(),this._deviceState[e]}}function t(e){return function(t){this._lazyLoadDeviceState(),this._deviceState[e]=t}}for(var r of Object.values(o.ScryptedInterfaceProperty))Object.defineProperty(ScryptedDeviceBase.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(MixinDeviceBase.prototype,r,{set:t(r),get:e(r)})}();let s={};try{s=Object.assign(s,{log:deviceManager.getDeviceLogger(void 0),deviceManager,endpointManager,mediaManager,systemManager,pluginHostAPI})}catch(e){console.error("sdk initialization error, import @scrypted/types or use @scrypted/web-sdk instead",e)}t.default=s},632:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ScryptedMimeTypes=t.ScryptedInterface=t.MediaPlayerState=t.LockState=t.ThermostatMode=t.TemperatureUnit=t.FanMode=t.HumidityMode=t.ScryptedDeviceType=t.ScryptedInterfaceDescriptors=t.ScryptedInterfaceProperty=t.DeviceBase=void 0;t.DeviceBase=class DeviceBase{},function(e){e.id="id",e.info="info",e.interfaces="interfaces",e.mixins="mixins",e.name="name",e.providedInterfaces="providedInterfaces",e.providedName="providedName",e.providedRoom="providedRoom",e.providedType="providedType",e.providerId="providerId",e.room="room",e.type="type",e.on="on",e.brightness="brightness",e.colorTemperature="colorTemperature",e.rgb="rgb",e.hsv="hsv",e.running="running",e.paused="paused",e.docked="docked",e.thermostatActiveMode="thermostatActiveMode",e.thermostatAvailableModes="thermostatAvailableModes",e.thermostatMode="thermostatMode",e.thermostatSetpoint="thermostatSetpoint",e.thermostatSetpointHigh="thermostatSetpointHigh",e.thermostatSetpointLow="thermostatSetpointLow",e.temperature="temperature",e.temperatureUnit="temperatureUnit",e.humidity="humidity",e.lockState="lockState",e.entryOpen="entryOpen",e.batteryLevel="batteryLevel",e.online="online",e.updateAvailable="updateAvailable",e.fromMimeType="fromMimeType",e.toMimeType="toMimeType",e.binaryState="binaryState",e.intrusionDetected="intrusionDetected",e.powerDetected="powerDetected",e.audioDetected="audioDetected",e.motionDetected="motionDetected",e.ambientLight="ambientLight",e.occupied="occupied",e.flooded="flooded",e.ultraviolet="ultraviolet",e.luminance="luminance",e.position="position",e.humiditySetting="humiditySetting",e.fan="fan"}(t.ScryptedInterfaceProperty||(t.ScryptedInterfaceProperty={})),t.ScryptedInterfaceDescriptors={ScryptedDevice:{name:"ScryptedDevice",methods:["listen","probe","setName","setRoom","setType"],properties:["id","info","interfaces","mixins","name","providedInterfaces","providedName","providedRoom","providedType","providerId","room","type"]},ScryptedPlugin:{name:"ScryptedPlugin",methods:["getPluginJson"],properties:[]},OnOff:{name:"OnOff",methods:["turnOff","turnOn"],properties:["on"]},Brightness:{name:"Brightness",methods:["setBrightness"],properties:["brightness"]},ColorSettingTemperature:{name:"ColorSettingTemperature",methods:["getTemperatureMaxK","getTemperatureMinK","setColorTemperature"],properties:["colorTemperature"]},ColorSettingRgb:{name:"ColorSettingRgb",methods:["setRgb"],properties:["rgb"]},ColorSettingHsv:{name:"ColorSettingHsv",methods:["setHsv"],properties:["hsv"]},Notifier:{name:"Notifier",methods:["sendNotification"],properties:[]},StartStop:{name:"StartStop",methods:["start","stop"],properties:["running"]},Pause:{name:"Pause",methods:["pause","resume"],properties:["paused"]},Dock:{name:"Dock",methods:["dock"],properties:["docked"]},TemperatureSetting:{name:"TemperatureSetting",methods:["setThermostatMode","setThermostatSetpoint","setThermostatSetpointHigh","setThermostatSetpointLow"],properties:["thermostatActiveMode","thermostatAvailableModes","thermostatMode","thermostatSetpoint","thermostatSetpointHigh","thermostatSetpointLow"]},Thermometer:{name:"Thermometer",methods:["setTemperatureUnit"],properties:["temperature","temperatureUnit"]},HumiditySensor:{name:"HumiditySensor",methods:[],properties:["humidity"]},Camera:{name:"Camera",methods:["getPictureOptions","takePicture"],properties:[]},VideoCamera:{name:"VideoCamera",methods:["getVideoStream","getVideoStreamOptions"],properties:[]},VideoCameraConfiguration:{name:"VideoCameraConfiguration",methods:["setVideoStreamOptions"],properties:[]},Intercom:{name:"Intercom",methods:["startIntercom","stopIntercom"],properties:[]},Lock:{name:"Lock",methods:["lock","unlock"],properties:["lockState"]},PasswordStore:{name:"PasswordStore",methods:["addPassword","getPasswords","removePassword"],properties:[]},Authenticator:{name:"Authenticator",methods:["checkPassword"],properties:[]},Scene:{name:"Scene",methods:["activate","deactivate","isReversible"],properties:[]},Entry:{name:"Entry",methods:["closeEntry","openEntry"],properties:[]},EntrySensor:{name:"EntrySensor",methods:[],properties:["entryOpen"]},DeviceProvider:{name:"DeviceProvider",methods:["getDevice"],properties:[]},DeviceDiscovery:{name:"DeviceDiscovery",methods:["discoverDevices"],properties:[]},DeviceCreator:{name:"DeviceCreator",methods:["createDevice","getCreateDeviceSettings"],properties:[]},Battery:{name:"Battery",methods:[],properties:["batteryLevel"]},Refresh:{name:"Refresh",methods:["getRefreshFrequency","refresh"],properties:[]},MediaPlayer:{name:"MediaPlayer",methods:["getMediaStatus","load","seek","skipNext","skipPrevious"],properties:[]},Online:{name:"Online",methods:[],properties:["online"]},SoftwareUpdate:{name:"SoftwareUpdate",methods:["checkForUpdate","installUpdate"],properties:["updateAvailable"]},BufferConverter:{name:"BufferConverter",methods:["convert"],properties:["fromMimeType","toMimeType"]},Settings:{name:"Settings",methods:["getSettings","putSetting"],properties:[]},BinarySensor:{name:"BinarySensor",methods:[],properties:["binaryState"]},IntrusionSensor:{name:"IntrusionSensor",methods:[],properties:["intrusionDetected"]},PowerSensor:{name:"PowerSensor",methods:[],properties:["powerDetected"]},AudioSensor:{name:"AudioSensor",methods:[],properties:["audioDetected"]},MotionSensor:{name:"MotionSensor",methods:[],properties:["motionDetected"]},AmbientLightSensor:{name:"AmbientLightSensor",methods:[],properties:["ambientLight"]},OccupancySensor:{name:"OccupancySensor",methods:[],properties:["occupied"]},FloodSensor:{name:"FloodSensor",methods:[],properties:["flooded"]},UltravioletSensor:{name:"UltravioletSensor",methods:[],properties:["ultraviolet"]},LuminanceSensor:{name:"LuminanceSensor",methods:[],properties:["luminance"]},PositionSensor:{name:"PositionSensor",methods:[],properties:["position"]},Readme:{name:"Readme",methods:["getReadmeMarkdown"],properties:[]},OauthClient:{name:"OauthClient",methods:["getOauthUrl","onOauthCallback"],properties:[]},MixinProvider:{name:"MixinProvider",methods:["canMixin","getMixin","releaseMixin"],properties:[]},HttpRequestHandler:{name:"HttpRequestHandler",methods:["onRequest"],properties:[]},EngineIOHandler:{name:"EngineIOHandler",methods:["onConnection"],properties:[]},PushHandler:{name:"PushHandler",methods:["onPush"],properties:[]},Program:{name:"Program",methods:["run"],properties:[]},Scriptable:{name:"Scriptable",methods:["eval","loadScripts","saveScript"],properties:[]},ObjectDetector:{name:"ObjectDetector",methods:["getDetectionInput","getObjectTypes"],properties:[]},ObjectDetection:{name:"ObjectDetection",methods:["detectObjects","getDetectionModel"],properties:[]},HumiditySetting:{name:"HumiditySetting",methods:["setHumidity"],properties:["humiditySetting"]},Fan:{name:"Fan",methods:["setFan"],properties:["fan"]}},function(e){e.Builtin="Builtin",e.Camera="Camera",e.Fan="Fan",e.Light="Light",e.Switch="Switch",e.Outlet="Outlet",e.Sensor="Sensor",e.Scene="Scene",e.Program="Program",e.Automation="Automation",e.Vacuum="Vacuum",e.Notifier="Notifier",e.Thermostat="Thermostat",e.Lock="Lock",e.PasswordControl="PasswordControl",e.Display="Display",e.Speaker="Speaker",e.Event="Event",e.Entry="Entry",e.Garage="Garage",e.DeviceProvider="DeviceProvider",e.DataSource="DataSource",e.API="API",e.Doorbell="Doorbell",e.Irrigation="Irrigation",e.Valve="Valve",e.Person="Person",e.Unknown="Unknown"}(t.ScryptedDeviceType||(t.ScryptedDeviceType={})),function(e){e.Humidify="Humidify",e.Dehumidify="Dehumidify",e.Auto="Auto",e.Off="Off"}(t.HumidityMode||(t.HumidityMode={})),function(e){e.Auto="Auto",e.Manual="Manual"}(t.FanMode||(t.FanMode={})),function(e){e.C="C",e.F="F"}(t.TemperatureUnit||(t.TemperatureUnit={})),function(e){e.Off="Off",e.Cool="Cool",e.Heat="Heat",e.HeatCool="HeatCool",e.Auto="Auto",e.FanOnly="FanOnly",e.Purifier="Purifier",e.Eco="Eco",e.Dry="Dry",e.On="On"}(t.ThermostatMode||(t.ThermostatMode={})),function(e){e.Locked="Locked",e.Unlocked="Unlocked",e.Jammed="Jammed"}(t.LockState||(t.LockState={})),function(e){e.Idle="Idle",e.Playing="Playing",e.Paused="Paused",e.Buffering="Buffering"}(t.MediaPlayerState||(t.MediaPlayerState={})),function(e){e.ScryptedDevice="ScryptedDevice",e.ScryptedPlugin="ScryptedPlugin",e.OnOff="OnOff",e.Brightness="Brightness",e.ColorSettingTemperature="ColorSettingTemperature",e.ColorSettingRgb="ColorSettingRgb",e.ColorSettingHsv="ColorSettingHsv",e.Notifier="Notifier",e.StartStop="StartStop",e.Pause="Pause",e.Dock="Dock",e.TemperatureSetting="TemperatureSetting",e.Thermometer="Thermometer",e.HumiditySensor="HumiditySensor",e.Camera="Camera",e.VideoCamera="VideoCamera",e.VideoCameraConfiguration="VideoCameraConfiguration",e.Intercom="Intercom",e.Lock="Lock",e.PasswordStore="PasswordStore",e.Authenticator="Authenticator",e.Scene="Scene",e.Entry="Entry",e.EntrySensor="EntrySensor",e.DeviceProvider="DeviceProvider",e.DeviceDiscovery="DeviceDiscovery",e.DeviceCreator="DeviceCreator",e.Battery="Battery",e.Refresh="Refresh",e.MediaPlayer="MediaPlayer",e.Online="Online",e.SoftwareUpdate="SoftwareUpdate",e.BufferConverter="BufferConverter",e.Settings="Settings",e.BinarySensor="BinarySensor",e.IntrusionSensor="IntrusionSensor",e.PowerSensor="PowerSensor",e.AudioSensor="AudioSensor",e.MotionSensor="MotionSensor",e.AmbientLightSensor="AmbientLightSensor",e.OccupancySensor="OccupancySensor",e.FloodSensor="FloodSensor",e.UltravioletSensor="UltravioletSensor",e.LuminanceSensor="LuminanceSensor",e.PositionSensor="PositionSensor",e.Readme="Readme",e.OauthClient="OauthClient",e.MixinProvider="MixinProvider",e.HttpRequestHandler="HttpRequestHandler",e.EngineIOHandler="EngineIOHandler",e.PushHandler="PushHandler",e.Program="Program",e.Scriptable="Scriptable",e.ObjectDetector="ObjectDetector",e.ObjectDetection="ObjectDetection",e.HumiditySetting="HumiditySetting",e.Fan="Fan"}(t.ScryptedInterface||(t.ScryptedInterface={})),function(e){e.AcceptUrlParameter="accept-url",e.Url="text/x-uri",e.InsecureLocalUrl="text/x-insecure-local-uri",e.LocalUrl="text/x-local-uri",e.PushEndpoint="text/x-push-endpoint",e.MediaStreamUrl="text/x-media-url",e.FFmpegInput="x-scrypted/x-ffmpeg-input",e.RTCAVSignalingPrefix="x-scrypted-rtc-signaling-",e.RTCAVOffer="x-scrypted/x-rtc-av-offer",e.RTCAVAnswer="x-scrypted/x-rtc-av-answer",e.RTCAVSignalingOfferSetup="x-scrypted/x-rtc-av-signalling-offer-setup",e.SchemePrefix="x-scrypted/x-scrypted-scheme-",e.MediaObject="x-scrypted/x-scrypted-media-object"}(t.ScryptedMimeTypes||(t.ScryptedMimeTypes={}))},568:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,r,i){var s,a;const c=!!n.default.env.SCRYPTED_FFMPEG_NOISY||!(null==i||!i.getItem("SCRYPTED_FFMPEG_NOISY"));function d(e){const i=n=>{const s=n.toString();for(const e of o)if(-1!==s.indexOf(e))return;if(!(c||r||-1===s.indexOf("frame=")&&-1===s.indexOf("size=")))return e(s),e("video/audio detected, discarding further input"),t.stdout.removeListener("data",i),void t.stderr.removeListener("data",i);e(s)};return i}null===(s=t.stdout)||void 0===s||s.on("data",d(e.log)),null===(a=t.stderr)||void 0===a||a.on("data",d(e.error)),t.on("exit",(()=>e.log("ffmpeg exited")))},t.safePrintFFmpegArguments=function(e,t){const r=[];for(const e of t)try{const t=new URL(e);r.push(`${t.protocol}[REDACTED]`)}catch(t){r.push(e)}e.log(r.join(" "))};var i,n=(i=r(282))&&i.__esModule?i:{default:i};const o=["decode_slice_header error","no frame!","non-existing PPS"]},81:e=>{"use strict";e.exports=require("child_process")},113:e=>{"use strict";e.exports=require("crypto")},891:e=>{"use strict";e.exports=require("dgram")},361:e=>{"use strict";e.exports=require("events")},808:e=>{"use strict";e.exports=require("net")},282:e=>{"use strict";e.exports=require("process")},781:e=>{"use strict";e.exports=require("stream")}},t={};function r(i){var n=t[i];if(void 0!==n)return n.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,r),o.exports}var i={};(()=>{"use strict";var e=i;Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t,n=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=f(t);if(r&&r.has(e))return r.get(e);var i={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var s=n?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(i,o,s):i[o]=e[o]}i.default=e,r&&r.set(e,i);return i}(r(510)),o=r(361),s=r(567),a=r(201),c=r(129),d=r(454),u=r(769),l=r(175),p=(t=r(808))&&t.__esModule?t:{default:t},m=r(961),h=r(653);function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(f=function(e){return e?r:t})(e)}function g(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}const{mediaManager:v,log:y,systemManager:S,deviceManager:b}=n.default,P=1e4,M="prebufferDuration",w="sendKeyframe",I="Default",O="AAC or No Audio",x=`${O} (Copy)`,D="Compatible Audio",C="Other Audio",A=["aac","mp3","mp2","opus"],k="-fflags +genpts",T=[O,D,C],_=["mpegts","mp4","rtsp"];class PrebufferSession{constructor(e,t,r,i){g(this,"prebuffers",{mp4:[],mpegts:[],rtsp:[]}),g(this,"detectedIdrInterval",0),g(this,"prevIdr",0),g(this,"audioDisabled",!1),g(this,"activeClients",0),this.mixin=e,this.streamName=t,this.streamId=r,this.stopInactive=i,this.storage=e.storage,this.console=e.console,this.mixinDevice=e.mixinDevice,this.audioConfigurationKey="audioConfiguration-"+this.streamId,this.ffmpegInputArgumentsKey="ffmpegInputArguments-"+this.streamId,this.rebroadcastModeKey="rebroadcastMode-"+this.streamId,this.lastDetectedAudioCodecKey="lastDetectedAudioCodec-"+this.streamId}clearPrebuffers(){this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.rtsp=[]}ensurePrebufferSession(){this.parserSessionPromise||this.mixin.released||(this.console.log(this.streamName,"prebuffer session started"),this.parserSessionPromise=this.startPrebufferSession(),this.parserSessionPromise.catch((()=>this.parserSessionPromise=void 0)))}getAudioConfig(){let e=this.storage.getItem(this.audioConfigurationKey)||"";T.find((t=>e.startsWith(t)))||(e="");const t=-1!==e.indexOf(O),r=-1!==e.indexOf(D),i=-1!==e.indexOf(C);return{isUsingDefaultAudioConfig:!(t||r||i),aacAudio:t,compatibleAudio:r,reencodeAudio:i}}async getMixinSettings(){const e=[],t=this.parserSession;let r=0,i=0;for(const e of this.prebuffers.mp4){i=i||e.time;for(const t of e.chunk.chunks)r+=t.byteLength}const n=Date.now()-i,o=Math.round(r/n*8),s=this.streamName?`Rebroadcast: ${this.streamName}`:"Rebroadcast";var a,c,d;(e.push({title:"Audio Codec Transcoding",group:s,description:"Configuring your camera to output AAC, MP3, MP2, or Opus is recommended. PCM/G711 cameras should set this to Transcode.",type:"string",key:this.audioConfigurationKey,value:this.storage.getItem(this.audioConfigurationKey)||I,choices:[I,x,"Compatible Audio (Copy)","Other Audio (Transcode)"]},{title:"FFmpeg Input Arguments Prefix",group:s,description:"Optional/Advanced: Additional input arguments to pass to the ffmpeg command. These will be placed before the input arguments.",key:this.ffmpegInputArgumentsKey,value:this.storage.getItem(this.ffmpegInputArgumentsKey),placeholder:k,choices:[k,"-use_wallclock_as_timestamps 1","-v verbose"],combobox:!0},{title:"Rebroadcast Mode",group:s,description:"THIS FEATURE IS IN TESTING. DO NOT CHANGE THIS FROM MPEG-TS. The stream format to use when rebroadcasting.",placeholder:"MPEG-TS",choices:["MPEG-TS","RTSP","RTSP+MP4"],key:this.rebroadcastModeKey,value:this.storage.getItem(this.rebroadcastModeKey)||"MPEG-TS"}),t)?e.push({key:"detectedResolution",group:s,title:"Detected Resolution and Bitrate",readonly:!0,value:`${(null==t||null===(a=t.inputVideoResolution)||void 0===a?void 0:a[0])||"unknown"} @ ${o||"unknown"} Kb/s`,description:"Configuring your camera to 1920x1080, 2000Kb/S, Variable Bit Rate, is recommended."},{key:"detectedCodec",group:s,title:"Detected Video/Audio Codecs",readonly:!0,value:((null==t||null===(c=t.inputVideoCodec)||void 0===c?void 0:c.toString())||"unknown")+"/"+((null==t||null===(d=t.inputAudioCodec)||void 0===d?void 0:d.toString())||"unknown"),description:"Configuring your camera to H264 video and AAC/MP3/MP2/Opus audio is recommended."},{key:"detectedKeyframe",group:s,title:"Detected Keyframe Interval",description:"Configuring your camera to 4 seconds is recommended (IDR aka Frame Interval = FPS * 4 seconds).",readonly:!0,value:((this.detectedIdrInterval||0)/1e3).toString()||"none"}):e.push({title:"Status",group:s,key:"status",description:"Rebroadcast is currently idle and will be started automatically on demand.",value:"Idle",readonly:!0});return e}getRebroadcastMode(){const e=this.storage.getItem(this.rebroadcastModeKey),t=null==e?void 0:e.startsWith("RTSP");return{rtspMode:null==e?void 0:e.startsWith("RTSP"),mp4Mode:!t||(null==e?void 0:e.includes("MP4"))}}async startPrebufferSession(){var e,t,r,i,o,s,d;this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.rtsp=[];const u=parseInt(this.storage.getItem(M))||P;let p;try{p=(await this.mixinDevice.getVideoStreamOptions()).find((e=>e.id===this.streamId))}catch(e){}const m=null===(null===(e=p)||void 0===e?void 0:e.audio),f=null===(t=p)||void 0===t||null===(r=t.audio)||void 0===r?void 0:r.codec,{isUsingDefaultAudioConfig:g,aacAudio:S,compatibleAudio:b,reencodeAudio:w}=this.getAudioConfig();let I=this.storage.getItem(this.lastDetectedAudioCodecKey)||void 0;"null"===I&&(I=null);let O=!1;m||f||!g||void 0!==I||(this.console.warn("Camera did not report an audio codec, muting the audio stream and probing the codec."),O=!0),!m&&f&&void 0!==I&&I!==f&&this.console.warn("Audio codec plugin reported vs detected mismatch",f,I);const x=void 0===I?null==f?void 0:f.toLowerCase():null===(i=I)||void 0===i?void 0:i.toLowerCase(),{rtspMode:D,mp4Mode:C}=this.getRebroadcastMode(),T=!D||C,E=!A.includes(x);!T||O||!1===(null===(o=p)||void 0===o?void 0:o.userConfigurable)||m||E&&(g&&y.a(`${this.mixin.name} is using the ${x} audio codec. Configuring your Camera to use AAC, MP3, MP2, or Opus audio is recommended. If this is not possible, Select 'Transcode Audio' in the camera stream's Rebroadcast settings to suppress this alert.`),this.console.warn("Configure your camera to output AAC, MP3, MP2, or Opus audio. Suboptimal audio codec in use:",x));const R=["-bsf:a","aac_adtstoasc"],L=[];let B;this.audioDisabled=!1;const j=null===I;let F=!1;var V;!O&&g&&E&&(!1===(null===(V=p)||void 0===V?void 0:V.userConfigurable)?this.console.log("camera reports it is not user configurable. transcoding due to incompatible codec",x):this.console.log("camera audio transcoding due to incompatible codec. configure the camera to use a compatible codec if possible."),F=!0);if(m||O)B=["-an"],this.audioDisabled=!0;else if(w||F)B=["-bsf:a","aac_adtstoasc","-acodec","libfdk_aac","-ar","8k","-b:a","100k","-bufsize","400k","-ac","1","-profile:a","aac_low","-flags","+global_header"];else if(S||j)B=["-acodec","copy"],B.push(...R);else if(b)B=["-acodec","copy"],B.push(...L);else{B=["-acodec","copy"];const e="aac"===x?R:L;B.push(...e)}const H=["-vcodec","copy"],N={console:this.console,timeout:6e4,parsers:{}};if(this.parsers=N.parsers,this.console.log("rebroadcast mode:",D?"rtsp":"mpegts"),D){const e=(0,l.createRtspParser)();this.sdp=e.sdp,N.parsers.rtsp=e}else N.parsers.mpegts=(0,c.createMpegTsParser)({vcodec:H,acodec:B});C&&(N.parsers.mp4=(0,c.createFragmentedMp4Parser)({vcodec:H,acodec:B}));const U=await this.mixinDevice.getVideoStream(p),K="x-scrypted/x-rfc4571"===U.mimeType;let $,q;if(this.storage.removeItem(this.lastDetectedAudioCodecKey),D&&K&&!C)$=await(0,h.startRFC4571Parser)(U,N),this.sdp=$.sdp.then((e=>Buffer.concat(e).toString()));else{const e=await v.convertMediaObjectToBuffer(U,n.ScryptedMimeTypes.FFmpegInput),t=JSON.parse(e.toString());q=t.mediaStreamOptions;const r=this.storage.getItem(this.ffmpegInputArgumentsKey)||k;t.inputArguments.unshift(...r.split(" ")),$=await(0,a.startParserSession)(t,N)}if($.inputAudioCodec?A.includes(null===(s=$.inputAudioCodec)||void 0===s?void 0:s.toLowerCase())?this.console.log("Detected audio codec is mp4/mpegts compatible.",$.inputAudioCodec):this.console.log("Detected audio codec is not mp4/mpegts compatible.",$.inputAudioCodec):this.console.log("No audio stream detected."),this.storage.setItem(this.lastDetectedAudioCodecKey,$.inputAudioCodec||"null"),"h264"!==$.inputVideoCodec&&this.console.error("Video codec is not h264. If there are errors, try changing your camera's encoder output."),O)return this.console.warn("Audio probe complete, ending rebroadcast session and restarting with detected codecs."),$.kill(),this.startPrebufferSession();if(this.parserSession=$,null!==(d=q)&&void 0!==d&&d.refreshAt){let e,t=q;const r=async()=>{if(!$.isActive)return;const e=await this.mixinDevice.getVideoStream(t),r=await v.convertMediaObjectToBuffer(e,n.ScryptedMimeTypes.FFmpegInput),o=JSON.parse(r.toString());t=o.mediaStreamOptions,i(t)},i=t=>{const i=t.refreshAt-Date.now()-3e4;this.console.log("refreshing media stream in",i),e=setTimeout(r,i)};i(t),$.once("killed",(()=>clearTimeout(e)))}$.once("killed",(()=>{this.parserSessionPromise=void 0,this.parserSession===$&&(this.parserSession=void 0)}));for(const e of _){let t=0;$.on(e,(r=>{const i=this.prebuffers[e],n=Date.now();for("mdat"===r.type&&(this.prevIdr&&(this.detectedIdrInterval=n-this.prevIdr),this.prevIdr=n),i.push({time:n,chunk:r});i.length&&i[0].time<n-u;)i.shift(),t++;t>1e3&&(this.prebuffers[e]=i.slice(),t=0)}))}return $}printActiveClients(){this.console.log(this.streamName,"active rebroadcast clients:",this.activeClients)}inactivityCheck(e,t){this.printActiveClients(),this.stopInactive&&(this.activeClients||this.inactivityTimeout&&!t||(clearTimeout(this.inactivityTimeout),this.inactivityTimeout=setTimeout((()=>{this.activeClients||(this.console.log(this.streamName,"terminating rebroadcast due to inactivity"),e.kill())}),3e4)))}async getVideoStream(e){var t,r;this.ensurePrebufferSession();const i=await this.parserSessionPromise,n="false"!==this.storage.getItem(w),o=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0);this.console.log(this.streamName,"prebuffer request started");const{rtspMode:s,mp4Mode:c}=this.getRebroadcastMode(),d=s?"rtsp":"mpegts",p=this.parsers[null==e?void 0:e.container]?null==e?void 0:e.container:d,m=Object.assign({},i.mediaStreamOptions);m.prebuffer=o;const{reencodeAudio:h}=this.getAudioConfig();s&&"rtsp"===p?m.audio={codec:null==i?void 0:i.inputAudioCodec}:this.audioDisabled?m.audio=null:m.audio=h?{codec:"aac",encoder:"libfdk_aac",profile:"aac_low"}:{codec:null==i?void 0:i.inputAudioCodec},m.video&&null!==(t=i.inputVideoResolution)&&void 0!==t&&t[2]&&null!==(r=i.inputVideoResolution)&&void 0!==r&&r[3]&&Object.assign(m.video,{width:parseInt(i.inputVideoResolution[2]),height:parseInt(i.inputVideoResolution[3])});const f=Date.now();let g=0;const y=this.prebuffers[p];for(const e of y)if(!(e.time<f-o))for(const t of e.chunk.chunks)g+=t.length;const S=Math.max(5e5,g).toString(),b=await(async t=>{const r=this.prebuffers[t];let n,s;if("rtsp"===t){this.sdp.then((e=>console.log(e)));const e=await(0,u.listenZeroSingleClient)();n=e.clientPromise.then((async e=>{let t=await this.sdp;const r=new l.RtspServer(e,t);return r.console=this.console,await r.handlePlayback(),e})),s=e.url.replace("tcp://","rtsp://")}else{const e=await(0,u.listenZeroSingleClient)();n=e.clientPromise,s=`tcp://127.0.0.1:${e.port}`}return(0,a.handleRebroadcasterClient)(n,{console:this.console,connect:(n,s)=>{this.activeClients++,this.printActiveClients();const a=Date.now(),c=e=>{n(e)>1e8&&(this.console.log("more than 100MB has been buffered, did downstream die? killing connection.",this.streamName),d())},d=()=>{s(),this.console.log(this.streamName,"prebuffer request ended"),i.removeListener(t,c),i.removeListener("killed",d)};i.on(t,c),i.once("killed",d);for(const e of r)e.time<a-o||c(e.chunk);return()=>{this.activeClients--,this.inactivityCheck(i,!1!==(null==e?void 0:e.refresh)),d()}}}),s})(p),P={url:b,container:p,inputArguments:["-analyzeduration","0","-probesize",S,...this.parsers[p].inputArguments||[],"-f",this.parsers[p].container,"-i",b],mediaStreamOptions:m};return v.createFFmpegMediaObject(P)}}class PrebufferMixin extends s.SettingsMixinDeviceBase{constructor(e,t,r,i){super(e,r,{providerNativeId:i,mixinDeviceInterfaces:t,group:"Prebuffer Settings",groupKey:"prebuffer"}),g(this,"released",!1),g(this,"sessions",new Map),this.delayStart()}delayStart(){this.console.log("prebuffer sessions starting in 5 seconds"),setTimeout((()=>this.ensurePrebufferSessions()),5e3)}async getVideoStream(e){await this.ensurePrebufferSessions();const t=null==e?void 0:e.id;let r=this.sessions.get(t);return!r||null!=e&&e.directMediaStream?this.mixinDevice.getVideoStream(e):(r.ensurePrebufferSession(),await r.parserSessionPromise,r=this.sessions.get(t),r?r.getVideoStream(e):this.mixinDevice.getVideoStream(e))}async ensurePrebufferSessions(){const e=await this.mixinDevice.getVideoStreamOptions(),t=this.getEnabledMediaStreamOptions(e),r=t?t.map((e=>e.id)):[void 0],i=(null==e?void 0:e.map((e=>e.id)))||[void 0];if("true"!==this.storage.getItem("warnedCloud")){(null==e?void 0:e.find((e=>"cloud"===e.source)))&&(this.storage.setItem("warnedCloud","true"),y.a(`${this.name} is a cloud camera. Prebuffering maintains a persistent stream and will not enabled by default. You must enable the Prebuffer stream manually.`))}const s=this.mixinDeviceInterfaces.includes(n.ScryptedInterface.Battery);let a=0;const c=i.length;for(const t of i){let i=this.sessions.get(t);if(!i){var d;const n=null==e?void 0:e.find((e=>e.id===t));null!=n&&n.prebuffer&&y.a(`Prebuffer is already available on ${this.name}. If this is a grouped device, disable the Rebroadcast extension.`);const u=null==n?void 0:n.name,l=!r.includes(t);if(i=new PrebufferSession(this,u,t,s||l),this.sessions.set(t,i),t===(null==e||null===(d=e[0])||void 0===d?void 0:d.id)&&this.sessions.set(void 0,i),s){this.console.log("camera is battery powered, prebuffering and rebroadcasting will only work on demand.");continue}if(l){this.console.log("stream",u,"will be rebroadcast on demand.");continue}(async()=>{for(;this.sessions.get(t)===i&&!this.released;){i.ensurePrebufferSession();try{const e=await i.parserSessionPromise;a++,this.online=a==c,await(0,o.once)(e,"killed"),this.console.error("prebuffer session ended")}catch(e){this.console.error("prebuffer session ended with error",e)}finally{a--,this.online=a==c}this.console.log("restarting prebuffer session in 5 seconds"),await new Promise((e=>setTimeout(e,5e3)))}this.console.log("exiting prebuffer session (released or restarted with new configuration)")})()}}b.onMixinEvent(this.id,this.mixinProviderNativeId,n.ScryptedInterface.Settings,void 0)}async getMixinSettings(){const e=[];try{const t=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(t);(null==t?void 0:t.length)>0&&e.push({title:"Prebuffered Streams",description:"The streams to prebuffer. Enable only as necessary to reduce traffic.",key:"enabledStreams",value:r.map((e=>e.name||"")),choices:t.map((e=>e.name)),multiple:!0})}catch(e){throw this.console.error("error in getVideoStreamOptions",e),e}e.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:M,value:this.storage.getItem(M)||P.toString()},{title:"Start at Previous Keyframe",description:"Start live streams from the previous key frame. Improves startup time.",type:"boolean",key:w,value:("false"!==this.storage.getItem(w)).toString()});for(const t of new Set([...this.sessions.values()]))if(t)try{e.push(...await t.getMixinSettings())}catch(e){throw this.console.error("error in prebuffer session getMixinSettings",e),e}return e}async putMixinSetting(e,t){const r=this.sessions;this.sessions=new Map,"enabledStreams"===e?this.storage.setItem(e,JSON.stringify(t)):this.storage.setItem(e,t.toString());for(const e of r.values()){var i;null==e||null===(i=e.parserSessionPromise)||void 0===i||i.then((e=>e.kill()))}this.ensurePrebufferSessions()}getEnabledMediaStreamOptions(e){if(!e)return;try{const t=JSON.parse(this.storage.getItem("enabledStreams"));return e.filter((e=>t.includes(e.name)))}catch(e){}const t=e.find((e=>"cloud"!==e.source));return t?[t]:[]}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=this.getEnabledMediaStreamOptions(e);const r=parseInt(this.storage.getItem(M))||P;for(const n of e){var i;(null!==(i=this.sessions.get(n.id))&&void 0!==i&&i.parserSession||t.includes(n))&&(n.prebuffer=r)}return e}release(){this.console.log("prebuffer releasing if started"),this.released=!0;for(const t of this.sessions.values()){var e;t&&(t.clearPrebuffers(),null===(e=t.parserSessionPromise)||void 0===e||e.then((e=>{this.console.log("prebuffer released"),e.kill(),t.clearPrebuffers()})))}}}class PrebufferProvider extends d.AutoenableMixinProvider{constructor(e){super(e),this.fromMimeType="x-scrypted/x-rfc4571",this.toMimeType=n.ScryptedMimeTypes.FFmpegInput;for(const e of Object.keys(S.getSystemState())){var t;const r=S.getDeviceById(e);null!==(t=r.mixins)&&void 0!==t&&t.includes(this.id)&&r.getVideoStreamOptions()}const r=function(){var e=new Date;return e.setHours(24),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0),e.getTime()-(new Date).getTime()}()+72e5;this.log.i(`Rebroadcaster scheduled for restart at 2AM: ${Math.round(r/1e3/60)} minutes`),setTimeout((()=>b.requestRestart()),r)}async convert(e,t,r){var i,n;const o=JSON.parse(e.toString()),{url:s,sdp:a}=o,c=parseInt(null===(i=a.match(/m=audio.* ([0-9]+)/))||void 0===i?void 0:i[1]),d=parseInt(null===(n=a.match(/m=video.* ([0-9]+)/))||void 0===n?void 0:n[1]),h=new URL(s);if(!h.protocol.startsWith("tcp"))throw new Error("rfc4751 url must be tcp");const{clientPromise:f,url:g}=await(0,u.listenZeroSingleClient)(),v={url:g,inputArguments:["-rtsp_transport","tcp","-max_delay","1000000","-i",g.replace("tcp","rtsp")]};return f.then((async e=>{const t=new l.RtspServer(e,a);t.console=this.console,await t.handlePlayback();const r=p.default.connect(parseInt(h.port),h.hostname);for(e.on("close",(()=>{r.destroy()})),r.on("close",(()=>{e.destroy()}));;){const i=(await(0,m.readLength)(r,2)).readInt16BE(0),n=await(0,m.readLength)(r,i),o=127&n[1];if(o===c)t.sendAudio(n,!1);else{if(o!==d)throw e.destroy(),r.destroy(),new Error("unknown payload type "+o);t.sendVideo(n,!1)}}})),Buffer.from(JSON.stringify(v))}async canMixin(e,t){return t.includes(n.ScryptedInterface.VideoCamera)?[n.ScryptedInterface.VideoCamera,n.ScryptedInterface.Settings,n.ScryptedInterface.Online]:null}async getMixin(e,t,r){return this.setHasEnabledMixin(r.id),new PrebufferMixin(e,t,r,this.nativeId)}async releaseMixin(e,t){t.online=!0,t.release()}}var E=new PrebufferProvider;e.default=E})();var n=exports="undefined"==typeof exports?{}:exports;for(var o in i)n[o]=i[o];i.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})})();
|
|
2
2
|
//# sourceMappingURL=main.nodejs.js.map
|
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/main.ts
CHANGED
|
@@ -175,6 +175,7 @@ class PrebufferSession {
|
|
|
175
175
|
choices: [
|
|
176
176
|
'MPEG-TS',
|
|
177
177
|
'RTSP',
|
|
178
|
+
'RTSP+MP4',
|
|
178
179
|
],
|
|
179
180
|
key: this.rebroadcastModeKey,
|
|
180
181
|
value: this.storage.getItem(this.rebroadcastModeKey) || 'MPEG-TS',
|
|
@@ -225,6 +226,15 @@ class PrebufferSession {
|
|
|
225
226
|
return settings;
|
|
226
227
|
}
|
|
227
228
|
|
|
229
|
+
getRebroadcastMode() {
|
|
230
|
+
const mode = this.storage.getItem(this.rebroadcastModeKey);
|
|
231
|
+
const rtspMode = mode?.startsWith('RTSP');
|
|
232
|
+
return {
|
|
233
|
+
rtspMode: mode?.startsWith('RTSP'),
|
|
234
|
+
mp4Mode: !rtspMode || mode?.includes('MP4'),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
228
238
|
async startPrebufferSession() {
|
|
229
239
|
this.prebuffers.mp4 = [];
|
|
230
240
|
this.prebuffers.mpegts = [];
|
|
@@ -270,10 +280,14 @@ class PrebufferSession {
|
|
|
270
280
|
? advertisedAudioCodec?.toLowerCase()
|
|
271
281
|
: detectedAudioCodec?.toLowerCase();
|
|
272
282
|
|
|
283
|
+
// rtsp mode can handle any codec, and its generally better to allow it do that.
|
|
284
|
+
const { rtspMode, mp4Mode } = this.getRebroadcastMode();
|
|
285
|
+
const nonRtsp = !rtspMode || mp4Mode;
|
|
286
|
+
|
|
273
287
|
// after probing the audio codec is complete, alert the user with appropriate instructions.
|
|
274
288
|
// assume the codec is user configurable unless the camera explictly reports otherwise.
|
|
275
289
|
const audioIncompatible = !COMPATIBLE_AUDIO_CODECS.includes(assumedAudioCodec);
|
|
276
|
-
if (!probingAudioCodec && mso?.userConfigurable !== false && !audioSoftMuted) {
|
|
290
|
+
if (nonRtsp && !probingAudioCodec && mso?.userConfigurable !== false && !audioSoftMuted) {
|
|
277
291
|
if (audioIncompatible) {
|
|
278
292
|
// show an alert that rebroadcast needs an explicit setting by the user.
|
|
279
293
|
if (isUsingDefaultAudioConfig) {
|
|
@@ -375,18 +389,14 @@ class PrebufferSession {
|
|
|
375
389
|
parsers: {
|
|
376
390
|
},
|
|
377
391
|
};
|
|
392
|
+
this.parsers = rbo.parsers;
|
|
378
393
|
|
|
379
|
-
const rtspMode = this.storage.getItem(this.rebroadcastModeKey) === 'RTSP';
|
|
380
394
|
this.console.log('rebroadcast mode:', rtspMode ? 'rtsp' : 'mpegts');
|
|
381
395
|
if (!rtspMode) {
|
|
382
396
|
rbo.parsers.mpegts = createMpegTsParser({
|
|
383
397
|
vcodec,
|
|
384
398
|
acodec,
|
|
385
399
|
});
|
|
386
|
-
rbo.parsers.mp4 = createFragmentedMp4Parser({
|
|
387
|
-
vcodec,
|
|
388
|
-
acodec,
|
|
389
|
-
});
|
|
390
400
|
}
|
|
391
401
|
else {
|
|
392
402
|
const parser = createRtspParser();
|
|
@@ -394,9 +404,16 @@ class PrebufferSession {
|
|
|
394
404
|
rbo.parsers.rtsp = parser;
|
|
395
405
|
}
|
|
396
406
|
|
|
397
|
-
|
|
407
|
+
if (mp4Mode) {
|
|
408
|
+
rbo.parsers.mp4 = createFragmentedMp4Parser({
|
|
409
|
+
vcodec,
|
|
410
|
+
acodec,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
398
413
|
|
|
399
414
|
const mo = await this.mixinDevice.getVideoStream(mso);
|
|
415
|
+
const isRfc4571 = mo.mimeType === 'x-scrypted/x-rfc4571';
|
|
416
|
+
|
|
400
417
|
let session: ParserSession<PrebufferParsers>;
|
|
401
418
|
let sessionMso: ResponseMediaStreamOptions;
|
|
402
419
|
|
|
@@ -404,7 +421,7 @@ class PrebufferSession {
|
|
|
404
421
|
// an erroneous cached codec could cause ffmpeg to fail to start.
|
|
405
422
|
this.storage.removeItem(this.lastDetectedAudioCodecKey);
|
|
406
423
|
|
|
407
|
-
if (
|
|
424
|
+
if (rtspMode && isRfc4571 && !mp4Mode) {
|
|
408
425
|
session = await startRFC4571Parser(mo, rbo);
|
|
409
426
|
this.sdp = session.sdp.then(buffers => Buffer.concat(buffers).toString());
|
|
410
427
|
}
|
|
@@ -412,7 +429,7 @@ class PrebufferSession {
|
|
|
412
429
|
const moBuffer = await mediaManager.convertMediaObjectToBuffer(mo, ScryptedMimeTypes.FFmpegInput);
|
|
413
430
|
const ffmpegInput = JSON.parse(moBuffer.toString()) as FFMpegInput;
|
|
414
431
|
sessionMso = ffmpegInput.mediaStreamOptions;
|
|
415
|
-
|
|
432
|
+
|
|
416
433
|
// create missing pts from dts so mpegts and mp4 muxing does not fail
|
|
417
434
|
const extraInputArguments = this.storage.getItem(this.ffmpegInputArgumentsKey) || DEFAULT_FFMPEG_INPUT_ARGUMENTS;
|
|
418
435
|
ffmpegInput.inputArguments.unshift(...extraInputArguments.split(' '));
|
|
@@ -460,7 +477,7 @@ class PrebufferSession {
|
|
|
460
477
|
scheduleRefresh(mso);
|
|
461
478
|
};
|
|
462
479
|
|
|
463
|
-
const scheduleRefresh = (refreshMso:ResponseMediaStreamOptions) => {
|
|
480
|
+
const scheduleRefresh = (refreshMso: ResponseMediaStreamOptions) => {
|
|
464
481
|
const when = refreshMso.refreshAt - Date.now() - 30000;
|
|
465
482
|
this.console.log('refreshing media stream in', when);
|
|
466
483
|
refreshTimeout = setTimeout(refreshStream, when);
|
|
@@ -625,7 +642,7 @@ class PrebufferSession {
|
|
|
625
642
|
return containerUrl;
|
|
626
643
|
}
|
|
627
644
|
|
|
628
|
-
const rtspMode = this.
|
|
645
|
+
const { rtspMode, mp4Mode } = this.getRebroadcastMode();
|
|
629
646
|
const defaultContainer = rtspMode ? 'rtsp' : 'mpegts';
|
|
630
647
|
|
|
631
648
|
const container: PrebufferParsers = this.parsers[options?.container] ? options?.container as PrebufferParsers : defaultContainer;
|
|
@@ -636,7 +653,7 @@ class PrebufferSession {
|
|
|
636
653
|
|
|
637
654
|
const { reencodeAudio } = this.getAudioConfig();
|
|
638
655
|
|
|
639
|
-
if (!rtspMode) {
|
|
656
|
+
if (!rtspMode || container !== 'rtsp') {
|
|
640
657
|
if (this.audioDisabled) {
|
|
641
658
|
mediaStreamOptions.audio = null;
|
|
642
659
|
}
|