【问题标题】:PJSUA2 - Python - How to handle callbacks and createRecorder?PJSUA2 - Python - 如何处理回调和 createRecorder?
【发布时间】:2019-05-30 05:08:07
【问题描述】:

通过 PJSIP 库站点中提供的 pygui 代码后,我将示例修改如下。我有两个问题

  1. 在每个回调函数中,最后我需要添加一个虚拟的 raise 异常,否则会出现异常。例如,如果我评论 onIncomingCall 的虚拟异常,来电将被断开并出现 500 错误。在 pyGUI 中,看起来 ttk.master.after() 起着至关重要的作用。当我在无头服务器中尝试它时(仅指 cli),不知道如何处理它?

  2. 当我尝试 createRecorder 时出现以下错误:

Traceback(最近一次调用最后一次):文件“pjsua2_cli_demo.py”,行 33、在onCallState中 self.recorder.createRecorder('xxxxxxxxxxx/PJSUA2/example/pygui/file.wav'); 文件“xxxxxxxxxxx/.local/lib/python3.6/site-packages/pjsua2.py”,行 4110,在创建记录器中 return _pjsua2.AudioMediaRecorder_createRecorder(self, file_name, enc_type, max_size, options) NotImplementedError: 错误的号码或类型 重载函数的参数 'AudioMediaRecorder_createRecorder'。可能的 C/C++ 原型有: pj::AudioMediaRecorder::createRecorder(pj::string const &,unsigned int,pj_ssize_t,unsigned int) pj::AudioMediaRecorder::createRecorder(pj::string const &,unsigned int,pj_ssize_t) pj::AudioMediaRecorder::createRecorder(pj::string const &,unsigned int) pj::AudioMediaRecorder::createRecorder(pj::string const &)

实际修改代码:

import pjsua2 as pj
import time
# Subclass to extend the Account and get notifications etc.

ep=None
# Call class
class Call(pj.Call):
    """
    High level Python Call object, derived from pjsua2's Call object.
    """
    def __init__(self, acc, peer_uri='', chat=None, call_id = pj.PJSUA_INVALID_ID):
        pj.Call.__init__(self, acc, call_id)
        self.acc = acc

        self.aud_med=pj.AudioMedia

    def onCallState(self, prm):
        ci = self.getInfo()
        self.connected = ci.state == pj.PJSIP_INV_STATE_CONFIRMED
        self.recorder=None
        if(self.connected ==True):
            player=pj.AudioMediaPlayer()
            #Play welcome message
            player.createPlayer('xxxxxxxxxxxxxx/PJSUA2/example/pygui/welcomeFull.wav');

            self.recorder=pj.AudioMediaRecorder()
            self.recorder.createRecorder('xxxxxxxxxxx/PJSUA2/example/pygui/file.wav', enc_type=0, max_size=0, options=0);
            i=0
            for media in ci.media:

                if (media.type == pj.PJMEDIA_TYPE_AUDIO):
                    self.aud_med = self.getMedia(i);
                    break;
                i=i+1;
            if self.aud_med!=None:
                # This will connect the sound device/mic to the call audio media
                mym= pj.AudioMedia.typecastFromMedia(self.aud_med)
                player.startTransmit( mym);
                #mym.startTransmit( self.recorder);        
        if(ci.state==pj.PJSIP_INV_STATE_DISCONNECTED):
            print(">>>>>>>>>>>>>>>>>>>>>>> Call disconnected")
            #mym= pj.AudioMedia.typecastFromMedia(self.aud_med)
            #mym.stopTransmit(self.recorder);
        raise Exception('onCallState done!')        


        if self.chat:
            self.chat.updateCallState(self, ci)

    def onCallMediaState(self, prm):
        ci = self.getInfo()
        for mi in ci.media:
            if mi.type == pj.PJMEDIA_TYPE_AUDIO and \
              (mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE or \
               mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD):
                if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold:
                    self.chat.addMessage(None, "'%s' sets call onhold" % (self.peerUri))
                    self.onhold = True
                elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold:
                    self.chat.addMessage(None, "'%s' sets call active" % (self.peerUri))
                    self.onhold = False
        raise Exception('onCallMediaState done!')        

class Account(pj.Account):
    def onRegState(self, prm):
        print ("***OnRegState: " + prm.reason)
    def onIncomingCall(self, prm):
        c = Call(self, call_id=prm.callId)
        call_prm = pj.CallOpParam()
        call_prm.statusCode = 180
        c.answer(call_prm)

        ci = c.getInfo()
        msg = "Incoming call  from  '%s'" % (ci.remoteUri)
        print(msg)
        call_prm.statusCode = 200
        c.answer(call_prm)
        raise Exception('onIncomingCall done!')        



# pjsua2 test function
def pjsua2_test():
    # Create and initialize the library
    ep_cfg = pj.EpConfig()
    ep_cfg.uaConfig.threadCnt = 0
    ep_cfg.uaConfig.mainThreadOnly = False
    ep = pj.Endpoint()
    ep.libCreate()
    ep.libInit(ep_cfg)

    # Create SIP transport. Error handling sample is shown
    sipTpConfig = pj.TransportConfig();
    sipTpConfig.port = 12345;
    tp=ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sipTpConfig);
    # Start the library
    ep.libStart();

    acfg = pj.AccountConfig();

    acfg.idUri = "sip:192.168.1.11:12345";

    # Create the account
    acc = Account();
    acc.create(acfg)


    while True:    
        ep.libHandleEvents(10)


    ep.libDestroy()
    del ep;

#
# main()
#
if __name__ == "__main__":
    pjsua2_test()

【问题讨论】:

  • 你能解决这个问题吗?
  • 没有。我切换到 C++。但我能够解决除 createRecorder 之外的所有问题。此外,我通过使用 ffmpeg 定向到文件进行了 POC。有兴趣可以上传到github分享。想法是创建一个虚拟音频设备并将其用作终端设备,并在 Linux 操作系统级别使用 ffmpeg 写入文件。
  • 我很想知道你是如何解决 onIncomingCall 上的回调问题的。你能在 github 上分享 POC 吗?
  • 不幸的是我的硬盘坏了。我能恢复的,我把它上传到github.com/sekarpdkt/misc。尚未测试(恢复后)。它还具有将音频发送到虚拟声音设备的代码,该设备将进一步使用 ffmpeg 将语音重定向到 wav 文件。你应该能够得到一个想法。

标签: python python-3.x pjsip pjsua2


【解决方案1】:

在编译 pjsua swig 之前,将以下行添加到 pjsip-apps/src/swig 文件夹中的 pjsua2.i

%inline %{
pj_ssize_t new_pj_ssize_t(int s) {
   return (pj_ssize_t) s;
}
%}

使用这个额外的函数,您可以创建一个 pj_ssize_t 类型的变量并将其传递给 createRecorder 函数。

max_size=pj.new_pj_ssize_t(0)
recorder.createRecorder(file_to_record, max_size=max_size)

基于this讨论的解决方法

【讨论】:

    猜你喜欢
    • 2019-10-16
    • 1970-01-01
    • 2017-11-06
    • 2015-06-23
    • 1970-01-01
    • 2011-02-26
    • 2015-11-13
    • 1970-01-01
    • 2016-06-25
    相关资源
    最近更新 更多