마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

무전기앱을 개발중 Mediacodec를 사용한 디코딩 문의드립니다.

0 추천

안녕하세요.

무전기 앱을 만들생각으로 작업중입니다.

마이크->녹음(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());
        }
    }

 

변강욱 (260 포인트) 님이 2021년 10월 29일 질문

답변 달기

· 글에 소스 코드 보기 좋게 넣는 법
· 질문에 대해 추가적인 질문이나 의견이 있으면 답변이 아니라 댓글로 달아주시기 바랍니다.
표시할 이름 (옵션):
개인정보: 당신의 이메일은 이 알림을 보내는데만 사용됩니다.
스팸 차단 검사:
스팸 검사를 다시 받지 않으려면 로그인하거나 혹은 가입 하세요.
...