【问题标题】:Asterisk stops transmitting RTP dataAsterisk 停止传输 RTP 数据
【发布时间】:2011-08-31 23:27:21
【问题描述】:

我正在使用 JAIN SIP API 的 NIST 实现在 Java 中开发一个 SIP 控制器。

我无法通过 Asterisk 从我的 SIP 控制器呼叫软件电话。 如果我使用其 IP 地址和端口号直接(而不是通过 Asterisk)呼叫软件电话,一切正常。呼叫建立,软件电话会听到我发送给它的音频(RTP 数据),并且我可以接收它发送给我的音频。

但是,当我通过 Asterisk 呼叫同一个软件电话时,呼叫建立,并且我开始从软件电话(通过 Asterisk)接收 RTP 数据。现在,我的发送流需要一些时间来设置,但在配置它时,我会从软件电话接收 RTP 数据。问题是,一旦我的发送流被初始化并开始传输 RTP 数据,我就停止从软件电话接收 RTP 数据!结果是通话建立后,我听到软电话半秒或最多一秒,然后什么也听不见。在这个阶段,网络电话可以听到我传出的 RTP 数据,但我听不到。

如果我不开始传输任何 RTP 数据,我会继续从网络电话接收 RTP 数据。但是一旦我开始传输,它就停止了!

如果有帮助,这里是建立呼叫的 SIP 对话类型(>> 表示传出消息,

>> INVITE sip:301@asterisk SIP/2.0  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
CSeq: 1 INVITE  
From: <sip:null>;tag=JqbJKA  
To: <sip:301@asterisk>  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bK34d24b3f748ac08a5ca46f500f110d38353436  
Max-Forwards: 70  
Contact: <sip:10.0.85.3:5060>  
Route: <sip:10.0.84.30;lr>  
Content-Type: application/sdp  
Content-Length: 106

v=0  
o=- 3515232260 3515232260 IN IP4 10.0.85.3  
s=-  
c=IN IP4 10.0.85.3  
t=0 0  
m=audio 42138 RTP/AVP 0  
a=rtpmap:0 PCMU/8000

<< SIP/2.0 407 Proxy Authentication Required  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bK34d24b3f748ac08a5ca46f500f110d38353436;received=10.0.85.3  
From: <sip:null>;tag=JqbJKA  
To: <sip:301@asterisk>;tag=as7077f414  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
CSeq: 1 INVITE  
User-Agent: Asterisk PBX (switchvox)  
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY  
Contact: <sip:301@10.0.84.30>  
Proxy-Authenticate: Digest realm="asterisk",nonce="4a1cbda4"  
Content-Length: 0


>> INVITE sip:301@asterisk SIP/2.0  
CSeq: 2 INVITE  
From: <sip:303@asterisk>;tag=JqbJKA  
To: <sip:301@asterisk>  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bKd1870f50e9fbf883b3e64fa3ef75dda9353436
Max-Forwards: 70  
Contact: <sip:10.0.85.3:5060>  
Route: <sip:10.0.84.30;lr>  
Proxy-Authorization: Digest username="303",realm="asterisk",nonce="4a1cbda4",response="249b2b7d7c0e7b54499c632ba410365c",algorithm=MD5,uri="sip:301@asterisk",nc=00000001  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
Content-Type: application/sdp  
Content-Length: 106

v=0  
o=- 3515232260 3515232260 IN IP4 10.0.85.3  
s=-  
c=IN IP4 10.0.85.3  
t=0 0  
m=audio 42138 RTP/AVP 0  
a=rtpmap:0 PCMU/8000`

`<< SIP/2.0 100 Trying  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bKd1870f50e9fbf883b3e64fa3ef75dda9353436;received=10.0.85.3  
From: <sip:303@asterisk>;tag=JqbJKA  
To: <sip:301@asterisk>  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
CSeq: 2 INVITE  
User-Agent: Asterisk PBX (switchvox)  
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,R EFER,SUBSCRIBE,NOTIFY  
Contact: <sip:301@10.0.84.30>  
Content-Length: 0


`<< SIP/2.0 180 Ringing  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bKd1870f50e9fbf883b3e64fa3ef75dda9353436;received=10.0.85.3  
From: <sip:303@asterisk>;tag=JqbJKA  
To: <sip:301@asterisk>;tag=as00faa25e  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
CSeq: 2 INVITE  
User-Agent: Asterisk PBX (switchvox)  
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY  
Contact: <sip:301@10.0.84.30>  
Content-Length: 0`


<< SIP/2.0 200 OK  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bKd1870f50e9fbf883b3e64fa3ef75dda9353436;received=10.0.85.3  
From: <sip:303@asterisk>;tag=JqbJKA  
To: <sip:301@asterisk>;tag=as00faa25e  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
CSeq: 2 INVITE  
User-Agent: Asterisk PBX (switchvox)  
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY  
Contact: <sip:301@10.0.84.30>  
Content-Type: application/sdp  
Content-Length: 154

v=0  
o=root 2593 2593 IN IP4 10.0.84.30  
s=session  
c=IN IP4 10.0.84.30  
t=0 0  
m=audio 10294 RTP/AVP 0  
a=rtpmap:0 PCMU/8000  
a=silenceSupp:off - - - -

>> ACK sip:301@10.0.84.30 SIP/2.0  
Call-ID: 8b92ba1ca9c922bcd266dce086596ce4@10.0.85.3  
CSeq: 2 ACK  
Via: SIP/2.0/UDP 10.0.85.3:5060;branch=z9hG4bK7e16ebc0de9c6eaf901db0e2e58f495f353436  
From: <sip:303@asterisk>;tag=JqbJKA  
To: <sip:301@asterisk>;tag=as00faa25e  
Max-Forwards: 70  
Contact: <sip:10.0.85.3:5060>  
Content-Length: 0

这是设置 RTP 会话的代码。首先是一些声明:

private RTPManager sessionManager = null;  
private Processor processor = null;  
private SendStream sendStream;`

首先调用以下方法:

public void startMedia(String peerIp,int peerPort,int receivePort,String format) throws IOException,MediaException,InvalidSessionAddressException  
{  
    stopMedia();  
    this.format = format;  
    RTPSessionMgr rtpSessionMgr = new RTPSessionMgr();  
    rtpSessionMgr.initSession(new SessionAddress(),null,0.05,0.25);  
    InetAddress localhost = InetAddress.getLocalHost();  
    SessionAddress localAddr = new SessionAddress(localhost,receivePort,localhost,receivePort + 1);  
    InetAddress destAddr = InetAddress.getByName(peerIp);  
    rtpSessionMgr.startSession(localAddr,localAddr,new SessionAddress(destAddr,peerPort,destAddr,peerPort + 1),null);  
    sessionManager = rtpSessionMgr;  
    for (ReceiveStreamListener nextListener : receiveStreamListeners)  
        sessionManager.addReceiveStreamListener(nextListener);  
}

然后,要开始通过 RTP 播放声音,调用此方法:

public void transmitSound(DataSource ds) throws NoProcessorException,IOException,UnsupportedFormatException,NotRealizedError  
{  
    stopTransmittingSound();  
    processor = Manager.createProcessor(ds);  
    for (ControllerListener nextListener : controllerListeners)  
        processor.addControllerListener(nextListener);  
    processor.addControllerListener(myControllerListener);  
    processor.configure();  
}

这里是控制器监听器的controllerUpdate()方法:

public void controllerUpdate(ControllerEvent event)  
    {  
        if (processor.getState()==Processor.Configured)  
        {  
            processor.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP));  
            processor.getTrackControls()[0].setFormat(new AudioFormat(format,8000,8,1));  
            processor.realize();  
        }  
        else if (processor.getState()==Processor.Realized)  
        {  
            try  
            {  
                sendStream = sessionManager.createSendStream(processor.getDataOutput(),0);  
                sendStream.start();  
                processor.start();  
            }  
            catch (IOException e)  
            {  
                e.printStackTrace();  
            }  
            catch (UnsupportedFormatException e)  
            {  
                e.printStackTrace();  
            }  
            catch (NotRealizedError e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }

这基本上是在发送 ACK 之后发生的:

  • 我创建了一个 RTP 会话用于传输和收听。
  • 我开始初始化用于传输 RTP 的处理器。
  • 与此同时,我收到大量 RTP 数据。
  • 处理器完成初始化,我开始发送 RTP 数据。
  • 在这个阶段,如果通过 Asterisk,我将停止接收 RTP 数据。如果直接拨打网络电话,一切正常。

有什么想法吗?

【问题讨论】:

  • 我仍然无法弄清楚为什么会出现这个问题。请告诉您是否知道我可能做错了什么。

标签: sip asterisk rtp sdp


【解决方案1】:

您确定您对 RTP 发送部分的处理是正确的吗?根据我的理解,应该有一个用于发送和接收的套接字 fd。您是否正在为发送部分创建新的套接字 fd 并关闭 recv fd?请查看并回复

您还可以有两个套接字 fd,一个用于接收,另一个用于发送。 RTP RFC-3550 没有说明任何关于实现的内容。

【讨论】:

  • 嗨,阿拉姆,为了发送和接收音频,我创建了一个 com.sun.media.rtp.RTPSessionMgr 对象。然后我配置一个处理器对象来播放声音。从处理器中,我通过其 getDataOutput() 方法获得一个 DataSource,并将其传递给会话管理器的 createSendStream() 方法,以获得我 start() 的 SendStream。使用单独的 RTPSessionMgr 进行发送和接收没有区别。我将在问题中添加设置它的代码。
【解决方案2】:

你应该尝试在你的邀请中添加媒体属性,如果你也在使用 ulaw 那么你可以添加:

a=rtpmap:0 PCMU/8000

也可以尝试更简单的测试,而不是拨打软件电话:

301,1,Answer
301,2,Echo

Echo 将从您的客户端捕获 rtp 流并将其发送回给您。如果一切正常,那么您可以在 2 个正常工作的软电话之间进行通话,并将跟踪记录与您的客户进行比较。 此外,如果可能,请尝试发布您的拨号方案和两个用户的配置。 (小提示:如果您为两个用户启用 canreinvite=yes 或 directrtpsetup=yes,他们将能够在彼此之间直接交换 rtp 流,而不是使用星号作为桥接)

【讨论】:

  • 嗨,戴维,我已经添加了媒体属性(我已经更新了问题以反映这一点),但这并没有什么不同。我怎么打那个号码?发送INVITE 301,1,Answer SIP/2.0 是不合法的,因为地址包含非法字符。
  • 我无权访问 Asterisk 的配置参数,因此无法重新配置它以进行直接 RTP 设置。
【解决方案3】:

听起来像 Asterisk 可能正在尝试重新邀请您的呼叫,以便它直接在您的 SIP 应用程序和软件电话之间流动。 Asterisk 这样做的好处是它使媒体路径更有效,Asterisk 服务器将不再只桥接呼叫媒体信令。缺点是,如果涉及 NAT 或 SIP 用户代理不支持重新邀请(您的情况可能就是这种情况),则可能会导致通过 RTP 出现问题。

如果是重新邀请问题,那么首先您应该能够看到额外的邀请请求到达您的 SIP 应用程序或使用 SIP 调试的 Asterisk 控制台。其次,您可以通过在您正在使用的 SIP 帐户上设置 canreinvite=no 来阻止 Asterisk 重新邀请。

【讨论】:

  • 嗨 sipwiz,我没有收到来自 Asterisk 的重新邀请。
【解决方案4】:

为了记录,我决定尝试不同的 PBX。我已经下载并安装了 3CX 电话系统,使用这个 PBX 一切正常!

现在,Beta 版的客户端在他的站点上使用 Patton,所以我只希望这个问题是我们的 Asterisk 设置所特有的,并且不会出现在那里。

【讨论】:

  • 我注意到,当我使用不同的 IP 地址呼叫软电话时,我遇到了与 RTP 类似的问题。事实证明,设置似乎只是因为软件电话与 PBX 在同一台计算机上运行。不过不用担心,我终于解决了这个问题。请参阅标记为正确的该问题的答案。
【解决方案5】:

我终于解决了这个问题!事实证明,问题不在于 SIP 消息,而在于设置 RTP 会话的代码。我仍然不太确定出了什么问题,但似乎此代码仅在直接呼叫软件电话(即不通过 PBX)或软件电话与 PBX 位于同一 IP 地址时才有效。

这是错误的代码:

public void startMedia(String peerIp,int peerPort,int receivePort,String format) throws IOException,MediaException,InvalidSessionAddressException  
{  
    stopMedia();  
    this.format = format;  
    RTPSessionMgr rtpSessionMgr = new RTPSessionMgr();  
    rtpSessionMgr.initSession(new SessionAddress(),null,0.05,0.25);  
    InetAddress localhost = InetAddress.getLocalHost();  
    SessionAddress localAddr = new SessionAddress(localhost,receivePort,localhost,receivePort + 1);  
    InetAddress destAddr = InetAddress.getByName(peerIp);  
    rtpSessionMgr.startSession(localAddr,localAddr,new SessionAddress(destAddr,peerPort,destAddr,peerPort + 1),null);  
    sessionManager = rtpSessionMgr;  
    for (ReceiveStreamListener nextListener : receiveStreamListeners)  
        sessionManager.addReceiveStreamListener(nextListener);  
}

这段代码改编自一本关于Java SIP编程的书(我想为了维护作者的声誉,我不应该分享那本书)。

当我查看 RTPManager 类的 javadoc 时,我在文档中发现了一些用于设置单播会话的示例代码,并针对我的场景进行了调整。这是更新后的startMedia() 方法:

public void startMedia(String peerIp,int peerPort,int receivePort,String format,int sampleRate,int sampleSizeInBits) throws IOException,MediaException,InvalidSessionAddressException
    {
        stopMedia();
        this.format = format;
        this.sampleRate = sampleRate;
        this.sampleSizeInBits = sampleSizeInBits;

sessionManager = RTPManager.newInstance();
        SessionAddress localAddress = new SessionAddress(InetAddress.getLocalHost(),receivePort);
        sessionManager.initialize(localAddress);
        for (ReceiveStreamListener nextListener : receiveStreamListeners)
            sessionManager.addReceiveStreamListener(nextListener);
        InetAddress ipAddress = InetAddress.getByName(peerIp);
        SessionAddress remoteAddress = new SessionAddress(ipAddress,peerPort);
        sessionManager.addTarget(remoteAddress);
    }

如您所见,这段代码虽然使用了相同的类,但与我在书中找到的代码完全不同(这使得很难确定问题所在),但它运行良好!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    相关资源
    最近更新 更多