Sean-Der / rtmp-to-webrtc

Demonstrate a RTMP server that publishes to WebRTC
73 stars 16 forks source link

Add audio support to convert AAC to opus #6

Open xiangxud opened 2 years ago

xiangxud commented 2 years ago

hi: I added audio support to convert AAC to opus. Welcome to test add import package "github.com/Glimesh/go-fdkaac/fdkaac" opus "gopkg.in/hraban/opus.v2" please run in bash: apt install -y pkg-config build-essential libopusfile-dev libfdk-aac-dev libavutil-dev libavcodec-dev libswscale-dev

source code:

type Handler struct { rtmp.DefaultHandler peerConnection webrtc.PeerConnection videoTrack, audioTrack webrtc.TrackLocalStaticSample audioDecoder fdkaac.AacDecoder audioEncoder opus.Encoder audioBuffer []byte audioClockRate uint32 } func (h Handler) SetOpusCtl() { h.audioEncoder.SetMaxBandwidth(opus.Bandwidth(2)) h.audioEncoder.SetComplexity(9) h.audioEncoder.SetBitrateToAuto() h.audioEncoder.SetInBandFEC(true) } func (h Handler) initAudio(clockRate uint32) error { // h.audioSequencer = rtp.NewFixedSequencer(0) // ftl client says this should be changed to a random value // h.audioPacketizer = rtp.NewPacketizer(FTL_MTU, FTL_AUDIO_PT, uint32(h.channelID), &codecs.OpusPayloader{}, h.audioSequencer, clockRate) // if h.audioEncoder != nil && h.audioDecoder != nil { // return nil // } encoder, err := opus.NewEncoder(48000, 2, opus.AppAudio) if err != nil { println(err.Error()) return err } h.audioEncoder = encoder h.SetOpusCtl() // h.audioEncoder, err = opus.NewEncoder(int(clockRate), 32000, 2) // // h.audioEncoder, err = opus.NewEncoder(int(clockRate), 2, opus.AppVoIP) // if err != nil { // return err // } h.audioDecoder = fdkaac.NewAacDecoder()

return nil

} func (h *Handler) OnAudio(timestamp uint32, payload io.Reader) error { // Convert AAC to opus var audio flvtag.AudioData if err := flvtag.DecodeAudioData(payload, &audio); err != nil { return err }

// data, err := io.ReadAll(audio.Data)
// if err != nil {
//  return err
// }
data := new(bytes.Buffer)
if _, err := io.Copy(data, audio.Data); err != nil {
    return err
}
if data.Len() <= 0 {
    log.Println("no audio datas", timestamp, payload)
    return fmt.Errorf("no audio datas")
}
// log.Println("\r\ntimestamp->", timestamp, "\r\npayload->", payload, "\r\naudio data->", data.Bytes())
// return nil
datas := data.Bytes()
// log.Println("\r\naudio data len:", len(datas), "->") // hex.EncodeToString(datas))
// if audio.
if audio.AACPacketType == flvtag.AACPacketTypeSequenceHeader {
    log.Println("Created new codec ", hex.EncodeToString(datas))
    // return h.audioTrack.WriteSample(media.Sample{
    //  Data:     datas, //data.Bytes(),
    //  Duration: 128 * time.Millisecond,
    // })
    err := h.initAudio(h.audioClockRate)
    if err != nil {
        log.Println(err, "error initializing Audio")
        return fmt.Errorf("can't initialize codec with %s", err.Error())
    }
    err = h.audioDecoder.InitRaw(datas)

    if err != nil {
        log.Println(err, "error initializing stream")
        return fmt.Errorf("can't initialize codec with %s", hex.EncodeToString(datas))
    }

    return nil
}

pcm, err := h.audioDecoder.Decode(datas)
if err != nil {
    log.Println("decode error: ", hex.EncodeToString(datas), err)
    return fmt.Errorf("decode error")
}
// log.Println("\r\npcm len ", len(pcm), " ->") //, pcm)
blockSize := 960
for h.audioBuffer = append(h.audioBuffer, pcm...); len(h.audioBuffer) >= blockSize*4; h.audioBuffer = h.audioBuffer[blockSize*4:] {
    pcm16 := make([]int16, blockSize*2)
    pcm16len := len(pcm16)
    for i := 0; i < pcm16len; i++ {
        pcm16[i] = int16(binary.LittleEndian.Uint16(h.audioBuffer[i*2:]))
    }
    bufferSize := 1024
    opusData := make([]byte, bufferSize)
    n, err := h.audioEncoder.Encode(pcm16, opusData)
    // n, err := h.audioEncoder.ReadEncode(pcm16, opusData)
    if err != nil {
        return err
    }
    // WriteSample(media.Sample{Data: audio.Data, Duration: sampleDuration1})
    opusOutput := opusData[:n]
    // log.Println(" pcm len:", pcm16len, " data->", " opusData len", n, " data->")
    if audioErr := h.audioTrack.WriteSample(media.Sample{
        Data:     opusOutput,
        Duration: 20 * time.Millisecond,
    }); audioErr != nil {
        log.Println("WriteSample err", audioErr)
    }
    // packets := h.audioPacketizer.Packetize(opusOutput, uint32(blockSize))

    // for _, p := range packets {
    //  h.audioPackets++
    //  if err := h.audioTrack.rtpTrack.WriteRTP(p); err != nil {
    //      h.log.Error(err)
    //      return err
    //  }
    // }
}

return nil

}

Sean-Der commented 2 years ago

Hi @xiangxud

that’s fantastic! Would you mind opening a PR I can help get it merged

xiangxud commented 2 years ago

It's my first PR,please forgive my poor skill

AAC convert to OPUS

Modify from source https://github.com/Glimesh/rtmp-ingest.git thanks Glimesh

macOS Development

brew install opusfile fdk-aac

Ubuntu / Linux Development

apt install -y pkg-config build-essential libopusfile-dev libfdk-aac-dev libavutil-dev libavcodec-dev libswscale-dev