转码和流式传输音频 - 如何发送内容范围标头

时间:2013-01-13 14:55:29

标签: node.js audio stream streaming audio-streaming

快速版:如何在不知道体长时发送正确的Content-Range标题?

我有一个FLAC文件。我想将其转码为MP3并立即将其流式传输给用户。 到目前为止我有这样的事情:

function transcode(file) {
  var spawn = require('child_process').spawn

  var decode = spawn('flac', [
    '--decode',
    '--stdout',
    file
  ])

  var encode = spawn('lame', [
    '-V0',
    '-',
    '-'
  ])

  decode.stdout.pipe(encode.stdin)

  return encode
}

var express = require('express')
var app = express()

app.get('/somefile.mp3', function (req, res) {
  res.setHeader('Accept-Ranges', 'bytes')
  res.setHeader('Content-Range', 'bytes')
  res.setHeader('Content-Type', 'audio/mpeg')
  transcode(file).stdout.pipe(res)
})

这可以按预期工作,但它是“流式传输”,所以我不能跳过。 显然我需要做Content-Range个东西。 使用:https://github.com/visionmedia/node-range-parser

function sliceStream(start, writeStream, readStream) {
  var length = 0
  var passed = false

  readStream.on('data', function (buf) {
    if (passed) return writeStream.write(buf);

    length += buf.length

    if (length < start) return;

    passed = true
    writeStream.write(buf.slice(length - start))
  })

  readStream.on('end', function () {
    writeStream.end()
  })
}

var parseRange = require('range-parser')

app.get('/somefile.mp3', function (req, res) {
  var ranges = parseRange(Infinity, req.headers['range'])

  if (ranges === -1 || ranges === -2) return res.send(400);

  var start = ranges[0].start

  res.setHeader('Accept-Ranges', 'bytes')
  res.setHeader('Content-Type', 'audio/mpeg')

  if (!start) {
    res.setHeader('Content-Range', 'bytes')
    transcode(file).stdout.pipe(res)
    return
  }

  res.setHeader('Content-Range', 'bytes ' + start + '-')
  sliceStream(start, transcode(file).stdout, res)
})

这就是我被困住的地方。 因为我不等到整首歌被编码, 我不知道这首歌的大小。 由于我刚刚在Chrome中“取消”, 我假设Content-Range标题在没有大小的情况下格式错误。

另外,我目前只是在浏览器中打开这首歌,所以我假设它使用<audio>元素。

建议?

1 个答案:

答案 0 :(得分:6)

是的,您的Content-Range标题在没有大小的情况下格式错误。但是,您可以尝试发送服务器已经转码的当前大小。虽然我怀疑Chrome会优雅地处理不断变化的大小...

There are a number of things you're not handling

  1. 您似乎没有发送206 Partial Content状态(可能这是由图书馆处理的,不确定)。
  2. 看起来你甚至没有检查范围请求的结尾部分。 Chrome通常不会发送除0-之外的任何内容,但其他浏览器会发送。实际上,有些人可能会在一个请求中发送多个范围(在支持中可能会产生巨大痛苦)。
  3. 您没有发送正确的Content-Range响应标头,因为您也未能包含您要发送的内容的结束索引。看起来应该是这样的:
    Content-Range: bytes 0-2048/3980841
  4. 最后,如果客户端发出超出范围的范围请求 - 也就是说,没有任何范围值与资源范围重叠 - 服务应该以416 Requested Range Not Satisfiable状态响应。
  5. 编辑:我没有测试过这种特殊情况,但是如果你是从FLAC转码到192kbps的CBR MP3,我想如果你只有一组限制可能会发生发送略微不准确的内容长度(关闭少于1000位):

    • 音频的最后会被播放器最后剪掉。 ~1000位将剪切大约 5ms 的音频(对人类来说不明显)。
    • 浏览器会忽略结束索引或内容长度,只是继续接受和/或请求您最初回复的Content-Range之外的范围,直到您关闭连接或发送416状态为止。
    • 音频的丢失/错误结束可能会导致<audio>抛出您只需要优雅处理的MEDIA_ERR_NETWORKMEDIA_ERR_DECODE错误。 (在这种情况下,音频仍会被剪裁。)