node-omx
Version:
Provide a nodejs interface for omxplayer (Raspberry Pi player). Support multiple file and loop.
308 lines (258 loc) • 7.37 kB
JavaScript
var spawn = require('child_process').spawn;
var path = require('path');
var fs = require('fs');
var glob = require('glob');
var EventEmitter = require('events').EventEmitter;
var LoopHelper = require('./loop_helper.js').LoopHelper;
var exec = require('child_process').exec;
var _ = require('lodash');
/* keep compatibility with older versions of nodejs */
if (fs.existsSync) {
path.existsSync = fs.existsSync;
} else {
fs.existsSync = path.existsSync;
}
var omxdirector = function () {
var that = Object.create(EventEmitter.prototype);
var nativeLoop = false;
var videoDir = './';
var videoSuffix = '.*';
var nextFile = null;
var currentVideos = [];
var currentSettings = {};
var loopHelper = null;
var commands = {
'pause': 'p',
'quit': 'q',
'volup': '+',
'voldown': '-'
};
var omxProcess = null;
var paused = false;
var sendAction = function (action) {
if (commands[action] && omxProcess) {
try {
omxProcess.stdin.write(commands[action], function (err) {
if (err) console.log(err);
});
} catch (err) {
console.log(err);
}
}
};
var resolveFilePaths = function (videos) {
/* reset currentFiles, it will contain only valid videos */
currentVideos = [];
var ret = [];
videos.forEach(function (video) {
var videoExtension = path.extname(video) === '' ? videoSuffix : '';
var files = glob.sync(path.resolve(videoDir, video + videoExtension));
if (files.length === 0) {
that.emit('error', new Error('Pattern or file not found ' + path.resolve(videoDir, video + videoExtension)));
}
_.forEach(files, function(file) {
currentVideos.push(path.basename(file));
ret.push(file);
});
});
return ret;
};
var checkWindowSettings = function(win) {
if (_.isUndefined(win)) return false;
for (coordinate in win) {
if (win.hasOwnProperty(coordinate)) {
if (!_.isNumber(win[coordinate])) return false
}
}
return true;
};
var open = function (videos, options) {
var settings = options || {};
currentSettings = settings;
var cmd = 'omxplayer';
var respawn = null;
var args = [];
if (!_.isUndefined(settings.audioOutput) && settings.audioOutput !== 'default') {
args.push('-o');
args.push(settings.audioOutput);
}
if (settings.loop === true) {
if (nativeLoop) {
args.push('--loop');
}
}
if (checkWindowSettings(settings.win)) {
args.push('--win');
args.push([settings.win.x1, settings.win.y1, settings.win.x2, settings.win.y2].join(','))
}
if (!_.isUndefined(settings.layer)) {
args.push('--layer');
args.push(settings.layer);
}
if (typeof videos === 'string') {
videos = [videos];
}
/* resolve video paths from pattern */
var realfiles = resolveFilePaths(videos);
// if no file is found we can stop here
if (_.isEmpty(realfiles)) return;
/* check if randomOne is active. If so pick a random video from realfiles*/
if (settings.randomOne) realfiles = [_.sample(realfiles)]
if (nativeLoop) {
/* all files to omxplayer parameters */
args.push.apply(args, realfiles);
} else {
/*
* only first file to omxplayer parameter. following files are handled by
* loopHelper. also set nextFile
*/
nextFile = realfiles[0];
args.push(realfiles[0]);
}
if (!nativeLoop && ((realfiles.length > 1) || (settings.loop))) {
/* no native loop support, enable helper */
loopHelper = LoopHelper(realfiles, settings.loop);
respawn = function () {
if (!loopHelper) {
/* respawn ignored, stop requested */
that.emit('stop');
return;
}
/* change file */
nextFile = loopHelper.getNext();
if (!nextFile) {
/* respawn ignored, loop ended */
loopHelper = null;
omxProcess = null;
that.emit('stop');
} else {
/* respawn */
args[args.length - 1] = nextFile;
omxProcess = spawn(cmd, args, {
stdio: ['pipe', null, process.stderr]
});
that.emit('changeVideo', nextFile);
omxProcess.once('exit', respawn);
/* check if omxplayer hangs when video should be finished */
}
};
omxProcess = spawn(cmd, args, {
stdio: ['pipe', null, process.stderr]
});
omxProcess.once('exit', respawn);
} else {
/* native loop support enabled or not requested */
omxProcess = spawn(cmd, args, {
stdio: ['pipe', null, process.stderr]
});
omxProcess.once('exit', function (code, signal) {
omxProcess = null;
that.emit('stop');
});
}
that.emit('load', videos, options);
};
var play = function (videos, options) {
if (omxProcess) {
if (!paused) {
return false;
}
sendAction('pause');
paused = false;
that.emit('play');
return true;
}
if (!videos) {
throw new TypeError("No files specified");
}
if (typeof videos !== 'string' && !_.isArray(videos)) {
throw new TypeError("Incorrect value for videos: " + videos);
}
open(videos, options);
that.emit('play');
return true;
};
var pause = function () {
if (paused) {
return false;
}
sendAction('pause');
paused = true;
that.emit('pause');
return true;
};
var volup = function () {
sendAction('volup');
that.emit('volup');
return true;
};
var voldown = function () {
sendAction('voldown');
that.emit('voldown');
return true;
};
var handleQuitTimeout = function (oldOmxProcess, timeout) {
var timeoutHandle = setTimeout(function () {
console.log('omxplayer still running. kill forced');
oldOmxProcess.kill('SIGTERM');
}, timeout);
oldOmxProcess.once('exit', function () {
clearTimeout(timeoutHandle);
});
};
var stop = function () {
if (!omxProcess) {
/* ignore, no omxProcess to stop */
return false;
}
loopHelper = null;
sendAction('quit');
handleQuitTimeout(omxProcess, 250);
omxProcess = null;
return true;
};
var isPlaying = function () {
return omxProcess && !paused;
};
var isLoaded = function () {
return omxProcess;
};
var getStatus = function () {
if (isLoaded()) {
return {
videos: currentVideos,
current: nextFile,
settings: currentSettings,
playing: isPlaying(),
loaded: true
};
}
return {
loaded: false
};
};
var setVideoDir = function (dir) {
videoDir = dir;
};
var setVideoSuffix = function (suffix) {
videoSuffix = suffix;
};
var enableNativeLoop = function () {
/* nativeLoop is currently disabled */
// nativeLoop = true;
return that;
};
that.play = play;
that.pause = pause;
that.stop = stop;
that.volup = volup;
that.voldown = voldown;
that.isPlaying = isPlaying;
that.isLoaded = isLoaded;
that.getStatus = getStatus;
that.setVideoDir = setVideoDir;
that.setVideoSuffix = setVideoSuffix;
that.enableNativeLoop = enableNativeLoop;
return that;
};
module.exports = omxdirector;