Search code examples
javaandroidstreamreal-timedelay

Real time java android stream


I'm trying to make an audio loop by a connection server (Java) - Client (Android) in real time. Server (PC / Java) client sends data to the client (mobile / Android) and return.

I put a piece of code to clarify: - Server

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * This class implements a TCP Server.<p>
 */
public class GTCPServer {

    public static final int SERVER_PORT = 4444; // Port number
    private boolean running = true; // Boolean to control stream

    private ServerSocket serverSocket;

    // Input-Output streams:
    private DataInputStream dis;
    private DataOutputStream dos;

    // Buffer:
    private byte[] bytes;
    private static final int BUFFER_SIZE = 512 * 4; // Size

    /**
     * Constructor. Starts Server.
     */
    public GTCPServer() {
        bytes = new byte[BUFFER_SIZE]; // Create buffer

        // Put values to test:
        bytes[0] = 3;
        bytes[BUFFER_SIZE - 1] = 7;

        try {
            // Create Server Socket and wait connections.
            serverSocket = new ServerSocket(SERVER_PORT);
            System.out.println("Conecting...");

            // Within accept methode we are waiting connection.
            // When it's ready, create Client Socket:
            Socket client = serverSocket.accept();
            System.out.println("Receiving...");

            try {
                // Streams:
                dis = new DataInputStream(client.getInputStream());
                dos = new DataOutputStream(client.getOutputStream());
            } catch (Exception e) {
                System.out.println("Error.");
                e.printStackTrace();
            } 
        } catch (Exception e) {
            System.out.println("Error.");
            e.printStackTrace();
        }
    } // GTCPServer()

    /**
     * Send (write) a byte[] buffer within TCP.
     * @param buffer - Data.
     * @param offset - Position.
     * @param count - Number of bytes to write.
     */
    public void write(byte[] buffer, int offset, int count) {
        try {
            dos.write(buffer, offset, count); // Write
        } catch (IOException e) {
            e.printStackTrace();
        }
    } // write()

} // GTCPServer
  • Client:

    import android.util.Log;
    import java.io.*;
    import java.net.InetAddress;
    import java.net.Socket;
    
    import com.appacoustic.java.g.G;
    
    public class GTCPClient_A {
    
    public static final String SERVER_IP = "192.168.1.101"; // Server IP (PC)
    public static final int SERVER_PORT = 4444; // Port number
    private boolean running = true; // Boolean to control stream
    
    // Input-output streams:
    private DataInputStream dis;
    private DataOutputStream dos;
    
    // Buffer:
    private byte[] bytes;
    private static final int BUFFER_SIZE = 512 * 4; // Size
    
    /**
     * Constructor.
     */
    public GTCPClient_A() {
        bytes = new byte[BUFFER_SIZE]; // Create buffer
    } // GTCPClient_A()
    
    /**
     * Execute Thread. It isn't override because we are using a subclass (connectTask) wich extends AsyncTask.
     */
    public void run() {
        try {
            // Get InetAddress (IPv4 Server)
            InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
            Log.e("GTCPClient_A", "Conecting...");
    
            // Create a Socket ti connect with Server:
            Socket socket = new Socket(serverAddr, SERVER_PORT);
    
            try {
                dis = new DataInputStream(socket.getInputStream());
                dos = new DataOutputStream(socket.getOutputStream());
    
                int lap = 0; // Para llevar la cuenta de las vueltas
    
                while (running) {
                    dis.read(bytes); // Leemos por TCP
                    System.out.println("IN_ini_["+lap+"]: "+bytes[0]);
                    System.out.println("IN_fin_["+lap+"]: "+bytes[BUFFER_SIZE - 1]);
    
                    G.toc = System.currentTimeMillis();
                    G.ticToc();
                    G.tic = G.toc;
    
                    dos.write(bytes); // Escribimos por TCP
                    System.out.println("OUT_ini_["+lap+"]: "+bytes[0]);
                    System.out.println("OUT_fin_["+lap+"]: "+bytes[BUFFER_SIZE - 1]);
    
                    lap++;
                }
            } catch (Exception e) {
                Log.e("GTCP", "SERVIDOR: Error", e);
            } finally {
                socket.close();
            }
        } catch (Exception e) {
            Log.e("GTCP", "CLIENTE: Error", e);
        }
    } // run()
    
    } // GTCPClient_A
    

I have programmed it via Bluetooth and TCP without good results. There is too much delay (around 5 seconds). My surprise was that when trying to deploy the Client in Java instead of Android, does work (the code is almost identical). In fact I speak into the microphone (I scheduled JASIOHost ASIO drivers), from the computer where I have the Server, TCP data travels to another computer within the same WiFi, return and hear real-time perfectly speakers in the Server computer.

So, it appears the problem is something Android. But I what I want is to use a smartphone or tablet as a Client.

I've tested with this line at Client too: android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); // Priority

Find Wear months trying unsuccessfully solution ...


Solution

  • Finally the solution has been UDP. It's faster than TCP or Bluetooth. I put you code here. Now Server is Android:

    public class GUDPServer_A {
    
        private int PORT_NUMBER = 4444; // Nº de puerto de conexión. Usamos el mismo para Servidor y Cliente (para no liar)
    
        private DatagramSocket serverSocket; // Socket Servidor
        private DatagramPacket inPacket; // Paquete de entrada
        private DatagramPacket outPacket; // Paquete de salida
    
        private boolean running = true; // Booleano para indicar que se está a la escucha del Cliente
    
        // Buffers:
        public static final int BUFFER_SIZE = 1024 * 4;
        public static byte[] bytes_in;
        public static byte[] bytes_out;
    
        public GUDPServer_A() {
            bytes_in = new byte[BUFFER_SIZE];
            bytes_out = new byte[BUFFER_SIZE];
            try {
                serverSocket = new DatagramSocket(PORT_NUMBER); // Sólo se hace una vez ya que siempre estamos mandando por el mismo puerto
                System.out.println("Servidor UDP en marcha.");
            } catch (SocketException e) {
                System.out.println("Socket: "+e.getMessage());
            } catch (IOException e) {
                System.out.println("IO: "+e.getMessage());
            }
        } // GUDPServer_A()
    
        /**
         * Ejecución del hilo. El método no está sobreescrito porque usamos una subclase (connectTask) que extiende de AsyncTask.
         */
        public void run() {
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); // Le damos prioridad "-19" al Thread (Lo máximo es -20)
    
            int lap = 0; // Para llevar la cuenta de las vueltas
            while (running) { 
                read(bytes_in); // Recibimos el paquete desde UDP
    
                // Procesamos (de momento lo dejamos igual, simplemente clonamos):
                bytes_out = bytes_in.clone();
    
                G.toc = System.currentTimeMillis();
                G.ticToc();
                G.tic = G.toc;
    
                write(bytes_out); // Enviamos el paquete de vuelta
    
                Log.d("Vuelta:", ""+lap);
                lap++;
            }
        } // run()
    
        /**
         * Envía (escribe) un buffer de bytes por UDP.
         * @param buffer - La memoria intermedia donde se almacenan los datos a enviar.
         */
        public void write(byte[] buffer) {
            outPacket = new DatagramPacket(buffer, inPacket.getLength(), inPacket.getAddress(), inPacket.getPort());
    
            try {
                serverSocket.send(outPacket); // Enviamos el paquete
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // write()
    
        /**
         * Recibe (lee) un buffer de bytes por UDP.
         * @param buffer - La memoria intermedia donde se almacenan los datos a recibir.
         */
        public void read(byte[] buffer) {
            inPacket = new DatagramPacket(buffer, buffer.length);
    
            try {
                serverSocket.receive(inPacket); // Recibimos el paquete
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // read()
    
        /**
         * Cierra la conexión.
         */
        public void stop() {
            if (serverSocket != null) {
                serverSocket.close();
            }
        } // stop()
    
    } // GUDPServer_A
    

    And Client is Java:

    public class GUDPClient {
    
        private int PORT_NUMBER = 4444; // Nº de puerto de conexión. Usamos el mismo para Servidor y Cliente (para no liar)
    
        private DatagramSocket clientSocket; // Socket Cliente
        private DatagramPacket inPacket; // Paquete de entrada
        private DatagramPacket outPacket; // Paquete de salida
    
        private InetAddress host; // Dirección IP del Servidor (LG L5)
        public static final String IP_LG = "192.168.1.102"; // IP del Servidor (LG L5)
        public static final String IP_TABLET = "192.168.1.105"; // IP del Servidor (Tablet)
    
        /**
         * Constructor.
         */
        public GUDPClient() {
            try {
                clientSocket = new DatagramSocket(); // No hace falta darle un nº de puerto (se lo damos al paquete)
                host = InetAddress.getByName(IP_LG); // Obtenemos el host del Servidor (LG L5)
                //host = InetAddress.getByName(IP_TABLET); // Obtenemos el host del Servidor (Tablet)
    
                System.out.println("Cliente UDP conectado.");
            } catch (SocketException e) {
                System.out.println("Socket: "+e.getMessage());
            } catch (IOException e) {
                System.out.println("IO: "+e.getMessage());
            }
        } // GUDPClient()
    
        /**
         * Envía (escribe) un buffer de bytes por UDP.
         * @param buffer - La memoria intermedia donde se almacenan los datos a enviar.
         */
        public void write(byte[] buffer) {
            outPacket = new DatagramPacket(buffer, buffer.length, host, PORT_NUMBER);
    
            try {
                clientSocket.send(outPacket); // Enviamos el paquete por UDP
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // write()
    
        /**
         * Recibe (lee) un buffer de bytes por UDP.
         * @param buffer - La memoria intermedia donde se almacenan los datos a recibir.
         */
        public void read(byte[] buffer) {
            inPacket = new DatagramPacket(buffer, buffer.length);
    
            try {
                clientSocket.receive(inPacket); // Recibimos el paquete procesado desde UDP
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // read()
    
        /**
         * Cierra la conexión.
         */
        public void stop() {
            if (clientSocket != null) {
                clientSocket.close();
            }
        } // stop()
    
    } // GUDPClient
    

    One important thing is setting fast Threads.

    Android: android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

    Java: thread.setPriority(Thread.MAX_PRIORITY);