Dr_Click
Dr_Click

Reputation: 469

java UDP sound streaming : why do I have interferences?

I'm trying to build a very simple audio streamer with a source and a receiver. But I have some interferences when I receive the sound in my "receiver". I'm using the UDP protocol. Is there a way to "improve" my code to avoid those interferences?

Here is my audio server:

import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

public class AudioPlayerServer implements Runnable {

    private SourceDataLine sLine;
    private AudioFormat audioFormat;
    private AudioInputStream audioInputStream=null;
    private String host="127.0.0.1";
    private int port=8000;
    private DatagramSocket server;
    private DatagramPacket packet;
    private long startTime;
    private long endTime=System.nanoTime();;
    private long elapsed=System.nanoTime();;
    private double sleepTime;
    private long sleepTimeMillis;
    private int sleepTimeNanos, epsilon;

    AudioPlayerServer(String host, int port) {      
        this.host=host;
        this.port=port;
        init();
    }

    public void init() {
        File file = new File("test.wav");
        try {
            audioInputStream=AudioSystem.getAudioInputStream(file);

        } catch (Exception e) {
            e.printStackTrace();
        }

        audioFormat = new AudioFormat(44100, 16, 2, true, false);
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
        System.out.println(info);

        try {
            server = new DatagramSocket();
            System.out.println("Server started");

        } catch (SocketException e) {
            e.printStackTrace();
        }       
    }

    public void run() {
        try {
            byte bytes[] =  new byte[4096];
            byte bytes2[] =  new byte[1024];
            int bytesRead=0;
            //The sending rythm of the data have to be compatible with an audio streaming.
            //So, I'll sleep the streaming thread for (1/SampleRate) seconds * (bytes.lenght/4) - epsilon
            //=> bytes.lenght/4 because 4 values = 1 frame => For ex, in  1024 bits, there are 1024/4 = 256 frames
            //epsilon because the instructions themselves takes time.
            //The value have to be convert in milliseconds et nanoseconds.
            sleepTime=(1024/audioFormat.getSampleRate());
            epsilon=400000;
            sleepTimeMillis=(long)(sleepTime*1000);
            sleepTimeNanos=(int)((sleepTime*1000-sleepTimeMillis)*1000000);
            System.out.println("Sleep time :"+sleepTimeMillis+" ms, "+sleepTimeNanos+" ns");

            while ((bytesRead=audioInputStream.read(bytes, 0, bytes.length))!= -1) {
                //getSignalLevel(bytes);

                try {                   
                    //startTime=System.nanoTime();
                    packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(host), port);
                    packet.setData(bytes);
                    server.send(packet);                    
                    packet.setLength(bytes.length);                 
                    //endTime=System.nanoTime();
                    //System.out.println(endTime-startTime);
                    Thread.sleep(sleepTimeMillis,sleepTimeNanos);                   
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }   
            System.out.println("No bytes anymore !");                   
        } catch (Exception e) {
            e.printStackTrace();
        }
        sLine.close();
        System.out.println("Line closed");

    }

}

Here is the client:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

public class AudioReceiver implements Runnable{
    private String host;
    private int port;
    private SourceDataLine sLine;
    private AudioFormat audioFormat;
    byte[] buffer=new byte[4096];
    DatagramPacket packet;

    AudioReceiver (String host, int port) {
        this.host=host;
        this.port=port;
        init();
        Thread t1=new Thread(new Reader());
        t1.start();
    }

    public void init() {
        audioFormat = new AudioFormat(44100, 16, 2, true, false);
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);

        try  {
            System.out.println(info);
            sLine=(SourceDataLine) AudioSystem.getLine(info);
            System.out.println(sLine.getLineInfo() + " - sample rate : "+audioFormat.getSampleRate());
        } catch (Exception e) {
            e.printStackTrace();
        }       
    }

    public void run() {
        System.out.println("Client started");
        try {
            sLine.open(audioFormat);
        } catch (Exception e){
            e.printStackTrace();
        }
        sLine.start();
        System.out.println("Line started");

        try {

            DatagramSocket client = new DatagramSocket(port, InetAddress.getByName(host));
            while (true) {
                try {
                    packet = new DatagramPacket(buffer, buffer.length);
                    //System.out.println("Reception beggins for host "+host+" : "+port);
                    client.receive(packet);
                    //System.out.println("Reception ends");
                    buffer=packet.getData();

                    //sLine.write(packet.getData(), 0, buffer.length);
                    packet.setLength(buffer.length);
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e1) {
            e1.printStackTrace();
        }

    }

    public class Reader implements Runnable {
        public void run() {
            while (true) {
                if (packet!=null) {
                    sLine.write(packet.getData(), 0, buffer.length);
                }
            }           
        }       
    }   
}

Upvotes: 3

Views: 1851

Answers (2)

jaybers
jaybers

Reputation: 2211

When creating a UDP streaming system, often the RTP protocol is used. RTP uses UDP which is a connectionless unreliable protocol. At the transport layer (UDP), you need to deal with losses and out of order arrivals. Additionally, the network layer is bursty and data will not arrive at a nice even rate. Rather, packets will arrive with inconsistent interarrival rates. As such, you must locally buffer the data to deal with this network jitter.
This post answers and explains concerning java, UDP, RTP, network jitter, buffering, packet loss. There are also different strategies in dealing with losses. You can fill it in with silence or estimate the lost data. Additionally, your client may play samples faster than your server and eventually run out data. This is dues to the variations in clock crystals between two systems without a common bus. This post answers and explains dealing with packet loss and clock drift.

Upvotes: 2

Stephen C
Stephen C

Reputation: 718788

I'm using an UDP protocol. Is there a way to "improve" my code to avoid those interferences ?

There are two possibilities:

  1. UDP messages are being lost.

  2. There is a problem with the application logic on the client or server side that is resulting in damage to the audio stream data.


Assuming that the problem is #1, your simplest option is to switch to TCP.

UDP is inherently susceptible to packet loss, and the lost packets cause distortion. TCP is not lossy, and if there is some delay (i.e. client-side buffering) in the "pipeline", you should be able to avoid distortion due to jitter caused by occasional packet loss and retransmission (at the TCP level).


I also note that your current client / server logic is attempting to control the play rate on the server side using sleep. You need to be aware that sleep is not guaranteed to wake up your sleeping thread at exactly the time point you expect. The semantics are "sleep for at least the specified time". This, and lack of any buffering on the client side, could also lead to distortion.

Upvotes: 0

Related Questions