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"> <select id="projector_device">
<option>Not Set</option> <option>Not Set</option>
</select> </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>
<div class="spacer"> <div class="spacer">
<input type="text" id="digital" name="digital" placeholder="Digital file" onclick="devices.digitalSelect();" readonly /> <input type="text" id="digital" name="digital" placeholder="Digital file" onclick="devices.digitalSelect();" data-file="" readonly />
<input type="radio" id="digital_type" name="digital_type" value="video" onclick="devices.digital();" /> <input type="radio" id="projector_type" name="projector_type" value="video" onclick="devices.digital();" />
</div> </div>
<div> <div>
<h4>Camera</h4> <h4>Camera</h4>

View File

@ -88,7 +88,7 @@ async function end () {
if (system.platform !== 'nix') { if (system.platform !== 'nix') {
await wv.close(); await wv.close();
} else { } 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" //-vf "select=gte(n\,${frame})" -compression_algo raw -pix_fmt rgb24 "export-${padded}.png"
try { try {
output = await exec(cmd);
console.log(cmd); console.log(cmd);
output = await exec(cmd);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
@ -145,7 +145,7 @@ async function checkDir () {
module.exports = (sys) => { module.exports = (sys) => {
system = sys; system = sys;
TMPDIR = path.join(system.tmp, 'intval_go_node'); TMPDIR = path.join(system.tmp, 'mcopy_digital');
checkDir(); checkDir();

View File

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

View File

@ -37,6 +37,7 @@ devices.listen = function () {
'use strict'; 'use strict';
ipcRenderer.on('ready', devices.ready); ipcRenderer.on('ready', devices.ready);
ipcRenderer.on('intval', devices.intvalCb); ipcRenderer.on('intval', devices.intvalCb);
ipcRenderer.on('digital', devices.digitalCb);
}; };
devices.ready = function (event, arg) { devices.ready = function (event, arg) {
'use strict'; '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) { devices.intvalCb = function (evt, args) {
'use strict'; 'use strict';
let state; 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; module.exports = devices;

View File

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

View File

@ -405,7 +405,8 @@ light.end = async function (rgb, id, ms) {
} }
proj.state = { proj.state = {
dir : true //default dir dir : true, //default dir
digital : false
} }
proj.init = function () { proj.init = function () {
proj.listen() proj.listen()
@ -419,20 +420,32 @@ proj.set = async function (dir, id) {
cmd = mcopy.cfg.arduino.cmd.proj_backward cmd = mcopy.cfg.arduino.cmd.proj_backward
} }
proj.state.dir = dir proj.state.dir = dir
try { if (proj.digital) {
ms = await arduino.send('projector', cmd) dig.set(dir)
} catch (err) { } else {
console.error(err) try {
ms = await arduino.send('projector', cmd)
} catch (err) {
console.error(err)
}
} }
return await proj.end(cmd, id, ms) return await proj.end(cmd, id, ms)
} }
proj.move = async function (frame, id) { proj.move = async function (frame, id) {
const cmd = mcopy.cfg.arduino.cmd.projector const cmd = mcopy.cfg.arduino.cmd.projector
let ms let ms
try { if (proj.digital) {
ms = await arduino.send('projector', cmd) try {
} catch (err) { ms = await dig.move()
console.error(err) } 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) return await proj.end(mcopy.cfg.arduino.cmd.projector, id, ms)
} }
@ -453,6 +466,7 @@ proj.listen = function () {
} }
event.returnValue = true event.returnValue = true
}) })
ipcMain.on('digital', proj.connectDigital)
} }
proj.end = async function (cmd, id, ms) { proj.end = async function (cmd, id, ms) {
let message = '' 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}) 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.intval = null
cam.state = { cam.state = {
dir : true //default dir dir : true //default dir
@ -510,10 +612,17 @@ cam.move = async function (frame, id) {
const cmd = mcopy.cfg.arduino.cmd.camera const cmd = mcopy.cfg.arduino.cmd.camera
let ms let ms
if (cam.intval) { if (cam.intval) {
try {
ms = await cam.intval.move() ms = await cam.intval.move()
} catch (err) {
console.error(err);
}
} else { } 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 }) log.info('Camera move time', { ms })
return cam.end(cmd, id, 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}) 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 () { log.file = function () {
let logPath = path.join(os.homedir(), `/.config/mcopy/`) let logPath = path.join(os.homedir(), `/.config/mcopy/`)
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
@ -675,6 +797,7 @@ var init = async function () {
proj.init() proj.init()
cam.init() cam.init()
dev.init() dev.init()
seq.init()
//capture = require('capture')(SYSTEM); //redundant //capture = require('capture')(SYSTEM); //redundant