mirror of
https://github.com/antos-rde/antosdk-apps.git
synced 2024-12-25 19:58:21 +01:00
add remote camera module
This commit is contained in:
parent
befa74e64c
commit
9ac6f51a66
6
RemoteCamera/README.md
Normal file
6
RemoteCamera/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# RemoteCamera
|
||||
|
||||
Connect to a V4L2 camera on server via Antunnel.
|
||||
|
||||
This application reauires the **tunel plugin** and the **ant-tunnel v4l2 publisher**
|
||||
on the server-side
|
21
RemoteCamera/assets/scheme.html
Normal file
21
RemoteCamera/assets/scheme.html
Normal file
@ -0,0 +1,21 @@
|
||||
<afx-app-window apptitle="RemoteCamera" width="650" height="550" data-id="RemoteCamera">
|
||||
<afx-vbox >
|
||||
<afx-hbox data-height="30">
|
||||
<div data-width="10"></div>
|
||||
<afx-label text="__(Resolution)" data-width="80"></afx-label>
|
||||
<afx-list-view dropdown=true data-id="resoctl"></afx-list-view>
|
||||
<div data-width="10"></div>
|
||||
<afx-label text="__(JPEG Quality)" data-width="90"></afx-label>
|
||||
<afx-slider data-id="qctl" ></afx-slider>
|
||||
<div data-width="10"></div>
|
||||
<afx-label text="__(FPS)" data-width="40"></afx-label>
|
||||
<afx-list-view dropdown=true data-id="fpsctl"></afx-list-view>
|
||||
<div data-width="10"></div>
|
||||
</afx-hbox>
|
||||
|
||||
<div data-id="container">
|
||||
<canvas data-id="player"></canvas>
|
||||
</div>
|
||||
|
||||
</afx-vbox>
|
||||
</afx-app-window>
|
6
RemoteCamera/build/debug/README.md
Normal file
6
RemoteCamera/build/debug/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# RemoteCamera
|
||||
|
||||
Connect to a V4L2 camera on server via Antunnel.
|
||||
|
||||
This application reauires the **tunel plugin** and the **ant-tunnel v4l2 publisher**
|
||||
on the server-side
|
12
RemoteCamera/build/debug/main.css
Normal file
12
RemoteCamera/build/debug/main.css
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
afx-app-window[data-id="RemoteCamera"] div[data-id="container"]
|
||||
{
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
afx-app-window[data-id="RemoteCamera"] div[data-id="container"] canvas
|
||||
{
|
||||
display: block;
|
||||
margin:0 auto;
|
||||
}
|
221
RemoteCamera/build/debug/main.js
Normal file
221
RemoteCamera/build/debug/main.js
Normal file
@ -0,0 +1,221 @@
|
||||
(function() {
|
||||
var RemoteCamera;
|
||||
|
||||
RemoteCamera = class RemoteCamera extends this.OS.application.BaseApplication {
|
||||
constructor(args) {
|
||||
super("RemoteCamera", args);
|
||||
}
|
||||
|
||||
main() {
|
||||
var fps, i, j;
|
||||
this.mute = false;
|
||||
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
|
||||
};
|
||||
fps = [];
|
||||
for (i = j = 5; j <= 30; i = j += 5) {
|
||||
fps.push({
|
||||
text: `${i}`,
|
||||
value: i
|
||||
});
|
||||
}
|
||||
this.fpsctl.data = fps;
|
||||
this.fpsctl.selected = this.cam_setting.fps / 5 - 1;
|
||||
this.fpsctl.onlistselect = (e) => {
|
||||
if (this.mute) {
|
||||
return;
|
||||
}
|
||||
this.cam_setting.fps = e.data.item.data.value;
|
||||
return this.setCameraSetting();
|
||||
};
|
||||
this.qctl.value = this.cam_setting.quality;
|
||||
this.resoctl = this.find("resoctl");
|
||||
this.resoctl.data = [
|
||||
{
|
||||
text: __("320x240"),
|
||||
mode: "qvga"
|
||||
},
|
||||
{
|
||||
text: __("640x480"),
|
||||
selected: true,
|
||||
mode: "vga"
|
||||
},
|
||||
{
|
||||
text: __("800x600"),
|
||||
mode: "svga"
|
||||
},
|
||||
{
|
||||
text: __("1024x760"),
|
||||
mode: "hd"
|
||||
},
|
||||
{
|
||||
text: __("1920×1080"),
|
||||
mode: "fhd"
|
||||
}
|
||||
];
|
||||
this.resoctl.onlistselect = (e) => {
|
||||
if (this.mute) {
|
||||
return;
|
||||
}
|
||||
switch (e.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 = (e) => {
|
||||
if (this.mute) {
|
||||
return;
|
||||
}
|
||||
this.cam_setting.quality = e.data;
|
||||
return this.setCameraSetting();
|
||||
};
|
||||
if (!Antunnel.tunnel) {
|
||||
return this.notify(__("Antunnel service is not available"));
|
||||
}
|
||||
if (!this.setting.channel) {
|
||||
return this.requestChannel();
|
||||
} else {
|
||||
return this.openSession();
|
||||
}
|
||||
}
|
||||
|
||||
requestChannel() {
|
||||
return this.openDialog("PromptDialog", {
|
||||
title: __("Enter camera channel"),
|
||||
label: __("Please enter camera channel name")
|
||||
}).then((v) => {
|
||||
this.setting.channel = v;
|
||||
return this.openSession();
|
||||
});
|
||||
}
|
||||
|
||||
menu() {
|
||||
return {
|
||||
text: "__(Option)",
|
||||
nodes: [
|
||||
{
|
||||
text: "__(Camera channel)"
|
||||
}
|
||||
],
|
||||
onchildselect: (e) => {
|
||||
return this.requestChannel();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
openSession() {
|
||||
if (!Antunnel) {
|
||||
return;
|
||||
}
|
||||
if (!this.setting.channel) {
|
||||
return;
|
||||
}
|
||||
this.tunnel = Antunnel.tunnel;
|
||||
this.sub = new Antunnel.Subscriber(this.setting.channel);
|
||||
this.sub.onopen = () => {
|
||||
return console.log("Subscribed to camera channel");
|
||||
};
|
||||
this.sub.onerror = (e) => {
|
||||
return this.error(__("Error: {0}", new TextDecoder("utf-8").decode(e.data)), e);
|
||||
};
|
||||
//@sub = undefined
|
||||
this.sub.onctrl = (e) => {
|
||||
var res;
|
||||
this.cam_setting.w = Antunnel.Msg.int_from(e.data, 0);
|
||||
this.cam_setting.h = Antunnel.Msg.int_from(e.data, 2);
|
||||
this.cam_setting.fps = e.data[4];
|
||||
this.cam_setting.quality = e.data[5];
|
||||
this.mute = true;
|
||||
this.qctl.value = this.cam_setting.quality;
|
||||
res = `${this.cam_setting.w}x${this.cam_setting.h}`;
|
||||
switch (res) {
|
||||
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;
|
||||
}
|
||||
this.fpsctl.selected = this.cam_setting.fps / 5 - 1;
|
||||
return this.mute = false;
|
||||
};
|
||||
this.sub.onmessage = (e) => {
|
||||
var context, imgData, jpeg;
|
||||
jpeg = new JpegImage();
|
||||
jpeg.parse(e.data);
|
||||
context = this.player.getContext("2d");
|
||||
this.player.width = jpeg.width;
|
||||
this.player.height = jpeg.height;
|
||||
//jpeg.copyToImageData(d)
|
||||
imgData = context.getImageData(0, 0, jpeg.width, jpeg.height);
|
||||
jpeg.copyToImageData(imgData);
|
||||
return context.putImageData(imgData, 0, 0);
|
||||
};
|
||||
this.sub.onclose = () => {
|
||||
this.sub = void 0;
|
||||
this.notify(__("Unsubscribed to the camera service"));
|
||||
return this.quit();
|
||||
};
|
||||
return Antunnel.tunnel.subscribe(this.sub);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if (this.sub) {
|
||||
return this.sub.close();
|
||||
}
|
||||
}
|
||||
|
||||
setCameraSetting() {
|
||||
var arr;
|
||||
if (!this.sub) {
|
||||
return;
|
||||
}
|
||||
arr = new Uint8Array(6);
|
||||
arr.set(Antunnel.Msg.bytes_of(this.cam_setting.w), 0);
|
||||
arr.set(Antunnel.Msg.bytes_of(this.cam_setting.h), 2);
|
||||
arr[4] = this.cam_setting.fps;
|
||||
arr[5] = this.cam_setting.quality;
|
||||
return this.sub.send(Antunnel.Msg.CTRL, arr);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
RemoteCamera.singleton = true;
|
||||
|
||||
RemoteCamera.dependencies = ["pkg://libjpeg/jpg.js"];
|
||||
|
||||
this.OS.register("RemoteCamera", RemoteCamera);
|
||||
|
||||
}).call(this);
|
16
RemoteCamera/build/debug/package.json
Normal file
16
RemoteCamera/build/debug/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"pkgname": "RemoteCamera",
|
||||
"app":"RemoteCamera",
|
||||
"name":"Remote Camera",
|
||||
"description":"Connect to remote camera via Antunnel",
|
||||
"info":{
|
||||
"author": "",
|
||||
"email": ""
|
||||
},
|
||||
"version":"0.0.1-a",
|
||||
"category":"Other",
|
||||
"iconclass":"fa fa-camera",
|
||||
"mimes":["none"],
|
||||
"dependencies":["libjpeg@0.1.1-a", "Antunnel@0.1.8-a"],
|
||||
"locale": {}
|
||||
}
|
21
RemoteCamera/build/debug/scheme.html
Normal file
21
RemoteCamera/build/debug/scheme.html
Normal file
@ -0,0 +1,21 @@
|
||||
<afx-app-window apptitle="RemoteCamera" width="650" height="550" data-id="RemoteCamera">
|
||||
<afx-vbox >
|
||||
<afx-hbox data-height="30">
|
||||
<div data-width="10"></div>
|
||||
<afx-label text="__(Resolution)" data-width="80"></afx-label>
|
||||
<afx-list-view dropdown=true data-id="resoctl"></afx-list-view>
|
||||
<div data-width="10"></div>
|
||||
<afx-label text="__(JPEG Quality)" data-width="90"></afx-label>
|
||||
<afx-slider data-id="qctl" ></afx-slider>
|
||||
<div data-width="10"></div>
|
||||
<afx-label text="__(FPS)" data-width="40"></afx-label>
|
||||
<afx-list-view dropdown=true data-id="fpsctl"></afx-list-view>
|
||||
<div data-width="10"></div>
|
||||
</afx-hbox>
|
||||
|
||||
<div data-id="container">
|
||||
<canvas data-id="player"></canvas>
|
||||
</div>
|
||||
|
||||
</afx-vbox>
|
||||
</afx-app-window>
|
177
RemoteCamera/coffees/main.coffee
Normal file
177
RemoteCamera/coffees/main.coffee
Normal file
@ -0,0 +1,177 @@
|
||||
class RemoteCamera extends this.OS.application.BaseApplication
|
||||
constructor: ( args ) ->
|
||||
super "RemoteCamera", args
|
||||
|
||||
main: () ->
|
||||
@mute = false
|
||||
@player = @find "player"
|
||||
|
||||
@qctl = @find "qctl"
|
||||
|
||||
@fpsctl = @find "fpsctl"
|
||||
|
||||
@cam_setting = {
|
||||
w: 640,
|
||||
h: 480,
|
||||
fps: 10,
|
||||
quality: 60
|
||||
}
|
||||
|
||||
fps = []
|
||||
for i in [5..30] by 5
|
||||
fps.push {
|
||||
text: "#{i}",
|
||||
value: i
|
||||
}
|
||||
@fpsctl.data = fps
|
||||
@fpsctl.selected = @cam_setting.fps/5 -1
|
||||
|
||||
@fpsctl.onlistselect = (e) =>
|
||||
return if @mute
|
||||
@cam_setting.fps = e.data.item.data.value
|
||||
@setCameraSetting()
|
||||
|
||||
@qctl.value = @cam_setting.quality
|
||||
|
||||
|
||||
@resoctl = @find "resoctl"
|
||||
|
||||
@resoctl.data = [
|
||||
{
|
||||
text: __("320x240"),
|
||||
mode: "qvga"
|
||||
},
|
||||
{
|
||||
text: __("640x480"),
|
||||
selected: true,
|
||||
mode: "vga"
|
||||
},
|
||||
{
|
||||
text: __("800x600"),
|
||||
mode: "svga"
|
||||
},
|
||||
{
|
||||
text: __("1024x760"),
|
||||
mode: "hd"
|
||||
},
|
||||
{
|
||||
text: __("1920×1080"),
|
||||
mode: "fhd"
|
||||
}
|
||||
]
|
||||
@resoctl.onlistselect = (e) =>
|
||||
return if @mute
|
||||
switch e.data.item.data.mode
|
||||
when "qvga"
|
||||
@cam_setting.w = 320
|
||||
@cam_setting.h = 240
|
||||
when "vga"
|
||||
@cam_setting.w = 640
|
||||
@cam_setting.h = 480
|
||||
when "svga"
|
||||
@cam_setting.w = 800
|
||||
@cam_setting.h = 600
|
||||
when "hd"
|
||||
@cam_setting.w = 1024
|
||||
@cam_setting.h = 768
|
||||
when "fhd"
|
||||
@cam_setting.w = 1920
|
||||
@cam_setting.h = 1080
|
||||
@setCameraSetting()
|
||||
|
||||
@qctl.onvaluechange = (e) =>
|
||||
return if @mute
|
||||
@cam_setting.quality = e.data
|
||||
@setCameraSetting()
|
||||
|
||||
return @notify __("Antunnel service is not available") unless Antunnel.tunnel
|
||||
if not @setting.channel
|
||||
@requestChannel()
|
||||
else
|
||||
@openSession()
|
||||
|
||||
requestChannel: () ->
|
||||
@openDialog "PromptDialog", {
|
||||
title: __("Enter camera channel"),
|
||||
label: __("Please enter camera channel name")
|
||||
}
|
||||
.then (v) =>
|
||||
@setting.channel = v
|
||||
@openSession()
|
||||
|
||||
menu: () ->
|
||||
{
|
||||
text: "__(Option)",
|
||||
nodes: [
|
||||
{ text: "__(Camera channel)" }
|
||||
],
|
||||
onchildselect: (e) => @requestChannel()
|
||||
}
|
||||
|
||||
openSession: () ->
|
||||
return unless Antunnel
|
||||
return unless @setting.channel
|
||||
@tunnel = Antunnel.tunnel
|
||||
@sub = new Antunnel.Subscriber(@setting.channel)
|
||||
@sub.onopen = () =>
|
||||
console.log("Subscribed to camera channel")
|
||||
|
||||
@sub.onerror = (e) =>
|
||||
@error __("Error: {0}", new TextDecoder("utf-8").decode(e.data)), e
|
||||
#@sub = undefined
|
||||
@sub.onctrl = (e) =>
|
||||
@cam_setting.w = Antunnel.Msg.int_from(e.data,0)
|
||||
@cam_setting.h = Antunnel.Msg.int_from(e.data,2)
|
||||
@cam_setting.fps = e.data[4]
|
||||
@cam_setting.quality = e.data[5]
|
||||
@mute = true
|
||||
@qctl.value = @cam_setting.quality
|
||||
res = "#{@cam_setting.w}x#{@cam_setting.h}"
|
||||
switch res
|
||||
when "320x240"
|
||||
@resoctl.selected = 0
|
||||
when "640x480"
|
||||
@resoctl.selected = 1
|
||||
when "800x600"
|
||||
@resoctl.selected = 2
|
||||
when "1024x768"
|
||||
@resoctl.selected = 3
|
||||
when "1920x1080"
|
||||
@resoctl.selected = 4
|
||||
@fpsctl.selected = @cam_setting.fps/5 -1
|
||||
@mute = false
|
||||
|
||||
@sub.onmessage = (e) =>
|
||||
jpeg = new JpegImage()
|
||||
jpeg.parse e.data
|
||||
context = @player.getContext("2d")
|
||||
@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 = undefined
|
||||
@notify __("Unsubscribed to the camera service")
|
||||
@quit()
|
||||
Antunnel.tunnel.subscribe @sub
|
||||
|
||||
cleanup: () ->
|
||||
@sub.close() if @sub
|
||||
|
||||
setCameraSetting: () ->
|
||||
return unless @sub
|
||||
arr = new Uint8Array(6)
|
||||
arr.set Antunnel.Msg.bytes_of(@cam_setting.w), 0
|
||||
arr.set Antunnel.Msg.bytes_of(@cam_setting.h), 2
|
||||
arr[4] = @cam_setting.fps
|
||||
arr[5] = @cam_setting.quality
|
||||
@sub.send Antunnel.Msg.CTRL, arr
|
||||
|
||||
RemoteCamera.singleton = true
|
||||
RemoteCamera.dependencies = [
|
||||
"pkg://libjpeg/jpg.js"
|
||||
]
|
||||
this.OS.register "RemoteCamera", RemoteCamera
|
11
RemoteCamera/css/main.css
Normal file
11
RemoteCamera/css/main.css
Normal file
@ -0,0 +1,11 @@
|
||||
afx-app-window[data-id="RemoteCamera"] div[data-id="container"]
|
||||
{
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
afx-app-window[data-id="RemoteCamera"] div[data-id="container"] canvas
|
||||
{
|
||||
display: block;
|
||||
margin:0 auto;
|
||||
}
|
16
RemoteCamera/package.json
Normal file
16
RemoteCamera/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"pkgname": "RemoteCamera",
|
||||
"app":"RemoteCamera",
|
||||
"name":"Remote Camera",
|
||||
"description":"Connect to remote camera via Antunnel",
|
||||
"info":{
|
||||
"author": "",
|
||||
"email": ""
|
||||
},
|
||||
"version":"0.0.1-a",
|
||||
"category":"Other",
|
||||
"iconclass":"fa fa-camera",
|
||||
"mimes":["none"],
|
||||
"dependencies":["libjpeg@0.1.1-a", "Antunnel@0.1.8-a"],
|
||||
"locale": {}
|
||||
}
|
7
RemoteCamera/project.json
Normal file
7
RemoteCamera/project.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "RemoteCamera",
|
||||
"css": ["css/main.css"],
|
||||
"javascripts": [],
|
||||
"coffees": ["coffees/main.coffee"],
|
||||
"copies": ["assets/scheme.html", "package.json", "README.md"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user