通过TCP发送音频流,UnsupportedAudioFileException

12

我已经成功地通过 TCP 套接字发送和读取文本和图像数据。但是我无法发送和读取音频流数据。

服务器的示例代码:

public class ServerAudio {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        try {
            ServerSocket serverSocker = new ServerSocket();
            Socket client = null;
            serverSocker.bind(new InetSocketAddress(6666));
            if (serverSocker.isBound()) {
                client = serverSocker.accept();
                OutputStream out = client.getOutputStream();
                while (true) {
                    AudioInputStream ain = testPlay("C:/Users/Public/Music/Sample Music/adios.wav");
                    if (ain != null) {
                        AudioSystem.write(ain, AudioFileFormat.Type.WAVE, out);
                    }
                }
            }
            serverSocker.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static AudioInputStream testPlay(String filename) {
        AudioInputStream din = null;
        try {
            File file = new File(filename);
            AudioInputStream in = AudioSystem.getAudioInputStream(file);
            System.out.println("Before :: " + in.available());

            AudioFormat baseFormat = in.getFormat();
            AudioFormat decodedFormat =
                    new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, baseFormat.getSampleRate(),
                            8, baseFormat.getChannels(), baseFormat.getChannels(),
                            baseFormat.getSampleRate(), false);
            din = AudioSystem.getAudioInputStream(decodedFormat, in);
            System.out.println("After :: " + din.available());
            return din;
        } catch (Exception e) {
            // Handle exception.
            e.printStackTrace();
        }
        return din;
    }
}

客户端的示例代码:

public class RDPPlayAudioBytes {
    private static Socket socket;
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // SocketAddress socketAddress = new InetSocketAddress("172.19.1.50", 4444);
        try {
            Socket socket = new Socket("172.19.0.109", 6666);
            // socket.connect(socketAddress, 10000);
            if (socket != null && socket.isConnected()) {
                InputStream inputStream = socket.getInputStream();
                // DataInputStream din=new DataInputStream(inputStream);
                while (inputStream != null) {
                    if (inputStream.available() > 0) {
                        System.out.println(inputStream.available());
                        InputStream bufferedIn = new BufferedInputStream(inputStream);
                        System.out.println("********** Buffred *********" + bufferedIn.available());
                        AudioInputStream ais = AudioSystem.getAudioInputStream(bufferedIn);
                    }
                }
            }


        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } /*
           * catch (LineUnavailableException e) { // TODO Auto-generated catch block
           * e.printStackTrace(); }
           */catch (UnsupportedAudioFileException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

我遇到的异常信息是

javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input stream
    at javax.sound.sampled.AudioSystem.getAudioInputStream(Unknown Source)

我注意到服务器向客户端发送了35394字节的数据,但在客户端这里我们只接收到了8192字节的数据。我无法理解为什么客户端会丢失这么多字节。

请帮助我了解如何通过TCP套接字发送音频流。


只是一个提示:不要使用 available()。在大多数情况下,它并不会做你期望的事情。 - Fildor
尝试使用以下代码:AudioInputStream ain = testPlay("C:\Users\Public\Music\Sample Music\adios.wav"); - madhu
请查看此网址:http://stackoverflow.com/questions/11174813/playn-java-mp3-support-unsupportedaudiofileexception - madhu
有几件事情需要检查。在testplay中创建file后,打印file.exists()file.canRead()file.getCanonicalPath()的值。如果一切正常,请尝试打印AudioSystem.getAudioFileFormat(file)的值。如果失败了,请仔细检查您的文件,确保它真的是一个WAV文件。 - Devon_C_Miller
2个回答

9

服务器: 服务器只是流传输音频文件的字节。没有音频系统参与。将音频文件作为参数传递即可:

java AudioServer "C:/Users/Public/Music/Sample Music/adios.wav"

音频服务器类的代码:

import java.io.*;
import java.net.*;

public class AudioServer {
    public static void main(String[] args) throws IOException {
        if (args.length == 0)
            throw new IllegalArgumentException("expected sound file arg");
        File soundFile = AudioUtil.getSoundFile(args[0]);

        System.out.println("server: " + soundFile);

        try (ServerSocket serverSocker = new ServerSocket(6666); 
            FileInputStream in = new FileInputStream(soundFile)) {
            if (serverSocker.isBound()) {
                Socket client = serverSocker.accept();
                OutputStream out = client.getOutputStream();

                byte buffer[] = new byte[2048];
                int count;
                while ((count = in.read(buffer)) != -1)
                    out.write(buffer, 0, count);
            }
        }

        System.out.println("server: shutdown");
    }
}

客户端:客户端可以通过命令行传递一个声音文件进行测试:

java AudioClient "C:/Users/Public/Music/Sample Music/adios.wav"

当没有参数时,它会连接到服务器并通过套接字播放接收到的文件:

java AudioClient

代码:

import java.io.*;
import java.net.*;
import javax.sound.sampled.*;


public class AudioClient {
    public static void main(String[] args) throws Exception {
        if (args.length > 0) {
            // play a file passed via the command line
            File soundFile = AudioUtil.getSoundFile(args[0]);
            System.out.println("Client: " + soundFile);
            try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(soundFile))) {
                play(in);
            }
        }
        else {
            // play soundfile from server
            System.out.println("Client: reading from 127.0.0.1:6666");
            try (Socket socket = new Socket("127.0.0.1", 6666)) {
                if (socket.isConnected()) {
                    InputStream in = new BufferedInputStream(socket.getInputStream());
                    play(in);
                }
            }
        }

        System.out.println("Client: end");
    }


    private static synchronized void play(final InputStream in) throws Exception {
        AudioInputStream ais = AudioSystem.getAudioInputStream(in);
        try (Clip clip = AudioSystem.getClip()) {
            clip.open(ais);
            clip.start();
            Thread.sleep(100); // given clip.drain a chance to start
            clip.drain();
        }
    }
}

这是一个由AudioServer和AudioClient使用的实用程序类:

import java.io.File;

public class AudioUtil {
    public static File getSoundFile(String fileName) {
        File soundFile = new File(fileName);
        if (!soundFile.exists() || !soundFile.isFile())
            throw new IllegalArgumentException("not a file: " + soundFile);
        return soundFile;
    }
}

7
TCP是可靠的,所以字节将会完整接收。还有一个小问题,仅创建音频输入流并不会播放接收到的音频,你需要从音频流中播放接收到的音频。
有多种可能的技术可以用于播放接收到的音频。您可以使用Java Sound API中的Clip或SourceDataLine。此外,不要多次创建AudioInputStream,只需创建一次并重复使用即可。
以下是播放接收到的音频的解决方案之一。
public class RDPPlayAudioBytes {

private static Socket socket;
private static BufferedInputStream inputStream;

/**
 * @param args
 * @throws javax.sound.sampled.LineUnavailableException
 */
public static void main(String[] args) throws LineUnavailableException {
    // TODO Auto-generated method stub
    // SocketAddress socketAddress = new InetSocketAddress("172.19.1.50", 4444);
    try {
        socket = new Socket("127.0.0.1", 6666);

        if (socket.isConnected()) {

            inputStream = new BufferedInputStream(socket.getInputStream());

            Clip clip = AudioSystem.getClip();
            AudioInputStream ais = AudioSystem.getAudioInputStream(inputStream);
            clip.open(ais);
            clip.start();

            while (inputStream != null) {
                if (clip.isActive()) {

                    System.out.println("********** Buffred *********" + inputStream.available());

                }
            }

        }

    } catch (IOException | UnsupportedAudioFileException e) {
        System.err.println(e);
    }
}
}

你可能需要根据你的要求选择不同的实现方式。这只是一个演示,展示了如何使用AudioInputStream来使用Clip播放音频。您可能会注意到我发布的代码中有相当多的更改。希望您很好地理解这一点。
您可以参考Java Sound API文档,了解播放音频的基础知识。
注意:
1.仅供您了解,您可能需要实现一个监听器,以便在音频剪辑播放完成之前程序不会关闭。在当前实现中,由于使用了循环,这种情况不会发生。但最好使用监听器。您可以参考此帖子
2.您还可以将音频数据读入byte[],并在接收到数据后立即播放。实现会略有不同。

这段代码永远不会终止:inputStream 永远不会得到 null。此外,这个循环和 clip.isActive() 的检查将会占用所有的 CPU 循环。 - wero
1
我同意,但我的目的只是展示在问题中发布代码的同时使用媒介播放声音的用法。我同意代码可以改进。 - Kunjan Thadani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接