OpenSL ES para Android

ADVERTENCIA: OpenSL ES está obsoleto. Los desarrolladores deben usar la biblioteca Oboe de código abierto que se encuentra disponible en GitHub. Oboe es un wrapper de C++ que proporciona una API muy similar a AAudio. Oboe llama a AAudio cuando está disponible, y recurre a OpenSL ES si no lo está.

En esta página, se proporciona información detallada sobre cómo difiere la especificación de referencia para OpenSL ES 1.0.1 de la implementación de OpenSL ES del NDK. Al usar un código de muestra 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 (API nivel 9) y versiones posteriores. Algunas funciones, que verás indicadas, solo están disponibles para Android 4.0 (nivel de API 14).

Nota: El Documento de definición de compatibilidad de Android (CDD) indica los requisitos de hardware y software de un dispositivo Android compatible. Consulta Compatibilidad con Android para obtener más información sobre el programa de compatibilidad general, y CDD para ver el documento de CDD específico.

OpenSL ES proporciona una interfaz de lenguaje C a la que también se puede acceder con C++. Expone funciones similares a las partes de audio de estas API de Java para Android:

Al igual que con todo el Kit de desarrollo nativo de Android (NDK), la finalidad principal de OpenSL ES para Android es facilitar la implementación de bibliotecas compartidas que se llamarán mediante la Interfaz nativa de Java (JNI). El NDK no debe usarse para escribir aplicaciones C/C++ puras. No obstante, OpenSL ES es una API repleta de funciones, por lo que suponemos que debería satisfacer la mayoría de tus necesidades de audio por sí sola, sin llamadas a código que se ejecute en el tiempo de ejecución de Android.

Nota: Si bien está basada en OpenSL ES, la API de audio nativo de Android (audio de alto rendimiento) no es una implementación conforme de un perfil 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 cualquiera de 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 del NDK de Android hereda gran parte del conjunto de atributos de la especificación de referencia, con ciertas limitaciones.

Puntos de entrada globales

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 siguiente tabla, se muestran los objetos y las interfaces que admite la implementación de OpenSL ES del NDK de Android. Si aparece en la celda, la función está disponible en esta implementación.

Compatibilidad del 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 dinámica de la interfaz
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í: Decodificar en PCM No No No
Silenciamiento No No No
Objeto
Localizador de combinación de salida Sí: Receptor No No No
Reproducción No No No
Velocidad de reproducción No No No
Estado de carga previa No No No
Ajuste predeterminado de reverberación No No No
Grabación No No No
Búsqueda No No No
Localizador de datos del 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. Estas limitaciones representan diferencias con respecto a la especificación de referencia. En el resto de esta sección, se proporciona información sobre esas diferencias.

Administración dinámica de la interfaz

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 estima que la carga de la CPU puede ser demasiado 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 SLEnvironmentalReverbSettings.

Formato de datos MIME

Puedes usar el formato de datos MIME solo con el localizador de datos del 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 y otros formatos de contenido que una app no pueda identificar mediante el 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: Puedes ver la lista de los formatos de audio compatibles con Android en Formatos de medios compatibles.

El manejo de estos y otros formatos para la implementación de OpenSL ES tienen las siguientes limitaciones:

  • 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 verificamos su compatibilidad con OpenSL ES para Android.
  • La implementación de OpenSL ES del NDK de Android no admite la reproducción directa de DRM ni contenido encriptado. Para reproducir contenido de audio protegido, primero debes desencriptarlo en tu aplicación, y esta debe aplicar las restricciones de DRM.

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

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

Formato de datos PCM

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

  • 8 bits sin firma o 16 bits con firma.
  • Mono o estéreo.
  • Ordenamiento de bytes little endian.
  • Tasas de muestreo de:
    • 8,000 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 admite para grabación en Android dependen del dispositivo; generalmente, se encuentra disponible la configuración de 16,000 Hz mono/16 bits firmada, independientemente del dispositivo.

El valor del campo samplesPerSec se indica en unidades de milihercios, a pesar de que su nombre no lo indique. Para evitar usar accidentalmente el valor incorrecto, te recomendamos inicializar el 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.

Velocidad de reproducción

La velocidad 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 velocidad de reproducción de 1,000 por mil es 1,000/1,000, o velocidad normal. Un rango de velocidades es un intervalo cerrado que expresa un rango de velocidades de reproducción posibles.

La compatibilidad con rangos de velocidades 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 el dispositivo.

Un dispositivo generalmente admite el mismo rango de velocidades para una fuente de datos en formato PCM y un rango de velocidad de unidades de 1,000 por mil a 1,000 por mil para otros formatos; es decir, el rango de velocidad 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 repetición indefinida de archivos completos. Para habilitar la repetición indefinida, establece 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 defines el localizador como la fuente de datos para Engine::CreateAudioRecorder(). Inicializa el localizador de datos del dispositivo usando los valores incluidos en el siguiente fragmento de código:

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

Localizador de datos del URI

OpenSL ES para Android solo puede usar el localizador de datos del URI con el formato de datos MIME, y solo para un reproductor de audio. No puedes usar el localizador de datos del 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 verificamos 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 usar con subprocesos. Admite un solo motor por aplicación y hasta 32 objetos por motor. La memoria y la CPU disponibles del dispositivo pueden limitar aún más la cantidad de objetos que pueden usarse.

Aunque se reconocen las siguientes opciones de motor, 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, incluimos 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.

Errores de la plataforma

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

Administración dinámica de la interfaz

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

Cómo planificar para versiones futuras de OpenSL ES

Las API de audio de alto rendimiento de Android están basadas en OpenSL ES 1.0.1 de Khronos Group. Khronos lanzó una versión 1.1 revisada del estándar. En la versión revisada, se incluyen nuevas funciones, aclaraciones, 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 no compatibles con Android.

Una aplicación desarrollada con esta versión debe funcionar en versiones futuras de la plataforma de Android, siempre que sigas los lineamientos de la sección Cómo planificar para la compatibilidad con objetos binarios a continuación.

Nota: Ofrecer compatibilidad con código fuente a futuro no es un objetivo que se haya planteado. Es decir que, 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 prevé que la mayoría de dichos cambios sean menores; consulta los detalles a continuación.

Cómo planificar para la compatibilidad con objetos binarios

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

  • Usa solo el subconjunto documentado de características de OpenSL ES 1.0.1 compatibles con Android.
  • No dependas de 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 escribirse de modo que puedan realizar el 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 poner otro búfer en cola, pero no crees un reproductor de audio.
  • Los controladores de devolución de llamada deben estar preparados para que se los llame con mayor o menor frecuencia, a fin de recibir tipos de eventos adicionales, y deben ignorar los tipos de eventos que no reconozcan. 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 argumento switch-case.
  • Usa el estado de carga previa y las devoluciones de llamada como indicadores generales de progreso, pero no dependas de niveles de relleno hard-coded ni secuencias de devolución de llamada específicos. El significado del nivel de relleno del estado de carga previa y el comportamiento para errores que se detectan durante la carga previa pueden cambiar.

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

Cómo planificar 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. Recomendamos que 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 grabar y decodificar en 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 la estructura SLchar * que se usen como valores de entrada. Esto no debería implicar cambios en tu código.
  • Se reemplazarán los tipos sin firma por algunos parámetros actualmente con firma. 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 aislar su uso.
  • Habrá campos adicionales en los tipos de estructura. Para los parámetros de salida, los nuevos campos se pueden ignorar, pero estos se deben inicializar para los parámetros de entrada. Afortunadamente, se prevé que todos estos campos estarán 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 afectará principalmente al localizador de datos del URI y al formato de datos MIME.
  • El nombre de SLDataFormat_MIME.mimeType cambiará a pMimeType, y el nombre de SLDataLocator_URI.URI cambiará a pURI. Te recomendamos que inicialices las estructuras de datos SLDataFormat_MIME y SLDataLocator_URI usando una lista de valores separados por comas y encerrados 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 con firma, un valor entero sin firma o un punto flotante. La implementación de Android supone que los datos de 8 bits están representados por un valor entero sin firma y los de 16 bits por uno con firma. Además, el campo samplesPerSec es un término incorrecto, ya que las unidades reales son milihercios. Esperamos que estos errores se resuelvan en la próxima versión de OpenSL ES, la cual incluirá 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á disponible de todos modos (aunque será obsoleto), no se requerirán cambios inmediatos en el código.