Skip to content

Commit

Permalink
Better compliance with the DTMF RFC4733 (#126)
Browse files Browse the repository at this point in the history
Better compliance with the DTMF RFC4733.
  • Loading branch information
dennwc authored Jul 23, 2024
1 parent 8338859 commit 6d1c823
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 13 deletions.
18 changes: 16 additions & 2 deletions pkg/media/dtmf/dtmf.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,20 +264,34 @@ func Write(ctx context.Context, audio media.PCM16Writer, events *rtp.Stream, dig
// generate telephony events
if events != nil && len(freq) != 0 {
dur := step + totalDur - remaining
first := totalDur == remaining
end := remaining-step <= 0

n, err := Encode(buf[:], Event{
Code: code,
Volume: eventVolume,
Dur: uint16(dur / (time.Second / SampleRate)),
End: remaining-step <= 0,
End: end,
})
if err != nil {
return err
}
err = events.WritePayload(buf[:n], totalDur == remaining)
// all packets for a digit must be sent with the same timestamp
err = events.WritePayloadAtCurrent(buf[:n], first)
if err != nil {
return err
}
if end {
// must repeat edn event 3 times, as per RFC
if err = events.WritePayloadAtCurrent(buf[:n], first); err != nil {
return err
}
if err = events.WritePayloadAtCurrent(buf[:n], first); err != nil {
return err
}
// advance the timestamp now
events.Delay(uint32(totalDur / (time.Second / SampleRate)))
}
}
remaining -= step
ts += step
Expand Down
76 changes: 67 additions & 9 deletions pkg/media/dtmf/dtmf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,74 @@ func TestDTMFDelay(t *testing.T) {
w := rtp.NewSeqWriter(&buf).NewStream(101, SampleRate)
err := Write(context.Background(), nil, w, "1w23")
require.NoError(t, err)
require.Len(t, buf, 39)

const packetDur = uint32(SampleRate / int(time.Second/rtp.DefFrameDur))
for i, p := range buf {
ts := packetDur * uint32(i)
if i >= 26 {
ts += 4 * (SampleRate / 4) // 2 * 250ms + 500ms
} else if i >= 13 {
ts += 3 * (SampleRate / 4) // 250ms after tone + 500ms user-defined
type packet struct {
SequenceNumber uint16
Timestamp uint32
Marker bool
Event
}
var (
exp []packet
seq uint16
ts uint32
)
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++
}
require.EqualValues(t, uint16(i), p.SequenceNumber)
require.EqualValues(t, ts, p.Timestamp, "i=%d, dt=%v", i, p.Timestamp-ts)
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,
})
}
require.Equal(t, exp, got)
}
16 changes: 14 additions & 2 deletions pkg/media/rtp/rtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,30 @@ type Stream struct {
ev Event
}

func (s *Stream) WritePayload(data []byte, marker bool) error {
func (s *Stream) writePayload(inc bool, data []byte, marker bool) error {
s.mu.Lock()
defer s.mu.Unlock()
s.ev.Payload = data
s.ev.Marker = marker
if err := s.s.WriteEvent(&s.ev); err != nil {
return err
}
s.ev.Timestamp += s.packetDur
if inc {
s.ev.Timestamp += s.packetDur
}
return nil
}

// WritePayload writes the payload to RTP and increments the timestamp.
func (s *Stream) WritePayload(data []byte, marker bool) error {
return s.writePayload(true, data, marker)
}

// WritePayloadAtCurrent writes the payload to RTP at the current timestamp.
func (s *Stream) WritePayloadAtCurrent(data []byte, marker bool) error {
return s.writePayload(false, data, marker)
}

func (s *Stream) Delay(dur uint32) {
s.mu.Lock()
defer s.mu.Unlock()
Expand Down

0 comments on commit 6d1c823

Please sign in to comment.