OpenSL ES pour Android

AVERTISSEMENT: OpenSL ES est obsolète. Les développeurs doivent utiliser la bibliothèque Oboe Open Source, disponible sur GitHub. Oboe est un wrapper C++ qui fournit une API ressemblant fortement à AAudio. Il appelle AAudio s'il est disponible et utilise OpenSL ES dans le cas contraire.

Cette page explique en quoi l'implémentation du NDK OpenSL ES™ diffère de la spécification de référence d'OpenSL ES 1.0.1. Si vous utilisez l'exemple de code fourni par la spécification, vous devrez peut-être le modifier pour qu'il fonctionne sur Android.

Sauf indication contraire, toutes les fonctionnalités sont disponibles à partir d'Android 2.3 (niveau d'API 9) ou version ultérieure. Les fonctionnalités qui ne sont disponibles que pour Android 4.0 (niveau d'API 14) sont indiquées.

Remarque : Le document de définition de compatibilité Android (CDD) indique la configuration matérielle et logicielle requise pour un appareil Android compatible. Pour plus d'informations sur le programme de compatibilité global, consultez Compatibilité Android. Vous pouvez également consulter le CDD lui-même.

OpenSL ES fournit une interface en langage C également accessible en C++. Elle propose des fonctionnalités semblables aux portions audio de ces API Java Android :

Comme pour tous les kits de développement natifs (NDK) Android, l'objectif principal d'OpenSL ES pour Android est de faciliter l'implémentation de bibliothèques partagées appelées à l'aide de l'interface native Java (JNI). Ce NDK n'est pas destiné à l'écriture d'applications en C/C++ pur. Cependant, OpenSL ES est une API complète, et nous pensons qu'elle devrait vous aider à répondre à la plupart de vos besoins audio, sans avoir à faire appel à du code en cours d'exécution dans l'environnement d'exécution Android.

Remarque : Bien que basée sur OpenSL ES, l'API audio native (audio hautes performances) Android n'est pas une implémentation conforme d'un profil OpenSL ES 1.0.1 (jeu, musique, ou téléphone). En effet, Android n'implémente pas toutes les fonctionnalités requises par les profils. Tous les cas connus où Android se comporte différemment de la spécification sont décrits sur la page Extensions Android.

Fonctionnalités héritées de la spécification de référence

L'implémentation du NDK Android OpenSL ES hérite d'une grande partie des caractéristiques liées à la spécification de référence, avec certaines limitations.

Points d'entrée mondiaux

OpenSL ES pour Android est compatible avec tous les points d'entrée mondiaux de la spécification Android. Ces points d'entrée sont les suivants :

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

Objets et interfaces

Le tableau suivant présente les objets et les interfaces compatibles avec l'implémentation du NDK Android OpenSL ES. Si un oui apparaît dans la cellule, la fonctionnalité est disponible dans cette implémentation.

Compatibilité avec Android NDK pour les objets et les interfaces

Fonctionnalité Lecteur audio Enregistreur audio Moteur Mixage de sortie
Amplification des basses Oui Non Non Oui
File d'attente de tampon Oui Non Non Non
Localisateur de données de la file d'attente de tampon Oui : source Non Non Non
Gestion dynamique des interfaces Oui Oui Oui Oui
Envoi d'effet Oui Non Non Non
Moteur Non Non Oui Non
Réverbération environnementale Non Non Non Oui
Égaliseur Oui Non Non Oui
Localisateur de données d'appareil d'E/S Non Oui : source Non Non
Extraction des métadonnées Oui : décodage en PCM Non Non Non
Couper le son Oui Non Non Non
Objet Oui Oui Oui Oui
Localisateur de mixage de sortie Oui : récepteur Non Non Non
Lecture Oui Non Non Non
Vitesse de lecture Oui Non Non Non
État de préchargement Oui Non Non Non
Réverbération prédéfinie Non Non Non Oui
Enregistrement Non Oui Non Non
Recherche Oui Non Non Non
Localisateur de données URI Oui : source Non Non Non
Virtualiseur Oui Non Non Oui
Volume Oui Non Non Non

La section suivante explique les limites de certaines de ces fonctionnalités.

Limites

Certaines limites s'appliquent aux fonctionnalités du tableau 1. Ces limites représentent des différences par rapport à la spécification de référence. Le reste de cette section fournit des informations sur ces différences.

Gestion dynamique des interfaces

OpenSL ES pour Android n'est pas compatible avec RemoveInterface ni ResumeInterface.

Combinaisons d'effets : réverbération environnementale et réverbération prédéfinie

Vous ne pouvez pas utiliser à la fois la réverbération environnementale et la réverbération prédéfinie sur le même mixage de sortie.

La plate-forme peut ignorer les demandes d'effets si elle estime que la charge du processeur serait trop élevée.

Envoi d'effet

SetSendLevel() accepte un seul niveau d'envoi par lecteur audio.

Réverbération environnementale

La réverbération environnementale n'est pas compatible avec les champs reflectionsDelay, reflectionsLevel ou reverbDelay de la structure SLEnvironmentalReverbSettings.

Format de données MIME

Vous ne pouvez utiliser le format de données MIME qu'avec le localisateur de données URI et uniquement pour un lecteur audio. Vous ne pouvez pas utiliser ce format pour un enregistreur audio.

L'implémentation Android d'OpenSL ES nécessite l'initialisation de mimeType sur NULL ou une chaîne UTF-8 valide. Vous devez également initialiser containerType sur une valeur valide. En l'absence d'autres considérations, telles que la portabilité vers d'autres implémentations ou formats de contenu qu'une application ne peut pas identifier par son en-tête, nous vous recommandons de définir mimeType sur NULL et containerType sur SL_CONTAINERTYPE_UNSPECIFIED.

OpenSL ES pour Android accepte les formats audio suivants, à condition que la plate-forme Android les accepte également :

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

Remarque : Pour obtenir la liste des formats audio compatibles avec Android, consultez la section Formats multimédias acceptés.

Les limites suivantes s'appliquent à la gestion de ces formats et d'autres dans cette implémentation d'OpenSL ES :

  • Les formats AAC doivent se trouver dans un conteneur MP4 ou ADTS.
  • OpenSL ES pour Android n'est pas compatible avec MIDI.
  • WMA ne fait pas partie d'AOSP, et nous n'avons pas vérifié sa compatibilité avec OpenSL ES pour Android.
  • L'implémentation du NDK Android OpenSL ES n'est pas compatible avec la lecture directe de DRM ou de contenu chiffré. Pour lire du contenu audio protégé, vous devez d'abord le déchiffrer dans votre application, laquelle doit appliquer les éventuelles restrictions DRM.

OpenSL ES pour Android n'accepte pas les méthodes suivantes pour manipuler les objets :

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

Format de données PCM

PCM est le seul format de données que vous pouvez utiliser avec les files d'attente de tampon. Les configurations de lecture PCM compatibles présentent les caractéristiques suivantes :

  • 8 bits non signé ou 16 bits signé
  • Mono ou stéréo
  • Ordre des octets Little Endian
  • Taux d'échantillonnage 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

Les configurations compatibles avec OpenSL ES pour Android dépendent de l'appareil. Généralement, les options 16 000 Hz mono/16 bits signé sont disponibles quel que soit l'appareil.

La valeur du champ samplesPerSec est exprimée en milliHz, malgré le nom qui prête à condition. Pour éviter d'utiliser accidentellement des valeurs incorrectes, nous vous recommandons d'initialiser ce champ à l'aide de l'une des constantes symboliques définies à cette fin, telles que SL_SAMPLINGRATE_44_1.

Android 5.0 (niveau d'API 21) ou version ultérieure est compatible avec les données à virgule flottante.

Vitesse de lecture

La vitesse de lecture OpenSL ES indique la vitesse à laquelle un objet présente des données, exprimée en millièmes de la vitesse normale, ou pour mille. Par exemple, une vitesse de lecture de 1 000 pour mille correspond à 1 000/1 000 ou à la vitesse normale. Une plage de fréquences est un intervalle fermé qui exprime une plage de vitesses de lecture possibles.

La compatibilité des plages de vitesses de lecture et d'autres capacités peut varier selon la version de la plate-forme et son implémentation. Votre application peut déterminer ces capacités au moment de l'exécution en utilisant PlaybackRate::GetRateRange() ou PlaybackRate::GetCapabilitiesOfRate() pour interroger l'appareil.

Un appareil accepte généralement la même plage de vitesses pour une source de données au format PCM, et une plage de vitesses unitaires de 1 000 pour mille à 1 000 pour mille pour les autres formats. Autrement dit, la plage de vitesses unitaires est en réalité une valeur unique.

Enregistrement

OpenSL ES pour Android n'est pas compatible avec les événements SL_RECORDEVENT_HEADATLIMIT ou SL_RECORDEVENT_HEADMOVING.

Recherche

La méthode SetLoop() active la lecture en boucle de tout le fichier. Pour activer la lecture en boucle, définissez le paramètre startPos sur 0 et le paramètre endPos sur SL_TIME_UNKNOWN.

Localisateur de données de la file d'attente de tampon

Un lecteur ou enregistreur audio avec un localisateur de données pour une file d'attente de tampon n'est compatible qu'avec le format de données PCM.

Localisateur de données d'appareil d'E/S

OpenSL ES pour Android ne permet d'utiliser un localisateur de données d'appareil d'E/S que si vous avez spécifié le localisateur comme source de données pour Engine::CreateAudioRecorder(). Initialisez le localisateur de données d'appareil à l'aide des valeurs indiquées dans l'extrait de code suivant :

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

Localisateur de données URI

OpenSL ES pour Android ne peut utiliser le localisateur de données URI qu'avec le format de données MIME, et uniquement pour un lecteur audio. Vous ne pouvez pas utiliser un localisateur de données URI pour un enregistreur audio. L'URI ne peut utiliser que les schémas http: et file:. Les autres schémas, tels que https:, ftp: ou content:, ne sont pas autorisés.

Nous n'avons pas validé la compatibilité de rtsp: avec le son sur la plate-forme Android.

Structures de données

Android est compatible avec les structures de données OpenSL ES 1.0.1 suivantes :

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

Configuration de la plate-forme

L'API OpenSL ES pour Android est conçue pour les applications multithread et est thread-safe. Elle accepte un seul moteur par application et jusqu'à 32 objets par moteur. La mémoire d'appareil et le processeur disponibles peuvent restreindre davantage le nombre d'objets utilisables.

Ces options de moteur sont reconnues, mais sont ignorées par slCreateEngine :

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

OpenMAX AL et OpenSL ES peuvent être utilisés ensemble dans la même application. Dans ce cas, il n'existe qu'un seul objet de moteur partagé en interne, et la limite de 32 objets est partagée entre OpenMAX AL et OpenSL ES. L'application doit créer les deux moteurs, les utiliser tous les deux, puis les détruire. L'implémentation conserve un nombre de référence sur le moteur partagé afin que la destruction ait lieu de manière appropriée lors de la deuxième opération de destruction.

Notes de programmation

Les notes de programmation OpenSL ES fournissent des informations supplémentaires pour garantir une implémentation correcte d'OpenSL ES.

Remarque : Pour vous aider, nous avons inclus une copie de la spécification OpenSL ES 1.0.1 avec le NDK dans docs/opensles/OpenSL_ES_Specification_1.0.1.pdf.

Problèmes de plate-forme

Cette section décrit les problèmes connus dans la version initiale de la plate-forme qui accepte ces API.

Gestion dynamique des interfaces

DynamicInterfaceManagement::AddInterface ne fonctionne pas. Spécifiez plutôt l'interface dans le tableau transmis à Create(), comme indiqué dans l'exemple de code pour la réverbération environnementale.

Planifier les futures versions d'OpenSL ES

Les API audio hautes performances d'Android sont basées sur la version OpenSL ES 1.0.1 de Khronos Group. Khronos a publié une version révisée 1.1 du standard. La version révisée comprend de nouvelles fonctionnalités, des précisions, des corrections d'erreurs typographiques et quelques incompatibilités. La plupart des incompatibilités attendues sont relativement mineures ou concernent des aspects d'OpenSL ES qui ne sont pas compatibles avec Android.

Une application développée avec cette version devrait fonctionner sur les futures versions de la plate-forme Android, à condition de suivre les consignes décrites dans la section Planifier la compatibilité binaire ci-dessous.

Remarque : La compatibilité des sources futures n'est pas un objectif en soi. Autrement dit, si vous passez à une version plus récente du NDK, vous devrez peut-être modifier le code source de votre application pour qu'il soit conforme à la nouvelle API. Nous pensons que la plupart de ces modifications seront mineures. Consultez les détails ci-dessous.

Planifier la compatibilité binaire

Nous recommandons à votre application de suivre ces consignes pour améliorer la compatibilité binaire future :

  • Utilisez uniquement le sous-ensemble de fonctionnalités Android compatibles d'OpenSL ES 1.0.1.
  • Ne vous basez pas sur un code de résultat particulier pour une opération ayant échoué. Soyez prêt à gérer un autre code de résultat.
  • Les gestionnaires de rappel d'applications s'exécutent généralement dans un contexte limité. Ils doivent être écrits pour effectuer leurs tâches rapidement, puis revenir le plus rapidement possible. N'exécutez pas d'opérations complexes dans un gestionnaire de rappel. Par exemple, dans un rappel de fin de file d'attente de tampon, vous pouvez mettre en file d'attente un autre tampon, mais ne créez pas de lecteur audio.
  • Les gestionnaires de rappel doivent être préparés pour être appelés plus ou moins fréquemment, pour recevoir des types d'événements supplémentaires, et ils doivent ignorer les types d'événements qu'ils ne reconnaissent pas. Les rappels configurés avec un masque d'événement composé de types d'événements activés doivent être préparés pour être appelés avec plusieurs bits de type d'événement définis simultanément. Utilisez "&" pour tester chaque bit d'événement plutôt qu'un cas de contacteur.
  • Utilisez l'état de préchargement et les rappels comme indications générales de la progression, mais ne dépendez pas des niveaux de remplissage ni des séquences de rappel codés en dur. La signification du niveau de remplissage de l'état de préchargement et le comportement en cas d'erreurs détectées lors du préchargement peuvent changer.

Remarque : Consultez la section Comportement de la file d'attente de tampon ci-dessous pour en savoir plus.

Planifier la compatibilité des sources

Comme indiqué précédemment, des incompatibilités de code source sont attendues dans la prochaine version d'OpenSL ES de Khronos Group. Voici les éléments susceptibles d'être concernés par des modifications :

  • L'interface de la file d'attente de tampon devrait avoir des modifications importantes, notamment dans les zones de BufferQueue::Enqueue, de la liste des paramètres de slBufferQueueCallback et du nom du champ SLBufferQueueState.playIndex. Nous vous recommandons d'utiliser plutôt les files d'attente de tampon simples Android. Dans l'exemple de code fourni avec le NDK, nous avons utilisé des files d'attente de tampon simples Android pour la lecture pour cette raison. Notez que nous utilisons également une file d'attente de tampon simple Android pour l'enregistrement et le décodage vers PCM, mais cela est dû au fait que la version OpenSL ES 1.0.1 standard n'est pas compatible avec l'enregistrement ni le décodage dans le récepteur de données d'une file d'attente de tampon.
  • const sera ajouté aux paramètres d'entrée transmis par référence et aux champs de structure SLchar * utilisés comme valeurs d'entrée. Cela ne devrait pas nécessiter de modification du code.
  • Certains types de paramètres actuellement non signés seront remplacés par des types signés. Vous devrez peut-être remplacer le type de paramètre SLint32 par SLuint32 ou par un type de paramètre similaire, ou ajouter une conversion.
  • Equalizer::GetPresetName copie la chaîne dans la mémoire de l'application au lieu de renvoyer un pointeur vers la mémoire d'implémentation. Il s'agit d'un changement significatif. Nous vous recommandons donc d'éviter d'appeler cette méthode. Dans le cas contraire, isolez votre utilisation.
  • Il existe des champs supplémentaires dans les types de structure. Pour les paramètres de sortie, ces nouveaux champs peuvent être ignorés, mais pour les paramètres d'entrée, les nouveaux champs devront être initialisés. Heureusement, tous ces champs devraient se trouver dans des zones non compatibles avec Android.
  • Les GUID de l'interface seront modifiés. Pour éviter toute dépendance, reportez-vous aux interfaces en fonction de leur nom symbolique plutôt que qu'en fonction du GUID.
  • SLchar passera de unsigned char à char. Cela concerne principalement le localisateur de données d'URI et le format de données MIME.
  • SLDataFormat_MIME.mimeType sera renommé pMimeType, et SLDataLocator_URI.URI sera renommé pURI. Nous vous recommandons d'initialiser les structures de données SLDataFormat_MIME et SLDataLocator_URI à l'aide d'une liste de valeurs séparées par des virgules et des accolades, plutôt que par le nom de champ, afin d'isoler votre code de cette modification. Cette technique est utilisée dans l'exemple de code.
  • SL_DATAFORMAT_PCM n'autorise pas l'application à spécifier la représentation des données sous forme d'entier signé, d'entier non signé ou de virgule flottante. L'implémentation Android suppose que les données 8 bits sont des entiers non signés et que les données 16 bits sont des entiers signés. De plus, le champ samplesPerSec prête à confusion, car les unités réelles sont en milliHz. Ces problèmes devraient être résolus dans la prochaine version d'OpenSL ES, qui inclura un nouveau format de données PCM étendues permettant à l'application de spécifier explicitement la représentation et de corriger le nom du champ. Comme il s'agit d'un nouveau format de données et que le format de données PCM actuel sera toujours disponible (bien qu'obsolète), aucune modification immédiate de votre code ne devrait être nécessaire.