|
|
@@ -1,7 +1,5 @@
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
-"use strict";
|
|
|
-
|
|
|
var hlsdump = require('commander');
|
|
|
hlsdump.version('0.0.0')
|
|
|
.usage('[options] <url>')
|
|
|
@@ -33,10 +31,12 @@ hlsdump.version('0.0.0')
|
|
|
var util = require('util'),
|
|
|
url = require('url'),
|
|
|
fs = require('fs'),
|
|
|
- http = require('http');
|
|
|
+ http = require('http'),
|
|
|
+ crypto = require('crypto');
|
|
|
|
|
|
var streamprocess = require('streamprocess'),
|
|
|
- oncemore = require('oncemore');
|
|
|
+ oncemore = require('oncemore'),
|
|
|
+ uristream = require('uristream');
|
|
|
var reader = require('../lib/reader'),
|
|
|
tssmooth = require('../lib/tssmooth'),
|
|
|
tsblast = require('../lib/tsblast');
|
|
|
@@ -59,6 +59,7 @@ var r = reader(src, {highWaterMark:(hlsdump.concurrent || 1) - 1, fullStream:hls
|
|
|
|
|
|
var totalDuration = 0, currentSegment = -1;
|
|
|
var reading = false;
|
|
|
+var keyCache = {};
|
|
|
|
|
|
streamprocess(r, function (obj, done) {
|
|
|
var meta = obj.meta;
|
|
|
@@ -76,7 +77,6 @@ streamprocess(r, function (obj, done) {
|
|
|
|
|
|
reading = true;
|
|
|
currentSegment = obj.seq;
|
|
|
- stream.pipe(buffer, { end: false });
|
|
|
if (size === -1 || !hooked) {
|
|
|
size = 0;
|
|
|
obj.stream.on('data', function(chunk) {
|
|
|
@@ -86,16 +86,53 @@ streamprocess(r, function (obj, done) {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- stream.once('end', 'error', function(err) {
|
|
|
- reading = false;
|
|
|
- console.error('segment done at '+totalDuration.toFixed(0)+' seconds, avg bitrate (kbps):', (size / (duration * 1024/8)).toFixed(1));
|
|
|
- if (err) {
|
|
|
- stats.meter('streamErrors').mark();
|
|
|
- console.error('stream error', err.stack || err);
|
|
|
+ var keyData = r.index.keyForSeqNo(obj.seq);
|
|
|
+ if (keyData && keyData.method === 'AES-128' && keyData.uri && keyData.uri.length > 2) {
|
|
|
+ fetchKey(function(err, key) {
|
|
|
+ if (err) {
|
|
|
+ console.error('key fetch failed:', err);
|
|
|
+ return pushBuffer(stream);
|
|
|
+ }
|
|
|
+ var iv = new Buffer(keyData.iv.slice(-32), 'hex');
|
|
|
+ var decrypt = crypto.createDecipheriv('aes-128-cbc', key, iv);
|
|
|
+
|
|
|
+ stream.on('error', function(err) {
|
|
|
+ decrypt.emit('error', err);
|
|
|
+ });
|
|
|
+ pushBuffer(oncemore(stream.pipe(decrypt)));
|
|
|
+ });
|
|
|
+
|
|
|
+ function fetchKey(cb) {
|
|
|
+ var uri = url.resolve(r.url, keyData.uri.slice(1,-1));
|
|
|
+ if (keyCache[uri]) return cb(null, keyCache[uri]);
|
|
|
+
|
|
|
+ var key = new Buffer(0);
|
|
|
+ oncemore(uristream(uri, { whitelist:['http', 'https', 'data'], timeout: 10*1000 }))
|
|
|
+ .on('data', function(chunk) {
|
|
|
+ key = Buffer.concat([key, chunk]);
|
|
|
+ })
|
|
|
+ .once('error', 'end', function(err) {
|
|
|
+ keyCache[uri] = key;
|
|
|
+ return cb(err, key);
|
|
|
+ });
|
|
|
}
|
|
|
- hook(buffer);
|
|
|
- done();
|
|
|
- });
|
|
|
+ } else {
|
|
|
+ pushBuffer(stream);
|
|
|
+ }
|
|
|
+
|
|
|
+ function pushBuffer(stream) {
|
|
|
+ stream.pipe(buffer, { end: false });
|
|
|
+ stream.once('end', 'error', function(err) {
|
|
|
+ reading = false;
|
|
|
+ console.error('segment done at '+totalDuration.toFixed(0)+' seconds, avg bitrate (kbps):', (size / (duration * 1024/8)).toFixed(1));
|
|
|
+ if (err) {
|
|
|
+ stats.meter('streamErrors').mark();
|
|
|
+ console.error('stream error', err.stack || err);
|
|
|
+ }
|
|
|
+ hook(buffer);
|
|
|
+ done();
|
|
|
+ });
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
r.once('index', function() {
|