Golang实现音频录制与流式传输

Golang实现音频录制与流式传输 大家好,我在自己的项目上遇到了一个非常棘手的问题。 我正在寻找一种方法,能够从麦克风录制音频,并在录制的同时将其分块发送,以便接收方能够持续听到传输的音频。

我很难确定应该使用哪种音频格式(编解码器),以及Go语言中有哪些好的开源包可以提供帮助……

我考虑过使用PortAudio的Go语言绑定,我认为它可以将音频录制为二进制原始数据。 但是,然后我该如何逐块播放,使得收听是连续的呢? 有人能给我指点一下方向吗?

3 回复

谢谢! 我最终使用了来自“layeh.com/gopus”的gopus,将PCM数据(使用Go plug for portAudio录制)编码为Opus格式。

更多关于Golang实现音频录制与流式传输的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


关于 pcm 怎么样?这就足够了。你也可以将其保存到磁盘。

关于 continuous 的一点小建议:

PhoneA <—(websocket)-> TalkServer-<—(websocket)–>PhoneB

我认为将音频数据发布到 mq 并用另一个程序处理会更好。

也许这个代码库可以帮到你 https://github.com/hraban/opus.git

在Go中实现音频录制与流式传输,推荐使用以下方案:

核心方案:PortAudio + Opus编解码 + WebSocket传输

1. 依赖包

import (
    "github.com/gordonklaus/portaudio"
    "github.com/pion/opus"
    "github.com/gorilla/websocket"
    "encoding/binary"
)

2. 音频录制与编码示例

// 音频配置
const (
    sampleRate   = 48000
    channels     = 1
    frameSize    = 960 // 20ms帧
    bufferFrames = 4800
)

type AudioStreamer struct {
    stream *portaudio.Stream
    encoder *opus.Encoder
    conn    *websocket.Conn
}

func NewAudioStreamer(wsConn *websocket.Conn) (*AudioStreamer, error) {
    // 初始化PortAudio
    portaudio.Initialize()
    
    // 创建Opus编码器
    encoder, err := opus.NewEncoder(sampleRate, channels, opus.AppVoIP)
    if err != nil {
        return nil, err
    }
    
    // 设置编码器比特率
    encoder.SetBitrate(64000)
    
    return &AudioStreamer{
        encoder: encoder,
        conn:    wsConn,
    }, nil
}

func (as *AudioStreamer) StartRecording() error {
    // 打开音频输入流
    stream, err := portaudio.OpenDefaultStream(1, 0, sampleRate, frameSize, as.audioCallback)
    if err != nil {
        return err
    }
    
    as.stream = stream
    return stream.Start()
}

func (as *AudioStreamer) audioCallback(in []float32) {
    // 将float32转换为int16(PCM格式)
    pcmData := make([]int16, len(in))
    for i, v := range in {
        pcmData[i] = int16(v * 32767)
    }
    
    // Opus编码
    encoded := make([]byte, 4000) // Opus最大帧大小
    n, err := as.encoder.Encode(pcmData, encoded)
    if err != nil {
        return
    }
    
    // 通过WebSocket发送编码后的数据
    if as.conn != nil {
        as.conn.WriteMessage(websocket.BinaryMessage, encoded[:n])
    }
}

func (as *AudioStreamer) Stop() {
    if as.stream != nil {
        as.stream.Stop()
        as.stream.Close()
    }
    portaudio.Terminate()
}

3. WebSocket服务器端

func StartAudioStreamServer() {
    upgrader := websocket.Upgrader{
        CheckOrigin: func(r *http.Request) bool { return true },
    }
    
    http.HandleFunc("/stream", func(w http.ResponseWriter, r *http.Request) {
        conn, err := upgrader.Upgrade(w, r, nil)
        if err != nil {
            return
        }
        defer conn.Close()
        
        // 创建音频流
        streamer, err := NewAudioStreamer(conn)
        if err != nil {
            return
        }
        defer streamer.Stop()
        
        // 开始录制和流式传输
        streamer.StartRecording()
        
        // 保持连接
        for {
            _, _, err := conn.ReadMessage()
            if err != nil {
                break
            }
        }
    })
    
    http.ListenAndServe(":8080", nil)
}

4. 接收端解码播放示例

type AudioPlayer struct {
    stream *portaudio.Stream
    decoder *opus.Decoder
}

func NewAudioPlayer() (*AudioPlayer, error) {
    portaudio.Initialize()
    
    decoder, err := opus.NewDecoder(sampleRate, channels)
    if err != nil {
        return nil, err
    }
    
    player := &AudioPlayer{decoder: decoder}
    
    // 打开音频输出流
    stream, err := portaudio.OpenDefaultStream(0, 1, sampleRate, frameSize, player.playCallback)
    if err != nil {
        return nil, err
    }
    
    player.stream = stream
    return player, nil
}

func (ap *AudioPlayer) PlayAudioData(encoded []byte) {
    // Opus解码
    pcmData := make([]int16, frameSize)
    n, err := ap.decoder.Decode(encoded, pcmData)
    if err != nil {
        return
    }
    
    // 转换为float32并播放
    floatData := make([]float32, n)
    for i, v := range pcmData[:n] {
        floatData[i] = float32(v) / 32767.0
    }
    
    // 这里需要将数据放入播放缓冲区
    // 实际实现中需要使用线程安全的缓冲区队列
}

func (ap *AudioPlayer) playCallback(out []float32) {
    // 从缓冲区获取数据填充out
    // 实现播放逻辑
}

func (ap *AudioPlayer) Start() error {
    return ap.stream.Start()
}

5. 客户端接收示例

func ReceiveAudioStream(url string) {
    conn, _, err := websocket.DefaultDialer.Dial(url, nil)
    if err != nil {
        return
    }
    defer conn.Close()
    
    player, err := NewAudioPlayer()
    if err != nil {
        return
    }
    defer player.Stop()
    
    player.Start()
    
    for {
        _, message, err := conn.ReadMessage()
        if err != nil {
            break
        }
        
        // 解码并播放
        player.PlayAudioData(message)
    }
}

关键点说明

  1. 音频格式选择:使用Opus编解码器,专为实时音频传输设计,延迟低、压缩率高
  2. 分块传输:每20ms(960个样本)作为一个数据包进行编码和传输
  3. 实时性保障:WebSocket提供全双工通信,适合实时音频流
  4. 缓冲区管理:需要实现环形缓冲区来处理音频数据的生产和消费

这个方案可以实现低延迟的音频录制和流式传输,适合语音聊天、实时广播等场景。

回到顶部