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:
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
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; }
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(); } } }
- Buffer Size Matching: The
receiveDatabuffer size should match the value returned byAudioRecord.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
ServerSocketcan 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

