/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2014, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
package org.restcomm.media.rtp.channels;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.restcomm.media.network.deprecated.RtpPortManager;
import org.restcomm.media.network.deprecated.UdpManager;
import org.restcomm.media.rtcp.RtcpChannel;
import org.restcomm.media.rtp.ChannelsManager;
import org.restcomm.media.rtp.RtpChannel;
import org.restcomm.media.rtp.RtpClock;
import org.restcomm.media.rtp.channels.AudioChannel;
import org.restcomm.media.rtp.crypto.DtlsSrtpServer;
import org.restcomm.media.rtp.crypto.DtlsSrtpServerProvider;
import org.restcomm.media.rtp.sdp.SdpFactory;
import org.restcomm.media.rtp.statistics.RtpStatistics;
import org.restcomm.media.scheduler.Clock;
import org.restcomm.media.scheduler.PriorityQueueScheduler;
import org.restcomm.media.scheduler.Scheduler;
import org.restcomm.media.scheduler.ServiceScheduler;
import org.restcomm.media.scheduler.WallClock;
import org.restcomm.media.sdp.fields.MediaDescriptionField;
import org.restcomm.media.sdp.format.AVProfile;
import org.restcomm.media.sdp.format.RTPFormats;
/**
*
* @author Henrique Rosa (henrique.rosa@telestax.com)
*
*/
public class MediaChannelTest {
private final PriorityQueueScheduler mediaScheduler;
private final Scheduler scheduler;
private final UdpManager udpManager;
private final ChannelsManager channelsManager;
private final Clock wallClock;
private final ChannelFactory factory;
private final AudioChannel localChannel;
private final AudioChannel remoteChannel;
private static final int cipherSuites[] = { 0xc030, 0xc02f, 0xc028, 0xc027, 0xc014, 0xc013, 0x009f, 0x009e, 0x006b, 0x0067,
0x0039, 0x0033, 0x009d, 0x009c, 0x003d, 0x003c, 0x0035, 0x002f, 0xc02b };
public MediaChannelTest() throws IOException {
// given
DtlsSrtpServerProvider mockedDtlsServerProvider = mock(DtlsSrtpServerProvider.class);
DtlsSrtpServer mockedDtlsSrtpServer = mock(DtlsSrtpServer.class);
// when
when(mockedDtlsServerProvider.provide()).thenReturn(mockedDtlsSrtpServer);
when(mockedDtlsSrtpServer.getCipherSuites()).thenReturn(cipherSuites);
// then
this.wallClock = new WallClock();
this.mediaScheduler = new PriorityQueueScheduler();
this.mediaScheduler.setClock(this.wallClock);
this.scheduler = new ServiceScheduler();
this.udpManager = new UdpManager(scheduler, new RtpPortManager(), new RtpPortManager());
this.channelsManager = new ChannelsManager(udpManager, mockedDtlsServerProvider);
this.channelsManager.setScheduler(this.mediaScheduler);
this.factory = new ChannelFactory();
this.localChannel = factory.buildAudioChannel();
this.remoteChannel = factory.buildAudioChannel();
}
@Before
public void before() throws InterruptedException {
this.mediaScheduler.start();
this.scheduler.start();
this.udpManager.start();
}
@After
public void after() {
this.mediaScheduler.stop();
this.udpManager.stop();
if(this.localChannel.isOpen()) {
this.localChannel.close();
}
if(this.remoteChannel.isOpen()) {
this.remoteChannel.close();
}
this.scheduler.stop();
}
@Test
public void testSipCallNonRtcpMux() throws IllegalStateException, IOException, InterruptedException {
/* GIVEN */
boolean rtcpMux = false;
/* WHEN */
// activate local channel and bind it to local address
// there will be two underlying channels for RTP and RTCP
localChannel.open();
localChannel.bind(false, false);
String localAddress = localChannel.rtpChannel.getLocalHost();
int localRtpPort = localChannel.rtpChannel.getLocalPort();
int localRtcpPort = localChannel.rtcpChannel.getLocalPort();
MediaDescriptionField audioOffer = SdpFactory.buildMediaDescription(localChannel, true);
// activate "remote" channel and bind it to local address
// there will be two underlying channels for RTP and RTCP
remoteChannel.open();
remoteChannel.bind(false, rtcpMux);
String remoteAddress = remoteChannel.rtpChannel.getLocalHost();
int remoteRtpPort = remoteChannel.rtpChannel.getLocalPort();
int remoteRtcpPort = remoteChannel.rtcpChannel.getLocalPort();
MediaDescriptionField audioAnswer = SdpFactory.buildMediaDescription(remoteChannel, false);
// ... remote peer receives SDP offer from local peer
// negotiate codecs with local peer
remoteChannel.negotiateFormats(audioOffer);
// connect to RTP and RTCP endpoints of local channel
remoteChannel.connectRtp(localAddress, localRtpPort);
remoteChannel.connectRtcp(localAddress, localRtcpPort);
// ... local peer receives SDP answer from remote peer
// negotiate codecs with remote peer
localChannel.negotiateFormats(audioAnswer);
// connect to RTP and RTCP endpoints of remote channel
localChannel.connectRtp(remoteAddress, remoteRtpPort);
localChannel.connectRtcp(remoteAddress, remoteRtcpPort);
// THEN
assertTrue(localChannel.isOpen());
assertTrue(localChannel.isAvailable());
assertFalse(localChannel.isRtcpMux());
assertEquals(remoteAddress, localChannel.rtpChannel.getRemoteHost());
assertEquals(remoteRtpPort, localChannel.rtpChannel.getRemotePort());
assertEquals(remoteAddress, localChannel.rtcpChannel.getRemoteHost());
assertEquals(remoteRtcpPort, localChannel.rtcpChannel.getRemotePort());
assertTrue(remoteChannel.isOpen());
assertTrue(remoteChannel.isAvailable());
assertFalse(remoteChannel.isRtcpMux());
assertEquals(localAddress, remoteChannel.rtpChannel.getRemoteHost());
assertEquals(localRtpPort, remoteChannel.rtpChannel.getRemotePort());
assertEquals(localAddress, remoteChannel.rtcpChannel.getRemoteHost());
assertEquals(localRtcpPort, remoteChannel.rtcpChannel.getRemotePort());
}
@Test
public void testSipCallWithRtcpMux() throws IllegalStateException, IOException, InterruptedException {
/* GIVEN */
boolean rtcpMux = true;
/* WHEN */
// activate local channel and bind it to local address
// there will be two underlying channels for RTP and RTCP
localChannel.open();
localChannel.bind(false, rtcpMux);
String localAddress = localChannel.rtpChannel.getLocalHost();
int localPort = localChannel.rtpChannel.getLocalPort();
MediaDescriptionField audioOffer = SdpFactory.buildMediaDescription(localChannel, true);
// activate "remote" channel and bind it to local address
// there will be two underlying channels for RTP and RTCP
remoteChannel.open();
remoteChannel.bind(false, rtcpMux);
String remoteAddress = remoteChannel.rtpChannel.getLocalHost();
int remotePort = remoteChannel.rtpChannel.getLocalPort();
MediaDescriptionField audioAnswer = SdpFactory.buildMediaDescription(remoteChannel, false);
// ... remote peer receives SDP offer from local peer
// negotiate codecs with local peer
remoteChannel.negotiateFormats(audioOffer);
// connect to RTP and RTCP endpoints of local channel
remoteChannel.connectRtp(localAddress, localPort);
remoteChannel.connectRtcp(localAddress, localPort);
// ... local peer receives SDP answer from remote peer
// negotiate codecs with remote peer
localChannel.negotiateFormats(audioAnswer);
// connect to RTP and RTCP endpoints of remote channel
localChannel.connectRtp(remoteAddress, remotePort);
localChannel.connectRtcp(remoteAddress, remotePort);
// THEN
assertTrue(localChannel.isOpen());
assertTrue(localChannel.isAvailable());
assertTrue(localChannel.isRtcpMux());
assertEquals(remoteAddress, localChannel.rtpChannel.getRemoteHost());
assertEquals(remotePort, localChannel.rtpChannel.getRemotePort());
assertFalse(localChannel.rtcpChannel.isOpen());
assertTrue(remoteChannel.isOpen());
assertTrue(remoteChannel.isAvailable());
assertTrue(remoteChannel.isRtcpMux());
assertEquals(localAddress, remoteChannel.rtpChannel.getRemoteHost());
assertEquals(localPort, remoteChannel.rtpChannel.getRemotePort());
assertFalse(remoteChannel.rtcpChannel.isOpen());
}
@Test
public void testSupportedCodecsFiltering() {
// given
final RTPFormats codecs = new RTPFormats(3);
codecs.add(AVProfile.audio.find(0));
codecs.add(AVProfile.audio.find(8));
codecs.add(AVProfile.audio.find(3));
codecs.add(AVProfile.audio.find(101));
final ChannelsManager channelProvider = mock(ChannelsManager.class);
final RtpChannel rtpChannel = mock(RtpChannel.class);
final RtcpChannel rtcpChannel = mock(RtcpChannel.class);
final Clock clock = mock(Clock.class);
// when
when(channelProvider.getCodecs()).thenReturn(codecs);
when(channelProvider.getRtpChannel(any(RtpStatistics.class), any(RtpClock.class), any(RtpClock.class))).thenReturn(rtpChannel);
when(channelProvider.getRtcpChannel(any(RtpStatistics.class))).thenReturn(rtcpChannel);
final AudioChannel audioChannel = new AudioChannel(clock, channelProvider);
RTPFormats supportedCodecs = audioChannel.getFormats();
// then
assertEquals(codecs, supportedCodecs);
}
/**
* Produces Media Channels
*
* @author Henrique Rosa
*
*/
private class ChannelFactory {
public AudioChannel buildAudioChannel() {
return new AudioChannel(wallClock, channelsManager);
}
}
}