Android应用开发(36)帧率API测试基于Surfaceview

Android应用开发学习笔记——目录索引

 参考android官网:

  1. Frame rate  |  Android media  |  Android Developers
  2. 多重刷新率  |  Android 开源项目  |  Android Open Source Project
  3. WindowManager.LayoutParams  |  Android Developers

Android 11 增加了对具有多种刷新率的设备的支持,目前市面上旗舰手机基本都是普及了LTPO屏幕,本章对帧率相关代码进行分析,然后写一个测试程序验证(基于Android应用开发(35)SufaceView基本用法)。

一、APP调用的API接口

1、APP获取屏幕支持的所有帧率

应用程序获取设备实际支持的显示刷新率,可以通过调用 Display.getSupportedModes()获取,Mode.mRefreshRate就是帧率信息,以便安全调用setFrameRate()

// Display.java
Display.Mode[] mSupportedModes = getWindowManager().getDefaultDisplay().getSupportedModes();
for (Display.Mode mode : mSupportedModes) {
   Log.d(TAG, "getSupportedModes: " + mode.toString());
   Log.d(TAG, "getRefreshRate: " + mode.getRefreshRate());
}
 

public static final class Mode implements Parcelable {
        public static final Mode[] EMPTY_ARRAY = new Mode[0];
 
        private final int mModeId;
        private final int mWidth;
        private final int mHeight;
        private final float mRefreshRate;
        @NonNull
        private final float[] mAlternativeRefreshRates;
...
}

2、APP设置帧率的API

setFrameRate( )

Android 公开了多种访问和控制界面的方法,因此有多个版本的setFrameRate()API。每个版本的 API 都采用相同的参数,并且与其他版本的工作方式相同:

要查看调用 是否会导致setFrameRate()显示刷新率发生更改,请通过调用 DisplayManager.registerDisplayListener() 或 来注册显示更改通知AChoreographer_registerRefreshRateCallback()。 

调用时setFrameRate(),最好传递精确的帧速率,而不是四舍五入为整数。例如,当渲染以 29.97Hz 录制的视频时,请传入 29.97,而不是四舍五入到 30。

对于视频应用程序,传递给的兼容性参数setFrameRate()应设置为Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,以向 Android 平台提供额外提示,即该应用程序将使用pulldown来适应不匹配的显示刷新率(这将导致抖动)。

surface.setFrameRate(contentFrameRate,
    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
    CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);

// 调用setFrameRate()时,最好传入准确的帧速率,而不是四舍五入为整数。例如,在渲染以 29.97Hz 录制的视频时,传入 29.97 而不是四舍五入为 30。
参数:Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE仅适用于视频应用程序。对于非视频用途,请使用FRAME_RATE_COMPATIBILITY_DEFAULT.
选择改变帧速率的策略:
google强烈建议应用在显示电影等长时间运行的视频时调用setFrameRate(fps , FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) ,其中 fps 是视频的帧速率。
当您预计视频播放持续几分钟或更短时间时,强烈建议您不要调用setFrameRate()带参数CHANGE_FRAME_RATE_ALWAYS。

SurfaceControl.Transaction.setFrameRate()
参数和surface.setFrameRate是一样的

setFrameRate( ) 与 PreferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId 是应用程序向平台指示其帧速率的另一种方式。某些应用程序只想更改显示刷新率,而不是更改其他显示模式设置,例如显示分辨率。一般情况下,使用 setFrameRate()代替preferredDisplayModeId. 该setFrameRate() 功能更易于使用,因为应用程序不需要搜索显示模式列表来查找具有特定帧速率的模式。

setFrameRate()在存在多个以不同帧速率运行的表面的情况下,使平台有更多机会选择兼容的帧速率。例如,考虑这样一种场景:两个应用程序在 Pixel 4 上以分屏模式运行,其中一个应用程序正在播放 24Hz 视频,另一个应用程序向用户显示可滚动列表。设备支持两种显示刷新率:60Hz 和 90Hz。使用preferredDisplayModeIdAPI,视频表面被迫选择 60Hz 或 90Hz。通过使用 setFrameRate()24Hz 调用,视频表面为平台提供了有关源视频帧速率的更多信息,使平台能够选择 90Hz 的显示刷新率,在此场景中这比 60Hz 更好。

然而,有些场景preferredDisplayModeId应该使用 来代替setFrameRate(),例如:

  • 如果应用程序想要更改分辨率或其他显示模式设置,请使用preferredDisplayModeId
  • setFrameRate()如果模式切换是轻量级的并且不太可能被用户注意到,则平台只会响应调用来切换显示模式 。如果应用程序更喜欢切换显示刷新率,即使它需要大量模式切换(例如,在 Android TV 设备上),请使用preferredDisplayModeId.
  • 无法处理以应用程序帧速率倍数运行的显示的应用程序(这需要在每个帧上设置演示时间戳)应使用preferredDisplayModeId.

Display.Mode[] mSupportedModes;
mSupportedModes = getWindowManager().getDefaultDisplay().getSupportedModes();


WindowManager.LayoutParams params = getWindow().getAttributes();
params.preferredDisplayModeId = mSupportedModes[x].getModeId();
getWindow().setAttributes(params);

Log.d(TAG, "RefreshRate:" + mSupportedModes[x].getRefreshRate());

setFrameRate( ) 与 PreferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate 在应用程序窗口上设置首选帧速率,并且该速率适用于窗口内的所有表面。无论设备支持的刷新率如何,应用程序都应指定其首选帧速率,类似于 setFrameRate(),以便为调度程序更好地提示应用程序的预期帧速率。

preferredRefreshRate对于使用 的表面将被忽略setFrameRate()。如果可能的话一般使用setFrameRate()

PreferredRefreshRate 与 PreferredDisplayModeId

如果应用程序只想更改首选刷新率,则最好使用 preferredRefreshRate,而不是preferredDisplayModeId

WindowManager.LayoutParams params = getWindow().getAttributes();
params.PreferredRefreshRate = preferredRefreshRate;

getWindow().setAttributes(params);

Log.d(TAG, "preferredRefreshRate:" + preferredRefreshRate);

避免过于频繁地调用setFrameRate( )

尽管该setFrameRate()调用在性能方面的成本并不高,但应用程序应避免setFrameRate()每帧调用或每秒调用多次。调用setFrameRate()可能会导致显示刷新率发生变化,从而可能导致转换期间出现帧丢失。您应该提前计算出正确的帧速率并调用 setFrameRate()一次。

用于游戏或其他非视频应用程序

尽管视频是该 API 的主要用例setFrameRate(),但它也可用于其他应用程序。例如,打算不高于 60Hz 运行的游戏(以减少功耗并实现更长的游戏时间)可以调用 Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT). 这样,默认情况下以 90Hz 运行的设备将在游戏处于活动状态时以 60Hz 运行,这将避免游戏以 60Hz 运行而显示器以 90Hz 运行时出现的抖动。

二、代码分析

1、surface.setFrameRate()

// Surface.java (frameworks\base\core\java\android\view)
    /**
     * Sets the intended frame rate for this surface.
     *
     * <p>On devices that are capable of running the display at different refresh rates,
     * the system may choose a display refresh rate to better match this surface's frame
     * rate. Usage of this API won't introduce frame rate throttling, or affect other
     * aspects of the application's frame production pipeline. However, because the system
     * may change the display refresh rate, calls to this function may result in changes
     * to Choreographer callback timings, and changes to the time interval at which the
     * system releases buffers back to the application.</p>
     *
     * <p>Note that this only has an effect for surfaces presented on the display. If this
     * surface is consumed by something other than the system compositor, e.g. a media
     * codec, this call has no effect.</p>
     *
     * @param frameRate The intended frame rate of this surface, in frames per second. 0
     * is a special value that indicates the app will accept the system's choice for the
     * display frame rate, which is the default behavior if this function isn't
     * called. The <code>frameRate</code> parameter does <em>not</em> need to be a valid refresh
     * rate for this device's display - e.g., it's fine to pass 30fps to a device that can only run
     * the display at 60fps.
     *
     * @param compatibility The frame rate compatibility of this surface. The
     * compatibility value may influence the system's choice of display frame rate.
     * This parameter is ignored when <code>frameRate</code> is 0.
     *
     * @param changeFrameRateStrategy Whether display refresh rate transitions caused by this
     * surface should be seamless. A seamless transition is one that doesn't have any visual
     * interruptions, such as a black screen for a second or two. This parameter is ignored when
     * <code>frameRate</code> is 0.
     *
     * @throws IllegalArgumentException If <code>frameRate</code>, <code>compatibility</code> or
     * <code>changeFrameRateStrategy</code> are invalid.
     */
    public void setFrameRate(@FloatRange(from = 0.0) float frameRate,
            @FrameRateCompatibility int compatibility,
            @ChangeFrameRateStrategy int changeFrameRateStrategy) {
        synchronized (mLock) {
            checkNotReleasedLocked();
            int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility,
                    changeFrameRateStrategy);
            if (error == -EINVAL) {
                throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()");
            } else if (error != 0) {
                throw new RuntimeException("Failed to set frame rate on Surface");
            }
        }
    }

    /**
     * Sets the intended frame rate for this surface. Any switching of refresh rates is
     * most probably going to be seamless.
     *
     * @see #setFrameRate(float, int, int)
     */
    public void setFrameRate(
            @FloatRange(from = 0.0) float frameRate, @FrameRateCompatibility int compatibility) {
        setFrameRate(frameRate, compatibility, CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
    }
// android_view_Surface.cpp (frameworks\base\core\jni)
static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate,
                               jint compatibility, jint changeFrameRateStrategy) {
    Surface* surface = reinterpret_cast<Surface*>(nativeObject);
    ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
    // Our compatibility is a Surface.FRAME_RATE_COMPATIBILITY_* value, and
    // NATIVE_WINDOW_SET_FRAME_RATE takes an
    // ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value. The values are identical
    // though, so no need to explicitly convert.
    return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, double(frameRate), compatibility,
                        int(changeFrameRateStrategy));
}
// Surface.cpp (frameworks\native\libs\gui)
int Surface::perform(int operation, va_list args)
{
    case NATIVE_WINDOW_SET_FRAME_RATE:
        res = dispatchSetFrameRate(args);
        break;
 
int Surface::dispatchSetFrameRate(va_list args) {
    float frameRate = static_cast<float>(va_arg(args, double));
    int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
    int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int));
    return setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
 
status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
                               int8_t changeFrameRateStrategy) {
    ATRACE_CALL();
    ALOGV("Surface::setFrameRate");
 
    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
                           "Surface::setFrameRate")) {
        return BAD_VALUE;
    }
 
    return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
                                           changeFrameRateStrategy);
}
// ISurfaceComposer.cpp (frameworks\native\libs\gui)
    status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                          int8_t compatibility, int8_t changeFrameRateStrategy) override {
 ..
        status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
    }
status_t BnSurfaceComposer::onTransact(
...setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy);
 
// SurfaceFlinger.cpp (miui\frameworks\native\services\surfaceflinger)
status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
                                      int8_t compatibility, int8_t changeFrameRateStrategy)
    layer->setFrameRate(Layer::FrameRate(Fps{frameRate}, Layer::FrameRate::convertCompatibility(compatibility), strategy))

1、surfaceflinger layerinfo保存app传递下来的结构体是FrameRate
    struct FrameRate {
        using Seamlessness = scheduler::Seamlessness;
        Fps rate;
        FrameRateCompatibility type;
        Seamlessness seamlessness;
}
 
enum class FrameRateCompatibility {
    Default, // 图层没有指定任何具体的处理策略
    Exact, // 图层需要准确的帧速率。如视频类
    ExactOrMultiple, // 图层需要精确的帧速率(或它的倍数)来呈现内容。 任何其他值都将导致下拉。
    NoVote, // 图层对刷新率没有任何要求,不考虑显示刷新率
};
 
// The seamlessness requirement of a Layer.
enum class Seamlessness {
    // Indicates a requirement for a seamless mode switch.
    OnlySeamless,
    // Indicates that both seamless and seamed mode switches are allowed.
    SeamedAndSeamless,
    // Indicates no preference for seamlessness. For such layers the system will
    // prefer seamless switches, but also non-seamless switches to the group of the
    // default config are allowed.
    Default
};

2、SurfaceControl.Transaction.setFrameRate()

// SurfaceControl.java (frameworks\base\core\java\android\view)
        /**
         * Sets the intended frame rate for this surface. Any switching of refresh rates is
         * most probably going to be seamless.
         *
         * @see #setFrameRate(SurfaceControl, float, int, int)
         */
        @NonNull
        public Transaction setFrameRate(@NonNull SurfaceControl sc,
                @FloatRange(from = 0.0) float frameRate,
                @Surface.FrameRateCompatibility int compatibility) {
            return setFrameRate(sc, frameRate, compatibility,
                    Surface.CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
        }
// android_view_SurfaceControl.cpp (frameworks\base\core\jni)
static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
                               jfloat frameRate, jint compatibility, jint changeFrameRateStrategy) {
    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
    const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
    // Our compatibility is a Surface.FRAME_RATE_COMPATIBILITY_* value, and
    // Transaction::setFrameRate() takes an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value. The
    // values are identical though, so no need to convert anything.
    transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility),
                              static_cast<int8_t>(changeFrameRateStrategy));
}
// SurfaceComposerClient.cpp (frameworks\native\libs\gui)
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
        const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
        int8_t changeFrameRateStrategy) {
    layer_state_t* s = getLayerState(sc);
    if (!s) {
        mStatus = BAD_INDEX;
        return *this;
    }
    // Allow privileged values as well here, those will be ignored by SF if
    // the caller is not privileged
    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
                           "Transaction::setFrameRate",
                           /*privileged=*/true)) {
        mStatus = BAD_VALUE;
        return *this;
    }
    s->what |= layer_state_t::eFrameRateChanged;
    s->frameRate = frameRate;
    s->frameRateCompatibility = compatibility;
    s->changeFrameRateStrategy = changeFrameRateStrategy;
    return *this;
}
// SurfaceFlinger.cpp (frameworks\native\services\surfaceflinger)
applyTransactionState
    setClientStateLocked
 
uint32_t SurfaceFlinger::setClientStateLocked(
        const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
        int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
...
    if (what & layer_state_t::eFrameRateChanged) {
        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
                              "SurfaceFlinger::setClientStateLocked", privileged)) {
            const auto compatibility =
                    Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
            const auto strategy =
                    Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
 
            if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
                flags |= eTraversalNeeded;
            }
        }
    }

3、preferredDisplayModeId

// RefreshRatePolicy.java (mframeworks\base\services\core\java\com\android\server\wm)
    int getPreferredModeId(WindowState w) {
        // If app is animating, it's not able to control refresh rate because we want the animation
        // to run in default refresh rate.
        if (w.isAnimating(TRANSITION | PARENTS)) {
            return 0;
        }
 
        return w.mAttrs.preferredDisplayModeId;
    }
// DisplayContent.java (frameworks\base\services\core\java\com\android\server\wm)
会保存到   
private static final class ApplySurfaceChangesTransactionState 的preferredModeId
 
    void applySurfaceChangesTransaction() {
        if (!mWmService.mDisplayFrozen) {
            mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                    mLastHasContent,
                    mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
                    mTmpApplySurfaceChangesTransactionState.preferredModeId,
                    mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate,
                    mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate,
                    mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
                    true /* inTraversal, must call performTraversalInTrans... below */);
        }
// DisplayManagerService.java (frameworks\base\services\core\java\com\android\server\display)
        public void setDisplayProperties(int displayId, boolean hasContent,
                float requestedRefreshRate, int requestedMode, float requestedMinRefreshRate,
                float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing,
                boolean inTraversal) {
            setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
                    requestedMode, requestedMinRefreshRate, requestedMaxRefreshRate,
                    requestedMinimalPostProcessing, inTraversal);
        }
setDisplayPropertiesInternal
|--> mDisplayModeDirector.getAppRequestObserver().setAppRequest(
                    displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
 
// DisplayModeDirector.java (frameworks\base\services\core\java\com\android\server\display)
        public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange,
                float requestedMaxRefreshRateRange) {
            synchronized (mLock) {
                setAppRequestedModeLocked(displayId, modeId);
                setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
                        requestedMaxRefreshRateRange);
            }
        }
 
setAppRequest 最后通过:
updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, vote);
还就是最后调用surfaceflinger的:
setDesiredDisplayModeSpecs // surface的标准API接口

三、写一个APP验证API

1、准备工作:surfaceflinger添加log验证

1、surface.setFrameRate() 和 SurfaceControl.Transaction.setFrameRate()
这两个api最后是调用到layer.cpp的bool Layer::setFrameRate(FrameRate frameRate),所以我们在这个函数加log
bool Layer::setFrameRate(FrameRate frameRate) {
    if (!mFlinger->useFrameRateApi) {
        return false;
    }
    if (mDrawingState.frameRate == frameRate) {
        return false;
    }
 
    mDrawingState.sequence++;
    mDrawingState.frameRate = frameRate;
    mDrawingState.modified = true;
    // 添加的验证log
    ALOGE("[lzl-test]frameRate = {%d, %s, %s}", frameRate.type, to_string(frameRate.rate).c_str(), toString(frameRate.seamlessness).c_str());
 
    updateTreeHasFrameRateVote();
 
    setTransactionFlags(eTransactionNeeded);
    return true;
}
 
2、WindowManager.LayoutParams preferredDisplayModeId
这个api最后调用到setDesiredDisplayModeSpecs,所以我们在这个函数加log
status_t SurfaceFlinger::setDesiredDisplayModeSpecs(
        const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
        float appRequestRefreshRateMax) {
    ATRACE_CALL();
 
...
// 下面是添加的验证log
    ALOGD("[lzl-test]setDesiredDisplayConfigSpecs:defaultMode %d primaryRefreshRateMin: %f "
      "primaryRefreshRateMax: %f "
      "appRequestRefreshRateMin:%f appRequestRefreshRateMax:%f",
      defaultMode, primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
      appRequestRefreshRateMax);

参考cts:​cts/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java

运行界面介绍

adb install -t  app-debug.apk

1区:显示从系统获取的帧率

2区:使用surface.setFrameRate() 测试,选择帧率,然后点击确认,

//check surfaceflinger log

04-14 16:18:02.854 13261 13261 D [lzl-test][MainActivity]: Button: setFrameRate fps:60
04-14 16:18:02.866 888 888 E Layer : [lzl-test]frameRate = {2, 60.00fps, OnlySeamless}

3区:使用preferredDisplayModeId 测试,选择帧率,然后点击确认

//check surfaceflinger log

04-14 16:17:24.792 13261 13261 D [lzl-test][MainActivity]: Spinner: onItemSelected...

04-14 16:17:24.792 13261 13261 D [lzl-test][MainActivity]: Spinner: preferredDisplayModeId fps:90

04-14 16:17:25.364 13261 13261 D [lzl-test][MainActivity]: Button: preferredDisplayModeId fps:90.0

04-14 16:17:25.379 888 888 E Layer : [lzl-test]frameRate = {190.00fps, SeamedAndSeamless}

04-14 16:17:25.415 888 1019 D SurfaceFlinger: [lzl-test]setDesiredDisplayConfigSpecs:defaultMode 3 primaryRefreshRateMin: 0.000000 primaryRefreshRateMax: 144.000000 appRequestRefreshRateMin:0.000000 appRequestRefreshRateMax:144.000000

04-14 16:17:25.415 888 888 I SurfaceFlinger: [lzl-test]Setting desired display mode specs: default mode ID: 3, allowGroupSwitching = 0, primary range: [0.00fps 144.00fps], app request range: [0.00fps 144.00fps]

04-14 16:17:25.418 13261 13261 D [lzl-test][MainActivity]: onDisplayChanged...

04-14 16:17:25.420 13261 13261 D [lzl-test][MainActivity]: getRefreshRate: 90.0

04-14 16:17:25.420 13261 13261 D [lzl-test][MainActivity]: getRealMetrics: DisplayMetrics{density=2.75, width=1080, height=2460, scaledDensity=2.75, xdpi=407.051, ydpi=407.051}

04-14 16:17:25.438 13261 13261 D [lzl-test][MainActivity]: Spinner: onItemSelected...

04-14 16:17:25.438 13261 13261 D [lzl-test][MainActivity]: Spinner: System fps:90

四、测试程序

完整源码

百度网盘链接:百度网盘 请输入提取码 提取码:test

FrameRateApiTest目录

点此查看Android应用开发学习笔记的完整目录

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在使用 CameraX API 进行相机开发时,可以使用 PreviewConfig 类来设置帧率。以下是设置帧率的示例代码: ``` val previewConfig = PreviewConfig.Builder().apply { setTargetFps(30) // 设置帧率为 30 帧每秒 }.build() val preview = Preview(previewConfig) ``` 在上面的代码中,我们使用 PreviewConfig.Builder() 创建了一个预览配置对象,并使用 setTargetFps() 方法设置了帧率为 30 帧每秒。然后,我们将预览配置对象传递给 Preview 构造函数来创建预览对象。 注意,实际帧率可能会受到硬件限制和其他因素的影响,因此设置的帧率可能不是最终实际帧率。 ### 回答2: CameraX是一个用于开发Android相机应用程序的Jetpack库。要设置CameraX的帧率,可以通过修改Preview配置来实现。 首先,需要创建一个Preview用例的实例: val preview = Preview.Builder().build() 然后,可以通过设置帧率参数来修改帧率: val frameRate = Rational(30, 1) // 设置为30fps preview.setTargetFps(frameRate) 最后,将Preview用例添加到CameraX的配置中: val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build() CameraX.bindToLifecycle(this, cameraSelector, preview) 这样就成功设置了CameraX的帧率为30fps。需要注意的是,实际的帧率可能会受到设备硬件和性能的限制,不能保证始终准确达到设置的帧率。可以根据需要调整帧率值,但应在设备支持的范围内设置合理的值以确保最佳性能和用户体验。 总结起来,要设置CameraX的帧率,需要创建Preview用例的实例并设置帧率参数,然后将Preview用例添加到CameraX的配置中。 ### 回答3: Camerax是一个相机库,用于在Android设备上以编程方式控制相机的功能。要设置相机的帧率,可以使用Camerax库中提供的一些方法和参数。 首先,我们需要创建一个Preview配置对象,并指定所需的帧率。可以使用`Builder`类来创建一个新的配置对象,然后使用`setTargetFps()`方法设置帧率。例如,如果我们希望设置帧率为30fps,可以这样做: ```java PreviewConfig config = new PreviewConfig.Builder() .setTargetFps(30) .build(); ``` 接下来,我们需要使用这个配置对象创建一个Preview实例。我们可以使用Camerax库中的`Preview`类来实现这一点。创建Preview实例的代码如下: ```java Preview preview = new Preview(config); ``` 然后,我们需要将Preview实例与相机绑定。我们可以使用Camerax库中的`CameraSelector`类来选择要绑定的相机。以下是将Preview实例与后置相机绑定的代码示例: ```java CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; CameraX.bindToLifecycle(this, cameraSelector, preview); ``` 最后,我们需要启动相机预览。我们可以使用Preview实例的`setSurfaceProvider()`方法将预览输出连接到界面上的SurfaceView或TextureView。以下是一个示例代码: ```java preview.setSurfaceProvider(surfaceView.getSurfaceProvider()); ``` 通过这些步骤,我们就可以使用Camerax库设置相机的帧率。可以根据需要调整帧率的值,并使用其他Camerax库中提供的配置选项来进一步定制相机的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liuzl_2010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值