Skip to content

Instantly share code, notes, and snippets.

@hoangtuan151
Last active June 13, 2024 13:27
Show Gist options
  • Save hoangtuan151/85471ec8ec02f6e863f7a40776f627e0 to your computer and use it in GitHub Desktop.
Save hoangtuan151/85471ec8ec02f6e863f7a40776f627e0 to your computer and use it in GitHub Desktop.
Testing inbound call with sample audio
package main
import (
"bytes"
"log"
"math/rand"
"net"
"os"
"path/filepath"
"time"
"github.com/jart/gosip/rtp"
"github.com/jart/gosip/sdp"
"github.com/jart/gosip/sip"
"github.com/jart/gosip/util"
)
const (
HOST = "10.124.68.213"
CALLEE = "1001"
)
func testCallToEchoDialplan() {
// Connect to the remote SIP UDP endpoint.
conn, err := net.Dial("udp", HOST+":5080")
if err != nil {
log.Print("sip dial:", err)
return
}
defer conn.Close()
raddr := conn.RemoteAddr().(*net.UDPAddr)
laddr := conn.LocalAddr().(*net.UDPAddr)
// Create an RTP socket.
rtpsock, err := net.ListenPacket("udp", HOST+":0")
if err != nil {
log.Print("rtp listen:", err)
return
}
defer rtpsock.Close()
rtpaddr := rtpsock.LocalAddr().(*net.UDPAddr)
// Create an invite message and attach the SDP.
invite := &sip.Msg{
CallID: util.GenerateCallID(),
CSeq: util.GenerateCSeq(),
Method: "INVITE",
CSeqMethod: "INVITE",
Request: &sip.URI{
Scheme: "sip",
User: CALLEE,
Host: raddr.IP.String(),
Port: uint16(raddr.Port),
},
Via: &sip.Via{
Host: laddr.IP.String(),
Port: uint16(laddr.Port),
Param: &sip.Param{Name: "branch", Value: util.GenerateBranch()},
},
From: &sip.Addr{
Display: "Echo Test",
Uri: &sip.URI{
Scheme: "sip",
Host: laddr.IP.String(),
Port: uint16(laddr.Port),
},
Param: &sip.Param{Name: "tag", Value: util.GenerateTag()},
},
To: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: raddr.IP.String(),
Port: uint16(raddr.Port),
},
},
Contact: &sip.Addr{
Uri: &sip.URI{
Scheme: "sip",
Host: laddr.IP.String(),
Port: uint16(laddr.Port),
},
},
UserAgent: "gosip/1.o",
Payload: sdp.New(rtpaddr, sdp.ULAWCodec, sdp.DTMFCodec),
}
// Turn invite message into a packet and send via UDP socket.
var b bytes.Buffer
invite.Append(&b)
log.Printf(">>> %s\n%s\n", raddr, b.String())
if amt, err := conn.Write(b.Bytes()); err != nil || amt != b.Len() {
log.Fatal(err)
}
// Receive provisional 100 Trying.
conn.SetDeadline(time.Now().Add(time.Second))
memory := make([]byte, 2048)
amt, err := conn.Read(memory)
if err != nil {
log.Fatal("read 100 trying:", err)
}
log.Printf("<<< %s\n%s\n", raddr, string(memory[0:amt]))
msg, err := sip.ParseMsg(memory[0:amt])
if err != nil {
log.Fatal("parse 100 trying", err)
}
if !msg.IsResponse() || msg.Status != 100 || msg.Phrase != "Trying" {
log.Fatal("didn't get 100 trying :[")
}
// Receive 200 OK.
for {
conn.SetDeadline(time.Now().Add(5 * time.Second))
amt, err = conn.Read(memory)
if err != nil {
log.Fatal("read 200 ok:", err)
}
log.Printf("<<< %s\n%s\n", raddr, string(memory[0:amt]))
msg, err = sip.ParseMsg(memory[0:amt])
if err != nil {
log.Fatal("parse 200 ok:", err)
}
if !msg.IsResponse() || msg.Status != 200 || msg.Phrase != "OK" {
log.Print("wanted 200 ok but got:", msg.Status, msg.Phrase, "... retry in 5s")
time.Sleep(5 * time.Second)
} else {
break
}
}
// Acknowledge the 200 OK to answer the call.
var ack sip.Msg
ack.Request = invite.Request
ack.From = msg.From
ack.To = msg.To
ack.CallID = msg.CallID
ack.Method = "ACK"
ack.CSeq = msg.CSeq
ack.CSeqMethod = "ACK"
ack.Via = msg.Via
b.Reset()
ack.Append(&b)
if amt, err := conn.Write(b.Bytes()); err != nil || amt != b.Len() {
log.Fatal(err)
}
// Figure out where they want us to send RTP.
var rrtpaddr *net.UDPAddr
if ms, ok := msg.Payload.(*sdp.SDP); ok {
rrtpaddr = &net.UDPAddr{IP: net.ParseIP(ms.Addr), Port: int(ms.Audio.Port)}
} else {
log.Fatal("200 ok didn't have sdp payload")
}
// Send RTP packets containing junk until we get an echo response.
raw_audio, _ := os.ReadFile("../assets/bot_greeting.wav")
raw_audio_idx := 0
log.Printf("read %d bytes from sample audio", len(raw_audio))
size := 160
quit := make(chan bool)
go func() {
frameout := make([]byte, rtp.HeaderSize+size)
rtpHeader := rtp.Header{
PT: sdp.ULAWCodec.PT,
Seq: 666,
TS: 0,
Ssrc: rand.Uint32(),
}
ticker := time.NewTicker(20 * time.Millisecond)
defer ticker.Stop()
ticker2 := time.NewTicker(1 * time.Second)
defer ticker2.Stop()
for {
select {
case <-ticker.C:
for n := 0; n < size; n++ {
if raw_audio_idx >= len(raw_audio) {
break
}
frameout[rtp.HeaderSize+n] = raw_audio[raw_audio_idx]
raw_audio_idx++
}
rtpHeader.Write(frameout)
rtpHeader.TS += uint32(size)
rtpHeader.Seq++
amt, err = rtpsock.WriteTo(frameout, rrtpaddr)
if err != nil {
log.Fatal("rtp write", err)
}
case <-ticker2.C:
log.Printf("written at audio idx: %d", raw_audio_idx)
case <-quit:
return
}
}
}()
for {
if raw_audio_idx < len(raw_audio) {
time.Sleep(20 * time.Microsecond)
} else {
quit <- true
break
}
}
// Hangup (we'll be lazy and just change up the ack Msg)
log.Print("Going to hangup...")
conn.SetDeadline(time.Now().Add(5 * time.Second))
ack.Method = "BYE"
ack.CSeqMethod = "BYE"
ack.CSeq++
b.Reset()
ack.Append(&b)
amt, err = conn.Write(b.Bytes())
if err != nil || amt != b.Len() {
log.Fatal("hangup err:", err)
}
// Wait for acknowledgment of hangup.
amt, err = conn.Read(memory)
if err != nil {
log.Fatal("read hangup ack err:", err)
}
msg, err = sip.ParseMsg(memory[0:amt])
if err != nil {
log.Fatal("parse err:", err)
}
if !msg.IsResponse() || msg.Status != 200 || msg.Phrase != "OK" {
log.Fatal("wanted bye response 200 ok but got:", msg.Status, msg.Phrase)
}
log.Print("End call !!!")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment