Extensões Android

O OpenSL ES para Android estende a especificação de referência do OpenSL ES para torná-la compatível com o Android e aproveitar o poder e a flexibilidade dessa plataforma.

A definição da API para as extensões Android pode ser encontrada em OpenSLES_Android.h e nos arquivos principais inclusos. Consulte OpenSLES_Android.h para ver detalhes sobre essas extensões. Esse arquivo fica localizado na raiz de instalação, no diretório sysroot/usr/include/SLES. A menos que indicado de outra forma, todas as interfaces são explícitas.

Essas extensões limitam a portabilidade do aplicativo a outras implementações do OpenSL ES porque elas são específicas ao Android. Para reduzir esse problema, evite as extensões ou use #ifdef para excluí-las no momento da compilação.

A tabela a seguir mostra as interfaces específicas do Android e os localizadores de dados compatíveis com o OpenSL ES Android para cada tipo de objeto. Os valores Sim nas células indicam as interfaces e os localizadores de dados disponíveis para cada tipo de objeto.

Recurso Player de áudio Gravador de áudio Motor Combinação de saída
Fila do buffer do Android Sim: fonte (decodificação) Não Não Não
Configuração do Android Sim Sim Não Não
Efeito do Android Sim Não Não Sim
Recursos de efeito do Android Não Não Sim Não
Envio de efeito do Android Sim Não Não Não
Fila do buffer simples do Android Sim: fonte (reprodução) ou coletor (decodificação) Sim Não Não
Localizador de dados da fila do buffer do Android Sim: fonte (decodificação) Não Não Não
Localizador de dados do descritor de arquivos do Android Sim: origem Não Não Não
Localizador de dados da fila do buffer simples do Android Sim: fonte (reprodução) ou coletor (decodificação) Sim: coletor Não Não

Interface de configuração do Android

A interface de configuração do Android fornece uma forma de definir parâmetros específicos da plataforma para objetos. Essa interface é diferente de outras do OpenSL ES 1.0.1, porque o aplicativo pode usá-la antes de instanciar o objeto correspondente. Dessa forma, é possível configurar o objeto antes de instanciá-lo. O arquivo principal OpenSLES_AndroidConfiguration.h, localizado em /sysroot/usr/include/SLES, documenta os seguintes valores e chaves de configuração disponíveis:

  • Tipo de streaming para players de áudio (SL_ANDROID_STREAM_MEDIA padrão)
  • Perfil de gravação para gravadores de áudio (SL_ANDROID_RECORDING_PRESET_GENERIC padrão).

O snippet de código abaixo mostra um exemplo de como definir o tipo de stream de áudio do Android em um player de áudio:

// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
// in the required interface ID array. Do not realize player yet.
// ...
SLAndroidConfigurationItf playerConfig;
result = (*playerObject)->GetInterface(playerObject,
    SL_IID_ANDROIDCONFIGURATION, &playerConfig);
assert(SL_RESULT_SUCCESS == result);
SLint32 streamType = SL_ANDROID_STREAM_ALARM;
result = (*playerConfig)->SetConfiguration(playerConfig,
    SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
assert(SL_RESULT_SUCCESS == result);
// ...
// Now realize the player here.

É possível usar um código parecido para configurar a predefinição para um gravador de áudio:

// ... obtain the configuration interface as the first four lines above, then:
SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
result = (*playerConfig)->SetConfiguration(playerConfig,
    RECORDING_PRESET, &presetValue, sizeof(SLuint32));

Interfaces de efeito do Android

As interfaces de efeito, envio de efeito e recursos de efeito do Android oferecem um mecanismo genérico para o aplicativo consultar e usar efeitos de áudio específicos do dispositivo. Os fabricantes de dispositivo precisam documentar todos os efeitos de áudio específicos do dispositivo que estejam disponíveis.

Aplicativos portáteis precisam usar as APIs do OpenSL ES 1.0.1 para efeitos de áudio em vez das extensões de efeito do Android.

Localizador de dados do descritor de arquivos do Android

Com o localizador de dados de descritor de arquivos do Android, é possível especificar a origem de um player de áudio como um descritor de arquivo aberto com acesso de leitura. O formato de dados deve ser MIME.

Essa extensão é especialmente útil em conjunto com o gerenciador de recursos nativos, porque o app lê recursos do APK por um descritor de arquivos.

Interface e localizador de dados da fila do buffer simples do Android

Na especificação de referência do OpenSL ES 1.0.1, as filas do buffer só podem ser usadas para players de áudio e são compatíveis com PCM e outros formatos de dados. As especificações da interface e do localizador de dados da fila do buffer simples do Android são idênticas às especificações de referência, com duas exceções:

  • É possível usar filas do buffer simples do Android com gravadores e players de áudio.
  • Só é possível usar o formato PCM com essas filas.

Para gravar, seu app precisa enfileirar buffers vazios. Quando um callback registrado envia uma notificação informando que o sistema terminou de gravar dados em um buffer, o app pode ler a partir desse buffer.

A reprodução funciona da mesma forma. No entanto, para compatibilidade futura do código-fonte, é recomendável que os aplicativos usem filas do buffer simples do Android, e não as do OpenSL ES 1.0.1.

Comportamento da fila do buffer

A implementação do Android não inclui a exigência da especificação de referência que determina que o cursor de reprodução retorne ao início do buffer em uso quando a reprodução entra no estado SL_PLAYSTATE_STOPPED. Essa implementação pode obedecer a esse comportamento ou não mudar o local do cursor de reprodução. Como resultado, seu app não poderá presumir a ocorrência de nenhum desses comportamentos. Portanto, é necessário chamar o método BufferQueue::Clear() explicitamente após uma transição para SL_PLAYSTATE_STOPPED. Fazer isso coloca a fila do buffer em um estado conhecido.

De forma similar, não há especificação que indique se o gatilho para o callback de uma fila do buffer precisa ser uma transição para SL_PLAYSTATE_STOPPED ou uma execução de BufferQueue::Clear(). Portanto, recomendamos não criar dependências em um dos métodos. Em vez disso, seu app precisa ser capaz de lidar com os dois casos.

Interfaces dinâmicas na criação de objetos

Por conveniência, a implementação do Android do OpenSL ES 1.0.1 permite que o app especifique interfaces dinâmicas durante a instanciação de um objeto. Isso pode ser usado como alternativa a DynamicInterfaceManagement::AddInterface() para adicionar essas interfaces após a instanciação.

Relatório de extensões

Há três métodos para consultar a compatibilidade da plataforma com extensões Android. Esses métodos são os seguintes:

  • Engine::QueryNumSupportedExtensions()
  • Engine::QuerySupportedExtension()
  • Engine::IsExtensionSupported()

Todos eles retornam ANDROID_SDK_LEVEL_<API-level>, em que API-level é o nível da API da plataforma. Por exemplo, ANDROID_SDK_LEVEL_23. APIs de plataforma de nível 9 ou mais recentes são compatíveis com as extensões.

Decodificar áudio para PCM

Esta seção descreve uma extensão obsoleta do OpenSL ES 1.0.1 específica para Android usada para decodificar um streaming codificado para PCM sem reprodução imediata. A tabela abaixo oferece recomendações de uso dessa extensão e algumas alternativas.

Nível da API Alternativas
15 e anteriores Um codec de código aberto com a licença adequada
16 a 20 A classe MediaCodec ou um codec de código aberto com a licença adequada
21 e mais recentes NDK MediaCodec nos arquivos principais <media/NdkMedia*.h>, a classe MediaCodec ou um codec de código aberto com a licença adequada

Observação: no momento, não há documentação para a versão do NDK da API MediaCodec. No entanto, você pode consultar o exemplo de código native-codec (link em inglês).

Um player de áudio padrão é reproduzido em um dispositivo de áudio, especificando o mix de saída como o coletor de dados. Com a extensão Android, o player de áudio age como decodificador se o app tiver especificado a origem dos dados como um URI ou como um localizador de dados de descritor de arquivos do Android descrito usando o formato MIME. Nesse caso, o coletor de dados é um localizador de dados da fila do buffer simples do Android que usa o formato PCM.

Esse recurso é destinado principalmente a jogos, para pré-carregar recursos de áudio ao mudar para uma nova fase, o que é semelhante à funcionalidade que a classe SoundPool oferece.

Inicialmente, o aplicativo enfileira um conjunto do buffers vazios na fila do buffer simples do Android. Depois disso, o app preenche os buffers com dados em formato PCM. O callback da fila do buffer simples do Android é disparado depois que todos os buffers são preenchidos. O gerenciador de callback processa os dados PCM, enfileira novamente o buffer vazio e depois retorna. O aplicativo é responsável por controlar os buffers decodificados. A lista de parâmetros de callback não tem informações suficientes para indicar o buffer que contém dados ou o que será enfileirado em seguida.

A origem de dados informa implicitamente o final do stream (EOS) fornecendo um evento SL_PLAYEVENT_HEADATEND ao final do stream. Depois que o app decodifica todos os dados recebidos, ele não faz outras chamadas ao callback de fila do buffer simples do Android.

O formato PCM do coletor normalmente tem a mesma taxa de amostragem, contagem de canais e profundidade de bits que a origem de dados codificados. No entanto, é possível decodificar para valores diferentes de taxa de amostragem, contagem de canais ou profundidade de bits. Para mais informações sobre um provisionamento para detectar o formato PCM real, consulte Determinar o formato dos dados PCM decodificados usando metadados.

O recurso de decodificação de PCM do OpenSL ES para Android é compatível com a pausa e a busca inicial, mas não com o controle de volume, efeitos, looping ou taxa de reprodução.

Dependendo da implementação da plataforma, a decodificação talvez exija recursos que não podem ficar ociosos. Por isso, é recomendável fornecer quantidades suficientes aos buffers de PCM vazios. Caso contrário, o decodificador ficará com privação. Isso pode acontecer, por exemplo, se o app retornar do callback da fila do buffer simples do Android sem enfileirar outro buffer vazio. O resultado da privação do decodificador não é especificado, mas pode incluir: queda nos dados PCM decodificados, pausa do processo de decodificação ou encerramento total do decodificador.

Observação: para decodificar um streaming codificado para PCM, mas não reproduzi-lo imediatamente, para apps executados no Android 4.x (API de nível 16 a 20), recomendamos usar a classe MediaCodec. Em novos aplicativos para Android 5.0 (API de nível 21) ou mais recentes, é recomendável usar o equivalente do NDK: <NdkMedia*.h>. Esses arquivos principais ficam no diretório media/ na raiz de instalação.

Decodificar streaming ADTS AAC para PCM

Um player de áudio atuará como decodificador de streaming se a origem de dados for um localizador de dados da fila do buffer do Android que usa o formato MIME e o coletor de dados for um localizador de dados da fila do buffer simples do Android que usa o formato PCM. Configure o formato MIME da seguinte forma:‎

  • Contêiner: SL_CONTAINERTYPE_RAW
  • String do tipo MIME: SL_ANDROID_MIME_AACADTS

Esse recurso se destina principalmente a aplicativos de streaming de mídia que lidam com áudio ACC, mas precisam fazer processamento de áudio personalizado antes da reprodução. A maioria dos aplicativos que precisam decodificar áudio para PCM precisam usar o método descrito na seção Decodificar áudio para PCM, porque ele é mais simples e lida com mais formatos de áudio. A técnica descrita aqui é uma abordagem mais especializada, que só poderá ser usada se as duas condições a seguir valerem:

  • A fonte de áudio comprimido é um stream de frames AAC em cabeçalhos ADTS.
  • O aplicativo gerencia esse streaming. Os dados não estão em um recurso de rede em que o identificador é um URI nem em um arquivo local em que o identificador é um descritor de arquivos.

Inicialmente, o aplicativo enfileira um conjunto de buffers cheios na fila do buffer do Android. Cada buffer tem um ou mais frames ADTS AAC completos. O callback da fila do buffer do Android é disparado depois que todos os buffers são esvaziados. O gerenciador de callback preenche e enfileira novamente o buffer e depois retorna. O aplicativo não precisa monitorar os buffers codificados. A lista de parâmetros de callback inclui informações suficientes para indicar o buffer que será enfileirado em seguida. O final do streaming é marcado explicitamente pelo enfileiramento de um item de EOS. Após o EOS, não serão permitidos mais enfileiramentos.

É recomendável fornecer buffers ADTS AAC completos para evitar a privação do decodificador. Isso pode acontecer, por exemplo, se o app retornar do callback da fila do buffer do Android sem enfileirar outro buffer completo. O resultado da privação do decodificador não é específico.

Em todos os aspectos, exceto para a fonte de dados, o método de decodificação de streaming é o mesmo descrito na seção Decodificar áudio para PCM.

Apesar da semelhança nos nomes, uma fila de buffer do Android não é igual a uma fila de buffer simples do Android. O decodificador de streaming usa os dois tipos de filas de buffer: uma fila do buffer do Android para a origem de dados ADTS AAC e uma fila do buffer simples do Android para o coletor de dados PCM. Para mais informações sobre a API da fila do buffer simples do Android, consulte Interface e localizador de dados da fila do buffer simples do Android. Para mais informações sobre a API da fila do buffer do Android, consulte o arquivo index.html no diretório docs/Additional_library_docs/openmaxal/ na raiz de instalação.

Determinar o formato dos dados PCM decodificados por metadados

A interface SLMetadataExtractionItf faz parte da especificação de referência. No entanto, as chaves de metadado que indicam o formato real dos dados PCM decodificados são específicas do Android. O arquivo principal OpenSLES_AndroidMetadata.h define essas chaves de metadados. Esse arquivo principal fica na raiz de instalação, no diretório /sysroot/usr/include/SLES.

Os índices de chave de metadados são disponibilizados imediatamente após o fim da execução do método Object::Realize(). No entanto, os valores associados não ficam disponíveis até que o app decodifique os primeiros dados codificados. Uma prática recomendada é solicitar os índices de chave na linha de execução principal depois de chamar o método Object::Realize e, ao chamá-lo pela primeira vez, ler os valores de metadados do formato PCM no gerenciador de callback da fila do buffer simples do Android. Consulte o exemplo de código no pacote do NDK (link em inglês) para ver como trabalhar com essa interface.

Os nomes das chaves de metadados são estáveis, mas os índices de chave não são documentados e estão sujeitos a mudanças. O aplicativo não deve pressupor que os índices são persistentes em diferentes execuções nem que várias instâncias de objeto compartilham índices na mesma execução.

Dados de ponto flutuante

Um app executado no Android 5.0 (API de nível 21) e versões mais recentes pode fornecer dados a um AudioPlayer em formato de ponto flutuante de precisão única.

No exemplo de código a seguir, o método Engine::CreateAudioPlayer() cria um player de áudio que usa dados de ponto flutuante:

#include <SLES/OpenSLES_Android.h>
...
SLAndroidDataFormat_PCM_EX pcm;
pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
pcm.numChannels = 2;
pcm.sampleRate = SL_SAMPLINGRATE_44_1;
pcm.bitsPerSample = 32;
pcm.containerSize = 32;
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
...
SLDataSource audiosrc;
audiosrc.pLocator = ...
audiosrc.pFormat = &pcm;
Leia mais sobre áudio de ponto flutuante na página de amostragem de áudio.