将音频从 nodejs 流式传输到 html5 音频标签

stream audio from nodejs to html5 audio tag

我在尝试将音频从 nodejs 服务器实时流式传输到多个 html 客户端 (也可能在稍后连接) 时遇到多个问题。我在下面的代码中所做的是加载 2 个 mp3 文件,在 bufferArray 中,然后当客户端连接时,我继续从 bufferArray 中出队,然后在 16384 bytes/sec ~ 128kbits/sec 处节流并将其写入客户端.但我觉得 缓冲区很快就会清空。我的做法是否正确。基本上我应该确保所有客户都应该播放歌曲的同一部分。首先,我的 none 个客户能够播放这些歌曲,这是另一个问题。

如有任何帮助,我们将不胜感激。

var http = require('http');
var fs = require("fs");
var url = require('url');
var stream = require('stream');
var Throttle = require('throttle');
var bufferArray = [];
var clients = [];
var entry = true;
setInterval(function () {
  if(bufferArray.length > 0 && clients.length > 0){
    entry = false;
    var buffer = bufferArray.shift();
    var bufferStream = new stream.PassThrough();
    var throttledStream = new stream.PassThrough();
    var throttle = new Throttle(16384);
    bufferStream.end(new Buffer(buffer));
    bufferStream.pipe(throttle).pipe(throttledStream);
    throttledStream.on('data',function(data){
      console.log("Going to write to all the clients "+clients.length);
      for(var i = 0; i < clients.length; i++){
          clients[i].write(data);
      }
    });
    throttledStream.on('end',function(){
      console.log("finished the buffer. Still have to write tot "+bufferArray.length+" buffers");
      entry = true;
    });
  }
},1);
function readMp3FilesInBuffer(songName,callback){
    var fd = fs.createReadStream("./songs/"+songName);
    fd.on('data',function(chunk){
      bufferArray.push(chunk);
    });
    fd.on('end',callback);
}

readMp3FilesInBuffer("FeelOfLove.mp3",function(){
  readMp3FilesInBuffer("ILoveAfrica.mp3",function () {
    console.log("successfully read the songs..");
    http.createServer(function (request, response) {
      var parsedUrl = url.parse(request.url,true);
      var requestType = parsedUrl.query.requestType;
      if(requestType == "renderHtml"){
        console.log("got a request to render html");
        response.writeHead(200, {
           'Content-Type': 'text/html'
       });
        var htmlStream = fs.createReadStream("./views/audioStreaming.html");
        htmlStream.pipe(response);
      }
      else if (requestType === "stream") {
        response.writeHead(200,{
            "Content-Type": "audio/mpeg",
            'Transfer-Encoding': 'chunked'
        });
        clients.push(response);
        console.log("got a request to stream. Tot clients available : "+clients.length);
      }
      else{
        response.end();
      }
    }).listen(8081,function () {
      console.log("listening");
    });

  });
});

这里有很多代码,也有很多缺失的信息...但也有很多值得指出的地方可能是您的特定问题的根源。

您提到客户端无法播放。他们最终是否以正确的方式使用您的流媒体?他们是否获得了正确的 headers 和数据?如果不是,请先修复它。

实际上只有大约三分之一的流媒体音频播放器能够正确处理分块编码。如果你是 in-browser(实际上是在网页上),你通常会没事,但除此之外,分块编码是 non-starter。您需要强制 Node.js 完全跳过分块编码。

您没有说明您使用的是哪个油门模块。 TooTallNate 是偶然的吗?如果是这样,我过去在使用该模块时遇到过麻烦。它最近没有更新,可能不适合现代 Node.js "streams3".

无论如何,你的节流方式是不合适的。虽然您的平均比特率可能是 128k,但不一定正好是 128k。你不应该假设比特率。您可以编写自己的代码来读取 MP3 帧 headers 并相应地进行同步,但是为什么在其他地方已经完成了呢?我会将 FFmpeg 作为一个 child 进程启动,并使其实时运行 运行。未经测试,但像这样:

ffmpeg -re -i yourfile.mp3 -map_metadata -1 -acodec copy -f mp3 -

除了基于实际媒体进行节流之外,这样做的好处是可以删除所有随机 ID3 标签和 iTunes 等软件喜欢嵌入 MP3 中的其他随机废话,为您提供干净的流媒体,几乎没有开销。

您将遇到的下一个问题是您的客户端将花费很长时间来缓冲和开始播放。有一个相当大的缓冲区准备好在它们连接后立即刷新给它们解决了这个问题。

许多浏览器(如 Chrome)会发出范围请求,试图像对待任何其他 MP3 文件一样对待您的流。如果您只是像您正在做的那样忽略那些范围请求,那么一切都会正常工作。不过,您可能会考虑根据您正在做的事情的具体情况来处理它们。

在一天结束时,您可以使它正常工作,但其中涉及很多内容,并且需要进行很多小的调整才能使流为您的所有听众服务。我是否建议将流服务留给已经这样做的东西,比如 Icecast?您仍然可以在 Node.js 中获取流的来源,从而允许您在流的来源周围绑定任何您想要的应用程序逻辑。有点自我推销......我有一些 Node.js 代码我可以授权给你连接到 Icecast。我还有一个 CDN,您可以直接从您的 Node.js 应用流式传输。您可以通过 https://audiopump.co 或 e-mail 我的 brad@audiopump.co 查看。即使您决定全部自己做,也请考虑从您的 Node.js 应用程序获取流并从其他东西提供服务。