[libcamera-devel,13/16] android: capabilties: Fix ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
diff mbox series

Message ID 20210827120757.110615-14-jacopo@jmondi.org
State Superseded
Headers show
Series
  • IPU3 control info update and HAL frame durations
Related show

Commit Message

Jacopo Mondi Aug. 27, 2021, 12:07 p.m. UTC
As reported by the CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES documentation
in the Android developer reference:

"For devices advertising any color filter arrangement other than NIR, or
devices not advertising color filter arrangement, this list will always
include (min, max) and (max, max) where min <= 15 and max = the maximum
output frame rate of the maximum YUV_420_888 output size."

Collect the higher FPS of the larger YUV stream and use 15 FPS as the
minimum value, if the camera can go slower than that. Populate the
ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES static metadata with the
newly computed values.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 src/android/camera_capabilities.cpp | 75 ++++++++++-------------------
 1 file changed, 26 insertions(+), 49 deletions(-)

Comments

Paul Elder Sept. 1, 2021, 9:31 a.m. UTC | #1
Hi Jacopo,

On Fri, Aug 27, 2021 at 02:07:54PM +0200, Jacopo Mondi wrote:
> As reported by the CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES documentation
> in the Android developer reference:
> 
> "For devices advertising any color filter arrangement other than NIR, or
> devices not advertising color filter arrangement, this list will always
> include (min, max) and (max, max) where min <= 15 and max = the maximum
> output frame rate of the maximum YUV_420_888 output size."
> 
> Collect the higher FPS of the larger YUV stream and use 15 FPS as the
> minimum value, if the camera can go slower than that. Populate the

If min is allowed to be less than 15, then why do you cap the minimum at
15?


Paul

> ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES static metadata with the
> newly computed values.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
>  src/android/camera_capabilities.cpp | 75 ++++++++++-------------------
>  1 file changed, 26 insertions(+), 49 deletions(-)
> 
> diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> index 8bde06e824ef..01cdbcf1395d 100644
> --- a/src/android/camera_capabilities.cpp
> +++ b/src/android/camera_capabilities.cpp
> @@ -862,55 +862,6 @@ int CameraCapabilities::initializeStaticMetadata()
>  	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
>  				  aeAvailableModes);
>  
> -	/* Initialize the AE frame duration limits. */
> -	int64_t minFrameDurationNsec = -1;
> -	int64_t maxFrameDurationNsec = -1;
> -	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> -	minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> -	maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> -
> -	/*
> -	 * Adjust the minimum frame duration to comply with Android
> -	 * requirements. The camera service mandates all preview/record
> -	 * streams to have a minimum frame duration < 33,366 milliseconds
> -	 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> -	 * implementation).
> -	 *
> -	 * If we're close enough (+ 500 useconds) to that value, round
> -	 * the minimum frame duration of the camera to an accepted
> -	 * value.
> -	 */
> -	static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> -	if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> -	    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> -		minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> -
> -	/*
> -	 * The AE routine frame rate limits are computed using the frame
> -	 * duration limits, as libcamera clips the AE routine to the
> -	 * frame durations.
> -	 */
> -	int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> -	int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> -	minFps = std::max(1, minFps);
> -
> -	/*
> -	 * Force rounding errors so that we have the proper frame
> -	 * durations for when we reuse these variables later
> -	 */
> -	minFrameDurationNsec = 1e9 / maxFps;
> -	maxFrameDurationNsec = 1e9 / minFps;
> -
> -	/*
> -	 * Register to the camera service {min, max} and {max, max}
> -	 * intervals as requested by the metadata documentation.
> -	 */
> -	int32_t availableAeFpsTarget[] = {
> -		minFps, maxFps, maxFps, maxFps
> -	};
> -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> -				  availableAeFpsTarget);
> -
>  	std::vector<int32_t> aeCompensationRange = {
>  		0, 0,
>  	};
> @@ -1256,8 +1207,12 @@ int CameraCapabilities::initializeStaticMetadata()
>  
>  	std::vector<uint32_t> availableStreamConfigurations;
>  	std::vector<int64_t> minFrameDurations;
> +	int maxYUVFps = 0;
> +	Size maxYUVSize;
> +
>  	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
>  	minFrameDurations.reserve(streamConfigurations_.size() * 4);
> +
>  	for (const auto &entry : streamConfigurations_) {
>  		/*
>  		 * Filter out YUV streams not capable of running at 30 FPS.
> @@ -1274,6 +1229,16 @@ int CameraCapabilities::initializeStaticMetadata()
>  		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB && fps < 30)
>  			continue;
>  
> +		/*
> +		 * Collect the FPS of the maximum YUV output size to populate
> +		 * AE_AVAILABLE_TARGET_FPS_RANGE
> +		 */
> +		if (entry.androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
> +		    entry.resolution > maxYUVSize) {
> +			maxYUVSize = entry.resolution;
> +			maxYUVFps = fps;
> +		}
> +
>  		/* Stream configuration map. */
>  		availableStreamConfigurations.push_back(entry.androidFormat);
>  		availableStreamConfigurations.push_back(entry.resolution.width);
> @@ -1301,6 +1266,18 @@ int CameraCapabilities::initializeStaticMetadata()
>  	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
>  				  minFrameDurations);
>  
> +	/*
> +	 * Register to the camera service {min, max} and {max, max} with
> +	 * max being the larger YUV stream maximum frame rate and min being
> +	 * 15 if the camera can do less than that.
> +	 */
> +	int32_t minFps = std::max(1e9 / maxFrameDuration_, 15.0);
> +	int32_t availableAeFpsTarget[] = {
> +		minFps, maxYUVFps, maxYUVFps, maxYUVFps,
> +	};
> +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> +				  availableAeFpsTarget);
> +
>  	std::vector<int64_t> availableStallDurations;
>  	for (const auto &entry : streamConfigurations_) {
>  		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> -- 
> 2.32.0
>
Jacopo Mondi Sept. 3, 2021, 8:52 a.m. UTC | #2
Hi Paul,

On Wed, Sep 01, 2021 at 06:31:42PM +0900, paul.elder@ideasonboard.com wrote:
> Hi Jacopo,
>
> On Fri, Aug 27, 2021 at 02:07:54PM +0200, Jacopo Mondi wrote:
> > As reported by the CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES documentation
> > in the Android developer reference:
> >
> > "For devices advertising any color filter arrangement other than NIR, or
> > devices not advertising color filter arrangement, this list will always
> > include (min, max) and (max, max) where min <= 15 and max = the maximum
> > output frame rate of the maximum YUV_420_888 output size."
> >
> > Collect the higher FPS of the larger YUV stream and use 15 FPS as the
> > minimum value, if the camera can go slower than that. Populate the
>
> If min is allowed to be less than 15, then why do you cap the minimum at
> 15?

Good question! I would say 'because the Intel HAL does so' but I know
it's a very fragile reasoning.

I'll try to use the max frame duration, after all our definition of
the FrameDurationLimits control matches

        The maximum frame duration provides the absolute limit to the shutter
        speed computed by the AE algorithm and it overrides any exposure mode
        setting specified with controls::AeExposureMode.

So I think it's fair to report it as the AE algorithm FPS limit to
Android.

-If- Android needs something different for reasons to be clarified,
we'll have to adjust it in the HAL I guess.

Thanks
   j

>
>
> Paul
>
> > ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES static metadata with the
> > newly computed values.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > ---
> >  src/android/camera_capabilities.cpp | 75 ++++++++++-------------------
> >  1 file changed, 26 insertions(+), 49 deletions(-)
> >
> > diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> > index 8bde06e824ef..01cdbcf1395d 100644
> > --- a/src/android/camera_capabilities.cpp
> > +++ b/src/android/camera_capabilities.cpp
> > @@ -862,55 +862,6 @@ int CameraCapabilities::initializeStaticMetadata()
> >  	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> >  				  aeAvailableModes);
> >
> > -	/* Initialize the AE frame duration limits. */
> > -	int64_t minFrameDurationNsec = -1;
> > -	int64_t maxFrameDurationNsec = -1;
> > -	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> > -	minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> > -	maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> > -
> > -	/*
> > -	 * Adjust the minimum frame duration to comply with Android
> > -	 * requirements. The camera service mandates all preview/record
> > -	 * streams to have a minimum frame duration < 33,366 milliseconds
> > -	 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> > -	 * implementation).
> > -	 *
> > -	 * If we're close enough (+ 500 useconds) to that value, round
> > -	 * the minimum frame duration of the camera to an accepted
> > -	 * value.
> > -	 */
> > -	static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> > -	if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> > -	    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> > -		minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> > -
> > -	/*
> > -	 * The AE routine frame rate limits are computed using the frame
> > -	 * duration limits, as libcamera clips the AE routine to the
> > -	 * frame durations.
> > -	 */
> > -	int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> > -	int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> > -	minFps = std::max(1, minFps);
> > -
> > -	/*
> > -	 * Force rounding errors so that we have the proper frame
> > -	 * durations for when we reuse these variables later
> > -	 */
> > -	minFrameDurationNsec = 1e9 / maxFps;
> > -	maxFrameDurationNsec = 1e9 / minFps;
> > -
> > -	/*
> > -	 * Register to the camera service {min, max} and {max, max}
> > -	 * intervals as requested by the metadata documentation.
> > -	 */
> > -	int32_t availableAeFpsTarget[] = {
> > -		minFps, maxFps, maxFps, maxFps
> > -	};
> > -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > -				  availableAeFpsTarget);
> > -
> >  	std::vector<int32_t> aeCompensationRange = {
> >  		0, 0,
> >  	};
> > @@ -1256,8 +1207,12 @@ int CameraCapabilities::initializeStaticMetadata()
> >
> >  	std::vector<uint32_t> availableStreamConfigurations;
> >  	std::vector<int64_t> minFrameDurations;
> > +	int maxYUVFps = 0;
> > +	Size maxYUVSize;
> > +
> >  	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
> >  	minFrameDurations.reserve(streamConfigurations_.size() * 4);
> > +
> >  	for (const auto &entry : streamConfigurations_) {
> >  		/*
> >  		 * Filter out YUV streams not capable of running at 30 FPS.
> > @@ -1274,6 +1229,16 @@ int CameraCapabilities::initializeStaticMetadata()
> >  		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB && fps < 30)
> >  			continue;
> >
> > +		/*
> > +		 * Collect the FPS of the maximum YUV output size to populate
> > +		 * AE_AVAILABLE_TARGET_FPS_RANGE
> > +		 */
> > +		if (entry.androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
> > +		    entry.resolution > maxYUVSize) {
> > +			maxYUVSize = entry.resolution;
> > +			maxYUVFps = fps;
> > +		}
> > +
> >  		/* Stream configuration map. */
> >  		availableStreamConfigurations.push_back(entry.androidFormat);
> >  		availableStreamConfigurations.push_back(entry.resolution.width);
> > @@ -1301,6 +1266,18 @@ int CameraCapabilities::initializeStaticMetadata()
> >  	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> >  				  minFrameDurations);
> >
> > +	/*
> > +	 * Register to the camera service {min, max} and {max, max} with
> > +	 * max being the larger YUV stream maximum frame rate and min being
> > +	 * 15 if the camera can do less than that.
> > +	 */
> > +	int32_t minFps = std::max(1e9 / maxFrameDuration_, 15.0);
> > +	int32_t availableAeFpsTarget[] = {
> > +		minFps, maxYUVFps, maxYUVFps, maxYUVFps,
> > +	};
> > +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > +				  availableAeFpsTarget);
> > +
> >  	std::vector<int64_t> availableStallDurations;
> >  	for (const auto &entry : streamConfigurations_) {
> >  		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> > --
> > 2.32.0
> >
Paul Elder Sept. 6, 2021, 8:49 a.m. UTC | #3
Hi Jacopo,

On Fri, Sep 03, 2021 at 10:52:25AM +0200, Jacopo Mondi wrote:
> Hi Paul,
> 
> On Wed, Sep 01, 2021 at 06:31:42PM +0900, paul.elder@ideasonboard.com wrote:
> > Hi Jacopo,
> >
> > On Fri, Aug 27, 2021 at 02:07:54PM +0200, Jacopo Mondi wrote:
> > > As reported by the CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES documentation
> > > in the Android developer reference:
> > >
> > > "For devices advertising any color filter arrangement other than NIR, or
> > > devices not advertising color filter arrangement, this list will always
> > > include (min, max) and (max, max) where min <= 15 and max = the maximum
> > > output frame rate of the maximum YUV_420_888 output size."
> > >
> > > Collect the higher FPS of the larger YUV stream and use 15 FPS as the
> > > minimum value, if the camera can go slower than that. Populate the
> >
> > If min is allowed to be less than 15, then why do you cap the minimum at
> > 15?
> 
> Good question! I would say 'because the Intel HAL does so' but I know
> it's a very fragile reasoning.
> 
> I'll try to use the max frame duration, after all our definition of
> the FrameDurationLimits control matches
> 
>         The maximum frame duration provides the absolute limit to the shutter
>         speed computed by the AE algorithm and it overrides any exposure mode
>         setting specified with controls::AeExposureMode.
> 
> So I think it's fair to report it as the AE algorithm FPS limit to
> Android.

Sounds good! \o/

With that fixed,

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

> 
> -If- Android needs something different for reasons to be clarified,
> we'll have to adjust it in the HAL I guess.
> 
> Thanks
>    j
> 
> >
> >
> > Paul
> >
> > > ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES static metadata with the
> > > newly computed values.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > ---
> > >  src/android/camera_capabilities.cpp | 75 ++++++++++-------------------
> > >  1 file changed, 26 insertions(+), 49 deletions(-)
> > >
> > > diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
> > > index 8bde06e824ef..01cdbcf1395d 100644
> > > --- a/src/android/camera_capabilities.cpp
> > > +++ b/src/android/camera_capabilities.cpp
> > > @@ -862,55 +862,6 @@ int CameraCapabilities::initializeStaticMetadata()
> > >  	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
> > >  				  aeAvailableModes);
> > >
> > > -	/* Initialize the AE frame duration limits. */
> > > -	int64_t minFrameDurationNsec = -1;
> > > -	int64_t maxFrameDurationNsec = -1;
> > > -	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
> > > -	minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
> > > -	maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
> > > -
> > > -	/*
> > > -	 * Adjust the minimum frame duration to comply with Android
> > > -	 * requirements. The camera service mandates all preview/record
> > > -	 * streams to have a minimum frame duration < 33,366 milliseconds
> > > -	 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
> > > -	 * implementation).
> > > -	 *
> > > -	 * If we're close enough (+ 500 useconds) to that value, round
> > > -	 * the minimum frame duration of the camera to an accepted
> > > -	 * value.
> > > -	 */
> > > -	static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
> > > -	if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
> > > -	    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
> > > -		minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
> > > -
> > > -	/*
> > > -	 * The AE routine frame rate limits are computed using the frame
> > > -	 * duration limits, as libcamera clips the AE routine to the
> > > -	 * frame durations.
> > > -	 */
> > > -	int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
> > > -	int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
> > > -	minFps = std::max(1, minFps);
> > > -
> > > -	/*
> > > -	 * Force rounding errors so that we have the proper frame
> > > -	 * durations for when we reuse these variables later
> > > -	 */
> > > -	minFrameDurationNsec = 1e9 / maxFps;
> > > -	maxFrameDurationNsec = 1e9 / minFps;
> > > -
> > > -	/*
> > > -	 * Register to the camera service {min, max} and {max, max}
> > > -	 * intervals as requested by the metadata documentation.
> > > -	 */
> > > -	int32_t availableAeFpsTarget[] = {
> > > -		minFps, maxFps, maxFps, maxFps
> > > -	};
> > > -	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > -				  availableAeFpsTarget);
> > > -
> > >  	std::vector<int32_t> aeCompensationRange = {
> > >  		0, 0,
> > >  	};
> > > @@ -1256,8 +1207,12 @@ int CameraCapabilities::initializeStaticMetadata()
> > >
> > >  	std::vector<uint32_t> availableStreamConfigurations;
> > >  	std::vector<int64_t> minFrameDurations;
> > > +	int maxYUVFps = 0;
> > > +	Size maxYUVSize;
> > > +
> > >  	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
> > >  	minFrameDurations.reserve(streamConfigurations_.size() * 4);
> > > +
> > >  	for (const auto &entry : streamConfigurations_) {
> > >  		/*
> > >  		 * Filter out YUV streams not capable of running at 30 FPS.
> > > @@ -1274,6 +1229,16 @@ int CameraCapabilities::initializeStaticMetadata()
> > >  		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB && fps < 30)
> > >  			continue;
> > >
> > > +		/*
> > > +		 * Collect the FPS of the maximum YUV output size to populate
> > > +		 * AE_AVAILABLE_TARGET_FPS_RANGE
> > > +		 */
> > > +		if (entry.androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
> > > +		    entry.resolution > maxYUVSize) {
> > > +			maxYUVSize = entry.resolution;
> > > +			maxYUVFps = fps;
> > > +		}
> > > +
> > >  		/* Stream configuration map. */
> > >  		availableStreamConfigurations.push_back(entry.androidFormat);
> > >  		availableStreamConfigurations.push_back(entry.resolution.width);
> > > @@ -1301,6 +1266,18 @@ int CameraCapabilities::initializeStaticMetadata()
> > >  	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
> > >  				  minFrameDurations);
> > >
> > > +	/*
> > > +	 * Register to the camera service {min, max} and {max, max} with
> > > +	 * max being the larger YUV stream maximum frame rate and min being
> > > +	 * 15 if the camera can do less than that.
> > > +	 */
> > > +	int32_t minFps = std::max(1e9 / maxFrameDuration_, 15.0);
> > > +	int32_t availableAeFpsTarget[] = {
> > > +		minFps, maxYUVFps, maxYUVFps, maxYUVFps,
> > > +	};
> > > +	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
> > > +				  availableAeFpsTarget);
> > > +
> > >  	std::vector<int64_t> availableStallDurations;
> > >  	for (const auto &entry : streamConfigurations_) {
> > >  		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)
> > > --
> > > 2.32.0
> > >

Patch
diff mbox series

diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp
index 8bde06e824ef..01cdbcf1395d 100644
--- a/src/android/camera_capabilities.cpp
+++ b/src/android/camera_capabilities.cpp
@@ -862,55 +862,6 @@  int CameraCapabilities::initializeStaticMetadata()
 	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_MODES,
 				  aeAvailableModes);
 
-	/* Initialize the AE frame duration limits. */
-	int64_t minFrameDurationNsec = -1;
-	int64_t maxFrameDurationNsec = -1;
-	const auto frameDurationsInfo = controlsInfo.find(&controls::FrameDurationLimits);
-	minFrameDurationNsec = frameDurationsInfo->second.min().get<int64_t>() * 1000;
-	maxFrameDurationNsec = frameDurationsInfo->second.max().get<int64_t>() * 1000;
-
-	/*
-	 * Adjust the minimum frame duration to comply with Android
-	 * requirements. The camera service mandates all preview/record
-	 * streams to have a minimum frame duration < 33,366 milliseconds
-	 * (see MAX_PREVIEW_RECORD_DURATION_NS in the camera service
-	 * implementation).
-	 *
-	 * If we're close enough (+ 500 useconds) to that value, round
-	 * the minimum frame duration of the camera to an accepted
-	 * value.
-	 */
-	static constexpr int64_t MAX_PREVIEW_RECORD_DURATION_NS = 1e9 / 29.97;
-	if (minFrameDurationNsec > MAX_PREVIEW_RECORD_DURATION_NS &&
-	    minFrameDurationNsec < MAX_PREVIEW_RECORD_DURATION_NS + 500000)
-		minFrameDurationNsec = MAX_PREVIEW_RECORD_DURATION_NS - 1000;
-
-	/*
-	 * The AE routine frame rate limits are computed using the frame
-	 * duration limits, as libcamera clips the AE routine to the
-	 * frame durations.
-	 */
-	int32_t maxFps = std::round(1e9 / minFrameDurationNsec);
-	int32_t minFps = std::round(1e9 / maxFrameDurationNsec);
-	minFps = std::max(1, minFps);
-
-	/*
-	 * Force rounding errors so that we have the proper frame
-	 * durations for when we reuse these variables later
-	 */
-	minFrameDurationNsec = 1e9 / maxFps;
-	maxFrameDurationNsec = 1e9 / minFps;
-
-	/*
-	 * Register to the camera service {min, max} and {max, max}
-	 * intervals as requested by the metadata documentation.
-	 */
-	int32_t availableAeFpsTarget[] = {
-		minFps, maxFps, maxFps, maxFps
-	};
-	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
-				  availableAeFpsTarget);
-
 	std::vector<int32_t> aeCompensationRange = {
 		0, 0,
 	};
@@ -1256,8 +1207,12 @@  int CameraCapabilities::initializeStaticMetadata()
 
 	std::vector<uint32_t> availableStreamConfigurations;
 	std::vector<int64_t> minFrameDurations;
+	int maxYUVFps = 0;
+	Size maxYUVSize;
+
 	availableStreamConfigurations.reserve(streamConfigurations_.size() * 4);
 	minFrameDurations.reserve(streamConfigurations_.size() * 4);
+
 	for (const auto &entry : streamConfigurations_) {
 		/*
 		 * Filter out YUV streams not capable of running at 30 FPS.
@@ -1274,6 +1229,16 @@  int CameraCapabilities::initializeStaticMetadata()
 		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB && fps < 30)
 			continue;
 
+		/*
+		 * Collect the FPS of the maximum YUV output size to populate
+		 * AE_AVAILABLE_TARGET_FPS_RANGE
+		 */
+		if (entry.androidFormat == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
+		    entry.resolution > maxYUVSize) {
+			maxYUVSize = entry.resolution;
+			maxYUVFps = fps;
+		}
+
 		/* Stream configuration map. */
 		availableStreamConfigurations.push_back(entry.androidFormat);
 		availableStreamConfigurations.push_back(entry.resolution.width);
@@ -1301,6 +1266,18 @@  int CameraCapabilities::initializeStaticMetadata()
 	staticMetadata_->addEntry(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
 				  minFrameDurations);
 
+	/*
+	 * Register to the camera service {min, max} and {max, max} with
+	 * max being the larger YUV stream maximum frame rate and min being
+	 * 15 if the camera can do less than that.
+	 */
+	int32_t minFps = std::max(1e9 / maxFrameDuration_, 15.0);
+	int32_t availableAeFpsTarget[] = {
+		minFps, maxYUVFps, maxYUVFps, maxYUVFps,
+	};
+	staticMetadata_->addEntry(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
+				  availableAeFpsTarget);
+
 	std::vector<int64_t> availableStallDurations;
 	for (const auto &entry : streamConfigurations_) {
 		if (entry.androidFormat != HAL_PIXEL_FORMAT_BLOB)