├── .gitignore ├── main.go ├── pjsua2 ├── create_wrapper.sh ├── pjsua2_libs.go └── pjsua2_solibs.go └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.old 2 | /old 3 | .vscode 4 | /pjsua2/pjsua2.go 5 | /pjsua2/pjsua2_wrap.cxx 6 | /pjsua2/pjsua2_wrap.h 7 | pjgo 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | "pjgo/pjsua2" 7 | "strings" 8 | "time" 9 | 10 | logging "github.com/op/go-logging" 11 | ) 12 | 13 | // Create main go logger 14 | var log = logging.MustGetLogger("pjgo") 15 | 16 | // Shows usage informations 17 | func usage() { 18 | flag.PrintDefaults() 19 | os.Exit(2) 20 | } 21 | 22 | // Initialize application 23 | func initialize() { 24 | // Customize logger. 25 | logBackend := logging.NewLogBackend(os.Stderr, "", 0) 26 | // Log format string. Everything except the message has a custom color 27 | // which is dependent on the log level. Many fields have a custom output 28 | // formatting too, eg. the time returns the hour down to the millisecond. 29 | logFormat := logging.MustStringFormatter( 30 | `%{color}%{time:2006.01.02 15:04:05.000} %{program} %{pid:05d} %{id:04x} %{level:.3s} ▶%{color:reset} %{message}`, 31 | ) 32 | logBackendFormatter := logging.NewBackendFormatter(logBackend, logFormat) 33 | logging.SetBackend(logBackendFormatter) 34 | 35 | // Process command line args 36 | flag.Usage = usage 37 | flag.Parse() 38 | } 39 | 40 | // Terminate application 41 | func terminate(exitCode int) { 42 | log.Infof("pjgo golang pjsip binding test stopped") 43 | os.Exit(exitCode) 44 | } 45 | 46 | // MyAccount extends pjsau2.Account to get notifications etc. 47 | type MyAccount struct { 48 | pjsua2.Account 49 | } 50 | 51 | // onRegState callback called by pjsip when account Sip registration changes 52 | func (a MyAccount) onRegState(prm pjsua2.OnRegStateParam) { 53 | log.Infof("*** on registration state (%s) (%s)", 54 | prm.GetCode(), prm.GetReason()) 55 | } 56 | 57 | // NewMyAccount constructs a new MyAccount 58 | func NewMyAccount() *MyAccount { 59 | p := MyAccount{} 60 | p.Account = pjsua2.NewAccount() 61 | return &p 62 | } 63 | 64 | // MyLogger extends base pjsua2 logger to create a customized logger 65 | type MyLogger struct { 66 | pjsua2.LogWriter 67 | } 68 | 69 | // NewMyLogger constructs a new custom logger instance 70 | func NewMyLogger() *MyLogger { 71 | p := MyLogger{} 72 | p.LogWriter = pjsua2.NewDirectorLogWriter(p) 73 | return &p 74 | } 75 | 76 | // Write gets calld by pjsip to output new log entries 77 | func (l MyLogger) Write(entry pjsua2.LogEntry) { 78 | msg := strings.TrimSpace(entry.GetMsg()) 79 | switch pjLogLevel := entry.GetLevel(); pjLogLevel { 80 | case 0: // fatal 81 | log.Fatalf("%s", msg) 82 | case 1: // error 83 | log.Errorf("%s", msg) 84 | case 2: // warning 85 | log.Warningf("%s", msg) 86 | case 3: // info 87 | log.Infof("%s", msg) 88 | case 4: // debug 89 | log.Debugf("%s", msg) 90 | case 5: // debug 91 | log.Debugf("%s", msg) 92 | case 6: // debug 93 | log.Debugf("%s", msg) 94 | } 95 | } 96 | 97 | // Main routine 98 | func main() { 99 | initialize() 100 | log.Infof("pjgo golang pjsip binding test started") 101 | 102 | // initialize endpoint 103 | ep := pjsua2.NewEndpoint() 104 | ep.LibCreate() 105 | epCfg := pjsua2.NewEpConfig() 106 | 107 | // configure logging 108 | logCfg := pjsua2.NewLogConfig() 109 | logCfg.SetWriter(NewMyLogger()) 110 | logCfg.SetDecor( 111 | uint(pjsua2.PJ_LOG_HAS_SENDER) | 112 | uint(pjsua2.PJ_LOG_HAS_THREAD_ID)) 113 | logCfg.SetLevel(6) 114 | epCfg.SetLogConfig(logCfg) 115 | 116 | ep.LibInit(epCfg) 117 | 118 | // Create SIP transport. 119 | tCfg := pjsua2.NewTransportConfig() 120 | tCfg.SetPort(5060) 121 | ep.TransportCreate(pjsua2.PJSIP_TRANSPORT_UDP, tCfg) 122 | 123 | // Start the library (worker threads etc) 124 | ep.LibStart() 125 | 126 | // Configure an account configuration 127 | aCfg := pjsua2.NewAccountConfig() 128 | aCfg.SetIdUri("sip:test@pjsip.org") 129 | aRegCfg := pjsua2.NewAccountRegConfig() 130 | aRegCfg.SetRegistrarUri("sip:pjsip.org") 131 | aCfg.SetRegConfig(aRegCfg) 132 | cred := pjsua2.NewAuthCredInfo("digest", "*", "test", 0, "secret") 133 | aSipCfg := pjsua2.NewAccountSipConfig() 134 | credVec := pjsua2.NewAuthCredInfoVector(int64(1)) 135 | credVec.Add(cred) 136 | aSipCfg.SetAuthCreds(credVec) 137 | aCfg.SetSipConfig(aSipCfg) 138 | 139 | // Create the account 140 | acc := NewMyAccount() 141 | acc.Create(aCfg) 142 | 143 | // Here we don't have anything else to do ... 144 | time.Sleep(5 * time.Second) 145 | 146 | // terminate 147 | ep.LibDestroy() 148 | 149 | exitCode := 0 150 | terminate(exitCode) 151 | } 152 | -------------------------------------------------------------------------------- /pjsua2/create_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | swig -c++ -go -cgo -intgosize 64 \ 4 | -outcurrentdir \ 5 | -I/usr/local/include \ 6 | ~/projects/c/pjsip/pjproject-2.8/pjsip-apps/src/swig/pjsua2.i 7 | 8 | # modify path above to match your installation. 9 | 10 | -------------------------------------------------------------------------------- /pjsua2/pjsua2_libs.go: -------------------------------------------------------------------------------- 1 | // +build !shared 2 | 3 | package pjsua2 4 | 5 | /* 6 | #cgo CPPFLAGS: -DPJ_AUTOCONF=1 -O2 -DPJ_IS_BIG_ENDIAN=0 7 | #cgo CPPFLAGS: -DPJ_IS_LITTLE_ENDIAN=1 8 | #cgo CPPFLAGS: -DPJMEDIA_USE_OLD_FFMPEG=1 9 | #cgo CPPFLAGS: -I/usr/local/include -I/usr/include 10 | #cgo CPPFLAGS: -I/usr/include/c++/5 -I/usr/include/x86_64-linux-gnu/c++/5 11 | #cgo LDFLAGS: -L/usr/local/lib 12 | #cgo LDFLAGS: -lpjsua2-x86_64-unknown-linux-gnu 13 | #cgo LDFLAGS: -lpjsua-x86_64-unknown-linux-gnu 14 | #cgo LDFLAGS: -lpjsip-ua-x86_64-unknown-linux-gnu 15 | #cgo LDFLAGS: -lpjsip-simple-x86_64-unknown-linux-gnu 16 | #cgo LDFLAGS: -lpjsip-x86_64-unknown-linux-gnu 17 | #cgo LDFLAGS: -lpjmedia-codec-x86_64-unknown-linux-gnu 18 | #cgo LDFLAGS: -lpjmedia-videodev-x86_64-unknown-linux-gnu 19 | #cgo LDFLAGS: -lpjmedia-audiodev-x86_64-unknown-linux-gnu 20 | #cgo LDFLAGS: -lpjmedia-x86_64-unknown-linux-gnu 21 | #cgo LDFLAGS: -lpjnath-x86_64-unknown-linux-gnu 22 | #cgo LDFLAGS: -lpjlib-util-x86_64-unknown-linux-gnu 23 | #cgo LDFLAGS: -lsrtp-x86_64-unknown-linux-gnu 24 | #cgo LDFLAGS: -lresample-x86_64-unknown-linux-gnu 25 | #cgo LDFLAGS: -lgsmcodec-x86_64-unknown-linux-gnu 26 | #cgo LDFLAGS: -lspeex-x86_64-unknown-linux-gnu 27 | #cgo LDFLAGS: -lilbccodec-x86_64-unknown-linux-gnu 28 | #cgo LDFLAGS: -lg7221codec-x86_64-unknown-linux-gnu 29 | #cgo LDFLAGS: -lyuv-x86_64-unknown-linux-gnu 30 | #cgo LDFLAGS: -lwebrtc-x86_64-unknown-linux-gnu 31 | #cgo LDFLAGS: -lpj-x86_64-unknown-linux-gnu 32 | #cgo LDFLAGS: -lssl -lcrypto -luuid -lm -lrt -lpthread -lasound 33 | */ 34 | import "C" 35 | -------------------------------------------------------------------------------- /pjsua2/pjsua2_solibs.go: -------------------------------------------------------------------------------- 1 | // +build shared 2 | 3 | package pjsua2 4 | 5 | /* 6 | #cgo CPPFLAGS: -DPJ_AUTOCONF=1 -O2 -DPJ_IS_BIG_ENDIAN=0 7 | #cgo CPPFLAGS: -DPJ_IS_LITTLE_ENDIAN=1 8 | #cgo CPPFLAGS: -DPJMEDIA_USE_OLD_FFMPEG=1 9 | #cgo CPPFLAGS: -I/usr/local/include -I/usr/include 10 | #cgo CPPFLAGS: -I/usr/include/c++/5 -I/usr/include/x86_64-linux-gnu/c++/5 11 | #cgo LDFLAGS: -L/usr/local/lib 12 | #cgo LDFLAGS: -lpjsua2 13 | #cgo LDFLAGS: -lpjsua 14 | #cgo LDFLAGS: -lpjsip-ua 15 | #cgo LDFLAGS: -lpjsip-simple 16 | #cgo LDFLAGS: -lpjsip 17 | #cgo LDFLAGS: -lpjmedia-codec 18 | #cgo LDFLAGS: -lpjmedia-videodev 19 | #cgo LDFLAGS: -lpjmedia-audiodev 20 | #cgo LDFLAGS: -lpjmedia 21 | #cgo LDFLAGS: -lpjnath 22 | #cgo LDFLAGS: -lpjlib-util 23 | #cgo LDFLAGS: -lsrtp 24 | #cgo LDFLAGS: -lresample 25 | #cgo LDFLAGS: -lgsmcodec 26 | #cgo LDFLAGS: -lspeex 27 | #cgo LDFLAGS: -lilbccodec 28 | #cgo LDFLAGS: -lg7221codec 29 | #cgo LDFLAGS: -lyuv 30 | #cgo LDFLAGS: -lwebrtc 31 | #cgo LDFLAGS: -lpj 32 | #cgo LDFLAGS: -lssl -lcrypto -luuid -lm -lrt -lpthread -lasound 33 | */ 34 | import "C" 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # pjgo - using pjsip with go 2 | 3 | This repository describes how to use the great, C/C++ based SIP library [pjsip](http://www.pjsip.org/) with [Go](https://golang.org/). 4 | 5 | Pjsip provides a full featured library with almost everything to build Sip based communication software like for example softphones or Sip proxy servers. But this feature-richness comes with a price: projects might become complex quite fast. That's why pjsip provides the C++ based Object Oriented pjsua2 library which combines the lower level pjsip library functions into easy to use classes and components. This makes building Sip software much simpler. 6 | 7 | Go makes it relatively easy to access native code by using [goc](https://golang.org/cmd/cgo/) and some special comments in your go source files. This works quite well for C but not so well for C++. So how could we access C++ based pjsua2? 8 | 9 | That's where [SWIG](http://www.swig.org/) comes into play - the [Simplified Wrapper and Interface Generator](http://www.swig.org/). SWIG reads some metadata from interface description files **.i** or **.swig** and creates wrappers where higher level languages (e.g. Python) can call lower level pjsip functions. pjsip ships with predefined wrappers for C#, Python and Java. 10 | 11 | Fortunately the pjsip source tree contains a SWIG definition for pjsua2 ([see github](https://github.com/pjsip/pjproject/tree/master/pjsip-apps/src/swig)). With this, SWIG can create a Go wrapper for pjsua2. 12 | 13 | ```sh 14 | swig -c++ -go -cgo -intgosize 64 \ 15 | -outcurrentdir \ 16 | -I/usr/local/include \ 17 | /path/to/pjsip/pjproject-2.8/pjsip-apps/src/swig/pjsua2.i 18 | ``` 19 | 20 | This generates files **pjsua2_wrap.cxx**, **pjsua2_wrap.h** and **pjsua2.go**. These files are not meant to be used by a programmer directly (almost unreadable generated Go code). Instead you import it into your Go project and can start using pjsip. As a last step you must decide if you want to link the static pjsip libraries (resulting in a larger binary) or against pjsip's shared libraries. This can be done by creating a go file (e.g. pjsua2_libs.go) in the same folder as the SWIG generated files which describes which pjsip libraries to link: 21 | 22 | for static libraries: 23 | ```go 24 | // +build !shared 25 | 26 | package pjsua2 27 | 28 | /* 29 | #cgo CPPFLAGS: -DPJ_AUTOCONF=1 -O2 -DPJ_IS_BIG_ENDIAN=0 30 | #cgo CPPFLAGS: -DPJ_IS_LITTLE_ENDIAN=1 31 | #cgo CPPFLAGS: -DPJMEDIA_USE_OLD_FFMPEG=1 32 | #cgo CPPFLAGS: -I/usr/local/include -I/usr/include 33 | #cgo CPPFLAGS: -I/usr/include/c++/5 -I/usr/include/x86_64-linux-gnu/c++/5 34 | #cgo LDFLAGS: -L/usr/local/lib 35 | #cgo LDFLAGS: -lpjsua2-x86_64-unknown-linux-gnu 36 | #cgo LDFLAGS: -lpjsua-x86_64-unknown-linux-gnu 37 | #cgo LDFLAGS: -lpjsip-ua-x86_64-unknown-linux-gnu 38 | #cgo LDFLAGS: -lpjsip-simple-x86_64-unknown-linux-gnu 39 | #cgo LDFLAGS: -lpjsip-x86_64-unknown-linux-gnu 40 | #cgo LDFLAGS: -lpjmedia-codec-x86_64-unknown-linux-gnu 41 | #cgo LDFLAGS: -lpjmedia-videodev-x86_64-unknown-linux-gnu 42 | #cgo LDFLAGS: -lpjmedia-audiodev-x86_64-unknown-linux-gnu 43 | #cgo LDFLAGS: -lpjmedia-x86_64-unknown-linux-gnu 44 | #cgo LDFLAGS: -lpjnath-x86_64-unknown-linux-gnu 45 | #cgo LDFLAGS: -lpjlib-util-x86_64-unknown-linux-gnu 46 | #cgo LDFLAGS: -lsrtp-x86_64-unknown-linux-gnu 47 | #cgo LDFLAGS: -lresample-x86_64-unknown-linux-gnu 48 | #cgo LDFLAGS: -lgsmcodec-x86_64-unknown-linux-gnu 49 | #cgo LDFLAGS: -lspeex-x86_64-unknown-linux-gnu 50 | #cgo LDFLAGS: -lilbccodec-x86_64-unknown-linux-gnu 51 | #cgo LDFLAGS: -lg7221codec-x86_64-unknown-linux-gnu 52 | #cgo LDFLAGS: -lyuv-x86_64-unknown-linux-gnu 53 | #cgo LDFLAGS: -lwebrtc-x86_64-unknown-linux-gnu 54 | #cgo LDFLAGS: -lpj-x86_64-unknown-linux-gnu 55 | #cgo LDFLAGS: -lssl -lcrypto -luuid -lm -lrt -lpthread -lasound 56 | */ 57 | import "C" 58 | ``` 59 | 60 | and for shared libraries: 61 | ```go 62 | // +build shared 63 | 64 | package pjsua2 65 | 66 | /* 67 | #cgo CPPFLAGS: -DPJ_AUTOCONF=1 -O2 -DPJ_IS_BIG_ENDIAN=0 68 | #cgo CPPFLAGS: -DPJ_IS_LITTLE_ENDIAN=1 69 | #cgo CPPFLAGS: -DPJMEDIA_USE_OLD_FFMPEG=1 70 | #cgo CPPFLAGS: -I/usr/local/include -I/usr/include 71 | #cgo CPPFLAGS: -I/usr/include/c++/5 -I/usr/include/x86_64-linux-gnu/c++/5 72 | #cgo LDFLAGS: -L/usr/local/lib 73 | #cgo LDFLAGS: -lpjsua2 74 | #cgo LDFLAGS: -lpjsua 75 | #cgo LDFLAGS: -lpjsip-ua 76 | #cgo LDFLAGS: -lpjsip-simple 77 | #cgo LDFLAGS: -lpjsip 78 | #cgo LDFLAGS: -lpjmedia-codec 79 | #cgo LDFLAGS: -lpjmedia-videodev 80 | #cgo LDFLAGS: -lpjmedia-audiodev 81 | #cgo LDFLAGS: -lpjmedia 82 | #cgo LDFLAGS: -lpjnath 83 | #cgo LDFLAGS: -lpjlib-util 84 | #cgo LDFLAGS: -lsrtp 85 | #cgo LDFLAGS: -lresample 86 | #cgo LDFLAGS: -lgsmcodec 87 | #cgo LDFLAGS: -lspeex 88 | #cgo LDFLAGS: -lilbccodec 89 | #cgo LDFLAGS: -lg7221codec 90 | #cgo LDFLAGS: -lyuv 91 | #cgo LDFLAGS: -lwebrtc 92 | #cgo LDFLAGS: -lpj 93 | #cgo LDFLAGS: -lssl -lcrypto -luuid -lm -lrt -lpthread -lasound 94 | */ 95 | import "C" 96 | ``` 97 | 98 | The pjsip documentation contains examples for C#, Python and Java. On in this repository you will find a go implementation of these examples. 99 | 100 | It can be built either to use the static pjsip libraries with 101 | ```sh 102 | go build 103 | ``` 104 | or to use the shared libraries with 105 | ```sh 106 | go build -tags shared 107 | ``` 108 | 109 | See also my original blog post at https://www.min.at/prinz/?x=entry:entry180924-185225 110 | --------------------------------------------------------------------------------