Аудио - Потоковое аудио от Java прерывисто - PullRequest
1 голос
/ 06 января 2020

Моя основная задача - создать прямую трансляцию зашифрованного голосового чата от mi c. Зашифрованный звук затем передается по сети от одного клиента другому. Проблема в том, что звук всегда заикается и прерывисто во время работы программы (потоковая передача).

  1. Я пробовал разные типы оборудования (P C, ноутбук, Raspberry Pi).
  2. Также в разных ОС.
  3. Только выборка незашифрованного звука для устранения любых проблем, вызываемых алгоритмом шифрования.
  4. Изменение частоты дискретизации звука.

К сожалению, все не удалось.

Для простоты я включил только код, необходимый для передачи звука по сети без шифрования.

MAIN CLASS - и отправитель, и приемник

package com.emaraic.securevoice;

import com.emaraic.securevoice.utils.AES;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import javax.sound.sampled.*;

public class SecureVoice 
{

    public static void main(String[] args) 
    {

        Receiver rx = new Receiver();
        rx.start();

        Transmitter tx = new Transmitter();
        tx.start();

    }

    public static AudioFormat getAudioFormat() 
    { //you may change these parameters to fit you mic
        float sampleRate = 8000.0f;  //8000,11025,16000,22050,44100
        int sampleSizeInBits = 16;    //8,16
        int channels = 1;             //1,2
        boolean signed = true;        //true,false
        boolean bigEndian = false;    //true,false
        return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
    }

    public static final String ANSI_BOLD = "\033[0;1m"; //not working in NetBeans
    public static final String ANSI_RESET = "\033[0m";
    public static final String ANSI_BLACK = "\033[30m";
    public static final String ANSI_RED = "\033[31m";
    public static final String ANSI_GREEN = "\033[32;4m";
    public static final String ANSI_YELLOW = "\033[33m";
    public static final String ANSI_BLUE = "\033[34m";
    public static final String ANSI_PURPLE = "\033[35m";
    public static final String ANSI_CYAN = "\033[36m";
    public static final String ANSI_WHITE = "\033[37m";
}

ОТПРАВИТЕЛЬ

package com.emaraic.securevoice;

import com.emaraic.securevoice.utils.AES;
import java.io.*;
import java.io.File;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Port;
import javax.sound.sampled.TargetDataLine;

public class Transmitter extends Thread
{    
    // these parameters must be copied and used in the Receiver class of the other client
    private static final String TX_IP = "10.101.114.179"; //ip to send to 
    private static final int TX_PORT = 1034;

    @Override
    public void run() 
    {
        SecureVoice color = new SecureVoice();
        Mixer.Info minfo[] = AudioSystem.getMixerInfo();
        System.out.println(color.ANSI_BLUE + "Detecting sound card drivers...");
        for (Mixer.Info minfo1 : minfo) 
        {
            System.out.println("   " + minfo1);
        }

        if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) 
        {
            try 
            {
                DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, SecureVoice.getAudioFormat());
                final TargetDataLine line = (TargetDataLine) AudioSystem.getLine(dataLineInfo); //recording from mic
                line.open(SecureVoice.getAudioFormat());
                line.start(); //start recording
                System.out.println(color.ANSI_GREEN + "Recording...");
                byte tempBuffer[] = new byte[line.getBufferSize()];
                System.out.println(color.ANSI_BLUE + "Buffer size = " + tempBuffer.length + " bytes");
                //AudioCapture audio = new AudioCapture(line); //capture the audio into .wav file
                //audio.start();
                while (true) //AES encryption
                {
                    int read = line.read(tempBuffer, 0, tempBuffer.length);
                    byte[] encrypt = AES.encrypt(tempBuffer, 0, read);
//                    sendToUDP(encrypt);
                    sendToUDP(tempBuffer);
                }


            }

            catch (Exception e) 
            {
                System.out.println(e.getMessage());
                System.exit(0);
            }
        }


    }




    public static void sendToUDP(byte soundpacket[]) 
    {
        try 
        {
//            EncryptedAudio encrypt = new EncryptedAudio(soundpacket);
//            encrypt.start();
            DatagramSocket sock = new DatagramSocket();
            sock.send(new DatagramPacket(soundpacket, soundpacket.length, InetAddress.getByName(TX_IP), TX_PORT));
            sock.close();
        } 
        catch (Exception e) 
        {
            System.out.println(e.getMessage());
        }
    }
}

ПРИЕМНИК

package com.emaraic.securevoice;


import com.emaraic.securevoice.utils.AES;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.logging.Level;
import java.util.logging.Logger;

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

public class Receiver extends Thread {
    // these parameters must by used in the Transmitter class of the other client
    private static final String RX_IP = "localhost"; 
    private static final int RX_PORT = 1034;


    @Override
    public void run() {
        byte b[] = null;
        while (true) {

                b = rxFromUDP();
                speak(b);

        }
    }


    public static byte[] rxFromUDP() {
        try {
            DatagramSocket sock = new DatagramSocket(RX_PORT);
             byte soundpacket[] = new byte[8192];
            DatagramPacket datagram = new DatagramPacket(soundpacket, soundpacket.length, InetAddress.getByName(RX_IP), RX_PORT);
            sock.receive(datagram);
            sock.close();

//            return AES.decrypt(datagram.getData(),0,soundpacket.length); // soundpacket ;
            return soundpacket; //  if you want to hear encrypted form 
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }

    }

    public static void speak(byte soundbytes[]) {

        try {
            DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, SecureVoice.getAudioFormat());
            try (SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo)) {
                sourceDataLine.open(SecureVoice.getAudioFormat());
                sourceDataLine.start();
                sourceDataLine.write(soundbytes, 0, soundbytes.length);
                sourceDataLine.drain();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }



}

ДОПОЛНИТЕЛЬНАЯ ССЫЛКА http://emaraic.com/blog/secure-voice-chat

Используется IDE - Netbeans 11.1

Java Версия JDK - Java 13 (Windows) - OpenJDK11 (Linux)

1 Ответ

0 голосов
/ 06 января 2020

Две проблемы. Потоковые данные сети будут иметь дрожание во время прибытия. Запуск и остановка воспроизведения аудио приведут к задержкам и дрожанию из-за времени загрузки ОС и драйвера оборудования. Существует также меньшая проблема синхронизации тактовой частоты аудиосэмплирования между системами записи и воспроизведения. Все это может влиять на непрерывный поток аудиосэмплов с фиксированной скоростью.

Чтобы избежать проблемы задержки запуска звука, не останавливайте воспроизведение звука или не записывайте систему между сетевыми пакетами, всегда храните аудиоданные готов играть непрерывно с текущей частотой дискретизации. Чтобы устранить дрожание сети, перед началом воспроизведения буферизируйте некоторое количество аудиоданных, чтобы всегда было готово воспроизводить звук, даже если следующий сетевой пакет заметно задерживается.

Возможно, вам придется собрать некоторые статистические данные о запуске аудио, задержке в сети и изменении задержки, чтобы определить подходящее количество для буферизации. Альтернативой является алгоритм маскирования пропадания звука, который гораздо сложнее реализовать.

...