OpenSL ES para Android

En esta página, se proporciona información detallada acerca de cómo la implementación de OpenSL ES™ del NDK difiere de la especificación de referencia para OpenSL ES 1.0.1. Al usar código fuente de la especificación, probablemente debas modificarlo para que funcione en Android.

A menos que se indique lo contrario, todas las funciones están disponibles en Android 2.3 (nivel de API 9) y versiones posteriores. Algunas funciones solo están disponibles para Android 4.0 (nivel de API 14); esas funciones se indican.

Nota: En el Documento de definición de compatibilidad de Android (CDD), se enumeran los requisitos de hardware y software para un dispositivo Android compatible. Consulta Compatibilidad con Android para obtener más información sobre el programa de compatibilidad completo, y CDD para acceder al documento de CDD real.

OpenSL ES proporciona una interfaz de idioma C a la que también se puede acceder usando C++. Esa interfaz ofrece funciones similares a las partes de audio de estas Android Java API:

Al igual que con todo el Kit de desarrollo nativo (NDK) de Android, el objetivo principal de OpenSL ES para Android es facilitar la implementación de bibliotecas compartidas para poder llamarlas usando la interfaz nativa Java (JNI). El NDK no está diseñado para escribir aplicaciones C/C++ puras. No obstante, OpenSL ES es una API repleta de funciones y esperamos que puedas satisfacer la mayoría de tus necesidades de audio usando solo esta API, sin llamadas a código ejecutándose en el tiempo de ejecución de Android.

Nota: Si bien se basa en OpenSL ES, la API de audio nativo de Android (audio de alto rendimiento) no es una implementación que cumpla con los perfiles de OpenSL ES 1.0.1 (juegos, música o teléfono). Esto se debe a que Android no implementa todas las funciones requeridas por los perfiles. Los casos conocidos en los cuales Android se comporta de forma diferente respecto de la especificación se describen en la página Extensiones de Android.

Funciones heredadas de la especificación de referencia

La implementación de OpenSL ES en NDK de Android hereda gran parte del conjunto de funciones de la especificación de referencia, con ciertas limitaciones.

Puntos de entrada global

OpenSL ES para Android admite todos los puntos de entrada global que se indican en la especificación de Android. Entre estos puntos de entrada se incluyen los siguientes:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

Objetos e interfaces

En la tabla siguiente, se muestran los objetos y las interfaces que admite la implementación de OpenSL ES en NDK de Android. Si aparece en la celda, la función está disponible en esta implementación.

Compatibilidad de NDK de Android con objetos e interfaces.

Función Reproductor de audio Grabador de audio Motor Combinación de salida
Aumento de graves No No
Cola de búfer No No No
Localizador de datos de la cola de búfer Sí: Fuente No No No
Administración de interfaz dinámica
Envío de efectos No No No
Motor No No No
Reverberación ambiental No No No
Ecualizador No No
Localizador de datos de dispositivos de E/S No Sí: Fuente No No
Extracción de metadatos Sí: Decodificación en PCM No No No
Silenciamiento No No No
Objeto
Localizador de combinación de salida Sí: Receptor No No No
Reproducir No No No
Frecuencia de reproducción No No No
Estado de captura previa No No No
Reverberación preestablecida No No No
Grabación No No No
Búsqueda No No No
Localizador de datos de URI Sí: Fuente No No No
Virtualizador No No
Volumen No No No

En la sección siguiente, se explican las limitaciones para algunas de estas funciones.

Limitaciones

Se aplican ciertas limitaciones a las funciones de la Tabla 1. Esas limitaciones representan diferencias respecto de la especificación de referencia. En el resto de esta sección, se proporciona información acerca de esas referencias.

Administración de interfaz dinámica

OpenSL ES para Android no admite RemoveInterface ni ResumeInterface.

Combinaciones de efectos: reverberación ambiental y preestablecida

No puede haber reverberación ambiental y preestablecida en la misma combinación de salida.

La plataforma podría ignorar las solicitudes de efectos si calcula que la carga de la CPU puede ser muy elevada.

Envío de efectos

SetSendLevel() admite un solo nivel de envío por reproductor de audio.

Reverberación ambiental

La reverberación ambiental no admite los campos reflectionsDelay, reflectionsLevel ni reverbDelay de la estructura de SLEnvironmentalReverbSettings.

Formato de datos MIME

Puedes usar el formato de datos MIME solo con el localizador de datos de URI y solo para un reproductor de audio. No puedes usar este formato de datos para un grabador de audio.

Para la implementación de OpenSL ES en Android es necesario que inicialices mimeType en NULL o en una string UTF-8 válida. También debes inicializar containerType en un valor válido. Ante la ausencia de otras consideraciones, como la portabilidad a otras implementaciones u otros formatos de contenido que una app no pueda identificar por encabezado, te recomendamos establecer mimeType en NULL y containerType en SL_CONTAINERTYPE_UNSPECIFIED.

OpenSL ES para Android admite los siguientes formatos de audio, siempre que la plataforma de Android también lo haga:

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

Nota: Para obtener una lista de los formatos de audio compatibles con Android, consulta Formatos de medios compatibles.

Las siguientes limitaciones se aplican al manejo de estos y otros formatos en esta implementación de OpenSL ES:

  • Los formatos AAC deben residir en un contenedor de MP4 o ADTS.
  • OpenSL ES para Android no admite MIDI.
  • El formato WMA no forma parte de AOSP y no hemos verificado su compatibilidad con OpenSL ES para Android.
  • La implementación de OpenSL ES en NDK de Android no admite la reproducción directa de DRM ni contenido encriptado. Para reproducir contenido de audio protegido, debes desencriptarlo en tu aplicación antes de reproducirlo y tu app debe cumplir con las restricciones de DRM.

Métodos relacionados con objetos

OpenSL ES para Android no admite los siguientes métodos para manipular objetos:

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

Formato de datos PCM

El formato PCM es el único formato de datos que puedes usar con colas de búfer. Las configuraciones de reproducción de PCM tienen las siguientes características:

  • 8 bits sin firmar o 16 bits con firma.
  • Mono o estéreo.
  • Ordenamiento de bytes little endian.
  • Índices de muestra:
    • 8000 Hz.
    • 11 025 Hz.
    • 12 000 Hz.
    • 16 000 Hz.
    • 22 050 Hz.
    • 24 000 Hz.
    • 32 000 Hz.
    • 44 100 Hz.
    • 48,000 Hz.

Las configuraciones que OpenSL ES para Android admite para la grabación dependen del dispositivo; generalmente, se encuentra disponible la de 16,000 Hz mono y 16 bits independientemente del dispositivo.

El valor del campo samplesPerSec se indica en unidades de milliHz, a pesar del nombre confuso. Para evitar usar accidentalmente el valor incorrecto, te recomendamos inicializar este campo usando una de las constantes simbólicas definidas para este fin, como SL_SAMPLINGRATE_44_1.

Android 5.0 (nivel de API 21) y las versiones posteriores admiten datos de punto flotante.

Frecuencia de reproducción

La frecuencia de reproducción de OpenSL ES indica la velocidad a la cual un objeto presenta datos, expresada en milésimas de la velocidad normal o por mil. Por ejemplo, una frecuencia de reproducción de 1,000 por mil es 1,000/1,000, o la velocidad normal. Un rango de frecuencias es un intervalo cerrado que expresa un rango de frecuencias de reproducción posibles.

La compatibilidad con rangos de frecuencia de reproducción y otras capacidades puede variar según la versión de la plataforma y la implementación. Tu app puede determinar estas capacidades en el tiempo de ejecución usando PlaybackRate::GetRateRange() o PlaybackRate::GetCapabilitiesOfRate() para consultar al dispositivo.

Un dispositivo generalmente admite el mismo rango de frecuencias para una fuente de datos en formato PCM y un rango de frecuencia de unidades de 1,000 por mil a 1,000 por mil para otros formatos; es decir, el rango de frecuencia de unidades es efectivamente un valor individual.

Grabación

OpenSL ES para Android no admite los eventos SL_RECORDEVENT_HEADATLIMIT ni SL_RECORDEVENT_HEADMOVING.

Búsqueda

El método SetLoop() admite la ejecución en bucle de archivos completos. Para habilitar la ejecución en bucle, fija el parámetro startPos en 0 y el parámetro endPos en SL_TIME_UNKNOWN.

Localizador de datos de la cola de búfer

Un reproductor o grabador de audio con localizador de datos para una cola de búfer solo admite el formato de datos PCM.

Localizador de datos de dispositivos de E/S

OpenSL ES para Android solo admite un localizador de datos de dispositivos de E/S cuando especificas el localizador como la fuente de datos para Engine::CreateAudioRecorder(). Inicializa el localizador de datos del dispositivo usando los valores contenidos en el siguiente fragmento de código:

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

Localizador de datos de URI

OpenSL ES para Android solo puede usar el localizador de datos de URI con el formato de datos MIME, y solo para un reproductor de audio. No puedes usar el localizador de datos de URI para un grabador de audio. El URI solo puede usar los esquemas http: y file:. Otros esquemas, como https:, ftp: o content: no están permitidos.

No hemos verificado la compatibilidad de rtsp: con audio en la plataforma de Android.

Estructuras de datos

Android admite las siguientes estructuras de datos de OpenSL ES 1.0.1:

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

Configuración de la plataforma

OpenSL ES para Android se diseñó para aplicaciones con varios subprocesos y es seguro para subprocesos. Admite un solo motor por aplicación y hasta 32 objetos por motor. La memoria y la CPU del dispositivo disponibles pueden limitar aun más la cantidad de objetos que pueden usarse.

Las siguientes opciones de motor se reconocen, pero slCreateEngine las ignora:

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

OpenMAX AL y OpenSL ES se pueden usar juntos en la misma aplicación. En este caso, hay un solo objeto de motor compartido internamente, y el límite de 32 objetos se comparte entre OpenMAX AL y OpenSL ES. La aplicación debe crear ambos motores, usarlos y, por último, destruirlos. La implementación mantiene un recuento de referencia del motor compartido para que se pueda destruir correctamente durante la segunda operación de destrucción.

Notas de programación

Las notas de programación de OpenSL ES proporcionan información complementaria para garantizar la implementación adecuada de OpenSL ES.

Nota: Para tu conveniencia, hemos incluido una copia de la especificación de OpenSL ES 1.0.1 con el NDK en docs/opensles/OpenSL_ES_Specification_1.0.1.pdf.

Problemas de la plataforma

En esta sección, se describen los problemas conocidos en la versión inicial de la plataforma que admite estas API.

Administración de interfaz dinámica

DynamicInterfaceManagement::AddInterface no funciona. En su lugar, especifica la interfaz en la matriz que se pasa a Create(), como se muestra en el ejemplo de código para la reverberación ambiental.

Planificación para versiones futuras de OpenSL ES

Las API de audio de alto rendimiento de Android se basan en OpenSL ES 1.0.1 de Khronos Group. Khronos ha lanzado una versión revisada 1.1 de la versión estándar. En la versión revisada, se incluyen nuevas características, aclaraciones y correcciones de errores tipográficos, y también algunas incompatibilidades. La mayoría de las incompatibilidades previstas son relativamente leves o se dan en áreas de OpenSL ES que Android no admite.

Una aplicación desarrollada con esta versión debe funcionar en versiones futuras de la plataforma de Android, siempre que sigas las pautas de la sección Planificación para la compatibilidad con ejecutables, a continuación.

Nota: La compatibilidad en el futuro con código fuente no es un objetivo. Es decir, si realizas una actualización a una nueva versión del NDK, probablemente debas modificar el código fuente de tu aplicación para cumplir con los requisitos de la nueva API. Se espera que la mayoría de los cambios como estos sean menores; consulta los detalles a continuación.

Planificación para la compatibilidad con ejecutables

Recomendamos que tu aplicación siga estas pautas para mejorar la compatibilidad con ejecutables en el futuro:

  • Usa solo el subconjunto documentado de características de OpenSL ES 1.0.1 compatibles con Android.
  • No te bases en un código de resultado específico para una operación incorrecta; debes estar preparado para trabajar con un código de resultado diferente.
  • Los controladores de devolución de llamada de la aplicación generalmente se ejecutan en un contexto restringido. Deben estar escritos de modo que puedan realizar su trabajo rápidamente y luego regresar lo más rápido posible. No ejecutes operaciones complejas dentro de un controlador de devolución de llamada. Por ejemplo, en una devolución de llamada de finalización de cola de búfer, puedes disponer en cola otro búfer, pero no crear un reproductor de audio.
  • Los controladores de devolución de llamada deben estar preparados para que se llamen con mayor o menor frecuencia, a fin de recibir tipos de eventos adicionales, y deben ignorar tipos de eventos que no reconocen. Las devoluciones de llamada que estén configuradas con una máscara de eventos compuesta por los tipos de eventos habilitados deben estar preparadas para recibir llamadas con varios bits de tipo de evento configurados simultáneamente. Usa "&" para probar cada bit del evento en lugar de un Switch Case.
  • Usa el estado de captura previa y las devoluciones de llamada como indicadores generales de progreso, pero no te bases en niveles de compleción específicos hard-coded ni secuencias de devolución de llamada. El significado del nivel de compleción del estado de captura previa y el comportamiento para errores que se detectan durante la captura previa pueden cambiar.

Nota: Consulta la sección Comportamiento de la cola de búfer a continuación para obtener más información.

Planificación para la compatibilidad con código fuente

Como se mencionó anteriormente, se prevén incompatibilidades con código fuente en la próxima versión de OpenSL ES de Khronos Group. Entre las áreas de cambio probables se incluyen las siguientes:

  • Se prevé que la interfaz de cola de búfer sufra cambios importantes, en especial en las áreas de BufferQueue::Enqueue, la lista de parámetros para slBufferQueueCallback y el nombre del campo SLBufferQueueState.playIndex. Te recomendamos que, en su lugar, el código de tu aplicación use colas de búfer simples de Android. Por este motivo, en el código de ejemplo que se proporciona con el NDK, usamos colas de búfer simples de Android para la reproducción. (También usamos una cola de búfer simple de Android para grabación y decodificación con PCM, pero esto se debe a que OpenSL ES 1.0.1 estándar no admite grabación ni decodificación en un receptor de datos de la cola de búfer).
  • Se agregará const a los parámetros de entrada que pase la referencia y a los campos de estructura SLchar * usados como valores de entrada. Esto no debe requerir cambios en tu código.
  • Se reemplazarán los tipos sin firmar para algunos parámetros que actualmente estén firmados. Es probable que debas cambiar un tipo de parámetro de SLint32 a SLuint32 o uno similar, o bien agregar una conversión de tipos.
  • Equalizer::GetPresetName copia la string a la memoria de la aplicación en lugar de mostrar un puntero a la memoria de la implementación. Este será un cambio importante, por lo cual te recomendamos que evites llamar a este método o aísles su uso.
  • Habrá campos adicionales en los tipos de estructura. Para los parámetros de salida, esos nuevos campos se pueden ignorar, pero para los parámetros de entrada los nuevos cambios deberán inicializarse. Afortunadamente, se prevé que todos estos cambios se produzcan en áreas que Android no admite.
  • Los GUID de la interfaz cambiarán. Haz referencia a las interfaces por su nombre simbólico en lugar de usar el GUID para evitar una dependencia.
  • SLchar cambiará de unsigned char a char. Esto afecta principalmente al localizador de datos del URI y al formato de datos MIME.
  • El nombre de SLDataFormat_MIME.mimeType se cambiará por pMimeType y el de SLDataLocator_URI.URI, por pURI. Te recomendamos que inicialices las estructuras de datos SLDataFormat_MIME y SLDataLocator_URI usando una lista de valores separados por comas y contenidos entre llaves, en lugar de hacerlo por nombre de campo, para aislar tu código de este cambio. Esta técnica se usa en el código de ejemplo.
  • SL_DATAFORMAT_PCM no permite que la aplicación especifique la representación de los datos como un valor entero firmado, un valor entero sin firmar o un punto flotante. La implementación de Android prevé que los datos de 8 bits están representados por un valor entero sin firmar y los de 16 bits por uno firmado. Además, el campo samplesPerSec es inapropiado, ya que las unidades reales son milliHz. Se prevé que estos problemas se aborden en la próxima versión de OpenSL ES, en la que se introducirá un nuevo formato de datos PCM extendido. Este permite que la aplicación especifique explícitamente la representación y corrija el nombre del campo. Debido a que se tratará de un nuevo formato de datos, y el formato de datos PCM actual estará de todos modos disponible (aunque dejará de usarse), no se requerirán cambios inmediatos en tu código.