#!/usr/bin/env node /* eslint-disable no-process-exit */ "use strict"; var hlsdump = require('commander'); hlsdump.version('0.0.0') .usage('[options] ') .option('-o, --output ', 'target file') .option('-u, --udp [host:port]', 'relay TS over UDP', function(val) { var r = { host: 'localhost', port: 1234 }; if (val) { var s = val.split(':'); if (s.length === 1) { r.port = parseInt(s[0], 10); } else { r.host = s[0]; r.port = parseInt(s[1], 10); } } return r; }) .option('-b, --buffer-size |full', 'try to buffer of input data (implies -s)', function(val) { if (val === 'full') return 0x80000000 - 1; return parseInt(val, 0); }) .option('-s, --sync', 'clock sync using stream PCR') .option('-f, --full-stream', 'fetch all stream data') .option('-c, --concurrent ', 'fetch using concurrent connections', parseInt) .option('-a, --user-agent ', 'HTTP User-Agent') .option('-i, --info-port ', 'report status using HTTP + json', parseInt) .option('--cookie ', 'add cookie header to key requests') .option('--key ', 'use oob key for decrypting segments', function(opt) {return new Buffer(opt, 'hex');}) .parse(process.argv); var fs = require('fs'), http = require('http'); var oncemore = require('oncemore'), HlsSegmentReader = require('hls-segment-reader'), UdpBlast = require('udp-blast'); var HlsReader = require('../lib/hls-reader'); var stats = require('measured').createCollection(); var src = hlsdump.args[0]; if (!src) { hlsdump.help(); process.exit(-1); } if (hlsdump.bufferSize) hlsdump.sync = true; var r = new HlsReader(new HlsSegmentReader(src, { highWaterMark: (hlsdump.concurrent || 1) - 1, fullStream: hlsdump.fullStream }), hlsdump); var totalDuration = 0, currentSegment = -1; r.on('segment', function(data) { var downloadSize = data.meta.size; var duration = data.segment.duration; // calculate size when missing if (downloadSize === -1) { downloadSize = 0; data.stream.on('data', function(chunk) { downloadSize += chunk.length; }); } var stopwatch = stats.timer('fetchTime').start(); oncemore(data.stream).once('close', 'end', 'error', function(err) { stopwatch.end(); console.error('segment done at ' + totalDuration.toFixed(0) + ' seconds, avg bitrate (kbps):', (downloadSize / (duration * 1024 / 8)).toFixed(1)); if (err) { stats.meter('streamErrors').mark(); } }); }); if (hlsdump.udp) { var dst = (hlsdump.udp === true) ? null : hlsdump.udp; r.pipe(new UdpBlast(dst, { packetSize: 7 * 188 })); } if (hlsdump.output) { if (hlsdump.output === '-') r.pipe(process.stdout); else r.pipe(fs.createWriteStream(hlsdump.output)); } // setup stat tracking stats.gauge('bufferBytes', function() { return r.buffer._readableState.length/* + buffer._writableState.length*/; }); stats.gauge('currentSegment', function() { return currentSegment; }); stats.gauge('index.first', function() { return r.reader.index ? r.reader.index.first_seq_no : -1; }); stats.gauge('index.last', function() { return r.reader.index ? r.reader.index.lastSeqNo() : -1; }); stats.gauge('totalDuration', function() { return totalDuration; }); stats.meter('streamErrors'); if (hlsdump.infoPort) { http.createServer(function (req, res) { if (req.method === 'GET') { var data = JSON.stringify(stats, null, ' '); res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': data.length }); res.write(data); } res.end(); }).listen(hlsdump.infoPort); }