Android MediaCodec AMR-NB 实时解码
Android MediaCodec AMR-NB realtime decoding
我正在通过像 API 这样的套接字开发 VoIP。 (使用窄带连接)
我需要对每个语音帧 (20ms) 进行编码并通过所述 api 发送,然后在另一端对其进行解码。
我尝试通过 NDK 与 Opus 合作,但它无处可去,所以我决定使用 MediaCodec 来 encode/decode 和 AMR-NB。
编码似乎有效,产生了预期大小的缓冲区(160 个原始字节到 20 个编码字节 + header 7.9Kbps)
但是当我处理编码缓冲区并尝试对其进行解码时,我收到了 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
但找不到任何可以帮助我的东西..
这是预期的行为。如果您使用的是 MediaMuxer,您会将 MediaFormat 传递给 addTrack()
,这需要一些必须来自 MediaCodec 的信息。此行为是 added in API 18 引入 MediaMuxer 时。
参见 EncodeAndMuxTest 示例。
如果你没有使用 MediaMuxer,你可以忽略它。 (您可能想要记录它只是为了确认一切都仍然是它应该的样子。)
我正在通过像 API 这样的套接字开发 VoIP。 (使用窄带连接)
我需要对每个语音帧 (20ms) 进行编码并通过所述 api 发送,然后在另一端对其进行解码。
我尝试通过 NDK 与 Opus 合作,但它无处可去,所以我决定使用 MediaCodec 来 encode/decode 和 AMR-NB。
编码似乎有效,产生了预期大小的缓冲区(160 个原始字节到 20 个编码字节 + header 7.9Kbps)
但是当我处理编码缓冲区并尝试对其进行解码时,我收到了 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
但找不到任何可以帮助我的东西..
这是预期的行为。如果您使用的是 MediaMuxer,您会将 MediaFormat 传递给 addTrack()
,这需要一些必须来自 MediaCodec 的信息。此行为是 added in API 18 引入 MediaMuxer 时。
参见 EncodeAndMuxTest 示例。
如果你没有使用 MediaMuxer,你可以忽略它。 (您可能想要记录它只是为了确认一切都仍然是它应该的样子。)