emiago / diago

Short of Dialog + GO. Library/Framework for building VOIP solutions in GO
Mozilla Public License 2.0
100 stars 10 forks source link

Incoming audio delay (jitter buffer required) #23

Open juanpmarin opened 22 hours ago

juanpmarin commented 22 hours ago

First off, thank you for your outstanding work on Diago!

I’m conducting tests with Diago using a telephony trunk to make real phone calls. During these tests, I’ve encountered an issue with audio latency. When using Linphone, there’s some latency for the incoming audio, but with Diago, it takes several seconds for the audio to reach my PC.

To debug this, I’ve implemented a simple Go function to play call audio on my PC. Here’s the code:

mediaProps := diago.MediaProps{}

audioReader, err := dlg.AudioReader(diago.WithAudioReaderMediaProps(&mediaProps))
if err != nil {
    return fmt.Errorf("error getting audio reader: %w", err)
}

decoder, err := audio.NewPCMDecoderReader(mediaProps.Codec.PayloadType, audioReader)
if err != nil {
    return err
}

streamer := beep.StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
    buf := make([]byte, len(samples)*2)
    n, err := decoder.Read(buf)
    if err != nil {
        if err == io.EOF {
            return 0, false
        }
        fmt.Println("Error reading audio:", err)
        return 0, false
    }

    for i := 0; i < n/2; i++ {
        val := int16(buf[2*i]) | int16(buf[2*i+1])<<8
        samples[i][0] = float64(val) / 32768
        samples[i][1] = float64(val) / 32768
    }

    return n / 2, true
})

sampleRate := beep.SampleRate(mediaProps.Codec.SampleRate)

err = speaker.Init(sampleRate, sampleRate.N(20*time.Millisecond))
if err != nil {
    return err
}

speaker.PlayAndWait(streamer)

I would appreciate your opinion about why is this delay ocurring.

These two logs are printed very often: sequence duplicate and Out of order pkt received

emiago commented 17 hours ago

Hi @juanpmarin , what you are experiencing is probably with lot of jitter. I do not think it is latency issue that big it is just that diago or beep (otto) playout fails to get samples in right time, or they out of order as you posted.

Currently diago relies mostly on system buffers (which you can increase) but what you actually need is jitter buffering on application level. THis is something I have in plan to add, and so far it was only done as very small POCs. What they are just doing is delaying your playout, while buffering packets and ordering them. Still if you are suffering from too bad network or bad provider they can not save you from big latency in playout.

Anyway as Diago is low level library, implementation of jitter buffers could be done from caller side as well.

What would be good, to have your pcap posted and I can tell you what is your network situation. I will mark this ticket for jitter buffer feature!

juanpmarin commented 12 hours ago

Thanks for your answer! Something that I didn't tell you is that with diago, audio has some distortion too. With linphone audio is just right and without delay. I would love to show you the pcap. But I can't post it publicly, how can I send it to you?