Você pode implementar o encapsulamento multimídia na estrutura do Android 5.0 e superior. Embora o encapsulamento de multimídia não seja necessário para o Android TV, ele oferece a melhor experiência para conteúdo de ultra-alta definição (4K).
Para Android 11 ou superior, você pode implementar o encapsulamento multimídia com conteúdo de áudio e vídeo alimentado diretamente do Tuner. Codec2
e AudioTrack
podem usar o ID de sincronização HW do Tuner, que pode corresponder ao canal de referência do relógio do programa (PCR) ou do relógio do sistema (STC).
Fundo
A estrutura de mídia do Android lida com conteúdo de áudio/vídeo de quatro maneiras:
- Software puro (decodificação local): O processador de aplicativos (AP) decodifica localmente o áudio para modulação por código de pulso (PCM) sem aceleração especial. Sempre usado para Ogg Vorbis e usado para MP3 e AAC quando não há suporte para descarregamento compactado.
- O descarregamento de áudio compactado envia dados de áudio compactados diretamente para o processador de sinal digital (DSP) e mantém o AP desligado o máximo possível. Use para reproduzir arquivos de música com a tela desligada.
- A passagem de áudio compactado envia áudio compactado (especificamente AC3 e E-AC3) diretamente por HDMI para uma TV externa ou receptor de áudio, sem decodificá-lo no dispositivo Android TV. A parte de vídeo é tratada separadamente.
- O encapsulamento multimídia envia dados de áudio e vídeo compactados juntos. Quando o fluxo codificado é recebido pelos decodificadores de vídeo e áudio, ele não volta para a estrutura. Idealmente, o fluxo não interrompe o AP.
- A passagem multimídia envia dados compactados de áudio e vídeo do Tuner para os decodificadores de vídeo e áudio sem envolver a estrutura.
Comparação de abordagem
Software puro | Descarregamento de áudio compactado | Passagem de áudio compactado | Tunelamento multimídia | Passagem multimídia | |
---|---|---|---|---|---|
Local de decodificação | PA | DSP | TV ou receptor de áudio/vídeo (AVR) | TV ou AVR | TV ou AVR |
Lida com áudio | sim | sim | sim | sim | não |
Lida com vídeo | sim | não | não | sim | não |
Para desenvolvedores de aplicativos
Crie uma instância SurfaceView
, obtenha uma ID de sessão de áudio e, em seguida, crie as instâncias AudioTrack
e MediaCodec
para fornecer o tempo e as configurações necessárias para reprodução e decodificação de quadros de vídeo.
Para Android 11 ou superior, como alternativa para o ID da sessão de áudio, o aplicativo pode obter o ID de sincronização de HW do Tuner e fornecê-lo às instâncias AudioTrack
e MediaCodec
para sincronização A/V.
Sincronização A/V
No modo de encapsulamento multimídia, áudio e vídeo são sincronizados em um relógio mestre.
- Para Android 11 ou superior, PCR ou STC do Tuner pode ser o relógio mestre para sincronização A/V.
- Para Android 10 ou inferior, o relógio de áudio é o relógio mestre usado para reprodução A/V.
Se a instância MediaCodec
de vídeo encapsulada e as instâncias AudioTrack
estiverem vinculadas à instância HW_AV_SYNC
em AudioTrack
, o relógio implícito derivado de HW_AV_SYNC
restringirá quando cada quadro de vídeo e amostra de áudio for apresentado, com base no carimbo de hora de apresentação (PTS) de áudio ou quadro de vídeo real.
Fluxo de chamadas da API
Para Android 11 ou superior, o cliente pode usar o ID de sincronização de HW do Tuner.
- Crie uma instância
SurfaceView
.SurfaceView sv = new SurfaceView(mContext);
- Obtenha um ID de sessão de áudio. Esse ID exclusivo é usado na criação da faixa de áudio (
AudioTrack
). Ele é passado para o codec de mídia (MediaCodec
) e usado pela estrutura de mídia para vincular os caminhos de áudio e vídeo.AudioManager am = mContext.getSystemService(AUDIO_SERVICE); int audioSessionId = am.generateAudioSessionId() // or, for Android 11 or higher int avSyncId = tuner.getAvSyncHwId();
- Crie
AudioTrack
com HW A/V syncAudioAttributes
.O gerenciador de política de áudio solicita à camada de abstração de hardware (HAL) uma saída de dispositivo que suporte
FLAG_HW_AV_SYNC
e cria uma trilha de áudio diretamente conectada a essa saída sem mixer intermediário.AudioAttributes.Builder aab = new AudioAttributes.Builder(); aab.setUsage(AudioAttributes.USAGE_MEDIA); aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE); aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC); // or, for Android 11 or higher new tunerConfig = TunerConfiguration(0, avSyncId); aab.setTunerConfiguration(tunerConfig); AudioAttributes aa = aab.build(); AudioTrack at = new AudioTrack(aa);
- Crie uma instância
MediaCodec
de vídeo e configure-a para reprodução de vídeo em túnel.// retrieve codec with tunneled video playback feature MediaFormat mf = MediaFormat.createVideoFormat(“video/hevc”, 3840, 2160); mf.setFeatureEnabled(CodecCapabilities.FEATURE_TunneledPlayback, true); MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); String codecName = mcl.findDecoderForFormat(mf); if (codecName == null) { return FAILURE; } // create codec and configure it mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId); // or, for Android 11 or higher mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId); MediaCodec mc = MediaCodec.createCodecByName(codecName); mc.configure(mf, sv.getSurfaceHolder().getSurface(), null, 0);
- Decodifique os quadros de vídeo.
mc.start(); for (;;) { int ibi = mc.dequeueInputBuffer(timeoutUs); if (ibi >= 0) { ByteBuffer ib = mc.getInputBuffer(ibi); // fill input buffer (ib) with valid data ... mc.queueInputBuffer(ibi, ...); } // no need to dequeue explicitly output buffers. The codec // does this directly to the sideband layer. } mc.stop(); mc.release(); mc = null;
Nota: Você pode alternar a ordem das etapas 3 e 4 neste processo, conforme visto nas duas figuras abaixo.
Para fabricantes de dispositivos
Os OEMs devem criar um componente decodificador de vídeo OpenMAX IL (OMX) separado para dar suporte à reprodução de vídeo em túnel. Este componente OMX deve anunciar que é capaz de reprodução em túnel (em media_codecs.xml
).
<Feature name=”tunneled-playback” required=”true” />
O componente também deve oferecer suporte a um parâmetro estendido OMX OMX.google.android.index.configureVideoTunnelMode
que usa uma estrutura ConfigureVideoTunnelModeParams
.
struct ConfigureVideoTunnelModeParams { OMX_U32 nSize; // IN OMX_VERSIONTYPE nVersion; // IN OMX_U32 nPortIndex; // IN OMX_BOOL bTunneled; // IN/OUT OMX_U32 nAudioHwSync; // IN OMX_PTR pSidebandWindow; // OUT };
Quando uma solicitação de criação de MediaCodec
encapsulada é feita, a estrutura configura o componente OMX no modo encapsulado (definindo bTunneled
como OMX_TRUE
) e passa o dispositivo de saída de áudio associado criado com um sinalizador AUDIO_HW_AV_SYNC
para o componente OMX (em nAudioHwSync
).
Se o componente oferecer suporte a essa configuração, ele deve alocar um identificador de banda lateral para esse codec e passá-lo de volta pelo membro pSidebandWindow
. Um identificador de banda lateral é uma etiqueta de identificação para a camada encapsulada que permite que o Hardware Composer (HW Composer) a identifique. Se o componente não suportar esta configuração, ele deve definir bTunneled
para OMX_FALSE
.
A estrutura recupera a camada encapsulada (o identificador de banda lateral) alocada pelo componente OMX e a passa para o HW Composer. O tipo de compositionType
dessa camada é definido como HWC_SIDEBAND
. (Consulte hardware/libhardware/include/hardware/hwcomposer.h
.)
O HW Composer é responsável por receber novos buffers de imagem do stream no momento apropriado (por exemplo, sincronizado com o dispositivo de saída de áudio associado), compondo-os com o conteúdo atual de outras camadas e exibindo a imagem resultante. Isso acontece independentemente do ciclo normal de preparação/configuração. As chamadas de preparação/configuração ocorrem somente quando outras camadas são alteradas ou quando as propriedades da camada de banda lateral (como posição ou tamanho) são alteradas.
Configuração
frameworks/av/services/audioflinger/AudioFlinger.cpp
O HAL retorna o ID HW_AV_SYNC
como uma representação decimal de cadeia de caracteres de um inteiro de 64 bits. (Veja frameworks/av/services/audioflinger/AudioFlinger.cpp
.)
audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); char *reply = dev->get_parameters(dev, AUDIO_PARAMETER_HW_AV_SYNC); AudioParameter param = AudioParameter(String8(reply)); int hwAVSyncId; param.getInt(String8(AUDIO_PARAMETER_HW_AV_SYNC), hwAVSyncId);
frameworks/av/services/audioflinger/Threads.cpp
A estrutura de áudio deve encontrar o fluxo de saída HAL ao qual essa ID de sessão corresponde e consultar o HAL para o hwAVSyncId
usando set_parameters
.
mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC=hwAVSyncId);
Configuração do decodificador OMX
MediaCodec.java
A estrutura de áudio encontra o fluxo de saída HAL correspondente para esta ID de sessão e recupera a ID audio-hw-sync
consultando a HAL para o sinalizador AUDIO_PARAMETER_STREAM_HW_AV_SYNC
usando get_parameters
.
// Retrieve HW AV sync audio output device from Audio Service // in MediaCodec.configure() if (entry.getKey().equals(MediaFormat.KEY_AUDIO_SESSION_ID)) { int sessionId = 0; try { sessionId = (Integer)entry.getValue(); } catch (Exception e) { throw new IllegalArgumentException("Wrong Session ID Parameter!"); } keys[i] = "audio-hw-sync"; values[i] = AudioSystem.getAudioHwSyncForSession(sessionId); } // ...
Esse ID de sincronização de HW é passado para o decodificador de vídeo em túnel OMX usando o parâmetro personalizado OMX.google.android.index.configureVideoTunnelMode
.
ACodec.cpp
Depois de obter o ID de sincronização de hardware de áudio, o ACodec o usa para configurar o decodificador de vídeo encapsulado para que o decodificador de vídeo encapsulado saiba qual faixa de áudio sincronizar.
// Assume you're going to use tunneled video rendering. // Configure OMX component in tunneled mode and grab sideband handle (sidebandHandle) from OMX // component. native_handle_t* sidebandHandle; // Configure OMX component in tunneled mode status_t err = mOMX->configureVideoTunnelMode(mNode, kPortIndexOutput, OMX_TRUE, audioHwSync, &sidebandHandle);
OMXNodeInstance.cpp
O componente OMX é configurado pelo método configureVideoTunnelMode
acima.
// paraphrased OMX_INDEXTYPE index; OMX_STRING name = const_cast<OMX_STRING>( "OMX.google.android.index.configureVideoTunnelMode"); OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); ConfigureVideoTunnelModeParams tunnelParams; InitOMXParams(&tunnelParams); tunnelParams.nPortIndex = portIndex; tunnelParams.bTunneled = tunneled; tunnelParams.nAudioHwSync = audioHwSync; err = OMX_SetParameter(mHandle, index, &tunnelParams); err = OMX_GetParameter(mHandle, index, &tunnelParams); sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;
ACodec.cpp
Depois que o componente OMX é configurado no modo encapsulado, o identificador de banda lateral é associado à superfície de renderização.
err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle); if (err != OK) { ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).", sidebandHandle, err); return err; }
Em seguida, a dica de resolução máxima, se presente, é enviada ao componente.
// Configure max adaptive playback resolution - as for any other video decoder int32_t maxWidth = 0, maxHeight = 0; if (msg->findInt32("max-width", &maxWidth) && msg->findInt32("max-height", &maxHeight)) { err = mOMX->prepareForAdaptivePlayback( mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight); }
Pausar suporte
O Android 5.0 e inferior não inclui suporte a pausa. Você pode pausar a reprodução em túnel somente por inanição de A/V, mas se o buffer interno para vídeo for grande (por exemplo, houver 1 segundo de dados no componente OMX), a pausa parecerá sem resposta.
No Android 5.1 e superior, o AudioFlinger suporta pausar e retomar para saídas de áudio diretas (em túnel). Se o HAL implementar pausa/retomada, a pausa/retomada da faixa será encaminhada para o HAL.
A sequência de chamada de pausa, liberação e retomada é respeitada executando as chamadas HAL no thread de reprodução (o mesmo que descarregamento).
Suporte a Codec2
Para Android 11 ou superior, o Codec2 é compatível com a reprodução em túnel.
CCodec.cpp
Para suportar a reprodução em túnel, o Codec2 funciona de forma semelhante ao OMX. Para suportar o ID de sincronização de HW do Tuner, o Codec2 procura o ID de sincronização nas fontes abaixo.
sp<ANativeWindow> nativeWindow = static_cast<ANativeWindow *>(surface.get()); int32_t audioHwSync = 0; if (!msg->findInt32("hw-av-sync-id", &audioHwSync)) { if (!msg->findInt32("audio-hw-sync", &audioHwSync)) { } } err = configureTunneledVideoPlayback(comp, audioHwSync, nativeWindow);
Sugestões de implementação
Áudio HAL
Para o Android 11, o ID de sincronização de HW do PCR ou STC pode ser usado para sincronização A/V, portanto, o fluxo somente de vídeo é compatível.
Para Android 10 ou inferior, os dispositivos compatíveis com a reprodução de vídeo em túnel devem ter pelo menos um perfil de fluxo de saída de áudio com os sinalizadores FLAG_HW_AV_SYNC
e AUDIO_OUTPUT_FLAG_DIRECT
em seu arquivo audio_policy.conf
. Esses sinalizadores são usados para definir o relógio do sistema a partir do relógio de áudio.
OMX
Os fabricantes de dispositivos devem ter um componente OMX separado para reprodução de vídeo em túnel. Os fabricantes podem ter componentes OMX adicionais para outros tipos de reprodução de áudio e vídeo, como reprodução segura.
Este componente deve especificar 0 buffers ( nBufferCountMin
, nBufferCountActual
) em sua porta de saída.
O componente encapsulado também deve implementar a extensão OMX.google.android.index.prepareForAdaptivePlayback setParameter
.
O componente encapsulado deve especificar seus recursos no arquivo media_codecs.xml
e declarar o recurso de reprodução encapsulada. Ele também deve esclarecer quaisquer limitações no tamanho do quadro, alinhamento ou taxa de bits.
<MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC.tunneled"
type="video/avc" >
<Feature name="adaptive-playback" />
<Feature name="tunneled-playback" required=”true” />
<Limit name="size" min="32x32" max="3840x2160" />
<Limit name="alignment" value="2x2" />
<Limit name="bitrate" range="1-20000000" />
...
</MediaCodec>
Se o mesmo componente OMX for usado para dar suporte à decodificação em túnel e não em túnel, ele deverá deixar o recurso de reprodução em túnel como não necessário. Ambos os decodificadores encapsulados e não encapsulados têm as mesmas limitações de capacidade.
<MediaCodec name="OMX.OEM_NAME.VIDEO.DECODER.AVC" type="video/avc" > <Feature name="adaptive-playback" /> <Feature name="tunneled-playback" /> <Limit name="size" min="32x32" max="3840x2160" /> <Limit name="alignment" value="2x2" /> <Limit name="bitrate" range="1-20000000" /> ... </MediaCodec>
Compositor HW
Quando há uma camada encapsulada (uma camada com HWC_SIDEBAND
compositionType
) em uma exibição, o sidebandStream
da camada é o identificador de banda lateral alocado pelo componente de vídeo OMX.
O HW Composer sincroniza quadros de vídeo decodificados (do componente OMX encapsulado) com a faixa de áudio associada (com o ID audio-hw-sync
). Quando um novo quadro de vídeo se torna atual, o HW Composer o compõe com o conteúdo atual de todas as camadas recebidas durante a última chamada de preparação/definição e exibe a imagem resultante. As chamadas prepare/set acontecem apenas quando outras camadas mudam, ou quando as propriedades da camada de banda lateral (como posição ou tamanho) mudam.
A Figura 4 representa o HW Composer trabalhando com o sincronizador HW (ou kernel/driver), para combinar quadros de vídeo (7b) com a última composição (7a) para exibição no momento correto, com base no áudio (7c).