AudioTrack: воспроизведение звука через Wi-Fi

У меня есть AudioTrack в моем приложении, которое настроено на режим Stream. Я хочу написать аудио, которое я получаю по беспроводному соединению. AudioTrack объявляется следующим образом:

mPlayer = new AudioTrack(STREAM_TYPE,
                         FREQUENCY,
                         CHANNEL_CONFIG_OUT,
                         AUDIO_ENCODING,
                         PLAYER_CAPACITY,
                         PLAY_MODE);

Если параметры определены следующим образом:

private static final int FREQUENCY = 8000,
                         CHANNEL_CONFIG_OUT = AudioFormat.CHANNEL_OUT_MONO,
                         AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT,
                         PLAYER_CAPACITY = 2048,
                         STREAM_TYPE = AudioManager.STREAM_MUSIC,
                         PLAY_MODE = AudioTrack.MODE_STREAM;

Однако, когда я пишу данные в AudioTrack с помощью write(), он будет играть прерывистый... Вызов

byte[] audio = packet.getData();
mPlayer.write(audio, 0, audio.length);

создается всякий раз, когда пакет принимается по сетевому соединению. Кто-нибудь знает, почему это звучит странно? Может быть, это имеет какое-то отношение к самому Wi-Fi-соединению? Я так не думаю, поскольку звук не звучит ужасно, наоборот, когда я отправляю данные с телефона Android в другой источник через UDP. Звук тогда звучит полным, а не изменчивым... Так кто-нибудь имеет представление о том, почему это происходит?

Ответ 1

Знаете ли вы, сколько байт в секунду вы получаете, среднее время между пакетами сравнивается и максимальное время между пакетами? Если нет, можете ли вы добавить код для его расчета?

Чтобы сохранить поток, вам необходимо усреднять 8000 выборок в секунду * 2 байта/образец = 16 000 байт в секунду.

Зазор более 2048 байт /(16000 байт в секунду) = 128 миллисекунд между входящими пакетами приведет к тому, что ваш поток будет работать сухим, а звук заикается.

Один из способов предотвратить увеличение размера буфера (PLAYER_CAPACITY). Более крупный буфер будет более способен обрабатывать изменения в размере и скорости входящего пакета. Стоимость дополнительной стабильности - это большая задержка при запуске воспроизведения, пока вы ждете, пока буфер не будет заполнен.

Ответ 2

Я частично решил его, поместив mPlayer.write(audio, 0, audio.length); в свой собственный Thread. Это отнимет часть изменчивости (из-за того, что запись является блокирующим вызовом), но она по-прежнему звучит порывисто после хорошей секунды или 2. У нее все еще есть значительная задержка в 2-3 секунды.

new Thread(){
    public void run(){
        byte[] audio = packet.getData();
        mPlayer.write(audio, 0, audio.length);
    }
}.start();

Просто анонимный Thread, который теперь пишет...

У кого-нибудь есть идея, как решить эту проблему?


Edit:

После некоторой дополнительной проверки и отладки я заметил, что это проблема с getBuffer. Я посмотрел на java-код AudioTrack и С++ код AudioTrack И я заметил, что он может отображаться только в коде на С++.

if (__builtin_expect(result!=NO_ERROR, false)) {
    LOGW(   "obtainBuffer timed out (is the CPU pegged?) "
            "user=%08x, server=%08x", u, s);
    mAudioTrack->start(); // FIXME: Wake up audioflinger
    timeout = 1;
}

Я заметил, что в этом фрагменте кода есть FIXME.: & Л; Но так или иначе, может ли кто-нибудь объяснить, как работает этот код на С++? У меня был некоторый опыт работы с ним, но он никогда не был таким сложным, как этот...


Изменить 2:

Я уже несколько раз пробовал разницу: разница в том, что я буферизую полученные данные, а затем, когда буфер заполнен некоторыми данными, он записывается в плеер. Тем не менее, игрок не отстает от потребления в течение нескольких циклов, затем появляется предупреждение obtainBuffer timed out (is the CPU pegged?), и нет никаких данных, написанных для игрока до тех пор, пока он не начнет возвращаться к жизни... После этого он будет постоянно получить данные, записанные на него, до тех пор, пока буфер не будет опустошен.

Другая небольшая разница заключается в том, что я передаю файл игроку сейчас. То есть, читая его в кусках, записывая эти куски в буфер. Это имитирует пакеты, принимаемые через Wi-Fi...

Я начинаю задаваться вопросом, является ли это просто проблемой ОС, которую имеет Android, и это не то, что я могу решить самостоятельно... У кого-нибудь есть идеи по этому поводу?


Изменить 3:

Я сделал больше тестов, но это не помогает мне дальше. Этот тест показывает мне, что я только задерживаюсь, когда впервые пытаюсь писать в AudioTrack. Это займет от 1 до 3 секунд. Я сделал это, используя следующий бит кода:

long beforeTime = Utilities.getCurrentTimeMillis(), afterTime = 0;
mPlayer.write(data, 0, data.length);
afterTime = Utilities.getCurrentTimeMillis();
Log.e("WriteToPlayerThread", "Writing a package took " + (afterTime - beforeTime) + " milliseconds");

Однако я получаю следующие результаты: Изображение Logcat http://img810.imageshack.us/img810/3453/logcatimage.png

Они показывают, что изначальное отставание происходит в начале, после чего AudioTrack постоянно продолжает получать данные... Мне действительно нужно, чтобы этот фиксированный...