/* / Copyright (C) 2009 Risto Känsäkoski- Sesca ISW Ltd / / This file is part of SIP-Applet (www.sesca.com, www.purplescout.com) / / This program is free software; you can redistribute it and/or / modify it under the terms of the GNU General Public License / as published by the Free Software Foundation; either version 2 / 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 General Public License for more details. / / You should have received a copy of the GNU General Public License / along with this program; if not, write to the Free Software / Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.sesca.audio; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import local.net.RtpPacket; import org.zoolu.net.IpAddress; import org.zoolu.sip.message.Message; import org.zoolu.sip.provider.ConnectionIdentifier; import org.zoolu.sip.provider.SipStack; import org.zoolu.sip.provider.Transport; import org.zoolu.sip.provider.TransportListener; import com.sesca.misc.Logger; import com.sesca.voip.transport.HttpTunnelTransport; import com.sesca.voip.ua.UserAgent; public class TunnelSenderReceiver implements AudioSender, AudioReceiver, TransportListener{ AudioReceiverListener listener=null; int seqn=0; int time=0; long ssi = 0; int sentPacketCount = 0; byte[] buffer = new byte[160+12]; RtpPacket packet=null; boolean firstTime=true; UserAgent ua=null; Hashtable connections=null; String outbound_addr=null; int outbound_port=0; int nmax_connections=0; //private InputStream input_stream =null; //private int p_type; //private long frame_rate; //private int frame_size; //private boolean do_sync; //public SocketChannel audiochannel; //private OutputStream output_stream =null; //private AudioOutput ao; //RtpPacket[] buffer = null; //int bufferSize = 10; //int bpointer = 0; //int blockSize = 5; //private boolean audioPaused = false; long lastSequenceNumber=-1; long lastTimeStamp=-1; int receivedPacketCount=0; private int payloadType; public void onReceivedFrame(byte[] b) { if (packet==null) Logger.error("TunnelSenderReceiver.onReceivedFrame: packet==null"); int num=b.length; packet.setPayload(b, num); packet.setPayloadType(this.payloadType); packet.setSequenceNumber(seqn++); packet.setTimestamp(time); packet.setPayloadLength(num); time+=num; send(packet); } public void close() { for (Enumeration e=connections.elements(); e.hasMoreElements(); ) { HttpTunnelTransport co=(HttpTunnelTransport)e.nextElement(); co.halt(); Logger.debug("conn "+co.toString()+" halted"); } // TODO Auto-generated method stub } public void go() { // TODO Auto-generated method stub } public void init(AudioReceiverListener listener) { // TODO Auto-generated method stub this.listener=listener; } public void startTransport(){ Logger.info("TunnelSenderReceiver.startTransport: http tunnel"); ConnectionIdentifier conn_id=new ConnectionIdentifier("tcp",new IpAddress(ua.user_profile.tunnelServer),ua.user_profile.tunnelPort); if (!connections.containsKey(conn_id)) { HttpTunnelTransport conn=null; try { Logger.info("Luo HttpTunnelTransport:"+System.currentTimeMillis()); conn=new HttpTunnelTransport(new IpAddress(ua.user_profile.tunnelServer),ua.user_profile.tunnelPort,this); Logger.info("HttpTunnelTransport luotu:"+System.currentTimeMillis()); Logger.debug("conn="+conn); } catch (Exception e) { Logger.warning("connection setup FAILED"); e.printStackTrace(); } Logger.info("connection "+conn+" opened"); Logger.info("Adding connection: "+conn); Logger.info("Lisää pooliin:"+System.currentTimeMillis()); addConnection(conn); Logger.info("Lisätty pooliin:"+System.currentTimeMillis()); Logger.debug(connections); } else { } Logger.info("Hae poolista:"+System.currentTimeMillis()); HttpTunnelTransport conn=(HttpTunnelTransport)connections.get(conn_id); Logger.info("Haettu poolista:"+System.currentTimeMillis()); if (conn!=null) { try { Logger.info("Trying to open tunnel..."); Logger.info("Luo Http-viesti:"+System.currentTimeMillis()); Message msg = new Message("GET / HTTP/1.1\r\nUdpHost: "+IpAddress.getByName(outbound_addr)+":"+outbound_port+"\r\n\r\n"); Logger.info("Lähetä viesti:"+System.currentTimeMillis()); conn.sendMessage(msg.toString().getBytes(),true); Logger.info("Viesti lähetetty:"+System.currentTimeMillis()); Logger.info("Tunnel opened succesfully"); } catch (IOException e) { Logger.warning("Tunnel failed"); } } else { // this point has not to be reached Logger.warning("ERROR: conn "+conn_id+" not found: abort."); } Logger.info("TunnelSenderReceiver.startTransport exit"); } private void addConnection(HttpTunnelTransport conn) { Logger.info("addConnection: conn="+conn); ConnectionIdentifier conn_id=new ConnectionIdentifier(conn); Logger.info("addConnection: conn_id="+conn_id); if (connections.containsKey(conn_id)) { // remove the previous connection HttpTunnelTransport old_conn=(HttpTunnelTransport)connections.get(conn_id); old_conn.halt(); connections.remove(conn_id); } else if (connections.size()>=nmax_connections) { // remove the older unused connection long older_time=System.currentTimeMillis(); ConnectionIdentifier older_id=null; for (Enumeration e=connections.elements(); e.hasMoreElements(); ) { HttpTunnelTransport co=(HttpTunnelTransport)e.nextElement(); if (co.getLastTimeMillis()<older_time) older_id=new ConnectionIdentifier(co); } if (older_id!=null) removeConnection(older_id); } connections.put(conn_id,conn); conn_id=new ConnectionIdentifier(conn); conn=(HttpTunnelTransport)connections.get(conn_id); // DEBUG log: Logger.debug("active connenctions:"); for (Enumeration e=connections.keys(); e.hasMoreElements(); ) { ConnectionIdentifier id=(ConnectionIdentifier)e.nextElement(); } } public void onReceivedMessage(Transport transport, Message msg) { Logger.warning("Tänne ei pitäisi koskaan tulla"); // TODO Auto-generated method stub } public void onTransportTerminated(Transport transport, Exception error) { Logger.warning("Terminoitu"); if (ua!=null) ua.onTunnelTerminated(); // TODO Auto-generated method stub } private void removeConnection(ConnectionIdentifier conn_id) { Logger.info("removeConnection: "+conn_id); if (connections.containsKey(conn_id)) { HttpTunnelTransport conn=(HttpTunnelTransport)connections.get(conn_id); conn.halt(); connections.remove(conn_id); // DEBUG log: Logger.debug("active connenctions:"); for (Enumeration e=connections.elements(); e.hasMoreElements(); ) { HttpTunnelTransport co=(HttpTunnelTransport)e.nextElement(); Logger.debug("conn "+co.toString()); } } } private void init(String dest_addr, int dest_port) { Logger.paranoia("TunnelSenderReceiver.init"); if (nmax_connections<=0) nmax_connections=SipStack.default_nmax_connections; connections=new Hashtable(); this.outbound_addr=dest_addr; this.outbound_port=dest_port; //this.input_stream=input_stream; //this.p_type=payload_type; //this.frame_rate=frame_rate; //this.frame_size=frame_size; //this.do_sync=do_sync; //buffer = new RtpPacket[bufferSize]; startTransport(); } public TunnelSenderReceiver(String dest_addr, int dest_port, UserAgent agent) { Logger.paranoia("TunnelSenderReceiver constructed (1)"); this.ua=agent; packet=new RtpPacket(buffer,buffer.length); //this.audiochannel=audiochannel; init(dest_addr,dest_port); } public TunnelSenderReceiver(String dest_addr, int dest_port) { Logger.paranoia("TunnelSenderReceiver constructed (2)"); packet=new RtpPacket(buffer,buffer.length); init(dest_addr,dest_port); } /* public void setOutputStream(OutputStream os) { this.output_stream=os; } */ /* public void setAudioOutput(AudioOutput ao) { this.ao=ao; } */ public boolean send(RtpPacket packet) { Logger.hysteria("TunnelSenderReceiver.send (1)"); send2(packet.getPacket()); sentPacketCount++; //System.out.println("Sent packet count="+sentPacketCount); return true; } public boolean send(RtpPacket packet, RtpPacket packet2) { Logger.hysteria("TunnelSenderReceiver.send (2)"); send2(packet.getPacket(),packet2.getPacket()); return true; } /* public void run() { if (bpointer>=blockSize) { int size=0; for (int i=0;i<=bpointer-1;i++) { size+=buffer[i].getLength(); } byte[] block=new byte[size+4*(bpointer+1)]; int index=0; for (int i=0;i<=bpointer-1;i++) { byte[] temp=buffer[i].getPacket(); int length=temp.length; block[index]=(byte)((length >>> 0) & 0xFF); block[index+1]=(byte)((length >>> 8) & 0xFF); block[index+2]=(byte)((length >>> 16) & 0xFF); block[index+3]=(byte)((length >>> 24) & 0xFF); index+=4; for (int j=0;j<temp.length;j++) { block[index]=temp[j]; index++; } } long a=System.currentTimeMillis(); send2(block); long b=System.currentTimeMillis(); Logger.debug(b-a+""); bpointer=0; } } */ public boolean send2(byte[] packet) { Logger.hysteria("TunnelSenderReceiver.send2 (1)"); ConnectionIdentifier conn_id=new ConnectionIdentifier("tcp",new IpAddress(ua.user_profile.tunnelServer),ua.user_profile.tunnelPort); if (!connections.containsKey(conn_id)) { HttpTunnelTransport conn=null; try { conn=new HttpTunnelTransport(new IpAddress(ua.user_profile.tunnelServer),ua.user_profile.tunnelPort,this); } catch (Exception e) { Logger.warning("connection setup FAILED"); e.printStackTrace(); return false; } addConnection(conn); } else { //printLog("active connection found matching "+conn_id,LogLevel.MEDIUM); } HttpTunnelTransport conn=(HttpTunnelTransport)connections.get(conn_id); if (conn!=null) { try { conn.sendMessage(packet,true); conn_id=new ConnectionIdentifier(conn); } catch (IOException e) { return false; } } else { // this point has not to be reached Logger.warning("ERROR: conn "+conn_id+" not found: abort."); return false; } return true; } public boolean send2(byte[] packet, byte[] packet2) { Logger.hysteria("TunnelSenderReceiver.send2 (2)"); ConnectionIdentifier conn_id=new ConnectionIdentifier("tcp",new IpAddress(ua.user_profile.tunnelServer),ua.user_profile.tunnelPort); if (!connections.containsKey(conn_id)) { HttpTunnelTransport conn=null; try { conn=new HttpTunnelTransport(new IpAddress(ua.user_profile.tunnelServer),ua.user_profile.tunnelPort,this); } catch (Exception e) { Logger.warning("connection setup FAILED"); e.printStackTrace(); return false; } addConnection(conn); } else { //printLog("active connection found matching "+conn_id,LogLevel.MEDIUM); } HttpTunnelTransport conn=(HttpTunnelTransport)connections.get(conn_id); if (conn!=null) { try { conn.sendMessage(packet,packet2,true); conn_id=new ConnectionIdentifier(conn); } catch (IOException e) { return false; } } else { Logger.warning("ERROR: conn "+conn_id+" not found: abort."); return false; } return true; } public void onReceivedMessage(RtpPacket packet) { if (firstTime) { firstTime=false; lastSequenceNumber=packet.getSequenceNumber(); lastTimeStamp=packet.getTimestamp(); //System.out.println("First sequence number:"+lastSequenceNumber); } receivedPacketCount++; Logger.hysteria("TunnelSenderReceiver.onReceivedMessage"); byte[] b = packet.getPayload(); int pType=packet.getPayloadType(); long sn = packet.getSequenceNumber(); long ts = packet.getTimestamp(); //System.out.println("Sequence number:"+sn); if (lastSequenceNumber>sn)Logger.debug("Incorrect packet order... "+lastSequenceNumber+"->"+sn+" ("+lastTimeStamp+"->"+ts+")" ); if (sn-lastSequenceNumber>1)Logger.debug("Packet loss..? "+lastSequenceNumber+"->"+sn+" ("+lastTimeStamp+"->"+ts+")"); lastSequenceNumber=sn; lastTimeStamp=ts; // TODO pistetään paketti eteenpäin listener.onIncomingReceivedFrame(b,pType); } /* private void pauseAudio() { // TODO Auto-generated method stub audioPaused=true; ao.stop(); } */ public void init(int payloadType) { this.payloadType = payloadType; } /* private void resumeAudio() { // TODO Auto-generated method stub audioPaused=false; ao.play(); } */ }