Skip to content

Commit

Permalink
Add audio prompts.
Browse files Browse the repository at this point in the history
  • Loading branch information
dennwc committed Nov 30, 2023
1 parent ce185ef commit 746e540
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 14 deletions.
1 change: 1 addition & 0 deletions build/sip/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ COPY go.sum .
RUN go mod download

# copy source
COPY res/ res/
COPY cmd/ cmd/
COPY pkg/ pkg/
COPY version/ version/
Expand Down
3 changes: 2 additions & 1 deletion build/test/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ WORKDIR /workspace
# install deps
RUN apt-get update && \
apt-get install -y \
curl
curl libopus-dev libopusfile-dev

RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then GOARCH=arm64; else GOARCH=amd64; fi && \
curl -L -o /tmp/go.tar.gz "https://go.dev/dl/go1.20.7.linux-$GOARCH.tar.gz"
Expand All @@ -34,6 +34,7 @@ COPY go.sum .
RUN go mod download

# copy source
COPY res/ res/
COPY pkg/ pkg/
COPY version/ version/

Expand Down
59 changes: 57 additions & 2 deletions pkg/sip/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@
package sip

import (
"bytes"
"encoding/binary"
"log"
"net"
"sync/atomic"
"time"

"github.com/at-wat/ebml-go"
"github.com/at-wat/ebml-go/webm"
"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media"
"github.com/zaf/g711"
"gopkg.in/hraban/opus.v2"

"github.com/livekit/sip/pkg/mixer"
"github.com/livekit/sip/res"

lksdk "github.com/livekit/server-sdk-go"
)
Expand All @@ -37,6 +41,44 @@ const (
sampleRate = 8000
)

type mediaRes struct {
enterPin [][]int16
roomJoin [][]int16
wrongPin [][]int16
}

func (s *Server) initMediaRes() {
s.res.enterPin = audioFileToFrames(res.EnterPinMkv)
s.res.roomJoin = audioFileToFrames(res.RoomJoinMkv)
s.res.wrongPin = audioFileToFrames(res.WrongPinMkv)
}

func audioFileToFrames(data []byte) [][]int16 {
var ret struct {
Header webm.EBMLHeader `ebml:"EBML"`
Segment webm.Segment `ebml:"Segment"`
}
if err := ebml.Unmarshal(bytes.NewReader(data), &ret); err != nil {
panic(err)
}

var frames [][]int16
for _, cluster := range ret.Segment.Cluster {
for _, block := range cluster.SimpleBlock {
for _, data := range block.Data {
decoded := g711.DecodeUlaw(data)
pcm := make([]int16, 0, len(decoded)/2)
for i := 0; i < len(decoded); i += 2 {
sample := binary.LittleEndian.Uint16(decoded[i:])
pcm = append(pcm, int16(sample))
}
frames = append(frames, pcm)
}
}
}
return frames
}

type mediaData struct {
conn *net.UDPConn
mix *mixer.Mixer
Expand Down Expand Up @@ -246,11 +288,24 @@ func (c *inboundCall) createLiveKitParticipant(roomName, participantIdentity str

func (c *inboundCall) joinRoom(roomName, identity string) {
log.Printf("Bridging SIP call %q -> %q to room %q (as %q)\n", c.from.Address.User, c.to.Address.User, roomName, identity)
c.playAudio(c.s.res.roomJoin)
if err := c.createLiveKitParticipant(roomName, identity); err != nil {
log.Println(err)
}
}

func (c *inboundCall) playPleaseEnterPin() {
// FIXME: play "Please enter room pin" audio
func (c *inboundCall) playAudio(frames [][]int16) {
input := c.media.mix.AddInput()
defer c.media.mix.RemoveInput(input)

tick := time.NewTicker(20 * time.Millisecond)
defer tick.Stop()
for range tick.C {
if len(frames) == 0 {
break
}
samples := frames[0]
frames = frames[1:]
input.Push(samples)
}
}
16 changes: 16 additions & 0 deletions pkg/sip/media_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package sip

import (
"testing"

"github.com/livekit/sip/res"
)

func TestDecodeRes(t *testing.T) {
res := res.EnterPinMkv
frames := audioFileToFrames(res)
for i, f := range frames {
t.Log(i, len(f))
}
t.Log(len(frames))
}
29 changes: 18 additions & 11 deletions pkg/sip/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"context"
"fmt"
"log"
"net"
"sync"
"sync/atomic"
"time"
Expand All @@ -27,6 +26,7 @@ import (
"github.com/emiago/sipgo/sip"
"github.com/icholy/digest"
"github.com/pion/sdp/v2"
"golang.org/x/exp/maps"

"github.com/livekit/sip/pkg/config"
)
Expand Down Expand Up @@ -56,27 +56,27 @@ type (
authenticationHandler authenticationHandlerFunc
dispatchRuleHandler dispatchRuleHandlerFunc
conf *config.Config

res mediaRes
}

inProgressInvite struct {
from string
challenge digest.Challenge
}

activeInvite struct {
udpConn *net.UDPConn
}
)

func NewServer(conf *config.Config, authenticationHandler authenticationHandlerFunc, dispatchRuleHandler dispatchRuleHandlerFunc) *Server {
return &Server{
s := &Server{
conf: conf,
publicIp: getPublicIP(),
activeCalls: make(map[string]*inboundCall),
inProgressInvites: []*inProgressInvite{},
authenticationHandler: authenticationHandler,
dispatchRuleHandler: dispatchRuleHandler,
}
s.initMediaRes()
return s
}

func getTagValue(req *sip.Request) (string, error) {
Expand Down Expand Up @@ -274,7 +274,7 @@ func (c *inboundCall) runMedia(offerData []byte) ([]byte, error) {
func (c *inboundCall) pinPrompt() {
log.Printf("Requesting Pin for SIP call %q -> %q\n", c.from.Address.User, c.to.Address.User)
const pinLimit = 16
c.playPleaseEnterPin()
c.playAudio(c.s.res.enterPin)
pin := ""
noPin := false
for {
Expand All @@ -293,10 +293,8 @@ func (c *inboundCall) pinPrompt() {

log.Printf("Checking Pin for SIP call %q -> %q = %q (noPin = %v)\n", c.from.Address.User, c.to.Address.User, pin, noPin)
roomName, identity, requirePin, reject := c.s.dispatchRuleHandler(c.from.Address.User, c.to.Address.User, c.src, pin, noPin)
if reject {
c.Close()
return
} else if requirePin || roomName == "" {
if reject || requirePin || roomName == "" {
c.playAudio(c.s.res.wrongPin)
c.Close()
return
}
Expand All @@ -306,6 +304,7 @@ func (c *inboundCall) pinPrompt() {
// Gather pin numbers
pin += string(b)
if len(pin) > pinLimit {
c.playAudio(c.s.res.wrongPin)
c.Close()
return
}
Expand All @@ -321,6 +320,7 @@ func (c *inboundCall) Close() error {
delete(c.s.activeCalls, c.tag)
c.s.cmu.Unlock()
c.closeMedia()
// FIXME: drop the actual call
return nil
}

Expand Down Expand Up @@ -366,6 +366,13 @@ func (s *Server) Start() error {
}

func (s *Server) Stop() error {
s.cmu.Lock()
calls := maps.Values(s.activeCalls)
s.activeCalls = make(map[string]*inboundCall)
s.cmu.Unlock()
for _, c := range calls {
c.Close()
}
if s.sipSrv != nil {
return s.sipSrv.Close()
}
Expand Down
12 changes: 12 additions & 0 deletions res/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package res

import _ "embed"

//go:embed enter_pin.mkv
var EnterPinMkv []byte

//go:embed room_join.mkv
var RoomJoinMkv []byte

//go:embed wrong_pin.mkv
var WrongPinMkv []byte
Binary file added res/enter_pin.mkv
Binary file not shown.
Binary file added res/room_join.mkv
Binary file not shown.
Binary file added res/wrong_pin.mkv
Binary file not shown.

0 comments on commit 746e540

Please sign in to comment.