Commit 198a5700 authored by Oleg Dzhimiev's avatar Oleg Dzhimiev

1. improved jp4 preview stability

2. multi cameras single control page
parent 8c90a903
# Runs 'make', 'make install', and 'make clean' in specified subdirectories
SUBDIRS := src/php_top src/python_tests src/debugfs-webgui src/jp4-canvas src/update src/eyesis4pi src/index src/pointers src/snapshot src/jp4-viewer src/photofinish # src1
SUBDIRS := src/php_top src/python_tests src/debugfs-webgui src/jp4-canvas src/update src/eyesis4pi src/index src/pointers src/snapshot src/jp4-viewer src/photofinish src/multicam # src1
INSTALLDIRS = $(SUBDIRS:%=install-%)
CLEANDIRS = $(SUBDIRS:%=clean-%)
......
......@@ -59,7 +59,7 @@
<script src="js/jcanvas.js"></script>
<script src="js/exif.js"></script>
<script src="js/jquery-jp4.js"></script>
<link rel="stylesheet" href="js/bootstrap/css/bootstrap.css">
<style>
.port_window{
......@@ -79,15 +79,15 @@
table td {
padding-right:10px;
}
.btn.active:focus, .btn:focus{
outline:none;
}
.btn-toggle{
padding: 1px 0px;
}
</style>
</head>
<body>
......@@ -99,9 +99,9 @@
$table_contents = "";
$port_links = "";
$sample_port = -1;
for($i=0;$i<4;$i++){
$sensor = $path."/sensor{$i}0";
if (is_file($sensor)){
......@@ -109,7 +109,7 @@
if ($c!="none"){
$sample_port = $i;
$sandp = "http://{$_SERVER["SERVER_ADDR"]}:".($port0+$i);
$href1 = "$sandp/bimg";
$href2 = "$sandp/mimg";
......@@ -132,7 +132,7 @@
$master_port = elphel_get_P_value($sample_port,ELPHEL_TRIG_MASTER);
$awb_on = elphel_get_P_value($master_port,ELPHEL_WB_EN);
$aexp_on = elphel_get_P_value($master_port,ELPHEL_AUTOEXP_ON);
echo "<table><tr>$table_contents</tr></table>\n";
echo "<br/>";
......@@ -162,7 +162,7 @@
<button class="btn btn-xs <?php echo (!$aexp_on)?"btn-danger active":"btn-default";?>">OFF</button>
</div>
</td>
</tr>
</tr>
</table>
<br />
<a href="autocampars.php" title="autocampars.php">Parameter Editor</a><br />
......@@ -189,7 +189,7 @@ $(function(){
function init_jp4_previews(){
$('.port_preview').each(function(){
index = parseInt($(this).attr("index"));
$(this).jp4({ip:"127.0.0.1",port:2323+index,width:300,fast:true,lowres:4});
$(this).jp4({ip:location.host,port:2323+index,width:300,fast:true,lowres:4});
});
}
......@@ -201,7 +201,7 @@ function init_awb_toggle(){
}else{
$(this).find('.btn.active').toggleClass('btn-danger');
}
// toggle active
$(this).find('.btn').toggleClass('active');
......@@ -214,7 +214,7 @@ function init_awb_toggle(){
}
$(this).find('.btn').toggleClass('btn-default');
url = "parsedit.php?immediate&sensor_port=<?php echo $master_port;?>&WB_EN="+wb_en+"&*WB_EN=0xf";
$.ajax({
......@@ -235,7 +235,7 @@ function init_aexp_toggle(){
}else{
$(this).find('.btn.active').toggleClass('btn-danger');
}
// toggle active
$(this).find('.btn').toggleClass('active');
......@@ -248,7 +248,7 @@ function init_aexp_toggle(){
}
$(this).find('.btn').toggleClass('btn-default');
url = "parsedit.php?immediate&sensor_port=<?php echo $master_port;?>&AUTOEXP_ON="+aexp_en+"&*AUTOEXP_ON=0xf";
$.ajax({
......@@ -257,7 +257,7 @@ function init_aexp_toggle(){
console.log("aexp "+(aexp_en?"on":"off"));
}
});
});
}
......
......@@ -34,15 +34,22 @@ if (isset($_GET['ip']))
$ip = $_GET['ip'];
else
$ip = "localhost";
/*
header("Location: http://{$_SERVER['HTTP_HOST']}:$port/$rel");
die();
*/
$port0 = 2323;
$pointers = elphel_get_circbuf_pointers(intval($port)-$port0,1);
$pointer = $pointers[count($pointers)-1]['circbuf_pointer'];
// allow CORS
header('Access-Control-Allow-Origin: *');
header('Content-type:image/jpeg');
echo file_get_contents("http://$ip:$port/$rel");
echo file_get_contents("http://$ip:$port/$pointer/$rel");
//echo file_get_contents("http://$ip:$port/$rel");
die();
?>
?>
......@@ -78,6 +78,8 @@
var cnv_working = $("<canvas>",{id:"working"});
var cnv_display = $("<canvas>",{id:"display"});
obj.busy = false;
// hide working canvas
cnv_working.css({display:"none"});
/*
......@@ -102,6 +104,7 @@
* make sure the image data starts with: "data:image/jpeg;base64,"
* EXIF.js does not like empty data type: "data:;base64,"
*/
obj.busy = true;
process_image(settings.image);
}else{
send_request();
......@@ -115,7 +118,8 @@
var http = new XMLHttpRequest();
if (settings.port!=""&&settings.ip!=""){
rq = "/get-image.php?ip="+settings.ip+"&port="+settings.port+"&rel=bimg&ts="+Date.now();
//rq = "/get-image.php?ip="+settings.ip+"&port="+settings.port+"&rel=bimg&ts="+Date.now();
rq = "http://"+settings.ip+"/get-image.php?ip="+settings.ip+"&port="+settings.port+"&rel=img&ts="+Date.now();
//rq = "get-image.php?ip="+settings.ip+"&port="+settings.port+"&rel=img&ts="+Date.now();
//settings.refresh = true;
}else{
......@@ -137,12 +141,16 @@
}
};
obj.busy = true;
http.send();
}
this.refresh = function(){
send_request();
// skip if busy?
if (!obj.busy){
send_request();
}
}
this.resize = function(w){
......@@ -244,6 +252,7 @@
}
$(this).trigger("canvas_ready");
obj.busy = false;
if (settings.refresh) {
if (DEBUG){
......@@ -360,6 +369,7 @@
}
//trigger here
cnv_working.trigger("canvas_ready");
obj.busy = false;
if (settings.refresh) {
if (DEBUG){
......@@ -368,6 +378,8 @@
}
send_request();
}
this.terminate();
}
}
......@@ -392,7 +404,7 @@
bayer_mode = 0; // r gr / gb b
if (typeof MakerNote !== 'undefined'){
bayer_mode = (MakerNote[10]>>2)&0x3;
console.log("Bayer mode = "+bayer_mode);
//console.log("Bayer mode = "+bayer_mode);
switch(bayer_mode){
case 0: BAYER = [["Gr","R"],["B","Gb"]];break;
case 1: BAYER = [["R","Gr"],["Gb","B"]];break;
......
......@@ -7,11 +7,11 @@ self.onmessage = function(e) {
var H = e.data.height;
var Mosaic = e.data.mosaic;
var Format = e.data.format;
var settings = e.data.settings;
var Pixels = new Uint8Array(e.data.pixels);
if (settings.lowres==0){
var reorderedPixels = Elphel.Pixels.reorderBlocksJPx(Pixels,W,H,Format,Mosaic,settings.fast);
//reorder first then downscale
......@@ -24,15 +24,16 @@ self.onmessage = function(e) {
W = W/2;
H = H/2;
}
Elphel.Pixels.applySaturation(reorderedPixels,W,H,2);
postMessage({
width: W,
height: H,
pixels: reorderedPixels.buffer
},[reorderedPixels.buffer]);
//Elphel.test();
this.close();
};
\ No newline at end of file
DOCUMENTROOT=$(DESTDIR)/www/pages/multicam
OWN = -o root -g root
INSTDOCS = 0644
INSTALL = install
DOCS= index.html \
multicam.js \
multicam.css \
multicam.php
all:
@echo "make all in src"
install:
@echo "make install in src"
$(INSTALL) $(OWN) -d $(DOCUMENTROOT)
$(INSTALL) $(OWN) -m $(INSTDOCS) $(DOCS) $(DOCUMENTROOT)
clean:
@echo "make clean in src"
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Multi cameras control</title>
<script src="../js/jquery-3.1.1.js"></script>
<script src="../js/elphel.js"></script>
<script src="../js/jcanvas.js"></script>
<script src="../js/exif.js"></script>
<script src="../js/jquery-jp4.js"></script>
<link rel="stylesheet" href="../js/bootstrap/css/bootstrap.css">
<script type='text/javascript' src='multicam.js'></script>
<link rel='stylesheet' type='text/css' href='multicam.css'></link>
</head>
<body>
<div id='settings'>
<table>
<tr>
<td>
<div id='rec_button' class='rec_outer' title='start recording'>
<div class='rec_inner'></div>
</div>
</td>
</tr>
<tr id="toggle_awb" title='Auto White Balance'>
<td>Auto WB:</td>
<td>
<div id="toggle_awb" class="btn-group btn-toggle">
<button class="btn btn-xs btn-success active">ON</button>
<button class="btn btn-xs btn-default">OFF</button>
</div>
</td>
</tr>
<tr id="toggle_aexp" title='Auto Exposure'>
<td>Auto Exposure:</td>
<td>
<div id="toggle_aexp" class="btn-group btn-toggle">
<button class="btn btn-xs btn-success active">ON</button>
<button class="btn btn-xs btn-default">OFF</button>
</div>
</td>
</tr>
<tr>
<td hidden>
<br/>
<div id='edit_addrs_input' hidden>
<div id='eai_text_div'>
<textarea id='eai_text' rows='4' cols='30'></textarea>
</div>
<div id='eai_ok_div'>
<button id='eai_ok' class="btn btn-sm btn-success">ok</button>
</div>
</div>
<div id='edit_addrs' title='edit ip addresses'>
<button id='ea_btn' class="btn btn-sm btn-success">+</button>
</div>
<div id='addrs'>
</div>
</td>
</tr>
</table>
</div>
<div id='display'>
<div id='display_status'></div>
<hr/>
<div id='display_previews'></div>
</div>
</body>
</html>
\ No newline at end of file
#settings{
padding:5px;
}
table td {
padding-right:10px;
}
.btn.active:focus, .btn:focus{
outline:none;
}
.btn-toggle{
padding: 1px 0px;
}
#edit_addrs{
padding: 2px;
}
#ea_btn{
outline:none;
font-weight:bold;
}
#addrs{
padding: 0px 2px 2px 2px;
}
#eai_text{
resize:none;
margin:0px;
}
#edit_addrs_input{
position:absolute;
top:2px;
left:2px;
border:1px solid rgb(169,169,169);
padding:2px;
background: white;
z-index: 100;
}
#eai_text_div{
line-height:14px;
}
#rec_start{
outline:none;
border: 0px solid #00AA66;
}
.rec_outer{
position:relative;
width:50px;
height:50px;
border-radius:50%;
border: 1px solid rgba(100,100,100,1);
background: white;
}
.rec_inner{
position:relative;
top:25%;
left:25%;
width:50%;
height:50%;
border-radius:50%;
background: rgb(166,14,14);
}
.rec_outer:active{
border: 1px solid rgb(166,14,14);
}
.rec_outer_active{
border: 1px solid rgb(166,14,14);
}
.rec_inner_running{
top:5%;
left:5%;
width:90%;
height:90%;
}
.port_preview{
width:200px;
}
.hist_preview{
border:1px solid rgba(200,200,200,0.5);
width:200px;
}
#display{
padding:5px;
}
hr{
margin-top:2px;
margin-bottom:2px;
}
#display_status th, #s_device{
text-align:center;
}
#s_space{
text-align:right;
}
#display_status td, #display_status th{
border:1px solid rgba(100,100,100,0.8);
padding:2px 5px;
}
// port class
var Port = function(options){
var defaults = {
port: 0,
index: 0,
awb: NaN,
aexp: NaN,
preview: null
};
this._data = $.extend(defaults,options);
this.port = this._data.port;
this.index = this._data.index;
this.awb = this._data.awb;
this.aexp = this._data.aexp;
this.preview = this._data.preview;
}
// camera class
var Camera = function(options){
var defaults = {
ip: "",
init: false,
status: false,
camogm: false,
recording: false,
ports: []
};
this._data = $.extend(defaults,options);
this.ip = this._data.ip;
this.init = this._data.init;
this.status = this._data.status;
this.ports = this._data.ports;
};
// global
var recording = false;
var ips_from_url = false;
var cams = [];
var wb_en = 1;
var aexp_en = 1;
$(function(){
init();
});
function parseURL(){
var parameters=location.href.replace(/\?/ig,"&").split("&");
for (var i=0;i<parameters.length;i++) parameters[i]=parameters[i].split("=");
for (var i=1;i<parameters.length;i++) {
switch (parameters[i][0]) {
case "ip":
ips_from_url = true;
ips_str = parameters[i][1];
ips_str = ips_str.replace(/,|;/gm,'\n');
addrs_str2ips(ips_str);
break;
}
}
}
function init(){
parseURL();
if (!ips_from_url){
//get config
$.ajax({
url: "multicam.php?cmd=read",
success: function(data){
var addrs = $(data).find('camera');
for(var i=0;i<addrs.length;i++){
cams.push(new Camera({ip:$(addrs[i]).text()}));
}
init2();
}
});
}else{
init2();
}
init_rec_button();
//
$("#ea_btn").on('click',function(){
$("#edit_addrs_input").css({
top: $(this).offset().top,
left: $(this).offset().left
}).show();
$("#eai_text").focus();
});
$("#eai_ok").on('click',function(){
addrs_ta2ips();
$("#edit_addrs_input").hide();
});
$("#display").css({
position:'absolute',
top: '2px',
left: $("#settings").find("table").width()+10
});
}
function init2(){
addrs_ips2ta();
// create tables
addrs_create_tables();
// now get the ports
get_ports();
init_awb_toggle();
init_aexp_toggle();
}
function get_ports(){
for(var i=0;i<cams.length;i++){
$.ajax({
url: "http://"+cams[i].ip+"/multicam/multicam.php?cmd=ports",
ip:cams[i].ip,
index: i,
success: function(response){
var index = this.index;
var ports = $(response).find("port");
cams[this.index].status = true;
cams[this.index].init = true;
// ports are already ordered in response
ports.each(function(){
if ($(this).text()!=='none'){
var tmp_port = new Port({port: $(this).attr("port"), index: $(this).attr("index")});
cams[index].ports.push(tmp_port);
init_port(index,cams[index].ports.length-1);
}
});
// check camogm is alive
check_camogm(index);
init3(index);
}
}).fail(function(data,status){
console.log(this.ip+" request failed. Check errors");
// will be checked when sending requests
cams[this.index].status = false;
addrs_mark_bad_ip(this.ip);
});
}
}
function check_camogm(cam_i){
// run_status does not interact with camogm, quickest response
$.ajax({
url: "http://"+cams[cam_i].ip+"/camogm_interface.php?cmd=run_status",
cam_i: cam_i,
success: function(res){
var state = $(res).find('state').text();
// false(if not init) or 'on' or 'off'
cams[this.cam_i].camogm = state;
if (state=='off'){
console.log(cams[this.cam_i].ip+": camogm is off");
// launch it
camogm_launch(this.cam_i);
}
if (state=='on'){
console.log(cams[this.cam_i].ip+": camogm is on");
check_camogm_status(this.cam_i);
}
}
});
}
function camogm_launch(cam_i){
// raw recording is default on start
$.ajax({
url: "http://"+cams[cam_i].ip+"/camogm_interface.php?cmd=run_camogm",
cam_i: cam_i,
success: function(res){
console.log(cams[this.cam_i].ip+": "+res);
cams[this.cam_i].camogm = 'on';
check_camogm_status(this.cam_i);
}
});
}
function check_camogm_status(cam_i){
// run_status does not interact with camogm, quickest response
$.ajax({
url: "http://"+cams[cam_i].ip+"/camogm_interface.php?cmd=status",
cam_i: cam_i,
success: function(res){
var cam = cams[this.cam_i];
if ($(res).find('state').length!=0){
var state = $(res).find('state').text();
state = state.replace(/"/gm,'');
// false(if not init) or 'on' or 'off'
cams[this.cam_i].recording = state;
rec_button_update_state();
var se = $("#display_status").find("tr[ip='"+cam.ip+"']");
// device
var device = $(res).find('raw_device_path').text().replace(/"/gm,'');
se.find("#s_device").html(device);
// free space
var lba_end = parseInt($(res).find('lba_end').text());
var lba_current = parseInt($(res).find('lba_current').text());
var free_space = (lba_end - lba_current)/2/1024/1024;
free_space = Math.round(100*free_space)/100;
se.find("#s_space").html(free_space+" GB");
}
}
}).fail(function(data,status){
console.log("status request failed");
});
}
var all_ready_init_run = true;
function rec_button_update_state(){
var all_ready = true;
var any_running = false;
var any_stopped = false;
for(var i=0;i<cams.length;i++){
if (cams[i].init){
if ((cams[i].camogm=='on')&&(!cams[i].recording)){
all_ready = false;
break;
}
if(cams[i].recording=='running'){
any_running = true;
}else if(cams[i].recording=='stopped'){
any_stopped = true;
}
}
}
if (all_ready){
if (any_running && any_stopped){
console.log("WARNING: some camogms are running, some are stopped");
}