Digital projector feature added. Select a video and it will be displayed on the main monitor (TODO: address this) and the camera will advance in sync, as with a regular optical printer.

This commit is contained in:
mmcwilliams 2019-02-08 18:21:16 -05:00
parent fafb313f7d
commit 40b5ecfe69
7 changed files with 260 additions and 67 deletions

View File

@ -259,11 +259,11 @@
<select id="projector_device">
<option>Not Set</option>
</select>
<input type="radio" id="digital_type_arduino" name="digital_type" value="arduino" checked="checked" />
<input type="radio" id="projector_type_arduino" name="projector_type" value="arduino" checked="checked" />
</div>
<div class="spacer">
<input type="text" id="digital" name="digital" placeholder="Digital file" onclick="devices.digitalSelect();" readonly />
<input type="radio" id="digital_type" name="digital_type" value="video" onclick="devices.digital();" />
<input type="text" id="digital" name="digital" placeholder="Digital file" onclick="devices.digitalSelect();" data-file="" readonly />
<input type="radio" id="projector_type" name="projector_type" value="video" onclick="devices.digital();" />
</div>
<div>
<h4>Camera</h4>

View File

@ -88,7 +88,7 @@ async function end () {
if (system.platform !== 'nix') {
await wv.close();
} else {
cp.kill()
if (cp) cp.kill()
}
}

View File

@ -40,8 +40,8 @@ async function frame (video, frame, obj) {
//-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
try {
output = await exec(cmd);
console.log(cmd);
output = await exec(cmd);
} catch (err) {
console.error(err);
}
@ -145,7 +145,7 @@ async function checkDir () {
module.exports = (sys) => {
system = sys;
TMPDIR = path.join(system.tmp, 'intval_go_node');
TMPDIR = path.join(system.tmp, 'mcopy_digital');
checkDir();

View File

@ -4,7 +4,7 @@ const fs = require('fs-extra');
const exec = require('exec');
//const spawn = require('spawn');
const exit = require('exit');
//const exit = require('exit');
let system = {};
@ -21,17 +21,24 @@ async function info (video) {
return exit(err, 5);
}
if (!exists) {
return exit(`File ${video} does not exist`, 6);
//return exit(`File ${video} does not exist`, 6);
console.error(err);
return false
}
try {
console.log(cmd);
raw = await exec(cmd);
} catch (err) {
return exit(err, 7);
//return exit(err, 7);
console.error(err);
return false
}
try {
json = JSON.parse(raw);
json = JSON.parse(raw.stdout);
} catch (err) {
return raw;
return raw.stdout;
}
if (json && json.streams) {
@ -57,22 +64,28 @@ async function frames (video) {
try {
exists = await fs.exists(video);
} catch (err) {
return exit(err, 5);
//return exit(err, 5);
console.error(err);
return false
}
if (!exists) {
return exit(`File ${video} does not exist`, 6);
//return exit(`File ${video} does not exist`, 6);
console.error(err);
return false;
}
try {
console.log(cmd);
raw = await exec(cmd);
} catch (err) {
console.error(err);
return false;
}
try {
frames = parseInt(raw)
frames = parseInt(raw.stdout)
} catch (err) {
return raw;
return raw.stdout;
}
return frames;

View File

@ -37,6 +37,7 @@ devices.listen = function () {
'use strict';
ipcRenderer.on('ready', devices.ready);
ipcRenderer.on('intval', devices.intvalCb);
ipcRenderer.on('digital', devices.digitalCb);
};
devices.ready = function (event, arg) {
'use strict';
@ -104,44 +105,6 @@ devices.intval = function () {
}
};
devices.digitalSelect = function () {
const elem = $('#digital');
dialog.showOpenDialog({
properties: [
'openFile',
'openDirectory'
],
filters: [
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4', 'mpeg', 'mov'] },
{ name: 'All Files', extensions: ['*'] }
]
});
}
devices.digital = function () {
'use strict';
const elem = $('#digital');
let proceed = false;
let obj = {
connect: true,
url : url
};
if ( url !== '' && typeof url !== 'undefined') {
proceed = confirm(`Are you sure you want to`);
} else {
alert('Cannot connect')
}
if (proceed) {
//gui.overlay(true);
//gui.spinner(true, `Connecting to`);
ipcRenderer.send('video', obj)
} else {
$('#camera_type_arduino').prop('checked', 'checked');
$('#intval').removeClass('active');
}
};
devices.intvalCb = function (evt, args) {
'use strict';
let state;
@ -163,4 +126,89 @@ devices.intvalCb = function (evt, args) {
}
};
devices.digitalSelect = function () {
'use strict';
const elem = $('#digital');
const extensions = ['mpg', 'mpeg', 'mov', 'mkv', 'avi'];
dialog.showOpenDialog({
title : `Select video or image sequence`,
properties : [`openFile`], // openDirectory, multiSelection, openFile
defaultPath: 'c:/',
filters :
[
{
name: 'Videos',
extensions
},
{
name: 'All Files',
extensions: ['*']
},
]
}, (files) => {
let valid = false;
console.dir(files)
let path = files[0]
if (path && path !== '') {
for (let ext of extensions) {
if (path.toLowerCase().indexOf(`.${ext}`) !== -1) {
valid = true;
}
}
if (!valid) return false;
log.info(`Selected video ${path.split('/').pop()}`, 'DIGITAL', true);
elem.attr('data-file', path);
elem.val(path.split('/').pop());
}
})
}
devices.digital = function () {
'use strict';
const elem = $('#digital');
const path = elem.attr('data-file');
const fileName = elem.val();
let proceed = false;
let obj = {
path,
fileName
}
if (path && path !== '') {
proceed = confirm(`Are you sure you want to use ${fileName}?`);
}
if (proceed) {
gui.overlay(true);
gui.spinner(true, `Getting info about ${fileName}`);
ipcRenderer.send('digital', obj)
} else {
$('#projector_type_digital').prop('checked', 'checked');
$('#digital').removeClass('active');
}
};
devices.digitalCb = function (evt, args) {
'use strict';
let state;
gui.spinner(false);
gui.overlay(false);
if (args.valid && args.valid === true) {
//success state
state = JSON.parse(args.state);
$('#digital').addClass('active');
$('#projector_type_digital').prop('checked', 'checked');
gui.notify('DEVICES', `Using video ${state.fileName}`);
mcopy.state.sequence.arr = ['PF', 'CF'];
gui.grid.state(0);
gui.grid.state(1);
gui.updateState();
} else {
$('#projector_type_digital').prop('checked', 'checked');
$('#digital').removeClass('active');
}
};
module.exports = devices;

View File

@ -26,6 +26,7 @@ seq.run = function () {
}
if (seq.i == 0) {
$('#loop_current').text(gui.fmtZero(mcopy.loopCount + 1, 6));
ipcRenderer.send('seq', { action : 'loop' });
}
if (seq.stop()) {
$('.row input').removeClass('h');
@ -73,7 +74,7 @@ seq.run = function () {
} else {
log.info('Sequence completed in ' + humanizeDuration(timeEnd), 'SEQUENCE', true);
}
ipcRenderer.send('seq', { action : 'stop' });
//capture.report = ipcRenderer.sendSync('transfer', { action: 'end'});
//if (capture.active) {
//alert(capture.report);
@ -90,6 +91,9 @@ seq.run = function () {
seq.stop = function (state) {
'use strict';
if (typeof state === 'undefined') {
if (seq.stopState === true) {
ipcRenderer.send('seq', { action : 'stop' });
}
return seq.stopState;
} else {
seq.stopState = state;
@ -97,7 +101,10 @@ seq.stop = function (state) {
if (state === false) {
mcopy.loopCount = 0
$('#loop_current').text('');
} else {
ipcRenderer.send('seq', { action : 'stop' });
}
return state
};
seq.init = function (start) {
'use strict';
@ -108,7 +115,9 @@ seq.init = function (start) {
}
seq.stop(false);
seq.i = start;
//ipcRenderer.sendSync('transfer', { action: 'start'});
ipcRenderer.send('seq', { action : 'start' });
seq.run();
};
seq.stats = function () {

View File

@ -405,7 +405,8 @@ light.end = async function (rgb, id, ms) {
}
proj.state = {
dir : true //default dir
dir : true, //default dir
digital : false
}
proj.init = function () {
proj.listen()
@ -419,20 +420,32 @@ proj.set = async function (dir, id) {
cmd = mcopy.cfg.arduino.cmd.proj_backward
}
proj.state.dir = dir
try {
ms = await arduino.send('projector', cmd)
} catch (err) {
console.error(err)
if (proj.digital) {
dig.set(dir)
} else {
try {
ms = await arduino.send('projector', cmd)
} catch (err) {
console.error(err)
}
}
return await proj.end(cmd, id, ms)
}
proj.move = async function (frame, id) {
const cmd = mcopy.cfg.arduino.cmd.projector
let ms
try {
ms = await arduino.send('projector', cmd)
} catch (err) {
console.error(err)
if (proj.digital) {
try {
ms = await dig.move()
} catch (err) {
console.error(err)
}
} else {
try {
ms = await arduino.send('projector', cmd)
} catch (err) {
console.error(err)
}
}
return await proj.end(mcopy.cfg.arduino.cmd.projector, id, ms)
}
@ -453,6 +466,7 @@ proj.listen = function () {
}
event.returnValue = true
})
ipcMain.on('digital', proj.connectDigital)
}
proj.end = async function (cmd, id, ms) {
let message = ''
@ -473,6 +487,94 @@ proj.end = async function (cmd, id, ms) {
return await mainWindow.webContents.send('proj', {cmd: cmd, id : id, ms: ms})
}
/**
* Use a file as the "digital" source on "projector"
*
**/
proj.connectDigital = async function (evt, arg) {
let info;
let frames = 0;
try {
info = await ffprobe.info(arg.path);
} catch (err) {
log.error(err, 'DIGITAL', true, true);
proj.digital = false;
await mainWindow.webContents.send('digital', { valid : false });
return false;
}
try {
frames = await ffprobe.frames(arg.path);
} catch (err) {
log.error(err, 'DIGITAL', true, true);
proj.digital = false;
await mainWindow.webContents.send('digital', { valid : false });
return false;
}
dig.state.frame = 0;
dig.state.path = arg.path;
dig.state.fileName = arg.fileName;
dig.state.frames = frames;
dig.state.info = info;
console.dir(dig.state);
log.info(`Opened ${dig.state.fileName}`, 'DIGITAL', true, true);
log.info(`Frames : ${frames}`, 'DIGITAL', true, true);
proj.digital = true;
return await mainWindow.webContents.send('digital', { valid : true, state : JSON.stringify(dig.state) });
}
const dig = {};
dig.state = {
frame : 0,
frames : 0,
path : null,
fileName : null,
info : {},
dir : true
};
dig.set = function (dir) {
dig.state.dir = dir;
}
dig.move = async function () {
let start = +new Date()
let last = dig.state.dir + 0;
if (dig.state.dir) {
dig.state.frame++
} else {
dig.state.frame--
}
if (dig.state.frame < 1) {
dig.state.frame = 1
}
if (last > 0) {
display.end()
//wipe last frame
try {
await ffmpeg.clear(last)
} catch (err) {
console.error(err)
}
}
try {
await ffmpeg.frame(dig.state.path, dig.state.frame)
} catch (err) {
console.error(err)
}
display.start(dig.state.frame)
await delay(100)
return (+new Date()) - start
}
cam.intval = null
cam.state = {
dir : true //default dir
@ -510,10 +612,17 @@ cam.move = async function (frame, id) {
const cmd = mcopy.cfg.arduino.cmd.camera
let ms
if (cam.intval) {
ms = await cam.intval.move()
try {
ms = await cam.intval.move()
} catch (err) {
console.error(err);
}
} else {
ms = await arduino.send('camera', cmd)
try {
ms = await arduino.send('camera', cmd)
} catch (err) {
console.error(err)
}
}
log.info('Camera move time', { ms })
return cam.end(cmd, id, ms)
@ -591,6 +700,19 @@ cam.end = async function (cmd, id, ms) {
mainWindow.webContents.send('cam', {cmd: cmd, id : id, ms: ms})
};
const seq = {};
seq.init = function () {
seq.listen();
}
seq.listen = function () {
ipcMain.on('seq', async (evt, arg) => {
if (arg.action === 'stop' && proj.digital) {
display.end()
}
})
}
log.file = function () {
let logPath = path.join(os.homedir(), `/.config/mcopy/`)
if (process.platform === 'darwin') {
@ -675,6 +797,7 @@ var init = async function () {
proj.init()
cam.init()
dev.init()
seq.init()
//capture = require('capture')(SYSTEM); //redundant