OpenSL ES para Android

En esta página, se proporciona información detallada sobre 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 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 (API nivel 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), el principal objetivo 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 esperamos que pueda satisfacer la mayoría de tus necesidades 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 cualquier 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 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

Interfaces y objetos

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 de NDK de Android con interfaces y objetos

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í: 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
Frecuencia 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 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. 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 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 ni 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 del struct 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 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: Para obtener una lista de los formatos de audio compatibles con Android, consulta Formatos de medios compatibles.

Se aplican las siguientes limitaciones 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 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 implementar 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 firmar o 16 bits con firma.
  • Mono o estéreo.
  • Ordenamiento de bytes little endian.
  • Tasas de muestreo:
    • 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 de 16,000 Hz mono/16 bits firmada, independientemente del dispositivo.

El valor del campo samplesPerSec se indica en unidades de milliHz, a pesar de que su nombre no lo indique. 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 (API nivel 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 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 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 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 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 subprocesos. Admite un solo motor por aplicación y hasta 32 objetos por motor. La memoria y la CPU del dispositivo disponibles pueden limitar incluso 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.

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 el arreglo 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 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 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 los lineamientos de la sección Cómo planificar para la compatibilidad con objetos binarios a continuación.

Note: Future source compatibility is not a goal. 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 prevé que la mayoría de los cambios como estos sea menor, 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 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 estar escritos 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 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 dependas de 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 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.

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 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 del struct SLchar * que se usen como valores de entrada. 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, lo que 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 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. Esperamos que se aborden estos problemas 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á disponible de todos modos (aunque será obsoleto), no se requerirán cambios inmediatos en el código.