안녕하세요.
무전기 앱을 만들생각으로 작업중입니다.
마이크->녹음(AudioRecord)->PCM->인코더(Mediacodec)-->UDP-->디코더(Mediacodec)-재생(AudioTrack)
위 단계중 인코더와 디코더만 빼면 훌륭하게 작동되는 무전기입니다만, 용량이 너무 커서 인코더와 디코더를 넣게 되었습니다.
인코딩된 용량은 PCM에 비해 확실히 줄어들더군요.(거의 10배)
그런데 마이크에 1 2 3 4라고 말하면 1 2 3 까지만 들립니다.
같은 인코딩 데이터를 다시 디코딩을 해보면 4 1 2 3 이라고 들립니다.(계속해서 반복하면 계속 달라집니다.)
디코더의 마지막 버퍼에 문제가 있을 것 같아... 여러가지 시도를 해보았지만, 잘 풀리지 않아 이렇게 질문을 남깁니다.
-마지막 인코딩된 데이터를 디코더에 넣을때 BUFFER_FLAG_END_OF_STREAM 설정하기도 했고
-마지막에 디코딩버퍼에 충분히 다른 소리데이터를 넣어보기도 했습니다.(대충은 동작하지만, 정석은 아닌듯 합니다.)
아래는 대략적인 레코더, 인코더, 디코더, 플레이어의 소스입니다.
선배님들의 좋은 의견 부탁드립니다.
레코더
public Recorder(Handler handler){
_handler = handler;
mAudioRecorder = new AudioRecord(
CommonDefine.AUDIO_SOURCE,
CommonDefine.AUDIO_SAMPLE_RATE,
CommonDefine.AUDIO_CHANNEL,
CommonDefine.AUDIO_ACC_PROFILE,
CommonDefine.AUDIO_BUFFER_SIZE );
mRecordingBuffer = new byte[CommonDefine.AUDIO_BUFFER_SIZE];
mAudioRecorder.startRecording();
mRecordingThread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
if(isRecording.get()){
record();
}
}
}
});
mRecordingThread.start();
}
private void record(){
byte[] recordedBuffer = null;
recordedBuffer = read();
if(recordedBuffer != null)
_handler.sendMessage(
_handler.obtainMessage(CommonDefine.TALK, recordedBuffer)
);
}
private byte[] read(){
try{
int readSize = mAudioRecorder.read(
mRecordingBuffer,
0,
mRecordingBuffer.length );
byte[] buffer = null;
if(readSize > 0){
buffer = Arrays.copyOf(mRecordingBuffer, readSize);
}
return buffer;
} catch (Exception e){
Log.e(TAG, "read-" + e.toString());
return null;
}
}
인코더
public byte[] encode(byte[] buffer){
try{
mInputBuffers = mEncoder.getInputBuffers();
mOutputBuffers = mEncoder.getOutputBuffers();
inputBufferIndex = mEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = mInputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buffer);
long pts = System.nanoTime() / 1000 - mPresentTimeUs;
mEncoder.queueInputBuffer(inputBufferIndex, 0, buffer.length, pts, 0);
}
mBufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 0);
ArrayList<byte[]> outputDatas = new ArrayList<>();
while (outputBufferIndex >= 0){
ByteBuffer outputBuffer = mOutputBuffers[outputBufferIndex];
outputBuffer.position(mBufferInfo.offset);
outputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);
byte[] outData = new byte[mBufferInfo.size];
outputBuffer.get(outData);
outputDatas.add(outData);
mEncoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 0);
}
return BytesTool.merge(outputDatas);
} catch (Exception e){
Log.e(TAG, "encode-" + e.toString());
}
return null;
}
public void setEncoder(){
mPresentTimeUs = System.nanoTime() / 1000;
try{
mEncoder = MediaCodec.createEncoderByType(CommonDefine.AUDIO_MIME);
MediaFormat mediaFormat = new MediaFormat();
mediaFormat.setString(MediaFormat.KEY_MIME, CommonDefine.AUDIO_MIME);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CommonDefine.AUDIO_CHANNEL_COUNT);
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, CommonDefine.AUDIO_SAMPLE_RATE);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, CommonDefine.AUDIO_BIT_RATE);
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, CommonDefine.AUDIO_ACC_PROFILE);
mEncoder.configure(mediaFormat, null, null, CommonDefine.AUDIO_ENCODE_FLAG);
} catch (Exception e){
Log.e(TAG, "setEncoder-" + e.toString());
}
}
디코더
public void play(byte[] buffer){
try{
mInputBuffers = mDecoder.getInputBuffers();
mOutputBuffers = mDecoder.getOutputBuffers();
inputBufferIndex = mDecoder.dequeueInputBuffer(TIMEOUT_US);
if(inputBufferIndex >= 0){
mInputBuffer = mInputBuffers[inputBufferIndex];
mInputBuffer.clear();
mInputBuffer.put(buffer);
long pts = System.nanoTime() / 1000 - mPresentTimeUs;
mDecoder.queueInputBuffer(inputBufferIndex, 0, buffer.length, pts, 0);
}
mBufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = mDecoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
while (outputBufferIndex >= 0){
mOutputBuffer = mOutputBuffers[outputBufferIndex];
mOutputBuffer.position(mBufferInfo.offset);
mOutputBuffer.limit(mBufferInfo.offset + mBufferInfo.size);
byte[] outData = new byte[mBufferInfo.size];
mOutputBuffer.get(outData);
mPlayer.play(outData);
//Log.i("testEncode", BytesTool.byteArrayToHexaString(outData));
Log.i("testEncode", Integer.toString(cnt) + "-" + Integer.toString(outData.length));
cnt++;
mDecoder.releaseOutputBuffer(outputBufferIndex, true);
outputBufferIndex = mDecoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
}
} catch (Exception e){
Log.e(TAG, "play-" + e.toString());
} finally {
}
}
private void setDecoder(){
mPresentTimeUs = System.nanoTime() / 1000;
try{
mDecoder = MediaCodec.createDecoderByType(CommonDefine.AUDIO_MIME);
MediaFormat mediaFormat = new MediaFormat();
mediaFormat.setString(MediaFormat.KEY_MIME, CommonDefine.AUDIO_MIME);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CommonDefine.AUDIO_CHANNEL_COUNT);
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, CommonDefine.AUDIO_SAMPLE_RATE);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, CommonDefine.AUDIO_BIT_RATE);
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, CommonDefine.AUDIO_ACC_PROFILE);
// int profile = 2; //AAC LC
// int freqIdx = 11; //8KHz
// int chanCfg = 1; //Mono
//
// ByteBuffer csd = ByteBuffer.allocate(2);
// csd.put(0, (byte) (profile << 3 | freqIdx >> 1));
// csd.put(1, (byte)((freqIdx & 0x01) << 7 | chanCfg << 3));
// mediaFormat.setByteBuffer("csd-0", csd);
mDecoder.configure(mediaFormat, null, null, 0);
} catch (Exception e){
Log.e(TAG, "setDecoder-" + e.toString());
}
}