sp-streams
Version:
Streamplace Streams for Piping Video Around and Stuff
121 lines (94 loc) • 4.24 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DEFAULT_WINDOW_SIZE = exports.DEFAULT_SEG_DURATION = exports.MANIFEST_NAME = undefined;
exports.default = dashStream;
var _ffmpeg = require("./ffmpeg");
var _ffmpeg2 = _interopRequireDefault(_ffmpeg);
var _stream = require("stream");
var _socketEgressStream = require("./socket-egress-stream");
var _socketEgressStream2 = _interopRequireDefault(_socketEgressStream);
var _express = require("express");
var _express2 = _interopRequireDefault(_express);
var _debug = require("debug");
var _debug2 = _interopRequireDefault(_debug);
var _fs = require("fs");
var _fs2 = _interopRequireDefault(_fs);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var MANIFEST_NAME = exports.MANIFEST_NAME = "manifest.mpd";
var DEFAULT_SEG_DURATION = exports.DEFAULT_SEG_DURATION = 5000;
var DEFAULT_WINDOW_SIZE = exports.DEFAULT_WINDOW_SIZE = 3;
var log = (0, _debug2.default)("sp:dash-stream");
function dashStream() {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var socketEgress = (0, _socketEgressStream2.default)({ useFirstBuffer: false });
var passThrough = new _stream.PassThrough();
passThrough.segDuration = opts.segDuration || DEFAULT_SEG_DURATION;
passThrough.windowSize = opts.windowSize || DEFAULT_WINDOW_SIZE;
var app = (0, _express2.default)();
var ffmpeg = void 0;
app.post("*", function (req, res) {
// weird bug where the HLS manifest from ffmpeg has two leading slashes
var filename = req.url.replace(/^\/+/, "");
log("got " + filename);
// No matter what, tell ffmpeg that it's chill when we're done
req.on("end", function () {
res.sendStatus(200);
});
if (filename === MANIFEST_NAME) {
// If it's the manifest, assemble the chunks and emit when done
var manifest = "";
req.on("data", function (chunk) {
manifest += chunk.toString();
});
req.on("end", function () {
// hack... this is usually accurate but I can't get ffmpeg to output it
passThrough.emit("manifest", manifest.replace("mp4a.40", "mp4a.40.2"));
});
} else {
// If it's data, pass the stream right on through
passThrough.emit("chunk", filename, req);
}
});
passThrough.on("end", function () {
listener.close(function () {
log("DASH HTTP ingress closed on port " + socketEgress.httpPort);
});
});
var listener = void 0;
Promise.resolve().then(function (resolve, reject) {
listener = app.listen(0, resolve);
}).then(function () {
socketEgress.httpPort = listener.address().port;
log("DASH HTTP ingress listening on port " + socketEgress.httpPort);
ffmpeg = (0, _ffmpeg2.default)().input("unix://" + socketEgress.path).inputFormat("mpegts").videoCodec("copy").audioCodec("copy").outputOptions([
// This section from default options at https://ffmpeg.org/ffmpeg-all.html#dash-2
"-bf 1", "-keyint_min 120", "-g 120", "-sc_threshold 0", "-b_strategy 0", "-ar:a:1 22050", "-use_timeline 1", "-use_template 1",
// hls too!
"-hls_playlist 1",
// Avoids Tag [15][0][0][0] incompatible with output codec id '86018' (mp4a)
"-tag:v avc1", "-tag:a mp4a", "-window_size " + passThrough.windowSize, "-min_seg_duration " + passThrough.segDuration * 1000 // ms ==> microseconds
]).output("http://127.0.0.1:" + socketEgress.httpPort + "/" + MANIFEST_NAME).outputFormat("dash");
return socketEgress.getPath();
}).then(function () {
passThrough.pipe(socketEgress);
// hack hack hack, but for now we need a newer version
var FFMPEG_UNSTABLE_PATH = "/usr/bin/ffmpeg-unstable";
if (!_fs2.default.existsSync(FFMPEG_UNSTABLE_PATH)) {
throw new Error("ffmpeg-unstable not found");
} else {
log("using " + FFMPEG_UNSTABLE_PATH);
// ugh, this is a global setting for some reason
ffmpeg._getFfmpegPath = function (cb) {
return cb(null, FFMPEG_UNSTABLE_PATH);
};
ffmpeg.run();
}
}).catch(function (err) {
log(err);
throw err;
});
passThrough.getPath = socketEgress.getPath.bind(socketEgress);
return passThrough;
}