OpenSL ES for Android

이 페이지에서는 OpenSL ES™의 NDK 구현과 OpenSL ES 1.0.1의 참조 사양 간 차이점에 대해 자세히 살펴봅니다. 사양의 샘플 코드를 사용할 때 Android에서 작업하려면 수정해야 할 수도 있습니다.

다른 언급이 없는 한, 모든 기능은 Android 2.3(API 레벨 9) 이상에서 사용할 수 있습니다. 일부 기능은 Android 4.0(API 레벨 14)에서만 사용 가능하며 이 경우에 별도로 명시됩니다.

참고: Android 호환성 정의 문서(CDD)에는 호환 가능한 Android 기기의 하드웨어 및 소프트웨어 요구사항이 열거되어 있습니다. 전체 호환성 프로그램에 대한 자세한 내용은 Android 호환성을, 실제 CDD 문서는 CDD를 참조하세요.

OpenSL ES는 C++로도 액세스할 수 있는 C 언어 인터페이스를 제공합니다. 다음과 같은 Android Java API의 오디오 기능과 유사한 기능을 제공합니다.

모든 Android NDK(Native Development Kit)와 마찬가지로, OpenSL ES for Android의 주요 용도는 JNI(Java Native Interface) 를 사용하여 공유 라이브러리의 구현을 호출하도록 유도하는 것입니다. NDK는 순수한 C/C++ 애플리케이션을 작성하기 위한 도구가 아닙니다. 하지만 OpenSL ES는 모든 기능을 갖춘 API이기 때문에, Android 런타임 시 실행 중인 코드에 대한 upcall 없이 이 API만으로도 대부분의 오디오 요구사항을 처리할 수 있을 것으로 기대하고 있습니다.

참고: OpenSL ES를 기반으로 한다고 해도 Android 네이티브 오디오(고성능 오디오) API가 모든 OpenSL ES 1.0.1 프로필(게임, 뮤직 또는 전화)에 알맞는 구현은 아닙니다. 왜냐하면 Android가 프로필에서 필요로 하는 모든 기능을 구현하지는 않기 때문입니다. Android가 사양과 다르게 작동하는 것으로 알려진 사례는 Android 확장 프로그램 페이지에 설명되어 있습니다.

참조 사양에서 상속된 기능

OpenSL ES의 Android NDK 구현은 참조 사양에서 대부분의 기능 집합을 상속하며, 이때 몇 가지 제한 사항이 적용됩니다.

전역 진입점

OpenSL ES for Android는 Android 사양의 모든 전역 진입점을 지원합니다. 지원되는 진입점을 다음과 같습니다.

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

객체 및 인터페이스

다음 표에서는 OpenSL ES의 Android NDK 구현이 지원하는 객체 및 인터페이스를 보여줍니다. 셀에 가 표시되어 있으면 해당 기능을 이 구현에서 사용할 수 있다는 의미입니다.

Android NDK가 지원하는 객체 및 인터페이스

기능 오디오 플레이어 오디오 레코더 엔진 출력 믹스
저음 보강 아니요 아니요
버퍼 큐 아니요 아니요 아니요
버퍼 큐 데이터 로케이터 예: 소스 아니요 아니요 아니요
동적 인터페이스 관리
이펙트 전송 아니요 아니요 아니요
엔진 아니요 아니요 아니요
주변 반향 아니요 아니요 아니요
이퀄라이저 아니요 아니요
I/O 기기 데이터 로케이터 아니요 예: 소스 아니요 아니요
메타데이터 추출 예: PCM으로 디코딩 아니요 아니요 아니요
음소거 솔로 아니요 아니요 아니요
객체
출력 믹스 로케이터 예: 싱크 아니요 아니요 아니요
재생 아니요 아니요 아니요
재생 속도 아니요 아니요 아니요
프리페치 상태 아니요 아니요 아니요
프리셋 반향 아니요 아니요 아니요
레코드 아니요 아니요 아니요
탐색 아니요 아니요 아니요
URI 데이터 로케이터 예: 소스 아니요 아니요 아니요
가상화기 아니요 아니요
볼륨 아니요 아니요 아니요

다음 섹션에서는 일부 기능에 대한 제한 사항을 설명합니다.

제한 사항

표 1의 기능에는 몇 가지 제한 사항이 적용됩니다. 이는 참조 사양과 차이점이 있다는 의미입니다. 이 섹션의 나머지 부분에서는 둘 사이의 차이점에 대해 설명합니다.

동적 인터페이스 관리

OpenSL ES for Android는 RemoveInterface 또는 ResumeInterface를 지원하지 않습니다.

이펙트 조합: 주변 반향 및 프리셋 반향

동일한 출력 믹스에 주변 반향과 프리셋 반향이 모두 있을 수는 없습니다.

CPU 로드가 지나치게 높을 것으로 예상되면 플랫폼에서 이펙트 요청을 무시할 수 있습니다.

이펙트 전송

SetSendLevel()은 오디오 플레이어마다 한 개의 전송 레벨을 지원합니다.

주변 반향

주변 반향은 SLEnvironmentalReverbSettings 구조체의 reflectionsDelay, reflectionsLevel 또는 reverbDelay 필드를 지원하지 않습니다.

MIME 데이터 형식

MIME 데이터 형식은 URI 데이터 로케이터와 함께 오디오 플레이어에서만 사용할 수 있습니다. 오디오 레코더에서는 이 데이터 형식을 사용할 수 없습니다.

OpenSL ES의 Android 구현에서는 mimeTypeNULL 또는 유효한 UTF-8 문자열로 초기화해야 합니다. 또한 containerType도 유효한 값으로 초기화해야 합니다. 앱이 헤더로 식별할 수 없는 다른 구현 또는 콘텐츠 형식으로 이식하는 문제와 같이, 추가로 고려해야 할 사항이 없으면 mimeTypeNULL로, containerTypeSL_CONTAINERTYPE_UNSPECIFIED로 설정하는 것이 좋습니다.

Android 플랫폼이 다음 오디오 형식을 지원하는 경우에 한해 OpenSL ES for Android도 이 오디오 형식을 지원합니다.

  • WAV PCM
  • WAV alaw
  • WAV ulaw
  • MP3 Ogg Vorbis
  • AAC LC
  • HE-AACv1(AAC+)
  • HE-AACv2(Enhanced AAC+)
  • AMR
  • FLAC

참고: Android가 지원하는 오디오 형식 목록은 지원되는 미디어 형식을 참조하세요.

OpenSL ES의 Android 구현에서 위 항목을 포함한 데이터 형식을 처리할 때 다음과 같은 제한 사항이 적용됩니다.

  • AAC 형식은 MP4 또는 ADTS 컨테이너 내에 있어야 합니다.
  • OpenSL ES for Android는 MIDI를 지원하지 않습니다.
  • WMA는 AOSP에 포함되어 있지 않기 때문에 Google은 OpenSL ES for Android와의 호환성을 확인하지 않았습니다.
  • OpenSL ES의 Android NDK 구현은 DRM 또는 암호화된 콘텐츠의 직접 재생을 지원하지 않습니다. 보호된 오디오 콘텐츠를 재생하려면 먼저 DRM 제한 사항을 적용하는 앱을 사용하여 애플리케이션에서 콘텐츠의 암호를 해독해야 합니다.

객체 관련 메서드

OpenSL ES for Android는 다음과 같은 객체 조작 메서드를 지원하지 않습니다.

  • Resume()
  • RegisterCallback()
  • AbortAsyncOperation()
  • SetPriority()
  • GetPriority()
  • SetLossOfControlInterfaces()

PCM 데이터 형식

PCM은 버퍼 큐와 함께 사용할 수 있는 유일한 데이터 형식입니다. 지원되는 PCM 재생 구성의 특징은 다음과 같습니다.

  • 부호 없는 8비트 또는 부호 있는 16비트
  • 모노 또는 스테레오
  • Little-endian 바이트 순서
  • 샘플 속도:
    • 8,000Hz
    • 11,025Hz
    • 12,000Hz
    • 16,000Hz
    • 22,050Hz
    • 24,000Hz
    • 32,000Hz
    • 44,100Hz
    • 48,000Hz

OpenSL ES for Android가 레코딩 시 지원하는 구성은 기기에 따라 다릅니다. 일반적으로 16,000Hz 모노/부호 있는 16비트는 기기에 상관없이 사용할 수 있습니다.

이름 때문에 오해하기 쉽지만 samplesPerSec 필드 값의 단위는 mHz입니다. 실수로 잘못된 값을 사용하지 않도록 SL_SAMPLINGRATE_44_1과 같이 이러한 용도로 정의된 기호 상수 중 하나를 사용하여 필드를 초기화하는 것이 좋습니다.

Android 5.0(API 레벨 21) 이상은 부동 소수점 데이터를 지원합니다.

재생 속도

OpenSL ES 재생 속도는 객체가 데이터를 표시하는 속도를 나타내며, 단위는 보통 속도의 1/1,000 또는 퍼밀입니다. 예를 들면, 재생 속도 1,000퍼밀은 1,000/1,000 또는 보통 속도입니다. 속도 범위는 가능한 재생 속도 범위를 표현하는 닫힌 구간입니다.

재생 속도 범위 및 기타 기능에 대한 지원은 플랫폼 버전과 구현에 따라 다를 수 있습니다. 앱은 런타임에 PlaybackRate::GetRateRange() 또는 PlaybackRate::GetCapabilitiesOfRate()로 기기를 쿼리하여 이 기능들을 확인할 수 있습니다.

일반적으로 기기는 PCM 형식의 데이터 소스에 동일한 속도 범위를, 기타 형식에는 Unity 속도 범위, 1,000퍼밀 ~ 1,000퍼밀을 지원합니다. 즉, Unity 속도 범위는 실질적으로 단일 값입니다.

레코드

OpenSL ES for Android는 SL_RECORDEVENT_HEADATLIMIT 또는 SL_RECORDEVENT_HEADMOVING 이벤트를 지원하지 않습니다.

탐색

SetLoop() 메서드는 파일 전체 루핑을 활성화합니다. 루핑을 활성화하려면 startPos 매개변수를 0으로, endPos 매개변수를 SL_TIME_UNKNOWN으로 설정합니다.

버퍼 큐 데이터 로케이터

버퍼 큐 데이터 로케이터가 있는 오디오 플레이어 또는 레코더는 PCM 데이터 형식만 지원합니다.

I/O 기기 데이터 로케이터

로케이터를 Engine::CreateAudioRecorder()의 데이터 소스로 지정한 경우에 OpenSL ES for Android는 I/O 기기 데이터 로케이터를 사용할 수 있도록 지원합니다. 다음 코드 스니펫에 포함된 값을 사용하여 기기 데이터 로케이터를 초기화하세요.

SLDataLocator_IODevice loc_dev =
  {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
  SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};

URI 데이터 로케이터

OpenSL ES for Android는 URI 데이터 로케이터를 MIME 데이터 형식과 함께 오디오 플레이어에서만 사용할 수 있습니다. 오디오 레코더에서는 URI 데이터 로케이터를 사용할 수 없습니다. URI는 http:file: 구성표만 사용할 수 있습니다. https:, ftp: 또는 content: 같은 기타 구성표는 허용되지 않습니다.

Google은 Android 플랫폼의 오디오에 대한 rtsp: 지원을 확인하지 않았습니다.

데이터 구조

Android는 다음과 같은 OpenSL ES 1.0.1 데이터 구조를 지원합니다.

  • SLDataFormat_MIME
  • SLDataFormat_PCM
  • SLDataLocator_BufferQueue
  • SLDataLocator_IODevice
  • SLDataLocator_OutputMix
  • SLDataLocator_URI
  • SLDataSink
  • SLDataSource
  • SLEngineOption
  • SLEnvironmentalReverbSettings
  • SLInterfaceID

플랫폼 구성

OpenSL ES for Android는 멀티스레드 애플리케이션을 위해 고안되었기 때문에 스레드로부터 안전합니다. 이 라이브러리는 애플리케이션마다 1개의 엔진과 엔진당 최대 32개의 객체를 지원합니다. 사용 가능한 기기 메모리 및 CPU에 따라 가용 객체 수가 추가적으로 제한될 수 있습니다.

다음 엔진 옵션은 인식 가능하지만 slCreateEngine에서 무시합니다.

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

OpenMAX AL과 OpenSL ES는 동일한 애플리케이션에서 함께 사용할 수 있습니다. 이 경우, 내부에 하나의 공유 엔진 객체가 있고 OpenMAX AL과 OpenSL ES가 32개로 제한된 객체를 공유합니다. 애플리케이션은 두 개의 엔진을 생성하여 사용한 후 모두 제거해야 합니다. 구현은 두 번째 제거 작업 중 공유 엔진을 확실하게 제거할 수 있도록 공유 엔진의 참조 카운트를 유지합니다.

프로그래밍 노트

OpenSL ES 프로그래밍 노트는 OpenSL ES를 제대로 구현하는 데 도움이 되는 추가 정보를 제공합니다.

참고: 편의상 docs/opensles/OpenSL_ES_Specification_1.0.1.pdf의 NDK에는 OpenSL ES 1.0.1 사양의 복사본이 포함되어 있습니다.

플랫폼 문제

이 섹션에서는 이 API를 지원하는 최초 플랫폼 릴리스의 알려진 문제에 대해 설명합니다.

동적 인터페이스 관리

DynamicInterfaceManagement::AddInterface가 작동하지 않습니다. 대신, 주변 반향의 예시 코드에 나오는 것처럼 Create()에 전달된 배열에 인터페이스를 지정합니다.

OpenSL ES의 향후 버전 계획

Android 고성능 오디오 API는 Khronos Group OpenSL ES 1.0.1을 기반으로 합니다. Khronos는 Standard 버전을 수정한 1.1 버전을 출시했습니다. 수정 버전에는 새로운 기능 및 설명, 철자 오류 수정 내용, 일부 비호환성 문제가 포함되어 있습니다. 예상되는 대부분의 비호환성 문제는 비교적 사소하거나 Android가 지원하지 않는 OpenSL ES에 해당하는 사항입니다.

아래 바이너리 호환성 계획 섹션에 요약되어 있는 가이드라인을 준수하면 이 버전으로 개발한 애플리케이션은 Android 플랫폼의 향후 버전에서 제대로 작동할 것입니다.

참고: 향후 버전에서 소스 비호환성 문제를 해결하는 것이 목표가 아닙니다. 다시 말해, 최신 NDK 버전으로 업그레이드해도 새로운 API에 맞게 애플리케이션 소스 코드를 수정해야 할 수도 있습니다. 이러한 변경 사항은 대부분 사소한 내용입니다. 자세한 내용은 아래를 참조하세요.

바이너리 호환성 계획

향후 바이너리 호환성 문제를 개선하기 위해 다음 가이드라인을 준수하시기 바랍니다.

  • OpenSL ES 1.0.1에서 문서화된 일부 Android 지원 기능만 사용합니다.
  • 실패한 작업의 결과 코드에만 매달리지 말고 다른 결과 코드를 처리할 수 있도록 준비하세요.
  • 일반적으로 애플리케이션 콜백 핸들러는 제한된 컨텍스트에서 실행됩니다. 콜백 핸들러는 작업을 신속하게 수행한 다음, 되도록 빨리 복귀하도록 작성해야 합니다. 콜백 핸들러 내에서 복잡한 작업은 실행하지 마세요. 예를 들면, 버퍼 큐 완료 콜백 내에서 또 다른 버퍼를 큐에 추가할 수는 있지만 오디오 플레이어를 생성해서는 안 됩니다.
  • 추가 이벤트 유형을 수신할 수 있도록 콜백 핸들러를 자주 호출하고 콜백 핸들러가 인식하지 못하는 이벤트 유형은 무시해야 합니다. 활성화된 이벤트 유형으로 이루어진 이벤트 마스크를 사용하여 구성한 콜백은 여러 이벤트 유형 비트가 동시에 설정된 상태로 호출해야 합니다. 각 이벤트 비트에 대한 테스트에는 switch case 대신 "&"를 사용하세요.
  • 일반적인 진행 상황 표시로 프리페치 상태와 콜백을 사용하되, 특정한 하드코딩된 채우기 수준 또는 콜백 시퀀스에 의존하지 마세요. 프리페치 상태 채우기 수준의 의미와 프리페치 중 발견되는 오류에 대한 동작은 변경될 수 있습니다.

참고: 자세한 내용은 아래 버퍼 큐 동작 섹션을 참조하세요.

소스 호환성 계획

이미 언급한 바와 같이, Khronos Group OpenSL ES의 다음 버전에서는 소스 코드 비호환성 문제가 발생할 것으로 보입니다. 변경 가능성이 높은 항목은 다음과 같습니다.

  • 버퍼 큐 인터페이스는 특히 BufferQueue::Enqueue, slBufferQueueCallback의 매개변수 목록 및 필드 SLBufferQueueState.playIndex의 이름에 상당한 변화가 있을 것으로 예상됩니다. 애플리케이션 코드에서 대신 Android 단순 버퍼 큐를 사용하는 것이 좋습니다. 이러한 이유 때문에 NDK이 제공하는 예시 코드에서는 재생에 Android 단순 버퍼 큐를 사용했습니다. (레코딩과 PCM으로의 디코딩에도 Android 단순 버퍼 큐를 사용하고 있지만 이것은 OpenSL ES 1.0.1 Standard가 레코딩 또는 버퍼 큐 데이터 싱크로의 디코딩을 지원하지 않기 때문입니다.)
  • 참조가 전달한 입력 매개변수와 입력 값으로 사용된 SLchar * 구조체 필드에 const가 추가됩니다. 이로 인해 코드를 변경할 필요는 없습니다.
  • 현재 부호가 있는 일부 매개변수가 부호 없는 유형으로 대체됩니다. 매개변수 유형을 SLint32에서 SLuint32 또는 유사한 항목으로 변경하거나 형변환을 추가해야 할 수도 있습니다.
  • Equalizer::GetPresetName은 구현 메모리에 대한 포인터를 반환하는 대신, 문자열을 애플리케이션 메모리로 복사합니다. 이는 커다란 변화이므로 해당 메서드의 호출을 피하거나 구분해서 사용하는 것이 좋습니다.
  • 구조체 유형에 필드가 추가됩니다. 출력 매개변수의 경우 새로운 필드를 무시할 수 있지만, 입력 매개변수의 경우에는 새로운 필드를 초기화해야 합니다. 다행히 모든 새로운 필드는 Android가 지원하지 않는 영역에 해당하는 것으로 보입니다.
  • 인터페이스 GUID가 변경됩니다. 종속성 문제를 최소화하려면 GUID 대신 기호화된 이름을 사용하여 인터페이스를 참조하세요.
  • SLcharunsigned char에서 char로 변경됩니다. 이 변경 사항은 주로 URI 데이터 로케이터와 MIME 데이터 형식에 영향을 미칩니다.
  • SLDataFormat_MIME.mimeType의 이름은 pMimeType으로, SLDataLocator_URI.URI의 이름은 pURI로 바뀝니다. 코드가 이 변경 사항의 영향을 받지 않도록 하려면 필드 이름 대신 중괄호로 묶인, 쉼표로 구분된 값 목록을 사용하여 SLDataFormat_MIMESLDataLocator_URI 데이터 구조를 초기화하는 것이 좋습니다. 예시 코드에는 이 방법이 사용되었습니다.
  • SL_DATAFORMAT_PCM은 애플리케이션이 데이터 표시를 부호 있는 정수, 부호 없는 정수 또는 부동 소수점으로 지정하는 것을 허용하지 않습니다. Android 구현에서는 8비트 데이터를 부호 없는 정수로, 16비트 데이터를 부호 있는 정수로 가정합니다. 또한 실제 단위가 mHz이기 때문에 필드 samplesPerSec은 부적절한 명칭입니다. 이 문제는 다음 OpenSL ES 버전에서 해결될 것으로 보입니다. 애플리케이션이 데이터 표시를 명시적으로 지정하도록 허용하고 필드 이름을 수정할 수 있는 새로운 확장 PCM 데이터 형식이 도입될 예정입니다. 새로운 데이터 형식이 도입되고 현재 PCM 데이터 형식에 대한 지원이 중단되어도 이 데이터 형식은 계속 사용할 수 있기 때문에 코드를 즉시 변경할 필요는 없습니다.