@scrypted/prebuffer-mixin 0.1.62 → 0.1.66

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.
@@ -1,2 +1,2 @@
1
- !function(e,t){for(var r in t)e[r]=t[r]}(window,function(e){var t={};function r(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{}};return e[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(i,n,function(t){return e[t]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}([function(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(2),t);const o=r(2);class s{constructor(e){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=s;class a{constructor(e,t,r,i){this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=i,this._deviceState=r}get storage(){return this._storage||(this._storage=deviceManager.getMixinStorage(this.id,this.mixinProviderNativeId)),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.mixinProviderNativeId,e,t)}_lazyLoadDeviceState(){}release(){}}t.MixinDeviceBase=a,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(s.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(a.prototype,r,{set:t(r),get:e(r)})}();let c={};try{c=Object.assign(c,{log:deviceManager.getDeviceLogger(void 0),deviceManager:deviceManager,endpointManager:endpointManager,mediaManager:mediaManager,systemManager:systemManager,pluginHostAPI:pluginHostAPI})}catch(e){console.error("sdk initialization error, import @scrypted/sdk/types instead",e)}t.default=c},function(e,t){e.exports=require("events")},function(e,t,r){"use strict";let i,n,o,s,a,c,d,u;Object.defineProperty(t,"__esModule",{value:!0}),t.ScryptedInterfaceDescriptors=t.ScryptedMimeTypes=t.ScryptedInterfaceProperty=t.ScryptedInterface=t.MediaPlayerState=t.LockState=t.ThermostatMode=t.TemperatureUnit=t.ScryptedDeviceType=void 0,t.ScryptedDeviceType=i,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"}(i||(t.ScryptedDeviceType=i={})),t.TemperatureUnit=n,function(e){e.C="C",e.F="F"}(n||(t.TemperatureUnit=n={})),t.ThermostatMode=o,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"}(o||(t.ThermostatMode=o={})),t.LockState=s,function(e){e.Locked="Locked",e.Unlocked="Unlocked",e.Jammed="Jammed"}(s||(t.LockState=s={})),t.MediaPlayerState=a,function(e){e.Idle="Idle",e.Playing="Playing",e.Paused="Paused",e.Buffering="Buffering"}(a||(t.MediaPlayerState=a={})),t.ScryptedInterface=c,function(e){e.ScryptedDevice="ScryptedDevice",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.Intercom="Intercom",e.Lock="Lock",e.PasswordStore="PasswordStore",e.Authenticator="Authenticator",e.Scene="Scene",e.Entry="Entry",e.EntrySensor="EntrySensor",e.DeviceProvider="DeviceProvider",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.OccupancySensor="OccupancySensor",e.FloodSensor="FloodSensor",e.UltravioletSensor="UltravioletSensor",e.LuminanceSensor="LuminanceSensor",e.PositionSensor="PositionSensor",e.MediaSource="MediaSource",e.MessagingEndpoint="MessagingEndpoint",e.OauthClient="OauthClient",e.MixinProvider="MixinProvider",e.HttpRequestHandler="HttpRequestHandler",e.EngineIOHandler="EngineIOHandler",e.PushHandler="PushHandler",e.Program="Program",e.Scriptable="Scriptable",e.ObjectDetector="ObjectDetector"}(c||(t.ScryptedInterface=c={})),t.ScryptedInterfaceProperty=d,function(e){e.id="id",e.interfaces="interfaces",e.mixins="mixins",e.info="info",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.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.motionDetected="motionDetected",e.audioDetected="audioDetected",e.occupied="occupied",e.flooded="flooded",e.ultraviolet="ultraviolet",e.luminance="luminance",e.position="position"}(d||(t.ScryptedInterfaceProperty=d={})),t.ScryptedMimeTypes=u,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.FFmpegInput="x-scrypted/x-ffmpeg-input",e.RTCAVOffer="x-scrypted/x-rtc-av-offer",e.RTCAVAnswer="x-scrypted/x-rtc-av-answer"}(u||(t.ScryptedMimeTypes=u={}));t.ScryptedInterfaceDescriptors={ScryptedDevice:{name:"ScryptedDevice",properties:["id","interfaces","mixins","info","name","providedInterfaces","providedName","providedRoom","providedType","providerId","room","type"],methods:["listen","setName","setRoom","setType"]},OnOff:{name:"OnOff",properties:["on"],methods:["turnOff","turnOn"]},Brightness:{name:"Brightness",properties:["brightness"],methods:["setBrightness"]},ColorSettingTemperature:{name:"ColorSettingTemperature",properties:["colorTemperature"],methods:["getTemperatureMaxK","getTemperatureMinK","setColorTemperature"]},ColorSettingRgb:{name:"ColorSettingRgb",properties:["rgb"],methods:["setRgb"]},ColorSettingHsv:{name:"ColorSettingHsv",properties:["hsv"],methods:["setHsv"]},Notifier:{name:"Notifier",properties:[],methods:["sendNotification"]},StartStop:{name:"StartStop",properties:["running"],methods:["start","stop"]},Pause:{name:"Pause",properties:["paused"],methods:["pause","resume"]},Dock:{name:"Dock",properties:["docked"],methods:["dock"]},TemperatureSetting:{name:"TemperatureSetting",properties:["thermostatAvailableModes","thermostatMode","thermostatSetpoint","thermostatSetpointHigh","thermostatSetpointLow"],methods:["setThermostatMode","setThermostatSetpoint","setThermostatSetpointHigh","setThermostatSetpointLow"]},Thermometer:{name:"Thermometer",properties:["temperature","temperatureUnit"],methods:[]},HumiditySensor:{name:"HumiditySensor",properties:["humidity"],methods:[]},Camera:{name:"Camera",properties:[],methods:["takePicture"]},VideoCamera:{name:"VideoCamera",properties:[],methods:["getVideoStream","getVideoStreamOptions"]},Intercom:{name:"Intercom",properties:[],methods:["startIntercom","stopIntercom"]},Lock:{name:"Lock",properties:["lockState"],methods:["lock","unlock"]},PasswordStore:{name:"PasswordStore",properties:[],methods:["addPassword","getPasswords","removePassword"]},Authenticator:{name:"Authenticator",properties:[],methods:["checkPassword"]},Scene:{name:"Scene",properties:[],methods:["activate","deactivate","isReversible"]},Entry:{name:"Entry",properties:[],methods:["closeEntry","openEntry"]},EntrySensor:{name:"EntrySensor",properties:["entryOpen"],methods:[]},DeviceProvider:{name:"DeviceProvider",properties:[],methods:["discoverDevices","getDevice"]},Battery:{name:"Battery",properties:["batteryLevel"],methods:[]},Refresh:{name:"Refresh",properties:[],methods:["getRefreshFrequency","refresh"]},MediaPlayer:{name:"MediaPlayer",properties:[],methods:["getMediaStatus","load","seek","skipNext","skipPrevious"]},Online:{name:"Online",properties:["online"],methods:[]},SoftwareUpdate:{name:"SoftwareUpdate",properties:["updateAvailable"],methods:["checkForUpdate","installUpdate"]},BufferConverter:{name:"BufferConverter",properties:["fromMimeType","toMimeType"],methods:["convert"]},Settings:{name:"Settings",properties:[],methods:["getSettings","putSetting"]},BinarySensor:{name:"BinarySensor",properties:["binaryState"],methods:[]},IntrusionSensor:{name:"IntrusionSensor",properties:["intrusionDetected"],methods:[]},PowerSensor:{name:"PowerSensor",properties:["powerDetected"],methods:[]},AudioSensor:{name:"AudioSensor",properties:["audioDetected"],methods:[]},MotionSensor:{name:"MotionSensor",properties:["motionDetected"],methods:[]},OccupancySensor:{name:"OccupancySensor",properties:["occupied"],methods:[]},FloodSensor:{name:"FloodSensor",properties:["flooded"],methods:[]},UltravioletSensor:{name:"UltravioletSensor",properties:["ultraviolet"],methods:[]},LuminanceSensor:{name:"LuminanceSensor",properties:["luminance"],methods:[]},PositionSensor:{name:"PositionSensor",properties:["position"],methods:[]},MediaSource:{name:"MediaSource",properties:[],methods:["getMedia"]},MessagingEndpoint:{name:"MessagingEndpoint",properties:[],methods:[]},OauthClient:{name:"OauthClient",properties:[],methods:["getOauthUrl","onOauthCallback"]},MixinProvider:{name:"MixinProvider",properties:[],methods:["canMixin","getMixin","releaseMixin"]},HttpRequestHandler:{name:"HttpRequestHandler",properties:[],methods:["onRequest"]},EngineIOHandler:{name:"EngineIOHandler",properties:[],methods:["onConnection"]},PushHandler:{name:"PushHandler",properties:[],methods:["onPush"]},Program:{name:"Program",properties:[],methods:["run"]},Scriptable:{name:"Scriptable",properties:[],methods:["saveScript","loadScripts","eval"]},ObjectDetector:{name:"ObjectDetector",properties:[],methods:["getDetectionInput","getObjectTypes"]}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(10);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]}}))}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i,n=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=p();if(t&&t.has(e))return t.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var o=i?Object.getOwnPropertyDescriptor(e,n):null;o&&(o.get||o.set)?Object.defineProperty(r,n,o):r[n]=e[n]}r.default=e,t&&t.set(e,r);return r}(r(0)),o=(i=r(1))&&i.__esModule?i:{default:i},s=r(5),a=r(6),c=r(3),d=r(11),u=r(13);function p(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return p=function(){return e},e}function l(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:m,log:f,systemManager:h,deviceManager:g}=n.default,v=["aac","mp3","mp2","",void 0,null];class y{constructor(e,t,r){l(this,"prebuffers",{mp4:[],mpegts:[],s16le:[]}),l(this,"events",new o.default),l(this,"detectedIdrInterval",0),l(this,"prevIdr",0),l(this,"incompatibleDetected",!1),l(this,"allowImmediateRestart",!1),l(this,"AUDIO_CONFIGURATION","audioConfiguration-"+this.streamId),this.mixin=e,this.streamName=t,this.streamId=r,this.storage=e.storage,this.console=e.console,this.mixinDevice=e.mixinDevice}ensurePrebufferSession(){this.parserSessionPromise||this.mixin.released||(this.console.log("prebuffer session started",this.streamId),this.parserSessionPromise=this.startPrebufferSession())}getAudioConfig(){const e=this.storage.getItem(this.AUDIO_CONFIGURATION)||"",t=-1!==e.indexOf("PCM Audio"),r=-1!==e.indexOf("Other Audio")||!t&&this.incompatibleDetected;return{audioConfig:e,pcmAudio:t,reencodeAudio:r}}async getMixinSettings(){var e,t,r;const i=[],n=this.parserSession;let o=0,s=0;for(const e of this.prebuffers.mp4){s=s||e.time;for(const t of e.chunk.chunks)o+=t.byteLength}const a=Date.now()-s,c=Math.round(o/a*8),d=this.streamName?"Rebroadcast: "+this.streamName:"Rebroadcast";return i.push({title:"Audio Codec Transcoding",group:d,description:"Configuring your camera to output AAC, MP3, or MP2 is recommended. PCM/G711 cameras should set this to Reencode.",type:"string",key:this.AUDIO_CONFIGURATION,value:this.storage.getItem(this.AUDIO_CONFIGURATION)||"MPEG-TS/MP4 Compatible or No Audio (Copy)",choices:["MPEG-TS/MP4 Compatible or No Audio (Copy)","Other Audio (Transcode)","PCM Audio (Copy, !Experimental!)"]},{group:d,title:"Detected Resolution and Bitrate",readonly:!0,value:`${(null==n||null===(e=n.inputVideoResolution)||void 0===e?void 0:e[0])||"unknown"} @ ${c||"unknown"} Kb/s`,description:"Configuring your camera to 1920x1080 is recommended."},{group:d,title:"Detected Video/Audio Codecs",readonly:!0,value:((null==n||null===(t=n.inputVideoCodec)||void 0===t?void 0:t.toString())||"unknown")+"/"+((null==n||null===(r=n.inputAudioCodec)||void 0===r?void 0:r.toString())||"unknown"),description:"Configuring your camera to H264 video (2000Kb/s) and AAC/MP3/MP2 audio is recommended."},{group:d,title:"Detected Keyframe Interval",description:"Configuring your camera to 4 seconds is recommended (IDR = FPS * 4 seconds).",readonly:!0,value:((this.detectedIdrInterval||0)/1e3).toString()||"none"}),i}async startPrebufferSession(){var e,t;this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.s16le=[];const r=parseInt(this.storage.getItem("prebufferDuration"))||1e4,i=await(0,c.probeVideoCamera)(this.mixinDevice);let o;i.options&&(o=i.options.find(e=>e.id===this.streamId));const s=null==i||null===(e=i.options)||void 0===e||null===(t=e[0].audio)||void 0===t?void 0:t.codec;this.incompatibleDetected=this.incompatibleDetected||s&&!v.includes(s),this.incompatibleDetected&&this.console.warn("configure your camera to output aac, mp3, or mp2 audio. incompatibl audio codec detected",s);const u=JSON.parse((await m.convertMediaObjectToBuffer(await this.mixinDevice.getVideoStream(o),n.ScryptedMimeTypes.FFmpegInput)).toString()),{audioConfig:p,pcmAudio:l,reencodeAudio:h}=this.getAudioConfig();let g;g=i.noAudio||l?["-an"]:h?[]:["-acodec","copy"];const y=["-vcodec","copy"],S={console:this.console,parsers:{mp4:(0,d.createFragmentedMp4Parser)({vcodec:y,acodec:g}),mpegts:(0,d.createMpegTsParser)({vcodec:y,acodec:g})},parseOnly:!0};l&&(S.parsers.s16le=(0,d.createPCMParser)()),this.parsers=S.parsers;const b=await(0,a.startRebroadcastSession)(u,S);let M;this.parserSession=b;const P=()=>{clearTimeout(M),M=setTimeout(()=>{this.console.error("watchdog for mp4 parser timed out... killing ffmpeg session"),b.kill()},6e4)};b.events.on("mp4-data",P),b.events.once("killed",()=>{this.parserSessionPromise=void 0,b.events.removeListener("mp4-data",P),clearTimeout(M)}),P(),b.inputAudioCodec?v.includes(b.inputAudioCodec)||(this.console.error("Detected audio codec was not AAC.",b.inputAudioCodec),p||(f.a(`${this.mixin.name} is using ${b.inputAudioCodec} audio. Enable Reencode Audio in Rebroadcast Settings Audio Configuration to disable this alert.`),this.incompatibleDetected=!0,this.allowImmediateRestart=!0)):this.console.warn("no audio detected."),"h264"!==b.inputVideoCodec&&this.console.error("video codec is not h264. If there are errors, try changing your camera's encoder output.");for(const e of["mpegts","mp4","s16le"]){const t=e+"-data";let i=this.prebuffers[e],n=0;b.events.on(t,o=>{const s=Date.now();for("mdat"===o.type&&(this.prevIdr&&(this.detectedIdrInterval=s-this.prevIdr),this.prevIdr=s),i.push({time:s,chunk:o});i.length&&i[0].time<s-r;)i.shift(),n++;n>1e3&&(i=this.prebuffers[e]=i.slice(),n=0),this.events.emit(t,o)})}return b}async getVideoStream(e){var t,r;this.ensurePrebufferSession();const i=await this.parserSessionPromise,n="false"!==this.storage.getItem("sendKeyframe"),o=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0);if(!(null!=e&&e.prebuffer||n)){return m.createFFmpegMediaObject(i.ffmpegInputs.mpegts)}this.console.log("prebuffer request started",this.streamId);const s=async e=>{const t=e+"-data",r=this.prebuffers[e],{server:n,port:s}=await(0,a.createRebroadcaster)({connect:(e,s)=>{n.close();const a=Date.now(),c=()=>{s(),this.console.log("prebuffer request ended"),this.events.removeListener(t,e),i.events.removeListener("killed",c)};this.events.on(t,e),i.events.once("killed",c);for(const t of r)t.time<a-o||e(t.chunk);return c}});return setTimeout(()=>n.close(),3e4),s},c=(null==e?void 0:e.container)||"mpegts",d=i.ffmpegInputs[c].mediaStreamOptions?Object.assign({},i.ffmpegInputs[c].mediaStreamOptions):{};d.prebuffer=o;const{audioConfig:u,pcmAudio:p,reencodeAudio:l}=this.getAudioConfig();l&&(d.audio={}),d.video&&null!==(t=i.inputVideoResolution)&&void 0!==t&&t[2]&&null!==(r=i.inputVideoResolution)&&void 0!==r&&r[3]&&Object.assign(d.video,{width:parseInt(i.inputVideoResolution[2]),height:parseInt(i.inputVideoResolution[3])});const f={inputArguments:["-f",c,"-i","tcp://127.0.0.1:"+await s(c)],mediaStreamOptions:d};p&&f.inputArguments.push("-f","s16le","-i","tcp://127.0.0.1:"+await s("s16le")),this.console.log("prebuffer ffmpeg input",f.inputArguments);return m.createFFmpegMediaObject(f)}}class S extends s.SettingsMixinDeviceBase{constructor(e,t,r,i){super(e,r,{providerNativeId:i,mixinDeviceInterfaces:t,group:"Prebuffer Settings",groupKey:"prebuffer"}),l(this,"released",!1),l(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();let t=null==e?void 0:e.id;if(!t&&!this.sessions.has(t)){var r;const i=await this.mixinDevice.getVideoStream(e),o=JSON.parse((await m.convertMediaObjectToBuffer(i,n.ScryptedMimeTypes.FFmpegInput)).toString());t=null==o||null===(r=o.mediaStreamOptions)||void 0===r?void 0:r.id,this.sessions.set(null==e?void 0:e.id,this.sessions.get(t))}let i=this.sessions.get(t);return i?(i.ensurePrebufferSession(),await i.parserSessionPromise,i=this.sessions.get(t),i?i.getVideoStream(e):this.mixinDevice.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];for(const t of r){let r=this.sessions.get(t);if(!r){var i;const n=null===(i=e.find(e=>e.id===t))||void 0===i?void 0:i.name;r=new y(this,n,t),r.ensurePrebufferSession(),this.sessions.set(t,r)}}g.onMixinEvent(this.id,this.mixinProviderNativeId,n.ScryptedInterface.Settings,void 0)}async getMixinSettings(){const e=[],t=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(t);r&&e.push({title:"Prebuffered Streams",description:"The streams to prebuffer.",key:"enabledStreams",value:r.map(e=>e.name),choices:t.map(e=>e.name),multiple:!0});for(const t of this.sessions.values())e.push(...await t.getMixinSettings());return e.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:"prebufferDuration",value:this.storage.getItem("prebufferDuration")||1e4.toString()},{title:"Start at Previous Keyframe",description:"Start live streams from the previous key frame. Improves startup time.",type:"boolean",key:"sendKeyframe",value:("false"!==this.storage.getItem("sendKeyframe")).toString()}),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===(i=e.parserSessionPromise)||void 0===i||i.then(e=>e.kill())}this.ensurePrebufferSessions()}getEnabledMediaStreamOptions(e){if(e&&e.length){try{const t=JSON.parse(this.storage.getItem("enabledStreams"));return e.filter(e=>t.includes(e.name))}catch(e){}return[e[0]]}}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=this.getEnabledMediaStreamOptions(e);const r=parseInt(this.storage.getItem("prebufferDuration"))||1e4;if(t)for(const e of t)e.prebuffer=r;else e.push({prebuffer:r});return e}release(){this.console.log("prebuffer releasing if started"),this.released=!0;for(const t of this.sessions.values()){var e;null===(e=t.parserSessionPromise)||void 0===e||e.then(e=>{this.console.log("prebuffer released"),e.kill()})}}}class b extends u.AutoenableMixinProvider{constructor(e){super(e);for(const e of Object.keys(h.getSystemState())){var t;const r=h.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(()=>g.requestRestart(),r)}async canMixin(e,t){return t.includes(n.ScryptedInterface.VideoCamera)?[n.ScryptedInterface.VideoCamera,n.ScryptedInterface.Settings]:null}async getMixin(e,t,r){return this.setHasEnabledMixin(r.id),new S(e,t,r,this.nativeId)}async releaseMixin(e,t){t.release()}}var M=new b;t.default=M},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var i=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=n();if(t&&t.has(e))return t.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var s=i?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(r,o,s):r[o]=e[o]}r.default=e,t&&t.set(e,r);return r}(r(0));function n(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return n=function(){return e},e}const{deviceManager:o}=i.default;class s extends i.MixinDeviceBase{constructor(e,t,r){super(e,r.mixinDeviceInterfaces,t,r.providerNativeId),this.settingsGroup=r.group,this.settingsGroupKey=r.groupKey}async getSettings(){const e=(this.mixinDeviceInterfaces.includes(i.ScryptedInterface.Settings)?await this.mixinDevice.getSettings():[])||[],t=await this.getMixinSettings();for(const e of t)e.group=e.group||this.settingsGroup,e.key=this.settingsGroupKey+":"+e.key;return e.push(...t),e}async putSetting(e,t){var r;const n=this.settingsGroupKey+":";if(null==e||!e.startsWith(n))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(n.length),t),null===(r=o.onMixinEvent)||void 0===r||r.call(o,this.id,this.mixinProviderNativeId,i.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseResolution=p,t.parseVideoCodec=m,t.parseAudioCodec=f,t.startRebroadcastSession=async function(e,t){let r,a=0,d=!0;const l=new s.EventEmitter,{console:g}=t;let v,y,S;function b(){d&&l.emit("killed"),d=!1,null==D||D.kill();for(const e of I)null==e||e.close()}function M(){t.timeout&&(clearTimeout(r),r=setTimeout(b,t.timeout))}M();const P={},O=e.inputArguments.slice(),I=[];let w;const x=new Promise(e=>w=e);for(const n of Object.keys(t.parsers)){const s=t.parsers[n],c=n+"-data";if(P[n]={mediaStreamOptions:e.mediaStreamOptions},!t.parseOnly){const{server:t,port:i}=await h({connect:(e,t)=>{a++,clearTimeout(r);const i=()=>{l.removeListener(c,e),l.removeListener("killed",t),a--,0===a&&M(),t()};return l.on(c,e),l.once("killed",i),i}});I.push(t),e.inputArguments=["-f",n,"-i","tcp://127.0.0.1:"+i]}const d=(0,i.createServer)(async e=>{d.close(),w(e);try{const i=n+"-data";for await(const n of s.parse(e,parseInt(null===(t=S)||void 0===t?void 0:t[2]),parseInt(null===(r=S)||void 0===r?void 0:r[3]))){var t,r;l.emit(i,n)}}catch(e){g.error("rebroadcast parse error",e),b()}});I.push(d);const u=await(0,o.listenZeroCluster)(d);O.push(...s.outputArguments,"tcp://127.0.0.1:"+u)}O.unshift("-hide_banner"),g.log(O);const D=n.default.spawn(await u.getFFmpegPath(),O);return(0,c.ffmpegLogInitialOutput)(g,D),D.on("exit",b),f(D).then(e=>v=e),m(D).then(e=>y=e),p(D).then(e=>S=e),await x,{inputAudioCodec:v,inputVideoCodec:y,inputVideoResolution:S,events:l,resetActivityTimer:M,isActive:()=>d,kill:b,servers:I,cp:D,ffmpegInputs:P}},t.createRebroadcaster=h;var i=r(7),n=d(r(8)),o=r(9),s=r(1),a=d(r(0)),c=r(3);function d(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:u}=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 l(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 m(e){return l(e,"Video")}async function f(e){return l(e,"Audio")}async function h(e){const t=(0,i.createServer)(t=>{let r=!0;const i=()=>{t.removeAllListeners(),t.destroy();const e=n;n=void 0,null==e||e()};let n=null==e?void 0:e.connect(e=>{r&&(r=!1,e.startStream&&t.write(e.startStream));for(const r of e.chunks)t.write(r)},i);t.on("end",i),t.on("close",i),t.on("error",i)});return{server:t,port:await(0,o.listenZeroCluster)(t)}}},function(e,t){e.exports=require("net")},function(e,t){e.exports=require("child_process")},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.listenZeroCluster=async function(e){for(;;){const t=1e4+Math.round(3e4*Math.random());e.listen(t);try{return await(0,i.once)(e,"listening"),e.address().port}catch(e){}}};var i=r(1)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,r){var n,o;function s(e){const n=o=>{const s=o.toString();for(const e of i)if(-1!==s.indexOf(e))return;if(!r&&(-1!==s.indexOf("frame=")||-1!==s.indexOf("size=")))return e(s),e("video/audio detected, discarding further input"),t.stdout.removeListener("data",n),void t.stderr.removeListener("data",n);e(s)};return n}null===(n=t.stdout)||void 0===n||n.on("data",s(e.log)),null===(o=t.stderr)||void 0===o||o.on("data",s(e.error)),t.on("exit",()=>e.log("ffmpeg exited"))},t.probeVideoCamera=async function(e){let t;try{t=await e.getVideoStreamOptions()||[]}catch(e){}const r=t&&t.length&&null===t[0].audio;return{options:t,noAudio:r}};const i=["decode_slice_header error","no frame!","non-existing PPS"]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createPCMParser=function(){return{container:"s16le",outputArguments:["-vn","-acodec","pcm_s16le","-f","s16le"],parse:s(512),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.parseFragmentedMP4=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=a(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.createRawVideoParser=function(e){let t;e=e||{};const{size:r,everyNFrames:i}=e;r&&(t=`scale=${r.width}:${r.height}`);i&&i>1&&(t?t+=",":t="",t+=`select=not(mod(n\\,${i}))`);return{container:"rawvideo",outputArguments:[...t?["-vf",t]:[],"-an","-vcodec","rawvideo","-pix_fmt","yuv420p","-f","rawvideo"],async*parse(e,t,i){if(!t||!i)throw new Error("error parsing rawvideo, unknown width and height");const o=(t=(null==r?void 0:r.width)||t)*(i=(null==r?void 0:r.height)||i)*1.5;for(;;){const r=await(0,n.readLength)(e,o);yield{chunks:[r],width:t,height:i}}},findSyncFrame:o}};var i=r(1),n=r(12);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]}}}}async function*a(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}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.readLength=async function(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)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var i=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=n();if(t&&t.has(e))return t.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var s=i?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(r,o,s):r[o]=e[o]}r.default=e,t&&t.set(e,r);return r}(r(0));function n(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return n=function(){return e},e}const{systemManager:o}=i.default;class s 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 maybeEnableMixin(e){var t;if(!e||null!==(t=e.mixins)&&void 0!==t&&t.includes(this.id))return;if(this.hasEnabledMixin[e.id])return;if(!await this.canMixin(e.type,e.interfaces))return;this.log.i("auto enabling mixin for "+e.name);const r=e.mixins||[];r.push(this.id);const i=await this.pluginsComponent;await i.setMixins(e.id,r)}setHasEnabledMixin(e){this.hasEnabledMixin[e]||(this.hasEnabledMixin[e]=!0,this.storage.setItem("hasEnabledMixin",JSON.stringify(this.hasEnabledMixin)))}}t.AutoenableMixinProvider=s}]));
1
+ !function(e,t){for(var r in t)e[r]=t[r]}(window,function(e){var t={};function r(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{}};return e[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(i,n,function(t){return e[t]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}([function(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(2),t);const o=r(2);class s{constructor(e){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=s;class a{constructor(e,t,r,i){this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=i,this._deviceState=r}get storage(){return this._storage||(this._storage=deviceManager.getMixinStorage(this.id,this.mixinProviderNativeId)),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.mixinProviderNativeId,e,t)}_lazyLoadDeviceState(){}release(){}}t.MixinDeviceBase=a,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(s.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(a.prototype,r,{set:t(r),get:e(r)})}();let c={};try{c=Object.assign(c,{log:deviceManager.getDeviceLogger(void 0),deviceManager:deviceManager,endpointManager:endpointManager,mediaManager:mediaManager,systemManager:systemManager,pluginHostAPI:pluginHostAPI})}catch(e){console.error("sdk initialization error, import @scrypted/sdk/types instead",e)}t.default=c},function(e,t){e.exports=require("events")},function(e,t,r){"use strict";let i,n,o,s,a,c,d,u;Object.defineProperty(t,"__esModule",{value:!0}),t.ScryptedInterfaceDescriptors=t.ScryptedMimeTypes=t.ScryptedInterfaceProperty=t.ScryptedInterface=t.MediaPlayerState=t.LockState=t.ThermostatMode=t.TemperatureUnit=t.ScryptedDeviceType=void 0,t.ScryptedDeviceType=i,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"}(i||(t.ScryptedDeviceType=i={})),t.TemperatureUnit=n,function(e){e.C="C",e.F="F"}(n||(t.TemperatureUnit=n={})),t.ThermostatMode=o,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"}(o||(t.ThermostatMode=o={})),t.LockState=s,function(e){e.Locked="Locked",e.Unlocked="Unlocked",e.Jammed="Jammed"}(s||(t.LockState=s={})),t.MediaPlayerState=a,function(e){e.Idle="Idle",e.Playing="Playing",e.Paused="Paused",e.Buffering="Buffering"}(a||(t.MediaPlayerState=a={})),t.ScryptedInterface=c,function(e){e.ScryptedDevice="ScryptedDevice",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.Intercom="Intercom",e.Lock="Lock",e.PasswordStore="PasswordStore",e.Authenticator="Authenticator",e.Scene="Scene",e.Entry="Entry",e.EntrySensor="EntrySensor",e.DeviceProvider="DeviceProvider",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.OccupancySensor="OccupancySensor",e.FloodSensor="FloodSensor",e.UltravioletSensor="UltravioletSensor",e.LuminanceSensor="LuminanceSensor",e.PositionSensor="PositionSensor",e.MediaSource="MediaSource",e.MessagingEndpoint="MessagingEndpoint",e.OauthClient="OauthClient",e.MixinProvider="MixinProvider",e.HttpRequestHandler="HttpRequestHandler",e.EngineIOHandler="EngineIOHandler",e.PushHandler="PushHandler",e.Program="Program",e.Scriptable="Scriptable",e.ObjectDetector="ObjectDetector"}(c||(t.ScryptedInterface=c={})),t.ScryptedInterfaceProperty=d,function(e){e.id="id",e.interfaces="interfaces",e.mixins="mixins",e.info="info",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.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.motionDetected="motionDetected",e.audioDetected="audioDetected",e.occupied="occupied",e.flooded="flooded",e.ultraviolet="ultraviolet",e.luminance="luminance",e.position="position"}(d||(t.ScryptedInterfaceProperty=d={})),t.ScryptedMimeTypes=u,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.FFmpegInput="x-scrypted/x-ffmpeg-input",e.RTCAVOffer="x-scrypted/x-rtc-av-offer",e.RTCAVAnswer="x-scrypted/x-rtc-av-answer"}(u||(t.ScryptedMimeTypes=u={}));t.ScryptedInterfaceDescriptors={ScryptedDevice:{name:"ScryptedDevice",properties:["id","interfaces","mixins","info","name","providedInterfaces","providedName","providedRoom","providedType","providerId","room","type"],methods:["listen","setName","setRoom","setType"]},OnOff:{name:"OnOff",properties:["on"],methods:["turnOff","turnOn"]},Brightness:{name:"Brightness",properties:["brightness"],methods:["setBrightness"]},ColorSettingTemperature:{name:"ColorSettingTemperature",properties:["colorTemperature"],methods:["getTemperatureMaxK","getTemperatureMinK","setColorTemperature"]},ColorSettingRgb:{name:"ColorSettingRgb",properties:["rgb"],methods:["setRgb"]},ColorSettingHsv:{name:"ColorSettingHsv",properties:["hsv"],methods:["setHsv"]},Notifier:{name:"Notifier",properties:[],methods:["sendNotification"]},StartStop:{name:"StartStop",properties:["running"],methods:["start","stop"]},Pause:{name:"Pause",properties:["paused"],methods:["pause","resume"]},Dock:{name:"Dock",properties:["docked"],methods:["dock"]},TemperatureSetting:{name:"TemperatureSetting",properties:["thermostatAvailableModes","thermostatMode","thermostatSetpoint","thermostatSetpointHigh","thermostatSetpointLow"],methods:["setThermostatMode","setThermostatSetpoint","setThermostatSetpointHigh","setThermostatSetpointLow"]},Thermometer:{name:"Thermometer",properties:["temperature","temperatureUnit"],methods:[]},HumiditySensor:{name:"HumiditySensor",properties:["humidity"],methods:[]},Camera:{name:"Camera",properties:[],methods:["takePicture"]},VideoCamera:{name:"VideoCamera",properties:[],methods:["getVideoStream","getVideoStreamOptions"]},Intercom:{name:"Intercom",properties:[],methods:["startIntercom","stopIntercom"]},Lock:{name:"Lock",properties:["lockState"],methods:["lock","unlock"]},PasswordStore:{name:"PasswordStore",properties:[],methods:["addPassword","getPasswords","removePassword"]},Authenticator:{name:"Authenticator",properties:[],methods:["checkPassword"]},Scene:{name:"Scene",properties:[],methods:["activate","deactivate","isReversible"]},Entry:{name:"Entry",properties:[],methods:["closeEntry","openEntry"]},EntrySensor:{name:"EntrySensor",properties:["entryOpen"],methods:[]},DeviceProvider:{name:"DeviceProvider",properties:[],methods:["discoverDevices","getDevice"]},Battery:{name:"Battery",properties:["batteryLevel"],methods:[]},Refresh:{name:"Refresh",properties:[],methods:["getRefreshFrequency","refresh"]},MediaPlayer:{name:"MediaPlayer",properties:[],methods:["getMediaStatus","load","seek","skipNext","skipPrevious"]},Online:{name:"Online",properties:["online"],methods:[]},SoftwareUpdate:{name:"SoftwareUpdate",properties:["updateAvailable"],methods:["checkForUpdate","installUpdate"]},BufferConverter:{name:"BufferConverter",properties:["fromMimeType","toMimeType"],methods:["convert"]},Settings:{name:"Settings",properties:[],methods:["getSettings","putSetting"]},BinarySensor:{name:"BinarySensor",properties:["binaryState"],methods:[]},IntrusionSensor:{name:"IntrusionSensor",properties:["intrusionDetected"],methods:[]},PowerSensor:{name:"PowerSensor",properties:["powerDetected"],methods:[]},AudioSensor:{name:"AudioSensor",properties:["audioDetected"],methods:[]},MotionSensor:{name:"MotionSensor",properties:["motionDetected"],methods:[]},OccupancySensor:{name:"OccupancySensor",properties:["occupied"],methods:[]},FloodSensor:{name:"FloodSensor",properties:["flooded"],methods:[]},UltravioletSensor:{name:"UltravioletSensor",properties:["ultraviolet"],methods:[]},LuminanceSensor:{name:"LuminanceSensor",properties:["luminance"],methods:[]},PositionSensor:{name:"PositionSensor",properties:["position"],methods:[]},MediaSource:{name:"MediaSource",properties:[],methods:["getMedia"]},MessagingEndpoint:{name:"MessagingEndpoint",properties:[],methods:[]},OauthClient:{name:"OauthClient",properties:[],methods:["getOauthUrl","onOauthCallback"]},MixinProvider:{name:"MixinProvider",properties:[],methods:["canMixin","getMixin","releaseMixin"]},HttpRequestHandler:{name:"HttpRequestHandler",properties:[],methods:["onRequest"]},EngineIOHandler:{name:"EngineIOHandler",properties:[],methods:["onConnection"]},PushHandler:{name:"PushHandler",properties:[],methods:["onPush"]},Program:{name:"Program",properties:[],methods:["run"]},Scriptable:{name:"Scriptable",properties:[],methods:["saveScript","loadScripts","eval"]},ObjectDetector:{name:"ObjectDetector",properties:[],methods:["getDetectionInput","getObjectTypes"]}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=r(10);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]}}))}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i,n=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=p();if(t&&t.has(e))return t.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var o=i?Object.getOwnPropertyDescriptor(e,n):null;o&&(o.get||o.set)?Object.defineProperty(r,n,o):r[n]=e[n]}r.default=e,t&&t.set(e,r);return r}(r(0)),o=(i=r(1))&&i.__esModule?i:{default:i},s=r(5),a=r(6),c=r(3),d=r(11),u=r(13);function p(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return p=function(){return e},e}function l(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:m,log:f,systemManager:h,deviceManager:g}=n.default,v=["aac","mp3","mp2","",void 0,null];class y{constructor(e,t,r){l(this,"prebuffers",{mp4:[],mpegts:[],s16le:[]}),l(this,"events",new o.default),l(this,"detectedIdrInterval",0),l(this,"prevIdr",0),l(this,"incompatibleDetected",!1),l(this,"allowImmediateRestart",!1),l(this,"AUDIO_CONFIGURATION","audioConfiguration-"+this.streamId),this.mixin=e,this.streamName=t,this.streamId=r,this.storage=e.storage,this.console=e.console,this.mixinDevice=e.mixinDevice}ensurePrebufferSession(){this.parserSessionPromise||this.mixin.released||(this.console.log("prebuffer session started",this.streamId),this.parserSessionPromise=this.startPrebufferSession())}getAudioConfig(){const e=this.storage.getItem(this.AUDIO_CONFIGURATION)||"",t=-1!==e.indexOf("PCM Audio"),r=-1!==e.indexOf("Other Audio")||!t&&this.incompatibleDetected;return{audioConfig:e,pcmAudio:t,reencodeAudio:r}}async getMixinSettings(){var e,t,r;const i=[],n=this.parserSession;let o=0,s=0;for(const e of this.prebuffers.mp4){s=s||e.time;for(const t of e.chunk.chunks)o+=t.byteLength}const a=Date.now()-s,c=Math.round(o/a*8),d=this.streamName?"Rebroadcast: "+this.streamName:"Rebroadcast";return i.push({title:"Audio Codec Transcoding",group:d,description:"Configuring your camera to output AAC, MP3, or MP2 is recommended. PCM/G711 cameras should set this to Reencode.",type:"string",key:this.AUDIO_CONFIGURATION,value:this.storage.getItem(this.AUDIO_CONFIGURATION)||"MPEG-TS/MP4 Compatible or No Audio (Copy)",choices:["MPEG-TS/MP4 Compatible or No Audio (Copy)","Other Audio (Transcode)","PCM Audio (Copy, !Experimental!)"]},{key:"detectedResolution",group:d,title:"Detected Resolution and Bitrate",readonly:!0,value:`${(null==n||null===(e=n.inputVideoResolution)||void 0===e?void 0:e[0])||"unknown"} @ ${c||"unknown"} Kb/s`,description:"Configuring your camera to 1920x1080 is recommended."},{key:"detectedCodec",group:d,title:"Detected Video/Audio Codecs",readonly:!0,value:((null==n||null===(t=n.inputVideoCodec)||void 0===t?void 0:t.toString())||"unknown")+"/"+((null==n||null===(r=n.inputAudioCodec)||void 0===r?void 0:r.toString())||"unknown"),description:"Configuring your camera to H264 video (2000Kb/s) and AAC/MP3/MP2 audio is recommended."},{key:"detectedKeyframe",group:d,title:"Detected Keyframe Interval",description:"Configuring your camera to 4 seconds is recommended (IDR = FPS * 4 seconds).",readonly:!0,value:((this.detectedIdrInterval||0)/1e3).toString()||"none"}),i}async startPrebufferSession(){var e,t;this.prebuffers.mp4=[],this.prebuffers.mpegts=[],this.prebuffers.s16le=[];const r=parseInt(this.storage.getItem("prebufferDuration"))||1e4,i=await(0,c.probeVideoCamera)(this.mixinDevice);let o;i.options&&(o=i.options.find(e=>e.id===this.streamId));const s=null==i||null===(e=i.options)||void 0===e||null===(t=e[0].audio)||void 0===t?void 0:t.codec;this.incompatibleDetected=this.incompatibleDetected||s&&!v.includes(s),this.incompatibleDetected&&this.console.warn("configure your camera to output aac, mp3, or mp2 audio. incompatibl audio codec detected",s);const u=JSON.parse((await m.convertMediaObjectToBuffer(await this.mixinDevice.getVideoStream(o),n.ScryptedMimeTypes.FFmpegInput)).toString()),{audioConfig:p,pcmAudio:l,reencodeAudio:h}=this.getAudioConfig();let g;g=i.noAudio||l?["-an"]:h?["-bsf:a","aac_adtstoasc","-acodec","libfdk_aac","-profile:a","aac_eld"]:["-acodec","copy"];const y=["-vcodec","copy"],S={console:this.console,parsers:{mp4:(0,d.createFragmentedMp4Parser)({vcodec:y,acodec:g}),mpegts:(0,d.createMpegTsParser)({vcodec:y,acodec:g})},parseOnly:!0};l&&(S.parsers.s16le=(0,d.createPCMParser)()),this.parsers=S.parsers;const b=await(0,a.startRebroadcastSession)(u,S);let M;this.parserSession=b;const P=()=>{clearTimeout(M),M=setTimeout(()=>{this.console.error("watchdog for mp4 parser timed out... killing ffmpeg session"),b.kill()},6e4)};b.events.on("mp4-data",P),b.events.once("killed",()=>{this.parserSessionPromise=void 0,b.events.removeListener("mp4-data",P),clearTimeout(M)}),P(),b.inputAudioCodec?v.includes(b.inputAudioCodec)||(this.console.error("Detected audio codec was not AAC.",b.inputAudioCodec),p||(f.a(`${this.mixin.name} is using ${b.inputAudioCodec} audio. Enable Reencode Audio in Rebroadcast Settings Audio Configuration to disable this alert.`),this.incompatibleDetected=!0,this.allowImmediateRestart=!0)):this.console.warn("no audio detected."),"h264"!==b.inputVideoCodec&&this.console.error("video codec is not h264. If there are errors, try changing your camera's encoder output.");for(const e of["mpegts","mp4","s16le"]){const t=e+"-data";let i=this.prebuffers[e],n=0;b.events.on(t,o=>{const s=Date.now();for("mdat"===o.type&&(this.prevIdr&&(this.detectedIdrInterval=s-this.prevIdr),this.prevIdr=s),i.push({time:s,chunk:o});i.length&&i[0].time<s-r;)i.shift(),n++;n>1e3&&(i=this.prebuffers[e]=i.slice(),n=0),this.events.emit(t,o)})}return b}async getVideoStream(e){var t,r;this.ensurePrebufferSession();const i=await this.parserSessionPromise,n="false"!==this.storage.getItem("sendKeyframe"),o=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0);if(!(null!=e&&e.prebuffer||n)){return m.createFFmpegMediaObject(i.ffmpegInputs.mpegts)}this.console.log("prebuffer request started",this.streamId);const s=async e=>{const t=e+"-data",r=this.prebuffers[e],{server:n,port:s}=await(0,a.createRebroadcaster)({connect:(e,s)=>{n.close();const a=Date.now(),c=()=>{s(),this.console.log("prebuffer request ended"),this.events.removeListener(t,e),i.events.removeListener("killed",c)};this.events.on(t,e),i.events.once("killed",c);for(const t of r)t.time<a-o||e(t.chunk);return c}});return setTimeout(()=>n.close(),3e4),s},c=(null==e?void 0:e.container)||"mpegts",d=i.ffmpegInputs[c].mediaStreamOptions?Object.assign({},i.ffmpegInputs[c].mediaStreamOptions):{};d.prebuffer=o;const{audioConfig:u,pcmAudio:p,reencodeAudio:l}=this.getAudioConfig();d.audio=l?{codec:"aac"}:{codec:null==i?void 0:i.inputAudioCodec},d.video&&null!==(t=i.inputVideoResolution)&&void 0!==t&&t[2]&&null!==(r=i.inputVideoResolution)&&void 0!==r&&r[3]&&Object.assign(d.video,{width:parseInt(i.inputVideoResolution[2]),height:parseInt(i.inputVideoResolution[3])});const f={inputArguments:["-f",c,"-i","tcp://127.0.0.1:"+await s(c)],mediaStreamOptions:d};p&&f.inputArguments.push("-f","s16le","-i","tcp://127.0.0.1:"+await s("s16le")),this.console.log("prebuffer ffmpeg input",f);return m.createFFmpegMediaObject(f)}}class S extends s.SettingsMixinDeviceBase{constructor(e,t,r,i){super(e,r,{providerNativeId:i,mixinDeviceInterfaces:t,group:"Prebuffer Settings",groupKey:"prebuffer"}),l(this,"released",!1),l(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();let t=null==e?void 0:e.id;if(!t&&!this.sessions.has(t)){var r;const i=await this.mixinDevice.getVideoStream(e),o=JSON.parse((await m.convertMediaObjectToBuffer(i,n.ScryptedMimeTypes.FFmpegInput)).toString());t=null==o||null===(r=o.mediaStreamOptions)||void 0===r?void 0:r.id,this.sessions.set(null==e?void 0:e.id,this.sessions.get(t))}let i=this.sessions.get(t);return i?(i.ensurePrebufferSession(),await i.parserSessionPromise,i=this.sessions.get(t),i?i.getVideoStream(e):this.mixinDevice.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];for(const t of r){let r=this.sessions.get(t);if(!r){var i;const n=null===(i=e.find(e=>e.id===t))||void 0===i?void 0:i.name;r=new y(this,n,t),r.ensurePrebufferSession(),this.sessions.set(t,r)}}g.onMixinEvent(this.id,this.mixinProviderNativeId,n.ScryptedInterface.Settings,void 0)}async getMixinSettings(){const e=[],t=await this.mixinDevice.getVideoStreamOptions(),r=this.getEnabledMediaStreamOptions(t);r&&(null==t?void 0:t.length)>1&&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}),e.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:"prebufferDuration",value:this.storage.getItem("prebufferDuration")||1e4.toString()},{title:"Start at Previous Keyframe",description:"Start live streams from the previous key frame. Improves startup time.",type:"boolean",key:"sendKeyframe",value:("false"!==this.storage.getItem("sendKeyframe")).toString()});for(const t of new Set([...this.sessions.values()]))e.push(...await t.getMixinSettings());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===(i=e.parserSessionPromise)||void 0===i||i.then(e=>e.kill())}this.ensurePrebufferSessions()}getEnabledMediaStreamOptions(e){if(e&&e.length){try{const t=JSON.parse(this.storage.getItem("enabledStreams"));return e.filter(e=>t.includes(e.name))}catch(e){}return[e[0]]}}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=this.getEnabledMediaStreamOptions(e);const r=parseInt(this.storage.getItem("prebufferDuration"))||1e4;if(t)for(const e of t)e.prebuffer=r;else e.push({prebuffer:r});return e}release(){this.console.log("prebuffer releasing if started"),this.released=!0;for(const t of this.sessions.values()){var e;null===(e=t.parserSessionPromise)||void 0===e||e.then(e=>{this.console.log("prebuffer released"),e.kill()})}}}class b extends u.AutoenableMixinProvider{constructor(e){super(e);for(const e of Object.keys(h.getSystemState())){var t;const r=h.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(()=>g.requestRestart(),r)}async canMixin(e,t){return t.includes(n.ScryptedInterface.VideoCamera)?[n.ScryptedInterface.VideoCamera,n.ScryptedInterface.Settings]:null}async getMixin(e,t,r){return this.setHasEnabledMixin(r.id),new S(e,t,r,this.nativeId)}async releaseMixin(e,t){t.release()}}var M=new b;t.default=M},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var i=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=n();if(t&&t.has(e))return t.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var s=i?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(r,o,s):r[o]=e[o]}r.default=e,t&&t.set(e,r);return r}(r(0));function n(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return n=function(){return e},e}const{deviceManager:o}=i.default;class s extends i.MixinDeviceBase{constructor(e,t,r){super(e,r.mixinDeviceInterfaces,t,r.providerNativeId),this.settingsGroup=r.group,this.settingsGroupKey=r.groupKey}async getSettings(){const e=(this.mixinDeviceInterfaces.includes(i.ScryptedInterface.Settings)?await this.mixinDevice.getSettings():[])||[],t=await this.getMixinSettings();for(const e of t)e.group=e.group||this.settingsGroup,e.key=this.settingsGroupKey+":"+e.key;return e.push(...t),e}async putSetting(e,t){var r;const n=this.settingsGroupKey+":";if(null==e||!e.startsWith(n))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(n.length),t),null===(r=o.onMixinEvent)||void 0===r||r.call(o,this.id,this.mixinProviderNativeId,i.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseResolution=p,t.parseVideoCodec=m,t.parseAudioCodec=f,t.startRebroadcastSession=async function(e,t){let r,a=0,d=!0;const l=new s.EventEmitter,{console:g}=t;let v,y,S;function b(){d&&l.emit("killed"),d=!1,null==D||D.kill();for(const e of I)null==e||e.close()}function M(){t.timeout&&(clearTimeout(r),r=setTimeout(b,t.timeout))}M();const P={},O=e.inputArguments.slice(),I=[];let w;const x=new Promise(e=>w=e);for(const n of Object.keys(t.parsers)){const s=t.parsers[n],c=n+"-data";if(P[n]={mediaStreamOptions:e.mediaStreamOptions},!t.parseOnly){const{server:t,port:i}=await h({connect:(e,t)=>{a++,clearTimeout(r);const i=()=>{l.removeListener(c,e),l.removeListener("killed",t),a--,0===a&&M(),t()};return l.on(c,e),l.once("killed",i),i}});I.push(t),e.inputArguments=["-f",n,"-i","tcp://127.0.0.1:"+i]}const d=(0,i.createServer)(async e=>{d.close(),w(e);try{const i=n+"-data";for await(const n of s.parse(e,parseInt(null===(t=S)||void 0===t?void 0:t[2]),parseInt(null===(r=S)||void 0===r?void 0:r[3]))){var t,r;l.emit(i,n)}}catch(e){g.error("rebroadcast parse error",e),b()}});I.push(d);const u=await(0,o.listenZeroCluster)(d);O.push(...s.outputArguments,"tcp://127.0.0.1:"+u)}O.unshift("-hide_banner"),g.log(O);const D=n.default.spawn(await u.getFFmpegPath(),O);return(0,c.ffmpegLogInitialOutput)(g,D),D.on("exit",b),f(D).then(e=>v=e),m(D).then(e=>y=e),p(D).then(e=>S=e),await x,{inputAudioCodec:v,inputVideoCodec:y,inputVideoResolution:S,events:l,resetActivityTimer:M,isActive:()=>d,kill:b,servers:I,cp:D,ffmpegInputs:P}},t.createRebroadcaster=h;var i=r(7),n=d(r(8)),o=r(9),s=r(1),a=d(r(0)),c=r(3);function d(e){return e&&e.__esModule?e:{default:e}}const{mediaManager:u}=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 l(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 m(e){return l(e,"Video")}async function f(e){return l(e,"Audio")}async function h(e){const t=(0,i.createServer)(t=>{let r=!0;const i=()=>{t.removeAllListeners(),t.destroy();const e=n;n=void 0,null==e||e()};let n=null==e?void 0:e.connect(e=>{r&&(r=!1,e.startStream&&t.write(e.startStream));for(const r of e.chunks)t.write(r)},i);t.on("end",i),t.on("close",i),t.on("error",i)});return{server:t,port:await(0,o.listenZeroCluster)(t)}}},function(e,t){e.exports=require("net")},function(e,t){e.exports=require("child_process")},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.listenZeroCluster=async function(e){for(;;){const t=1e4+Math.round(3e4*Math.random());e.listen(t);try{return await(0,i.once)(e,"listening"),e.address().port}catch(e){}}};var i=r(1)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,r){var n,o;function s(e){const n=o=>{const s=o.toString();for(const e of i)if(-1!==s.indexOf(e))return;if(!r&&(-1!==s.indexOf("frame=")||-1!==s.indexOf("size=")))return e(s),e("video/audio detected, discarding further input"),t.stdout.removeListener("data",n),void t.stderr.removeListener("data",n);e(s)};return n}null===(n=t.stdout)||void 0===n||n.on("data",s(e.log)),null===(o=t.stderr)||void 0===o||o.on("data",s(e.error)),t.on("exit",()=>e.log("ffmpeg exited"))},t.probeVideoCamera=async function(e){let t;try{t=await e.getVideoStreamOptions()||[]}catch(e){}const r=t&&t.length&&null===t[0].audio;return{options:t,noAudio:r}};const i=["decode_slice_header error","no frame!","non-existing PPS"]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createPCMParser=function(){return{container:"s16le",outputArguments:["-vn","-acodec","pcm_s16le","-f","s16le"],parse:s(512),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.parseFragmentedMP4=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=a(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.createRawVideoParser=function(e){let t;e=e||{};const{size:r,everyNFrames:i}=e;r&&(t=`scale=${r.width}:${r.height}`);i&&i>1&&(t?t+=",":t="",t+=`select=not(mod(n\\,${i}))`);return{container:"rawvideo",outputArguments:[...t?["-vf",t]:[],"-an","-vcodec","rawvideo","-pix_fmt","yuv420p","-f","rawvideo"],async*parse(e,t,i){if(!t||!i)throw new Error("error parsing rawvideo, unknown width and height");const o=(t=(null==r?void 0:r.width)||t)*(i=(null==r?void 0:r.height)||i)*1.5;for(;;){const r=await(0,n.readLength)(e,o);yield{chunks:[r],width:t,height:i}}},findSyncFrame:o}};var i=r(1),n=r(12);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]}}}}async function*a(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}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.readLength=async function(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)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var i=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=n();if(t&&t.has(e))return t.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var s=i?Object.getOwnPropertyDescriptor(e,o):null;s&&(s.get||s.set)?Object.defineProperty(r,o,s):r[o]=e[o]}r.default=e,t&&t.set(e,r);return r}(r(0));function n(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return n=function(){return e},e}const{systemManager:o}=i.default;class s 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 maybeEnableMixin(e){var t;if(!e||null!==(t=e.mixins)&&void 0!==t&&t.includes(this.id))return;if(this.hasEnabledMixin[e.id])return;if(!await this.canMixin(e.type,e.interfaces))return;this.log.i("auto enabling mixin for "+e.name);const r=e.mixins||[];r.push(this.id);const i=await this.pluginsComponent;await i.setMixins(e.id,r)}setHasEnabledMixin(e){this.hasEnabledMixin[e]||(this.hasEnabledMixin[e]=!0,this.storage.setItem("hasEnabledMixin",JSON.stringify(this.hasEnabledMixin)))}}t.AutoenableMixinProvider=s}]));
2
2
  //# sourceMappingURL=main.nodejs.js.map
package/dist/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrypted/prebuffer-mixin",
3
- "version": "0.1.62",
3
+ "version": "0.1.66",
4
4
  "description": "Rebroadcast and Prebuffer for VideoCameras.",
5
5
  "author": "Scrypted",
6
6
  "license": "Apache-2.0",
package/src/main.ts CHANGED
@@ -118,6 +118,7 @@ class PrebufferSession {
118
118
  ],
119
119
  },
120
120
  {
121
+ key: 'detectedResolution',
121
122
  group,
122
123
  title: 'Detected Resolution and Bitrate',
123
124
  readonly: true,
@@ -125,6 +126,7 @@ class PrebufferSession {
125
126
  description: 'Configuring your camera to 1920x1080 is recommended.',
126
127
  },
127
128
  {
129
+ key: 'detectedCodec',
128
130
  group,
129
131
  title: 'Detected Video/Audio Codecs',
130
132
  readonly: true,
@@ -132,6 +134,7 @@ class PrebufferSession {
132
134
  description: 'Configuring your camera to H264 video (2000Kb/s) and AAC/MP3/MP2 audio is recommended.'
133
135
  },
134
136
  {
137
+ key: 'detectedKeyframe',
135
138
  group,
136
139
  title: 'Detected Keyframe Interval',
137
140
  description: "Configuring your camera to 4 seconds is recommended (IDR = FPS * 4 seconds).",
@@ -169,7 +172,11 @@ class PrebufferSession {
169
172
  }
170
173
  else if (reencodeAudio) {
171
174
  // setting no audio codec will allow ffmpeg to do an implicit conversion.
172
- acodec = [];
175
+ acodec = [
176
+ '-bsf:a', 'aac_adtstoasc',
177
+ '-acodec', 'libfdk_aac',
178
+ '-profile:a', 'aac_eld'
179
+ ];
173
180
  }
174
181
  else {
175
182
  // NOTE: if there is no audio track, this will still work fine.
@@ -349,8 +356,13 @@ class PrebufferSession {
349
356
  const { audioConfig, pcmAudio, reencodeAudio } = this.getAudioConfig();
350
357
 
351
358
  if (reencodeAudio) {
352
- // could be anything... as we let the defaults decide. fix this to hardcode aac maybe.
353
359
  mediaStreamOptions.audio = {
360
+ codec: 'aac',
361
+ }
362
+ }
363
+ else {
364
+ mediaStreamOptions.audio = {
365
+ codec: session?.inputAudioCodec,
354
366
  }
355
367
  }
356
368
 
@@ -376,7 +388,7 @@ class PrebufferSession {
376
388
  )
377
389
  }
378
390
 
379
- this.console.log('prebuffer ffmpeg input', ffmpegInput.inputArguments);
391
+ this.console.log('prebuffer ffmpeg input', ffmpegInput);
380
392
  const mo = mediaManager.createFFmpegMediaObject(ffmpegInput);
381
393
  return mo;
382
394
  }
@@ -445,23 +457,19 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
445
457
 
446
458
  const msos = await this.mixinDevice.getVideoStreamOptions();
447
459
  const enabledStreams = this.getEnabledMediaStreamOptions(msos);
448
- if (enabledStreams) {
460
+ if (enabledStreams && msos?.length > 1) {
449
461
  settings.push(
450
462
  {
451
463
  title: 'Prebuffered Streams',
452
- description: 'The streams to prebuffer.',
464
+ description: 'The streams to prebuffer. Enable only as necessary to reduce traffic.',
453
465
  key: 'enabledStreams',
454
- value: enabledStreams.map(mso => mso.name),
466
+ value: enabledStreams.map(mso => mso.name || ''),
455
467
  choices: msos.map(mso => mso.name),
456
468
  multiple: true,
457
469
  },
458
470
  )
459
471
  }
460
472
 
461
- for (const session of this.sessions.values()) {
462
- settings.push(...await session.getMixinSettings());
463
- }
464
-
465
473
  settings.push(
466
474
  {
467
475
  title: 'Prebuffer Duration',
@@ -478,6 +486,12 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
478
486
  value: (this.storage.getItem(SEND_KEYFRAME) !== 'false').toString(),
479
487
  },
480
488
  );
489
+
490
+
491
+ for (const session of new Set([...this.sessions.values()])) {
492
+ settings.push(...await session.getMixinSettings());
493
+ }
494
+
481
495
  return settings;
482
496
  }
483
497