OpenSL ES para Android

Esta página fornece detalhes sobre como a implementação do OpenSL ES™ do NDK difere da especificação de referência do OpenSL ES 1.0.1. Se você usar exemplos de código da especificação, pode ser necessário modificá-los para que funcionem no Android.

A menos que observado de forma diferente, todos os recursos estão disponíveis no Android 2.3 (API de nível 9) e em posteriores. Alguns recursos só estão disponíveis para Android 4.0 (API de nível 14) — esses estão especificados.

Observação: O Documento de definição de compatibilidade do Android (CDD) enumera os requisitos de hardware e software de um dispositivo Android compatível. Consulte Compatibilidade do Android para saber mais sobre o programa de compatibilidade geral e o CDD para ver o CDD atual.

O OpenSL ES fornece uma interface em C que também é acessível usando C++. Ele expõe recursos similares às partes de áudio das seguintes Android Java APIs:

Assim como com o Android Native Development Kit (NDK), o objetivo principal do OpenSL ES para Android é facilitar a implementação de bibliotecas compartilhadas chamadas usando a Interface Java nativa (JNI ). O NDK não visa a programação de aplicativos puramente em C/C++. No entanto, o OpenSL ES é uma API repleta de recursos, e esperamos que você possa atender à maioria das suas necessidades de áudio usando apenas essa API, sem precisar chamar código em execução no tempo de execução do Android.

Observação: Embora baseada no OpenSL ES, a API de áudio nativo do Android (áudio de alto desempenho) não é uma implementação que visa a conformidade com nenhum perfil do OpenSL ES 1.0.1 (jogo, música ou celular). Isso porque o Android não implementa todos os recursos exigidos por nenhum dos perfis. Todos os casos conhecidos em que o Android se comporta de maneira diferente da especificação são descritos na página Extensões do Android.

Recursos herdados da especificação de referência

A implementação do OpenSL ES do Android NDK herda grande parte do conjunto de recursos da especificação de referência, com algumas limitações.

Pontos de entrada globais

O OpenSL ES para Android é compatível com todos os pontos de entrada globais na especificação do Android. Esses pontos de entrada incluem:

  • slCreateEngine
  • slQueryNumSupportedEngineInterfaces
  • slQuerySupportedEngineInterfaces

Objetos e interfaces

A tabela a seguir exibe os objetos e interfaces compatíveis com a implementação do OpenSL ES do Android NDK. Se houver um Sim na célula, o recurso está disponível nessa implementação.

Compatibilidade do Android NDK com objetos e interfaces.

Recurso Reprodutor de áudio Gravador de áudio Mecanismo Combinação de saída
Intensificação de grave Sim Não Não Sim
Fila de buffer Sim Não Não Não
Localizador de dados de fila de buffer Sim: Fonte Não Não Não
Gerenciamento dinâmico da interface Sim Sim Sim Sim
Entrada de efeitos Sim Não Não Não
Mecanismo Não Não Sim Não
Reverberação do ambiente Não Não Não Sim
Equalizador Sim Não Não Sim
Localizador de dados do dispositivo de E/S Não Sim: Fonte Não Não
Extração de metadados Sim: Decodificar para PCM Não Não Não
Desativar som/isolar Sim Não Não Não
Objeto Sim Sim Sim Sim
Localizador de combinação de saída Sim: Coletor Não Não Não
Reprodução Sim Não Não Não
Taxa de reprodução Sim Não Não Não
Status de pré-busca Sim Não Não Não
Reverberação predefinida Não Não Não Sim
Gravação Não Sim Não Não
Busca Sim Não Não Não
Localizador de dados de URI Sim: Fonte Não Não Não
Virtualizador Sim Não Não Sim
Volume Sim Não Não Não

A seção a seguir explica as limitações de alguns desses recursos.

Limitações

Determinadas limitações se aplicam aos recursos da Tabela 1. Essas limitações representam diferenças em relação à especificação de referência. O resto desta seção trata dessas diferenças.

Gerenciamento dinâmico da interface

O OpenSL ES para Android não é compatível com RemoveInterface nem ResumeInterface.

Combinações de efeito: reveberação do ambiente e reverberação predefinida

Não é possível ter ambos os tipos de reveberação na mesma combinação de saída.

A plataforma pode ignorar solicitações de efeito se entender que a carga sobre o CPU seria muito alta.

Entrada de efeitos

SetSendLevel() suporta um único nível de entrada por reprodutor de áudio.

Reverberação do ambiente

A reverberação do ambiente não aceita os campos reflectionsDelay, reflectionsLevel e reverbDelay da estrutura SLEnvironmentalReverbSettings.

Formato MIME de dados

Só é possível usar o formato MIME com o localizador de dados de URI e somente para um reprodutor de áudio. Não é possível usar esse formato de dado para um gravador de áudio.

A implementação do OpenSL ES do Android exige inicializar o mimeType como NULL ou uma string UTF-8 válida. Você ainda deve inicializar containerType como um valor válido. Na ausência de outras considerações, como a portabilidade para outras implementações ou formatos de conteúdo que o aplicativo não identifica por cabeçalho, recomendamos definir mimeType como NULL e containerType como SL_CONTAINERTYPE_UNSPECIFIED.

O OpenSL ES para Android aceita os seguintes formatos de áudio, desde que a plataforma Android os aceite também:

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

Observação: Para ver uma lista dos formatos de áudio que o Android aceita, consulte Formatos de mídia suportados.

As limitações a seguir se aplicam ao tratamento desses e outros formatos nessa implementação do OpenSL ES:

  • Os formatos AAC devem ficar dentro de um contêiner ADTS ou MP4.
  • O OpenSL ES para Android não aceita MIDI.
  • WMA não faz parte de AOSP e não verificamos sua compatibilidade com o OpenSL ES para Android.
  • A implementação do OpenSL ES do Android NDK não aceita reprodução direta de DRM nem conteúdo criptografado. Para reproduzir conteúdo de áudio protegido, deve-se decodificá-lo no aplicativo antes da reprodução, com o aplicativo impondo todas as restrições de DRM.

Métodos relacionados a objeto

O OpenSL ES para Android não aceita os seguintes métodos para manipular objetos:

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

Formato PCM de dados

PCM é o único formato de dados que pode ser usado com filas de buffer. As configurações de reprodução de PCM compatíveis têm as seguintes características:

  • 8 bits não assinada ou 16 bits assinada.
  • Mono ou estéreo.
  • Ordenação de byte little-endian.
  • Taxas de amostragem 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

As configurações que o OpenSL ES para Android aceita para gravação dependem do dispositivo. Normalmente, a configuração 16.000 Hz mono/16 bits assinada está disponível para todos os dispositivos.

O valor do campo samplesPerSec está em unidades de mHz, apesar do nome. Para evitar aplicação acidental do valor errado, recomendamos inicializar esse campo usando uma das constantes simbólicas definidas com esse objetivo, como SL_SAMPLINGRATE_44_1.

O Android 5.0 (API de nível 21) e posteriores aceitam dados de ponto flutuante.

Taxa de reprodução

A taxa de reprodução do OpenSL ES indica a velocidade a que um objeto apresenta dados, expressa em milésimos da velocidade normal — ou por mil. Por exemplo, uma taxa de reprodução de 1.000 por mil é 1,000/1,000, ou velocidade normal. Um intervalo de taxa é um intervalo fechado que expressa a variação das possíveis taxas de reprodução.

O suporte com intervalos de taxa de reprodução e outros recursos pode variar dependendo da versão da plataforma e da implementação. Seu aplicativo pode determinar esses recursos em tempo de execução usando PlaybackRate::GetRateRange() ou PlaybackRate::GetCapabilitiesOfRate() para consultar o dispositivo.

Normalmente, um dispositivo aceita o mesmo intervalo de taxa para uma fonte de dados no formato PCM, e um intervalo de taxa unitário de 1.000 por mil a 1.000 por mil para outros formatos, ou seja, o intervalo de taxa unitário é, efetivamente, um único valor.

Gravação

O OpenSL ES para Android não aceita os eventos SL_RECORDEVENT_HEADATLIMIT e SL_RECORDEVENT_HEADMOVING.

Busca

O método SetLoop() permite gerar loop em todo o arquivo. Para permitir o loop, defina o parâmetro startPos como 0 e o parâmetro endPos como SL_TIME_UNKNOWN.

Localizador de dados de fila de buffer

Reprodutores ou gravadores de áudio com um localizador de dados para uma fila de buffer só aceitam o formato PCM.

Localizador de dados do dispositivo de E/S

O OpenSL ES para Android só aceita o uso de um localizador de dados do dispositivo de E/S quando se especifica o localizador como a fonte de dados para Engine::CreateAudioRecorder(). Inicialize o localizador de dados do dispositivo usando os valores contidos no seguinte fragmento de código:

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

Localizador de dados de URI

O OpenSL ES para Android só pode usar o localizador de dados de URI com o formato MIME e somente para um reprodutor de áudio. Não é possível usar um localizador de dados de URI para um gravador de áudio. O URI só pode usar os esquemas http: e file:. Outros esquemas, como https:, ftp: ou content:, não são permitidos.

Não verificamos a compatibilidade com rtsp: com áudio na plataforma Android.

Estruturas de dados

O Android aceita as seguintes estruturas de dados do OpenSL ES 1.0.1:

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

Configuração da plataforma

O OpenSL ES para Android foi desenvolvido para aplicativos multiencadeamento e é seguro para os encadeamentos. Ele oferece compatibilidade com um único mecanismo por aplicativo e até 32 objetos por mecanismo. A memória e o CPU disponíveis do dispositivo podem restringir mais o número de objetos usável.

Essas opções de mecanismo são reconhecidas, mas ignoradas pelos seguintes objetos de slCreateEngine:

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

O OpenMAX AL e o OpenSL ES podem ser usados juntos no mesmo aplicativo. Nesse caso, há um único objeto de mecanismo compartilhado internamente, e o limite de 32 objetos é compartilhado entre o OpenMAX AL e o OpenSL ES. O aplicativo deve criar, usar e, por fim, destruir ambos os mecanismos. A implementação mantém uma contagem de referência no mecanismo compartilhado para que ele seja corretamente destruído durante a segunda operação de destruição.

Notas de programação

As notas de programação do OpenSL ES fornecem informações complementares para garantir a implementação adequada do OpenSL ES.

Observação: Para a sua conveniência, incluímos uma cópia da especificação do OpenSL ES 1.0.1 no NDK em docs/opensles/OpenSL_ES_Specification_1.0.1.pdf.

Problemas da plataforma

Esta seção descreve problemas conhecidos na versão inicial da plataforma que aceita estas APIs.

Gerenciamento dinâmico da interface

DynamicInterfaceManagement::AddInterface não funciona. Em vez disso, especifique a interface na matriz que é passada a Create(), conforme exibido no exemplo de código de reverberação do ambiente.

Planejamento para versões futuras do OpenSL ES

As APIs de áudio de alto desempenho do Android se baseiam no OpenSL ES 1.0.1 do Khronos Group. A Khronos lançou a versão 1.1, uma revisão da versão padrão. A versão revisada inclui novos recursos, esclarecimentos, correções de erros tipográficos e algumas incompatibilidades. A maioria das compatibilidades esperadas são relativamente pequenas ou estão em áreas do OpenSL ES que não são compatíveis com o Android.

Um aplicativo desenvolvido com essa versão deve funcionar em versões futuras da plataforma Android, desde que as orientações definidas na seção Planejamento para compatibilidade binária abaixo sejam seguidas.

Observação: A compatibilidade com fonte futura não é um objetivo. Ou seja, se você atualizar para uma nova versão do NDK, pode precisar modificar o código-fonte do aplicativo para se adequar à nova API. Esperamos que a maior parte dessas mudanças sejam pequenas. Veja os detalhes abaixo.

Planejamento para compatibilidade binária

Recomendamos que o seu aplicativo siga estas orientações para melhorar a compatibilidade binária futura:

  • Usar somente o subconjunto documentado de recursos do OpenSL ES 1.0.1 aceitos pelo Android.
  • Não dependa de um determinado código resultante para uma operação sem sucesso. Prepare-se para lidar com um código resultante diferente.
  • Os manipuladores de retorno de chamada do aplicativo geralmente são executados em um contexto restrito. Eles devem ser escritos para realizar sua função rapidamente e retornar assim que possível. Não execute operações complexas dentro de um manipulador de retornos de chamada. Por exemplo: em um retorno de chamada de conclusão de fila de buffer, é possível enfileirar outro buffer, mas não crie um reprodutor de áudio.
  • Os manipuladores de retorno de chamada devem estar preparados para serem chamados com mais ou menos frequência, para receber tipos de evento adicionais e devem ignorar tipos de evento que não reconhecem. Os retornos de chamada configurados com uma máscara de eventos feita de tipos de evento ativos devem estar preparados para serem chamados com diversos conjuntos de bits de tipo de evento simultaneamente. Use “&” para testar cada bit de evento em vez de um caso de opção.
  • Use o status de pré-busca e retornos de chamada como indicadores gerais de progresso, mas não dependa de níveis de preenchimento nem sequências de retornos de chamada específicos embutidos no código. O significado do nível de preenchimento do status de pré-busca e o comportamento em relação a erros detectados durante a pré-busca podem mudar.

Observação: Consulte a seção Comportamento da fila de buffer abaixo para saber mais.

Planejamento para compatibilidade de fonte

Como mencionado, esperam-se incompatibilidades do código-fonte na próxima versão do OpenSL ES do Khronos Group. As prováveis áreas de mudança incluem:

  • Espera-se que a interface de fila de buffer tenha mudanças importantes, especialmente nas áreas de BufferQueue::Enqueue, na lista de parâmetros de slBufferQueueCallback e no nome do campo SLBufferQueueState.playIndex. Recomendamos que o código do seu aplicativo use filas de buffer simples do Android. Por esse motivo, no exemplo de código fornecido com o NDK, usamos as filas de buffer simples do Android para reprodução (também usamos fila de buffer simples do Android para gravar e decodificar para PCM, mas isso porque o OpenSL ES 1.0.1 padrão não aceita gravação nem decodificação para um coletor de dados de fila de buffer).
  • Haverá uma adição de const aos parâmetros de entrada passados por referência e aos campos de estrutura SLchar * usados como valores de entrada, mas ela não deve exigir nenhuma mudança no código.
  • Haverá uma substituição de tipos não assinados para alguns parâmetros que atualmente estão assinados. Você pode precisar alterar o tipo de um parâmetro de SLint32 para SLuint32 ou parecido, ou adicionar uma transmissão.
  • Equalizer::GetPresetName copia a string para a memória do aplicativo em vez de retornar um ponteiro para a memória de implementação. Essa será uma mudança significativa, por isso recomendamos evitar chamar esse método ou isolar o uso dele.
  • Haverá campos adicionais nos tipos de estrutura. Quanto aos parâmetros de saída, esses novos campos podem ser ignorados, mas para parâmetros de entrada, os novos campos terão que ser inicializados. Felizmente, esperamos que todos esses campos estejam em áreas não aceitas pelo Android.
  • Os GUIDs mudarão. Consulte as interfaces por nome simbólico em vez de por GUID para evitar uma dependência.
  • SLchar mudará de unsigned char para char. Isso afetará principalmente o localizador de dados de URI e o formato MIME de dados.
  • SLDataFormat_MIME.mimeType será renomeado para pMimeType e SLDataLocator_URI.URI para pURI. Recomendamos inicializar as estruturas de dados SLDataFormat_MIME e SLDataLocator_URI usando uma lista de valores separados por vírgula e fechados por chave, em vez de por nome de campo, para isolar seu código dessa mudança. Essa técnica é usada no exemplo de código.
  • SL_DATAFORMAT_PCM não permite que o aplicativo especifique a representação dos dados como número inteiro assinado, número inteiro não assinado nem ponto flutuante. A implementação do Android assume que dados de 8 bits são números inteiros não assinados e os de 16 bits são números inteiros assinados. Além disso, o campo samplesPerSec é um nome incorreto, já que a unidade real é mHz. Esperam-se que esses problemas sejam resolvidos na próxima versão do OpenSL ES, que introduzirá um novo formato PCM de dados estendido, que permitirá ao aplicativo especificar explicitamente a representação e corrigir o nome do campo. Como esse será um formato de dados novo, e o formato PCM atual continuará disponível (embora obsoleto), não deve ser necessário promover mudanças imediatas no código.