Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions dtmf/dtmf.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package dtmf
import (
"context"
"encoding/binary"
"fmt"
"io"
"math"
"time"
Expand All @@ -27,19 +28,20 @@ import (
)

const (
SDPNameOnly = "telephone-event"
SDPNameAndRate = SDPNameOnly + "/8000"
SDPName = SDPNameAndRate // Deprecated: use SDPNameOnly or SDPNameAndRate
SDPNameOnly = "telephone-event"
)
const SampleRate = 8000

func init() {
media.RegisterCodec(media.NewCodec(media.CodecInfo{
SDPName: SDPNameAndRate,
SampleRate: SampleRate,
RTPIsStatic: false,
Priority: -100, // let it be last in SDP
}))
for i, rate := range []int{
8000, 16000, 48000,
} {
media.RegisterCodec(media.NewCodec(media.CodecInfo{
SDPName: fmt.Sprintf("%s/%d", SDPNameOnly, rate),
SampleRate: rate,
RTPIsStatic: false,
Priority: -100 - i, // let it be last in SDP
}))
}
}

const (
Expand Down Expand Up @@ -219,7 +221,7 @@ func Encode(out []byte, ev Event) (int, error) {
// Write in-band (analog) and off-band (digital) DTMF tones to audio and RTP streams respectively.
//
// Digits may contain a special character 'w' which adds a 0.5 sec delay.
func Write(ctx context.Context, audio media.Writer[media.PCM16Sample], events *rtp.Stream, startTs uint32, digits string) error {
func Write(ctx context.Context, audio media.Writer[media.PCM16Sample], events *rtp.Stream, eventsRate int, startTs uint32, digits string) error {
const framesPerSec = int(time.Second / rtp.DefFrameDur)
var (
buf [4]byte
Expand Down Expand Up @@ -247,7 +249,7 @@ func Write(ctx context.Context, audio media.Writer[media.PCM16Sample], events *r
totalDur = dt
nextDelay = 0
if events != nil {
events.Delay(uint32(dt / (time.Second / SampleRate)))
events.Delay(uint32(dt / (time.Second / time.Duration(eventsRate))))
}
}

Expand Down Expand Up @@ -303,7 +305,7 @@ func Write(ctx context.Context, audio media.Writer[media.PCM16Sample], events *r
n, err := Encode(buf[:], Event{
Code: code,
Volume: eventVolume,
Dur: uint16(dur / (time.Second / SampleRate)),
Dur: uint16(dur / (time.Second / time.Duration(eventsRate))),
End: end,
})
if err != nil {
Expand All @@ -323,7 +325,7 @@ func Write(ctx context.Context, audio media.Writer[media.PCM16Sample], events *r
return err
}
// advance the timestamp now
events.Delay(uint32(totalDur / (time.Second / SampleRate)))
events.Delay(uint32(totalDur / (time.Second / time.Duration(eventsRate))))
}
}
remaining -= step
Expand Down
157 changes: 81 additions & 76 deletions dtmf/dtmf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package dtmf
import (
"context"
"encoding/hex"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -132,83 +133,87 @@ func TestDecodeRTPWithEnd(t *testing.T) {
func TestDTMFDelay(t *testing.T) {
const startTime = 1242

var buf rtp.Buffer
w := rtp.NewSeqWriter(&buf).NewStream(101, SampleRate)
err := Write(context.Background(), nil, w, startTime, "1w23")
require.NoError(t, err)
for _, eventsRate := range []int{
8000, 16000,
} {
t.Run(strconv.Itoa(eventsRate), func(t *testing.T) {
var buf rtp.Buffer
w := rtp.NewSeqWriter(&buf).NewStream(101, eventsRate)
err := Write(context.Background(), nil, w, eventsRate, startTime, "1w23")
require.NoError(t, err)

type packet struct {
SequenceNumber uint16
Timestamp uint32
Marker bool
Event
}
var (
exp []packet
seq uint16
ts uint32
)
const (
packetDur = uint32(SampleRate / int(time.Second/rtp.DefFrameDur))
)

ts = startTime

expectDigit := func(code byte, digit byte) {
start := ts
const n = 13
for i := 0; i < n-1; i++ {
exp = append(exp, packet{
SequenceNumber: seq,
Timestamp: start, // should be the same for all events
Marker: i == 0,
Event: Event{
Code: code,
Digit: digit,
Volume: eventVolume,
Dur: uint16(i+1) * uint16(packetDur),
End: false,
},
})
ts += packetDur
seq++
}
// end event must be sent 3 times with the same duration
for i := 0; i < 3; i++ {
exp = append(exp, packet{
SequenceNumber: seq,
Timestamp: start, // should be the same for all events
Marker: false,
Event: Event{
Code: code,
Digit: digit,
Volume: eventVolume,
Dur: uint16(n) * uint16(packetDur),
End: true,
},
})
seq++
}
ts += packetDur
// delay between digits
ts += uint32(eventDur / (time.Second / SampleRate))
// rounding error (12.5 events in a sec)
ts -= packetDur / 2
}
expectDigit(1, '1')
ts += SampleRate / 2 // 500ms delay
expectDigit(2, '2')
expectDigit(3, '3')
var got []packet
for _, p := range buf {
e, err := Decode(p.Payload)
require.NoError(t, err)
got = append(got, packet{
SequenceNumber: p.SequenceNumber,
Timestamp: p.Timestamp,
Marker: p.Marker,
Event: e,
type packet struct {
SequenceNumber uint16
Timestamp uint32
Marker bool
Event
}
var (
exp []packet
seq uint16
ts uint32
)
packetDur := uint32(eventsRate / int(time.Second/rtp.DefFrameDur))

ts = startTime

expectDigit := func(code byte, digit byte) {
start := ts
const n = 13
for i := 0; i < n-1; i++ {
exp = append(exp, packet{
SequenceNumber: seq,
Timestamp: start, // should be the same for all events
Marker: i == 0,
Event: Event{
Code: code,
Digit: digit,
Volume: eventVolume,
Dur: uint16(i+1) * uint16(packetDur),
End: false,
},
})
ts += packetDur
seq++
}
// end event must be sent 3 times with the same duration
for i := 0; i < 3; i++ {
exp = append(exp, packet{
SequenceNumber: seq,
Timestamp: start, // should be the same for all events
Marker: false,
Event: Event{
Code: code,
Digit: digit,
Volume: eventVolume,
Dur: uint16(n) * uint16(packetDur),
End: true,
},
})
seq++
}
ts += packetDur
// delay between digits
ts += uint32(eventDur / (time.Second / time.Duration(eventsRate)))
// rounding error (12.5 events in a sec)
ts -= packetDur / 2
}
expectDigit(1, '1')
ts += uint32(eventsRate) / 2 // 500ms delay
expectDigit(2, '2')
expectDigit(3, '3')
var got []packet
for _, p := range buf {
e, err := Decode(p.Payload)
require.NoError(t, err)
got = append(got, packet{
SequenceNumber: p.SequenceNumber,
Timestamp: p.Timestamp,
Marker: p.Marker,
Event: e,
})
}
require.Equal(t, exp, got)
})
}
require.Equal(t, exp, got)
}
4 changes: 2 additions & 2 deletions resample_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ func memstats(pid int) int64 {
}

func TestResampleLeak(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("windows is not supported for this test")
if runtime.GOOS != "linux" {
t.Skip("only linux supported for this test")
}
pid := os.Getpid()

Expand Down
Loading
Loading