nodejs视频流讨论
发布于 6个月前 作者 node-node 1211 次浏览 最后一次编辑是 5个月前

首先我先建一个简单的视频流server, 浏览器请求就可以直接播放啦,但是。。。

var http = require('http')
var fs = require('fs')

http.createServer(function(request, response){
    var mp4 = 'SX28_hd.mp4';
    var stat = fs.statSync(mp4);

    response.writeHead(200, {
        'Content-Type': 'video/mp4',
        'Content-Length': stat.size
    })

    var readableStream = fs.createReadStream(mp4);
    readableStream.pipe(response);
}).listen(3000)

------------------------------我是美丽的分割线-------------------------------------------------------

我再建一个接受视频流的客户端, 代码如下,
var http = require('http')
var fs = require('fs')

var options = {
    host:'localhost',
    port:'3000',
    path:'/',
    method:'GET'
}

var writeStream = fs.createWriteStream('file.mp4')

var passedLength = 0;
var lastSize = 0;
var startTime = Date.now();
var totalSize = 0;

var req = http.request(options, function(res){
    console.log('STATUS' + res.statusCode)

    totalSize = res.headers['content-length'];
    console.log(totalSize);

   // console.log('HEADERS' + JSON.stringify(res.headers))
   // res.setEncoding('utf8')

    res.on('data', function(chunk){
      //  console.log('BODY' + chunk);
        passedLength += chunk.length;
        writeStream.write(chunk);
    })

    res.on('end', function(){
        writeStream.end();
        console.log('xx');
    })
})

req.end();
var out = process.stdout;

setTimeout(function show() {
    var percent = Math.ceil((passedLength / totalSize) * 100);
    var size = Math.ceil(passedLength / 1000000);
    var diff = size - lastSize;
    lastSize = size;
    out.clearLine();
    out.cursorTo(0);
    out.write('已完成' + size + 'MB, ' + percent + '%, 速度:' + diff * 2 + 'MB/s');
    if (passedLength < totalSize) {
        setTimeout(show, 50);
    } else {
        var endTime = Date.now();
        console.log();
        console.log('共用时:' + (endTime - startTime) / 1000 + '秒。');
    }
}, 50);

------------------------------我是美丽的分割线------------------------------------------------------- 但是,如果视频还没有下完, 我就把进程关闭, 发现下载到本地的一部分视频是可以播放的, 我有两个疑问

  1. 如果时时下载, 客户端(ios 和安卓)一开始下载的那一部分能够播放吗?
  2. 这样能够支持多客户端播放吗?

------------------------------我是美丽的分割线-------------------------------------------------------

这看起来只是一个静态的文件服务器, 我想要实现一个能够任意跳转点播的视频服务器, 找了很多资料,java在这块很成熟, 有很多开源产品,但nodejs该怎么弄啊? 目前所知的两种协议:rtmp, http live stream, 但这两种协议在nodejs应用呢? 欢迎大家一起讨论啊!!!!

7 回复

还有一种解决方案, 七牛支持视频切片, 跳转点播,但想自己弄个,而且七牛那api …

mp4格式的视频要边下边播支持拖动,一般要在服务端支持range——拖动选时观看(seek)是基于索引的,mp4的索引在头部,要seek选时观看,播放器会根据索引计算出offset,生成一个带range的http请求去获取数据——这是一般播放器的做法,也有实现的差的没有做这个功能。

rtmp 兼容比较好吧

@TadyCao 感谢你的指导, 已经实现带range的视频,音频播放功能,

把源码放出来,有需要的童鞋也可以参考下

/**
 * Created by nodefx on 8/29/14.
 */

var path = require('path');
var fs = require('fs')
var http = require('http')
var url = require('url')
var mime = require('./mime').types
var config = require("./config");
var utils = require("./utils")

var port = 8000;

var server = http.createServer(function(request, response){

    var pathname = url.parse(request.url).pathname;
    var realpath = path.join("assets", path.normalize(pathname.replace(/\.\./g, "")))
    console.log(realpath)
    var ext = path.extname(realpath)
    ext = ext ? ext.slice(1):"unknown"
    var contentType = mime[ext] || "text/plain"
    console.log(contentType)

    fs.exists(realpath, function(exists) {

        if (!exists) {
            response.writeHead(404, {
                'Content-Type': 'text/plain'
            })

            response.write("This request URL " + pathname + "was not found on this server");

            response.end();
        } else {
            response.setHeader("Content-Type",contentType);
            var stats = fs.statSync(realpath);
                if (request.headers["range"]) {
                    console.log(request.headers["range"])
                    var range = utils.parseRange(request.headers["range"], stats.size);
                    console.log(range)
                    if (range) {
                        response.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stats.size);
                        response.setHeader("Content-Length", (range.end - range.start + 1));
                        var stream = fs.createReadStream(realpath, {
                            "start": range.start,
                            "end": range.end
                        });

                        response.writeHead('206', "Partial Content");
                        stream.pipe(response);
                    } else {
                        response.removeHeader("Content-Length");
                        response.writeHead(416, "Request Range Not Satisfiable");
                        response.end();
                    }
                } else {
                    var stream = fs.createReadStream(realpath);
                    response.writeHead('200', "Partial Content");
                    stream.pipe(response);
                }

        }
    })
})
server.listen(port)

刚刚和ios和安卓客户端 测试下了,客户端可以直接调用本地播放器播放,意味着可以做个类似blibli的视频播放的app啦, 完整代码在 https://github.com/node-node/vedio_stream

@node-node 帮忙看看我这个问题:http://cnodejs.org/topic/5400406dcd66f2eb3705de38#54007d8dcd66f2eb37123c25 其实跟你的程序查不多,"stream.pipe(response);"超过14分钟左右会收到response.event.close,目前无解。不知道你的程序有没有遇到相同现象。

hls是要切片的吧,不过我试过切片后的m3u8文件做过类似的,好像没啥问题

回到顶部