Frame rate

A API de frame rate permite que os apps informem a Plataforma Android sobre o frame pretendido e está disponível em apps direcionados ao Android 11 (nível 30 da API) ou versões mais recentes. Tradicionalmente, a maioria dos dispositivos oferece suporte a uma única taxa de atualização da tela, normalmente 60 Hz, mas isso está mudando. Muitos dispositivos como 90 Hz ou 120 Hz. Alguns dispositivos oferecem suporte à taxa de atualização contínua interruptores, enquanto outros mostram brevemente uma tela preta, geralmente com duração de um segundo.

O objetivo principal da API é permitir que os aplicativos aproveitem melhor todas as taxas de atualização de exibição compatíveis. Por exemplo, um app que reproduz um vídeo de 24 Hz que chama setFrameRate() pode fazer com que o dispositivo mude a tela de 60 Hz para 120 Hz. Essa nova taxa de atualização permite uma interface a reprodução de vídeos a 24 Hz sem trepidação e sem a necessidade do menu suspenso de 3:2, como seria necessário para reproduzir o mesmo vídeo em uma tela de 60 Hz. Isso resulta em uma melhor experiência do usuário.

Uso básico

O Android expõe várias maneiras de acessar e controlar superfícies. Portanto, há várias versões da API setFrameRate(). Cada versão da API usa e funciona da mesma forma que os outros:

O app não precisa considerar as taxas de atualização de tela compatíveis. que pode ser obtido chamando Display.getSupportedModes(), para chamar setFrameRate() com segurança. Por exemplo, mesmo que apenas o dispositivo ofereça suporte a 60 Hz, chame setFrameRate() com o frame rate de preferência do app. Os dispositivos que não têm uma correspondência melhor para o frame rate do app continuarão com a taxa de atualização da tela atual.

Para conferir se uma chamada para setFrameRate() resulta em uma mudança na atualização da tela registrar-se para notificações de alteração de exibição chamando DisplayManager.registerDisplayListener() ou AChoreographer_registerRefreshRateCallback().

Ao chamar setFrameRate(), recomendamos transmitir o frame rate exato em vez do que arredondar para um número inteiro. Por exemplo, ao renderizar um vídeo gravado 29,97 Hz, passe 29,97 em vez de arredondar para 30.

Para apps de vídeo, o parâmetro de compatibilidade transmitido para setFrameRate() precisa ser definido. para Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE para dar uma dica adicional a a plataforma Android na qual o aplicativo usará o menu suspenso para se adaptar a um a taxa de atualização da tela, o que resulta em trepidação.

Em alguns casos, a plataforma de vídeo vai parar de enviar frames, mas vai permanecer visível na tela por algum tempo. Cenários comuns incluem quando a reprodução chega ao final do vídeo ou quando o usuário pausa a reprodução. Nesses casos, Chame setFrameRate() com o parâmetro de frame rate definido como 0 para limpar o frame rate para o valor padrão. Como limpar a configuração de frame rate isso não é necessário ao destruir a superfície ou quando ela estiver oculta porque o usuário muda para outro app. Limpar o frame rate configuração apenas quando a superfície permanece visível sem ser usada.

Troca de frame rate não perfeita

Em alguns dispositivos, a mudança da taxa de atualização pode ter interrupções visuais, como um preto na tela por um ou dois segundos. Isso normalmente ocorre em conversores, painéis de TV e dispositivos semelhantes. Por padrão, o framework do Android não alterna de modo. quando o Surface.setFrameRate() API é chamada, para evitar essas interrupções visuais.

Alguns usuários preferem uma interrupção visual no início e fim de vídeos mais longos. Isso permite que a taxa de atualização da tela corresponda o frame rate do vídeo e evite artefatos de conversão de frame rate como 3:2 trepidação no menu suspenso para a reprodução de filmes.

Por essa razão, as chaves de taxa de atualização não contínuas poderão ser ativadas se as ativação por usuário e apps:

Recomendamos que você sempre use CHANGE_FRAME_RATE_ALWAYS para vídeos longos, como filmes. Isso ocorre porque o benefício de corresponder o frame rate do vídeo supera a interrupção que ocorre ao alterar e a taxa de atualização.

Recomendações adicionais

Siga estas recomendações para cenários comuns.

Várias superfícies

A plataforma Android foi projetada para lidar corretamente com cenários em que há várias plataformas com diferentes configurações de frame rate. Quando seu app tem vários com frame rates diferentes, chame setFrameRate() com a o frame rate de cada plataforma. Mesmo que o dispositivo execute vários aplicativos usando a tela dividida ou o modo picture-in-picture, cada app pode chamar setFrameRate() para as próprias plataformas.

A plataforma não muda no frame rate do app

Mesmo que o dispositivo seja compatível com o frame rate especificado pelo app em uma chamada para setFrameRate(), há casos em que o dispositivo não muda a tela para essa taxa de atualização. Por exemplo, uma superfície de prioridade mais alta pode ter um do frame rate, ou o dispositivo pode estar no modo de economia de bateria (definindo um restrição na taxa de atualização da tela para economizar bateria). O app ainda precisa funcionam corretamente quando o dispositivo não muda a taxa de atualização da tela para o à configuração de frame rate do app, mesmo que o dispositivo mude para um ritmo normal em cada situação.

Cabe ao app decidir como responder quando a taxa de atualização da tela não corresponde ao frame rate do app. Para vídeos, o frame rate é o valor do o vídeo de origem, e o menu suspenso será necessário para mostrar o conteúdo do vídeo. Um pode optar por executar na taxa de atualização da tela em vez manter o frame rate preferido. O app não deve mudar o valor é transmitido para setFrameRate() com base no que a plataforma faz. Ele deve ficar definido para o frame rate preferencial do app, independentemente de como ele lida com casos em que a plataforma não se ajustar para corresponder à solicitação do app. Dessa forma, se o dispositivo as condições mudam para permitir o uso de taxas de atualização de exibição adicionais, a plataforma tenha as informações corretas para alternar para o frame preferido do app e a taxa de conversão.

Nos casos em que o aplicativo não pode ou não pode ser executado com a taxa de atualização da tela, o aplicativo devem especificar carimbos de data/hora da apresentação para cada frame, usando um dos mecanismos da plataforma para definir carimbos de data/hora da apresentação:

O uso desses carimbos de data/hora também impede que a plataforma apresente um frame do app cedo, o que resultaria em trepidação desnecessária. Uso correto do frame os carimbos de data/hora da apresentação é um pouco complicado. Para jogos, consulte nossa guia de ritmo de frames para mais informações sobre como evitar trepidação, e considere usar o Biblioteca Android Frame Pacing.

Em alguns casos, a plataforma pode alternar para um múltiplo do frame rate do app especificado em setFrameRate(). Por exemplo, um app pode chamar setFrameRate() com 60 Hz, e o dispositivo pode mudar a tela para 120 Hz. Uma razão pela qual isso pode acontecer se outro app tiver uma plataforma com uma configuração de frame rate de 24 Hz. Em nesse caso, executar a tela a 120 Hz permitirá que a superfície de 60 Hz e Superfície de 24 Hz para execução sem necessidade de abrir o menu suspenso.

Quando a tela está sendo executada em um múltiplo do frame rate do app, ele deve especificar carimbos de data/hora da apresentação para cada frame para evitar o uso desnecessário trepidação. Para jogos, a biblioteca Android Frame Pacing é útil para corrigir definir carimbos de data/hora da apresentação do frame.

setFrameRate() vs. PreferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId é outra forma de os apps indicarem o frame rate para a plataforma. Algumas os aplicativos querem apenas alterar a taxa de atualização da tela, em vez de alterar outros configurações do modo de exibição, como a resolução da tela. Em geral, use setFrameRate() em vez de preferredDisplayModeId. O setFrameRate() é mais fácil de usar porque o aplicativo não precisa procurar na lista de modos de exibição para encontrar um modo com um frame rate específico.

setFrameRate() dá à plataforma mais oportunidades de escolher um em cenários em que há várias superfícies em execução frame rates diferentes. Por exemplo, considere um cenário em que dois aplicativos executados no modo de tela dividida em um Pixel 4, em que um app reproduz um vídeo de 24 Hz e o outro está mostrando ao usuário uma lista rolável. O Pixel 4 oferece suporte a dois exibir taxas de atualização: 60 Hz e 90 Hz. Usando a API preferredDisplayModeId, a superfície de vídeo for forçada a escolher 60 Hz ou 90 Hz; Ligando para setFrameRate() a 24 Hz, a superfície de vídeo oferece à plataforma mais informações sobre o frame rate do vídeo de origem, permitindo que a plataforma escolher 90 Hz para a taxa de atualização da tela, que é melhor do que 60 Hz neste diferente.

No entanto, há cenários em que preferredDisplayModeId precisa ser usado em vez de setFrameRate(), como no exemplo a seguir:

  • Se o app quiser alterar a resolução ou outras configurações do modo de exibição, use preferredDisplayModeId.
  • A plataforma só alternará os modos de exibição em resposta a uma chamada para setFrameRate() se a chave de modo for leve e provavelmente não será perceptíveis para o usuário. Se o app preferir alternar a atualização da tela mesmo que exija uma chave de modo grosso (por exemplo, no Android TV dispositivo), use preferredDisplayModeId.
  • Apps que não podem processar a exibição em um múltiplo do frame do app que requer a definição de carimbos de data/hora da apresentação em cada frame, deve use preferredDisplayModeId.

setFrameRate() vs. PreferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate define um frame rate preferencial na janela do app, e a taxa é aplicável. a todas as superfícies dentro da janela. O app deve especificar a preferência independentemente das taxas de atualização compatíveis com o dispositivo, semelhante às setFrameRate(), para dar ao programador uma dica melhor sobre a intenção do app frame rate.

preferredRefreshRate é ignorado nas plataformas que usam setFrameRate(). Em use setFrameRate(), se possível, de forma geral.

PreferredRefreshRate vs. PreferredDisplayModeId

Se os aplicativos só quiserem alterar a taxa de atualização preferencial, é preferível usar preferredRefreshRate, em vez de preferredDisplayModeId.

Evitar chamar setFrameRate() com muita frequência

Embora a chamada setFrameRate() não tenha um custo muito elevado em termos de desempenho, os apps precisam evitar chamar setFrameRate() a cada frame ou várias vezes por segundo. As chamadas para setFrameRate() provavelmente resultarão em uma mudança no a taxa de atualização da tela, o que pode resultar em uma queda de frames durante a transição. Descobrir o frame rate correto com antecedência e chamar setFrameRate() uma vez.

Uso em jogos ou outros apps que não são de vídeo

Embora o vídeo seja o principal caso de uso da API setFrameRate(), ele pode ser usados por outros apps. Por exemplo, um jogo que não pretende executar mais de 60 Hz (para reduzir o uso de energia e ter sessões de reprodução mais longas) pode chamar Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT): Neste Dessa forma, um dispositivo que executa a 90 Hz por padrão será executado a 60 Hz, enquanto o está ativo, o que evita a trepidação que ocorreria se o foi executado a 60 Hz, e a tela, a 90 Hz.

Uso de FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

O FRAME_RATE_COMPATIBILITY_FIXED_SOURCE é destinado apenas a apps de vídeo. Para sem vídeo, use FRAME_RATE_COMPATIBILITY_DEFAULT.

Como escolher uma estratégia para mudar o frame rate

  • É altamente recomendável que os aplicativos, ao exibirem vídeos de longa duração, filmes, chamada a setFrameRate(qps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) em que QPS é o frame rate do vídeo.
  • Não recomendamos apps que chamem setFrameRate() com CHANGE_FRAME_RATE_ALWAYS. quando você espera que a reprodução do vídeo dure vários minutos ou menos.

Exemplo de integração para apps de reprodução de vídeo

Recomendamos as seguintes etapas para integrar as mudanças de taxa de atualização em apps de reprodução de vídeo:

  1. Decida a changeFrameRateStrategy:
    1. Se estiver assistindo um vídeo de longa duração, como um filme, use MATCH_CONTENT_FRAMERATE_ALWAYS
    2. Se estiver assistindo um vídeo curto, como um trailer de movimento, use CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. Se o changeFrameRateStrategy for CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , vá para a etapa 4.
  3. Detecte se uma mudança de taxa de atualização não contínua está prestes a acontecer verificando que ambos os fatos são verdadeiros:
    1. Não é possível mudar para o modo contínuo devido à taxa de atualização atual. chamá-lo de C) ao frame rate do vídeo (vamos chamá-lo de V). Isso vai se C e V forem diferentes e Display.getMode().getAlternativeRefreshRates não contém um múltiplo de V.
    2. O usuário aceitou mudanças contínuas na taxa de atualização. Você pode detectar isso verificando se DisplayManager.getMatchContentFrameRateUserPreference retorna MATCH_CONTENT_FRAMERATE_ALWAYS
  4. Se a migração for simples, faça o seguinte:
    1. Chamar setFrameRate e passar fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, e changeFrameRateStrategy, em que fps é o frame rate do vídeo.
    2. Iniciar a reprodução do vídeo
  5. Se uma mudança de modo não contínua estiver prestes a acontecer, faça o seguinte:
    1. Mostre a UX para notificar o usuário. Recomendamos que você implemente uma forma que o usuário dispense essa UX e ignore o atraso adicional na etapa 5.d. Isso é porque o atraso recomendado é maior que o necessário em telas que exibir tempos de troca mais rápidos.
    2. Chamar setFrameRate e passar fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, e CHANGE_FRAME_RATE_ALWAYS, fps é o frame rate do vídeo.
    3. Aguarde o evento onDisplayChanged o retorno de chamada.
    4. Aguarde 2 segundos para que a troca de modo seja concluída.
    5. Iniciar a reprodução do vídeo

O pseudocódigo para oferecer suporte apenas à alternância perfeita é o seguinte:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
    contentFrameRate,
    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
    CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();

O pseudocódigo para oferecer suporte a alternâncias contínuas e não contínuas, conforme descrito acima, é o seguinte:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (isSeamlessSwitch(contentFrameRate)) {
  transaction.setFrameRate(surfaceControl,
      contentFrameRate,
      FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
      CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
  transaction.apply();
  beginPlayback();
} else if (displayManager.getMatchContentFrameRateUserPreference()
      == MATCH_CONTENT_FRAMERATE_ALWAYS) {
  showRefreshRateSwitchUI();
  sleep(shortDelaySoUserSeesUi);
  displayManager.registerDisplayListener(…);
  transaction.setFrameRate(surfaceControl,
      contentFrameRate,
      FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
      CHANGE_FRAME_RATE_ALWAYS);
  transaction.apply();
  waitForOnDisplayChanged();
  sleep(twoSeconds);
  hideRefreshRateSwitchUI();
  beginPlayback();
}