SmartTube Auto Frame Rate (AFR) Implementation: Architecture and Source Code Analysis
SmartTube implements Auto Frame Rate (AFR) through a coordinated pipeline that detects video frame rates from ExoPlayer, validates device capabilities, and delegates display mode switching to a helper subsystem that optionally pauses playback during the transition.
SmartTube's Auto Frame Rate feature automatically matches your TV's refresh rate to the native frame rate of the video being played, eliminating judder on 24fps film content and variable frame rate videos. The implementation resides in the yuliskov/SmartTube repository and consists of a controller layer that manages UI preferences and state machines, coupled with a helper layer that executes low-level display operations. This architecture bridges the gap between ExoPlayer's format detection and Android's display subsystem APIs.
How Auto Frame Rate Detection Works
When a video loads in the SmartTube player, the AFR system initiates a multi-stage detection and application process.
Video Loading Detection
The process begins in AutoFrameRateController.java at onVideoLoaded(), which captures the current playback state and schedules a delayed AFR application via applyAfrDelayed(). This delay ensures the video format metadata is fully available before attempting a refresh rate switch.
// From AutoFrameRateController.java
public void onVideoLoaded() {
savePlaybackState();
applyAfrDelayed();
}
The controller obtains the current FormatItem from ExoPlayer and passes it to the helper system only if the user has enabled AFR and not elected to skip the current content type (such as Shorts).
User-Configurable Settings
The addUiOptions() method in AutoFrameRateController.java constructs the settings UI with three distinct categories:
- Core Toggles: Enable AFR, resolution switching, FPS correction, double refresh rate, skip 24Hz modes, and skip Shorts
- Pause Configuration: Radio list defining how long to pause playback during mode changes (stored in milliseconds)
- Supported Modes: Read-only display of the TV's available refresh rates
These preferences persist in PlayerData.java, which stores values for afrEnabled, afrResSwitchEnabled, afrPauseMs, and other configuration flags.
Core AFR Components
The implementation separates concerns between orchestration logic and hardware abstraction.
AutoFrameRateController
Located at common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/AutoFrameRateController.java, this class serves as the primary coordinator. It implements AutoFrameRateListener to receive callbacks from the helper layer regarding switch status (start, error, or cancel).
Key responsibilities include:
- Guarding AFR application with
skipAfr()checks based on user preferences - Scheduling playback pauses via
Utils.postDelayed(mPlaybackResumeHandler, delayMs)using the duration specified ingetPlayerData().getAfrPauseMs() - Restoring original display states through
restoreOriginalState(), which clears pending sync data viaModeSyncManagerwhen AFR is disabled or the activity stops
AutoFrameRateHelper
The AutoFrameRateHelper.java file wraps the low-level DisplaySyncHelper and implements the actual display mode logic. It validates device capabilities through supportsDisplayModeChangeComplex() and throttles rapid calls to prevent system instability.
The helper performs FPS correction to account for broadcast standards versus exact frame rates:
- 24 fps becomes 23.97 fps
- 30 fps becomes 29.97 fps
- 60 fps becomes 59.94 fps
When apply() is invoked with a FormatItem, the helper calls syncMode(), which forwards to DisplaySyncHelper.syncDisplayMode() using the Activity's window object to request the new display mode (width + refresh rate combination).
Display Mode Switching Flow
The actual refresh rate change involves coordination between internal helpers and an external service.
Mode Synchronization
The syncMode() method in AutoFrameRateHelper.java handles the atomic display switch operation. It accepts the target activity and format, validates that the device supports the requested mode, and applies the change through the Android window manager.
// Conceptual flow from AutoFrameRateHelper.java
public void apply(Activity activity, FormatItem format, boolean force) {
if (!force && skipAfr()) return;
if (!supportsDisplayModeChangeComplex()) return;
// Throttle check omitted for brevity
syncMode(activity, format.getWidth(), format.getFrameRate());
}
External Service Integration
After the helper applies the mode, AutoFrameRateController invokes TvQuickActions.sendStartAFR() to notify the external tv-quick-actions daemon. This service, implemented in TvQuickActions.java, handles the actual display engine communication on certain TV platforms. When AFR deactivates, sendStopAFR() restores normal operation.
Playback Pause Handling
During the mode switch, the controller optionally pauses playback to prevent audio/video desync or corruption. The pause duration is configurable (typically 0-5000ms) and executes on the main thread handler:
// Implementation detail from AutoFrameRateController
private void maybePausePlayback() {
long delayMs = getPlayerData().getAfrPauseMs();
if (delayMs > 0) {
Utils.postDelayed(mPlaybackResumeHandler, delayMs);
}
}
SmartTube AFR Implementation Examples
Enabling AFR Programmatically
Configure Auto Frame Rate settings through the PlayerData singleton:
// Obtain the central PlayerData instance
PlayerData player = PlayerData.instance(context);
// Enable core AFR functionality
player.setAfrEnabled(true);
player.setAfrResSwitchEnabled(true);
player.setAfrFpsCorrectionEnabled(true);
// Configure 2-second pause during mode switches
player.setAfrPauseMs(2000);
Manual AFR Application
Force an immediate frame rate check for a specific video format:
// Get format from ExoPlayer
FormatItem fmt = player.getVideoFormat();
// Apply with user settings respected (force = false)
AutoFrameRateHelper.instance(context).apply(activity, fmt, false);
// Apply ignoring throttle (useful for testing)
AutoFrameRateHelper.instance(context).apply(activity, fmt, true);
Restoring Original Display State
Revert to the system's default refresh rate when exiting playback or disabling AFR:
// Restore original state and clear sync data
AutoFrameRateHelper.instance(context).restoreOriginalState(activity);
Summary
- AutoFrameRateController manages the AFR lifecycle, UI options, and playback pausing in
common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/AutoFrameRateController.java - AutoFrameRateHelper executes display mode switches and FPS corrections in
common/src/main/java/com/liskovsoft/smartyoutubetv2/common/autoframerate/AutoFrameRateHelper.java - The system detects frame rates from ExoPlayer's
FormatItemand converts broadcast rates (24/30/60) to exact refresh rates (23.97/29.97/59.94) - TvQuickActions communicates with external TV services to finalize display changes
- User preferences control enablement, resolution switching, correction algorithms, and pause durations during transitions
- State restoration via
restoreOriginalState()ensures the TV returns to default settings when AFR deactivates
Frequently Asked Questions
How does SmartTube detect the video frame rate for AFR?
SmartTube obtains the frame rate from ExoPlayer's FormatItem object when onVideoLoaded() fires in AutoFrameRateController.java. The controller extracts the current video format and passes its width and frame rate properties to AutoFrameRateHelper.apply(), which then validates whether the TV supports a matching display mode before requesting the switch.
What is the difference between AutoFrameRateController and AutoFrameRateHelper?
AutoFrameRateController is a playback controller that manages high-level decisions: checking user preferences via skipAfr(), building the settings UI through addUiOptions(), handling playback pauses, and restoring original states. AutoFrameRateHelper is a utility class that performs the actual hardware interaction: throttling rapid calls, correcting FPS values (24→23.97), and calling DisplaySyncHelper.syncDisplayMode() to change the Android window's display mode.
Why does video pause when Auto Frame Rate activates?
The pause prevents audio-video desync during the HDMI handshake and mode switch. When applyAfr() determines a mode change is necessary, it schedules a delayed resume using Utils.postDelayed() with the duration specified in PlayerData.getAfrPauseMs(). Users can configure this duration (including zero delay) in the AFR settings dialog under "Pause-During-Switch" options.
How do I disable AFR for specific content like YouTube Shorts?
The skipAfr() method in AutoFrameRateController.java contains logic to bypass frame rate switching based on content type and user preferences. Enable the "Skip Shorts" toggle in the AFR settings UI, which stores the preference in PlayerData and causes the controller to return early during applyAfr() when it detects Shorts content, thereby preserving the TV's current refresh rate.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →