Transcoding and streaming audio - how to send content-range headers

南笙酒味 提交于 2019-12-05 01:39:11

问题


Quick version: how to send correct Content-Range headers when don't know the body length?

I have a FLAC file. I want to transcode it to MP3 and stream it to the user immediately. I have something like this so far:

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)
})

This works as intended, but it's "streaming", so I can't skip around. Apparently I need to do Content-Range stuff. Using: 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)
})

This is where I'm stuck. Since I'm not waiting until the entire song is encoded, I don't know the size of the song. Since I just get a "canceled" in Chrome, I'm assuming that the Content-Range header is malformed without the size.

Also, I'm current just opening the song in my browser, so I assume it uses the <audio> element.

Suggestions?


回答1:


Yes, your Content-Range header is malformed without the size. However, you could attempt to send the current size your server had already transcoded. Although I'm doubtful that Chrome would handle the changing size gracefully…

There are a number of things you're not handling:

  1. You don't appear to be sending the 206 Partial Content status (maybe this is handled by the library, not sure).
  2. It doesn't look like you're even checking the end part of the range request. Chrome doesn't normally send anything but 0-, but other browsers will. In fact, some might send multiple ranges in a single request (royal pain in the ass to support).
  3. You're not sending a proper Content-Range response header as you are also failing to include the end index of the content you're sending. It should look like this:
    Content-Range: bytes 0-2048/3980841
  4. Finally if the client makes a range request that is out of bounds—that is, none of the range values overlap the extent of the resource—the service should respond with a 416 Requested Range Not Satisfiable status.

Edit: I haven't tested this particular case, but if you were transcoding from FLAC to a 192kbps CBR MP3, I would imagine there are only a limit set of possibilities that would occur if you were sending a slightly inaccurate content-length (off by less than a 1000 bits):

  • The very end of the audio would get clipped off at the end by the player. ~1000 bits would clip approximately 5ms of audio (not obvious to a human).
  • The browser would ignore the end index or content-length and simply keep accepting and/or requesting ranges outside of the Content-Range you responded with originally until you close the connection or send the 416 status.
  • The missing/erroneous end of the audio may cause the <audio> to throw a MEDIA_ERR_NETWORK or a MEDIA_ERR_DECODE error that you'd simply have to handle gracefully. (The audio would still be clipped in this case.)


来源:https://stackoverflow.com/questions/14304642/transcoding-and-streaming-audio-how-to-send-content-range-headers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!