You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java服务端将Android客户端UDP音频流保存为WAV文件

Got it, let's work through this together. You're receiving raw PCM audio over UDP from an Android client using DatagramSocket, and you need to package that raw data into a valid WAV file. Here's a complete, tested solution with explanations:

Step 1: Align Audio Parameters with Android Client

First, you need to define the exact audio parameters used by your Android AudioRecord instance. These values must match perfectly to avoid distorted or silent WAV files:

// Match these to your Android client's AudioRecord settings!
private static final int SAMPLE_RATE = 16000;    // 16kHz sampling rate
private static final int CHANNELS = 1;           // Mono audio
private static final int BITS_PER_SAMPLE = 16;   // 16-bit PCM encoding
Step 2: Implement WAV Header Generator

WAV files require a specific 44-byte header that describes the audio format and data length. This method generates a valid header based on your parameters:

private static byte[] createWavHeader(long totalAudioLen, long totalDataLen) {
    byte[] header = new byte[44];
    
    // RIFF chunk descriptor
    header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F';
    header[4] = (byte) (totalDataLen & 0xff);
    header[5] = (byte) ((totalDataLen >> 8) & 0xff);
    header[6] = (byte) ((totalDataLen >> 16) & 0xff);
    header[7] = (byte) ((totalDataLen >> 24) & 0xff);
    
    // WAVE format
    header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E';
    
    // fmt subchunk
    header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' ';
    header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0; // PCM format size
    header[20] = 1; header[21] = 0; // Audio format (1 = PCM)
    header[22] = (byte) CHANNELS; header[23] = 0; // Channel count
    header[24] = (byte) (SAMPLE_RATE & 0xff);
    header[25] = (byte) ((SAMPLE_RATE >> 8) & 0xff);
    header[26] = (byte) ((SAMPLE_RATE >> 16) & 0xff);
    header[27] = (byte) ((SAMPLE_RATE >> 24) & 0xff); // Sample rate
    
    long byteRate = SAMPLE_RATE * CHANNELS * BITS_PER_SAMPLE / 8;
    header[28] = (byte) (byteRate & 0xff);
    header[29] = (byte) ((byteRate >> 8) & 0xff);
    header[30] = (byte) ((byteRate >> 16) & 0xff);
    header[31] = (byte) ((byteRate >> 24) & 0xff); // Byte rate
    
    short blockAlign = (short) (CHANNELS * BITS_PER_SAMPLE / 8);
    header[32] = (byte) (blockAlign & 0xff);
    header[33] = (byte) ((blockAlign >> 8) & 0xff); // Block align
    
    header[34] = (byte) BITS_PER_SAMPLE; header[35] = 0; // Bits per sample
    
    // data subchunk
    header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a';
    header[40] = (byte) (totalAudioLen & 0xff);
    header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
    header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
    header[43] = (byte) ((totalAudioLen >> 24) & 0xff); // Audio data length
    
    return header;
}
Step 3: Complete the Audio Receiving & WAV Saving Logic

Here's the updated version of your receiveAudioFromAndroidClient method that collects raw PCM data, generates the WAV header, and saves the complete file:

private static void receiveAudioFromAndroidClient() {
    DatagramSocket serverSocket = null;
    FileOutputStream fos = null;
    ByteArrayOutputStream pcmBuffer = new ByteArrayOutputStream();

    try {
        // Initialize UDP socket on port 50005
        serverSocket = new DatagramSocket(50005);
        byte[] receiveData = new byte[4096]; // Match Android's AudioRecord buffer size!
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

        System.out.println("Waiting for audio data from Android client...");

        // Receive audio packets until stop condition is met
        long totalAudioBytes = 0;
        while (true) {
            serverSocket.receive(receivePacket);
            int bytesReceived = receivePacket.getLength();

            // Stop condition: adjust this to match your client's termination signal
            if (bytesReceived == 0 || new String(receiveData, 0, bytesReceived).equals("STOP")) {
                break;
            }

            // Collect raw PCM data
            pcmBuffer.write(receiveData, 0, bytesReceived);
            totalAudioBytes += bytesReceived;
            System.out.printf("Received %d bytes | Total: %d bytes%n", bytesReceived, totalAudioBytes);
        }

        // Calculate WAV file lengths
        long totalDataLen = totalAudioBytes + 36; // Total file size = header (44) - 8 + audio data
        byte[] wavHeader = createWavHeader(totalAudioBytes, totalDataLen);

        // Write header + PCM data to WAV file
        fos = new FileOutputStream("received_audio.wav");
        fos.write(wavHeader);
        fos.write(pcmBuffer.toByteArray());

        System.out.println("Success! WAV file saved as received_audio.wav");

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // Clean up resources
        if (serverSocket != null && !serverSocket.isClosed()) serverSocket.close();
        if (fos != null) try { fos.close(); } catch (IOException e) { e.printStackTrace(); }
        try { pcmBuffer.close(); } catch (IOException e) { e.printStackTrace(); }
    }
}
Key Notes to Avoid Issues
  • Buffer Size Matching: The receiveData buffer size should match the value returned by AudioRecord.getMinBufferSize() on your Android client. Using an incorrect size can cause audio glitches.
  • UDP Reliability: UDP doesn't guarantee packet delivery. If you need reliable audio transmission, consider adding an application-level retransmission mechanism or switching to TCP (your existing ServerSocket can be repurposed for this).
  • Stop Condition: The example uses a "STOP" string as a termination signal. Adjust this to match how your client indicates the end of the audio stream (e.g., empty packet, specific byte sequence).

内容的提问来源于stack exchange,提问作者Sadegh Bakhshandeh Sajjad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:29:28