web-dev-qa-db-fra.com

Streaming vocal entre Android Téléphones via WiFi

J'essaie de diffuser de l'audio depuis le micro de 1 Android vers un autre via WiFi. Après avoir regardé quelques exemples, j'ai fait 2 applications avec une seule activité dans chacune, 1 pour capturer et envoyer de l'audio et l'autre à recevoir.

J'ai utilisé les classes Audiorecord et Audiotrack pour capturer et jouer. Cependant, j'entends juste un crépitement (qui s'est maintenant arrêté après que j'ai apporté quelques modifications bien que je sois revenu)

L'activité pour envoyer la voix.

public class VoiceSenderActivity extends Activity {

private EditText target;
private TextView streamingLabel;
private Button startButton,stopButton;

public byte[] buffer;
public static DatagramSocket socket;
private int port=50005;         //which port??
AudioRecord recorder;

//Audio Configuration. 
private int sampleRate = 8000;      //How much will be ideal?
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;    
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;       

private boolean status = true;




@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    target = (EditText) findViewById (R.id.target_IP);
    streamingLabel = (TextView) findViewById(R.id.streaming_label);
    startButton = (Button) findViewById (R.id.start_button);
    stopButton = (Button) findViewById (R.id.stop_button);

    streamingLabel.setText("Press Start! to begin");

    startButton.setOnClickListener (startListener);
    stopButton.setOnClickListener (stopListener);
}

private final OnClickListener stopListener = new OnClickListener() {

    @Override
    public void onClick(View arg0) {
                status = false;
                recorder.release();
                Log.d("VS","Recorder released");
    }

};

private final OnClickListener startListener = new OnClickListener() {

    @Override
    public void onClick(View arg0) {
                status = true;
                startStreaming();           
    }

};

public void startStreaming() {


    Thread streamThread = new Thread(new Runnable() {

        @Override
        public void run() {
            try {


                int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
                DatagramSocket socket = new DatagramSocket();
                Log.d("VS", "Socket Created");

                byte[] buffer = new byte[minBufSize];

                Log.d("VS","Buffer created of size " + minBufSize);
                DatagramPacket packet;

                final InetAddress destination = InetAddress.getByName(target.getText().toString());
                Log.d("VS", "Address retrieved");


                recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRate,channelConfig,audioFormat,minBufSize);
                Log.d("VS", "Recorder initialized");

                recorder.startRecording();


                while(status == true) {


                    //reading data from MIC into buffer
                    minBufSize = recorder.read(buffer, 0, buffer.length);

                    //putting buffer in the packet
                    packet = new DatagramPacket (buffer,buffer.length,destination,port);

                    socket.send(packet);


                }



            } catch(UnknownHostException e) {
                Log.e("VS", "UnknownHostException");
            } catch (IOException e) {
                Log.e("VS", "IOException");
            } 


        }

    });
    streamThread.start();
 }
 }

L'activité pour recevoir la voix

public class VoiceReceiverActivity extends Activity {


private Button receiveButton,stopButton;

public static DatagramSocket socket;
private AudioTrack speaker;

//Audio Configuration. 
private int sampleRate = 8000;      //How much will be ideal?
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;    
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;       

private boolean status = true;


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    receiveButton = (Button) findViewById (R.id.receive_button);
    stopButton = (Button) findViewById (R.id.stop_button);
    findViewById(R.id.receive_label);

    receiveButton.setOnClickListener(receiveListener);
    stopButton.setOnClickListener(stopListener);

}


private final OnClickListener stopListener = new OnClickListener() {

    @Override
    public void onClick(View v) {
        status = false;
        speaker.release();
        Log.d("VR","Speaker released");

    }

};


private final OnClickListener receiveListener = new OnClickListener() {

    @Override
    public void onClick(View arg0) {
        status = true;
        startReceiving();

    }

};

public void startReceiving() {

    Thread receiveThread = new Thread (new Runnable() {

        @Override
        public void run() {

            try {

                DatagramSocket socket = new DatagramSocket(50005);
                Log.d("VR", "Socket Created");


                byte[] buffer = new byte[256];


                //minimum buffer size. need to be careful. might cause problems. try setting manually if any problems faced
                int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

                speaker = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,channelConfig,audioFormat,minBufSize,AudioTrack.MODE_STREAM);

                speaker.play();

                while(status == true) {
                    try {


                        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
                        socket.receive(packet);
                        Log.d("VR", "Packet Received");

                        //reading content from packet
                        buffer=packet.getData();
                        Log.d("VR", "Packet data read into buffer");

                        //sending data to the Audiotrack obj i.e. speaker
                        speaker.write(buffer, 0, minBufSize);
                        Log.d("VR", "Writing buffer content to speaker");

                    } catch(IOException e) {
                        Log.e("VR","IOException");
                    }
                }


            } catch (SocketException e) {
                Log.e("VR", "SocketException");
            }


        }

    });
    receiveThread.start();
}

}

J'ai utilisé Wheelshark pour vérifier si les paquets sont envoyés et je peux voir les paquets. Cependant, la source est l'adresse MAC du périphérique d'envoi et la destination, quelque chose comme une adresse physique. Je ne sais pas si cela est pertinent cependant.

Alors quel est le problème?

30
Alabhya

Hé, il y a une bibliothèque Open Source appelée "Libstreaming" qui est utilisée pour diffuser de la voix/vidéo sur le réseau en utilisant le WIFI. Jetez-y un œil:

https://github.com/fyhertz/libstreaming

Il y a aussi quelques exemples fournis, veuillez les consulter:

https://github.com/fyhertz/libstreaming-examples

J'ai utilisé la bibliothèque pour diffuser de l'audio RTSP sur le réseau, j'espère que cela peut être utile.

7
user3482497

J'essaierais de diviser le problème en trois parties.

Partie 1

Assurez-vous que Socket Connection fonctionne correctement en commentant tout ce qui concerne l'audio

Partie 2

Envoyez simplement un message texte arbitraire [ Bonjour WiFi ] de l'expéditeur, puis recevez-le et imprimez-le dans l'application côté récepteur.

Partie 3

Si l'enregistreur fonctionne réellement? essayez de tester votre mode d'enregistrement dans un projet séparé pour voir s'il fonctionne correctement ou non.

Utilisez ce code pour capturer le microphone et le jouer.

Mon expérience

J'ai déjà travaillé sur un projet similaire et pour le tester, ce que j'ai fait après l'enregistrement, j'ai écrit les données audio enregistrées sous forme de fichier sur la carte SD

(ce serait de l'audio brut, donc la plupart des lecteurs de musique ne pourront pas le lire ... mPlayer devrait le jouer je suppose)

3
Atul Goyal

Vous devez soigneusement considérer votre utilisation de UDP (classe DatagramSocket) comme protocole réseau.

UDP est un protocole léger qui ne garantit pas de maintenir l'ordre des paquets reçus. Cela peut être une des raisons pour lesquelles l'audio est tronqué. Un paquet reçu dans le désordre entraînera une lecture audio de paquets d'une valeur de paquets. À la limite de ces paquets hors séquence, vous entendrez des clics/pops où l'échantillon audio est effectivement corrompu. En plus de cela, les paquets UDP ne sont pas garantis pour être livrés avec succès. Tout paquet abandonné ajoutera évidemment à tout brouillage ou distorsion qui est entendu.

TCP (classe Socket) serait une meilleure option pour une qualité audio optimale. TCP est un protocole plus robuste qui maintiendra l'ordre de réception des paquets. Il a également une vérification des erreurs intégrée et renverra tous les paquets perdus. Cependant, en raison de cette fonctionnalité attentionnelle, TCP a une surcharge réseau plus élevée.

J'ai commencé cette réponse en disant que vous devez soigneusement réfléchir au protocole que vous utilisez. En effet, il y a lieu d'utiliser l'une ou l'autre selon ce qui est important pour vous.

Si vous voulez une lecture à latence ultra faible mais que vous êtes heureux de sacrifier la qualité audio, alors UDP fonctionnera. Cependant, il faudra une certaine expérimentation pour trouver la meilleure taille de tampon et d'échantillon.

Si vous voulez la meilleure re-production audio possible sans distorsion mais êtes heureux d'introduire un peu plus de latence, alors TCP est la voie à suivre.

Je ne peux pas dire combien de latence supplémentaire TCP ajouterait. Mais il est possible qu'il puisse être implémenté sans impact sur l'expérience utilisateur. La seule façon de le savoir est de l'essayer et de voir.

2
Martyn