@scrypted/prebuffer-mixin 0.1.50 → 0.1.51

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,3 +1,3 @@
1
1
  {
2
- "scrypted.debugHost": "127.0.0.1",
2
+ "scrypted.debugHost": "192.168.2.119",
3
3
  }
@@ -1,2 +1,2 @@
1
- !function(e,t){for(var r in t)e[r]=t[r]}(window,function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},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 n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},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=5)}([function(e,t,r){"use strict";var n=r(6);try{n=Object.assign(n,{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)}e.exports=n,e.exports.default=n},function(e,t){e.exports=require("events")},function(e,t){e.exports=require("net")},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,n.once)(e,"listening"),e.address().port}catch(e){}}};var n=r(1)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(11);Object.keys(n).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===n[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return n[e]}}))}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,o=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=f();if(t&&t.has(e))return t.get(e);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=n?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(r,o,i):r[o]=e[o]}r.default=e,t&&t.set(e,r);return r}(r(0)),i=r(2),s=r(3),a=(n=r(1))&&n.__esModule?n:{default:n},c=r(8),d=r(9),u=r(4),p=r(12),l=r(14);function f(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return f=function(){return e},e}function m(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:h,log:g,systemManager:v,deviceManager:y}=o.default;class S extends c.SettingsMixinDeviceBase{constructor(e,t,r,n){super(e,r,{providerNativeId:n,mixinDeviceInterfaces:t,group:"Rebroadcast and Prebuffer Settings",groupKey:"prebuffer"}),m(this,"prebuffers",{mp4:[],mpegts:[]}),m(this,"events",new a.default),m(this,"released",!1),m(this,"detectedIdrInterval",0),m(this,"prevIdr",0),m(this,"unexpectedPCM",!1),console.log(this.name+" prebuffer session starting in 10 seconds"),setTimeout(()=>this.ensurePrebufferSession(),1e4)}async getMixinSettings(){var e,t,r,n,o,i,s;const a=[];return a.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:"prebufferDuration",value:this.storage.getItem("prebufferDuration")||15e3.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()},{title:"Reencode Audio",description:"Reencode the audio (necessary if camera outputs PCM).",type:"boolean",key:"reencodeAudio",value:("true"===this.storage.getItem("reencodeAudio")).toString()},{group:"Media Information",title:"Detected Resolution",readonly:!0,key:"detectedAcodec",value:""+((null===(e=this.session)||void 0===e||null===(t=e.inputVideoResolution)||void 0===t?void 0:t[0])||"unknown")},{group:"Media Information",title:"Detected Video/Audio Codecs",readonly:!0,key:"detectedVcodec",value:((null===(r=this.session)||void 0===r||null===(n=r.inputVideoCodec)||void 0===n?void 0:n.toString())||"unknown")+"/"+((null===(o=this.session)||void 0===o||null===(i=o.inputAudioCodec)||void 0===i?void 0:i.toString())||"unknown")},{group:"Media Information",title:"Detected Keyframe Interval",description:"Currently detected keyframe interval. This value may vary based on the stream behavior.",readonly:!0,key:"detectedIdr",value:(null===(s=this.detectedIdrInterval)||void 0===s?void 0:s.toString())||"none"}),a}async putMixinSetting(e,t){var r;this.storage.setItem(e,t.toString()),null===(r=this.prebufferSession)||void 0===r||r.then(e=>e.kill())}ensurePrebufferSession(){this.prebufferSession||this.released||(console.log(this.name+" prebuffer session started"),this.prebufferSession=this.startPrebufferSession())}async startPrebufferSession(){this.prebuffers.mp4=[],this.prebuffers.mpegts=[];const e=parseInt(this.storage.getItem("prebufferDuration"))||15e3,t=JSON.parse((await h.convertMediaObjectToBuffer(await this.mixinDevice.getVideoStream(),o.ScryptedMimeTypes.FFmpegInput)).toString()),r="true"===this.storage.getItem("reencodeAudio")||this.unexpectedPCM,n=await(0,u.probeVideoCamera)(this.mixinDevice);let i;i=n.noAudio?["-an"]:r?[]:["-acodec","copy"];const s=["-vcodec","copy"],a=await(0,d.startRebroadcastSession)(t,{parsers:{mp4:(0,p.createFragmentedMp4Parser)({vcodec:s,acodec:i}),mpegts:(0,p.createMpegTsParser)({vcodec:s,acodec:i})}});this.session=a,this.session.inputAudioCodec?"aac"!==this.session.inputAudioCodec&&(console.error(this.name,"Detected audio codec was not AAC."),n.noAudio||!a.inputAudioCodec||-1===a.inputAudioCodec.indexOf("pcm")||r||(g.a(this.name+" is using PCM audio and will be reencoded. Enable Reencode Audio in Rebroadcast Settings to disable this alert."),this.unexpectedPCM=!0)):console.warn(this.name,"no audio detected."),"h264"!==this.session.inputVideoCodec&&console.error(this.name+" video codec is not h264. If there are errors, try changing your camera's encoder output."),a.events.on("killed",()=>{this.prebufferSession=void 0});for(const t of["mpegts","mp4"]){const r=t+"-data",n=this.prebuffers[t];a.events.on(r,t=>{const o=Date.now();for("mdat"===t.type&&(this.prevIdr&&(this.detectedIdrInterval=o-this.prevIdr),this.prevIdr=o),n.push({time:o,chunk:t});n.length&&n[0].time<o-e;)n.shift();this.events.emit(r,t)})}return a}async getVideoStream(e){var t;this.ensurePrebufferSession();const r=await this.prebufferSession;if(null!=e&&e.id&&e.id!==(null===(t=r.ffmpegInputs.mpegts.mediaStreamOptions)||void 0===t?void 0:t.id))return console.log(this.name,"rebroadcast session cant be used here",e),this.mixinDevice.getVideoStream(e);const n="false"!==this.storage.getItem("sendKeyframe");if(!(null!=e&&e.prebuffer||n)){return h.createFFmpegMediaObject(r.ffmpegInputs.mpegts)}console.log(this.name,"prebuffer request started");const o=(null==e?void 0:e.container)||"mpegts",a=o+"-data",c=this.prebuffers[o],d=new i.Server(t=>{d.close();const r=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0),o=Date.now();let i,s=!0;const u=e=>{s&&(s=!1,e.startStream&&t.write(e.startStream)),t.write(e.chunk)};for(const e of c)e.time<o-r||u(e.chunk);this.events.on(a,u),i=()=>{console.log(this.name,"prebuffer request ended"),this.events.removeListener(a,u),this.events.removeListener("killed",i),t.removeAllListeners(),t.destroy()},this.events.once("killed",i),t.once("end",i),t.once("close",i),t.once("error",i)});setTimeout(()=>d.close(),3e4);const u=await(0,s.listenZeroCluster)(d),p=r.ffmpegInputs[o].mediaStreamOptions?Object.assign({},r.ffmpegInputs[o].mediaStreamOptions):void 0;if(p&&p.audio){"true"===this.storage.getItem("reencodeAudio")&&(p.audio={codec:"aac"})}p.video&&Object.assign(p.video,{width:parseInt(this.session.inputVideoResolution[2]),height:parseInt(this.session.inputVideoResolution[3])});const l={inputArguments:["-f",o,"-i","tcp://127.0.0.1:"+u],mediaStreamOptions:p};console.log(this.name,"prebuffer ffmpeg input",l.inputArguments[3]);return h.createFFmpegMediaObject(l)}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=e[0];return t||(t={},e.push(t)),t.prebuffer=parseInt(this.storage.getItem("prebufferDuration"))||15e3,e}release(){var e;console.log(this.name,"prebuffer releasing if started"),this.released=!0,null===(e=this.prebufferSession)||void 0===e||e.then(e=>{console.log(this.name,"prebuffer released"),e.kill()})}}class b extends l.AutoenableMixinProvider{constructor(e){super(e);for(const e of Object.keys(v.getSystemState())){var t;const r=v.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(()=>y.requestRestart(),r)}async canMixin(e,t){return t.includes(o.ScryptedInterface.VideoCamera)?[o.ScryptedInterface.VideoCamera,o.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";const n=r(7);class o{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())}}class i{constructor(e,t,r,n){this.mixinDevice=e,this.mixinDevice=e,this.mixinDeviceInterfaces=t,this._deviceState=r,this.mixinProviderNativeId=n}get storage(){return this._storage||(this._storage=deviceManager.getMixinStorage(this.id,this.mixinProviderNativeId)),this._storage}_lazyLoadDeviceState(){}release(){}}!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(n.ScryptedInterfaceProperty))Object.defineProperty(o.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(i.prototype,r,{set:t(r),get:e(r)})}();const s={ScryptedDeviceBase:o,MixinDeviceBase:i};Object.assign(s,n),e.exports=s,e.exports.default=s},function(e,t,r){"use strict";const n={};e.exports=n,e.exports.default=n,e.exports.ScryptedDeviceType={Builtin:"Builtin",Camera:"Camera",Fan:"Fan",Light:"Light",Switch:"Switch",Outlet:"Outlet",Sensor:"Sensor",Scene:"Scene",Program:"Program",Automation:"Automation",Vacuum:"Vacuum",Notifier:"Notifier",Thermostat:"Thermostat",Lock:"Lock",PasswordControl:"PasswordControl",Display:"Display",Speaker:"Speaker",Event:"Event",Entry:"Entry",Garage:"Garage",DeviceProvider:"DeviceProvider",DataSource:"DataSource",API:"API",Doorbell:"Doorbell",Irrigation:"Irrigation",Valve:"Valve",Unknown:"Unknown"},e.exports.TemperatureUnit={C:"C",F:"F"},e.exports.ThermostatMode={Off:"Off",Cool:"Cool",Heat:"Heat",HeatCool:"HeatCool",Auto:"Auto",FanOnly:"FanOnly",Purifier:"Purifier",Eco:"Eco",Dry:"Dry",On:"On"},e.exports.LockState={Locked:"Locked",Unlocked:"Unlocked",Jammed:"Jammed"},e.exports.MediaPlayerState={Idle:"Idle",Playing:"Playing",Paused:"Paused",Buffering:"Buffering"},e.exports.ScryptedInterface={ScryptedDevice:"ScryptedDevice",OnOff:"OnOff",Brightness:"Brightness",ColorSettingTemperature:"ColorSettingTemperature",ColorSettingRgb:"ColorSettingRgb",ColorSettingHsv:"ColorSettingHsv",Notifier:"Notifier",StartStop:"StartStop",Pause:"Pause",Dock:"Dock",TemperatureSetting:"TemperatureSetting",Thermometer:"Thermometer",HumiditySensor:"HumiditySensor",Camera:"Camera",VideoCamera:"VideoCamera",Intercom:"Intercom",Lock:"Lock",PasswordStore:"PasswordStore",Authenticator:"Authenticator",Scene:"Scene",Entry:"Entry",EntrySensor:"EntrySensor",DeviceProvider:"DeviceProvider",Battery:"Battery",Refresh:"Refresh",MediaPlayer:"MediaPlayer",Online:"Online",SoftwareUpdate:"SoftwareUpdate",BufferConverter:"BufferConverter",Settings:"Settings",BinarySensor:"BinarySensor",IntrusionSensor:"IntrusionSensor",PowerSensor:"PowerSensor",AudioSensor:"AudioSensor",MotionSensor:"MotionSensor",OccupancySensor:"OccupancySensor",FloodSensor:"FloodSensor",UltravioletSensor:"UltravioletSensor",LuminanceSensor:"LuminanceSensor",PositionSensor:"PositionSensor",MediaSource:"MediaSource",MessagingEndpoint:"MessagingEndpoint",OauthClient:"OauthClient",MixinProvider:"MixinProvider",HttpRequestHandler:"HttpRequestHandler",EngineIOHandler:"EngineIOHandler",PushHandler:"PushHandler",Program:"Program",Scriptable:"Scriptable"},e.exports.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"]}},e.exports.ScryptedInterfaceProperty={},Object.values(e.exports.ScryptedInterfaceDescriptors).map(e=>e.properties).flat().forEach(t=>e.exports.ScryptedInterfaceProperty[t]=t),e.exports.ScryptedMimeTypes={AcceptUrlParameter:"accept-url",Url:"text/x-uri",InsecureLocalUrl:"text/x-insecure-local-uri",LocalUrl:"text/x-local-uri",PushEndpoint:"text/x-push-endpoint",FFmpegInput:"x-scrypted/x-ffmpeg-input",RTCAVOffer:"x-scrypted/x-rtc-av-offer",RTCAVAnswer:"x-scrypted/x-rtc-av-answer"}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var n=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=o();if(t&&t.has(e))return t.get(e);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if(Object.prototype.hasOwnProperty.call(e,i)){var s=n?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(r,i,s):r[i]=e[i]}r.default=e,t&&t.set(e,r);return r}(r(0));function o(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return o=function(){return e},e}const{deviceManager:i}=n.default;class s extends n.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(n.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 o=this.settingsGroupKey+":";if(null==e||!e.startsWith(o))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(o.length),t),null===(r=i.onMixinEvent)||void 0===r||r.call(i,this.id,this.mixinProviderNativeId,n.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseResolution=p,t.parseVideoCodec=f,t.parseAudioCodec=m,t.startRebroadcastSession=async function(e,t){let r,a=0,d=!0;const l=new s.EventEmitter;let h,g,v;function y(){d&&l.emit("killed"),d=!1,null==w||w.kill();for(const e of P)null==e||e.close()}function S(){t.timeout&&(clearTimeout(r),r=setTimeout(y,t.timeout))}S();const b={},M=e.inputArguments.slice(),P=[];let x;const O=new Promise(e=>x=e);for(const o of Object.keys(t.parsers)){const s=t.parsers[o],c=o+"-data",d=(0,n.createServer)(e=>{a++,console.log("rebroadcast client",a),clearTimeout(r);let t=!0;const n=r=>{t&&(t=!1,r.startStream&&e.write(r.startStream)),e.write(r.chunk)},o=()=>{e.removeAllListeners(),l.removeListener(c,n),a--,0===a&&S(),e.destroy()};l.on(c,n),e.on("end",o),e.on("close",o),e.on("error",o)});P.push(d);const u=await(0,i.listenZeroCluster)(d);b[o]={inputArguments:["-f",o,"-i","tcp://127.0.0.1:"+u],mediaStreamOptions:e.mediaStreamOptions};const p=(0,n.createServer)(async e=>{p.close(),x(e);try{const n=o+"-data";for await(const o of s.parse(e,parseInt(null===(t=v)||void 0===t?void 0:t[2]),parseInt(null===(r=v)||void 0===r?void 0:r[3]))){var t,r;l.emit(n,o)}}catch(e){console.error("rebroadcast parse error",e),y()}});P.push(p);const f=await(0,i.listenZeroCluster)(p);M.push(...s.outputArguments,"tcp://127.0.0.1:"+f)}M.unshift("-hide_banner"),console.log(M);const w=o.default.spawn(await u.getFFmpegPath(),M);return(0,c.ffmpegLogInitialOutput)(console,w),w.on("exit",y),m(w).then(e=>h=e),f(w).then(e=>g=e),p(w).then(e=>v=e),await O,{inputAudioCodec:h,inputVideoCodec:g,inputVideoResolution:v,events:l,resetActivityTimer:S,isActive:()=>d,kill:y,servers:P,cp:w,ffmpegInputs:b}};var n=r(2),o=d(r(10)),i=r(3),s=r(1),a=d(r(0)),c=r(4);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=n=>{const o=n.toString(),i=/(([0-9]{2,5})x([0-9]{2,5}))/.exec(o);i&&(e.stdout.removeListener("data",r),e.stderr.removeListener("data",r),t(i))};e.stdout.on("data",r),e.stderr.on("data",r)})}async function l(e,t){return new Promise(r=>{const n=o=>{const i=o.toString(),s=i.indexOf(t+": ");if(-1!==s){const o=i.substring(s+t.length+1).trim();let a=o.indexOf(" ");const c=o.indexOf(",");-1!==a&&c<a&&(a=c),-1!==a&&(e.stdout.removeListener("data",n),e.stderr.removeListener("data",n),r(o.substring(0,a)))}};e.stdout.on("data",n),e.stderr.on("data",n)})}async function f(e){return l(e,"Video")}async function m(e){return l(e,"Audio")}},function(e,t){e.exports=require("child_process")},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,r){var o,i;function s(e){const o=i=>{const s=i.toString();for(const e of n)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",o),void t.stderr.removeListener("data",o);e(s)};return o}null===(o=t.stdout)||void 0===o||o.on("data",s(e.log)),null===(i=t.stderr)||void 0===i||i.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 n=["decode_slice_header error","no frame!","non-existing PPS"]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createMpegTsParser=function(e){return{container:"mpegts",outputArguments:["-f","mpegts",...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[]],async*parse(e){let t=[],r=0;for(;;){const o=e.read();if(!o){await(0,n.once)(e,"readable");continue}if(t.push(o),r+=o.length,r<188)continue;const i=Buffer.concat(t);if(71!=i[0])throw new Error("Invalid sync byte in mpeg-ts packet. Terminating stream.");const s=i.length%188,a=i.slice(0,i.length-s),c=i.slice(i.length-s);t=[c],r=c.length,yield{chunk:a}}}}},t.parseFragmentedMP4=i,t.createFragmentedMp4Parser=function(e){return{container:"mp4",outputArguments:["-f","mp4",...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-movflags","frag_keyframe+empty_moov+default_base_moof"],async*parse(e){const t=i(e);let r,n,o;for await(const e of t)r?n||(n=e):r=e,yield{startStream:o,chunk:Buffer.concat([e.header,e.data]),type:e.type},r&&n&&!o&&(o=Buffer.concat([r.header,r.data,n.header,n.data]))}}},t.createRawVideoParser=function(e){let t;e&&(t=["-vf",`scale=${e.width}:${e.height}`]);return{container:"rawvideo",outputArguments:[...t||[],"-an","-vcodec","rawvideo","-pix_fmt","yuv420p","-f","rawvideo"],async*parse(t,r,n){if(!r||!n)throw new Error("error parsing rawvideo, unknown width and height");const i=(r=(null==e?void 0:e.width)||r)*(n=(null==e?void 0:e.height)||n)*1.5;for(;;){const e=await(0,o.readLength)(t,i);yield{chunk:e,width:r,height:n}}}}};var n=r(1),o=r(13);async function*i(e){for(;;){const t=await(0,o.readLength)(e,8),r=t.readInt32BE(0)-8,n=t.slice(4).toString(),i=await(0,o.readLength)(e,r);yield{header:t,length:r,type:n,data:i}}}},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,n)=>{const o=()=>{const n=e.read(t);n&&(s(),r(n))},i=()=>{s(),n(new Error(`stream ended during read for minimum ${t} bytes`))},s=()=>{e.removeListener("readable",o),e.removeListener("end",i)};e.on("readable",o),e.on("end",i)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var n=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=o();if(t&&t.has(e))return t.get(e);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if(Object.prototype.hasOwnProperty.call(e,i)){var s=n?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(r,i,s):r[i]=e[i]}r.default=e,t&&t.set(e,r);return r}(r(0));function o(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return o=function(){return e},e}const{systemManager:i}=n.default;class s extends n.ScryptedDeviceBase{constructor(e){var t,r,o;super(e),o={},(r="hasEnabledMixin")in(t=this)?Object.defineProperty(t,r,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[r]=o;try{this.hasEnabledMixin=JSON.parse(this.storage.getItem("hasEnabledMixin"))}catch(e){this.hasEnabledMixin={}}this.pluginsComponent=i.getComponent("plugins"),i.listen(async(e,t,r)=>{t.eventInterface!==n.ScryptedInterface.ScryptedDevice||t.property||this.maybeEnableMixin(e)});for(const e of Object.keys(i.getSystemState())){const t=i.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 n=await this.pluginsComponent;await n.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(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},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 n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},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=5)}([function(e,t,r){"use strict";var n=r(6);try{n=Object.assign(n,{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)}e.exports=n,e.exports.default=n},function(e,t){e.exports=require("events")},function(e,t){e.exports=require("net")},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,n.once)(e,"listening"),e.address().port}catch(e){}}};var n=r(1)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(12);Object.keys(n).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===n[e]||Object.defineProperty(t,e,{enumerable:!0,get:function(){return n[e]}}))}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,o=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=f();if(t&&t.has(e))return t.get(e);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=n?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(r,o,i):r[o]=e[o]}r.default=e,t&&t.set(e,r);return r}(r(0)),i=r(2),s=r(3),a=(n=r(1))&&n.__esModule?n:{default:n},c=r(9),d=r(10),u=r(4),p=r(13),l=r(15);function f(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return f=function(){return e},e}function m(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:h,log:g,systemManager:v,deviceManager:y}=o.default;class S extends c.SettingsMixinDeviceBase{constructor(e,t,r,n){super(e,r,{providerNativeId:n,mixinDeviceInterfaces:t,group:"Rebroadcast and Prebuffer Settings",groupKey:"prebuffer"}),m(this,"prebuffers",{mp4:[],mpegts:[]}),m(this,"events",new a.default),m(this,"released",!1),m(this,"detectedIdrInterval",0),m(this,"prevIdr",0),m(this,"unexpectedPCM",!1),this.console.log("prebuffer session starting in 10 seconds"),setTimeout(()=>this.ensurePrebufferSession(),1e4)}async getMixinSettings(){var e,t,r,n,o,i,s;const a=[];return a.push({title:"Prebuffer Duration",description:"Duration of the prebuffer in milliseconds.",type:"number",key:"prebufferDuration",value:this.storage.getItem("prebufferDuration")||15e3.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()},{title:"Reencode Audio",description:"Reencode the audio (necessary if camera outputs PCM).",type:"boolean",key:"reencodeAudio",value:("true"===this.storage.getItem("reencodeAudio")).toString()},{group:"Media Information",title:"Detected Resolution",readonly:!0,key:"detectedAcodec",value:""+((null===(e=this.session)||void 0===e||null===(t=e.inputVideoResolution)||void 0===t?void 0:t[0])||"unknown")},{group:"Media Information",title:"Detected Video/Audio Codecs",readonly:!0,key:"detectedVcodec",value:((null===(r=this.session)||void 0===r||null===(n=r.inputVideoCodec)||void 0===n?void 0:n.toString())||"unknown")+"/"+((null===(o=this.session)||void 0===o||null===(i=o.inputAudioCodec)||void 0===i?void 0:i.toString())||"unknown")},{group:"Media Information",title:"Detected Keyframe Interval",description:"Currently detected keyframe interval. This value may vary based on the stream behavior.",readonly:!0,key:"detectedIdr",value:(null===(s=this.detectedIdrInterval)||void 0===s?void 0:s.toString())||"none"}),a}async putMixinSetting(e,t){var r;this.storage.setItem(e,t.toString()),null===(r=this.prebufferSession)||void 0===r||r.then(e=>e.kill())}ensurePrebufferSession(){this.prebufferSession||this.released||(console.log("prebuffer session started"),this.prebufferSession=this.startPrebufferSession())}async startPrebufferSession(){this.prebuffers.mp4=[],this.prebuffers.mpegts=[];const e=parseInt(this.storage.getItem("prebufferDuration"))||15e3,t=JSON.parse((await h.convertMediaObjectToBuffer(await this.mixinDevice.getVideoStream(),o.ScryptedMimeTypes.FFmpegInput)).toString()),r="true"===this.storage.getItem("reencodeAudio")||this.unexpectedPCM,n=await(0,u.probeVideoCamera)(this.mixinDevice);let i;i=n.noAudio?["-an"]:r?[]:["-acodec","copy"];const s=["-vcodec","copy"],a=await(0,d.startRebroadcastSession)(t,{console:this.console,parsers:{mp4:(0,p.createFragmentedMp4Parser)({vcodec:s,acodec:i}),mpegts:(0,p.createMpegTsParser)({vcodec:s,acodec:i})}});this.session=a,this.session.inputAudioCodec?"aac"!==this.session.inputAudioCodec&&(this.console.error("Detected audio codec was not AAC."),n.noAudio||!a.inputAudioCodec||-1===a.inputAudioCodec.indexOf("pcm")||r||(g.a(this.name+" is using PCM audio and will be reencoded. Enable Reencode Audio in Rebroadcast Settings to disable this alert."),this.unexpectedPCM=!0)):this.console.warn("no audio detected."),"h264"!==this.session.inputVideoCodec&&this.console.error("video codec is not h264. If there are errors, try changing your camera's encoder output."),a.events.on("killed",()=>{this.prebufferSession=void 0});for(const t of["mpegts","mp4"]){const r=t+"-data",n=this.prebuffers[t];a.events.on(r,t=>{const o=Date.now();for("mdat"===t.type&&(this.prevIdr&&(this.detectedIdrInterval=o-this.prevIdr),this.prevIdr=o),n.push({time:o,chunk:t});n.length&&n[0].time<o-e;)n.shift();this.events.emit(r,t)})}return a}async getVideoStream(e){var t;this.ensurePrebufferSession();const r=await this.prebufferSession;if(null!=e&&e.id&&e.id!==(null===(t=r.ffmpegInputs.mpegts.mediaStreamOptions)||void 0===t?void 0:t.id))return this.console.log("rebroadcast session cant be used here",e),this.mixinDevice.getVideoStream(e);const n="false"!==this.storage.getItem("sendKeyframe");if(!(null!=e&&e.prebuffer||n)){return h.createFFmpegMediaObject(r.ffmpegInputs.mpegts)}this.console.log("prebuffer request started");const o=(null==e?void 0:e.container)||"mpegts",a=o+"-data",c=this.prebuffers[o],d=new i.Server(t=>{d.close();const r=(null==e?void 0:e.prebuffer)||(n?1.5*Math.max(4e3,this.detectedIdrInterval||4e3):0),o=Date.now();let i,s=!0;const u=e=>{s&&(s=!1,e.startStream&&t.write(e.startStream)),t.write(e.chunk)};for(const e of c)e.time<o-r||u(e.chunk);this.events.on(a,u),i=()=>{this.console.log("prebuffer request ended"),this.events.removeListener(a,u),this.events.removeListener("killed",i),t.removeAllListeners(),t.destroy()},this.events.once("killed",i),t.once("end",i),t.once("close",i),t.once("error",i)});setTimeout(()=>d.close(),3e4);const u=await(0,s.listenZeroCluster)(d),p=r.ffmpegInputs[o].mediaStreamOptions?Object.assign({},r.ffmpegInputs[o].mediaStreamOptions):void 0;if(p&&p.audio){"true"===this.storage.getItem("reencodeAudio")&&(p.audio={codec:"aac"})}p.video&&Object.assign(p.video,{width:parseInt(this.session.inputVideoResolution[2]),height:parseInt(this.session.inputVideoResolution[3])});const l={inputArguments:["-f",o,"-i","tcp://127.0.0.1:"+u],mediaStreamOptions:p};this.console.log("prebuffer ffmpeg input",l.inputArguments[3]);return h.createFFmpegMediaObject(l)}async getVideoStreamOptions(){const e=await this.mixinDevice.getVideoStreamOptions()||[];let t=e[0];return t||(t={},e.push(t)),t.prebuffer=parseInt(this.storage.getItem("prebufferDuration"))||15e3,e}release(){var e;this.console.log("prebuffer releasing if started"),this.released=!0,null===(e=this.prebufferSession)||void 0===e||e.then(e=>{this.console.log("prebuffer released"),e.kill()})}}class b extends l.AutoenableMixinProvider{constructor(e){super(e);for(const e of Object.keys(v.getSystemState())){var t;const r=v.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(()=>y.requestRestart(),r)}async canMixin(e,t){return t.includes(o.ScryptedInterface.VideoCamera)?[o.ScryptedInterface.VideoCamera,o.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";const n=r(7);r(8);class o{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())}}class i{constructor(e,t,r,n){this.mixinDevice=e,this.mixinDevice=e,this.mixinDeviceInterfaces=t,this._deviceState=r,this.mixinProviderNativeId=n}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}_lazyLoadDeviceState(){}release(){}}!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(n.ScryptedInterfaceProperty))Object.defineProperty(o.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(i.prototype,r,{set:t(r),get:e(r)})}();const s={ScryptedDeviceBase:o,MixinDeviceBase:i};Object.assign(s,n),e.exports=s,e.exports.default=s},function(e,t,r){"use strict";const n={};e.exports=n,e.exports.default=n,e.exports.ScryptedDeviceType={Builtin:"Builtin",Camera:"Camera",Fan:"Fan",Light:"Light",Switch:"Switch",Outlet:"Outlet",Sensor:"Sensor",Scene:"Scene",Program:"Program",Automation:"Automation",Vacuum:"Vacuum",Notifier:"Notifier",Thermostat:"Thermostat",Lock:"Lock",PasswordControl:"PasswordControl",Display:"Display",Speaker:"Speaker",Event:"Event",Entry:"Entry",Garage:"Garage",DeviceProvider:"DeviceProvider",DataSource:"DataSource",API:"API",Doorbell:"Doorbell",Irrigation:"Irrigation",Valve:"Valve",Unknown:"Unknown"},e.exports.TemperatureUnit={C:"C",F:"F"},e.exports.ThermostatMode={Off:"Off",Cool:"Cool",Heat:"Heat",HeatCool:"HeatCool",Auto:"Auto",FanOnly:"FanOnly",Purifier:"Purifier",Eco:"Eco",Dry:"Dry",On:"On"},e.exports.LockState={Locked:"Locked",Unlocked:"Unlocked",Jammed:"Jammed"},e.exports.MediaPlayerState={Idle:"Idle",Playing:"Playing",Paused:"Paused",Buffering:"Buffering"},e.exports.ScryptedInterface={ScryptedDevice:"ScryptedDevice",OnOff:"OnOff",Brightness:"Brightness",ColorSettingTemperature:"ColorSettingTemperature",ColorSettingRgb:"ColorSettingRgb",ColorSettingHsv:"ColorSettingHsv",Notifier:"Notifier",StartStop:"StartStop",Pause:"Pause",Dock:"Dock",TemperatureSetting:"TemperatureSetting",Thermometer:"Thermometer",HumiditySensor:"HumiditySensor",Camera:"Camera",VideoCamera:"VideoCamera",Intercom:"Intercom",Lock:"Lock",PasswordStore:"PasswordStore",Authenticator:"Authenticator",Scene:"Scene",Entry:"Entry",EntrySensor:"EntrySensor",DeviceProvider:"DeviceProvider",Battery:"Battery",Refresh:"Refresh",MediaPlayer:"MediaPlayer",Online:"Online",SoftwareUpdate:"SoftwareUpdate",BufferConverter:"BufferConverter",Settings:"Settings",BinarySensor:"BinarySensor",IntrusionSensor:"IntrusionSensor",PowerSensor:"PowerSensor",AudioSensor:"AudioSensor",MotionSensor:"MotionSensor",OccupancySensor:"OccupancySensor",FloodSensor:"FloodSensor",UltravioletSensor:"UltravioletSensor",LuminanceSensor:"LuminanceSensor",PositionSensor:"PositionSensor",MediaSource:"MediaSource",MessagingEndpoint:"MessagingEndpoint",OauthClient:"OauthClient",MixinProvider:"MixinProvider",HttpRequestHandler:"HttpRequestHandler",EngineIOHandler:"EngineIOHandler",PushHandler:"PushHandler",Program:"Program",Scriptable:"Scriptable"},e.exports.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"]}},e.exports.ScryptedInterfaceProperty={},Object.values(e.exports.ScryptedInterfaceDescriptors).map(e=>e.properties).flat().forEach(t=>e.exports.ScryptedInterfaceProperty[t]=t),e.exports.ScryptedMimeTypes={AcceptUrlParameter:"accept-url",Url:"text/x-uri",InsecureLocalUrl:"text/x-insecure-local-uri",LocalUrl:"text/x-local-uri",PushEndpoint:"text/x-push-endpoint",FFmpegInput:"x-scrypted/x-ffmpeg-input",RTCAVOffer:"x-scrypted/x-rtc-av-offer",RTCAVAnswer:"x-scrypted/x-rtc-av-answer"}},function(e,t){e.exports=require("stream")},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SettingsMixinDeviceBase=void 0;var n=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=o();if(t&&t.has(e))return t.get(e);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if(Object.prototype.hasOwnProperty.call(e,i)){var s=n?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(r,i,s):r[i]=e[i]}r.default=e,t&&t.set(e,r);return r}(r(0));function o(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return o=function(){return e},e}const{deviceManager:i}=n.default;class s extends n.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(n.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 o=this.settingsGroupKey+":";if(null==e||!e.startsWith(o))return this.mixinDevice.putSetting(e,t);await this.putMixinSetting(e.substring(o.length),t),null===(r=i.onMixinEvent)||void 0===r||r.call(i,this.id,this.mixinProviderNativeId,n.ScryptedInterface.Settings,null)}}t.SettingsMixinDeviceBase=s},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseResolution=p,t.parseVideoCodec=f,t.parseAudioCodec=m,t.startRebroadcastSession=async function(e,t){let r,a=0,d=!0;const l=new s.EventEmitter,{console:h}=t;let g,v,y;function S(){d&&l.emit("killed"),d=!1,null==I||I.kill();for(const e of x)null==e||e.close()}function b(){t.timeout&&(clearTimeout(r),r=setTimeout(S,t.timeout))}b();const M={},P=e.inputArguments.slice(),x=[];let O;const w=new Promise(e=>O=e);for(const o of Object.keys(t.parsers)){const s=t.parsers[o],c=o+"-data",d=(0,n.createServer)(e=>{a++,h.log("rebroadcast client",a),clearTimeout(r);let t=!0;const n=r=>{t&&(t=!1,r.startStream&&e.write(r.startStream)),e.write(r.chunk)},o=()=>{e.removeAllListeners(),l.removeListener(c,n),a--,0===a&&b(),e.destroy()};l.on(c,n),e.on("end",o),e.on("close",o),e.on("error",o)});x.push(d);const u=await(0,i.listenZeroCluster)(d);M[o]={inputArguments:["-f",o,"-i","tcp://127.0.0.1:"+u],mediaStreamOptions:e.mediaStreamOptions};const p=(0,n.createServer)(async e=>{p.close(),O(e);try{const n=o+"-data";for await(const o of s.parse(e,parseInt(null===(t=y)||void 0===t?void 0:t[2]),parseInt(null===(r=y)||void 0===r?void 0:r[3]))){var t,r;l.emit(n,o)}}catch(e){h.error("rebroadcast parse error",e),S()}});x.push(p);const f=await(0,i.listenZeroCluster)(p);P.push(...s.outputArguments,"tcp://127.0.0.1:"+f)}P.unshift("-hide_banner"),h.log(P);const I=o.default.spawn(await u.getFFmpegPath(),P);return(0,c.ffmpegLogInitialOutput)(h,I),I.on("exit",S),m(I).then(e=>g=e),f(I).then(e=>v=e),p(I).then(e=>y=e),await w,{inputAudioCodec:g,inputVideoCodec:v,inputVideoResolution:y,events:l,resetActivityTimer:b,isActive:()=>d,kill:S,servers:x,cp:I,ffmpegInputs:M}};var n=r(2),o=d(r(11)),i=r(3),s=r(1),a=d(r(0)),c=r(4);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=n=>{const o=n.toString(),i=/(([0-9]{2,5})x([0-9]{2,5}))/.exec(o);i&&(e.stdout.removeListener("data",r),e.stderr.removeListener("data",r),t(i))};e.stdout.on("data",r),e.stderr.on("data",r)})}async function l(e,t){return new Promise(r=>{const n=o=>{const i=o.toString(),s=i.indexOf(t+": ");if(-1!==s){const o=i.substring(s+t.length+1).trim();let a=o.indexOf(" ");const c=o.indexOf(",");-1!==a&&c<a&&(a=c),-1!==a&&(e.stdout.removeListener("data",n),e.stderr.removeListener("data",n),r(o.substring(0,a)))}};e.stdout.on("data",n),e.stderr.on("data",n)})}async function f(e){return l(e,"Video")}async function m(e){return l(e,"Audio")}},function(e,t){e.exports=require("child_process")},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ffmpegLogInitialOutput=function(e,t,r){var o,i;function s(e){const o=i=>{const s=i.toString();for(const e of n)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",o),void t.stderr.removeListener("data",o);e(s)};return o}null===(o=t.stdout)||void 0===o||o.on("data",s(e.log)),null===(i=t.stderr)||void 0===i||i.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 n=["decode_slice_header error","no frame!","non-existing PPS"]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createMpegTsParser=function(e){return{container:"mpegts",outputArguments:["-f","mpegts",...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[]],async*parse(e){let t=[],r=0;for(;;){const o=e.read();if(!o){await(0,n.once)(e,"readable");continue}if(t.push(o),r+=o.length,r<188)continue;const i=Buffer.concat(t);if(71!=i[0])throw new Error("Invalid sync byte in mpeg-ts packet. Terminating stream.");const s=i.length%188,a=i.slice(0,i.length-s),c=i.slice(i.length-s);t=[c],r=c.length,yield{chunk:a}}}}},t.parseFragmentedMP4=i,t.createFragmentedMp4Parser=function(e){return{container:"mp4",outputArguments:["-f","mp4",...(null==e?void 0:e.vcodec)||[],...(null==e?void 0:e.acodec)||[],"-movflags","frag_keyframe+empty_moov+default_base_moof"],async*parse(e){const t=i(e);let r,n,o;for await(const e of t)r?n||(n=e):r=e,yield{startStream:o,chunk:Buffer.concat([e.header,e.data]),type:e.type},r&&n&&!o&&(o=Buffer.concat([r.header,r.data,n.header,n.data]))}}},t.createRawVideoParser=function(e){let t;e&&(t=["-vf",`scale=${e.width}:${e.height}`]);return{container:"rawvideo",outputArguments:[...t||[],"-an","-vcodec","rawvideo","-pix_fmt","yuv420p","-f","rawvideo"],async*parse(t,r,n){if(!r||!n)throw new Error("error parsing rawvideo, unknown width and height");const i=(r=(null==e?void 0:e.width)||r)*(n=(null==e?void 0:e.height)||n)*1.5;for(;;){const e=await(0,o.readLength)(t,i);yield{chunk:e,width:r,height:n}}}}};var n=r(1),o=r(14);async function*i(e){for(;;){const t=await(0,o.readLength)(e,8),r=t.readInt32BE(0)-8,n=t.slice(4).toString(),i=await(0,o.readLength)(e,r);yield{header:t,length:r,type:n,data:i}}}},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,n)=>{const o=()=>{const n=e.read(t);n&&(s(),r(n))},i=()=>{s(),n(new Error(`stream ended during read for minimum ${t} bytes`))},s=()=>{e.removeListener("readable",o),e.removeListener("end",i)};e.on("readable",o),e.on("end",i)})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AutoenableMixinProvider=void 0;var n=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var t=o();if(t&&t.has(e))return t.get(e);var r={},n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if(Object.prototype.hasOwnProperty.call(e,i)){var s=n?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(r,i,s):r[i]=e[i]}r.default=e,t&&t.set(e,r);return r}(r(0));function o(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return o=function(){return e},e}const{systemManager:i}=n.default;class s extends n.ScryptedDeviceBase{constructor(e){var t,r,o;super(e),o={},(r="hasEnabledMixin")in(t=this)?Object.defineProperty(t,r,{value:o,enumerable:!0,configurable:!0,writable:!0}):t[r]=o;try{this.hasEnabledMixin=JSON.parse(this.storage.getItem("hasEnabledMixin"))}catch(e){this.hasEnabledMixin={}}this.pluginsComponent=i.getComponent("plugins"),i.listen(async(e,t,r)=>{t.eventInterface!==n.ScryptedInterface.ScryptedDevice||t.property||this.maybeEnableMixin(e)});for(const e of Object.keys(i.getSystemState())){const t=i.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 n=await this.pluginsComponent;await n.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.50",
3
+ "version": "0.1.51",
4
4
  "description": "Rebroadcast and Prebuffer for VideoCameras.",
5
5
  "author": "Scrypted",
6
6
  "license": "Apache-2.0",
@@ -20,7 +20,7 @@
20
20
  "plugin"
21
21
  ],
22
22
  "scrypted": {
23
- "name": "VideoCamera Rebroadcast",
23
+ "name": "Rebroadcast Plugin",
24
24
  "singleInstance": true,
25
25
  "type": "API",
26
26
  "interfaces": [
package/src/main.ts CHANGED
@@ -47,7 +47,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
47
47
  });
48
48
 
49
49
  // to prevent noisy startup/reload/shutdown, delay the prebuffer starting.
50
- console.log(`${this.name} prebuffer session starting in 10 seconds`);
50
+ this.console.log(`prebuffer session starting in 10 seconds`);
51
51
  setTimeout(() => this.ensurePrebufferSession(), 10000);
52
52
  }
53
53
 
@@ -110,7 +110,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
110
110
  ensurePrebufferSession() {
111
111
  if (this.prebufferSession || this.released)
112
112
  return;
113
- console.log(`${this.name} prebuffer session started`);
113
+ console.log(`prebuffer session started`);
114
114
  this.prebufferSession = this.startPrebufferSession();
115
115
  }
116
116
 
@@ -142,6 +142,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
142
142
  ];
143
143
 
144
144
  const session = await startRebroadcastSession(ffmpegInput, {
145
+ console: this.console,
145
146
  parsers: {
146
147
  mp4: createFragmentedMp4Parser({
147
148
  vcodec,
@@ -157,10 +158,10 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
157
158
  this.session = session;
158
159
 
159
160
  if (!this.session.inputAudioCodec) {
160
- console.warn(this.name, 'no audio detected.');
161
+ this.console.warn('no audio detected.');
161
162
  }
162
163
  else if (this.session.inputAudioCodec !== 'aac') {
163
- console.error(this.name, 'Detected audio codec was not AAC.');
164
+ this.console.error('Detected audio codec was not AAC.');
164
165
  if (!probe.noAudio && session.inputAudioCodec && session.inputAudioCodec.indexOf('pcm') !== -1 && !reencodeAudio) {
165
166
  log.a(`${this.name} is using PCM audio and will be reencoded. Enable Reencode Audio in Rebroadcast Settings to disable this alert.`);
166
167
  this.unexpectedPCM = true;
@@ -168,7 +169,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
168
169
  }
169
170
 
170
171
  if (this.session.inputVideoCodec !== 'h264') {
171
- console.error(`${this.name} video codec is not h264. If there are errors, try changing your camera's encoder output.`);
172
+ this.console.error(`video codec is not h264. If there are errors, try changing your camera's encoder output.`);
172
173
  }
173
174
 
174
175
  session.events.on('killed', () => {
@@ -211,7 +212,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
211
212
 
212
213
  // if a specific stream is requested, and it's not what we're streaming, just fall through to source.
213
214
  if (options?.id && options.id !== session.ffmpegInputs['mpegts'].mediaStreamOptions?.id) {
214
- console.log(this.name, 'rebroadcast session cant be used here', options);
215
+ this.console.log('rebroadcast session cant be used here', options);
215
216
  return this.mixinDevice.getVideoStream(options);
216
217
  }
217
218
 
@@ -221,7 +222,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
221
222
  return mo;
222
223
  }
223
224
 
224
- console.log(this.name, 'prebuffer request started');
225
+ this.console.log('prebuffer request started');
225
226
 
226
227
  const container = options?.container || 'mpegts';
227
228
  const eventName = container + '-data';
@@ -255,7 +256,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
255
256
 
256
257
  this.events.on(eventName, writeData);
257
258
  cleanup = () => {
258
- console.log(this.name, 'prebuffer request ended');
259
+ this.console.log('prebuffer request ended');
259
260
  this.events.removeListener(eventName, writeData);
260
261
  this.events.removeListener('killed', cleanup);
261
262
  socket.removeAllListeners();
@@ -299,12 +300,12 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
299
300
  mediaStreamOptions,
300
301
  }
301
302
 
302
- console.log(this.name, 'prebuffer ffmpeg input', ffmpegInput.inputArguments[3]);
303
+ this.console.log('prebuffer ffmpeg input', ffmpegInput.inputArguments[3]);
303
304
  const mo = mediaManager.createFFmpegMediaObject(ffmpegInput);
304
305
  return mo;
305
306
  }
306
307
 
307
- async getVideoStreamOptions(): Promise<void | MediaStreamOptions[]> {
308
+ async getVideoStreamOptions(): Promise<MediaStreamOptions[]> {
308
309
  const ret: MediaStreamOptions[] = await this.mixinDevice.getVideoStreamOptions() || [];
309
310
  let first = ret[0];
310
311
  if (!first) {
@@ -316,10 +317,10 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
316
317
  }
317
318
 
318
319
  release() {
319
- console.log(this.name, 'prebuffer releasing if started');
320
+ this.console.log('prebuffer releasing if started');
320
321
  this.released = true;
321
322
  this.prebufferSession?.then(start => {
322
- console.log(this.name, 'prebuffer released');
323
+ this.console.log('prebuffer released');
323
324
  start.kill();
324
325
  });
325
326
  }