├── .gitignore ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rtmpdump 2 | ============= 3 | ### A streamable wrapper around the rtmpdump CLI 4 | 5 | This module is a thin wrapper around the `rtmpdump` binary. It provides 6 | a readable stream you can pipe to your heart's content. 7 | 8 | The `rtmpdump` binary must be installed on your system. 9 | 10 | The options are those used by `rtmpdump`, both short and long options are supported (see example). 11 | `$ rtmpdump --help` to list them all. 12 | 13 | Installation 14 | ------------ 15 | 16 | ``` bash 17 | $ npm install rtmpdump 18 | ``` 19 | 20 | Example 21 | ------- 22 | 23 | ``` javascript 24 | var rtmpdump = require('rtmpdump'); 25 | var fs = require('fs'); 26 | 27 | var options = { 28 | rtmp: 'rtmp://host.tld/app/path', 29 | playpath: 'mp4:playpath', 30 | pageUrl: 'http://host.tld/somepage.html', 31 | swfVfy: 'http://host.tld/player.swf', 32 | v: null // parameter-less command line switches must have null as a value 33 | }; 34 | 35 | var stream = rtmpdump.createStream(options); 36 | 37 | stream.on('connected', function(info) { 38 | // info provides various details about the stream 39 | // duration, resolution, codecs, ... 40 | console.log(info); 41 | }); 42 | 43 | stream.on('progress', function(kbytes, elapsed, percent) { 44 | console.log('%s kbytes read, %s secs elapsed, %s%%', kbytes, elapsed, percent); 45 | }); 46 | 47 | stream.on('error', function(err) { 48 | // as usual, unhandled error events will throw 49 | console.log(err); 50 | process.exit(1); 51 | }); 52 | 53 | stream.pipe(fs.createWriteStream('video.mp4')); 54 | 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var spawn = require('child_process').spawn; 2 | var split = require('split'); 3 | 4 | function parseInfo(chunk) { 5 | 6 | var info = {}; 7 | var currentSection; 8 | 9 | var lines = chunk.replace(/\r/g, '').split('\n'); 10 | // the .replace removes the remaining \r which appear 11 | // on windows systems after the split operation 12 | 13 | lines.forEach(function(line) { 14 | // skip every line once we have an error 15 | if(info.status && info.status === 'error') return; 16 | 17 | var tmp = line.split(' '); 18 | var prefix = tmp.shift(); 19 | var data = tmp.join(' '); 20 | 21 | if(prefix === 'ERROR:' || prefix === 'rtmpdump:') { 22 | 23 | info.status = 'error'; 24 | info.message = data; 25 | 26 | } else if(prefix === 'INFO:') { 27 | 28 | if(info.status === 'connected') { 29 | if(data.indexOf(':') !== -1) { 30 | // this is a "section:" line 31 | currentSection = data.slice(0, -1).toLowerCase(); // remove trailing ':' 32 | info[currentSection] = {}; 33 | } else { 34 | var kv = data.trim().split(/\s+/); 35 | info[currentSection][kv[0].toLowerCase()] = isNaN(kv[1]) ? kv[1] : Number(kv[1]); 36 | } 37 | } else if(data === 'Connected...') { 38 | info.status = 'connected'; 39 | } 40 | 41 | } 42 | 43 | }); 44 | 45 | return info; 46 | } 47 | 48 | function createStream(options) { 49 | 50 | // build an argument array from an object, skipping null *values* 51 | // { b: 'foo', o: null, bar: 'baz' } becomes "-b foo -o --bar baz" 52 | var args = []; 53 | Object.keys(options).forEach(function(key) { 54 | if(key.length === 1) args.push('-' + key); 55 | else args.push('--' + key); 56 | if(options[key]) args.push(options[key]); 57 | }); 58 | 59 | var rtmpdump = spawn('rtmpdump', args); 60 | 61 | var readable = rtmpdump.stdout; 62 | 63 | rtmpdump.stderr 64 | .pipe(split(/\r(?!\n)/)) 65 | .once('data', function(chunk) { 66 | // when split by \r the first chunk is status / stream info 67 | // since windows systems encode a newline with \r\n we need 68 | // to check if the \r has no trailing \n to get the right \r 69 | var info = parseInfo(chunk); 70 | 71 | if(info.status === 'connected') { 72 | delete info.status; 73 | readable.emit('connected', info); 74 | this.on('data', function(chunk) { 75 | var matches = chunk.match(/(\d+\.\d+)/g); 76 | // emit "progress" with kbytes, elapsed, percent 77 | readable.emit.apply(readable, ['progress'].concat(matches)); 78 | }); 79 | } else { 80 | readable.emit('error', new Error(info.message)); 81 | } 82 | 83 | }); 84 | 85 | return readable; 86 | } 87 | 88 | module.exports.createStream = createStream; 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtmpdump", 3 | "version": "0.1.1", 4 | "description": "A streamable wrapper around the rtmpdump CLI", 5 | "author": "thibauts", 6 | "license": "MIT", 7 | "main": "index.js", 8 | "dependencies": { 9 | "split": "~0.3.0" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/thibauts/node-rtmpdump.git" 17 | }, 18 | "keywords": [ 19 | "rtmp", 20 | "rtmpdump" 21 | ] 22 | } 23 | --------------------------------------------------------------------------------