UNPKG

node-omx

Version:

Provide a nodejs interface for omxplayer (Raspberry Pi player). Support multiple file and loop.

308 lines (258 loc) 7.37 kB
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;