/** * The class that process audio data * @author bencall * */ import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * Main class that listen for new packets. * @author bencall * */ public class AudioServer implements UDPDelegate{ // Constantes public static final int BUFFER_FRAMES = 512; // Total buffer size (number of frame) public static final int START_FILL = 282; // Alac will wait till there are START_FILL frames in buffer public static final int MAX_PACKET = 2048; // Also in UDPListener (possible to merge it in one place?) // Sockets private DatagramSocket sock, csock; private UDPListener l1; // client address private InetAddress rtpClient; // Audio infos and datas private AudioSession session; private AudioBuffer audioBuf; // The audio player private PCMPlayer player; /** * Constructor. Initiate instances vars * @param aesiv * @param aeskey * @param fmtp * @param controlPort * @param timingPort */ public AudioServer(AudioSession session){ // Init instance var this.session = session; // Init functions audioBuf = new AudioBuffer(session, this); this.initRTP(); player = new PCMPlayer(session, audioBuf); player.start(); } public void stop(){ player.stopThread(); l1.stopThread(); //l2.stopThread(); synchronized(sock){ sock.close(); } csock.close(); } public void setVolume(double vol){ player.setVolume(vol); } /** * Return the server port for the bonjour service * @return */ public int getServerPort() { return sock.getLocalPort(); } /** * Opens the sockets and begin listening */ private void initRTP(){ int port = 6000; while(true){ try { sock = new DatagramSocket(port); csock = new DatagramSocket(port+1); } catch (IOException e) { port = port + 2; continue; } break; } l1 = new UDPListener(sock, this); } /** * When udpListener gets a packet */ public void packetReceived(DatagramSocket socket, DatagramPacket packet) { this.rtpClient = packet.getAddress(); // The client address int type = packet.getData()[1] & ~0x80; if (type == 0x60 || type == 0x56) { // audio data / resend // Decale de 4 bytes supplementaires int off = 0; if(type==0x56){ off = 4; } //seqno is on two byte int seqno = (int)((packet.getData()[2+off] & 0xff)*256 + (packet.getData()[3+off] & 0xff)); // + les 12 (cfr. RFC RTP: champs a ignorer) byte[] pktp = new byte[packet.getLength() - off - 12]; for(int i=0; i<pktp.length; i++){ pktp[i] = packet.getData()[i+12+off]; } audioBuf.putPacketInBuffer(seqno, pktp); } } /** * Ask iTunes to resend packet * FUNCTIONAL??? NO PROOFS * @param first * @param last */ public void request_resend(int first, int last) { System.out.println("Resend Request: " + first + "::" + last); if(last<first){ return; } int len = last - first + 1; byte[] request = new byte[] { (byte) 0x80, (byte) (0x55|0x80), 0x01, 0x00, (byte) ((first & 0xFF00) >> 8), (byte) (first & 0xFF), (byte) ((len & 0xFF00) >> 8), (byte) (len & 0xFF)}; try { DatagramPacket temp = new DatagramPacket(request, request.length, rtpClient, session.getControlPort()); csock.send(temp); } catch (IOException e) { e.printStackTrace(); } } /** * Flush the audioBuffer */ public void flush(){ audioBuf.flush(); } }