Android 확장 프로그램

OpenSL ES 참조 사양을 확장한 OpenSL ES for Android는 Android와 호환 가능하며 Android 플랫폼의 성능 및 유연성을 활용할 수 있습니다.

Android 확장 프로그램용 API에 관한 정의는 OpenSLES_Android.h와 그 안에 포함된 헤더 파일에서 확인하세요. 확장 프로그램과 관련된 자세한 내용은 OpenSLES_Android.h를 참조하세요. 이 파일은 설치 루트의 sysroot/usr/include/SLES 디렉터리에 있습니다. 다른 언급이 없는 한 모든 인터페이스는 명시적입니다.

이 확장 프로그램은 Android 전용이기 때문에 애플리케이션을 다른 OpenSL ES 구현으로 이동하는 것이 제한됩니다. 확장 프로그램 사용을 자제하거나 #ifdef를 사용하여 컴파일 시 확장 프로그램을 제외하면 문제를 최소화할 수 있습니다.

다음 표는 Android OpenSL ES에서 각 객체 유형에 지원하는 Android 전용 인터페이스 및 데이터 로케이터를 보여줍니다. 셀의 값이 이면 인터페이스 및 데이터 로케이터를 각 객체 유형에 사용할 수 있다는 의미입니다.

기능 오디오 플레이어 오디오 레코더 엔진 출력 믹스
Android 버퍼 큐 예: 소스(디코딩) 아니요 아니요 아니요
Android 구성 아니요 아니요
Android 이펙트 아니요 아니요
Android 이펙트 기능 아니요 아니요 아니요
Android 이펙트 전송 아니요 아니요 아니요
Android 단순 버퍼 큐 예: 소스(재생) 또는 싱크(디코딩) 아니요 아니요
Android 버퍼 큐 데이터 로케이터 예: 소스(디코딩) 아니요 아니요 아니요
Android 파일 설명자 데이터 로케이터 예: 소스 아니요 아니요 아니요
Android 단순 버퍼 큐 데이터 로케이터 예: 소스(재생) 또는 싱크(디코딩) 예: 싱크 아니요 아니요

Android 구성 인터페이스

Android 구성 인터페이스는 객체에 플랫폼별 매개변수를 설정할 방법을 제공합니다. 이 인터페이스는 해당 객체를 인스턴스화하기 전에 앱이 사용할 수 있다는 점에서 다른 OpenSL ES 1.0.1 인터페이스와 다릅니다. 따라서, 개발자는 객체를 인스턴스화하기 전에 미리 구성할 수 있습니다. /sysroot/usr/include/SLES에 있는 OpenSLES_AndroidConfiguration.h 헤더 파일에는 다음과 같이 사용 가능한 구성 키 및 값이 포함되어 있습니다.

  • 오디오 플레이어용 스트림 유형(기본값 SL_ANDROID_STREAM_MEDIA)
  • 오디오 레코더용 레코드 프로필(기본값 SL_ANDROID_RECORDING_PRESET_GENERIC)

다음 코드 스니펫에서는 오디오 플레이어와 관련해 Android 오디오 스트림 유형을 설정하는 방법의 예시를 보여줍니다.

// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
// in the required interface ID array. Do not realize player yet.
// ...
SLAndroidConfigurationItf playerConfig;
result = (*playerObject)->GetInterface(playerObject,
    SL_IID_ANDROIDCONFIGURATION, &playerConfig);
assert(SL_RESULT_SUCCESS == result);
SLint32 streamType = SL_ANDROID_STREAM_ALARM;
result = (*playerConfig)->SetConfiguration(playerConfig,
    SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
assert(SL_RESULT_SUCCESS == result);
// ...
// Now realize the player here.

유사한 코드를 사용하여 오디오 레코더에 관한 프리셋을 구성할 수 있습니다.

// ... obtain the configuration interface as the first four lines above, then:
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
result = (*playerConfig)->SetConfiguration(playerConfig,
    RECORDING_PRESET, &presetValue, sizeof(SLuint32));

Android 이펙트 인터페이스

Android의 이펙트, 이펙트 전송 및 이펙트 기능 인터페이스는 애플리케이션에서 기기별 오디오 이펙트를 쿼리하고 사용할 수 있는 일반적인 메커니즘을 제공합니다. 기기 제조업체는 자사가 제공하는 사용 가능한 기기별 오디오 이펙트를 문서로 작성해야 합니다.

이동 가능한 애플리케이션에서는 Android 이펙트 확장 기능 대신 오디오 이펙트용 OpenSL ES 1.0.1 API를 사용해야 합니다.

Android 파일 설명자 데이터 로케이터

Android 파일 설명자 데이터 로케이터를 사용하면 오디오 플레이어용 소스를 읽기 권한이 있는 오픈 파일 설명자로 지정할 수 있습니다. 데이터 형식은 MIME여야 합니다.

앱이 APK에서 파일 설명자를 통해 자산을 읽기 때문에 이 확장 기능은 특히 네이티브 Asset Manager와 함께 사용하면 유용합니다.

Android 단순 버퍼 큐 데이터 로케이터 및 인터페이스

OpenSL ES 1.0.1 참조 사양에서 버퍼 큐는 오디오 플레이어용으로만 사용할 수 있으며 PCM 및 기타 데이터 형식과 호환됩니다. Android 단순 버퍼 큐 데이터 로케이터 및 인터페이스 사양은 다음의 두 가지 점만 제외하면 참조 사양과 동일합니다.

  • Android 단순 버퍼 큐는 오디오 레코더와 오디오 플레이어에 사용할 수 있습니다.
  • Android 단순 버퍼 큐에는 PCM 데이터 형식만 사용할 수 있습니다.

레코딩하려면 앱에서 빈 버퍼를 큐에 넣어야 합니다. 등록된 콜백이 시스템이 버퍼에 데이터를 쓰는 작업을 완료했다는 알림을 보내면 앱은 버퍼에서 읽기 작업을 할 수 있습니다.

재생도 같은 방식으로 실행됩니다. 하지만 향후 소스 코드 호환성을 위해 애플리케이션에서 OpenSL ES 1.0.1 버퍼 큐 대신 Android 단순 버퍼 큐를 사용하는 것이 좋습니다.

버퍼 큐 동작

Android 구현에는 재생이 SL_PLAYSTATE_STOPPED 상태가 되면 재생 커서가 현재 재생되는 버퍼의 처음으로 돌아가야 한다는 참조 사양의 요구사항이 포함되지 않습니다. 이 구현은 이 동작을 따를 수도 있고, 재생 커서 위치를 변경하지 않고 그대로 둘 수도 있습니다. 따라서 앱은 둘 중 어떤 동작이 일어날지 가정할 수 없습니다. 그러므로 SL_PLAYSTATE_STOPPED로 전환된 후에 BufferQueue::Clear() 메서드를 명시적으로 호출해야 합니다. 이렇게 하면 버퍼 큐는 알려진 상태로 설정됩니다.

마찬가지로, 버퍼 큐 콜백의 트리거가 SL_PLAYSTATE_STOPPED로의 전환인지 아니면 BufferQueue::Clear()의 실행인지를 제어하는 사양도 없습니다. 따라서 둘 중 어느 하나에 대한 종속성만 생성하지 말고 앱에서 두 경우 모두를 처리할 수 있도록 하는 것이 좋습니다.

객체 생성 시 동적 인터페이스

OpenSL ES 1.0.1의 Android 구현은 편의상 앱이 객체를 인스턴스화할 때 동적 인터페이스를 지정하도록 허용합니다. 이 방법은 인스턴스화 후 이러한 인터페이스를 추가하는 데 DynamicInterfaceManagement::AddInterface()를 사용하는 대신 사용할 수 있습니다.

확장 프로그램 보고

플랫폼에서 Android 확장 프로그램을 지원하는지 쿼리할 수 있는 메서드는 세 가지가 있습니다. 세 가지 메서드는 다음과 같습니다.

  • Engine::QueryNumSupportedExtensions()
  • Engine::QuerySupportedExtension()
  • Engine::IsExtensionSupported()

이러한 메서드는 API-level이 플랫폼 API 수준일 때 ANDROID_SDK_LEVEL_<API-level>를 반환합니다(예: ANDROID_SDK_LEVEL_23). 플랫폼 API 수준이 9 이상이면 해당 플랫폼이 이 확장 프로그램을 지원한다는 뜻입니다.

PCM으로 오디오 디코딩

이 섹션에서는 인코딩된 스트림을 즉시 재생하지 않고 PCM으로 디코딩하기 위해 OpenSL ES 1.0.1에 관한 Android 전용 확장 프로그램(지원 중단)을 설명합니다. 아래 표에는 이러한 확장 프로그램 사용에 관한 권장사항과 대안이 나와 있습니다.

API 수준 대안
15 이하 적합한 라이선스가 있는 오픈소스 코덱
16~20 MediaCodec 클래스 또는 적합한 라이선스가 있는 오픈소스 코덱
21 이상 <media/NdkMedia*.h> 헤더 파일의 NDK MediaCodec, MediaCodec 클래스 또는 적합한 라이선스가 있는 오픈소스 코덱

참고: 현재 MediaCodec API의 NDK 버전에 관한 문서는 없습니다. 하지만 네이티브 코덱 샘플 코드를 예시로 참조할 수 있습니다.

표준 오디오 플레이어는 출력 믹스를 데이터 싱크로 지정하여 오디오 기기에서 재생됩니다. Android 확장 프로그램의 차이점은 앱이 데이터 소스를 URI 또는 MIME 데이터 형식을 사용하여 설명된 Android 파일 설명자 데이터 로케이터로 지정하면 오디오 플레이어가 디코더 역할을 대신한다는 것입니다. 이 경우, 데이터 싱크는 PCM 데이터 형식을 사용하는 Android 단순 버퍼 큐 데이터 로케이터입니다.

이 기능은 게임에서 새로운 게임 레벨로 변경할 때 오디오 자산을 미리 로드하는 데 주로 사용되며, SoundPool 클래스가 제공하는 기능과 유사합니다.

애플리케이션은 처음에 빈 버퍼 세트를 Android 단순 버퍼 큐에 추가해야 합니다. 그런 다음, 앱은 버퍼를 PCM 데이터로 채웁니다. 각 버퍼가 채워질 때마다 Android 단순 버퍼 큐 콜백이 실행됩니다. 콜백 핸들러는 PCM 데이터를 처리하고 현재 비어 있는 버퍼를 다시 큐에 넣은 다음 반환합니다. 애플리케이션은 디코딩된 버퍼를 추적하는 역할을 합니다. 콜백 매개변수 목록에는 버퍼에 데이터가 포함되어 있는지 또는 다음에 버퍼를 큐에 넣어야 하는지를 나타내는 정보가 충분하지 않습니다.

데이터 소스는 스트림 끝에 SL_PLAYEVENT_HEADATEND 이벤트를 제공하여 암시적으로 스트림 끝(EOS)을 보고합니다. 앱은 수신한 데이터를 모두 디코딩한 후에는 더 이상 Android 단순 버퍼 큐 콜백을 호출하지 않습니다.

일반적으로 싱크와 인코딩된 데이터 소스의 PCM 데이터 형식은 샘플링 레이트, 채널 수 및 비트 깊이가 일치합니다. 그러나 다른 샘플링 레이트, 채널 수 및 비트 깊이로 디코딩할 수도 있습니다. 실제 PCM 형식을 감지하는 프로비저닝에 관한 자세한 내용은 메타데이터를 통해 디코딩된 PCM 데이터 형식 확인을 참조하세요.

Android PCM 디코딩 기능용 OpenSL ES는 일시중지 및 초기 탐색을 지원하지만 볼륨 조절이나 이펙트, 루프, 재생 속도는 지원하지 않습니다.

플랫폼 구현에 따라 디코딩에 유휴 상태로 두어서는 안 되는 리소스가 필요할 수 있습니다. 따라서 충분한 수의 빈 PCM 버퍼를 제공하는 것이 좋습니다. 그러지 않으면 디코더 기아 상태가 발생할 수 있습니다. 예를 들면 앱이 다른 빈 버퍼를 큐에 추가하지 않고 Android 단순 버퍼 큐 콜백에서 복귀하는 경우입니다. 확실히 밝혀진 바는 없지만, 디코더 기아 상태로 인해 디코딩된 PCM 데이터 삭제, 디코딩 프로세스 일시중지 또는 디코더 완전 종료 등이 발생할 수 있습니다.

참고: 인코딩된 스트림을 즉시 재생하지 않고 PCM으로 디코딩하려면 Android 4.x(API 수준 16~20)에서 실행되는 앱의 경우에 MediaCodec 클래스를 사용하고, Android 5.0(API 수준 21) 이상에서 실행되는 새로운 애플리케이션의 경우에는 NDK에 해당하는 <NdkMedia*.h>를 사용하는 것이 좋습니다. 이 헤더 파일은 설치 루트의 media/ 디렉터리에 있습니다.

PCM으로 스트리밍 ADTS AAC 디코딩

데이터 소스가 MIME 데이터 형식을 사용하는 Android 버퍼 큐 데이터 로케이터이고, 데이터 싱크는 PCM 데이터 형식을 사용하는 Android 단순 버퍼 큐 데이터 로케이터면 오디오 플레이어는 스트리밍 디코더 역할을 합니다. MIME 데이터 형식을 다음과 같이 구성합니다.

  • 컨테이너: SL_CONTAINERTYPE_RAW
  • MIME 유형 문자열: SL_ANDROID_MIME_AACADTS

이 기능은 AAC 오디오를 다루지만 재생하기 전에 맞춤 오디오를 처리해야 하는 스트리밍 미디어 애플리케이션에서 주로 사용됩니다. 오디오를 PCM으로 디코딩해야 하는 대부분의 애플리케이션에서는 PCM으로 오디오 디코딩에서 설명한 메서드를 사용해야 합니다. 이 메서드가 훨씬 간편하고 더 많은 오디오 형식을 처리하기 때문입니다. 여기에서 설명하는 기법은 훨씬 전문화된 접근 방식으로, 다음 조건이 모두 적용되는 경우에만 사용합니다.

  • 압축된 오디오 소스는 ADTS 헤더에 포함된 AAC 프레임의 스트림입니다.
  • 애플리케이션이 이 스트림을 관리합니다. 데이터의 위치는 식별자가 URI인 네트워크 리소스나 식별자가 파일 설명자인 로컬 파일이 아닙니다.

애플리케이션이 처음에 채워진 버퍼 세트를 Android 버퍼 큐에 추가해야 합니다. 각 버퍼는 하나 이상의 완전한 ADTS AAC 프레임을 담고 있습니다. 각 버퍼가 비워질 때마다 Android 버퍼 큐 콜백이 실행됩니다. 콜백 핸들러는 버퍼를 다시 채우고 큐에 추가한 다음, 반환해야 합니다. 애플리케이션이 인코딩된 버퍼를 추적하지 않아도 됩니다. 콜백 매개변수 목록에 다음으로 큐에 추가해야 할 버퍼를 나타내는 충분한 정보가 포함되어 있기 때문입니다. 스트림 종료(EOS)는 EOS 항목을 큐에 추가함으로써 명시적으로 표시됩니다. EOS 후에는 더 이상 큐에 항목을 추가할 수 없습니다.

디코더 기아 상태를 방지하려면 채워진 ADTS AAC 버퍼를 제공하는 것이 좋습니다. 예를 들어, 앱이 채워진 다른 버퍼를 큐에 넣지 않고 Android 버퍼 큐 콜백에서 복귀하면 디코더 기아 상태가 발생할 수 있습니다. 디코더 기아 상태로 인한 영향은 확실히 밝혀진 바는 없습니다.

스트리밍 디코딩 메서드는 데이터 소스를 제외한 모든 점이 PCM으로 오디오 디코딩에서 설명한 메서드와 동일합니다.

이름이 비슷하긴 하지만 Android 버퍼 큐는 Android 단순 버퍼 큐와 같지 않습니다. 스트리밍 디코더는 두 종류의 버퍼 큐를 모두 사용합니다. 즉, ADTS AAC 데이터 소스에는 Android 버퍼 큐를, PCM 데이터 싱크에는 Android 단순 버퍼 큐를 사용합니다. Android 단순 버퍼 큐 API에 관한 자세한 내용은 Android 단순 버퍼 큐 데이터 로케이터 및 인터페이스를 참조하세요. Android 버퍼 큐 API에 관한 자세한 내용은 설치 루트 아래의 docs/Additional_library_docs/openmaxal/ 디렉터리 내 index.html 파일을 참조하세요.

메타데이터를 통해 디코딩된 PCM 데이터 형식 확인

SLMetadataExtractionItf 인터페이스는 참조 사양에 포함되어 있습니다. 하지만 디코딩된 PCM 데이터의 실제 형식을 나타내는 메타데이터 키는 Android 전용입니다. OpenSLES_AndroidMetadata.h 헤더 파일은 이러한 메타데이터 키를 정의합니다. 이 헤더 파일은 설치 루트의 /sysroot/usr/include/SLES 디렉터리에 있습니다.

메타데이터 키 인덱스는 Object::Realize() 메서드가 실행을 마친 후에 바로 사용할 수 있습니다. 하지만, 연결된 값은 앱이 첫 번째 인코딩된 데이터를 디코딩한 후에만 사용할 수 있습니다. Object::Realize 메서드를 호출한 후에 메인 스레드의 키 인덱스를 쿼리하고, Android 단순 버퍼 큐 콜백 핸들러를 처음 호출할 때 해당 PCM 형식 메타데이터 값을 읽는 것이 좋습니다. 이 인터페이스를 사용한 예시는 NDK 패키지의 예시 코드를 참조하세요.

메타데이터 키 이름은 변경되지 않지만 키 인덱스는 문서화되어 있지 않으며 언제든지 변경될 수 있습니다. 애플리케이션은 다양한 실행 작업에서 인덱스가 지속되며 여러 객체 인스턴스가 동일한 실행 내에서 인덱스를 공유한다고 가정해서는 안 됩니다.

부동 소수점 데이터

Android 5.0(API 수준 21) 이상에서 실행되는 앱은 AudioPlayer에 데이터를 단정밀도 부동 소수점 형식으로 제공할 수 있습니다.

다음 예시 코드에서 Engine::CreateAudioPlayer() 메서드는 부동 소수점 데이터를 사용하는 오디오 플레이어를 생성합니다.

#include <SLES/OpenSLES_Android.h>
...
SLAndroidDataFormat_PCM_EX pcm;
pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
pcm.numChannels = 2;
pcm.sampleRate = SL_SAMPLINGRATE_44_1;
pcm.bitsPerSample = 32;
pcm.containerSize = 32;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
...
SLDataSource audiosrc;
audiosrc.pLocator = ...
audiosrc.pFormat = &pcm;
오디오 샘플링 페이지에서 부동 소수점 오디오에 관해 자세히 알아보세요.