|
@@ -1,17 +1,23 @@
|
|
|
'use strict';
|
|
'use strict';
|
|
|
|
|
|
|
|
const Util = require('util');
|
|
const Util = require('util');
|
|
|
|
|
+const Url = require('url');
|
|
|
const Readable = require('readable-stream/readable');
|
|
const Readable = require('readable-stream/readable');
|
|
|
const Passthrough = require('readable-stream/passthrough');
|
|
const Passthrough = require('readable-stream/passthrough');
|
|
|
|
|
|
|
|
|
|
+const Async = require('async');
|
|
|
const StreamEach = require('stream-each');
|
|
const StreamEach = require('stream-each');
|
|
|
const Oncemore = require('oncemore');
|
|
const Oncemore = require('oncemore');
|
|
|
|
|
+const UriStream = require('uristream');
|
|
|
|
|
+const deepEqual = require('deep-equal');
|
|
|
|
|
|
|
|
-const tssmooth = require('./tssmooth');
|
|
|
|
|
|
|
+const TsSmooth = require('./tssmooth');
|
|
|
const SegmentDecrypt = require('./segment-decrypt');
|
|
const SegmentDecrypt = require('./segment-decrypt');
|
|
|
|
|
|
|
|
|
|
|
|
|
const internals = {
|
|
const internals = {
|
|
|
|
|
+ mapFetchTimeout: 30 * 1000,
|
|
|
|
|
+
|
|
|
NOOP: function(){},
|
|
NOOP: function(){},
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -91,13 +97,38 @@ HlsReader.prototype.process = function(segmentInfo, done) {
|
|
|
|
|
|
|
|
this.isReading = true;
|
|
this.isReading = true;
|
|
|
|
|
|
|
|
- return this.decrypt(segmentInfo.stream, segmentInfo.details.keys, (err, stream) => {
|
|
|
|
|
|
|
+ Async.parallel({
|
|
|
|
|
+ map: (next) => {
|
|
|
|
|
+
|
|
|
|
|
+ if (!deepEqual(segmentInfo.details.map, this.map)) {
|
|
|
|
|
+ this.map = segmentInfo.details.map;
|
|
|
|
|
+ if (this.map) {
|
|
|
|
|
+ return this.appendMap(this.map, next);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return next();
|
|
|
|
|
+ },
|
|
|
|
|
+ stream: (next) => {
|
|
|
|
|
+
|
|
|
|
|
+ return this.decrypt(segmentInfo.stream, segmentInfo.details.keys, (err, stream) => {
|
|
|
|
|
+
|
|
|
|
|
+ if (err) {
|
|
|
|
|
+ console.error('decrypt failed', err.stack);
|
|
|
|
|
+ stream = segmentInfo.stream;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return next(null, stream);
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ }, (err, results) => {
|
|
|
|
|
|
|
|
if (err) {
|
|
if (err) {
|
|
|
- console.error('decrypt failed', err.stack);
|
|
|
|
|
- stream = segmentInfo.stream;
|
|
|
|
|
|
|
+ return done(err);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ let stream = results.stream;
|
|
|
|
|
+
|
|
|
this.emit('segment', segmentInfo);
|
|
this.emit('segment', segmentInfo);
|
|
|
|
|
|
|
|
stream = Oncemore(stream);
|
|
stream = Oncemore(stream);
|
|
@@ -135,7 +166,7 @@ HlsReader.prototype.hook = function hook() {
|
|
|
|
|
|
|
|
let s = this.buffer;
|
|
let s = this.buffer;
|
|
|
if (this.sync) {
|
|
if (this.sync) {
|
|
|
- let smooth = tssmooth();
|
|
|
|
|
|
|
+ let smooth = TsSmooth();
|
|
|
smooth.on('unpipe', () => {
|
|
smooth.on('unpipe', () => {
|
|
|
|
|
|
|
|
this.unpipe();
|
|
this.unpipe();
|
|
@@ -158,7 +189,33 @@ HlsReader.prototype.hook = function hook() {
|
|
|
this.emit('ready');
|
|
this.emit('ready');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-HlsReader.prototype.decrypt = function (stream, keyAttrs, next) {
|
|
|
|
|
|
|
+HlsReader.prototype.appendMap = function(map, next) {
|
|
|
|
|
+
|
|
|
|
|
+ if (!map.uri) {
|
|
|
|
|
+ return next(new Error('missing "uri" attribute from map'));
|
|
|
|
|
+ }
|
|
|
|
|
+ let mapUri = Url.resolve(this.reader.baseUrl, map.quotedString('uri'));
|
|
|
|
|
+
|
|
|
|
|
+ let fetchOptions = {
|
|
|
|
|
+ timeout: internals.mapFetchTimeout,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (map.byterange) {
|
|
|
|
|
+ let n = map.quotedString('byterange').split('@');
|
|
|
|
|
+ if (n.length !== 2) {
|
|
|
|
|
+ return next(new Error('invalid "byterange" attribute from map'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fetchOptions.start = parseInt(n[1], 10);
|
|
|
|
|
+ fetchOptions.end = fetchOptions.start + parseInt(n[0], 10) - 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Oncemore(UriStream(mapUri, fetchOptions))
|
|
|
|
|
+ .once('end', 'error', next)
|
|
|
|
|
+ .pipe(this.buffer, { end: false })
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+HlsReader.prototype.decrypt = function(stream, keyAttrs, next) {
|
|
|
|
|
|
|
|
return SegmentDecrypt.decrypt(stream, keyAttrs, { base: this.reader.baseUrl, key: this.key, cookie: this.cookie }, next);
|
|
return SegmentDecrypt.decrypt(stream, keyAttrs, { base: this.reader.baseUrl, key: this.key, cookie: this.cookie }, next);
|
|
|
};
|
|
};
|