Created
February 11, 2014 20:19
-
-
Save deandob/8943268 to your computer and use it in GitHub Desktop.
Streaming MP4 to HTML5 clients
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--------------- 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
I tried your code , but got a "rtsp is not defined" error, do you know how to resolve that? thanks