/* * TeleStax, Open Source Cloud Communications * Copyright 2011-2017, Telestax Inc and individual contributors * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.restcomm.media.pcap; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URL; import java.nio.channels.DatagramChannel; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.restcomm.media.network.netty.NettyNetworkManager; import org.restcomm.media.network.netty.channel.NettyNetworkChannelGlobalContext; import org.restcomm.media.pcap.AsyncPcapChannel; import org.restcomm.media.pcap.AsyncPcapChannelHandler; import org.restcomm.media.pcap.PcapPacketEncoder; import org.restcomm.media.pcap.PcapPlayer; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; import io.netty.bootstrap.Bootstrap; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioDatagramChannel; /** * @author Henrique Rosa (henrique.rosa@telestax.com) * */ public class PcapPlayerTest { private ScheduledThreadPoolExecutor executor; private ListeningScheduledExecutorService scheduler; private NioEventLoopGroup eventGroup; private Bootstrap bootstrap; private NettyNetworkManager networkManager; private AsyncPcapChannel channel; private PcapPlayer player; private DatagramChannel remotePeer; @Before public void before() { this.executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5); this.executor.prestartAllCoreThreads(); this.executor.setRemoveOnCancelPolicy(true); this.scheduler = MoreExecutors.listeningDecorator(executor); this.eventGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), scheduler); this.bootstrap = new Bootstrap().channel(NioDatagramChannel.class).group(eventGroup); this.networkManager = new NettyNetworkManager(bootstrap); } @SuppressWarnings("unchecked") @After public void after() { if (player != null) { if (player.isPlaying()) { player.stop(); } player = null; } if (channel != null) { if (channel.isOpen()) { channel.close(mock(FutureCallback.class)); } channel = null; } if(remotePeer != null) { try { remotePeer.close(); } catch (IOException e) { e.printStackTrace(); } } this.networkManager = null; this.bootstrap = null; if(this.eventGroup != null) { if(!this.eventGroup.isShutdown()) { this.eventGroup.shutdownGracefully(0L, 0L, TimeUnit.NANOSECONDS); } this.eventGroup = null; } if(this.scheduler != null) { if(!this.scheduler.isShutdown()) { scheduler.shutdown(); } scheduler = null; } if(this.executor != null) { if(!this.executor.isShutdown()) { this.executor.shutdown(); } this.executor = null; } } private DatagramChannel openRemotePeer(SocketAddress address) throws IOException { DatagramChannel datagramChannel = DatagramChannel.open(); try { datagramChannel.bind(address); } catch (Exception e) { datagramChannel.close(); throw e; } return datagramChannel; } @SuppressWarnings("unchecked") @Test public void testPlayHashRfc2833() throws Exception { // given int rtpEventPacketCount = 35; int rtpEventPacketLength = 24; int rtpPacketCount = 39; int rtpPacketLength = 180; long rtpStreamDuration = 900; int totalOctets = (rtpEventPacketLength * rtpEventPacketCount) + (rtpPacketCount * rtpPacketLength); int totalPackets = rtpEventPacketCount + rtpPacketCount; InetSocketAddress localAddress = new InetSocketAddress("127.0.0.1", 64000); InetSocketAddress remoteAddress = new InetSocketAddress("127.0.0.1", 65000); String filepath = "dtmf-oob-one-hash.cap.gz"; PcapPacketEncoder packetEncoder = new PcapPacketEncoder(); AsyncPcapChannelHandler channelInitializer = new AsyncPcapChannelHandler(packetEncoder); bootstrap.handler(channelInitializer); NettyNetworkChannelGlobalContext context = new NettyNetworkChannelGlobalContext(networkManager); channel = new AsyncPcapChannel(context); remotePeer = openRemotePeer(remoteAddress); player = new PcapPlayer(channel, scheduler); FutureCallback<Void> openCallback = mock(FutureCallback.class); FutureCallback<Void> bindCallback = mock(FutureCallback.class); FutureCallback<Void> connectCallback = mock(FutureCallback.class); // when channel.open(openCallback); verify(openCallback, timeout(100)).onSuccess(null); channel.bind(localAddress, bindCallback); verify(bindCallback, timeout(100)).onSuccess(null); channel.connect(remoteAddress, connectCallback); verify(connectCallback, timeout(100)).onSuccess(null); URL pcap = PcapPlayerTest.class.getResource(filepath); player.play(pcap); // then Assert.assertTrue(player.isPlaying()); Thread.sleep(rtpStreamDuration); Assert.assertFalse(player.isPlaying()); Assert.assertEquals(totalPackets, player.countPacketsSent()); Assert.assertEquals(totalOctets, player.countOctetsSent()); } @SuppressWarnings("unchecked") @Test public void testPlayHashThenHashRfc2833() throws Exception { // given int rtpEventPacketCount = 72; int rtpEventPacketLength = 24; int rtpPacketCount = 429; int rtpPacketLength = 180; long rtpStreamDuration = 9000; int totalOctets = (rtpEventPacketLength * rtpEventPacketCount) + (rtpPacketCount * rtpPacketLength); int totalPackets = rtpEventPacketCount + rtpPacketCount; InetSocketAddress localAddress = new InetSocketAddress("127.0.0.1", 64000); InetSocketAddress remoteAddress = new InetSocketAddress("127.0.0.1", 65000); String filepath = "dtmf-oob-two-hash.cap.gz"; PcapPacketEncoder packetEncoder = new PcapPacketEncoder(); AsyncPcapChannelHandler channelInitializer = new AsyncPcapChannelHandler(packetEncoder); bootstrap.handler(channelInitializer); NettyNetworkChannelGlobalContext context = new NettyNetworkChannelGlobalContext(networkManager); channel = new AsyncPcapChannel(context); remotePeer = openRemotePeer(remoteAddress); player = new PcapPlayer(channel, scheduler); FutureCallback<Void> openCallback = mock(FutureCallback.class); FutureCallback<Void> bindCallback = mock(FutureCallback.class); FutureCallback<Void> connectCallback = mock(FutureCallback.class); // when channel.open(openCallback); verify(openCallback, timeout(100)).onSuccess(null); channel.bind(localAddress, bindCallback); verify(bindCallback, timeout(100)).onSuccess(null); channel.connect(remoteAddress, connectCallback); verify(connectCallback, timeout(100)).onSuccess(null); URL pcap = PcapPlayerTest.class.getResource(filepath); player.play(pcap); // then Assert.assertTrue(player.isPlaying()); Thread.sleep(rtpStreamDuration); Assert.assertFalse(player.isPlaying()); Assert.assertEquals(totalPackets, player.countPacketsSent()); Assert.assertEquals(totalOctets, player.countOctetsSent()); } @SuppressWarnings("unchecked") @Test public void testGigasetN510IpProRfc2833() throws Exception { // given int rtpEventPacketCount = 70; int rtpEventPacketLength = 24; int rtpPacketCount = 2494; int rtpPacketLength = 180; long rtpStreamDuration = 50000; int totalOctets = (rtpEventPacketLength * rtpEventPacketCount) + (rtpPacketCount * rtpPacketLength); int totalPackets = rtpEventPacketCount + rtpPacketCount; InetSocketAddress localAddress = new InetSocketAddress("127.0.0.1", 64000); InetSocketAddress remoteAddress = new InetSocketAddress("127.0.0.1", 65000); String filepath = "gigaset-n510-ip-pro-rfc2833.pcap"; PcapPacketEncoder packetEncoder = new PcapPacketEncoder(); AsyncPcapChannelHandler channelInitializer = new AsyncPcapChannelHandler(packetEncoder); bootstrap.handler(channelInitializer); NettyNetworkChannelGlobalContext context = new NettyNetworkChannelGlobalContext(networkManager); channel = new AsyncPcapChannel(context); remotePeer = openRemotePeer(remoteAddress); player = new PcapPlayer(channel, scheduler); FutureCallback<Void> openCallback = mock(FutureCallback.class); FutureCallback<Void> bindCallback = mock(FutureCallback.class); FutureCallback<Void> connectCallback = mock(FutureCallback.class); // when channel.open(openCallback); verify(openCallback, timeout(100)).onSuccess(null); channel.bind(localAddress, bindCallback); verify(bindCallback, timeout(100)).onSuccess(null); channel.connect(remoteAddress, connectCallback); verify(connectCallback, timeout(100)).onSuccess(null); URL pcap = PcapPlayerTest.class.getResource(filepath); player.play(pcap); // then Assert.assertTrue(player.isPlaying()); Thread.sleep(rtpStreamDuration); Assert.assertFalse(player.isPlaying()); Assert.assertEquals(totalPackets, player.countPacketsSent()); Assert.assertEquals(totalOctets, player.countOctetsSent()); } }