Last active
April 3, 2022 05:31
-
-
Save akirayou/8d206c3d3ec4ef9434c7b20bef50fcd2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script> | |
<script src="digest-fetch.js"></script> | |
<script src="script.js"></script> | |
</head> | |
<body> | |
<a-scene> | |
<a-camera wasd-controls-enabled="false"></a-camera> | |
<a-sky theta_preview id="asky" rotation="0 -90 0"> | |
<a-sphere | |
material=" | |
shader: flat; | |
src:tex.png; | |
blending:multiply; | |
transparent:true; | |
side:back;" | |
position="0 1.7 0" | |
radius="4" > </a-sphere> | |
</a-sky> | |
</a-scene> | |
</body> | |
</html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>replit</title> | |
</head> | |
<body> | |
Hello world | |
<button onclick="THETA.stopPreview();">stop</button> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
nginxなどで、/osc/をTHETAのIPに転送してしたうえで、そこにhtmlをおくことで、corss origin問題を回避する必用がある。 | |
帯域は20Mbps程度 @ THETA Z1 | |
location /osc/ { | |
proxy_pass http://192.168.1.134/osc/; | |
} | |
*/ | |
/* | |
認証をスキップするために以下のライブラリを利用 | |
https://www.npmjs.com/package/digest-fetch | |
*/ | |
const DigestFetch = window.DigestFetch; | |
var THETA = THETA || {}; | |
THETA.fetch=new DigestFetch("THETAYN35100930","35100930", { algorithm: 'MD5' });//THETAのシリアル番号を入れる(パスワード) | |
THETA.sleep = msec => new Promise(resolve => setTimeout(resolve, msec)); | |
THETA.getStatus = async function () { | |
const res = await THETA.fetch.fetch('/osc/state', { method: 'POST' }); | |
const data = await res.json(); | |
console.log(data) | |
return data; | |
} | |
THETA.waitIdle = async function () { | |
for (var i = 0; i < 100; i++) {//wait for max 20 sec | |
const st = await THETA.getStatus(); | |
if (st.state._captureStatus == 'idle') return true; | |
await THETA.sleep(200); | |
} | |
return false; | |
} | |
THETA.exec = async function (cmd) { | |
var ret = await THETA.waitIdle(); | |
if (!ret) return false; | |
const res = await THETA.fetch.fetch('/osc/commands/execute', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(cmd) | |
}); | |
const data = await res.json(); | |
return data; | |
} | |
THETA.startPreview = async function(srcSetFunc) { | |
THETA.isPreviewing = true; | |
var cmd = { | |
'name': 'camera.setOptions', | |
'parameters': { | |
"options": { | |
"previewFormat": {"width": 1920, "height": 960, "framerate": 8} , | |
}, | |
}, | |
} | |
console.log("Setoptions result:"+ JSON.stringify(await THETA.exec(cmd))); | |
cmd = { | |
'name': 'camera.getLivePreview', | |
'parameters': {}, | |
} | |
var ret = await THETA.waitIdle(); | |
if (!ret){ | |
THETA.isPreviewing = false; | |
return false; | |
} | |
const res = await THETA.fetch.fetch('/osc/commands/execute', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify(cmd) | |
}); | |
let headers = ''; | |
let contentLength = -1; | |
let imageBuffer = null; | |
let bytesRead = 0; | |
let frame=null; | |
const getLength = (headers) => { | |
let contentLength = -1; | |
headers.split('\n').forEach((header, _) => { | |
const pair = header.split(':'); | |
if (pair[0].toLowerCase() === 'content-length') contentLength = pair[1]; | |
}) | |
return contentLength; | |
}; | |
const reader = res.body.getReader(); | |
var readBuffer = new Uint8Array(0); | |
while (true) { | |
if (!THETA.isPreviewing)reader.cancel(); | |
const { value, done } = await reader.read(); | |
if (done) { | |
console.log("preview stream end"); | |
break; | |
} | |
tmp=new Uint8Array(readBuffer.length+value.length); | |
tmp.set(readBuffer); | |
tmp.set(value,readBuffer.length); | |
readBuffer=tmp; | |
if(0<contentLength){//JPEGデータそのものをきりだしたい | |
if(readBuffer.length>contentLength){ | |
imageBuffer=readBuffer.slice(0,contentLength); | |
readBuffer=readBuffer.slice(contentLength); | |
srcSetFunc(imageBuffer); | |
imageBuffer=null; | |
contentLength = -1; | |
} | |
}else{//ヘッダを処理したい | |
idx=readBuffer.findIndex(e => e==0xff); | |
if(0<idx){ | |
contentLength = getLength(new TextDecoder().decode( readBuffer.slice(0,idx))); | |
readBuffer=readBuffer.slice(idx) | |
} | |
} | |
} | |
THETA.isPreviewing=false; | |
} | |
THETA.stopPreview = function(){THETA.isPreviewing=false;} | |
start_preview=function () { | |
now_loading=false; | |
THETA.frame_tex=null; | |
const loader=new THREE.TextureLoader(); | |
THETA.startPreview(function(imageBuffer){ //例えばここでimgタグのimg.src frameに入っているblob:~~というURLを設定すれば画像が見える | |
if(THETA.frame_tex===null && now_loading===false){//tickが遅すぎてtextureの消費がまにあってないならskip && texture loader が遅すぎて再突入するのを防止する | |
now_loading=true; | |
frame = URL.createObjectURL(new Blob([imageBuffer], { type: 'image/jpeg' })) | |
loader.load(frame ,(tex) =>{THETA.frame_tex=tex;now_loading=false;URL.revokeObjectURL(frame);} ); //今回はA-Frameようにtextureとして読み込んでとっておく | |
} | |
}).then(); | |
}; | |
AFRAME.registerComponent('theta_preview', { | |
init:function(){ | |
start_preview(); | |
this.material=this.el.getObject3D("mesh").material; | |
}, | |
tick: function (time, timeDelta) { | |
if(THETA.isPreviewing===false)start_preview();// 再スタートする(撮影ボタンなどで中断された時の再起動用) | |
if(THETA.frame_tex){ | |
if(!this.material.map)this.material.needsUpdate=true;//今までtextureが使われてなかったら必須 | |
else this.material.map.dispose();//使い終わったtextureを消す(これやらないとメモリが大変な事になる) | |
this.material.map=THETA.frame_tex; | |
THETA.frame_tex=null; | |
} | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment