Commit 07235766 authored by sakal's avatar sakal Committed by Commit bot

Update FPS selection logic in VideoCaptureCamera to select a wider FPS range.

The code fixes the bug referenced by the bug field. If requested fps is 15 and
the camera supports fps ranges 0 - 30, 10 - 20, 14 - 16 and 15 - 15. Previously,
we would choose 15 - 15 fps. This is bad because it doesn't leave the camera
room to adjust based on the lighting conditions. Heuristic copied from WebRTC
chooses the 10 - 20 fps range instead.

BUG=630266

Review-Url: https://codereview.chromium.org/2354923002
Cr-Commit-Position: refs/heads/master@{#441755}
parent 92899656
......@@ -13,6 +13,9 @@ import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Video Capture Device base class, defines a set of methods that native code
......@@ -21,6 +24,19 @@ import java.nio.ByteBuffer;
**/
@JNINamespace("media")
public abstract class VideoCapture {
/**
* Common class for storing a framerate range. Values should be multiplied by 1000.
*/
protected static class FramerateRange {
public int min;
public int max;
public FramerateRange(int min, int max) {
this.min = min;
this.max = max;
}
}
// The angle (0, 90, 180, 270) that the image needs to be rotated to show in
// the display's native orientation.
protected int mCameraNativeOrientation;
......@@ -58,7 +74,7 @@ public abstract class VideoCapture {
/**
* @param zoom Zoom level, should be ignored if 0.
* @param focusMode Focus mode following AndroidMeteringMode enum.
* @param exposureMode Focus mode following AndroidMeteringMode enum.
* @param exposureMode Exposure mode following AndroidMeteringMode enum.
* @param pointsOfInterest2D 2D normalized points of interest, marshalled with
* x coordinate first followed by the y coordinate.
* @param hasExposureCompensation Indicates if |exposureCompensation| is set.
......@@ -147,6 +163,52 @@ public abstract class VideoCapture {
return orientation;
}
/**
* Finds the framerate range matching |targetFramerate|. Tries to find a range with as low of a
* minimum value as possible to allow the camera adjust based on the lighting conditions.
* Assumes that all framerate values are multiplied by 1000.
*
* This code is mostly copied from WebRTC:
* CameraEnumerationAndroid.getClosestSupportedFramerateRange
* in webrtc/api/android/java/src/org/webrtc/CameraEnumerationAndroid.java
*/
protected static FramerateRange getClosestFramerateRange(
final List<FramerateRange> framerateRanges, final int targetFramerate) {
return Collections.min(framerateRanges, new Comparator<FramerateRange>() {
// Threshold and penalty weights if the upper bound is further away than
// |MAX_FPS_DIFF_THRESHOLD| from requested.
private static final int MAX_FPS_DIFF_THRESHOLD = 5000;
private static final int MAX_FPS_LOW_DIFF_WEIGHT = 1;
private static final int MAX_FPS_HIGH_DIFF_WEIGHT = 3;
// Threshold and penalty weights if the lower bound is bigger than |MIN_FPS_THRESHOLD|.
private static final int MIN_FPS_THRESHOLD = 8000;
private static final int MIN_FPS_LOW_VALUE_WEIGHT = 1;
private static final int MIN_FPS_HIGH_VALUE_WEIGHT = 4;
// Use one weight for small |value| less than |threshold|, and another weight above.
private int progressivePenalty(
int value, int threshold, int lowWeight, int highWeight) {
return (value < threshold)
? value * lowWeight
: threshold * lowWeight + (value - threshold) * highWeight;
}
int diff(FramerateRange range) {
final int minFpsError = progressivePenalty(range.min, MIN_FPS_THRESHOLD,
MIN_FPS_LOW_VALUE_WEIGHT, MIN_FPS_HIGH_VALUE_WEIGHT);
final int maxFpsError = progressivePenalty(Math.abs(targetFramerate - range.max),
MAX_FPS_DIFF_THRESHOLD, MAX_FPS_LOW_DIFF_WEIGHT, MAX_FPS_HIGH_DIFF_WEIGHT);
return minFpsError + maxFpsError;
}
@Override
public int compare(FramerateRange range1, FramerateRange range2) {
return diff(range1) - diff(range2);
}
});
}
// Method for VideoCapture implementations to call back native code.
public native void nativeOnFrameAvailable(
long nativeVideoCaptureDeviceAndroid, byte[] data, int length, int rotation);
......
......@@ -287,26 +287,17 @@ public class VideoCaptureCamera
Log.e(TAG, "allocate: no fps range found");
return false;
}
final ArrayList<FramerateRange> framerateRanges =
new ArrayList<FramerateRange>(listFpsRange.size());
for (int[] range : listFpsRange) {
framerateRanges.add(new FramerateRange(range[0], range[1]));
}
// API fps ranges are scaled up x1000 to avoid floating point.
int frameRateScaled = frameRate * 1000;
// Use the first range as the default chosen range.
int[] chosenFpsRange = listFpsRange.get(0);
int frameRateNearest = Math.abs(frameRateScaled - chosenFpsRange[0])
< Math.abs(frameRateScaled - chosenFpsRange[1])
? chosenFpsRange[0]
: chosenFpsRange[1];
int chosenFrameRate = (frameRateNearest + 999) / 1000;
int fpsRangeSize = Integer.MAX_VALUE;
for (int[] fpsRange : listFpsRange) {
if (fpsRange[0] <= frameRateScaled && frameRateScaled <= fpsRange[1]
&& (fpsRange[1] - fpsRange[0]) <= fpsRangeSize) {
chosenFpsRange = fpsRange;
chosenFrameRate = frameRate;
fpsRangeSize = fpsRange[1] - fpsRange[0];
}
}
Log.d(TAG, "allocate: fps set to %d, [%d-%d]", chosenFrameRate, chosenFpsRange[0],
chosenFpsRange[1]);
final FramerateRange chosenFramerateRange =
getClosestFramerateRange(framerateRanges, frameRateScaled);
final int[] chosenFpsRange = new int[] {chosenFramerateRange.min, chosenFramerateRange.max};
Log.d(TAG, "allocate: fps set to [%d-%d]", chosenFpsRange[0], chosenFpsRange[1]);
// Calculate size.
List<android.hardware.Camera.Size> listCameraSize = parameters.getSupportedPreviewSizes();
......@@ -347,9 +338,8 @@ public class VideoCaptureCamera
Log.d(TAG, "Continuous focus mode not supported.");
}
// Fill the capture format.
mCaptureFormat = new VideoCaptureFormat(
matchedWidth, matchedHeight, chosenFrameRate, BuggyDeviceHack.getImageFormat());
mCaptureFormat = new VideoCaptureFormat(matchedWidth, matchedHeight,
chosenFpsRange[1] / 1000, BuggyDeviceHack.getImageFormat());
parameters.setPictureSize(matchedWidth, matchedHeight);
parameters.setPreviewSize(matchedWidth, matchedHeight);
parameters.setPreviewFpsRange(chosenFpsRange[0], chosenFpsRange[1]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment