OpenSL ES para Android

Esta página fornece detalhes sobre como a implementação do OpenSL ESTM do NDK difere da especificação de referência do OpenSL ES 1.0.1. Ao usar o exemplo de código da especificação, pode ser necessário modificá-lo para que ele funcione no Android.

A menos que indicado de outra forma, todos os recursos estão disponíveis no Android 2.3 (nível de API 9) e versões mais recentes. 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 (CDD) do Android 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 consultar o documento do CDD.

A OpenSL ES fornece uma interface de linguagem C que também pode ser acessada usando C++. Ela expõe recursos semelhantes às partes de áudio dessas APIs Java para Android:

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 se destina à programação de aplicativos puramente em C/C++. No entanto, o OpenSL ES é uma API repleta de recursos, e esperamos que ela possa atender à maioria das suas necessidades de áudio, sem precisar chamar código em execução no tempo de execução do Android.

Observação: embora ela seja 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 no Android NDK. Se houver um Sim na célula, isso significa que o recurso está disponível nessa implementação.

Compatibilidade do Android NDK com objetos e interfaces.

Recurso Player de áudio Gravador de áudio Motor Mix de saída
Aumento de graves Sim Não Não Sim
Fila do buffer Sim Não Não Não
Localizador de dados de fila de buffer Sim: origem 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
Motor 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: origem Não Não
Extração de metadados Sim: decodificar para PCM Não Não Não
Silenciar solo Sim Não Não Não
Objeto Sim Sim Sim Sim
Localizador do mix de saída Sim: coletor Não Não Não
Reproduzir 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 da predefinição 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: origem 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

Algumas 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: reverberação do ambiente e reverberação da predefinição

Não é possível ter ambos os tipos de reverberação no mesmo mix de saída.

A plataforma poderá ignorar solicitações de efeito se estimar que a carga da CPU seria muito alta.

Entrada de efeitos

SetSendLevel() aceita um único nível de saída por player de áudio.

Reverberação do ambiente

A reverberação do ambiente não aceita os campos reflectionsDelay, reflectionsLevel ou 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 player de áudio. Não é possível usar esse formato de dados para um gravador de áudio.

A implementação do OpenSL ES do Android exige a inicialização de mimeType como NULL ou uma string UTF-8 válida. Além disso, é necessário 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 app 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 também os aceite:

  • 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 compatíveis.

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

  • Os formatos AAC (link em inglês) devem ficar dentro de um contêiner ADTS ou MP4.
  • O OpenSL ES para Android não é compatível com MIDI.
  • WMA não faz parte da AOSP, e não verificamos a compatibilidade desse formato 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, é necessário descriptografá-lo no aplicativo antes da reprodução, com o app aplicando todas as restrições de DRM.

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

O 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 versões mais recentes oferecem suporte a dados de ponto flutuante.

Taxa de reprodução

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

A compatibilidade 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 valor único.

Gravar

O OpenSL ES para Android não aceita os eventos SL_RECORDEVENT_HEADATLIMIT ou 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 o localizador é especificado como a fonte de dados para Engine::CreateAudioRecorder(). Inicialize o localizador de dados do dispositivo usando os valores no seguinte snippet 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 de dados MIME e apenas para um player 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 o suporte para 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 com várias linhas de execução e é seguro para essas linhas. Ele oferece compatibilidade com um único mecanismo por aplicativo e até 32 objetos por mecanismo. A memória e a CPU disponíveis do dispositivo podem restringir mais o número de objetos que podem ser usados.

Estas opções de mecanismo são reconhecidas, mas ignoradas pelo slCreateEngine:

  • SL_ENGINEOPTION_THREADSAFE
  • SL_ENGINEOPTION_LOSSOFCONTROL

O OpenMAX AL e o OpenSL ES podem ser usados juntos no mesmo aplicativo. Nesse caso, haverá um único objeto de mecanismo compartilhado internamente, e o limite de 32 objetos será compartilhado entre o OpenMAX AL e o OpenSL ES. O aplicativo precisa criar, usar e destruir os dois 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 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 são baseadas no OpenSL ES 1.0.1 do Khronos Group (link em inglês). A Khronos lançou a versão 1.1, uma revisão do padrão. A versão revisada inclui novos recursos, esclarecimentos, correções de erros tipográficos e algumas incompatibilidades. A maioria das compatibilidades esperadas é relativamente pequena ou está em áreas do OpenSL ES não compatíveis com o Android.

Um aplicativo desenvolvido com essa versão funcionará em versões futuras da plataforma Android, desde que as orientações definidas na seção Planejar a 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, talvez seja necessário modificar o código-fonte do aplicativo para se adequar à nova API. Esperamos que a maior parte dessas alterações seja pequena. 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:

  • Use somente o subconjunto documentado de recursos do OpenSL ES 1.0.1 aceitos pelo Android.
  • Não dependa de determinado código resultante para uma operação sem sucesso. Prepare-se para lidar com um código resultante diferente.
  • Os gerenciadores de callback do aplicativo geralmente são executados em um contexto restrito. Eles precisam ser escritos para realizar uma função rapidamente e retornar assim que possível. Não execute operações complexas dentro de um gerenciador de callback. Por exemplo: em um callback de conclusão de fila de buffer, é possível enfileirar outro buffer, mas não crie um player de áudio.
  • Os gerenciadores de callback precisam estar preparados para serem chamados com mais ou menos frequência, para receber outros tipos de evento e ignorar tipos de evento que não reconhecem. Os callbacks configurados com uma máscara de eventos feita de tipos de evento ativos precisam estar preparados para serem chamados com vários conjuntos de bits de tipo de evento simultaneamente. Use “&” para testar cada bit de evento, em vez de um caso de alternância.
  • Use o status de pré-busca e callbacks como indicadores gerais de progresso, mas não dependa de níveis de preenchimento nem de sequências de callback específicas codificadas. 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, são esperadas incompatibilidades do código-fonte na próxima versão do OpenSL ES do Khronos Group. As prováveis áreas de mudança incluem:

  • A interface de fila do buffer deverá trazer mudanças importantes, principalmente 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 filas de buffer simples do Android para reprodução. Além disso, usamos uma fila de buffer simples do Android para gravar e decodificar para PCM. No entanto, essa opção foi usada porque o OpenSL ES 1.0.1 padrão não aceita gravação nem decodificação para um coletor de dados de fila do 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. Isso 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. Talvez seja necessário adicionar uma transmissão ou alterar o tipo de um parâmetro de SLint32 para SLuint32 ou algo parecido.
  • 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á outros campos nos tipos de estrutura. Em relação aos parâmetros de saída, esses novos campos podem ser ignorados. No entanto, nos parâmetros de entrada, os novos campos precisarão ser inicializados. Esperamos que todos esses campos estejam em áreas não aceitas pelo Android.
  • Os GUIDs de interface mudarão. Para evitar uma dependência, consulte as interfaces por nome simbólico, e não por GUID.
  • SLchar mudará de unsigned char para char. Isso afetará principalmente o localizador de dados de URI e o formato de dados MIME.
  • SLDataFormat_MIME.mimeType será renomeado para pMimeType, e SLDataLocator_URI.URI para pURI. É recomendável inicializar as estruturas de dados SLDataFormat_MIME e SLDataLocator_URI usando uma lista de valores separados por vírgula e fechados por chave, e não 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 os 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 tem um nome incorreto, já que a unidade real é mHz. Esses problemas deverão ser resolvidos na próxima versão do OpenSL ES, que introduzirá um novo formato de dados PCM 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 será necessário fazer modificações imediatas no código.