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

MediaCodec 및 MediaSync 를 이용한 미디어플레이어 만드는데 문제가 있습니다..

0 추천

메인액티비티에는 SurfaceView와 재생, 일시중지, 정지버튼이 있고, 재생시 스레드를 생성, 시작시킵니다. 여기저기서 최대한 공부하고 작성된 코드들 참고해서 만들긴 했는데 문제는 재생시,

' 정상 출력 비디오, 오디오 + 싱크가 맞지 않는 매우 빠른 속도의 오디오 '

이렇게 겹쳐서 재생이 되는 상태입니다. 오디오 버퍼를 releaseOutput 하면서 렌더 여부를 false로 하고 복사한 버퍼만 queueAudio 하기 때문에 오디오가 이중으로 재생될 이유가 없을 것 같은데.. 무엇이 문제인지 도움 좀 부탁드립니다..

그리고 사실 복사 과정을 거쳐야 하는 이유도 잘 모르겠습니다.. 복사 과정을 거치지 않으면 싱크가 맞지 않던데..

그리고 재생이 끝났을 때, 반복문이 끝나면서 

Log.i("mintest", "first end");
Log.i("mintest", "second end");
Log.i("mintest", "third end");

이 로그가 실행 되어야 하는데, 첫번째만 실행이 됩니다. 이는 다른 문제를 일으키지는 않지만, 추가적으로 이유를 아신다면 가르쳐주시길 바랍니다!

  
    public boolean init(Surface surface, AssetFileDescriptor filePath) {
        eosReceived = false;
        isPaused = false;
        try {
            mExtractor = new MediaExtractor();
            mExtractor.setDataSource(filePath);

            mAudioExtractor = new MediaExtractor();
            mAudioExtractor.setDataSource(filePath);

            mMediaSync = new MediaSync();
            mMediaSync.setSurface(surface);

            Surface surface1 = mMediaSync.createInputSurface();

            for (int i = 0; i < mExtractor.getTrackCount(); i++) {
                MediaFormat format = mExtractor.getTrackFormat(i);
                String mime = format.getString(MediaFormat.KEY_MIME);

                if (mime.startsWith(VIDEO)) {
                    mExtractor.selectTrack(i);
                    mDecoder = MediaCodec.createDecoderByType(mime);

                    mDecoder.configure(format, surface1, null, 0);
                    mDecoder.start();
                    break;
                }
            }

            for (int i = 0; i < mAudioExtractor.getTrackCount(); i++) {
                MediaFormat format = mAudioExtractor.getTrackFormat(i);
                String mime = format.getString(MediaFormat.KEY_MIME);

                if (mime.startsWith(AUDIO)) {
                    mAudioExtractor.selectTrack(i);
                    mAudioDecoder = MediaCodec.createDecoderByType(mime);

                    mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);

                    mAudioDecoder.configure(format, null, null, 0);
                    mAudioDecoder.start();

                    int buffsize = AudioTrack.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

                    AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate,
                            AudioFormat.CHANNEL_OUT_STEREO,
                            AudioFormat.ENCODING_PCM_16BIT,
                            buffsize,
                            AudioTrack.MODE_STREAM);
                    mMediaSync.setAudioTrack(audioTrack);
                    break;
                }
            }

            info = new BufferInfo();
            audioInfo = new BufferInfo();
            inputBuffers = mDecoder.getInputBuffers();
            audioInputBuffers = mAudioDecoder.getInputBuffers();

            isInput = true;
            isAudioInput = true;


        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }

    @Override
    public void run() {

        while (!eosReceived) {
            if (!isPaused) {
                if (isInput) {
                    int inputIndex = mDecoder.dequeueInputBuffer(10000);
                    if (inputIndex >= 0) {

                        ByteBuffer inputBuffer = inputBuffers[inputIndex];
                        int sampleSize = mExtractor.readSampleData(inputBuffer, 0);

                        if (mExtractor.advance() && sampleSize > 0) {
                            mDecoder.queueInputBuffer(inputIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
                        } else {

                            mDecoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            isInput = false;
                        }
                    }
                }

                int outIndex = mDecoder.dequeueOutputBuffer(info, 10000);
                switch (outIndex) {
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:

                        break;

                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:

                        break;

                    case MediaCodec.INFO_TRY_AGAIN_LATER:
    
                        break;

                    default:
                        mDecoder.releaseOutputBuffer(outIndex, true );

                        break;
                }

                if (isAudioInput) {
                    int audioInputIndex = mAudioDecoder.dequeueInputBuffer(10000);
                    if (audioInputIndex >= 0) {
                        ByteBuffer audioInputBuffer = audioInputBuffers[audioInputIndex];
                        int audioSampleSize = mAudioExtractor.readSampleData(audioInputBuffer, 0);
                        if (mAudioExtractor.advance() && audioSampleSize > 0) {
                            mAudioDecoder.queueInputBuffer(audioInputIndex, 0, audioSampleSize, mAudioExtractor.getSampleTime(), 0);
                        } else {
                            mAudioDecoder.queueInputBuffer(audioInputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            isAudioInput = false;
                        }
                    }
                }

                int audioOutIndex = mAudioDecoder.dequeueOutputBuffer(audioInfo, 10000);
                switch (audioOutIndex) {
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        mAudioDecoder.getOutputBuffers();
                        break;

                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                        break;

                    case MediaCodec.INFO_TRY_AGAIN_LATER:

                        break;

                    default:

                        ByteBuffer decoderBuffer = mAudioDecoder.getOutputBuffer(audioOutIndex);
                        ByteBuffer copyBuffer = ByteBuffer.allocate(decoderBuffer.remaining());
                        copyBuffer.put(decoderBuffer);
                        copyBuffer.flip();
                        mAudioDecoder.releaseOutputBuffer(audioOutIndex, false);
                        mMediaSync.queueAudio(copyBuffer, audioOutIndex, audioInfo.presentationTimeUs);

                        break;

                }

                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {

                    break;
                }

                mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(0.5f));
            } else {

                mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(0.f));
            }

        }


        Log.i("mintest", "first end");

        mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(0.f));
        mMediaSync.release();

        mDecoder.stop();
        mDecoder.release();
        mExtractor.release();

        Log.i("mintest", "second end");

        ...

        Log.i("mintest", "third end");
    }
    
...
}

 

익명사용자 님이 2019년 2월 11일 질문

1개의 답변

0 추천
mDecoder.releaseOutputBuffer(outIndex, true );  가 불리면 화면을 그릴텐데. .mediaSync와 연동이 제대로 되지 않는 것으로 생각됩니다.

https://github.com/skysign/MediaSyncExample 를 참조 해 보세요.

 
PS.,  MediaSync 가 마쉬멜로 부터 지원이나,  아직 킷캣, 젤리빈 단말도 많다보니  특정 단말만 지원하는게 아니면,   사용하기엔 애매 할 수 있습니다. 애써 개발하고 못 써먹을 수 있으니, 확인 해 보세요..
익명사용자 님이 2019년 2월 11일 답변
2019년 2월 11일 수정
답변 감사합니다. 링크해주신 예제를 이미 참조하여 만든 것인데, 문제가 생기네요.. 제 추측으로는
releaseOutputBuffer(outIndex, true); 를 통해 비디오는 문제 없이 나오는데,
releaseOutputBuffer(outIndex, false); 를 통한 오디오가 재생 되어버리는 문제 같습니다.
결과적으로 mediaSync.queueAudio() 로 정상 속도의 오디오 재생과
releaseOutputBuffer(outIndex, false) 의 비정상 속도의 오디오 재생이 겹쳐진 것 같은데 혹시 releaseOutputBuffer()의 렌더링 여부를 false 로 해도 오디오가 재생이 되는게 맞나요? 맞다면 재생되지 않게끔 단순히 바이트버퍼를 제거하고 MediaCodec으로 빈 버퍼를 되돌려주는 방법이 따로 있는지 좀 알려주시면 정말 감사하겠습니다..

+ 제 추측이 잘못 되었을 수도 있겠는 건, releaseOutputBuffer(outIndex, false) 가 오디오를 출력하는 문제라고 가정했으니 mediaSync.queueAudio() 를 주석처리했을 때, 비정상 속도의 오디오가 출력되어야 하는데 아무 소리도 나오질 않네요.. 반대로 releaseOutput()을 호출하지 않아도 소리가 나지 않는 건 당연하고요..ㅠㅠ
예제의 비디오는 mVideoDecoder.releaseOutputBuffer(i, bufferInfo.presentationTimeUs * 1000); 를 통해 특정 시간에 그리게 되어 있습니다.
하지만 만드신 코드는 releaseOutputBuffer(outIndex, true);  로 특정 시간이 아니라 그냥 그리라고 되어 있어서 시간 맞추는데 문제가 있을 것으로 생각되서 말씀 드린 겁니다.
...