프레임 속도 API는 Android 11 (API 수준 30) 이상을 타겟팅하는 앱에서 사용할 수 있으며 앱은 이 API를 사용하여 의도한 프레임 속도를 Android 플랫폼에 알릴 수 있습니다. 일반적으로 대부분의 기기는 보통 60Hz의 단일 디스플레이 새로고침 빈도만 지원했는데 이러한 상황이 바뀌고 있습니다. 이제 많은 기기가 90Hz 또는 120Hz와 같은 추가 새로고침 빈도를 지원합니다. 일부 기기는 원활한 새로고침 빈도 전환을 지원하는 반면 다른 기기는 일반적으로 1초 동안 검은색 화면을 잠깐 표시합니다.
API의 기본 목적은 앱이 지원되는 모든 디스플레이 새로고침 빈도를 더 잘 활용할 수 있도록 하는 것입니다. 예를 들어 setFrameRate()를 호출하는 24Hz 동영상을 재생하는 앱은 기기가 디스플레이 새로고침 빈도를 60Hz에서 120Hz로 변경하도록 할 수 있습니다. 이 새로운 새로고침 빈도를 사용하면 60Hz 디스플레이에서 동일한 동영상을 재생하는 데 필요한 3:2 풀다운이 필요 없이 24Hz 동영상을 부드럽고 끊김 없이 재생할 수 있습니다. 따라서 사용자 환경이 개선됩니다.
기본 사용법
Android는 노출 영역에 액세스하고 노출 영역을 제어하는 여러 방법을 제공합니다. 따라서 setFrameRate() API에는 다음과 같은 여러 가지 버전이 있습니다. API의 각 버전은 동일한 매개변수를 사용하고 다른 버전과 동일하게 작동합니다.
Surface.setFrameRate()SurfaceControl.Transaction.setFrameRate()ANativeWindow_setFrameRate()ASurfaceTransaction_setFrameRate()
앱은 setFrameRate()를 안전하게 호출하기 위해 Display.getSupportedModes()를 호출하여 가져올 수 있는 실제 지원되는 디스플레이 새로고침 빈도를 고려할 필요가 없습니다. 예를 들어 기기가 60Hz만 지원하더라도 앱이 선호하는 프레임 속도로 setFrameRate()를 호출합니다.
앱의 프레임 속도와 더 잘 일치하는 항목이 없는 기기는 현재 디스플레이 새로고침 빈도를 유지합니다.
setFrameRate() 호출로 인해 디스플레이 새로고침
빈도가 변경되는지 확인하려면
DisplayManager.registerDisplayListener()
또는 AChoreographer_registerRefreshRateCallback()을 호출하여 디스플레이 변경 알림을 등록합니다.
setFrameRate()를 호출할 때는 정수로 반올림하는 대신 정확한 프레임 속도를 전달하는 것이 가장 좋습니다. 예를 들어 29.97Hz로 녹화된 동영상을 렌더링할 때는 30으로 반올림하는 대신 29.97을 전달합니다.
동영상 앱의 경우 setFrameRate()에 전달되는 호환성 매개변수를 Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE로 설정하여 앱이 풀다운을 사용하여 일치하지 않는 화면 재생 빈도에 적응할 것임을 Android 플랫폼에 추가로 알려야 합니다 (이로 인해 끊김이 발생함).
일부 시나리오에서는 동영상 노출 영역이 프레임 제출을 중지하지만 화면에 잠시 표시됩니다. 일반적인 시나리오에는 재생이 동영상 끝에 도달하거나 사용자가 재생을 일시중지하는 경우가 포함됩니다. 이러한 경우 프레임 속도 매개변수가 0으로 설정된 setFrameRate()를 호출하여 노출 영역의 프레임 속도 설정을 기본값으로 다시 지웁니다. 노출 영역을 소멸할 때 또는 사용자가 다른 앱으로 전환하여 노출 영역이 숨겨질 때는 이와 같이 프레임 속도 설정을 지울 필요가 없습니다. 노출 영역이 사용되지 않고 계속 표시되는 경우에만 프레임 속도 설정을 지웁니다.
원활하지 않은 프레임 속도 전환
일부 기기에서는 새로고침 빈도 전환 시 1~2초 동안 검은색 화면이 표시되는 것과 같은 시각적인 방해가 있을 수 있습니다. 이 문제는 일반적으로 셋톱박스, TV 패널 및 유사한 기기에서 발생합니다. 기본적으로 Android 프레임워크는 이러한 시각적인 방해를 방지하기 위해 모드를 전환하지 않습니다.
Surface.setFrameRate()
API가 호출될 때
일부 사용자는 긴 동영상의 시작과 끝에 시각적인 방해가 있는 것을 선호합니다. 이렇게 하면 디스플레이의 새로고침 빈도가 동영상 프레임 속도와 일치하고 영화 재생 시 3:2 풀다운 끊김과 같은 프레임 속도 변환 아티팩트를 방지할 수 있습니다.
이러한 이유로 사용자와 앱이 모두 선택하는 경우 원활하지 않은 새로고침 빈도 전환을 사용 설정할 수 있습니다.
- 사용자: 선택하려면 사용자가 콘텐츠 프레임 속도 일치 사용자 설정을 사용 설정하면 됩니다.
- 앱: 선택하려면 앱이
CHANGE_FRAME_RATE_ALWAYS를setFrameRate()에 전달하면 됩니다.
영화와 같은 장시간 실행되는 동영상에는 항상 CHANGE_FRAME_RATE_ALWAYS
를 사용하는 것이 좋습니다. 이는 동영상 프레임 속도를 일치시키는 것이 새로고침 빈도를 변경할 때 발생하는 방해보다 더 유익하기 때문입니다.
추가 권장사항
일반적인 시나리오에서는 다음 권장사항을 따르세요.
여러 노출 영역
Android 플랫폼은 프레임 속도 설정이 다른 여러 노출 영역이 있는 시나리오를 올바르게 처리하도록 설계되었습니다. 앱에 프레임 속도가 다른 여러 노출 영역이 있는 경우 각 노출 영역에 올바른 프레임 속도로 setFrameRate()를 호출합니다. 기기가 분할 화면 또는 PIP 모드를 사용하여 여러 앱을 동시에 실행하더라도 각 앱은 자체 노출 영역에 대해 setFrameRate()를 안전하게 호출할 수 있습니다.
플랫폼이 앱의 프레임 속도로 변경되지 않음
기기가 setFrameRate() 호출에서 앱이 지정하는 프레임 속도를 지원하더라도 기기가 디스플레이를 해당 새로고침 빈도로 전환하지 않는 경우가 있습니다. 예를 들어 우선순위가 높은 노출 영역의 프레임 속도 설정이 다를 수 있거나 기기가 배터리 절약 모드일 수 있습니다 (배터리를 보존하기 위해 디스플레이 새로고침 빈도에 제한을 설정함). 기기가 정상적인 상황에서 전환하더라도 앱은 기기가 디스플레이 새로고침 빈도를 앱의 프레임 속도 설정으로 전환하지 않을 때 올바르게 작동해야 합니다.
디스플레이 새로고침 빈도가 앱 프레임 속도와 일치하지 않을 때 어떻게 응답할지는 앱에서 결정합니다. 동영상의 경우 프레임 속도는 소스 동영상의 프레임 속도로 고정되며 동영상 콘텐츠를 표시하려면 풀다운이 필요합니다. 게임은 선호하는 프레임 속도를 유지하는 대신 디스플레이 새로고침 빈도로 실행하려고 선택할 수 있습니다. 앱은 플랫폼의 동작에 따라 setFrameRate()에 전달하는 값을 변경해서는 안 됩니다. 앱이 플랫폼이 앱의 요청에 맞게 조정되지 않는 경우를 처리하는 방법과 관계없이 앱의 선호하는 프레임 속도로 설정된 상태를 유지해야 합니다. 이렇게 하면 기기 조건이 변경되어 추가 디스플레이 새로고침 빈도를 사용할 수 있게 되면 플랫폼은 앱의 선호하는 프레임 속도로 전환할 수 있는 올바른 정보를 갖게 됩니다.
앱이 디스플레이 새로고침 빈도로 실행되지 않거나 실행할 수 없는 경우 앱은 프레젠테이션 타임스탬프를 설정하기 위한 플랫폼의 메커니즘 중 하나를 사용하여 각 프레임의 프레젠테이션 타임스탬프를 지정해야 합니다.
이러한 타임스탬프를 사용하면 플랫폼이 앱 프레임을 너무 일찍 표시하여 불필요한 끊김이 발생하는 것을 방지할 수 있습니다. 프레임 프레젠테이션 타임스탬프를 올바르게 사용하는 것은 약간 까다롭습니다. 게임의 경우 끊김을 방지하는 방법에 관한 자세한 내용은 프레임 속도 가이드 를 참고하고 Android Frame Pacing 라이브러리 사용을 고려하세요.
경우에 따라 플랫폼이 앱이 setFrameRate()에서 지정한 프레임 속도의 배수로 전환할 수 있습니다. 예를 들어 앱이 60Hz로 setFrameRate()를 호출하고 기기가 디스플레이를 120Hz로 전환할 수 있습니다. 이러한 상황이 발생하는 한 가지 이유는 다른 앱에 프레임 속도 설정이 24Hz인 노출 영역이 있는 경우입니다. 이 경우 디스플레이를 120Hz로 실행하면 60Hz 표시 경로와 24Hz 표시 경로를 모두 풀다운 없이 실행할 수 있습니다.
디스플레이가 앱의 프레임 속도의 배수로 실행되는 경우 앱은 불필요한 끊김을 방지하기 위해 각 프레임의 프레젠테이션 타임스탬프를 지정해야 합니다. 게임의 경우 Android Frame Pacing 라이브러리는 프레임 프레젠테이션 타임스탬프를 올바르게 설정하는 데 유용합니다.
setFrameRate()와 preferredDisplayModeId 비교
WindowManager.LayoutParams.preferredDisplayModeId
는 앱이 플랫폼에 프레임 속도를 표시할 수 있는 또 다른 방법입니다. 일부 앱은 디스플레이 해상도와 같은 다른 디스플레이 모드 설정을 변경하는 대신 디스플레이 새로고침 빈도만 변경하려고 합니다. 일반적으로 preferredDisplayModeId 대신 setFrameRate()를 사용합니다. setFrameRate() 함수는 앱이 특정 프레임 속도의 모드를 찾기 위해 디스플레이 모드 목록을 검색할 필요가 없으므로 사용하기 더 쉽습니다.
setFrameRate() 는 프레임 속도가 다른 여러 표시 경로가 실행되는 시나리오에서 플랫폼이 호환되는 프레임 속도를 선택할 수 있는 더 많은 기회를 제공합니다. 예를 들어 Pixel 4에서 두 개의 앱이 화면 분할 모드로 실행되는 시나리오를 생각해 보세요. 한 앱은 24Hz 동영상을 재생하고 다른 앱은 사용자에게 스크롤 가능한 목록을 표시합니다. Pixel 4는 60Hz와 90Hz의 두 가지 디스플레이 새로고침 빈도를 지원합니다. preferredDisplayModeId API를 사용하면 동영상 노출 영역이 60Hz 또는 90Hz를 선택해야 합니다. 24Hz로 setFrameRate()를 호출하면 동영상 노출 영역은 소스 동영상의 프레임 속도에 관한 더 많은 정보를 플랫폼에 제공하여 플랫폼이 이 시나리오에서 60Hz보다 더 나은 디스플레이 새로고침 빈도로 90Hz를 선택할 수 있도록 합니다.
그러나 다음과 같은 시나리오에서는 setFrameRate() 대신 preferredDisplayModeId를 사용해야 합니다.
- 앱이 해상도 또는 기타 디스플레이 모드 설정을 변경하려면
preferredDisplayModeId를 사용합니다. - 플랫폼은 모드 전환이 가볍고 사용자에게 눈에 띄지 않을 가능성이 낮은 경우에만
setFrameRate()호출에 응답하여 디스플레이 모드를 전환합니다. 앱이 무거운 모드 전환이 필요하더라도 (예: Android TV 기기) 디스플레이 새로고침 빈도를 전환하는 것을 선호하는 경우preferredDisplayModeId를 사용합니다. - 각 프레임에 프레젠테이션 타임스탬프를 설정해야 하는 앱의 프레임 속도의 배수로 실행되는 디스플레이를 처리할 수 없는 앱은
preferredDisplayModeId를 사용해야 합니다.
setFrameRate()와 preferredRefreshRate 비교
WindowManager.LayoutParams#preferredRefreshRate
는 앱의 창에 선호하는 프레임 속도를 설정하며 이 속도는 창 내의 모든 노출 영역에 적용됩니다. 앱은 스케줄러에 앱의 의도한 프레임 속도에 관한 더 나은 힌트를 제공하기 위해 setFrameRate()와 마찬가지로 기기의 지원되는 새로고침 빈도와 관계없이 선호하는 프레임 속도를 지정해야 합니다.
preferredRefreshRate는 setFrameRate()를 사용하는 노출 영역에 무시됩니다. 일반적으로 가능한 경우 setFrameRate()를 사용합니다.
preferredRefreshRate와 preferredDisplayModeId 비교
앱이 선호하는 새로고침 빈도만 변경하려는 경우 preferredDisplayModeId보다는 preferredRefreshRate를 사용하는 것이 좋습니다.
setFrameRate()를 너무 자주 호출하지 않기
setFrameRate() 호출은 성능 면에서 비용이 많이 들지 않지만 앱은 모든 프레임 또는 초당 여러 번 setFrameRate()를 호출하지 않아야 합니다. setFrameRate()를 호출하면 디스플레이 새로고침 빈도가 변경될 수 있으며 이로 인해 전환 중에 프레임이 삭제될 수 있습니다.
미리 올바른 프레임 속도를 파악하고 setFrameRate()를 한 번 호출해야 합니다.
게임 또는 동영상이 아닌 기타 앱의 사용
동영상은 setFrameRate() API의 기본 사용 사례이지만 다른 앱에도 사용할 수 있습니다. 예를 들어 60Hz보다 높게 실행하지 않으려는 게임 (전력 사용량을 줄이고 더 긴 재생 세션을 달성하기 위해)은 Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)를 호출할 수 있습니다. 이렇게 하면 기본적으로 90Hz로 실행되는 기기가 게임이 활성 상태인 동안 60Hz로 실행되므로 디스플레이가 90Hz로 실행되는 동안 게임이 60Hz로 실행될 때 발생하는 끊김을 방지할 수 있습니다.
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE 사용
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE는 동영상 앱 전용입니다. 동영상이 아닌 용도로는 FRAME_RATE_COMPATIBILITY_DEFAULT를 사용합니다.
프레임 속도 변경 전략 선택
- 앱은 영화와 같은 장시간 실행되는 동영상을 표시할 때
setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)를 호출하는 것이 좋습니다. 여기서 fps는 동영상의 프레임 속도입니다. - 동영상 재생이 몇 분 이하로 지속될 것으로 예상되는 경우 앱이
CHANGE_FRAME_RATE_ALWAYS로setFrameRate()를 호출하지 않는 것이 좋습니다.
동영상 재생 앱의 통합 예
동영상 재생 앱에서 새로고침 빈도 전환을 통합하려면 다음 단계를 따르는 것이 좋습니다.
changeFrameRateStrategy를 결정합니다.- 영화와 같은 장시간 실행되는 동영상을 재생하는 경우
MATCH_CONTENT_FRAMERATE_ALWAYS를 사용합니다. - 영화 예고편과 같은 짧은 형식 동영상을 재생하는 경우
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS를 사용합니다.
- 영화와 같은 장시간 실행되는 동영상을 재생하는 경우
changeFrameRateStrategy가CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS인 경우 4단계로 이동합니다.- 다음 두 가지 사실이 모두 참인지 확인하여 원활하지 않은 새로고침 빈도 전환이 발생하려고 하는지 감지합니다.
- 현재 새로고침 빈도 (C라고 함)에서 동영상의 프레임 속도 (V라고 함)로 원활한 모드 전환이 불가능합니다. C와 V가 다르고
Display.getMode().getAlternativeRefreshRates에 V의 배수가 포함되어 있지 않은 경우에 해당합니다. - 사용자가 원활하지 않은 새로고침 빈도 변경을 선택했습니다.
이를 감지할 수 있습니다.
DisplayManager.getMatchContentFrameRateUserPreference가MATCH_CONTENT_FRAMERATE_ALWAYS를 반환하는지 확인하여
- 현재 새로고침 빈도 (C라고 함)에서 동영상의 프레임 속도 (V라고 함)로 원활한 모드 전환이 불가능합니다. C와 V가 다르고
- 전환이 원활하게 진행되는 경우 다음 단계를 따르세요.
setFrameRate를 호출하고fps,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 및changeFrameRateStrategy를 전달합니다. 여기서fps는 동영상의 프레임 속도입니다.- 동영상 재생을 시작합니다.
- 원활하지 않은 모드 변경이 발생하려고 하는 경우 다음 단계를 따르세요.
- 사용자에게 알리는 UX를 표시합니다. 사용자가 이 UX를 닫고 5.d단계의 추가 지연을 건너뛸 수 있는 방법을 구현하는 것이 좋습니다. 이는 권장 지연 시간이 전환 시간이 더 빠른 디스플레이에서 필요한 시간보다 길기 때문입니다.
setFrameRate를 호출하고fps,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 및CHANGE_FRAME_RATE_ALWAYS, 를 전달합니다. 여기서fps는 동영상의 프레임 속도입니다.onDisplayChanged콜백을 기다립니다.- 모드 전환이 완료될 때까지 2초 동안 기다립니다.
- 동영상 재생을 시작합니다.
원활한 전환만 지원하는 의사 코드는 다음과 같습니다.
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
위에서 설명한 대로 원활한 전환과 원활하지 않은 전환을 지원하는 의사 코드는 다음과 같습니다.
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();
}