Pjsip-pjsua python

From 탱이의 잡동사니
Revision as of 15:31, 12 January 2015 by Pchero (talk | contribs)
Jump to navigation Jump to search

Overview

Psjua 는 그자체로도 이미 훌륭한 SIP client 이지만 본격적인 진가는 지원 API 가 아닐까 싶다. C/Python 으로 library 모듈을 지원하는데, 이를 사용하면 강력한 SIP 테스팅 툴을 만들 수 있기 때문이다. 여기서는 pjsua-python 모듈을 사용한 테스팅 툴 제작을 설명한다.

Pjsua-python module은 Signaling, media, Call control API, account management, buddy list management, presence, instant messaging, local conferencing, file streaming, local playback, voice recording, NAT traversal(STUN, TURN, ICE) 등을 지원한다.

전체 프로그래밍 가이드는 이곳<ref>http://trac.pjsip.org/repos/wiki/Python_SIP_Tutorial</ref> 에서 확인할 수 있다.

Installation

당연한 말이겠지만 pjsua-python 모듈 사용을 위해서는 해당 모듈 설치가 필요하다.

Linux

$ cd your-pjsip-root-dir
$ ./configure && make dep && make
$ cd pjsip-apps/src/python
$ sudo make

Mobiles

IOS, Android 를 비롯한 모바일 운영체제 역시 지원한다. 자세한 내용은 다음를 참조하자. IOS<ref>http://trac.pjsip.org/repos/wiki/Getting-Started/iPhone</ref> , Android<ref>http://trac.pjsip.org/repos/wiki/Getting-Started/Android</ref> , BlabkBerry<ref>http://trac.pjsip.org/repos/wiki/Getting-Started/BB10</ref> , Windows_Mobile<ref>http://trac.pjsip.org/repos/wiki/Getting-Started/Windows-Mobile</ref> , Windows_phone_8<ref>http://trac.pjsip.org/repos/wiki/Getting-Started/Windows-Phone</ref> , Symbian<ref>http://trac.pjsip.org/repos/wiki/Getting-Started/Symbian</ref>

Others

자세한 설치 내용은 이곳<ref>http://trac.pjsip.org/repos/wiki/Python_SIP/Build_Install</ref>을 참조하자.


Simple example

P2P 다이얼 방식으로 전화를 거는 예제. Asterisk 설정이 되어 있지 않다면 당연히 오류가 난다. 오류 메시지를 확인하며 정상적인 오류 메시지가 나타나는지를 확인하면 된다. Call is DISCONNCTD last code = 503 (Service Unavailable) 메시지가 나타나야 한다.

  • Code

<syntaxhighlight lang="python"> import sys import pjsua as pj

  1. Logging callback

def log_cb(level, str, len):

   print str,
  1. Callback to receive events from Call

class MyCallCallback(pj.CallCallback):

   def __init__(self, call=None):
       pj.CallCallback.__init__(self, call)
   # Notification when call state has changed
   def on_state(self):
       print "Call is ", self.call.info().state_text,
       print "last code =", self.call.info().last_code, 
       print "(" + self.call.info().last_reason + ")"
       
   # Notification when call's media state has changed.
   def on_media_state(self):
       global lib
       if self.call.info().media_state == pj.MediaState.ACTIVE:
           # Connect the call to sound device
           call_slot = self.call.info().conf_slot
           lib.conf_connect(call_slot, 0)
           lib.conf_connect(0, call_slot)
           print "Hello world, I can talk!"


  1. Check command line argument

if len(sys.argv) != 2:

   print "Usage: simplecall.py <dst-URI>"
   sys.exit(1)

try:

   # Create library instance
   lib = pj.Lib()
   # Init library with default config
   lib.init(log_cfg = pj.LogConfig(level=3, callback=log_cb))
   # Create UDP transport which listens to any available port
   transport = lib.create_transport(pj.TransportType.UDP)
   
   # Start the library
   lib.start()
   # Create local/user-less account
   acc = lib.create_account_for_transport(transport)
   # Make call
   call = acc.make_call(sys.argv[1], MyCallCallback())
   # Wait for ENTER before quitting
   print "Press <ENTER> to quit"
   input = sys.stdin.readline().rstrip("\r\n")
   # We're done, shutdown the library
   lib.destroy()
   lib = None

except pj.Error, e:

   print "Exception: " + str(e)
   lib.destroy()
   lib = None
   sys.exit(1)

</syntaxhighlight>

  • Result
pchero@mywork:~/workspace/Study/Program/pjsip/scripts/pjsip_samples$ python simple.py sip:201@127.0.0.1
14:54:35.316 os_core_unix.c !pjlib 2.3 for POSIX initialized
14:54:35.316 sip_endpoint.c  .Creating endpoint instance...
14:54:35.316          pjlib  .select() I/O Queue created (0x1ff0250)
14:54:35.316 sip_endpoint.c  .Module "mod-msg-print" registered
14:54:35.316 sip_transport.  .Transport manager created.
14:54:35.316   pjsua_core.c  .PJSUA state changed: NULL --> CREATED
ALSA lib pcm_dsnoop.c:618:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
bt_audio_service_open: connect() failed: Connection refused (111)
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
14:54:35.340   pjsua_core.c  .pjsua version 2.3 for Linux-3.13.0.43/x86_64/glibc-2.19 initialized
Expression 'paInvalidSampleRate' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2048
Expression 'PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture, &realSr )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2719
Expression 'PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer, &inputLatency, &outputLatency, &hostBufferSizeMode )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2843
Expression 'paInvalidSampleRate' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2048
Expression 'PaAlsaStreamComponent_InitialConfigure( &self->capture, inParams, self->primeBuffers, hwParamsCapture, &realSr )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2719
Expression 'PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer, &inputLatency, &outputLatency, &hostBufferSizeMode )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2843
Call is  CALLING last code = 0 ()
Press <ENTER> to quit

Call is  DISCONNCTD last code = 503 (Service Unavailable)
python: ../src/pjsua-lib/pjsua_call.c:1862: pjsua_call_get_user_data: Assertion `call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls' failed.
Aborted (core dumped)

Basic concepts

Asynchronous operations

Pjsip 에서는 Sending/Receiving 관련 항목들은 모두 Async 로 동작한다. 무슨뜻인가 하면, Sending/Receiving 함수를 호출하면 바로 리턴이 돌아온다는 뜻이다. 그리고 실제로 동작잉 끝났을 경우, 함께 등록한 Callback 함수가 동작하게 된다.

Account 클래스의 make_call() 메소드를 예로 들어보자. 이 함수는 외부로 발신할때 사용하는 함수이다. 이 함수를 호출 후, 정상적인 Return 을 받았을 때, 정상적으로 콜이 발신되었다는 것을 의미하지는 않는다. 단지 정상적으로 initiated 되었다는 것을 의미할 뿐이다. 실제로는 initiated 후 진행되는 사항들이 많을 것이다. 나중에 정상적인 발신이 되었을 경우/진행사항을 확인할 경우, CallCallback 클래스의 on_state() 메소드를 통해서 이를 알아볼 수 있다. <syntaxhighlight lang=python> make_call(self, dst_uri, cb=None, hdr_list=None)

   Make outgoing call to the specified URI.
    
   Keyword arguments:
   dst_uri  -- Destination SIP URI.
   cb       -- CallCallback instance to be installed to the newly
               created Call object. If this CallCallback is not
               specified (i.e. None is given), it must be installed
               later using call.set_callback().
   hdr_list -- Optional list of headers to be sent with outgoing
               INVITE
    
   Return:
       Call instance.

</syntaxhighlight>

Relationship between objects and handles

Pjsua 에서는 Account, call, buddy 와 관련된 항목들을 handle 로써 관리한다. 그리고 Python 모듈에서는 이를 다시 Class로 감싸는 형식으로 되어있다. 때문에 아래와 같이 단순히 해당 Object 들을 delete 하는 것 만으로는 정상적인 객체 삭제가 이루어지지 않는다. <syntaxhighlight lang=python> del acc del call del buddy </syntaxhighlight>

따라서, 해당 Object들을 삭제하고 싶다면 다음과 같이 해야한다. <syntaxhighlight lang=python> acc.delete() del acc call.hangup() del call buddy.delete del buddy </syntaxhighlight>

The main classes

Lib class
Library 의 main class 이다. 반드시 단 하나의 객체만이 생성되도록 해야한다. 생성된 객체를 초기화하고 프로그램을 시작하게 된다. 또한 다른 객체(accounts, transports)들도 여기로부터 생성된다.

Transport class
설명 필요

Account class
Endpoint 정보를 식별하는(계정 정보) 클래스. 최소


References

<references />