UNPKG

pixelstream-cn

Version:

基于虚幻引擎的像素流平台

363 lines (336 loc) 13.7 kB
import "./adapter.js"; import run from "./run/index.js"; //引擎运行程序 import actionBase from "./action/base.js"; import action from "./action/index.js"; //引擎动作 import message from "./message.js"; //引擎消息 import player from "./player.js"; import progress from "./progress.js"; import _structure from "./structure/index.js"; import bufferUtil from "./util/buffer.js"; import messageType from "./action/messageType.js"; import gamepad from "./gamepad.js"; /** * @class pixelStream * @function constructor 构造函数 * @param config<Array> * @key keyboard<Array> 允许开放的键盘监听 * @example ["a","s","d","w","A","S","D","W","F1","F2","F3","F4","F5"," "] * @key idleClose<Object> 鼠标/键盘无操作自动关闭配置 {timeout:Number,callback:Function} * @param element<DOM> 渲染的DOM容器对象 * @param tools<Boolen> 工具是否出现 * @param baseHost<String> 云端基础地址 * * @function render 渲染画面 * @param code<String> 渲染编号 * @param screen<String> 画面名称 * @return Promise then {} catch msg<String> resolve以后表示渲染画面成功接入 * * @function send 发送消息 * @param data<pixelStream.action> 给UE程序发送数据对象 该对象按照pixelStream.action对象提供的标准封装 * @return Promise then {} catch msg<String> resolve为针对当前消息UE程序的回复 * * * 继承方式封装动作 pixelStream.action * class MyAction extends pixelStream.action{ * $action="my_test"; * $data={message:"hello world"} * } * * @example * import pixelStream from "pixelStream"; * let ps = new pixelStream({element:this.$refs.engine}); * ps.message.responseHandel=(response)=>{ //收到ue4发送过来的数据包 该方法也会接受发送数据包的响应包 * console.log(response); * } * ps.message.waitHandel=(num)=>{ //排队事件触发 * console.log("前面还有" + num + "人排队"); * } * ps.message.errorHandel=(message)=>{ //错误触发 * console.log("错误消息:" + message); * } * ps.message.speedHandel=(speed)=>{ //流畅度 数字越小越流畅 0-100 * console.log("当前流畅值:" + speed); * } * * ps.render(code,"first3D").then(_=>{ * console.log("渲染画面成功接入"); * ps.send(new MyAction()).then(data=>{ * //收到针对该消息的响应 * }); //采用继承方式封装动作对象 并且发送 * ps.send(new pixelStream.action("my_test",{message:"hello world"})); //世界实例化pixelStream.action对象并发送 * }).catch(e=>{console.error(e)}); */ class pixelStream{ constructor(config){ config = config || {}; this.config={ mode:"ratio", //图像尺寸模式 stretch拉伸 ratio等比(默认) keyboard:[], //允许的键盘 element:null, //渲染的element对象 tools:false, //是否展现工具栏 baseHost:"wss://sws.pixelstream.cn/player", encodeKey:"CljiLwnyih7POqtNja8KeQV6sq77U9ge", mouseSimulate:null, actionTimeout:null, idleClose:null, }; this.run=null; this.rtc=null; this.player=null; this.message=new message(); if(config["mouseSimulate"]){ this.config.mouseSimulate=config["mouseSimulate"]; } if(config["mode"]){ this.config.mode=config["mode"]; } if(config["encodeKey"]){ this.config.encodeKey = config["encodeKey"]; } if(typeof(config["keyboard"])!="undefined"){ this.config.keyboard=config["keyboard"]; } if(config["element"]){ this.config.element=config["element"]; } if(config["tools"]){ this.config.tools=config["tools"]; } if(config["baseHost"]){ this.config.baseHost=config["baseHost"]; } if(config["actionTimeout"]){ this.config.actionTimeout=config["actionTimeout"]; } setIdleCloseConfig(this,config); } setConfig(config){ config = config || {}; if(typeof(config["keyboard"])!="undefined"){ this.config.keyboard=config["keyboard"]; if(this.player){ this.player.setKeyboard(config["keyboard"]); } } if(typeof(config["actionTimeout"])!="undefined"){ this.config.actionTimeout=config["actionTimeout"]; if(isConnected(this)){ this.keepLive(); }else{ clearTimeoutClose(this); } } if(typeof(config["idleClose"])!="undefined" || typeof(config["idleTimeout"])!="undefined"){ setIdleCloseConfig(this,config); if(isConnected(this)){ keepInputLive(this); }else{ clearIdleTimeoutClose(this); } } } render(code,screen){ let _this = this; let swsUrl = this.config.baseHost+"/"+code+"/"+screen; let element = this.config.element; return new Promise((resolve,reject)=>{ if(!element){ reject("渲染对象不存在"); return ; } //初始化外部element对象 let outElement = elementHelper.init(); element.append(outElement); let gamepadObj = new gamepad(); let pro = new progress(outElement); this.run = new run(); this.run.message(this.message).start(swsUrl,this.config.encodeKey,pro).then(rtc=>{ _this.keepLive(); keepInputLive(_this); _this.rtc = rtc; gamepadObj.emitAxisMove = (controllerIndex, axisIndex, analogValue)=>{ _this.send(new action.gamepad.axisMove(controllerIndex, axisIndex, analogValue)); } gamepadObj.emitButtonPressed = (controllerIndex, buttonIndex, isRepeat)=>{ _this.send(new action.gamepad.ButtonPressed(controllerIndex, buttonIndex, isRepeat)); } gamepadObj.emitButtonReleased = (controllerIndex, buttonIndex)=>{ _this.send(new action.gamepad.ButtonReleased(controllerIndex, buttonIndex)); } gamepadObj.init(); _this.rtc.message(_this.message); _this.player = new player(_this.config.tools,_this.config.keyboard,_this.config.mode,_this.config.mouseSimulate); _this.player.init(outElement); _this.player.mouseDownHandel=(button,coord)=>{ sendInput(_this,new action.mouse.down(button,coord)); }; _this.player.mouseMoveHandel=(coord,delta)=>{ sendInput(_this,new action.mouse.move(coord,delta)); }; _this.player.mouseEnterHandel=()=>{ sendInput(_this,new action.mouse.enter()); }; _this.player.mouseLeaveHandel=()=>{ sendInput(_this,new action.mouse.leave()); }; _this.player.mouseUpHandel=(button,coord)=>{ sendInput(_this,new action.mouse.up(button,coord)); }; _this.player.mouseWheelHandel=(coord,delta)=>{ sendInput(_this,new action.mouse.wheel(coord,delta)); }; _this.player.mousePressHandel=(buttons,coord)=>{ if(buttons){ sendInput(_this,new action.mouse.down(0,coord)); sendInput(_this,new action.mouse.down(1,coord)); sendInput(_this,new action.mouse.down(2,coord)); sendInput(_this,new action.mouse.down(3,coord)); sendInput(_this,new action.mouse.down(4,coord)); } }; _this.player.mouseReleaseHandel=(buttons,coord)=>{ if(buttons){ sendInput(_this,new action.mouse.up(0,coord)); sendInput(_this,new action.mouse.up(1,coord)); sendInput(_this,new action.mouse.up(2,coord)); sendInput(_this,new action.mouse.up(3,coord)); sendInput(_this,new action.mouse.up(4,coord)); } }; _this.player.touchStartHandel=(touches)=>{ _this.send(new action.touch.start(touches)); } _this.player.touchEndHandel=(touches)=>{ _this.send(new action.touch.end(touches)); } _this.player.touchMoveHandel=(touches)=>{ _this.send(new action.touch.move(touches)); } _this.player.keyboardDownHandel=(e)=>{ sendInput(_this,new action.keyboard.down(e)); }; _this.player.keyboardUpHandel=(e)=>{ sendInput(_this,new action.keyboard.up(e)); }; _this.player.keyboardPressHandel=(e)=>{ sendInput(_this,new action.keyboard.press(e)); }; rtc.setAudioHandel((stream)=>{ _this.player.audio(stream); }); rtc.setStreamHandel((stream)=>{ pro.over(); _this.player.play(stream); resolve(); }); }).catch(msg=>{ reject(msg); }); }); } close(){ clearTimeoutClose(this); clearIdleTimeoutClose(this); if(this.run){ this.run.close(); this.run=null; }else if(this.rtc && this.rtc.close){ this.rtc.close(); } this.rtc=null; } send(data){ if(!isConnected(this)){ return new Promise((resolve,reject)=>{ reject("还未建立连接,无法发送指令"); }); } this.keepLive(); return this.rtc.send(data); } keepLive(){ clearTimeoutClose(this); if(this.config.actionTimeout){ this.timeoutClose=setTimeout(this.sendTimeout.bind(this),this.config.actionTimeout*1000); } } sendTimeout(){ this.close(); } sendUIInteraction(str){ if(!isConnected(this)){ return new Promise((resolve,reject)=>{ reject("还未建立连接,无法发送指令"); }); } this.keepLive(); let ArrayBuffer = bufferUtil.toBuffer(str,messageType.UIInteraction); return this.rtc.sendBuffer(ArrayBuffer.buffer); } } const idleCloseTimerStore = new WeakMap(); function sendInput(instance,data){ if(isConnected(instance)){ keepInputLive(instance); } return instance.send(data); } function isConnected(instance){ return instance.rtc && instance.rtc.pc && instance.rtc.pc.sctp && instance.rtc.pc.sctp.state=="connected"; } function keepInputLive(instance){ clearIdleTimeoutClose(instance); let idleClose = instance.config.idleClose; if(idleClose && idleClose.timeout){ idleCloseTimerStore.set(instance,setTimeout(()=>{ runIdleClose(instance); },idleClose.timeout*1000)); } } function runIdleClose(instance){ clearIdleTimeoutClose(instance); let idleClose = instance.config.idleClose; if(idleClose && typeof(idleClose.callback)=="function"){ try { idleClose.callback.call(instance,instance); } catch (error) { console.error(error); } } instance.close(); } function setIdleCloseConfig(instance,config){ if(typeof(config["idleClose"])!="undefined"){ instance.config.idleClose=normalizeIdleCloseConfig(config["idleClose"]); return; } if(typeof(config["idleTimeout"])!="undefined"){ instance.config.idleClose=normalizeIdleCloseConfig(config["idleTimeout"]); } } function normalizeIdleCloseConfig(config){ if(typeof(config)=="number"){ return { timeout:config, callback:null, }; } if(config && typeof(config)=="object"){ return { timeout:config.timeout || null, callback:typeof(config.callback)=="function" ? config.callback : null, }; } return null; } function clearTimeoutClose(instance){ if(instance.timeoutClose){ clearTimeout(instance.timeoutClose); instance.timeoutClose=null; } } function clearIdleTimeoutClose(instance){ let timeoutClose = idleCloseTimerStore.get(instance); if(timeoutClose){ clearTimeout(timeoutClose); idleCloseTimerStore.delete(instance); } } pixelStream.action=actionBase; pixelStream.structure=_structure; class elementHelper{ static init(){ let div = document.createElement("div"); div.style.width="100%"; div.style.height="100%"; div.style.position="relative"; div.style.outline="none"; div.style.tabindex="0"; return div; } } export default pixelStream;