Changed output to processor to break dependancy on HTTP
This commit is contained in:
parent
1b29f0bac1
commit
8d3ce97474
71
basehttpstreamprocessor.js
Normal file
71
basehttpstreamprocessor.js
Normal file
@ -0,0 +1,71 @@
|
||||
var util = require('util');
|
||||
var http = require('http');
|
||||
|
||||
var BaseProcessor = require('./baseprocessor');
|
||||
|
||||
|
||||
function BaseHTTPStreamProcessor(camId, cam, now)
|
||||
{
|
||||
BaseProcessor.apply(this, arguments);
|
||||
}
|
||||
|
||||
util.inherits(BaseHTTPStreamProcessor, BaseProcessor);
|
||||
|
||||
|
||||
BaseHTTPStreamProcessor.prototype.run = function()
|
||||
{
|
||||
var self = this;
|
||||
var timer = null;
|
||||
var req = null;
|
||||
|
||||
var doCleanup = function()
|
||||
{
|
||||
if (timer !== null)
|
||||
{
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
|
||||
self.cleanup();
|
||||
self.doEnd();
|
||||
};
|
||||
|
||||
req = http.request(self.cam.options.url, function(res)
|
||||
{
|
||||
self.doStart();
|
||||
|
||||
timer = setTimeout(function()
|
||||
{
|
||||
req.abort();
|
||||
doCleanup();
|
||||
}, self.cam.options.time || 10000);
|
||||
|
||||
|
||||
res.on('end', function()
|
||||
{
|
||||
doCleanup();
|
||||
});
|
||||
|
||||
res.pipe(self.getStream());
|
||||
});
|
||||
|
||||
req.on('error', function(e)
|
||||
{
|
||||
console.log(e);
|
||||
doCleanup();
|
||||
});
|
||||
|
||||
req.end();
|
||||
};
|
||||
|
||||
|
||||
BaseHTTPStreamProcessor.prototype.getStream = function()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BaseHTTPStreamProcessor.prototype.cleanup = function()
|
||||
{
|
||||
}
|
||||
|
||||
module.exports = BaseHTTPStreamProcessor;
|
42
baseprocessor.js
Normal file
42
baseprocessor.js
Normal file
@ -0,0 +1,42 @@
|
||||
var events = require('events');
|
||||
var util = require('util');
|
||||
|
||||
function BaseProcessor(camId, cam, now)
|
||||
{
|
||||
this.camId = camId;
|
||||
this.cam = cam;
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
/*
|
||||
Descendants are required to call the following functions:
|
||||
|
||||
doStart
|
||||
Call when the processing starts. This may be delayed slightly, for example,
|
||||
right after the stream is connected or when the first chunk of data arrives.
|
||||
|
||||
doEnd
|
||||
Call when the processor is done.
|
||||
|
||||
*/
|
||||
util.inherits(BaseProcessor, events.EventEmitter);
|
||||
|
||||
|
||||
BaseProcessor.prototype.run = function()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BaseProcessor.prototype.doStart = function()
|
||||
{
|
||||
this.emit('start');
|
||||
}
|
||||
|
||||
|
||||
BaseProcessor.prototype.doEnd = function()
|
||||
{
|
||||
this.emit('end');
|
||||
}
|
||||
|
||||
|
||||
module.exports = BaseProcessor;
|
69
capture.js
69
capture.js
@ -76,7 +76,7 @@ module.exports =
|
||||
{
|
||||
if (cams.hasOwnProperty(camId))
|
||||
{
|
||||
require('./output-' + cams[camId].output);
|
||||
require('./processor-' + cams[camId].processor);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -84,71 +84,38 @@ module.exports =
|
||||
|
||||
start: function(camId, cam, now)
|
||||
{
|
||||
var timer = null;
|
||||
var req = null;
|
||||
var output = new (require('./output-' + cam.output))(camId, cam.outputOptions, now);
|
||||
var duringDone = false;
|
||||
var queueAfter = false;
|
||||
var processor = new (require('./processor-' + cam.processor))(camId, cam, now);
|
||||
var duringCommandsDone = false;
|
||||
var queueAfterCommand = false;
|
||||
|
||||
|
||||
runCommands(cam.before, 'before', function()
|
||||
{
|
||||
var cleanup = function()
|
||||
{
|
||||
if (timer !== null)
|
||||
{
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
|
||||
if (output !== null)
|
||||
{
|
||||
output.cleanup();
|
||||
output = null;
|
||||
}
|
||||
|
||||
// If the stream is cut short, or the time is configured
|
||||
// to be less than the duration of the 'during' commands,
|
||||
// queue it up.
|
||||
if (duringDone)
|
||||
runCommands(cam.after, 'after', function() { });
|
||||
else
|
||||
queueAfter = true;
|
||||
};
|
||||
|
||||
req = http.request(cam.url, function(res)
|
||||
processor.on('start', function()
|
||||
{
|
||||
runCommands(cam.during, 'during', function()
|
||||
{
|
||||
if (queueAfter)
|
||||
// Check if the processor has already finished and the
|
||||
// 'after' commands should be run immediately.
|
||||
if (queueAfterCommand)
|
||||
runCommands(cam.after, 'after', function() { });
|
||||
else
|
||||
duringDone = true;
|
||||
duringCommandsDone = true;
|
||||
});
|
||||
|
||||
|
||||
timer = setTimeout(function()
|
||||
{
|
||||
req.abort();
|
||||
cleanup();
|
||||
}, cam.time || config.defaultTime || 10000);
|
||||
|
||||
|
||||
res.on('end', function()
|
||||
{
|
||||
cleanup();
|
||||
});
|
||||
|
||||
res.pipe(output.getStream());
|
||||
});
|
||||
|
||||
req.on('error', function(e)
|
||||
processor.on('end', function()
|
||||
{
|
||||
console.log(e);
|
||||
cleanup();
|
||||
// If the stream is cut short, or the time is configured
|
||||
// to be less than the duration of the 'during' commands,
|
||||
// queue it up.
|
||||
if (duringCommandsDone)
|
||||
runCommands(cam.after, 'after', function() { });
|
||||
else
|
||||
queueAfterCommand = true;
|
||||
});
|
||||
|
||||
req.end();
|
||||
processor.run();
|
||||
});
|
||||
}
|
||||
};
|
@ -2,23 +2,23 @@ var foscam = require('./config-foscam');
|
||||
var config = {};
|
||||
|
||||
config.port = 5705;
|
||||
config.defaultTime = 5000;
|
||||
|
||||
config.cams =
|
||||
{
|
||||
frontdoor:
|
||||
{
|
||||
// You can specify any URL string here, the Foscam helper just makes it easier
|
||||
// for compatible models. If you add your own config helper, please publish!
|
||||
url: foscam.mjpegStream('10.138.1.10', 'viewer', 'verysecure'),
|
||||
time: 5000,
|
||||
|
||||
output: 'ffmpeg',
|
||||
outputOptions:
|
||||
processor: 'ffmpeg',
|
||||
options:
|
||||
{
|
||||
// You can specify any URL string here, the Foscam helper just makes it easier
|
||||
// for compatible models. If you add your own config helper, please publish!
|
||||
url: foscam.mjpegStream('10.138.1.10', 'viewer', 'verysecure'),
|
||||
time: 5000,
|
||||
|
||||
inputFormat: 'mjpeg',
|
||||
outputFormat: 'avi',
|
||||
videoCodec: 'libx264',
|
||||
|
||||
filename: '[/srv/www/publiccam/]YYYY-MM-DD HH.mm.ss[/<camId>.avi]'
|
||||
}
|
||||
|
||||
@ -27,16 +27,16 @@ config.cams =
|
||||
|
||||
backdoor:
|
||||
{
|
||||
// You can use username:password@ in the URL to log in with basic
|
||||
// authentication. Note that some cams, like Foscam, use Digest
|
||||
// authentication which is not supported. For Foscam you can provide
|
||||
// the login in the parameters instead.
|
||||
url: 'http://viewer:verysecure@10.138.1.11/video.cgi',
|
||||
time: 5000,
|
||||
|
||||
output: 'mjpeg-split',
|
||||
outputOptions:
|
||||
processor: 'mjpeg-split',
|
||||
options:
|
||||
{
|
||||
// You can use username:password@ in the URL to log in with basic
|
||||
// authentication. Note that some cams, like Foscam, use Digest
|
||||
// authentication which is not supported. For Foscam you can provide
|
||||
// the login in the parameters instead.
|
||||
url: 'http://viewer:verysecure@10.138.1.11/video.cgi',
|
||||
time: 5000,
|
||||
|
||||
filename: '[/srv/www/publiccam/]YYYY-MM-DD HH.mm.ss[/<camId> <frame>.avi]'
|
||||
}
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
var util = require('util');
|
||||
var stream = require('stream');
|
||||
var FfmpegCommand = require('fluent-ffmpeg');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
|
||||
|
||||
function OutputFFMPEG(camId, options, now)
|
||||
{
|
||||
this.output = new stream.PassThrough();
|
||||
var command = new FfmpegCommand();
|
||||
command
|
||||
.input(this.output)
|
||||
.inputFormat(options.inputFormat);
|
||||
|
||||
if (options.inputFormat === 'mjpeg')
|
||||
command.inputOption('-use_wallclock_as_timestamps 1');
|
||||
|
||||
command
|
||||
.output(helpers.createVariableFilename(options.filename, now,
|
||||
{
|
||||
camId: camId
|
||||
}))
|
||||
.videoCodec(options.videoCodec)
|
||||
.outputFormat(options.outputFormat)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
||||
OutputFFMPEG.prototype.getStream = function()
|
||||
{
|
||||
return this.output;
|
||||
};
|
||||
|
||||
|
||||
OutputFFMPEG.prototype.cleanup = function()
|
||||
{
|
||||
if (this.output !== null)
|
||||
{
|
||||
this.output.end();
|
||||
this.output = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OutputFFMPEG;
|
@ -1,44 +0,0 @@
|
||||
var MjpegConsumer = require('mjpeg-consumer');
|
||||
var FileOnWrite = require('file-on-write');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
|
||||
|
||||
function OutputMJPEGSplit(camId, options, now)
|
||||
{
|
||||
var frameCounter = 0;
|
||||
|
||||
this.output = new FileOnWrite({
|
||||
filename: function(data)
|
||||
{
|
||||
frameCounter++;
|
||||
|
||||
return helpers.createVariableFilename(options.filename, now,
|
||||
{
|
||||
camId: camId,
|
||||
frame: frameCounter
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.consumer = new MjpegConsumer();
|
||||
this.consumer.pipe(this.output);
|
||||
}
|
||||
|
||||
|
||||
OutputMJPEGSplit.prototype.getStream = function()
|
||||
{
|
||||
return this.consumer;
|
||||
};
|
||||
|
||||
|
||||
OutputMJPEGSplit.prototype.cleanup = function()
|
||||
{
|
||||
if (this.consumer !== null)
|
||||
{
|
||||
this.consumer.end();
|
||||
this.consumer = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OutputMJPEGSplit;
|
@ -1,30 +0,0 @@
|
||||
var fs = require('fs');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
|
||||
|
||||
function OutputRaw(camId, options, now)
|
||||
{
|
||||
this.output = fs.createWriteStream(helpers.createVariableFilename(options.filename, now,
|
||||
{
|
||||
camId: camId
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
OutputRaw.prototype.getStream = function()
|
||||
{
|
||||
return this.output;
|
||||
};
|
||||
|
||||
|
||||
OutputRaw.prototype.cleanup = function()
|
||||
{
|
||||
if (this.output !== null)
|
||||
{
|
||||
this.output.end();
|
||||
this.output = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OutputRaw;
|
55
processor-ffmpeg.js
Normal file
55
processor-ffmpeg.js
Normal file
@ -0,0 +1,55 @@
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var stream = require('stream');
|
||||
|
||||
var FfmpegCommand = require('fluent-ffmpeg');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
var BaseProcessor = require('./baseprocessor');
|
||||
|
||||
|
||||
function FFMPEGProcessor()
|
||||
{
|
||||
BaseProcessor.apply(this, arguments);
|
||||
}
|
||||
|
||||
util.inherits(FFMPEGProcessor, BaseProcessor);
|
||||
|
||||
|
||||
|
||||
FFMPEGProcessor.prototype.run = function()
|
||||
{
|
||||
var self = this;
|
||||
var command = new FfmpegCommand();
|
||||
command
|
||||
.input(this.cam.options.input)
|
||||
.inputOptions(['-t ' + this.cam.options.time]);
|
||||
|
||||
if (typeof(this.cam.options.inputFormat) !== 'undefined')
|
||||
{
|
||||
command.inputFormat(this.cam.options.inputFormat);
|
||||
if (this.cam.options.inputFormat === 'mjpeg')
|
||||
command.inputOption('-use_wallclock_as_timestamps 1');
|
||||
}
|
||||
|
||||
command
|
||||
.output(helpers.createVariableFilename(this.cam.options.filename, this.now,
|
||||
{
|
||||
camId: this.camId
|
||||
}))
|
||||
.videoCodec(this.cam.options.videoCodec)
|
||||
.outputFormat(this.cam.options.outputFormat);
|
||||
|
||||
command.on('end', function()
|
||||
{
|
||||
self.doEnd();
|
||||
});
|
||||
|
||||
self.doStart();
|
||||
command.run();
|
||||
|
||||
FFMPEGProcessor.super_.prototype.run.call(this);
|
||||
}
|
||||
|
||||
|
||||
module.exports = FFMPEGProcessor;
|
59
processor-http-ffmpeg.js
Normal file
59
processor-http-ffmpeg.js
Normal file
@ -0,0 +1,59 @@
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var stream = require('stream');
|
||||
|
||||
var FfmpegCommand = require('fluent-ffmpeg');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
var BaseHTTPStreamProcessor = require('./basehttpstreamprocessor');
|
||||
|
||||
|
||||
function HTTPFFMPEGProcessor()
|
||||
{
|
||||
BaseHTTPStreamProcessor.apply(this, arguments);
|
||||
}
|
||||
|
||||
util.inherits(HTTPFFMPEGProcessor, BaseHTTPStreamProcessor);
|
||||
|
||||
|
||||
|
||||
HTTPFFMPEGProcessor.prototype.run = function()
|
||||
{
|
||||
this.output = new stream.PassThrough();
|
||||
var command = new FfmpegCommand();
|
||||
command
|
||||
.input(this.output)
|
||||
.inputFormat(this.cam.options.inputFormat);
|
||||
|
||||
if (this.cam.options.inputFormat === 'mjpeg')
|
||||
command.inputOption('-use_wallclock_as_timestamps 1');
|
||||
|
||||
command
|
||||
.output(helpers.createVariableFilename(this.cam.options.filename, this.now,
|
||||
{
|
||||
camId: this.camId
|
||||
}))
|
||||
.videoCodec(this.cam.options.videoCodec)
|
||||
.outputFormat(this.cam.options.outputFormat)
|
||||
.run();
|
||||
|
||||
HTTPFFMPEGProcessor.super_.prototype.run.call(this);
|
||||
}
|
||||
|
||||
|
||||
HTTPFFMPEGProcessor.prototype.getStream = function()
|
||||
{
|
||||
return this.output;
|
||||
}
|
||||
|
||||
|
||||
HTTPFFMPEGProcessor.prototype.cleanup = function()
|
||||
{
|
||||
if (this.output !== null)
|
||||
{
|
||||
this.output.end();
|
||||
this.output = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HTTPFFMPEGProcessor;
|
64
processor-http-mjpegsplit.js
Normal file
64
processor-http-mjpegsplit.js
Normal file
@ -0,0 +1,64 @@
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
|
||||
var MjpegConsumer = require('mjpeg-consumer');
|
||||
var FileOnWrite = require('file-on-write');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
var BaseHTTPStreamProcessor = require('./basehttpstreamprocessor');
|
||||
|
||||
function HTTPMJPEGSplitProcessor()
|
||||
{
|
||||
BaseHTTPStreamProcessor.apply(this, arguments);
|
||||
}
|
||||
|
||||
util.inherits(HTTPMJPEGSplitProcessor, BaseHTTPStreamProcessor);
|
||||
|
||||
|
||||
|
||||
HTTPMJPEGSplitProcessor.prototype.run = function()
|
||||
{
|
||||
var self = this;
|
||||
var frameCounter = 0;
|
||||
|
||||
this.output = new FileOnWrite({
|
||||
filename: function(data)
|
||||
{
|
||||
frameCounter++;
|
||||
|
||||
return helpers.createVariableFilename(self.cam.options.filename, self.now,
|
||||
{
|
||||
camId: self.camId,
|
||||
frame: frameCounter
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.consumer = new MjpegConsumer();
|
||||
this.consumer.pipe(this.output);
|
||||
|
||||
|
||||
HTTPMJPEGSplitProcessor.super_.prototype.run.call(this);
|
||||
}
|
||||
|
||||
|
||||
HTTPMJPEGSplitProcessor.prototype.getStream = function()
|
||||
{
|
||||
return this.consumer;
|
||||
}
|
||||
|
||||
module.exports = HTTPMJPEGSplitProcessor;
|
||||
|
||||
|
||||
|
||||
|
||||
HTTPMJPEGSplitProcessor.prototype.cleanup = function()
|
||||
{
|
||||
if (this.consumer !== null)
|
||||
{
|
||||
this.consumer.end();
|
||||
this.consumer = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HTTPMJPEGSplitProcessor;
|
46
processor-http-raw.js
Normal file
46
processor-http-raw.js
Normal file
@ -0,0 +1,46 @@
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
|
||||
var helpers = require('./helpers');
|
||||
var BaseHTTPStreamProcessor = require('./basehttpstreamprocessor');
|
||||
|
||||
function HTTPRawProcessor()
|
||||
{
|
||||
BaseHTTPStreamProcessor.apply(this, arguments);
|
||||
}
|
||||
|
||||
util.inherits(HTTPRawProcessor, BaseHTTPStreamProcessor);
|
||||
|
||||
|
||||
|
||||
HTTPRawProcessor.prototype.run = function()
|
||||
{
|
||||
this.output = fs.createWriteStream(helpers.createVariableFilename(this.cam.options.filename, this.now,
|
||||
{
|
||||
camId: this.camId
|
||||
}));
|
||||
|
||||
HTTPRawProcessor.super_.prototype.run.call(this);
|
||||
}
|
||||
|
||||
|
||||
HTTPRawProcessor.prototype.getStream = function()
|
||||
{
|
||||
return this.output;
|
||||
}
|
||||
|
||||
module.exports = HTTPRawProcessor;
|
||||
|
||||
|
||||
|
||||
|
||||
HTTPRawProcessor.prototype.cleanup = function()
|
||||
{
|
||||
if (this.output !== null)
|
||||
{
|
||||
this.output.end();
|
||||
this.output = null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HTTPRawProcessor;
|
Loading…
Reference in New Issue
Block a user