You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将WebRTC采集的PCMA音频保存至磁盘?

如何将音频保存为PCMA格式?

问题核心

你遇到的问题是OGG容器不兼容PCMA(G.711 A律)音频流——OGG通常用于封装Opus、Vorbis这类编码,直接将PCMA数据写入OGG文件会导致格式混乱,播放器无法解析。

PCMA属于原始音频编码,有两种可靠的保存方式:直接保存为PCMA裸流文件(后缀.pcm),或者封装为WAV容器文件(后缀.wav,兼容性更强,多数播放器可直接识别)。


代码调整方案

1. 修改Track回调逻辑

替换原OGG保存逻辑,改为保存PCMA裸流或WAV文件:

peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
    codec := track.Codec()
    fmt.Println(codec.MimeType)
    if strings.EqualFold(codec.MimeType, webrtc.MimeTypePCMA) {
        fmt.Println("Got PCMA track, saving to disk as output.pcm (8 kHz, 1 channel)")
        // 方式1:保存为原始PCMA裸流
        saveRawPCMA("output.pcm", track)
        // 方式2:保存为WAV文件(推荐,无需额外转换即可播放)
        // savePCMAAsWAV("output.wav", track)
    }
})

2. 实现PCMA裸流保存函数

直接将RTP payload中的PCMA原始数据写入文件:

import "os"

func saveRawPCMA(filePath string, track *webrtc.TrackRemote) {
    file, err := os.Create(filePath)
    if err != nil {
        panic(err)
    }
    defer file.Close()

    buf := make([]byte, 1500)
    for {
        n, _, err := track.Read(buf)
        if err != nil {
            return
        }
        // 直接写入PCMA原始数据
        if _, err := file.Write(buf[:n]); err != nil {
            panic(err)
        }
    }
}

3. (可选)封装为WAV文件

给PCMA数据加上WAV头,让播放器直接识别:

import (
    "encoding/binary"
    "os"
)

func savePCMAAsWAV(filePath string, track *webrtc.TrackRemote) {
    file, err := os.Create(filePath)
    if err != nil {
        panic(err)
    }
    defer file.Close()

    // WAV文件头结构,适配PCMA编码(8kHz采样率、单声道、8位A律)
    var wavHeader = struct {
        ChunkID       [4]byte
        ChunkSize     uint32
        Format        [4]byte
        Subchunk1ID   [4]byte
        Subchunk1Size uint32
        AudioFormat   uint16 // 6代表PCMA编码
        NumChannels   uint16
        SampleRate    uint32
        ByteRate      uint32
        BlockAlign    uint16
        BitsPerSample uint16
        Subchunk2ID   [4]byte
        Subchunk2Size uint32
    }{
        ChunkID:       [4]byte{'R', 'I', 'F', 'F'},
        Format:        [4]byte{'W', 'A', 'V', 'E'},
        Subchunk1ID:   [4]byte{'f', 'm', 't', ' '},
        Subchunk1Size: 16,
        AudioFormat:   6,
        NumChannels:   1,
        SampleRate:    8000,
        ByteRate:      8000 * 1, // 采样率×声道数(PCMA每个采样占1字节)
        BlockAlign:    1,
        BitsPerSample: 8,
        Subchunk2ID:   [4]byte{'d', 'a', 't', 'a'},
    }

    // 先写入初始头(后续更新数据大小)
    if err := binary.Write(file, binary.LittleEndian, wavHeader); err != nil {
        panic(err)
    }

    buf := make([]byte, 1500)
    dataSize := uint32(0)
    for {
        n, _, err := track.Read(buf)
        if err != nil {
            break
        }
        if _, err := file.Write(buf[:n]); err != nil {
            panic(err)
        }
        dataSize += uint32(n)
    }

    // 更新头中的大小字段
    wavHeader.ChunkSize = 36 + dataSize
    wavHeader.Subchunk2Size = dataSize
    if _, err := file.Seek(0, 0); err != nil {
        panic(err)
    }
    if err := binary.Write(file, binary.LittleEndian, wavHeader); err != nil {
        panic(err)
    }
}

验证方法

  • 若保存为.pcm文件:可通过ffmpeg转换为WAV播放,命令如下:
    ffmpeg -f alaw -ar 8000 -ac 1 -i output.pcm output.wav
    
  • 若保存为.wav文件:直接用系统默认播放器打开即可。

内容的提问来源于stack exchange,提问作者user22950238

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 14:55:25