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

Android端通过Socket.io播放流式音频时声音卡顿破碎的问题求助

Android端通过Socket.io播放流式音频时声音卡顿破碎的问题求助

大家好,我最近做了一个简单的语音通话应用,逻辑是把Android设备麦克风采集的音频通过Socket.io发送到Python写的服务器,再由服务器广播给接收端,接收端拿到音频数据后播放。

现在遇到的棘手问题是:Python客户端接收并播放音频完全正常,但Android端播放时声音严重卡顿、破碎,完全没法正常使用。我已经尝试了好几种调整方案,但还是没解决问题,实在找不到问题出在哪了,想请各位帮忙看看!

我的Python服务器代码:

import eventlet
from eventlet.wsgi import server as wsgi_server
from socketio import Server as SocketServer, WSGIApp

sio = SocketServer()
socket_app = WSGIApp(sio)

@sio.event
def broadcast_audio(sid, data):
    sio.emit('audio_stream', data)

wsgi_server(eventlet.listen(('', 5000)), socket_app)

Android端Java代码:

package dev.codewithcoffee.penpal;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;

import java.net.URISyntaxException;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

public class MainActivity extends AppCompatActivity {

    private Socket mSocket;
    private AudioTrack audioTrack;
    private int audioTrackOffset = 0;
    private int recordSampleRate, bufferSize;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LayoutInflater inflater = LayoutInflater.from(this);
        View callView = inflater.inflate(R.layout.activity_call, null);

        // recordSampleRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
        // bufferSize = AudioTrack.getMinBufferSize(recordSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        recordSampleRate = 44100;
        bufferSize = 4096;

        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, recordSampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
        audioTrack.play();

        initializeSocket();
        startBroadcastAudio();

        setContentView(callView);
    }

    private void initializeSocket() {
        IO.Options options = new IO.Options();
        options.forceNew = true;
        options.reconnection = true;
        options.reconnectionDelay = 2000;
        options.reconnectionDelayMax = 5000;

        try {
            mSocket = IO.socket("http://192.168.1.3:5000");
            mSocket.on("audio_stream", onAudioStream);

            if (!mSocket.connected()) {
                mSocket.connect();
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    private final Emitter.Listener onAudioStream = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            try {
                byte[] audioBytes = (byte[]) args[0];
                audioTrack.write(audioBytes, 0, audioBytes.length);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    private void broadcastAudio() {
        if (ActivityCompat.checkSelfPermission(this,
                android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            return;
        }

        AudioRecord audioRecorder = new AudioRecord(
                MediaRecorder.AudioSource.MIC,
                recordSampleRate,
                AudioFormat.CHANNEL_IN_STEREO,
                AudioFormat.ENCODING_PCM_16BIT,
                bufferSize
        );

        // Continuously read audio data from the recorder and send it to the server
        byte[] buffer = new byte[bufferSize];
        audioRecorder.startRecording();

        while (true) {
            int numBytesRead = audioRecorder.read(buffer, 0, bufferSize);
            if (numBytesRead > 0) {
                // Send audio data to server through socket connection
                mSocket.emit("broadcast_audio", buffer);
            }
        }
    }

    private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            // Run your function here
            broadcastAudio();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            // This method is called on the main thread
            // You can update the UI here if needed
        }
    }

    // Call this method to start the async task
    private void startBroadcastAudio() {
        MyAsyncTask task = new MyAsyncTask();
        task.execute();
    }
}

我已经尝试过的调试方案:

  • 手动设置不同的缓冲区大小(比如改成4096、8192等)
  • 用AudioTrack.getMinBufferSize()方法自动计算适配的缓冲区大小
  • 调整采样率为Android系统原生的输出采样率

但这些尝试都没解决Android端播放卡顿破碎的问题,希望各位能帮我排查一下问题所在,谢谢!

备注:内容来源于stack exchange,提问作者Aryan Arabshahi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 10:47:35