@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.
- package/.vscode/settings.json +1 -1
- package/dist/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +2 -2
- package/src/main.ts +13 -12
package/.vscode/settings.json
CHANGED
package/dist/main.nodejs.js
CHANGED
|
@@ -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.
|
|
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": "
|
|
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(
|
|
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(
|
|
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(
|
|
161
|
+
this.console.warn('no audio detected.');
|
|
161
162
|
}
|
|
162
163
|
else if (this.session.inputAudioCodec !== 'aac') {
|
|
163
|
-
console.error(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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<
|
|
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(
|
|
320
|
+
this.console.log('prebuffer releasing if started');
|
|
320
321
|
this.released = true;
|
|
321
322
|
this.prebufferSession?.then(start => {
|
|
322
|
-
console.log(
|
|
323
|
+
this.console.log('prebuffer released');
|
|
323
324
|
start.kill();
|
|
324
325
|
});
|
|
325
326
|
}
|