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

mp3 파일에서 pcm 데이터 얻기

0 추천
try
        {
            Log.e("Decode ", "Start");

            File aac = new File(Environment.getExternalStorageDirectory()+"/audio.raw");

            if(!aac.exists())
                aac.createNewFile();

            FileInputStream fis = new FileInputStream(audioPath);
            BufferedInputStream bis = new BufferedInputStream(fis);

            extractor.setDataSource(audioPath);

            // Select the first audio track we find.
            int numTracks = extractor.getTrackCount();

            for (int i = 0; i < numTracks; ++i) {
                format = extractor.getTrackFormat(i);
                mime = format.getString(MediaFormat.KEY_MIME);
                if (mime.startsWith("audio/")) {
                    extractor.selectTrack(i);
                    break;
                }
            }

            MediaMuxer muxerd = new MediaMuxer(aac.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

            format = MediaFormat.createAudioFormat("audio/raw", sample_rate, 1);
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            format.setInteger(MediaFormat.KEY_BIT_RATE, COMPRESSED_AUDIO_FILE_BIT_RATE);

            codec = MediaCodec.createDecoderByType("audio/raw");
            codec.configure(format, null, null, 0);
            codec.start();

            Log.e("line 1", "pass");

            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();

            int audioTrackIdx = 0;
            int totalBytesRead = 0;

            boolean inputEos = false;
            boolean outputEos = false;
            int inputBufIndex, outputBufIndex;
            int sampleSize;
            ByteBuffer readBuffer, writeBuffer;
            double presentationTimeUs = 0;

            Log.e("line 2", "pass");

            while (!outputEos) {
                if (!inputEos) {
                    inputBufIndex = codec.dequeueInputBuffer(timeoutUs);
                    if (inputBufIndex >= 0)
                    {
                        readBuffer = codec.getInputBuffer(inputBufIndex);
                        readBuffer.clear();

                        int i = readBuffer.remaining();
                        byte[] tempBuffer = new byte[i];

                        sampleSize = bis.read(tempBuffer);

                        if (sampleSize < 0) {
                            inputEos = true;
                            codec.queueInputBuffer(inputBufIndex, 0, 0, (long) presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        } else {
                            totalBytesRead += sampleSize;
                            if (little_endian)
                                readBuffer.put(tempBuffer, 0, sampleSize);
                            else {
                                ByteBuffer bb = ByteBuffer.wrap(tempBuffer);
                                ShortBuffer sb = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer();
                                short[] shorts = new short[i / 2];
                                sb.get(shorts);
                                for (short s : shorts) {
                                    readBuffer.putShort(s);
                                }
                            }

                            codec.queueInputBuffer(inputBufIndex, 0, sampleSize, (long) presentationTimeUs, 0);
                            presentationTimeUs = 1000000L * (totalBytesRead / 2) / sample_rate;
                        }
                    }
                }

                Log.e("line 3", "pass");

                outputBufIndex = codec.dequeueOutputBuffer(info, timeoutUs);
                if (outputBufIndex >= 0) {
                    writeBuffer = codec.getOutputBuffer(outputBufIndex);
                    writeBuffer.position(info.offset);
                    writeBuffer.limit(info.offset + info.size);

                    if (info.flags == 0 && info.size > 0)
                        muxerd.writeSampleData(audioTrackIdx, writeBuffer, info);

                    codec.releaseOutputBuffer(outputBufIndex, false);
                    writeBuffer.clear();
                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                        Log.d(TAG, "saw output EOS.");
                        outputEos = true;
                    }
                } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    MediaFormat oformat = codec.getOutputFormat();
                    audioTrackIdx = muxerd.addTrack(oformat);
                    muxerd.start();

                    format = oformat;

                    Log.e(TAG, "output format has changed to " + oformat);
                    Log.e("oformat key mime= ", String.valueOf(oformat.KEY_MIME));

                } else {
                    Log.d(TAG, "dequeueOutputBuffer returned " + outputBufIndex);
                }
            }

            fis.close();
            codec.stop();
            codec.release();

            muxerd.stop();
            muxerd.release();

            Log.e("DECODE", " COMPLETE");
        }
        catch (IOException e)
        {
            Log.e("IOExtractor ", "ERROR");

            e.printStackTrace();
        }
        catch (Exception e)
        {
            Log.e("Extractor ", "ERROR");

            e.printStackTrace();
        }

이런식으로 해서 mp3를 raw 파일로 만들어봤는데 mp3 크기와 raw 크기가 같아서 성공했을 줄 알았는데 그게 아니였던것 같습니다;; 어떻게해야 mp3에서 pcm 데이터를 얻을 수 있죠???
알파고 (4,320 포인트) 님이 2018년 10월 29일 질문

1개의 답변

+1 추천
 
채택된 답변

 extractor 생성은 했는데 샘플 읽는 코드가 안보입니다. 

readSampleData 로 데이터를 뽑아야 할텐데.

지금은 FileInputStream 로 mp3를 다이렉트로 읽어 frame단위로 코덱에 넣고 있어서, 제대로 디코딩을 못 시켜 0으로 된 데이터만 적어진듯 합니다.

아래 경로를 참조하세요..

https://stackoverflow.com/questions/22673011/how-to-extract-pcm-samples-from-mediacodec-decoders-output 

http://drcarter.tistory.com/162 (Audio Track write 하는 데이터가 PCM입니다.)

 

PS.  실제 동작엔 문제 없었지만.. 아래 부분은 인코딩시  AAC코덱 설정하는 코드일 텐데.. PCM 디코딩 시 넣고 있는데, 인코딩 시 사용하게 제거 되어야 할 듯 합니다.

format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
format.setInteger(MediaFormat.KEY_BIT_RATE, COMPRESSED_AUDIO_FILE_BIT_RATE);

익명사용자 님이 2018년 10월 29일 답변
알파고님이 2018년 11월 6일 채택됨
File raw = new File(Environment.getExternalStorageDirectory()+"/audio.raw");
            File rawTest = new File(Environment.getExternalStorageDirectory()+"/audioTest.raw");

            extractor.setDataSource(audioPath);

            MediaFormat format = extractor.getTrackFormat(0);
            extractor.selectTrack(0);

            MediaCodecList mediaCodeList = new MediaCodecList(MediaCodecList.ALL_CODECS);
            String name = mediaCodeList.findDecoderForFormat(format);
            MediaCodec codec = MediaCodec.createByCodecName(name);

            final FileOutputStream fis = new FileOutputStream(raw);
            final FileOutputStream fis2 = new FileOutputStream(rawTest);

            codec.setCallback(new MediaCodec.Callback()
            {
                @Override
                public void onInputBufferAvailable(@NonNull MediaCodec cd, int index) {

                    Log.e("mediaCodec = ", "onInputBufferAvailable");

                    ByteBuffer readBuffer = cd.getInputBuffer(index);
                    int size = extractor.readSampleData(readBuffer, 0);
                    long presentationTime = extractor.getSampleTime();

                    if(size < 0)
                    {
                        cd.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);

                        Log.e("audio extractor", " End Of Stream");
                    }
                    else
                    {
                        cd.queueInputBuffer(index, 0, size, extractor.getSampleTime(), 0);

                        extractor.advance();
                    }
                }

                @Override
                public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {

                    Log.e("mediaCodec = ", "onOutputBufferAvailable");

                    ByteBuffer writeBuffer = codec.getOutputBuffer(index);

                    byte[] b = new byte[info.size-info.offset];
                    int a = writeBuffer.position();
                    writeBuffer.get(b);
                    writeBuffer.position(a);

                    codec.releaseOutputBuffer(index, true);
                    Log.e("byte data : ", String.valueOf(b));

                    try
                    {
                        fis.write(b, 0, info.size-info.offset);
                    }
                    catch (IOException e)
                    {
                        Log.e("fis write ", "Error");
                        e.printStackTrace();
                    }

                }

                @Override
                public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {

                    Log.e("onError = ", "ERROR");

                }

                @Override
                public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {

                    Log.e("mediaCodec = ", "onOutputFormatChanged");

                    Log.e("format Change => ", String.valueOf(codec.getOutputFormat()));

                }
            });

            codec.configure(format, null, null, 0);
            int channel = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
            Log.e("chaneel = ", String.valueOf(channel));

            codec.start();

            Log.e("Decode", "END");

말씀해주신 링크 들어가서 참조해서 만들어봤는데요 이게 제대로 된건지 모르겠어요 ㅠㅠ 저는 mp3에서 pcm 을 받아서 raw 파일로 만들어서 한번 더 mp4 파일로 만들려고 하는데요;

녹음해서 얻은 pcm데이터를 이용하면 mp4 파일이 만들어지는데 저 수정한 코드로 시도하면 파일안에 뭔가가 들어가긴 들어가는데 동영상이 mp3 파일보다 시간이 더 길어지고 지지직 거리는 소리밖에 안나네요;;
예제 코드 찾아드린거라.. pcm생성에선 큰 문제가 안보이네요..
우선 raw파일이 제대로 되었는지 ffplay같은 것으로 확인 해 보세요.
ffplay -ar 샘플레이트 -ac 채널 audio.raw  로 돌려 재생이 되면  PCM은 잘 생성된겁니다.
이걸  aac로 다시 인코딩 해서 audio.aac를 만들어 다시 재생 테스트 해 보시고, 그 이후에 이 파일로 mp4 만드는 방식으로 차근차근 해 보세요.. 근데 .. ffmpeg로 만드는게 이경운 더 편한데. 왜 고생하시는진 모르겠네요...ㅎㅎ
https://github.com/WritingMinds/ffmpeg-android-java 써서 커멘드로 아래와 같이 입력하면 a.mp3가 a.mp4로 변환되거든요.. ('-c:a copy' 는 오디오 코덱 정보를 그대로 복사한다는 의미입니다.)
ffmpeg -i a.mp3 -c:a copy a.mp4
...