출처 : http://thdev.net/567


오랜만에 개발관련 글을 작성합니다. 오늘의 개발 관련 글은 MediaCodec을 이용한 AAC 디코딩을 위한 필요한 부분에 대해서 작성합니다. 제가 작성하는 부분은 파일을 읽어 들여 디코딩하는것이 아닌 byte를 읽어서 Decode 하는것에 대해서 짧게 설명하려고 합니다. 우선 AAC가 무엇인지부터 간단하게 살펴보겠습니다.



AAC

 AAC는 Advanced Audio Coding의 약자로 고급 오디오 부호화라고 한국어로 해석하고 있습니다. AAC는 또 다른 명칭으로 MPEG-4 Audio 라는 이름을 사용하고 있습니다. 일반적으로 MPEG-4 Audio라고 말하지 않고 AAC라는 명칭을 사용합니다.


AAC 설명 (영문) : http://en.wikipedia.org/wiki/Advanced_Audio_Coding


 모든 코덱은 표준 규약이 존재하며, 이 규약에 따라서 인코딩/디코딩을 진행하게 됩니다.

 간단하게 인코딩이라는 말은 압축을 말하는데 오디오는 RAW 데이터를 PCM 데이터라고 말합니다. 이를 AAC 규약에 맞게 압축해둔것이 바로 AAC가 됩니다.


 반대로 디코딩은 압축을 해제한 것을 말하는데 AAC를 규약에 맞게 디코딩한 것이 바로 PCM 데이터라고 생각하시면 됩니다.


 이에 대한 문서 정의는 아래 링크를 통해서 확인이 가능하실것입니다. 디코딩/인코딩을 자체를 개발한다면 아래와 같은 문서가 꼭 필요한것이지만 제가 필요한 정보는 MediaCodec에서 디코딩하기 위한 정보를 필요로 합니다. 역시 해당 문서가 필요합니다.


MPEG-4 Audio : http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio


 문서를 간단하게 보면, AAC의 종류도 많은것을 알 수 있으며, Audio samplerate, 채널수 등도 다양한것을 확인할 수 있습니다. 저는 안드로이드에서 필요로 하는 내용을 Android의 풀 소스를 참고하여 만들어봤습니다. 추후 해당 코드는 Github에 업로드 예정입니다.


필요한 API

 MediaCodec : http://developer.android.com/reference/android/media/MediaCodec.html

 MPEG-4 Audio 문서 : http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config



Android에서 AAC 디코딩을 위한 방법

 일반적으로는 로컬 파일을 디코딩시에는 MediaCodec과 MediaExtractor(파일 읽어 들이는 API) 2개만 가지고 처리가 가능하나, 그렇게 하지 않을 경우를 예로 작성한 코드로, AAC 디코딩을 위해 필요한 정보를 직접 생성하여 디코딩하는 방법을 설명합니다.


MediaCodec을 통해 AAC 디코딩시 필요로 하는 정보

 Audio Decoder Type (디코딩을 위한 코덱을 호출할때 필요

   - API : http://developer.android.com/reference/android/media/MediaCodec.html#createDecoderByType(java.lang.String)

 디코딩 대상 파일의 채널 수

  - 1, 2채널이 기본이며 그외 지원은 Android API와 MPEG-4 Audio 문서를 살펴보세요.

 디코딩 대상 파일의 Sample rate

  - Android에서 제공하는 Sample rate는 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 입니다.

 디코딩 대상 파일의 profile 정보 

   - Android API 참고 : http://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel.html



 Audio Decoder Type의 경우 문서에서 정의한대로 직접 써넣으면 되지만 디코딩 채널수, Samplerate, profile 정보는 직접 만들어주거나 MediaExtractor에서 생성해주도록 만들어야 합니다. 저는 직접 생성하는 방법으로 아래와 같은 코드를 만들었습니다.


관련 예제 코드

 Github : https://github.com/taehwandev/MediaCodecExample/tree/master/src/net/thdev/audiodecoder


예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * The code profile, Sample rate, channel Count is used to
 * produce the AAC Codec SpecificData.
 * Android 4.4.2/frameworks/av/media/libstagefright/avc_utils.cpp refer
 * to the portion of the code written.
 *
 * MPEG-4 Audio refer : http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config
 *
 * @param audioProfile is MPEG-4 Audio Object Types
 * @param sampleRate
 * @param channelConfig
 * @return MediaFormat
 */
private MediaFormat makeAACCodecSpecificData(int audioProfile, int sampleRate, int channelConfig) {
    MediaFormat format = new MediaFormat();
    format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm"); // AAC의 Decoder type
    format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate); // sample rate 정의
    format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channelConfig); // channel 정의
     
    int samplingFreq[] = { // Android 참고 코드상 아래와 같은 samplerate를 지원
        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
        16000, 12000, 11025, 8000
    };
     
    // Search the Sampling Frequencies
        // 아래 코드를 통해 0~11 에 맞는 값을 가져와야 합니다.
        // 일반적으로 44100을 사용하고 있으며, 여기에서는 4번에 해당됩니다.
    int sampleIndex = -1;
    for (int i = 0; i < samplingFreq.length; ++i) {
        if (samplingFreq[i] == sampleRate) {
            Log.d("TAG", "kSamplingFreq " + samplingFreq[i] + " i : " + i);
            sampleIndex = i;
        }
    }
     
    if (sampleIndex == -1) {
        return null;
    }
     
        /* 디코딩에 필요한 csd-0의 byte를 생성합니다. 이 부분은 Android 4.4.2의 Full source를 참고하여 작성
         * csd-0에서 필요한 byte는 2 byte 입니다. 2byte에 필요한 정보는 audio Profile 정보와
         * sample index, channelConfig 정보가 됩니다.
        */
    ByteBuffer csd = ByteBuffer.allocate(2);
        // 첫 1 byte에는 Audio Profile에 3 bit shift 처리합니다. 그리고 sample index를 1bit shift 합니다.
    csd.put((byte) ((audioProfile << 3) | (sampleIndex >> 1)));
     
    csd.position(1);
        // 다음 1 byte에는 sample index를 7bit shift 하고, channel 수를 3bit shift 처리합니다.
    csd.put((byte) ((byte) ((sampleIndex << 7) & 0x80) | (channelConfig << 3)));
    csd.flip();
       // MediaCodec에서 필요하는 MediaFormat에 방금 생성한 "csd-0"을 저장합니다.
    format.setByteBuffer("csd-0", csd); // add csd-0
     
    return format;
}



마무리

 해당 코드는 샘플코드이며, Android Full source를 참고하여서 작성하였습니다.

 해당 코드는 다음의 경로에서 확인이 가능합니다.

  https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/avc_utils.cpp 에서 "MakeAACCodecSpecificData"를 참고하여 작성한 코드입니다.

Posted by Real_G