Android MediaCodec AMR-NB декодирование в реальном времени
Я работаю над VoIP через сокет, как API. (С узкополосным соединением)
Мне нужно кодировать каждый голосовой кадр (20 мс) и отправлять через указанный API, а затем декодировать его на другой стороне.
Я пытался работать с Opus через NDK, но это никуда не делось, поэтому я решил использовать MediaCodec для кодирования / декодирования с AMR-NB.
Кодирование, похоже, работает, в результате получается буфер ожидаемого размера (160 необработанных байтов на 20 закодированных байтов + заголовок на 7,9 Кбит / с)
Но когда я обрабатываю закодированный буфер и пытаюсь декодировать его, я получаю INFO_OUTPUT_FORMAT_CHANGED
результат.
Кодер:
// Set up recorder
int recordBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
arec = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, recordBufferSize);
// Set Up codec
try {
encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate.getVal());
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
} catch (IOException e) {
e.printStackTrace();
return;
}
// Start Recording
int read;
byte[] buffer1 = new byte[bufferSize];
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
Frame frame;
try {
encoder.start();
arec.startRecording();
isRecording = true;
while (isRecording) {
read = arec.read(buffer1, 0, bufferSize);
inputBuffers = encoder.getInputBuffers();
outputBuffers = encoder.getOutputBuffers();
inputBufferIndex = encoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buffer1);
encoder.queueInputBuffer(inputBufferIndex, 0, buffer1.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = encoder.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);
//-------------
frame = new Frame(outData);
handler.onFrameEncoded(frame);
//------------
encoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
}
}
encoder.stop();
arec.stop();
} catch (Exception e) {
e.printStackTrace();
}
И декодер:
@Override
public void onFrameEncoded(Frame frame) {
try {
MediaCodec decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_BIT_RATE, 7950);
decoder.configure(format, null, null, 0);
decoder.start();
byte[] outData;
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
inputBuffers = decoder.getInputBuffers();
outputBuffers = decoder.getOutputBuffers();
// Fill decoder input buffer
inputBufferIndex = decoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(frame.Buffer);
decoder.queueInputBuffer(inputBufferIndex, 0, frame.Buffer.length, 0, 0);
}
// Get Output
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, -1);
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { // Just a check I threw in
MediaFormat format2 = decoder.getOutputFormat(); // Returns format RAW
}
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("Decoder", outData.length + " bytes encoded");
decoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
}
} catch (IOException e) {
e.printStackTrace();
}
}
Я искал везде INFO_OUTPUT_FORMAT_CHANGED
но не нашел ничего, что могло бы помочь мне..
1 ответ
Это ожидаемое поведение. Если бы вы использовали MediaMuxer, вы бы передали этот MediaFormat addTrack()
, который нуждается в некоторой части информации, которая должна прийти от MediaCodec. Это поведение было добавлено в API 18, когда был представлен MediaMuxer.
Посмотрите EncodeAndMuxTest для примера.
Если вы не используете MediaMuxer, вы можете игнорировать его. (Вы можете захотеть войти в систему, чтобы убедиться, что все по-прежнему так и должно быть.)