A API de frame rate permite que os apps informem à plataforma Android a taxa de frame pretendida e está disponível em apps direcionados ao Android 11 (nível 30 da API) ou mais recente. 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 agora são compatíveis com taxas de atualização adicionais, 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 principal objetivo da API é permitir que os apps 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 taxa de atualização
da 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:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
O app não precisa considerar as taxas de atualização de exibição compatíveis,
que podem ser obtidas 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 saber 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 a taxa de frames exata 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 extra à
plataforma Android de que o app vai usar o menu suspenso para se adaptar a uma taxa de atualização
de tela incompatível (o que vai resultar em tremor).
Em alguns casos, a superfície do vídeo para de enviar frames, mas permanece
visível na tela por algum tempo. Os 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 o
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 de taxa de atualização pode ter interrupções visuais, como uma tela
preta por um ou dois segundos. Isso geralmente ocorre em set-top boxes, 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 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 evite artefatos de conversão de frame rate, como o judder de puxada 3:2 para reprodução de filmes.
Por esse motivo, as chaves de taxa de atualização não contínuas poderão ser ativadas se as ativação de usuário e apps:
- Usuários: para ativar o recurso, os usuários podem ativar a opção Corresponder ao frame rate do conteúdo. do ambiente.
- Apps: para ativar, os apps podem transmitir
CHANGE_FRAME_RATE_ALWAYS
emsetFrameRate()
.
Recomendamos que você sempre use CHANGE_FRAME_RATE_ALWAYS
para vídeos mais longos, como filmes. Isso ocorre porque o benefício de corresponder
à taxa de frames do vídeo é maior do que 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 processar corretamente cenários em que há
várias plataformas com diferentes configurações de taxa de frames. Quando o app tiver várias
superfícies com taxas de frames diferentes, chame setFrameRate()
com a taxa de frames
correta para cada superfície. 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 para a taxa de frames do app
Mesmo que o dispositivo ofereça suporte à taxa de frames especificada pelo app em uma chamada para
setFrameRate()
, há casos em que o dispositivo não alterna a tela para
essa taxa de atualização. Por exemplo, uma superfície de prioridade mais alta pode ter uma configuração
de taxa de frames 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
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, a taxa de frames é fixada na do
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 pode mudar o valor que
é 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 as condições
do dispositivo mudarem para permitir que outras taxas de atualização da tela sejam usadas, a
plataforma terá as informações corretas para mudar para a taxa de
quadros preferida do app.
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. O uso correto de carimbos de data/hora de apresentação de frames é 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 alternar a tela para 120 Hz. Isso pode
acontecer se outro app tiver uma superfície com uma configuração de taxa de frames de 24 Hz. Nesse
caso, executar a tela a 120 Hz permite que a superfície de 60 Hz e
a de 24 Hz sejam executadas sem a necessidade de pulldown.
Quando a tela estiver executando em um múltiplo da taxa de frames do app, ele precisa especificar os carimbos de data/hora de apresentação para cada frame para evitar tremeliques desnecessários. Para jogos, a biblioteca Android Frame Pacing é útil para corrigir definindo 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, pois o aplicativo não precisa procurar na
lista de modos de exibição para encontrar um modo com um frame rate específico.
setFrameRate()
oferece à plataforma mais oportunidades de escolher uma taxa de frames
compatível em cenários em que há várias plataformas em execução com
taxas de frames diferentes. Por exemplo, considere um cenário em que dois apps estão
executando 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 oferece suporte a duas
taxas de atualização da tela: 60 Hz e 90 Hz. Ao usar 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 superfície do vídeo fornece à plataforma mais
informações sobre a taxa de frames do vídeo de origem, permitindo que a plataforma
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 no exemplo a seguir:
- Se o app quiser mudar 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 mudar a taxa de atualização da tela, mesmo que exija uma mudança de modo pesado (por exemplo, em um dispositivo Android TV), usepreferredDisplayModeId
. - Os apps que não conseguem processar a exibição em uma taxa de frames
múltipla, o que exige a configuração de carimbos de data/hora de apresentação em cada frame, precisam
usar
preferredDisplayModeId
.
setFrameRate() vs. PreferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
define uma taxa de frames 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 para 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
.
Como 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 todos os frames 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 para jogos ou outros apps que não sejam de vídeo
Embora o vídeo seja o caso de uso principal da API setFrameRate()
, ela pode ser
usada para 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)
: Dessa
forma, um dispositivo que funciona a 90 Hz por padrão será executado a 60 Hz enquanto o
jogo estiver ativo, o que evitará o tremor que ocorreria se o
jogo fosse executado a 60 Hz enquanto a tela estivesse a 90 Hz.
Uso de FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
é destinado apenas a apps de vídeo. Para
usos que não sejam de vídeo, use FRAME_RATE_COMPATIBILITY_DEFAULT
.
Como escolher uma estratégia para mudar a taxa de frames
- É 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 que os apps chamem
setFrameRate()
comCHANGE_FRAME_RATE_ALWAYS
quando você espera que a reprodução de 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:
- Decida o
changeFrameRateStrategy
:- Se você estiver reproduzindo um vídeo longo, como um filme, use
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Se estiver assistindo um vídeo curto, como um trailer de movimento, use
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
.
- Se você estiver reproduzindo um vídeo longo, como um filme, use
- Se o
changeFrameRateStrategy
forCHANGE_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
que ambos os fatos são verdadeiros:
- 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
acontece se C e V forem diferentes e
Display.getMode().getAlternativeRefreshRates
não contiver um múltiplo de V. - O usuário aceitou mudanças contínuas na taxa de atualização. Você pode detectar
isso verificando se
DisplayManager.getMatchContentFrameRateUserPreference
retornaMATCH_CONTENT_FRAMERATE_ALWAYS
- 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
acontece se C e V forem diferentes e
- Se a migração for simples, faça o seguinte:
- Chamar
setFrameRate
e passarfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, echangeFrameRateStrategy
, em quefps
é o frame rate do vídeo. - Iniciar a reprodução do vídeo
- Chamar
- 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 exibimos tempos de troca mais rápidos.
- Chamar
setFrameRate
e passarfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, eCHANGE_FRAME_RATE_ALWAYS
,fps
é o frame rate do vídeo. - Aguarde o evento
onDisplayChanged
o retorno de chamada. - Aguarde 2 segundos para que a troca de modo seja concluída.
- 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 à alternância perfeita e não perfeita, 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();
}