When I use mediaCodec to convert PCM to AAC, AAC is missing the last 50ms of data. Here is my code.
public class PcmToAAc {
private static final String TAG = "PcmToAac";
private static final int sampleRate = 16000;
private static final int bitRate = sampleRate * 2;
private static final int channelCount = 1;
private static final int SAMPLE_RATE_INDEX = 8;
private MediaCodec mMediaCodec;
private MediaCodec.BufferInfo encodeBufferInfo;
private FileOutputStream fos;
private FileInputStream fis;
public void test(Context context) throws IOException {
String pcmPath = context.getExternalFilesDir(null).getPath() + "/pcm";
String aacPath = context.getExternalFilesDir(null).getPath() + "/aac";
fis = new FileInputStream(pcmPath);
fos = new FileOutputStream(aacPath);
byte[] pcmData = new byte[640];
init();
while (true) {
int readSize = fis.read(pcmData);
if (readSize < 0) {
break;
}
byte[] tempBuf = new byte[readSize];
Log.d(TAG, "read size = " + readSize);
System.arraycopy(pcmData, 0, tempBuf, 0, readSize);
encode(tempBuf);
}
inputEndFlag();
ensureDataOutput();
release();
}
public void release() {
try {
fos.flush();
fos.close();
fis.close();
mMediaCodec.stop();
mMediaCodec.release();
} catch (IOException e) {
e.printStackTrace();
}
}
private void init() {
MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
try {
mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
encodeBufferInfo = new MediaCodec.BufferInfo();
} catch (IOException e) {
e.printStackTrace();
}
}
public void encode(byte[] pcmData) {
int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);
Log.d(TAG, "encode pcm data size = " + pcmData.length);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);
inputBuffer.clear();
inputBuffer.put(pcmData);
mMediaCodec.queueInputBuffer(inputBufferIndex, 0, pcmData.length, 0 ,0);
Log.d(TAG, "queue input buffer");
}
int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0);
while (outputBufferIndex >= 0) {
fetchEncodeAndReleaseOutputBuffer(outputBufferIndex);
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0);
}
}
void inputEndFlag() {
int inputIndex = mMediaCodec.dequeueInputBuffer(-1);
if (inputIndex >= 0) {
ByteBuffer inputByteBuf = mMediaCodec.getInputBuffer(inputIndex);
int inputBufferSize = 0;
int flag = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
mMediaCodec.queueInputBuffer(inputIndex, 0, inputBufferSize, 0, flag);
}
}
void ensureDataOutput() {
int outputIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 5000);
while (encodeBufferInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
if (outputIndex >= 0) {
fetchEncodeAndReleaseOutputBuffer(outputIndex);
outputIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 5000);
}
}
if (outputIndex >= 0) {
fetchEncodeAndReleaseOutputBuffer(outputIndex);
}
}
private void fetchEncodeAndReleaseOutputBuffer(int outputBufferIndex) {
//获取缓存信息的长度
int byteBufSize = encodeBufferInfo.size;
//添加ADTS头部后的长度
int bytePacketSize = byteBufSize + 7;
ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
outputBuffer.position(encodeBufferInfo.offset);
outputBuffer.limit(encodeBufferInfo.offset + encodeBufferInfo.size);
byte[] aacData = new byte[bytePacketSize];
addADTStoPacket(aacData, bytePacketSize);
outputBuffer.get(aacData, 7, byteBufSize);
outputBuffer.position(encodeBufferInfo.offset);
Log.d("TAG", "get aacData from output buf, size = " + (aacData.length - 7));
try {
fos.write(aacData);
} catch (IOException e) {
e.printStackTrace();
}
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
}
private void addADTStoPacket(byte[] packet, int packetLen) {
// AAC LC
int profile = 2;
// 16KHz
int freqIdx = SAMPLE_RATE_INDEX;
// CPE
int chanCfg = channelCount;
// fill in ADTS data
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF9;
packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
packet[6] = (byte) 0xFC;
}
}
I have used a lot of PCM files for testing, and after converting to AAC, the last 50ms or so of data will be lost. I have no idea. I use BUFFER_FLAG_END_OF_STREAM to make sure all data is output.
Please help me to find the reason for missing.
Looks like every time
mMediaCodec.dequeueOutputBuffer(...)returnsINFO_TRY_AGAIN_LATER(inside theencodefunction) one buffer of data is being lost.Also, consider using existing components for reading and writing audio files (MediaExtractor and MediaMuxer).