Last active
June 13, 2024 13:27
-
-
Save hoangtuan151/85471ec8ec02f6e863f7a40776f627e0 to your computer and use it in GitHub Desktop.
Testing inbound call with sample audio
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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