Skip to content

Instantly share code, notes, and snippets.

@vnoder
Forked from deandob/gist:8943268
Created December 18, 2015 13:07
Show Gist options
  • Save vnoder/97b1f0c812e2d1ddd25b to your computer and use it in GitHub Desktop.
Save vnoder/97b1f0c812e2d1ddd25b to your computer and use it in GitHub Desktop.
Streaming MP4 to HTML5 clients
--------------- FFMPEG command line
ffmpeg = child_process.spawn("ffmpeg", [
"-i", rtsp , "-vcodec", "copy", "-f", "mp4", "-f", "segment", "-segment_time", recSeg, "-segment_wrap", 2, "-map", "0", "-segment_format", "mp4", "-reset_timestamps", "1", "-y", "plugins/security/videos/" + camName + "/rec-%01d.mp4"
], {detached: false});
---------------- Node.JS streamer
// Stream mp4 video file based on URL request from client player. Accept request for partial streams
// Code attribution: https://github.com/meloncholy/vid-streamer/blob/master/index.js (MIT license)
var recStream = function (req, resp) {
var stream;
var stat;
//var rootFolder = "videos/";
var rootFolder = "";
var info = {};
var range = typeof req.headers.range === "string" ? req.headers.range : undefined;
var reqUrl = url.parse(req.url, true);
info.path = typeof reqUrl.pathname === "string" ? reqUrl.pathname.substring(1) : undefined;
if (info.path) {
try {
info.path = decodeURIComponent(info.path);
} catch (exception) {
console.log("Video Streamer bad request received - " + resp); // Can throw URI malformed exception.
return false;
}
}
info.file = info.path.match(/(.*[\/|\\])?(.+?)$/)[2];
info.path = rootFolder + info.path;
try {
stat = fs.statSync(info.path);
if (!stat.isFile()) {
console.log("Video Streamer bad file specified - " + resp);
return false;
}
} catch (e) {
console.log("Video Streamer bad file specified - " + resp + " " + e);
return false;
}
info.start = 0;
info.end = stat.size - 1;
info.size = stat.size;
info.modified = stat.mtime;
info.rangeRequest = false;
info.maxAge = "3600"
info.server = info.file
info.mime = "video/mp4"
if (range !== undefined && (range = range.match(/bytes=(.+)-(.+)?/)) !== null) {
// Check range contains numbers and they fit in the file. Make sure info.start & info.end are numbers (not strings) or stream.pipe errors out if start > 0.
info.start = isNumber(range[1]) && range[1] >= 0 && range[1] < info.end ? range[1] - 0 : info.start;
info.end = isNumber(range[2]) && range[2] > info.start && range[2] <= info.end ? range[2] - 0 : info.end;
info.rangeRequest = true;
} else if (reqUrl.query.start || reqUrl.query.end) {
// This is a range request, but doesn't get range headers.
info.start = isNumber(reqUrl.query.start) && reqUrl.query.start >= 0 && reqUrl.query.start < info.end ? reqUrl.query.start - 0 : info.start;
info.end = isNumber(reqUrl.query.end) && reqUrl.query.end > info.start && reqUrl.query.end <= info.end ? reqUrl.query.end - 0 : info.end;
}
info.length = info.end - info.start + 1;
var code = 200;
var header = {
"Cache-Control": "public; max-age=" + info.maxAge,
Connection: "keep-alive",
"Content-Type": info.mime,
"Content-Disposition": "inline; filename=" + info.file + ";"
};
if (info.rangeRequest) { // Partial http response
code = 206;
header.Status = "206 Partial Content";
header["Accept-Ranges"] = "bytes";
header["Content-Range"] = "bytes " + info.start + "-" + info.end + "/" + info.size;
}
header.Pragma = "public";
header["Last-Modified"] = info.modified.toUTCString();
header["Content-Transfer-Encoding"] = "binary";
header["Content-Length"] = info.length;
header.Server = info.server;
resp.writeHead(code, header);
stream = fs.createReadStream(info.path, { flags: "r", start: info.start, end: info.end });
stream.pipe(resp);
return true;
};
var isNumber = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n); // http://stackoverflow.com/a/1830844/648802
};
--------------------------------------------
HTML5 client code
---------- HTML
<div id="group" style="position: absolute; left:0px; top:0px; height: 100px; width: 100px">
<video id="widget" width="100" height="100" controls>
<source id="vidSource" type="video/mp4">
</video>
</div>
---------- Javascript
liveStreamPort = 8088
recStreamPort = 3000
recPath = "plugins/security/videos/"
serverName = document.domain
camCanvas = document.getElementById("group")
vidSource = document.getElementById("vidSource")
widgetID.controls = true;
streamPort = recStreamPort
resourceName = cameraName
vidSource.src = "http://" + serverName + ":" + streamPort + "/" + resourceName // live stream
widgetID.load()
widgetID.play();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment