Przeglądaj źródła

Support map option by prepending to dumped segment data

Gil Pedersen 9 lat temu
rodzic
commit
c263c693d3
2 zmienionych plików z 64 dodań i 6 usunięć
  1. 63 6
      lib/hls-reader.js
  2. 1 0
      package.json

+ 63 - 6
lib/hls-reader.js

@@ -1,17 +1,23 @@
 'use strict';
 
 const Util = require('util');
+const Url = require('url');
 const Readable = require('readable-stream/readable');
 const Passthrough = require('readable-stream/passthrough');
 
+const Async = require('async');
 const StreamEach = require('stream-each');
 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 internals = {
+  mapFetchTimeout: 30 * 1000,
+
   NOOP: function(){},
 };
 
@@ -91,13 +97,38 @@ HlsReader.prototype.process = function(segmentInfo, done)  {
 
   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) {
-      console.error('decrypt failed', err.stack);
-      stream = segmentInfo.stream;
+      return done(err);
     }
 
+    let stream = results.stream;
+
     this.emit('segment', segmentInfo);
 
     stream = Oncemore(stream);
@@ -135,7 +166,7 @@ HlsReader.prototype.hook = function hook() {
 
   let s = this.buffer;
   if (this.sync) {
-    let smooth = tssmooth();
+    let smooth = TsSmooth();
     smooth.on('unpipe', () => {
 
       this.unpipe();
@@ -158,7 +189,33 @@ HlsReader.prototype.hook = function hook() {
   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);
 };

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
   "author": "Gil Pedersen <gpdev@gpost.dk>",
   "license": "BSD-2-Clause",
   "dependencies": {
+    "async": "^2.0.0-rc.6",
     "commander": "^2.3.0",
     "debug": "^2.0.0",
     "deep-equal": "^1.0.0",