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

Android录制音频发送至服务器后文件过小问题求助

Android AudioRecord录制音频发送至Python服务器仅收到极小文件的解决方案

核心问题分析

你的代码里最大的问题是录制逻辑错误:

  • 调用audioRecord.stop()后才尝试读取音频数据,此时AudioRecord已经停止工作,内部缓冲区的音频数据无法被完整读取。
  • 只执行了一次audioRecord.read(),没有在录制过程中持续采集数据,所以只能拿到单次缓冲区大小的数据(也就是你看到的约2KB)。

Android端代码修复

1. 重构录制逻辑,在录制过程中持续读取数据

需要在startRecording时启动协程,不断从AudioRecord读取数据并缓存,停止录制时再停止读取并发送缓存的完整数据。

修改后的MediaRecorder类:

class MediaRecorder(app: Application) {
    private val bufferSize: Int
    private lateinit var audioRecord: AudioRecord
    private val sampleRate: Int = 16000
    private val channelConfig: Int = AudioFormat.CHANNEL_IN_MONO
    private val audioFormat: Int = AudioFormat.ENCODING_PCM_16BIT
    private val app: Application = app
    private var isRecording = false
    private val recordedData = ByteArrayOutputStream() // 缓存所有录制的音频数据

    init {
        bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) * 4
    }

    fun startRecording() {
        if (ActivityCompat.checkSelfPermission(
                app.applicationContext,
                Manifest.permission.RECORD_AUDIO
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return // 建议补充权限申请逻辑,避免无权限时崩溃
        }

        audioRecord = AudioRecord(
            MediaRecorder.AudioSource.MIC,
            sampleRate,
            channelConfig,
            audioFormat,
            bufferSize
        )
        audioRecord.startRecording()
        isRecording = true

        // 启动协程持续读取音频数据
        CoroutineScope(Dispatchers.IO).launch {
            val buffer = ByteArray(bufferSize)
            while (isRecording) {
                val bytesRead = audioRecord.read(buffer, 0, bufferSize)
                if (bytesRead > 0) {
                    recordedData.write(buffer, 0, bytesRead) // 仅写入实际读取到的字节
                }
            }
        }
    }

    fun stopRecording() {
        isRecording = false
        audioRecord.stop()
        audioRecord.release()

        val fullAudioData = recordedData.toByteArray()
        Log.i("Audio", "Total recorded bytes: ${fullAudioData.size}")

        if (fullAudioData.isNotEmpty()) {
            sendByteArrayToServer(fullAudioData)
        } else {
            Log.e("Audio", "No audio data recorded.")
        }
        recordedData.reset() // 重置缓冲区,方便下次录制
    }
}

2. 发送代码优化(可选)

当前发送逻辑可用,但添加数据长度标识后,服务器可以精准接收完整数据,避免提前终止:

fun sendByteArrayToServer(byteArray: ByteArray) {
    CoroutineScope(Dispatchers.IO).launch {
        try {
            val serverAddress = "your_ip_here"
            val serverPort = 9999 

            val socket = Socket(serverAddress, serverPort)
            val outputStream = socket.getOutputStream()
            val dataOutputStream = DataOutputStream(outputStream)

            // 先发送数据总长度,让服务器明确接收目标
            dataOutputStream.writeInt(byteArray.size)
            dataOutputStream.write(byteArray)
            dataOutputStream.flush()

            Log.i("Server", "Byte array sent successfully, total bytes: ${byteArray.size}")

            socket.close()
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e("Server", "Error sending byte array to server: ${e.message}")
        }
    }
}

Python服务器代码适配(对应发送端的长度标识优化)

如果修改了发送端添加长度标识,服务器需要先读取长度,再接收对应字节的数据:

import socket

host = '0.0.0.0'
port = 9999

sample_rate = 16000
channels = 1

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(1)

print(f"Server listening on port {port}")

while True:
    client_socket, client_address = server_socket.accept()
    print(f"Accepted connection from {client_address}")

    audio_file = open("received_audio.pcm", "wb")

    try:
        # 先读取4字节的数据长度(对应Java的int类型)
        length_data = client_socket.recv(4)
        if not length_data:
            print("No length data received")
            continue
        total_length = int.from_bytes(length_data, byteorder='big')
        print(f"Expecting {total_length} bytes of audio data")

        received_bytes = 0
        while received_bytes < total_length:
            # 每次读取不超过剩余需要接收的字节数
            chunk_size = min(1024, total_length - received_bytes)
            data = client_socket.recv(chunk_size)
            if not data:
                break
            audio_file.write(data)
            received_bytes += len(data)
        print(f"Received {received_bytes}/{total_length} bytes")
    except Exception as e:
        print(f"Error receiving data: {e}")
    finally:
        audio_file.close()
        client_socket.close()

    print("PCM audio data received and saved as 'received_audio.pcm'")

额外注意事项

  • 确保Android端已申请RECORD_AUDIO和INTERNET权限,且运行时获取了录音权限。
  • 可先将录制的PCM数据保存到本地,验证数据长度是否正常,再排查网络发送问题。
  • PCM为原始音频数据,需用Audacity等工具打开,并设置对应采样率、位深和通道数才能正常播放。

内容的提问来源于stack exchange,提问作者Pulsarr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:47:05