Asterisk 停止传输 RTP 数据
我正在使用 JAIN SIP API 的 NIST 实现在 Java 中开发 SIP 控制器。
我在通过 Asterisk 从 SIP 控制器向软件电话拨打电话时遇到问题。 如果我使用 IP 地址和端口号直接呼叫软件电话(而不是通过 Asterisk),则一切正常。呼叫建立,软件电话听到我发送的音频(RTP 数据),并且我可以接收它发送给我的音频。
但是,当我通过 Asterisk 呼叫同一个软件电话时,呼叫建立,并且我开始从软件电话接收 RTP 数据(通过 Asterisk)。现在,我的发送流需要一些时间来设置,但在配置时我会从软件电话接收 RTP 数据。问题是,一旦我的发送流初始化并开始传输 RTP 数据,我就停止从软件电话接收 RTP 数据!结果是,通话建立后,我听到软电话最多半秒或一秒,然后就没有声音了。在此阶段,软件电话可以听到我传出的 RTP 数据,但我听不到。
如果我不开始传输任何 RTP 数据,我会继续从软件电话接收 RTP 数据。但一旦我开始传送,它就停止了!
如果有帮助,这里是建立呼叫的 SIP 会话类型(>> 表示传出消息,<< 表示传入消息):
>> INVITE sip:301@asterisk SIP/2.0
Call-ID: [email protected]
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: [email protected]
CSeq: 1 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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: [email protected]
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: [email protected]
CSeq: 2 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,R EFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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: [email protected]
CSeq: 2 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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: [email protected]
CSeq: 2 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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:[email protected] SIP/2.0
Call-ID: [email protected]
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 数据。如果直接拨打软件电话,一切正常。
有什么想法吗?
I am developing a SIP controller in Java using the NIST implementation of the JAIN SIP API.
I am having trouble making a call from my SIP controller to a softphone via Asterisk.
If I call the softphone directly (not via Asterisk) using its IP address and port number, everything works fine. The call gets established, the softphone hears the audio (RTP data) I send it, and I can receive the audio that it sends me.
However, when I call the same softphone via Asterisk, the call gets established, and I start to receive RTP data from the softphone (via Asterisk). Now, my send stream takes a little while to set up, but while it is being configured I receive the RTP data from the softphone. The problem is that as soon as my send stream is initialized and starts to transmit RTP data, I stop receiving RTP data from the softphone! The result is that after the call is established, I hear the softphone for half a second or a second at most, and then nothing. At this stage the softphone can hear my outgoing RTP-data, but I cannot hear it.
If I don't start transmitting any RTP data, I keep on receiving RTP data from the softphone. But as soon as I start transmitting, it stops coming!
In case it helps, here is the type of SIP-conversation that establishes the call (>> indicates an outgoing message and << indicates indicates an incoming message):
>> INVITE sip:301@asterisk SIP/2.0
Call-ID: [email protected]
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: [email protected]
CSeq: 1 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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: [email protected]
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: [email protected]
CSeq: 2 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,R EFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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: [email protected]
CSeq: 2 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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: [email protected]
CSeq: 2 INVITE
User-Agent: Asterisk PBX (switchvox)
Allow: INVITE,ACK,CANCEL,OPTIONS,BYE,REFER,SUBSCRIBE,NOTIFY
Contact: <sip:[email protected]>
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:[email protected] SIP/2.0
Call-ID: [email protected]
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
Here is the code that sets up the RTP-session. First some declarations:
private RTPManager sessionManager = null;
private Processor processor = null;
private SendStream sendStream;`
The following method is called first:
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);
}
Then, to start playing the sound over RTP, this method is called:
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();
}
Here is the controllerUpdate() method of the controller listener:
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();
}
}
}
This is what basically happens after the ACK is sent:
- I create an RTP-session for transmitting and listening.
- I start initializing a processor for transmitting RTP.
- In the meanwhile I receive lots of RTP-data.
- The processor finishes initialization and I start sending RTP-data.
- At this stage I stop receiving RTP-data if going through Asterisk. If calling a softphone directly, everything works fine.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您确定您对 RTP 发送部分的处理正确吗?根据我的理解,应该有一个套接字fd既用于发送又用于接收。您是否正在为发送部分创建新的套接字文件描述符并关闭接收文件描述符?请检查并回复
您还可以有两个套接字fd,一个用于接收,另一个用于发送。 RTP RFC-3550 没有提及任何有关实现的内容。
Are you sure your handling for send part of RTP is correct? According to my understanding there should be one socket fd both for sending and receiving. Are you creating new socket fd for send part and closing recv fd? Please check and reply
You can also have two socket fds one for receiving and another for sending. RTP RFC-3550 doesnt say anything about implementation.
您应该尝试在邀请中添加媒体属性,如果您也使用 ulaw 那么您可以添加:
还尝试使用更简单的测试,而不是拨打软件电话:
Echo 将从您的客户端捕获 rtp 流并将其发回给你。如果一切正常,那么您可以在 2 个工作的软件电话之间拨打电话,并将痕迹与您的客户端进行比较。
另外,如果可能的话,尝试发布您的拨号计划和两个用户的配置。
(小提示:如果您为两个用户启用 canreinvite=yes 或 directrtpsetup=yes,他们将能够直接在彼此之间交换 rtp 流,而不是使用星号作为桥梁)
you should try to add media attribute in your invite, if you're using ulaw too then you can add:
Also try with a simpler test, instead of calling a softphone call:
Echo will capture the rtp stream from your client and send it back to you. If everything works fine then you can make a call between 2 working softphones and compare the traces with your client.
Also if possible try to post your dialplan and both users configuration.
(samll tip: if you enable canreinvite=yes or directrtpsetup=yes for both users they will be able to exchange rtp stream directly between each other instead of using asterisk as bridge)
听起来 Asterisk 可能正在尝试重新邀请您的呼叫,以便它直接在您的 SIP 应用程序和软件电话之间流动。 Asterisk 这样做的优点是它使媒体路径更加高效,Asterisk 服务器将不再仅桥接信令呼叫媒体。缺点是,如果涉及 NAT 或者 SIP 用户代理不支持重新邀请(您的情况可能就是这样),则可能会导致 RTP 通过时出现问题。
如果是重新 INVITE 问题,那么首先您应该能够看到额外的 INVITE 请求到达您的 SIP 应用程序或使用 SIP 调试的 Asterisk 控制台。其次,您可以通过在您正在使用的 SIP 帐户上设置 canreinvite=no 来阻止 Asterisk 进行重新邀请。
Sounds like Asterisk may be attempting to re-INVITE your call so that it flows directly between your SIP app and the softphone. The advantage of Asterisk doing that is it makes the media path more efficient, the Asterisk server will no longer be bridging the call media only the signalling. The disadvantage is it can cause problems getting the RTP through if there are NATs involved or if a SIP user agent didn't support re-INVITEs, which may be the case with yours.
If it is a re-INVITE issue then firstly you should be able to see the extra INVITE request arrive at your SIP app or on the Asterisk console using a SIP debug. Secondly you can stop Asterisk doing re-INVITEs by setting canreinvite=no on the SIP account you are using.
作为记录,我决定尝试不同的 PBX。我已经下载并安装了 3CX 电话系统,使用此 PBX 一切工作正常!
现在,测试版的客户端在他的站点上使用 Patton,所以我只希望这个问题是我们的 Asterisk 设置所特有的,并且不会在那里出现。
For the record, I've decided to try a different PBX. I've downloaded and installed the 3CX Phone System and with this PBX everything works perfectly!
Now, the client for the beta version uses Patton at his site, so I just hope that this problem is specific to our Asterisk setup, and that it won't manifest there.
我终于解决了这个问题!事实证明,问题不在于 SIP 消息,而在于设置 RTP 会话的代码。我仍然不太确定出了什么问题,但似乎此代码仅在直接调用软件电话(即不通过 PBX)或软件电话与 PBX 位于同一 IP 地址时才有效。
这是错误的代码:
该代码改编自一本关于 Java 中 SIP 编程的书(我想为了维护作者的声誉,我不应该分享那本书)。
当我查看 RTPManager 类的 javadoc 时,我在文档中发现了一些用于设置单播会话的示例代码,并根据我的场景进行了调整。这是更新后的 startMedia() 方法:
正如您所看到的,这段代码 - 尽管它使用相同的类 - 与我在书中找到的代码有很大不同(这使得很难确定问题是什么),但它工作得很好!
I've finally solved this problem! It turns out that the problem was not with the SIP messages, but with the code that set up the RTP session. I'm still not quite sure what went wrong, but it seems as though this code only works when the softphone is called directly (that is, not through a PBX) or when the softphone is on the same IP-address as the PBX.
This is the erroneous code:
This code was adapted from a book on SIP programming in Java (I guess that in order to preserve the author's reputation, I should not share which book that is).
When I went to look at the javadoc of the
RTPManager
class, I spotted some sample code in the documentation for setting up a unicast session and adapted it for my scenario. Here is the updatedstartMedia()
method that works:As you can see this code - although it uses the same classes - is quite different than that which I found in the book (which makes it hard to determine what the problem was), but it works perfectly!