Asterisk прекращает передачу данных RTP - PullRequest
1 голос
/ 24 мая 2011

Я занимаюсь разработкой SIP-контроллера на Java с использованием NIST-реализации JAIN SIP API.

У меня проблемы с вызовом с моего SIP-контроллера на программный телефон через Asterisk. Если я звоню на софтфон напрямую (не через Asterisk), используя его IP-адрес и номер порта, все работает нормально. Вызов устанавливается, программный телефон слышит звук (данные RTP), который я отправляю, и я могу получить звук, который он мне отправляет.

Однако, когда я звоню на тот же программный телефон через Asterisk, вызов устанавливается, и я начинаю получать данные RTP с программного телефона (через Asterisk). Теперь мой поток отправки требует немного времени для настройки, но пока он настраивается, я получаю данные RTP с программного телефона. Проблема в том, что как только мой поток отправки инициализируется и начинает передавать данные RTP, я прекращаю получать данные RTP с программного телефона! В результате после установления вызова я слышу софтфон максимум полсекунды или секунды, а затем ничего. На этом этапе софтфон может слышать мои исходящие RTP-данные, но я не слышу их.

Если я не начинаю передавать какие-либо данные RTP, я продолжаю получать данные RTP с программного телефона. Но как только я начинаю передачу, он перестает приходить!

В случае, если это помогает, вот тип SIP-диалога, который устанавливает вызов (>> указывает на исходящее сообщение, а << указывает на входящее сообщение): </p>

>> 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-данные.
  • На этом этапе я прекращаю получать RTP-данные при переходе через Asterisk. При прямом вызове софтфона все работает нормально.

Есть идеи?

Ответы [ 5 ]

1 голос
/ 25 мая 2011

Вы уверены, что ваша обработка для отправки части RTP правильна? Насколько я понимаю, должен быть один сокет FD для отправки и получения. Вы создаете новый сокет fd для отправки части и закрытия recv fd? Пожалуйста, проверьте и ответьте

Вы также можете иметь два сокета fds, один для получения, а другой для отправки. RTP RFC-3550 ничего не говорит о реализации.

0 голосов
/ 08 июня 2011

Я наконец-то решил эту проблему!Оказывается, проблема была не в сообщениях SIP, а в коде, который настраивал сеанс RTP.Я все еще не совсем уверен, что пошло не так, но кажется, что этот код работает, только когда программный телефон вызывается напрямую (то есть не через УАТС) или когда программный телефон находится на том же 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);  
}

Этот код был взят из книги по SIP-программированию на Java (я полагаю, что для сохранения репутации автора я не должен рассказывать, какойкнига, которая есть).

Когда я пошел посмотреть на javadoc класса RTPManager, я обнаружил в документации пример кода для настройки сеанса одноадресной рассылки и адаптировал его для своего сценария.Вот обновленный метод 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);
    }

Как вы можете видеть, этот код - хотя он использует те же классы - довольно сильно отличается от того, который я нашел в книге (что делает его труднымопределить в чем проблема) но работает отлично!

0 голосов
/ 06 июня 2011

Для справки, я решил попробовать другую АТС. Я скачал и установил 3CX Phone System, и с этой УАТС все работает отлично!

Теперь клиент для бета-версии использует Patton на своем сайте, поэтому я просто надеюсь, что эта проблема связана с нашей настройкой Asterisk и что она там не проявится.

0 голосов
/ 25 мая 2011

Похоже, что Asterisk может пытаться повторно ПРИГЛАСИТЬ ваш вызов, чтобы он передавался напрямую между вашим SIP-приложением и программным телефоном. Преимущество Asterisk в том, что он делает медиа-путь более эффективным, сервер Asterisk больше не будет связывать медиа-вызовы только сигнализацией. Недостатком является то, что это может вызвать проблемы с прохождением RTP, если задействованы NAT или пользовательский агент SIP не поддерживает повторные INVITE, что может быть в случае с вашим.

Если это проблема повторного ПРИГЛАШЕНИЯ, то сначала вы должны увидеть дополнительный запрос INVITE, поступивший в ваше приложение SIP или на консоль Asterisk с помощью отладки SIP. Во-вторых, вы можете остановить Asterisk, делая повторные приглашения, установив canreinvite = no для используемой учетной записи SIP.

0 голосов
/ 25 мая 2011

вы должны попытаться добавить атрибут мультимедиа в ваше приглашение, если вы тоже используете ulaw, тогда вы можете добавить:

a=rtpmap:0 PCMU/8000

Также попробуйте выполнить более простой тест вместо вызова программного вызова:

301,1,Answer
301,2,Echo

Echo перехватит поток rtp с вашего клиента и отправит его вам.Если все работает нормально, вы можете позвонить между двумя работающими программными телефонами и сравнить их с вашим клиентом.Также, если возможно, попытайтесь опубликовать свой диалплан и конфигурацию обоих пользователей.(подсказка samll: если вы включите canreinvite = yes или directrtpsetup = yes для обоих пользователей, они смогут обмениваться потоком rtp напрямую между собой вместо использования звездочки в качестве моста)

...