@scrypted/prebuffer-mixin 0.1.50 → 0.1.54

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(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=6)}([function(e,t,r){"use strict";var n=Object.create?function(e,t,r,n){void 0===n&&(n=r),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]},o=function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.MixinDeviceBase=t.ScryptedDeviceBase=void 0,o(r(2),t);const i=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,n){this.mixinDevice=e,this.mixinDeviceInterfaces=t,this.mixinProviderNativeId=n,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(i.ScryptedInterfaceProperty))Object.defineProperty(s.prototype,r,{set:t(r),get:e(r)}),Object.defineProperty(a.prototype,r,{set:t(r),get:e(r)})}();let d={};try{d=Object.assign(d,{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=d},function(e,t){e.exports=require("events")},function(e,t,r){"use strict";let n,o,i,s,a,d,c,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=n,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"}(n||(t.ScryptedDeviceType=n={})),t.TemperatureUnit=o,function(e){e.C="C",e.F="F"}(o||(t.TemperatureUnit=o={})),t.ThermostatMode=i,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"}(i||(t.ThermostatMode=i={})),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=d,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"}(d||(t.ScryptedInterface=d={})),t.ScryptedInterfaceProperty=c,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"}(c||(t.ScryptedInterfaceProperty=c={})),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){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(10);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=m();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(3),s=r(4),a=(n=r(1))&&n.__esModule?n:{default:n},d=r(7),c=r(8),u=r(5),p=r(11),l=r(13);function m(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return m=function(){return e},e}function f(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 d.SettingsMixinDeviceBase{constructor(e,t,r,n){super(e,r,{providerNativeId:n,mixinDeviceInterfaces:t,group:"Rebroadcast and Prebuffer Settings",groupKey:"prebuffer"}),f(this,"prebuffers",{mp4:[],mpegts:[]}),f(this,"events",new a.default),f(this,"released",!1),f(this,"detectedIdrInterval",0),f(this,"prevIdr",0),f(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,c.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";let n=this.prebuffers[t],o=0;a.events.on(r,i=>{const s=Date.now();for("mdat"===i.type&&(this.prevIdr&&(this.detectedIdrInterval=s-this.prevIdr),this.prevIdr=s),n.push({time:s,chunk:i});n.length&&n[0].time<s-e;)n.shift(),o++;o>1e3&&(n=this.prebuffers[t]=n.slice(),o=0),this.events.emit(r,i)})}let d;const l=()=>{clearTimeout(d),d=setTimeout(()=>{this.console.error("watchdog for mp4 parser timed out... killing ffmpeg session"),a.kill()},3e4)};return a.events.on("mp4-data",l),a.events.once("killed",()=>clearTimeout(d)),l(),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",d=this.prebuffers[o],c=new i.Server(t=>{c.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));for(const r of e.chunks)t.write(r)};for(const e of d)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(()=>c.close(),3e4);const u=await(0,s.listenZeroCluster)(c),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";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=m,t.parseAudioCodec=f,t.startRebroadcastSession=async function(e,t){let r,a=0,c=!0;const l=new s.EventEmitter,{console:h}=t;let g,v,y;function S(){c&&l.emit("killed"),c=!1,null==x||x.kill();for(const e of O)null==e||e.close()}function b(){t.timeout&&(clearTimeout(r),r=setTimeout(S,t.timeout))}b();const M={},P=e.inputArguments.slice(),O=[];let w;const I=new Promise(e=>w=e);for(const o of Object.keys(t.parsers)){const s=t.parsers[o],d=o+"-data",c=(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(d,n),a--,0===a&&b(),e.destroy()};l.on(d,n),e.on("end",o),e.on("close",o),e.on("error",o)});O.push(c);const u=await(0,i.listenZeroCluster)(c);M[o]={inputArguments:["-f",o,"-i","tcp://127.0.0.1:"+u],mediaStreamOptions:e.mediaStreamOptions};const p=(0,n.createServer)(async e=>{p.close(),w(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()}});O.push(p);const m=await(0,i.listenZeroCluster)(p);P.push(...s.outputArguments,"tcp://127.0.0.1:"+m)}P.unshift("-hide_banner"),h.log(P);const x=o.default.spawn(await u.getFFmpegPath(),P);return(0,d.ffmpegLogInitialOutput)(h,x),x.on("exit",S),f(x).then(e=>g=e),m(x).then(e=>v=e),p(x).then(e=>y=e),await I,{inputAudioCodec:g,inputVideoCodec:v,inputVideoResolution:y,events:l,resetActivityTimer:b,isActive:()=>c,kill:S,servers:O,cp:x,ffmpegInputs:M}};var n=r(3),o=c(r(9)),i=r(4),s=r(1),a=c(r(0)),d=r(5);function c(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 d=o.indexOf(",");-1!==a&&d<a&&(a=d),-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 m(e){return l(e,"Video")}async function f(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),d=i.slice(i.length-s);t=[d],r=d.length,yield{chunks:[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,chunks:[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=e||{};const{size:r,everyNFrames:n}=e;r&&(t=`scale=${r.width}:${r.height}`);n&&n>1&&(t?t+=",":t="",t+=`select=not(mod(n\\,${n}))`);return{container:"rawvideo",outputArguments:[...t?["-vf",t]:[],"-an","-vcodec","rawvideo","-pix_fmt","yuv420p","-f","rawvideo"],async*parse(e,t,n){if(!t||!n)throw new Error("error parsing rawvideo, unknown width and height");const i=(t=(null==r?void 0:r.width)||t)*(n=(null==r?void 0:r.height)||n)*1.5;for(;;){const r=await(0,o.readLength)(e,i);yield{chunks:[r],width:t,height:n}}}}};var n=r(1),o=r(12);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.54",
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', () => {
@@ -177,7 +178,8 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
177
178
 
178
179
  for (const container of ['mpegts', 'mp4']) {
179
180
  const eventName = container + '-data';
180
- const prebufferContainer: PrebufferStreamChunk[] = this.prebuffers[container];
181
+ let prebufferContainer: PrebufferStreamChunk[] = this.prebuffers[container];
182
+ let shifts = 0;
181
183
 
182
184
  session.events.on(eventName, (chunk: StreamChunk) => {
183
185
  const now = Date.now();
@@ -195,12 +197,30 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
195
197
 
196
198
  while (prebufferContainer.length && prebufferContainer[0].time < now - prebufferDurationMs) {
197
199
  prebufferContainer.shift();
200
+ shifts++;
201
+ }
202
+
203
+ if (shifts > 1000) {
204
+ prebufferContainer = this.prebuffers[container] = prebufferContainer.slice();
205
+ shifts = 0;
198
206
  }
199
207
 
200
208
  this.events.emit(eventName, chunk);
201
209
  });
202
210
  }
203
211
 
212
+ let watchdog: NodeJS.Timeout;
213
+ const restartWatchdog = () => {
214
+ clearTimeout(watchdog);
215
+ watchdog = setTimeout(() => {
216
+ this.console.error('watchdog for mp4 parser timed out... killing ffmpeg session');
217
+ session.kill();
218
+ }, 30000);
219
+ }
220
+ session.events.on('mp4-data', restartWatchdog);
221
+ session.events.once('killed', () => clearTimeout(watchdog));
222
+ restartWatchdog();
223
+
204
224
  return session;
205
225
  }
206
226
 
@@ -211,7 +231,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
211
231
 
212
232
  // if a specific stream is requested, and it's not what we're streaming, just fall through to source.
213
233
  if (options?.id && options.id !== session.ffmpegInputs['mpegts'].mediaStreamOptions?.id) {
214
- console.log(this.name, 'rebroadcast session cant be used here', options);
234
+ this.console.log('rebroadcast session cant be used here', options);
215
235
  return this.mixinDevice.getVideoStream(options);
216
236
  }
217
237
 
@@ -221,7 +241,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
221
241
  return mo;
222
242
  }
223
243
 
224
- console.log(this.name, 'prebuffer request started');
244
+ this.console.log('prebuffer request started');
225
245
 
226
246
  const container = options?.container || 'mpegts';
227
247
  const eventName = container + '-data';
@@ -243,7 +263,9 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
243
263
  socket.write(data.startStream)
244
264
  }
245
265
  }
246
- socket.write(data.chunk);
266
+ for (const chunk of data.chunks) {
267
+ socket.write(chunk);
268
+ }
247
269
  };
248
270
 
249
271
  for (const prebuffer of prebufferContainer) {
@@ -255,7 +277,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
255
277
 
256
278
  this.events.on(eventName, writeData);
257
279
  cleanup = () => {
258
- console.log(this.name, 'prebuffer request ended');
280
+ this.console.log('prebuffer request ended');
259
281
  this.events.removeListener(eventName, writeData);
260
282
  this.events.removeListener('killed', cleanup);
261
283
  socket.removeAllListeners();
@@ -299,12 +321,12 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
299
321
  mediaStreamOptions,
300
322
  }
301
323
 
302
- console.log(this.name, 'prebuffer ffmpeg input', ffmpegInput.inputArguments[3]);
324
+ this.console.log('prebuffer ffmpeg input', ffmpegInput.inputArguments[3]);
303
325
  const mo = mediaManager.createFFmpegMediaObject(ffmpegInput);
304
326
  return mo;
305
327
  }
306
328
 
307
- async getVideoStreamOptions(): Promise<void | MediaStreamOptions[]> {
329
+ async getVideoStreamOptions(): Promise<MediaStreamOptions[]> {
308
330
  const ret: MediaStreamOptions[] = await this.mixinDevice.getVideoStreamOptions() || [];
309
331
  let first = ret[0];
310
332
  if (!first) {
@@ -316,10 +338,10 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
316
338
  }
317
339
 
318
340
  release() {
319
- console.log(this.name, 'prebuffer releasing if started');
341
+ this.console.log('prebuffer releasing if started');
320
342
  this.released = true;
321
343
  this.prebufferSession?.then(start => {
322
- console.log(this.name, 'prebuffer released');
344
+ this.console.log('prebuffer released');
323
345
  start.kill();
324
346
  });
325
347
  }