Encapsulamento de multimídia

O encapsulamento multimídia permite que dados de vídeo compactados sejam encapsulados por um hardware decodificador de vídeo diretamente em uma tela, sem ser processado pelo código do app ou Código do framework do Android. O código específico do dispositivo abaixo da pilha do Android determina quais frames de vídeo devem ser enviados à tela e quando enviá-los comparando os carimbos de data/hora da apresentação do frame de vídeo com uma das opções a seguir tipos de relógios internos:

  • Para a reprodução de vídeos sob demanda no Android 5 ou superior, um AudioTrack relógio sincronizado com carimbos de data/hora de apresentações em áudio aprovados pelo app

  • Para reprodução de transmissão ao vivo no Android 11 ou versões mais recentes, um relógio de referência do programa (PCR, na sigla em inglês) ou relógio de horário do sistema (STC, na sigla em inglês) acionado por um sintonizador

Contexto

A reprodução de vídeo tradicional no Android notifica quando um frame de vídeo compactado for decodificado. Em seguida, o app versões o frame de vídeo decodificado para a tela ser renderizado no mesmo relógio do sistema que o frame de áudio correspondente, recuperando dados históricos AudioTimestamps instâncias para calcular o tempo correto.

Como a reprodução de vídeo encapsulada ignora o código do app e reduz o número de processos que atuam no vídeo, ele pode fornecer uma renderização de vídeo mais eficiente dependendo da implementação do OEM. Ele também pode fornecer imagens cadência e sincronização para o relógio escolhido (PRC, STC ou áudio), evitando problemas de tempo introduzidos por possível desvio entre o tempo do Android solicitações para renderizar vídeo e o tempo de vsyncs de hardware reais. No entanto, o encapsulamento também pode reduzir a compatibilidade com efeitos da GPU, como desfoque ou cantos arredondados em janelas picture-in-picture (PiP), porque os buffers ignorar a pilha de gráficos do Android.

O diagrama a seguir mostra como o encapsulamento simplifica o processo de reprodução de vídeo.

comparação dos modos de túnel e tradição

Figura 1. Comparação entre processos de reprodução de vídeo tradicionais e em túnel

Para desenvolvedores de apps

Porque a maioria dos desenvolvedores de apps faz a integração com uma biblioteca para reprodução na maioria dos casos exige apenas a reconfiguração desse para reprodução encapsulada. Para implementação de baixo nível de um vídeo encapsulado siga as instruções a seguir.

Para reprodução de vídeos sob demanda no Android 5 ou mais recente:

  1. Crie uma instância SurfaceView.

  2. Crie uma instância audioSessionId.

  3. Criar instâncias AudioTrack e MediaCodec com o audioSessionId instância criada na etapa 2.

  4. Coloque os dados de áudio na fila de AudioTrack com o carimbo de data/hora da apresentação do primeiro frame de áudio nos dados de áudio.

Para a reprodução de transmissões ao vivo no Android 11 ou versões mais recentes, faça o seguinte:

  1. Crie uma instância SurfaceView.

  2. Receba uma instância avSyncHwId de Tuner.

  3. Criar instâncias AudioTrack e MediaCodec com a instância avSyncHwId criado na etapa 2.

O fluxo de chamadas de API é mostrado nos snippets de código a seguir:

aab.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE);

// configure for audio clock sync
aab.setFlag(AudioAttributes.FLAG_HW_AV_SYNC);
// or, for tuner clock sync (Android 11 or higher)
new tunerConfig = TunerConfiguration(0, avSyncId);
aab.setTunerConfiguration(tunerConfig);
if (codecName == null) {
  return FAILURE;
}

// configure for audio clock sync
mf.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
// or, for tuner clock sync (Android 11 or higher)
mf.setInteger(MediaFormat.KEY_HARDWARE_AV_SYNC_ID, avSyncId);

Comportamento da reprodução de vídeo sob demanda

Como a reprodução de vídeo sob demanda em túnel está vinculada implicitamente a AudioTrack do vídeo, o comportamento da reprodução de vídeo em túnel poderá depender do comportamento de reprodução de áudio.

  • Na maioria dos dispositivos, por padrão, um frame de vídeo não é renderizado até que o áudio a reprodução for iniciada. No entanto, o app pode precisar renderizar um frame de vídeo antes iniciar a reprodução de áudio, por exemplo, para mostrar ao usuário o vídeo atual posição durante a busca.

    • Para sinalizar que o primeiro frame de vídeo na fila deve ser renderizado assim que estiver decodificado, defina o PARAMETER_KEY_TUNNEL_PEEK como 1. Quando frames de vídeo compactados são reordenados na fila (como quando frames B presentes), o primeiro frame de vídeo exibido deve ser sempre uma com o iframe.

    • Se você não quiser que o primeiro frame de vídeo na fila seja renderizado até o áudio a reprodução começar, defina esse parâmetro como 0.

    • Se esse parâmetro não for definido, o OEM determinará o comportamento para o dispositivo.

  • Quando dados de áudio não são fornecidos para AudioTrack e os buffers estão vazios (redução do áudio), a reprodução do vídeo para até que mais dados de áudio sejam gravados porque o relógio de áudio não está mais avançando.

  • Durante a reprodução, podem aparecer descontinuidades que o app não consegue corrigir carimbos de data/hora da apresentação em áudio. Quando isso acontece, o OEM corrige o problema lacunas interrompendo o frame atual do vídeo, e lacunas positivas caindo quadros de vídeo ou inserção de quadros de áudio silenciosos (dependendo do OEM) implementação. A posição do frame AudioTimestamp não aumenta nos frames de áudio silenciosos inseridos.

Para fabricantes de dispositivos

Configuração

Os OEMs precisam criar um decodificador de vídeo separado para oferecer suporte à reprodução em túnel. Esse decodificador deve anunciar que é capaz de reprodução em túnel na Arquivo media_codecs.xml:

<Feature name="tunneled-playback" required="true"/>

Quando uma instância MediaCodec em túnel é configurada com um ID de sessão de áudio, ela consulta AudioFlinger para esse ID de HW_AV_SYNC:

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);
}

Durante essa consulta, AudioFlinger recupera o ID da HW_AV_SYNC. do dispositivo de áudio principal e o associa internamente ao ID da sessão:

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);

Se uma instância AudioTrack já tiver sido criada, o ID HW_AV_SYNC será transmitidos para o stream de saída com o mesmo ID da sessão de áudio. Se ainda não tiver sido for criado, o ID HW_AV_SYNC será transmitido para o stream de saída durante Criação de AudioTrack. Isso é feito pelo botão de reprodução conversa:

mOutput->stream->common.set_parameters(&mOutput->stream->common, AUDIO_PARAMETER_STREAM_HW_AV_SYNC, hwAVSyncId);

O ID HW_AV_SYNC, se corresponde a um stream de saída de áudio ou a um configuração Tuner, é passada ao componente OMX ou Codec2 para que o O código OEM pode associar o codec ao stream de saída de áudio correspondente ou o fluxo do sintonizador.

Durante a configuração do componente, o componente OMX ou Codec2 deve retornar um Alça de banda lateral que pode ser usada para associar o codec a um Hardware Composer (HWC). Quando o app associa uma superfície a MediaCodec, essa banda lateral é transmitido para o HWC por SurfaceFlinger, que configura camada como um camada de banda lateral.

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;
}

O HWC é responsável por receber novos buffers de imagem da saída do codec no o momento apropriado, seja sincronizado com o stream de saída de áudio associado ou o relógio de referência do programa do sintonizador, compondo os buffers com os valores conteúdos de outras camadas e exibição da imagem resultante. Isso acontece seja qual for o ciclo normal de preparação e definição. As chamadas "preparar" e "definir" acontecem apenas quando outras camadas mudam ou quando as propriedades da camada de banda lateral (como posição ou tamanho).

OMX

Um componente decodificador encapsulado precisa ser compatível com o seguinte:

  • Como definir a extensão OMX.google.android.index.configureVideoTunnelMode que usa a estrutura ConfigureVideoTunnelModeParams para transmitir no ID do HW_AV_SYNC associado ao dispositivo de saída de áudio.

  • Configurar o parâmetro OMX_IndexConfigAndroidTunnelPeek que informa ao para renderizar ou não renderizar o primeiro frame de vídeo decodificado, independentemente se a reprodução de áudio foi iniciada.

  • Enviar o evento OMX_EventOnFirstTunnelFrameReady quando o primeiro túnel frame de vídeo foi decodificado e está pronto para ser renderizado.

A implementação do AOSP configura o modo de túnel ACodec através OMXNodeInstance conforme mostrado neste snippet de código:

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;

Se o componente for compatível com esta configuração, deverá alocar uma banda lateral processar esse codec e transmiti-lo de volta pelo membro pSidebandWindow. para que o HWC possa identificar o codec associado. Se o componente não oferecem suporte a essa configuração, ele precisa definir bTunneled como OMX_FALSE.

Codec2

No Android 11 ou em versões mais recentes, o Codec2 oferece suporte à reprodução em túnel. O decodificador deve ser compatível com o seguinte:

  • Configurar C2PortTunneledModeTuning, que configura o modo de túnel e transmite na HW_AV_SYNC extraída do dispositivo de saída de áudio ou a configuração do sintonizador.

  • Consulte o C2_PARAMKEY_OUTPUT_TUNNEL_HANDLE para alocar e recuperar o alça da banda lateral para HWC.

  • Como processar C2_PARAMKEY_TUNNEL_HOLD_RENDER quando anexado a um C2Work, que instrui o codec a decodificar e sinalizar a conclusão do trabalho, mas não a renderizar o buffer de saída até que 1) o codec seja instruído a renderizá-lo ou 2) a reprodução do áudio for iniciada.

  • Como processar C2_PARAMKEY_TUNNEL_START_RENDER, que instrui o codec a imediatamente o frame que foi marcado com C2_PARAMKEY_TUNNEL_HOLD_RENDER, mesmo que a reprodução de áudio não tenha sido iniciada.

  • Deixe debug.stagefright.ccodec_delayed_params sem configuração (recomendado). Se depois de configurá-lo, defina como false.

A implementação do AOSP configura o modo de túnel CCodec usando C2PortTunnelModeTuning, conforme mostrado no snippet de código a seguir:

if (msg->findInt32("audio-hw-sync", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::AUDIO_HW_SYNC;
} else if (msg->findInt32("hw-av-sync-id", &tunneledPlayback->m.syncId[0])) {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::HW_AV_SYNC;
} else {
    tunneledPlayback->m.syncType =
            C2PortTunneledModeTuning::Struct::sync_type_t::REALTIME;
    tunneledPlayback->setFlexCount(0);
}
c2_status_t c2err = comp->config({ tunneledPlayback.get() }, C2_MAY_BLOCK,
        failures);
std::vector<std::unique_ptr<C2Param>> params;
c2err = comp->query({}, {C2PortTunnelHandleTuning::output::PARAM_TYPE},
        C2_DONT_BLOCK, &params);
if (c2err == C2_OK && params.size() == 1u) {
    C2PortTunnelHandleTuning::output *videoTunnelSideband =
            C2PortTunnelHandleTuning::output::From(params[0].get());
    return OK;
}

Se o componente for compatível com esta configuração, deverá alocar uma banda lateral processar esse codec e transmiti-lo de volta por C2PortTunnelHandlingTuning para que para que o HWC possa identificar o codec associado.

HAL de áudio

Para a reprodução de vídeos sob demanda, a HAL de áudio recebe a apresentação de áudio carimbos de data/hora inline com os dados de áudio no formato big-endian dentro de um cabeçalho encontrado no início de cada bloco de dados de áudio que o app grava:

struct TunnelModeSyncHeader {
  // The 32-bit data to identify the sync header (0x55550002)
  int32 syncWord;
  // The size of the audio data following the sync header before the next sync
  // header might be found.
  int32 sizeInBytes;
  // The presentation timestamp of the first audio sample following the sync
  // header.
  int64 presentationTimestamp;
  // The number of bytes to skip after the beginning of the sync header to find the
  // first audio sample (20 bytes for compressed audio, or larger for PCM, aligned
  // to the channel count and sample size).
  int32 offset;
}

Para que o HWC renderize quadros de vídeo em sincronia com os quadros de áudio correspondentes, a A HAL de áudio precisa analisar o cabeçalho de sincronização e usar o carimbo de data/hora da apresentação para ressincronizar o relógio de reprodução com a renderização de áudio. Para sincronizar novamente quando o áudio compactado estiver sendo reproduzido, a HAL de áudio poderá precisar analisar metadados dentro dos dados de áudio comprimido para determinar a duração da reprodução.

Pausar suporte

O Android 5 e versões anteriores não incluem suporte para pausa. É possível pausar reprodução apenas pela privação de A/V, mas se o buffer interno do vídeo for grande (por exemplo, houver um segundo de dados no componente OMX), isso pausa aparentam não responder.

No Android 5.1 ou versões mais recentes, AudioFlinger é compatível com pausa e retomada para em túneis diferentes. Se a HAL implementar a pausa e retomar, rastrear a pausa e a retomada são encaminhadas à HAL.

A sequência de pausa, limpeza e retomada de chamadas é respeitada pela execução de chamadas HAL. na sequência de reprodução (igual ao descarregamento).

Sugestões de implementação

HAL de áudio

No Android 11, o ID de sincronização de HW da PCR ou STC pode ser usado para sincronização A/V. streaming somente de vídeo é aceito.

Para o Android 10 ou versões anteriores, os dispositivos que oferecem suporte à reprodução de vídeo encapsulada precisam ter ter pelo menos um perfil de stream de saída de áudio com FLAG_HW_AV_SYNC e AUDIO_OUTPUT_FLAG_DIRECT no arquivo audio_policy.conf. Essas sinalizações 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 vídeos em túnel reprodução (os fabricantes podem ter componentes OMX adicionais para outros tipos de reprodução de áudio e vídeo, como reprodução segura). O componente em túnel deve:

  • Especificar 0 buffers (nBufferCountMin, nBufferCountActual) na saída porta

  • Implemente a extensão OMX.google.android.index.prepareForAdaptivePlayback setParameter.

  • Especifique os recursos no arquivo media_codecs.xml e declare o recurso de reprodução encapsulada. Ela também deve esclarecer as limitações tamanho, alinhamento ou taxa de bits. Veja um exemplo abaixo:

    <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 encapsulada, ele deve deixar o recurso de reprodução encapsulada como não obrigatório. Tanto tunelamento quanto os decodificadores sem túnel terão as mesmas limitações de capacidade. Um exemplo é mostrados abaixo:

<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 de hardware (HWC)

Quando há uma camada em túnel (uma camada com HWC_SIDEBAND compositionType) em uma tela, o sidebandStream da camada é a alça da banda lateral alocada pelo Componente de vídeo OMX.

O HWC sincroniza quadros de vídeo decodificados (do componente OMX encapsulado) para a faixa de áudio associada (com o ID audio-hw-sync). Quando um novo frame de vídeo se torna atual, o HWC a compõe com o conteúdo atual de todas as camadas recebidas durante a última chamada de preparação ou configuração e exibe a imagem resultante. As chamadas de preparo ou definição acontecem apenas quando outras camadas mudam ou quando as propriedades da camada de banda lateral (como posição ou tamanho).

A figura a seguir representa o HWC funcionando com o hardware (ou kernel ou driver) para combinar quadros de vídeo (7b) com a composição mais recente (7a) para exibição no momento correto, com base no áudio (7c).

HWC combinando frames de vídeo com base em áudio

Figura 2. Sincronizador de hardware (ou kernel ou driver) de HWC