A API de frame rate permite que os apps informem à plataforma Android o frame rate pretendido e está disponível em apps destinados ao Android 11 (nível 30 da API) ou mais recente. Tradicionalmente, a maioria dos dispositivos oferece suporte a apenas uma taxa de atualização de tela, normalmente 60 Hz, mas isso está mudando. Muitos dispositivos agora oferecem suporte a outras taxas de atualização, como 90 Hz ou 120 Hz. Alguns dispositivos oferecem suporte a mudanças de taxa de atualização sem interrupções, enquanto outros mostram brevemente uma tela preta, geralmente por um segundo.
O principal objetivo da API é permitir que os apps aproveitem melhor todas
as taxas de atualização de tela compatíveis. Por exemplo, um app que reproduz um vídeo de 24 Hz
e chama setFrameRate() pode fazer com que o dispositivo mude a taxa de atualização
da tela de 60 Hz para 120 Hz. Essa nova taxa de atualização permite uma reprodução suave e sem trepidação de vídeos de 24 Hz, sem a necessidade de pulldown 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, então há
várias versões da API setFrameRate(). Cada versão da API usa os
mesmos parâmetros e funciona da mesma forma que as outras:
Surface.setFrameRate()SurfaceControl.Transaction.setFrameRate()ANativeWindow_setFrameRate()ASurfaceTransaction_setFrameRate()
O app não precisa considerar as taxas de atualização de tela compatíveis,
que podem ser obtidas chamando
Display.getSupportedModes(),
para chamar setFrameRate() com segurança. Por exemplo, mesmo que o dispositivo só
ofereça suporte a 60 Hz, chame setFrameRate() com a frame rate que seu app prefere.
Os dispositivos que não tiverem uma correspondência melhor para o frame rate do app vão permanecer com
a taxa de atualização atual.
Para conferir se uma chamada para setFrameRate() resulta em uma mudança na taxa de atualização
da tela, registre-se para receber notificações de mudança de tela chamando
DisplayManager.registerDisplayListener()
ou AChoreographer_registerRefreshRateCallback().
Ao chamar setFrameRate(), é melhor transmitir o frame rate exato em vez de arredondar para um número inteiro. Por exemplo, ao renderizar um vídeo gravado a
29,97 Hz, transmita 29,97 em vez de arredondar para 30.
Para apps de vídeo, o parâmetro de compatibilidade transmitido para setFrameRate() precisa ser definido
como Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE para dar uma dica adicional à
plataforma Android de que o app vai usar o pulldown para se adaptar a uma taxa de atualização de tela
não correspondente (o que vai resultar em trepidação).
Em alguns cenários, a superfície 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 fim 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 a configuração de frame rate
da superfície e voltar ao valor padrão. Não é necessário limpar a configuração de frame rate
ao destruir a superfície ou quando ela está
oculta porque o usuário mudou para outro app. Limpe a configuração de frame rate
somente quando a superfície permanecer visível sem ser usada.
Mudança de frame rate não integrada
Em alguns dispositivos, a mudança na taxa de atualização pode ter interrupções visuais, como uma tela
preta por um ou dois segundos. Isso geralmente ocorre em conversores, painéis de TV
e dispositivos semelhantes. Por padrão, o framework do Android não muda de modo
quando a API Surface.setFrameRate()
é chamada, para evitar essas interrupções visuais.
Alguns usuários preferem uma interrupção visual no início e no final de vídeos mais longos. Isso permite que a taxa de atualização da tela corresponda à taxa de frames do vídeo e evita artefatos de conversão de taxa de frames, como trepidação de pulldown 3:2 para reprodução de filmes.
Por esse motivo, as mudanças de taxa de atualização não contínuas podem ser ativadas se o usuário e os apps aceitarem:
- Usuários: para ativar, os usuários podem habilitar a configuração Ajustar frame rate do conteúdo.
- Apps: para ativar, os apps podem transmitir
CHANGE_FRAME_RATE_ALWAYSparasetFrameRate().
Recomendamos que você sempre use CHANGE_FRAME_RATE_ALWAYS
para vídeos de longa duração, como filmes. Isso ocorre porque o benefício de corresponder
ao frame rate do vídeo supera a interrupção que ocorre ao mudar 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 superfícies com diferentes configurações de frame rate. Quando o app tiver várias
superfícies com frame rates diferentes, chame setFrameRate() com o frame rate
correto para cada superfície. Mesmo que o dispositivo esteja executando vários apps ao
mesmo tempo, usando o modo de tela dividida ou picture-in-picture, cada app pode chamar
setFrameRate() com segurança para as próprias superfícies.
A plataforma não muda para o 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 uma configuração de frame rate diferente, ou o dispositivo pode estar no modo de economia de bateria (definindo uma restrição na taxa de atualização da tela para preservar a bateria). O app ainda precisa
funcionar corretamente quando o dispositivo não muda a taxa de atualização da tela para a
configuração de frame rate do app, mesmo que o dispositivo mude em circunstâncias
normais.
Cabe ao app decidir como responder quando a taxa de atualização da tela
não corresponde à frame rate do app. Para vídeo, o frame rate é fixado ao do vídeo de origem, e o pulldown será necessário para mostrar o conteúdo do vídeo. Um
jogo pode tentar ser executado na taxa de atualização da tela em vez de
permanecer com a frame rate preferida. O app não pode mudar o valor transmitido para setFrameRate() com base no que a plataforma faz. Ela precisa permanecer definida
como a frame rate preferencial do app, independentemente de como o app lida com casos em que
a plataforma não se ajusta para corresponder à solicitação do app. Dessa forma, se as condições do dispositivo
mudarem para permitir o uso de outras taxas de atualização de tela, a
plataforma terá as informações corretas para mudar para o frame rate
preferido do app.
Nos casos em que o app não pode ser executado na taxa de atualização da tela, ele precisa especificar carimbos de data/hora de apresentação para cada frame, usando um dos mecanismos da plataforma para definir carimbos de data/hora de apresentação:
O uso desses carimbos de data/hora impede que a plataforma apresente um frame do app muito cedo, o que resultaria em trepidação desnecessária. O uso correto de carimbos de data/hora de apresentação de frames é um pouco complicado. Para jogos, consulte nosso guia de frame pacing para mais informações sobre como evitar trepidação e considere usar a biblioteca Android Frame Pacing.
Em alguns casos, a plataforma pode mudar para um múltiplo do frame rate especificado pelo app
em setFrameRate(). Por exemplo, um app pode chamar setFrameRate()
com 60 Hz, e o dispositivo pode mudar a tela para 120 Hz. Isso pode acontecer,
por exemplo, se outro app tiver uma superfície com uma configuração de frame rate de 24 Hz. Nesse
caso, executar a tela a 120 Hz vai permitir que as superfícies de 60 Hz e
24 Hz sejam executadas sem a necessidade de pulldown.
Quando a tela está sendo executada em um múltiplo da frame rate do app, o app precisa especificar carimbos de data/hora de apresentação para cada frame para evitar trepidações desnecessárias. Para jogos, a biblioteca Android Frame Pacing é útil para definir corretamente os carimbos de data/hora de apresentação de frames.
setFrameRate() x preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
é outra maneira de os apps indicarem a frame rate para a plataforma. Alguns
apps só querem mudar a taxa de atualização da tela, em vez de mudar outras
configurações do modo de exibição, como a resolução. Em geral, use
setFrameRate() em vez de preferredDisplayModeId. A função setFrameRate()
é mais fácil de usar porque o app não precisa pesquisar na
lista de modos de exibição para encontrar um modo com uma frame rate específica.
setFrameRate() oferece à plataforma mais oportunidades de escolher uma frame rate
compatível em cenários em que há várias superfícies em execução com
frame rates diferentes. Por exemplo, considere um cenário em que dois apps estão
em execução no modo de tela dividida em um Pixel 4, em que um app está reproduzindo um vídeo de 24 Hz
e o outro está mostrando ao usuário uma lista rolável. O Pixel 4 é compatível com duas
taxas de atualização de tela: 60 Hz e 90 Hz. Usando a API preferredDisplayModeId,
a superfície de vídeo é forçada a escolher 60 Hz ou 90 Hz. Ao chamar
setFrameRate() com 24 Hz, a plataforma de vídeo fornece à plataforma mais
informações sobre o frame rate do vídeo de origem, permitindo que ela
escolha 90 Hz para a taxa de atualização da tela, que é melhor do que 60 Hz nesse
cenário.
No entanto, há cenários em que preferredDisplayModeId precisa ser usado
em vez de setFrameRate(), como os seguintes:
- Se o app quiser mudar a resolução ou outras configurações do modo de exibição,
use
preferredDisplayModeId. - A plataforma só vai mudar os modos de exibição em resposta a uma chamada para
setFrameRate()se a mudança for leve e provavelmente não será perceptível para o usuário. Se o app preferir mudar a taxa de atualização da tela, mesmo que isso exija uma mudança de modo pesada (por exemplo, em um dispositivo Android TV), usepreferredDisplayModeId. - Os apps que não conseguem processar a tela em execução em um múltiplo da frame rate
do app, o que exige a definição de carimbos de data/hora de apresentação em cada frame, precisam
usar
preferredDisplayModeId.
setFrameRate() x preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
define uma frame rate preferencial na janela do app, e a taxa é aplicável
a todas as superfícies dentro da janela. O app precisa especificar o frame rate
preferencial, independente das taxas de atualização compatíveis com o dispositivo, de forma semelhante a
setFrameRate(), para dar ao programador uma dica melhor do frame rate
pretendido pelo app.
preferredRefreshRate é ignorado para plataformas que usam setFrameRate(). Em
geral, use setFrameRate(), se possível.
preferredRefreshRate x preferredDisplayModeId
Se os apps quiserem apenas mudar a taxa de atualização preferencial, é melhor usar
preferredRefreshRate em vez de preferredDisplayModeId.
Evitar chamar setFrameRate() com muita frequência
Embora a chamada setFrameRate() não seja muito cara em termos de desempenho,
os apps precisam evitar chamar setFrameRate() em cada frame ou várias vezes por
segundo. As chamadas para setFrameRate() provavelmente vão resultar em uma mudança na
taxa de atualização da tela, o que pode resultar em uma queda de frame durante a transição.
Descubra o frame rate correto com antecedência e chame
setFrameRate() uma vez.
Uso para 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(), ela pode ser
usada para outros apps. Por exemplo, um jogo que não pretende ser executado acima de
60 Hz (para reduzir o consumo de energia e aumentar as sessões de jogo) pode chamar
Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT). Dessa
forma, um dispositivo que funciona a 90 Hz por padrão vai funcionar a 60 Hz enquanto o
jogo estiver ativo, o que evita a trepidação que ocorreria se o
jogo funcionasse a 60 Hz enquanto a tela funcionasse a 90 Hz.
Uso de FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE é destinado apenas a apps de vídeo. Para
uso sem vídeo, use FRAME_RATE_COMPATIBILITY_DEFAULT.
Como escolher uma estratégia para mudar o frame rate
- Recomendamos que os apps, ao exibir vídeos de longa duração, como
filmes, chamem
setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS), em que fps é o frame rate do vídeo. - Não recomendamos que os apps chamem
setFrameRate()comCHANGE_FRAME_RATE_ALWAYSquando você espera que a reprodução do vídeo dure alguns 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:
- Decida o
changeFrameRateStrategy:- Se estiver reproduzindo um vídeo longo, como um filme, use
MATCH_CONTENT_FRAMERATE_ALWAYS. - Se estiver reproduzindo um vídeo curto, como um trailer de filme, use
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
- Se estiver reproduzindo um vídeo longo, como um filme, use
- Se o
changeFrameRateStrategyforCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, vá para a etapa 4. - Detecte se uma mudança de taxa de atualização não contínua está prestes a acontecer verificando
se estes dois fatos são verdadeiros:
- Não é possível alternar o modo de forma integrada da taxa de atualização atual (vamos
chamá-la de C) para o frame rate do vídeo (vamos chamá-lo de V). Esse será o caso se C e V forem diferentes e
Display.getMode().getAlternativeRefreshRatesnão contiver um múltiplo de V. - O usuário aceitou mudanças na taxa de atualização que não são contínuas. Para detectar
isso, verifique se
DisplayManager.getMatchContentFrameRateUserPreferenceretornaMATCH_CONTENT_FRAMERATE_ALWAYS.
- Não é possível alternar o modo de forma integrada da taxa de atualização atual (vamos
chamá-la de C) para o frame rate do vídeo (vamos chamá-lo de V). Esse será o caso se C e V forem diferentes e
- Se a troca for fácil, faça o seguinte:
- Chame
setFrameRatee transmitafps,FRAME_RATE_COMPATIBILITY_FIXED_SOURCEechangeFrameRateStrategy, em quefpsé o frame rate do vídeo. - Iniciar reprodução de vídeo
- Chame
- Se uma mudança de modo não contínua estiver prestes a acontecer, faça o seguinte:
- Mostre a UX para notificar o usuário. Recomendamos que você implemente uma maneira de o usuário dispensar essa UX e pular o atraso adicional na etapa 5.d. Isso ocorre porque o atraso recomendado é maior do que o necessário em telas que apresentam tempos de troca mais rápidos.
- Chame
setFrameRatee transmitafps,FRAME_RATE_COMPATIBILITY_FIXED_SOURCEeCHANGE_FRAME_RATE_ALWAYS, em quefpsé o frame rate do vídeo. - Aguarde o callback
onDisplayChanged. - Aguarde 2 segundos para que a mudança de modo seja concluída.
- Iniciar reprodução de vídeo
O pseudocódigo para oferecer suporte apenas a Mudar de dispositivo é 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 à Mudar de dispositivo contínua e não contínua, 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();
}