Я пытаюсь реализовать
AudioRecord (MIC) ->
PCM -> AAC Encoder
AAC -> PCM Decode
-> AudioTrack?? (SPEAKER)
с MediaCodec
на Android 4.1+ (API16).
Во-первых, я успешно (но не уверен, правильно оптимизирован) реализован PCM -> AAC Encoder
на MediaCodec
, как указано ниже
private boolean setEncoder(int rate)
{
encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
return true;
}
INPUT: PCM Битрейт = 44100 (Гц) x 16 (бит) x 1 (Монорал) = 705600 бит/с
ВЫХОД: AAC-HE Битрейт = 64 x 1024 (бит) = 65536 бит/с
Итак, размер данных приблизительно сжат x11
, и я подтвердил это, выполнив наблюдение за журналом
- AudioRecoder: прочитано 4096 байт
- AudioEncoder: закодировано 369 байт
размер данных приблизительно сжат x11
, насколько это хорошо.
Теперь у меня есть UDP-сервер для получения закодированных данных, а затем его декодирование.
Профиль декодера устанавливается следующим образом:
private boolean setDecoder(int rate)
{
decoder = MediaCodec.createDecoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
decoder.configure(format, null, null, 0);
return true;
}
Так как размер буфера пакета UDPserver 1024
- UDPserver: получено 1024 байта
и поскольку это сжатые данные AAC, я ожидаю, что размер декодирования будет
приблизительно 1024 x11
, однако фактический результат
- AudioDecoder: декодировано 8192 байта.
Это примерно x8
, и я чувствую, что что-то не так.
Код декодера выглядит следующим образом:
IOudpPlayer = new Thread(new Runnable()
{
public void run()
{
SocketAddress sockAddress;
String address;
int len = 1024;
byte[] buffer2 = new byte[len];
DatagramPacket packet;
byte[] data;
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
try
{
decoder.start();
isPlaying = true;
while (isPlaying)
{
try
{
packet = new DatagramPacket(buffer2, len);
ds.receive(packet);
sockAddress = packet.getSocketAddress();
address = sockAddress.toString();
Log.d("UDP Receiver"," received !!! from " + address);
data = new byte[packet.getLength()];
System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());
Log.d("UDP Receiver", data.length + " bytes received");
//===========
inputBuffers = decoder.getInputBuffers();
outputBuffers = decoder.getOutputBuffers();
inputBufferIndex = decoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
decoder.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0)
{
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
Log.d("AudioDecoder", outData.length + " bytes decoded");
decoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
}
//===========
}
catch (IOException e)
{
}
}
decoder.stop();
}
catch (Exception e)
{
}
}
});
полный код:
https://gist.github.com/kenokabe/9029256
также требуется разрешение:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
Член fadden, который работает в Google, сказал мне
Похоже, я не устанавливаю позицию и не ограничиваюсь выходным буфером.
Я прочитал VP8 Encoding Nexus 5 возвращает пустые/0-фреймы, но не уверен, как правильно реализовать.
ОБНОВЛЕНИЕ: я вроде понял, где изменить для
Похоже, я не устанавливаю позицию и не ограничиваю выходного буфера.
, поэтому добавьте 2 строки в цикл while Encoder и Decoder следующим образом:
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
https://gist.github.com/kenokabe/9029256/revisions
Однако результат тот же.
и теперь, я думаю, ошибки:
W/SoftAAC2﹕ AAC decoder returned error 16388, substituting silence.
указывает, что этот декодер полностью не работает с первого. Это снова вопрос the data is not seekable
. Поиск потоков AAC на Android Очень неутешительно, если декодер AAC не может обрабатывать потоковые данные таким образом, но только с добавлением некоторого заголовка.
UPDATE2: UDP-приемник сделал ошибку, поэтому изменил
https://gist.github.com/kenokabe/9029256
Теперь ошибка
W/SoftAAC2﹕ AAC decoder returned error 16388, substituting silence.
исчезли!!
Итак, это означает, что декодер работает без ошибок, по крайней мере,
однако это журнал из 1 цикла:
D/AudioRecoder﹕ 4096 bytes read
D/AudioEncoder﹕ 360 bytes encoded
D/UDP Receiver﹕ received !!! from /127.0.0.1:39000
D/UDP Receiver﹕ 360 bytes received
D/AudioDecoder﹕ 8192 bytes decoded
PCM (4096) → AACencoded (360) → UDP-AAC (360) → (предположительно) PCM (8192)
Конечный результат - это 2x размер исходного PCM, что-то по-прежнему не так.
Итак, мой вопрос здесь будет
-
Можете ли вы правильно оптимизировать код для правильной работы?
-
Правильно ли использовать API
AudioTrack
для воспроизведения необработанных данных декодированного PCM на лету, и можете ли вы показать мне правильный способ сделать это? Примерный код оценивается.
Спасибо.
PS. Мой проект нацелен на Android4.1 + (API16), я читал, что в API18 (Andeoid 4.3+) проще, но по очевидным причинам совместимости, к сожалению, мне нужно пропустить MediaMuxer и т.д. Здесь...