【问题标题】:AudioTrack stream is choppyAudioTrack 流断断续续
【发布时间】:2023-11-15 14:35:01
【问题描述】:

我正在尝试通过服务器将音频发送到我播放它的安卓手机(流式传输)。 服务器每 50 毫秒发送一次音频字节。

此解决方案有效,但不正确(在同一循环中接收和写入音轨(写入需要一些时间))

private class SoundTask extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {

        [...]

            AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 16000,4, AudioFormat.ENCODING_PCM_16BIT, BUF_SIZE,    AudioTrack.MODE_STREAM);
            [...]
            byte[] output;              
            at.play();
            byte[] buf = new byte[BUF_SIZE];

            //Here is the loop
            while(true)
            {                                    
                DatagramPacket pack = new DatagramPacket(buf, BUF_SIZE);                    
                socket.receive(pack);
                at.write(pack.getData(), 0, pack.getLength());      
            }
        }           
    }

所以我尝试将接收和写入分开在两个线程中,但它不起作用,几秒钟后声音断断续续! 我的两个线程(使用 ArrayBlockingQueue)
制作人:

public void run() {     
        [...]
        byte[] buf = new byte[bufferSize];  
        while(true)
        {                            
            DatagramPacket pack = new DatagramPacket(buf, bufferSize);              
            socket.receive(pack);               
            queueAudio.put(pack);                       
        }
    }

消费者:

    public void run() {
    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,  16000, 4,   AudioFormat.ENCODING_PCM_16BIT, bufferSize,     AudioTrack.MODE_STREAM);
    audioTrack.play();
    while(true)
    {
        DatagramPacket packet;
        try 
        {
            packet = queueAudio.take();                         
            audioTrack.write(packet.getData(), 0, packet.getData().length); 
        }  

我注意到当我的队列大小等于 1 时,声音是正常的,而当它优于 1(2-3 秒后)时,声音是断断续续的。

有人可以向我解释为什么我的音轨不起作用吗? 提前致谢!

编辑:

好的,我明白了这个问题。问题来自UDP数据包的接收,通常我应该每50毫秒接收一次数据包。

我计算了接收两个数据包之间的时间间隔,似乎有时这个间隔优于 100 毫秒,然后小于 1 毫秒!!!

当这种情况发生时,声音会变得断断续续。我简单地解决了这个问题:当间隔小于 1 毫秒时,我不会将数据放入队列中。

这行得通,声音不是断断续续的,但有时会有一些小切。

我该如何解决这个问题?有没有办法定期接收我的 UDP 数据包?我应该尝试在服务中进行接收吗???

【问题讨论】:

  • 你在这 50 毫秒内处理了多少?也许你消耗的太少了?还有哪些质量方面?
  • 不,我不这么认为,50 毫秒的缓冲区大小为 1600 字节。编码格式为 PCM 16 位,采样率为 16000 Hz。

标签: android audio android-asynctask audiotrack


【解决方案1】:

您应该为 AudioTrack 使用比您写入的块大小更大的缓冲区大小。来自AudioTrack 类的文档:

bufferSizeInBytes 从中读取音频数据以进行播放的内部缓冲区的总大小(以字节为单位)。如果轨道的创建模式是 MODE_STREAM,您可以将数据以小于或等于此大小的块写入此缓冲区,并且通常使用总大小的 1/2 的块来允许双缓冲

使用更大的缓冲区创建 AudioTrack:

AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 
                16000, 4, 
                AudioFormat.ENCODING_PCM_16BIT,
                BUF_SIZE * 2, // use a buffer twice the size of
                              // the chunks you will write to it
                AudioTrack.MODE_STREAM);

但要继续写入相同大小的块。希望双缓冲能消除波动。如果这不起作用,您还可以尝试增加BUF_SIZE

【讨论】:

  • 我尝试了使用更大缓冲区的解决方案,但这不起作用,声音仍然断断续续。也增加 BUF_SIZE 不会改变任何东西。我经常有 3 秒长的“正常”声音,然后它变得波涛汹涌。但是感谢您的帮助;)
【解决方案2】:

好的,我明白了这个问题。 问题来自 UDP 数据包的接收,通常我应该每 50 毫秒接收一次数据包。

我计算了接收两个数据包之间的时间间隔,似乎有时这个间隔优于 100 毫秒,然后小于 1 毫秒 !!!

当这种情况发生时,声音会变得断断续续。 我简单地解决了这个问题:当间隔小于 1 毫秒时,我不会将数据放入队列中。

这行得通,声音不是断断续续的,但有时会有一些小切。

我该如何解决这个问题?有没有办法定期接收我的 UDP 数据包? 我应该尝试在服务中进行接收吗???

【讨论】:

    【解决方案3】:

    首先,确保 AudioTrack 没有等待音频被消耗,而其缓冲区中没有其他内容可播放,因此请尝试在不从网络流式传输 PCM 数据的情况下验证瓶颈不是来自您的流。分析您的代码以确定瓶颈的来源。

    其次,不要在onProgressUpdate 内写入 AudioTrack,因为这是在 UI 线程内完成的,并且写入 AudioTrack 会阻塞。您需要在另一个单独的线程中创建、启动和提供您的 AudioTrack 对象。

    最后,如果您打算播放音频超过几秒钟,我不建议使用 AsyncTask,引用文档:AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.)

    【讨论】:

    • 流没有问题,但我的音轨。我尝试制作 2 个线程,一个用于接收数据包,另一个用于 Audiotrack。它是使用 ArrayBlocking 队列 (developer.android.com/reference/java/util/concurrent/…) 的典型生产者/消费者。但是声音还是很刺耳。我的第一个实现是在 DoInBackGround 的音轨中接收和写入数据包,但这不是我应该做的方式。
    • 编辑:我注意到,在我的日志中,当我的 BlockingQueue 的大小等于 1 时,声音是正常的,但是当大小大于 1 时,声音是断断续续的