mirror of
https://github.com/antos-rde/antosdk-apps.git
synced 2024-12-26 04:08:21 +01:00
use worker on remote camera to decode jpg image
This commit is contained in:
parent
0a6f8c0f8c
commit
71ed35ae00
@ -4,3 +4,6 @@ Connect to a V4L2 camera on server via Antunnel.
|
|||||||
|
|
||||||
This application reauires the **tunel plugin** and the **ant-tunnel v4l2 publisher**
|
This application reauires the **tunel plugin** and the **ant-tunnel v4l2 publisher**
|
||||||
on the server-side
|
on the server-side
|
||||||
|
|
||||||
|
## Change log
|
||||||
|
* v0.1.2-a: user worker for jpeg decoding
|
@ -4,3 +4,6 @@ Connect to a V4L2 camera on server via Antunnel.
|
|||||||
|
|
||||||
This application reauires the **tunel plugin** and the **ant-tunnel v4l2 publisher**
|
This application reauires the **tunel plugin** and the **ant-tunnel v4l2 publisher**
|
||||||
on the server-side
|
on the server-side
|
||||||
|
|
||||||
|
## Change log
|
||||||
|
* v0.1.2-a: user worker for jpeg decoding
|
42
RemoteCamera/build/debug/decoder.js
Normal file
42
RemoteCamera/build/debug/decoder.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
decode = (arr) =>
|
||||||
|
{
|
||||||
|
if(!JpegImage)
|
||||||
|
{
|
||||||
|
console.error("libjpeg is not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let raw = new Uint8Array(arr);
|
||||||
|
let jpeg = new JpegImage();
|
||||||
|
jpeg.parse(raw);
|
||||||
|
let data = jpeg.getData(jpeg.width, jpeg.height);
|
||||||
|
let msg = {
|
||||||
|
w: jpeg.width,
|
||||||
|
h: jpeg.height,
|
||||||
|
pixels: undefined
|
||||||
|
}
|
||||||
|
msg.pixels = new Uint8Array(msg.w*msg.h*4);
|
||||||
|
for(let j = 0; j < msg.h; j++)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < msg.w; i++)
|
||||||
|
{
|
||||||
|
let index = j*msg.w*4 + i*4;
|
||||||
|
msg.pixels[index] = data[j*msg.w*3 + i*3];
|
||||||
|
msg.pixels[index+1] = data[j*msg.w*3 + i*3 + 1];
|
||||||
|
msg.pixels[index+2] = data[j*msg.w*3 + i*3 + 2];
|
||||||
|
msg.pixels[index+3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.pixels = msg.pixels.buffer;
|
||||||
|
postMessage(msg, [msg.pixels]);
|
||||||
|
}
|
||||||
|
|
||||||
|
onmessage = (e) => {
|
||||||
|
if(e.data.libjpeg)
|
||||||
|
{
|
||||||
|
importScripts(e.data.libjpeg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decode(e.data);
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
(function(){var t;(t=class extends this.OS.application.BaseApplication{constructor(t){super("RemoteCamera",t)}main(){var t,e,s;for(this.mute=!1,this.player=this.find("player"),this.qctl=this.find("qctl"),this.fpsctl=this.find("fpsctl"),this.cam_setting={w:640,h:480,fps:10,quality:60},t=[],e=s=5;s<=30;e=s+=5)t.push({text:""+e,value:e});return this.fpsctl.data=t,this.fpsctl.selected=this.cam_setting.fps/5-1,this.fpsctl.onlistselect=t=>{if(!this.mute)return this.cam_setting.fps=t.data.item.data.value,this.setCameraSetting()},this.qctl.value=this.cam_setting.quality,this.resoctl=this.find("resoctl"),this.resoctl.data=[{text:__("320x240"),mode:"qvga"},{text:__("640x480"),selected:!0,mode:"vga"},{text:__("800x600"),mode:"svga"},{text:__("1024x760"),mode:"hd"},{text:__("1920×1080"),mode:"fhd"}],this.resoctl.onlistselect=t=>{if(!this.mute){switch(t.data.item.data.mode){case"qvga":this.cam_setting.w=320,this.cam_setting.h=240;break;case"vga":this.cam_setting.w=640,this.cam_setting.h=480;break;case"svga":this.cam_setting.w=800,this.cam_setting.h=600;break;case"hd":this.cam_setting.w=1024,this.cam_setting.h=768;break;case"fhd":this.cam_setting.w=1920,this.cam_setting.h=1080}return this.setCameraSetting()}},this.qctl.onvaluechange=t=>{if(!this.mute)return this.cam_setting.quality=t.data,this.setCameraSetting()},Antunnel.tunnel?this.setting.channel?this.openSession():this.requestChannel():this.notify(__("Antunnel service is not available"))}requestChannel(){return this.openDialog("PromptDialog",{title:__("Enter camera channel"),label:__("Please enter camera channel name")}).then(t=>(this.setting.channel=t,this.openSession()))}menu(){return{text:"__(Option)",nodes:[{text:"__(Camera channel)"}],onchildselect:t=>this.requestChannel()}}openSession(){if(Antunnel&&this.setting.channel)return this.tunnel=Antunnel.tunnel,this.sub=new Antunnel.Subscriber(this.setting.channel),this.sub.onopen=()=>console.log("Subscribed to camera channel"),this.sub.onerror=t=>this.error(__("Error: {0}",new TextDecoder("utf-8").decode(t.data)),t),this.sub.onctrl=t=>{switch(this.cam_setting.w=Antunnel.Msg.int_from(t.data,0),this.cam_setting.h=Antunnel.Msg.int_from(t.data,2),this.cam_setting.fps=t.data[4],this.cam_setting.quality=t.data[5],this.mute=!0,this.qctl.value=this.cam_setting.quality,`${this.cam_setting.w}x${this.cam_setting.h}`){case"320x240":this.resoctl.selected=0;break;case"640x480":this.resoctl.selected=1;break;case"800x600":this.resoctl.selected=2;break;case"1024x768":this.resoctl.selected=3;break;case"1920x1080":this.resoctl.selected=4}return this.fpsctl.selected=this.cam_setting.fps/5-1,this.mute=!1},this.sub.onmessage=t=>{var e,s,i;return(i=new JpegImage).parse(t.data),e=this.player.getContext("2d"),this.player.width=i.width,this.player.height=i.height,s=e.getImageData(0,0,i.width,i.height),i.copyToImageData(s),e.putImageData(s,0,0)},this.sub.onclose=()=>(this.sub=void 0,this.notify(__("Unsubscribed to the camera service")),this.quit()),Antunnel.tunnel.subscribe(this.sub)}cleanup(){if(this.sub)return this.sub.close()}setCameraSetting(){var t;if(this.sub)return(t=new Uint8Array(6)).set(Antunnel.Msg.bytes_of(this.cam_setting.w),0),t.set(Antunnel.Msg.bytes_of(this.cam_setting.h),2),t[4]=this.cam_setting.fps,t[5]=this.cam_setting.quality,this.sub.send(Antunnel.Msg.CTRL,t)}}).singleton=!0,t.dependencies=["pkg://libjpeg/jpg.js"],this.OS.register("RemoteCamera",t)}).call(this);
|
(function(){var t;(t=class extends this.OS.application.BaseApplication{constructor(t){super("RemoteCamera",t)}main(){var t,e,s;for(this.decoder=new Worker("pkg://RemoteCamera/decoder.js".asFileHandle().getlink()),this.decoder.onmessage=t=>this.paint(t.data),this.decoder.postMessage({libjpeg:"pkg://libjpeg/jpg.js".asFileHandle().getlink()}),this.mute=!1,this.player=this.find("player"),this.qctl=this.find("qctl"),this.fpsctl=this.find("fpsctl"),this.cam_setting={w:640,h:480,fps:10,quality:60},t=[],e=s=5;s<=30;e=s+=5)t.push({text:""+e,value:e});return this.fpsctl.data=t,this.fpsctl.selected=this.cam_setting.fps/5-1,this.fpsctl.onlistselect=t=>{if(!this.mute)return this.cam_setting.fps=t.data.item.data.value,this.setCameraSetting()},this.qctl.value=this.cam_setting.quality,this.resoctl=this.find("resoctl"),this.resoctl.data=[{text:__("320x240"),mode:"qvga"},{text:__("640x480"),selected:!0,mode:"vga"},{text:__("800x600"),mode:"svga"},{text:__("1024x760"),mode:"hd"},{text:__("1920×1080"),mode:"fhd"}],this.resoctl.onlistselect=t=>{if(!this.mute){switch(t.data.item.data.mode){case"qvga":this.cam_setting.w=320,this.cam_setting.h=240;break;case"vga":this.cam_setting.w=640,this.cam_setting.h=480;break;case"svga":this.cam_setting.w=800,this.cam_setting.h=600;break;case"hd":this.cam_setting.w=1024,this.cam_setting.h=768;break;case"fhd":this.cam_setting.w=1920,this.cam_setting.h=1080}return this.setCameraSetting()}},this.qctl.onvaluechange=t=>{if(!this.mute)return this.cam_setting.quality=t.data,this.setCameraSetting()},Antunnel.tunnel?this.setting.channel?this.openSession():this.requestChannel():this.notify(__("Antunnel service is not available"))}requestChannel(){return this.openDialog("PromptDialog",{title:__("Enter camera channel"),label:__("Please enter camera channel name")}).then(t=>(this.setting.channel=t,this.sub?(this.sub.onclose=t=>this.openSession(),this.sub.close()):this.openSession()))}paint(t){var e,s,i;return s=new Uint8Array(t.pixels),e=this.player.getContext("2d",{alpha:!1}),this.player.width=t.w,this.player.height=t.h,(i=e.createImageData(t.w,t.h)).data.set(s),e.putImageData(i,0,0)}menu(){return{text:"__(Option)",nodes:[{text:"__(Camera channel)"}],onchildselect:t=>this.requestChannel()}}openSession(){if(Antunnel&&this.setting.channel)return this.tunnel=Antunnel.tunnel,this.sub=new Antunnel.Subscriber(this.setting.channel),this.sub.onopen=()=>console.log("Subscribed to camera channel"),this.sub.onerror=t=>this.error(__("Error: {0}",new TextDecoder("utf-8").decode(t.data)),t),this.sub.onctrl=t=>{switch(this.cam_setting.w=Antunnel.Msg.int_from(t.data,0),this.cam_setting.h=Antunnel.Msg.int_from(t.data,2),this.cam_setting.fps=t.data[4],this.cam_setting.quality=t.data[5],this.mute=!0,this.qctl.value=this.cam_setting.quality,`${this.cam_setting.w}x${this.cam_setting.h}`){case"320x240":this.resoctl.selected=0;break;case"640x480":this.resoctl.selected=1;break;case"800x600":this.resoctl.selected=2;break;case"1024x768":this.resoctl.selected=3;break;case"1920x1080":this.resoctl.selected=4}return this.fpsctl.selected=this.cam_setting.fps/5-1,this.mute=!1},this.sub.onmessage=t=>{if(console.log("receive"),this.decoder)return this.decoder.postMessage(t.data.buffer,[t.data.buffer])},this.sub.onclose=()=>(this.sub=void 0,this.notify(__("Unsubscribed to the camera service")),this.quit()),Antunnel.tunnel.subscribe(this.sub)}cleanup(){if(this.sub&&this.sub.close(),this.decoder)return this.decoder.terminate()}setCameraSetting(){var t;if(this.sub)return(t=new Uint8Array(6)).set(Antunnel.Msg.bytes_of(this.cam_setting.w),0),t.set(Antunnel.Msg.bytes_of(this.cam_setting.h),2),t[4]=this.cam_setting.fps,t[5]=this.cam_setting.quality,this.sub.send(Antunnel.Msg.CTRL,t)}}).singleton=!0,this.OS.register("RemoteCamera",t)}).call(this);
|
@ -7,7 +7,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"email": ""
|
"email": ""
|
||||||
},
|
},
|
||||||
"version":"0.0.1-a",
|
"version":"0.1.2-a",
|
||||||
"category":"Other",
|
"category":"Other",
|
||||||
"iconclass":"fa fa-camera",
|
"iconclass":"fa fa-camera",
|
||||||
"mimes":["none"],
|
"mimes":["none"],
|
||||||
|
Binary file not shown.
@ -3,6 +3,14 @@ class RemoteCamera extends this.OS.application.BaseApplication
|
|||||||
super "RemoteCamera", args
|
super "RemoteCamera", args
|
||||||
|
|
||||||
main: () ->
|
main: () ->
|
||||||
|
|
||||||
|
@decoder = new Worker("pkg://RemoteCamera/decoder.js".asFileHandle().getlink())
|
||||||
|
|
||||||
|
@decoder.onmessage = (e) =>
|
||||||
|
@paint e.data
|
||||||
|
|
||||||
|
@decoder.postMessage {libjpeg: "pkg://libjpeg/jpg.js".asFileHandle().getlink()}
|
||||||
|
|
||||||
@mute = false
|
@mute = false
|
||||||
@player = @find "player"
|
@player = @find "player"
|
||||||
|
|
||||||
@ -10,6 +18,8 @@ class RemoteCamera extends this.OS.application.BaseApplication
|
|||||||
|
|
||||||
@fpsctl = @find "fpsctl"
|
@fpsctl = @find "fpsctl"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@cam_setting = {
|
@cam_setting = {
|
||||||
w: 640,
|
w: 640,
|
||||||
h: 480,
|
h: 480,
|
||||||
@ -97,7 +107,20 @@ class RemoteCamera extends this.OS.application.BaseApplication
|
|||||||
}
|
}
|
||||||
.then (v) =>
|
.then (v) =>
|
||||||
@setting.channel = v
|
@setting.channel = v
|
||||||
@openSession()
|
return @openSession() unless @sub
|
||||||
|
@sub.onclose = (e) =>
|
||||||
|
@openSession()
|
||||||
|
@sub.close()
|
||||||
|
|
||||||
|
paint: (msg) ->
|
||||||
|
# console.log msg
|
||||||
|
data = new Uint8Array msg.pixels
|
||||||
|
ctx = @player.getContext "2d", { alpha: false }
|
||||||
|
@player.width = msg.w
|
||||||
|
@player.height = msg.h
|
||||||
|
imgData = ctx.createImageData msg.w, msg.h
|
||||||
|
imgData.data.set data
|
||||||
|
ctx.putImageData imgData, 0, 0
|
||||||
|
|
||||||
menu: () ->
|
menu: () ->
|
||||||
{
|
{
|
||||||
@ -142,24 +165,19 @@ class RemoteCamera extends this.OS.application.BaseApplication
|
|||||||
@mute = false
|
@mute = false
|
||||||
|
|
||||||
@sub.onmessage = (e) =>
|
@sub.onmessage = (e) =>
|
||||||
jpeg = new JpegImage()
|
console.log("receive")
|
||||||
jpeg.parse e.data
|
return unless @decoder
|
||||||
context = @player.getContext("2d")
|
@decoder.postMessage e.data.buffer, [e.data.buffer]
|
||||||
@player.width = jpeg.width
|
|
||||||
@player.height = jpeg.height
|
|
||||||
#jpeg.copyToImageData(d)
|
|
||||||
imgData = context.getImageData(0,0,jpeg.width,jpeg.height)
|
|
||||||
jpeg.copyToImageData imgData
|
|
||||||
context.putImageData(imgData, 0, 0)
|
|
||||||
|
|
||||||
@sub.onclose = () =>
|
@sub.onclose = () =>
|
||||||
@sub = undefined
|
@sub = undefined
|
||||||
@notify __("Unsubscribed to the camera service")
|
@notify __("Unsubscribed to the camera service")
|
||||||
@quit()
|
return @quit()
|
||||||
Antunnel.tunnel.subscribe @sub
|
Antunnel.tunnel.subscribe @sub
|
||||||
|
|
||||||
cleanup: () ->
|
cleanup: () ->
|
||||||
@sub.close() if @sub
|
@sub.close() if @sub
|
||||||
|
@decoder.terminate() if @decoder
|
||||||
|
|
||||||
setCameraSetting: () ->
|
setCameraSetting: () ->
|
||||||
return unless @sub
|
return unless @sub
|
||||||
@ -171,7 +189,5 @@ class RemoteCamera extends this.OS.application.BaseApplication
|
|||||||
@sub.send Antunnel.Msg.CTRL, arr
|
@sub.send Antunnel.Msg.CTRL, arr
|
||||||
|
|
||||||
RemoteCamera.singleton = true
|
RemoteCamera.singleton = true
|
||||||
RemoteCamera.dependencies = [
|
|
||||||
"pkg://libjpeg/jpg.js"
|
|
||||||
]
|
|
||||||
this.OS.register "RemoteCamera", RemoteCamera
|
this.OS.register "RemoteCamera", RemoteCamera
|
42
RemoteCamera/js/decoder.js
Normal file
42
RemoteCamera/js/decoder.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
decode = (arr) =>
|
||||||
|
{
|
||||||
|
if(!JpegImage)
|
||||||
|
{
|
||||||
|
console.error("libjpeg is not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let raw = new Uint8Array(arr);
|
||||||
|
let jpeg = new JpegImage();
|
||||||
|
jpeg.parse(raw);
|
||||||
|
let data = jpeg.getData(jpeg.width, jpeg.height);
|
||||||
|
let msg = {
|
||||||
|
w: jpeg.width,
|
||||||
|
h: jpeg.height,
|
||||||
|
pixels: undefined
|
||||||
|
}
|
||||||
|
msg.pixels = new Uint8Array(msg.w*msg.h*4);
|
||||||
|
for(let j = 0; j < msg.h; j++)
|
||||||
|
{
|
||||||
|
for(let i = 0; i < msg.w; i++)
|
||||||
|
{
|
||||||
|
let index = j*msg.w*4 + i*4;
|
||||||
|
msg.pixels[index] = data[j*msg.w*3 + i*3];
|
||||||
|
msg.pixels[index+1] = data[j*msg.w*3 + i*3 + 1];
|
||||||
|
msg.pixels[index+2] = data[j*msg.w*3 + i*3 + 2];
|
||||||
|
msg.pixels[index+3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.pixels = msg.pixels.buffer;
|
||||||
|
postMessage(msg, [msg.pixels]);
|
||||||
|
}
|
||||||
|
|
||||||
|
onmessage = (e) => {
|
||||||
|
if(e.data.libjpeg)
|
||||||
|
{
|
||||||
|
importScripts(e.data.libjpeg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decode(e.data);
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"email": ""
|
"email": ""
|
||||||
},
|
},
|
||||||
"version":"0.0.1-a",
|
"version":"0.1.2-a",
|
||||||
"category":"Other",
|
"category":"Other",
|
||||||
"iconclass":"fa fa-camera",
|
"iconclass":"fa fa-camera",
|
||||||
"mimes":["none"],
|
"mimes":["none"],
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
"css": ["css/main.css"],
|
"css": ["css/main.css"],
|
||||||
"javascripts": [],
|
"javascripts": [],
|
||||||
"coffees": ["coffees/main.coffee"],
|
"coffees": ["coffees/main.coffee"],
|
||||||
"copies": ["assets/scheme.html", "package.json", "README.md"]
|
"copies": ["assets/scheme.html", "js/decoder.js", "package.json", "README.md"]
|
||||||
}
|
}
|
@ -205,7 +205,7 @@
|
|||||||
"description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/RemoteCamera/README.md",
|
"description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/RemoteCamera/README.md",
|
||||||
"category": "Other",
|
"category": "Other",
|
||||||
"author": "",
|
"author": "",
|
||||||
"version": "0.0.1-a",
|
"version": "0.1.2-a",
|
||||||
"dependencies": ["libjpeg@0.1.1-a","Antunnel@0.1.8-a"],
|
"dependencies": ["libjpeg@0.1.1-a","Antunnel@0.1.8-a"],
|
||||||
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/RemoteCamera/build/release/RemoteCamera.zip"
|
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/RemoteCamera/build/release/RemoteCamera.zip"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user