Pjsip-pjsua python: Difference between revisions
(Created page with " == Overview == Psjua 는 그자체로도 이미 훌륭한 SIP client 이지만 본격적인 진가는 지원 API 가 아닐까 싶다. C/Python 으로 library 모듈을 지...") |
No edit summary |
||
Line 29: | Line 29: | ||
=== Others === | === Others === | ||
자세한 설치 내용은 이곳<ref>http://trac.pjsip.org/repos/wiki/Python_SIP/Build_Install</ref>을 참조하자. | 자세한 설치 내용은 이곳<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 | |||
# Logging callback | |||
def log_cb(level, str, len): | |||
print str, | |||
# 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!" | |||
# 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 | |||
<pre> | |||
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) | |||
</pre> | |||
== 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'''<br> | |||
Library 의 main class 이다. 반드시 단 하나의 객체만이 생성되도록 해야한다. 생성된 객체를 초기화하고 프로그램을 시작하게 된다. 또한 다른 객체(accounts, transports)들도 여기로부터 생성된다. | |||
'''Transport class'''<br> | |||
설명 필요 | |||
'''Account class'''<br> | |||
Endpoint 정보를 식별하는(계정 정보) 클래스. 최소 | |||
== References == | == References == | ||
<references /> | <references /> |
Revision as of 15:31, 12 January 2015
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
- Logging callback
def log_cb(level, str, len):
print str,
- 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!"
- 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 />