HTML5的流媒体容器<audio>标签

huangapple go评论116阅读模式
英文:

Streaming container for HTML5 <audio> tag

问题

我有一个Go应用程序,我想要将实时的未压缩音频流传输到浏览器。我希望通过HTTP流传输,只需让浏览器打开与流对应的URL,并通过该连接提供音频数据。

我打算使用WAV来发送未压缩的数据,但是WAV文件格式要求在头部预定义文件大小。在Go中,有没有更好的容器格式可以轻松处理这种流传输?

我知道一种方法是使用适当的流媒体服务器,并通过该服务器转码我的音频,但是如果我自己实现,是否有一种相对简单的方法来开始?也许有一个Go库可以使这更容易?

谢谢

编辑 我已经通过ffmpeg解决了这个问题,如下所述。

英文:

I have a Go application in which I would like to stream live uncompressed audio to the browser. I was hoping to stream over HTTP by just having the browser open a URL corresponding to the stream, and then feeding that connection with the audio data.

I was planning on using WAV to send the data uncompressed, However the WAV file format requires the file size be predefined in the header. Is there a better container format for doing this streaming that I can work with easily in Go?

I know that one approach would be to use a proper streaming server and transcode my audio through that, but if I'm implementing this myself, is there a fairly straightforward way to get off the ground? Perhaps a Go library to make this easier?

Thanks

EDIT I've solved this via ffmpeg as outlined in my answer below

答案1

得分: 2

如果你仍然考虑无损WAV文件以获得最佳音质,那么我认为你有两个选择:

  1. 在头部中撒谎 - 声称WAV文件非常长(比如2GB)
  2. 在RIFF容器中添加额外的块

第一个选择很容易实现,但会导致天真的浏览器尝试下载一个2GB的文件而不是流式传输。我希望任何实现HTML5的东西都不会那么天真,但你可能需要尝试一下。

第二个选择,添加额外的块,似乎得到了RIFF规范的支持,其中WAV文件是其中的一个子集。然后你可以在WAV文件中写入多个较短的声音数据块。这应该会被WAV播放器支持,但可能不太符合WAV文件的标准化要求,因为WAV文件从来没有特别好的标准化,很多播放器只会寻找一个44字节的头部,这并不是非常符合RIFF规范!

如果我是你,我会先尝试第一个选项,看看效果如何。

英文:

If you are still thinking about uncompressed WAVs for ultimate quality then I think you've got two choices

  1. Lie in the header - say that the WAV is very long (2GB say)
  2. Add additional chunks into the RIFF container

The first choice is easy to implement, but will mean naive browsers try to download a 2GB file instead of streaming it. I would hope that anything implementing HTML5 wouldn't be that naive, but I think you'd need to try it.

The second choice, adding additional chunks seems to be supported by the RIFF spec of which WAV files are a subset. You could then write a WAV file with multiple shorter chunks of sound data within. This should be supported by WAV players, but may not be as WAV files have never been particularly well standardised, and a lot of players just look for a 44 byte header which is not very RIFF compliant!

If I was you I'd try the first option first and see how that works out.

答案2

得分: 0

好的,这是我最终做的事情,我的Go代码通过一个ffmpeg进程将原始PCM数据转换为mp3。我假设packets是一个通道,我的音频数据会出现在上面,而response是一个http.ResponseWriter。

cmd := exec.Command("ffmpeg", "-v", "verbose", "-f", "u16le", "-ar", "44100", "-ac", "1", "-i", "-", "-f", "mp3", "-")
audioIn, err := cmd.StdinPipe()
if err != nil {
    log.Println("无法创建标准输入管道")
}
audioOut, err := cmd.StdoutPipe()
if err != nil {
    log.Println("无法创建标准输出管道")
}
err = cmd.Start()
if err != nil {
    log.Println("无法启动ffmpeg命令,错误:", err)
}
go func() {
    for {
        packet := <-packets
        audioIn.Write(packet.Payload)
    }
}()
go func() {
    amount, err := io.Copy(response, audioOut)
    if err != nil {
        log.Println("io复制终止时出现错误", err)
    }
    log.Printf("完成复制音频数据:%d字节\n", amount)
}()
err = cmd.Wait()
if err != nil {
    log.Println("ffmpeg命令异常终止", err)
}
英文:

Ok, here's what I ended up doing, my Go code pipes the raw PCM data through an ffmpeg process which converts it to mp3. I assume packets is a channel that my audio data is appearing over, and response is an http.ResponseWriter

cmd := exec.Command(&quot;ffmpeg&quot;, &quot;-v&quot;, &quot;verbose&quot;, &quot;-f&quot;, &quot;u16le&quot;, &quot;-ar&quot;, &quot;44100&quot;, &quot;-ac&quot;, &quot;1&quot;, &quot;-i&quot;, &quot;-&quot;, &quot;-f&quot;, &quot;mp3&quot;, &quot;-&quot;)
audioIn, err := cmd.StdinPipe()
if err != nil {
    log.Println(&quot;Failed to create stdin pipe&quot;)
}
audioOut, err := cmd.StdoutPipe()
if err != nil {
    log.Println(&quot;Failed to create stdout pipe&quot;)
}
err = cmd.Start()
if err != nil {
    log.Println(&quot;Failed to start ffmpeg command, error: &quot;, err)
}
go func() {
    for {
        packet := &lt;-packets
        audioIn.Write(packet.Payload)
    }
}
go func() {
    amount, err := io.Copy(response, audioOut)
    if err != nil {
        log.Println(&quot;io copy terminated with an error&quot;, err)
    }
    log.Printf(&quot;Done copying audio data: %d bytes\n&quot;, amount)
}()
err = cmd.Wait()
if err != nil {
    log.Println(&quot;ffmpeg command terminated incorrectly&quot;, err)
}

huangapple
  • 本文由 发表于 2013年3月6日 02:03:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/15230929.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定