Merge pi-qpr1-release PQ1A.181105.017.A1 to pi-platform-release
Change-Id: I9c35ee348a2df6048481577e2597911cc35b6ea5
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index fea5a1d..a9aae0b 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -4,3 +4,4 @@
group audio camera input drmrpc
ioprio rt 4
writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ rlimit rtprio 10 10
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 3010646..c1f5ddc 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -479,11 +479,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of android.control.maxRegionsAe.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* (ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.width - 1,
- * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * (ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.width - 1,
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * (ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.width - 1,
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -504,8 +519,10 @@
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
* ymax.</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_CONTROL_AE_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 4,
@@ -641,11 +658,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of focus areas supported by the device is determined by the value
* of android.control.maxRegionsAf.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* (ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.width - 1,
- * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * (ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.width - 1,
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * (ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.width - 1,
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -667,8 +699,10 @@
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
* ymax.</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_CONTROL_AF_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 8,
@@ -800,11 +834,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of android.control.maxRegionsAwb.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* (ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.width - 1,
- * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * (ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.width - 1,
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * (ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.width - 1,
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must range from 0 to 1000, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -825,8 +874,10 @@
* The rectangle is defined to be inclusive on xmin and ymin, but exclusive on xmax and
* ymax.</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_CONTROL_AWB_REGIONS = // int32[5*area_count]
ACAMERA_CONTROL_START + 12,
@@ -2261,7 +2312,9 @@
* <p>If this device is the largest or only camera device with a given facing, then this
* position will be <code>(0, 0, 0)</code>; a camera device with a lens optical center located 3 cm
* from the main sensor along the +X axis (to the right from the user's perspective) will
- * report <code>(0.03, 0, 0)</code>.</p>
+ * report <code>(0.03, 0, 0)</code>. Note that this means that, for many computer vision
+ * applications, the position needs to be negated to convert it to a translation from the
+ * camera to the origin.</p>
* <p>To transform a pixel coordinates between two cameras facing the same direction, first
* the source camera ACAMERA_LENS_DISTORTION must be corrected for. Then the source
* camera ACAMERA_LENS_INTRINSIC_CALIBRATION needs to be applied, followed by the
@@ -2273,7 +2326,8 @@
* <p>To compare this against a real image from the destination camera, the destination camera
* image then needs to be corrected for radial distortion before comparison or sampling.</p>
* <p>When ACAMERA_LENS_POSE_REFERENCE is GYROSCOPE, then this position is relative to
- * the center of the primary gyroscope on the device.</p>
+ * the center of the primary gyroscope on the device. The axis definitions are the same as
+ * with PRIMARY_CAMERA.</p>
*
* @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_INTRINSIC_CALIBRATION
@@ -2367,13 +2421,15 @@
* </code></pre>
* <p>which can then be combined with the camera pose rotation
* <code>R</code> and translation <code>t</code> (ACAMERA_LENS_POSE_ROTATION and
- * ACAMERA_LENS_POSE_TRANSLATION, respective) to calculate the
+ * ACAMERA_LENS_POSE_TRANSLATION, respectively) to calculate the
* complete transform from world coordinates to pixel
* coordinates:</p>
- * <pre><code>P = [ K 0 * [ R t
- * 0 1 ] 0 1 ]
+ * <pre><code>P = [ K 0 * [ R -Rt
+ * 0 1 ] 0 1 ]
* </code></pre>
- * <p>and with <code>p_w</code> being a point in the world coordinate system
+ * <p>(Note the negation of poseTranslation when mapping from camera
+ * to world coordinates, and multiplication by the rotation).</p>
+ * <p>With <code>p_w</code> being a point in the world coordinate system
* and <code>p_s</code> being a point in the camera active pixel array
* coordinate system, and with the mapping including the
* homogeneous division by z:</p>
@@ -2395,6 +2451,13 @@
* activeArraySize rectangle), to determine the final pixel
* coordinate of the world point for processed (non-RAW)
* output buffers.</p>
+ * <p>For camera devices, the center of pixel <code>(x,y)</code> is located at
+ * coordinate <code>(x + 0.5, y + 0.5)</code>. So on a device with a
+ * precorrection active array of size <code>(10,10)</code>, the valid pixel
+ * indices go from <code>(0,0)-(9,9)</code>, and an perfectly-built camera would
+ * have an optical center at the exact center of the pixel grid, at
+ * coordinates <code>(5.0, 5.0)</code>, which is the top-left corner of pixel
+ * <code>(5,5)</code>.</p>
*
* @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_POSE_ROTATION
@@ -2979,9 +3042,17 @@
* </ul></p>
*
* <p>This control can be used to implement digital zoom.</p>
- * <p>The crop region coordinate system is based off
- * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with <code>(0, 0)</code> being the
- * top-left corner of the sensor active array.</p>
+ * <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Output streams use this rectangle to produce their output,
* cropping to a smaller region if necessary to maintain the
* stream's aspect ratio, then scaling the sensor input to
@@ -3000,18 +3071,26 @@
* outputs will crop horizontally (pillarbox), and 16:9
* streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The width and height of the crop region cannot
- * be set to be smaller than
+ * <p>If the coordinate system is ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, the width and height
+ * of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM )</code> and
* <code>floor( activeArraySize.height / ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM )</code>, respectively.</p>
+ * <p>If the coordinate system is ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, the width
+ * and height of the crop region cannot be set to be smaller than
+ * <code>floor( preCorrectionActiveArraySize.width / ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM )</code>
+ * and
+ * <code>floor( preCorrectionActiveArraySize.height / ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM )</code>,
+ * respectively.</p>
* <p>The camera device may adjust the crop region to account
* for rounding and other hardware requirements; the final
* crop region used will be included in the output capture
* result.</p>
* <p>The data representation is int[4], which maps to (left, top, width, height).</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_SCALER_CROP_REGION = // int32[4]
ACAMERA_SCALER_START,
@@ -3977,12 +4056,24 @@
* ACAMERA_SCALER_CROP_REGION, is defined relative to the active array rectangle given in
* this field, with <code>(0, 0)</code> being the top-left of this rectangle.</p>
* <p>The active array may be smaller than the full pixel array, since the full array may
- * include black calibration pixels or other inactive regions, and geometric correction
- * resulting in scaling or cropping may have been applied.</p>
+ * include black calibration pixels or other inactive regions.</p>
+ * <p>For devices that do not support ACAMERA_DISTORTION_CORRECTION_MODE control, the active
+ * array must be the same as ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.</p>
+ * <p>For devices that support ACAMERA_DISTORTION_CORRECTION_MODE control, the active array must
+ * be enclosed by ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE. The difference between
+ * pre-correction active array and active array accounts for scaling or cropping caused
+ * by lens geometric distortion correction.</p>
+ * <p>In general, application should always refer to active array size for controls like
+ * metering regions or crop region. Two exceptions are when the application is dealing with
+ * RAW image buffers (RAW_SENSOR, RAW10, RAW12 etc), or when application explicitly set
+ * ACAMERA_DISTORTION_CORRECTION_MODE to OFF. In these cases, application should refer
+ * to ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.</p>
* <p>The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE = // int32[4]
ACAMERA_SENSOR_INFO_START,
@@ -4224,9 +4315,9 @@
* <ol>
* <li>ACAMERA_LENS_DISTORTION.</li>
* </ol>
- * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
- * as the post-distortion-corrected rectangle given in
- * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
+ * <p>If the camera device doesn't support geometric distortion correction, or all of the
+ * geometric distortion fields are no-ops, this rectangle will be the same as the
+ * post-distortion-corrected rectangle given in ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
* <p>This rectangle is defined relative to the full pixel array; (0,0) is the top-left of
* the full pixel array, and the size of the full pixel array is given by
* ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE.</p>
@@ -4372,11 +4463,22 @@
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* </ul></p>
*
- * <p>The coordinate system is that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
+ * <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE == FULL</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see ACAMERA_STATISTICS_FACE_DETECT_MODE
*/
ACAMERA_STATISTICS_FACE_LANDMARKS = // int32[n*6]
@@ -4392,12 +4494,23 @@
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* </ul></p>
*
- * <p>The coordinate system is that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
+ * <p>For devices not supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system always follows that of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting ACAMERA_DISTORTION_CORRECTION_MODE control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Only available if ACAMERA_STATISTICS_FACE_DETECT_MODE != OFF
- * The data representation is <code>int[4]</code>, which maps to <code>(left, top, width, height)</code>.</p>
+ * The data representation is <code>int[4]</code>, which maps to <code>(left, top, right, bottom)</code>.</p>
*
+ * @see ACAMERA_DISTORTION_CORRECTION_MODE
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see ACAMERA_STATISTICS_FACE_DETECT_MODE
*/
ACAMERA_STATISTICS_FACE_RECTANGLES = // int32[n*4]
@@ -4574,8 +4687,8 @@
ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE = // byte (acamera_metadata_enum_android_statistics_lens_shading_map_mode_t)
ACAMERA_STATISTICS_START + 16,
/**
- * <p>A control for selecting whether OIS position information is included in output
- * result metadata.</p>
+ * <p>A control for selecting whether optical stabilization (OIS) position
+ * information is included in output result metadata.</p>
*
* <p>Type: byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)</p>
*
@@ -4585,6 +4698,12 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
+ * <p>Since optical image stabilization generally involves motion much faster than the duration
+ * of individualq image exposure, multiple OIS samples can be included for a single capture
+ * result. For example, if the OIS reporting operates at 200 Hz, a typical camera operating
+ * at 30fps may have 6-7 OIS samples per capture result. This information can be combined
+ * with the rolling shutter skew to account for lens motion during image exposure in
+ * post-processing algorithms.</p>
*/
ACAMERA_STATISTICS_OIS_DATA_MODE = // byte (acamera_metadata_enum_android_statistics_ois_data_mode_t)
ACAMERA_STATISTICS_START + 17,
@@ -4616,11 +4735,15 @@
* </ul></p>
*
* <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
- * A positive value is a shift from left to right in active array coordinate system. For
- * example, if the optical center is (1000, 500) in active array coordinates, a shift of
- * (3, 0) puts the new optical center at (1003, 500).</p>
+ * A positive value is a shift from left to right in the pre-correction active array
+ * coordinate system. For example, if the optical center is (1000, 500) in pre-correction
+ * active array coordinates, a shift of (3, 0) puts the new optical center at (1003, 500).</p>
* <p>The number of shifts must match the number of timestamps in
* ACAMERA_STATISTICS_OIS_TIMESTAMPS.</p>
+ * <p>The OIS samples are not affected by whether lens distortion correction is enabled (on
+ * supporting devices). They are always reported in pre-correction active array coordinates,
+ * since the scaling of OIS shifts would depend on the specific spot on the sensor the shift
+ * is needed.</p>
*
* @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
*/
@@ -4637,11 +4760,15 @@
* </ul></p>
*
* <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
- * A positive value is a shift from top to bottom in active array coordinate system. For
- * example, if the optical center is (1000, 500) in active array coordinates, a shift of
- * (0, 5) puts the new optical center at (1000, 505).</p>
+ * A positive value is a shift from top to bottom in pre-correction active array coordinate
+ * system. For example, if the optical center is (1000, 500) in active array coordinates, a
+ * shift of (0, 5) puts the new optical center at (1000, 505).</p>
* <p>The number of shifts must match the number of timestamps in
* ACAMERA_STATISTICS_OIS_TIMESTAMPS.</p>
+ * <p>The OIS samples are not affected by whether lens distortion correction is enabled (on
+ * supporting devices). They are always reported in pre-correction active array coordinates,
+ * since the scaling of OIS shifts would depend on the specific spot on the sensor the shift
+ * is needed.</p>
*
* @see ACAMERA_STATISTICS_OIS_TIMESTAMPS
*/
@@ -4993,12 +5120,26 @@
* the following code snippet can be used:</p>
* <pre><code>// Returns true if the device supports the required hardware level, or better.
* boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+ * final int[] sortedHwLevels = {
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ * };
* int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- * if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
- * return requiredLevel == deviceLevel;
+ * if (requiredLevel == deviceLevel) {
+ * return true;
* }
- * // deviceLevel is not LEGACY, can use numerical sort
- * return requiredLevel <= deviceLevel;
+ *
+ * for (int sortedlevel : sortedHwLevels) {
+ * if (sortedlevel == requiredLevel) {
+ * return true;
+ * } else if (sortedlevel == deviceLevel) {
+ * return false;
+ * }
+ * }
+ * return false; // Should never reach here
* }
* </code></pre>
* <p>At a high level, the levels are:</p>
@@ -5012,6 +5153,8 @@
* post-processing settings, and image capture at a high rate.</li>
* <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
* with additional output stream configurations.</li>
+ * <li><code>EXTERNAL</code> devices are similar to <code>LIMITED</code> devices with exceptions like some sensor or
+ * lens information not reorted or less stable framerates.</li>
* </ul>
* <p>See the individual level enums for full descriptions of the supported capabilities. The
* ACAMERA_REQUEST_AVAILABLE_CAPABILITIES entry describes the device's capabilities at a
@@ -5315,18 +5458,36 @@
* any correction at all would slow down capture rate. Every output stream will have a
* similar amount of enhancement applied.</p>
* <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
- * applied to any RAW output. Metadata coordinates such as face rectangles or metering
- * regions are also not affected by correction.</p>
- * <p>Applications enabling distortion correction need to pay extra attention when converting
- * image coordinates between corrected output buffers and the sensor array. For example, if
- * the app supports tap-to-focus and enables correction, it then has to apply the distortion
- * model described in ACAMERA_LENS_DISTORTION to the image buffer tap coordinates to properly
- * calculate the tap position on the sensor active array to be used with
- * ACAMERA_CONTROL_AF_REGIONS. The same applies in reverse to detected face rectangles if
- * they need to be drawn on top of the corrected output buffers.</p>
+ * applied to any RAW output.</p>
+ * <p>This control will be on by default on devices that support this control. Applications
+ * disabling distortion correction need to pay extra attention with the coordinate system of
+ * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+ * metadata coordinates follow the coordinate system of
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE. When distortion is not OFF, metadata
+ * coordinates follow the coordinate system of ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE. The
+ * camera device will map these metadata fields to match the corrected image produced by the
+ * camera device, for both capture requests and results. However, this mapping is not very
+ * precise, since rectangles do not generally map to rectangles when corrected. Only linear
+ * scaling between the active array and precorrection active array coordinates is
+ * performed. Applications that require precise correction of metadata need to undo that
+ * linear scaling, and apply a more complete correction that takes into the account the app's
+ * own requirements.</p>
+ * <p>The full list of metadata that is affected in this way by distortion correction is:</p>
+ * <ul>
+ * <li>ACAMERA_CONTROL_AF_REGIONS</li>
+ * <li>ACAMERA_CONTROL_AE_REGIONS</li>
+ * <li>ACAMERA_CONTROL_AWB_REGIONS</li>
+ * <li>ACAMERA_SCALER_CROP_REGION</li>
+ * <li>android.statistics.faces</li>
+ * </ul>
*
+ * @see ACAMERA_CONTROL_AE_REGIONS
* @see ACAMERA_CONTROL_AF_REGIONS
+ * @see ACAMERA_CONTROL_AWB_REGIONS
* @see ACAMERA_LENS_DISTORTION
+ * @see ACAMERA_SCALER_CROP_REGION
+ * @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
ACAMERA_DISTORTION_CORRECTION_MODE = // byte (acamera_metadata_enum_android_distortion_correction_mode_t)
ACAMERA_DISTORTION_CORRECTION_START,
@@ -7056,11 +7217,11 @@
* camera in the list of supported camera devices.</p>
* <p>This capability requires the camera device to support the following:</p>
* <ul>
- * <li>This camera device must list the following static metadata entries in <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html">CameraCharacteristics</a>:<ul>
- * <li>android.logicalMultiCamera.physicalIds</li>
- * <li>ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE</li>
- * </ul>
- * </li>
+ * <li>The IDs of underlying physical cameras are returned via
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getPhysicalCameraIds">CameraCharacteristics#getPhysicalCameraIds</a>.</li>
+ * <li>This camera device must list static metadata
+ * ACAMERA_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE in
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html">CameraCharacteristics</a>.</li>
* <li>The underlying physical cameras' static metadata must list the following entries,
* so that the application can correlate pixels from the physical streams:<ul>
* <li>ACAMERA_LENS_POSE_REFERENCE</li>
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 4603515..d1859d1 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -140,14 +140,6 @@
}
/*
- * Returns "true" if the device is rotated 90 degrees.
- */
-static bool isDeviceRotated(int orientation) {
- return orientation != DISPLAY_ORIENTATION_0 &&
- orientation != DISPLAY_ORIENTATION_180;
-}
-
-/*
* Configures and starts the MediaCodec encoder. Obtains an input surface
* from the codec.
*/
@@ -242,22 +234,11 @@
const DisplayInfo& mainDpyInfo) {
// Set the region of the layer stack we're interested in, which in our
- // case is "all of it". If the app is rotated (so that the width of the
- // app is based on the height of the display), reverse width/height.
- bool deviceRotated = isDeviceRotated(mainDpyInfo.orientation);
- uint32_t sourceWidth, sourceHeight;
- if (!deviceRotated) {
- sourceWidth = mainDpyInfo.w;
- sourceHeight = mainDpyInfo.h;
- } else {
- ALOGV("using rotated width/height");
- sourceHeight = mainDpyInfo.w;
- sourceWidth = mainDpyInfo.h;
- }
- Rect layerStackRect(sourceWidth, sourceHeight);
+ // case is "all of it".
+ Rect layerStackRect(mainDpyInfo.w, mainDpyInfo.h);
// We need to preserve the aspect ratio of the display.
- float displayAspect = (float) sourceHeight / (float) sourceWidth;
+ float displayAspect = (float) mainDpyInfo.h / (float) mainDpyInfo.w;
// Set the way we map the output onto the display surface (which will
@@ -334,6 +315,22 @@
}
/*
+ * Set the main display width and height to the actual width and height
+ */
+static status_t getActualDisplaySize(const sp<IBinder>& mainDpy, DisplayInfo* mainDpyInfo) {
+ Rect viewport;
+ status_t err = SurfaceComposerClient::getDisplayViewport(mainDpy, &viewport);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to get display viewport\n");
+ return err;
+ }
+ mainDpyInfo->w = viewport.width();
+ mainDpyInfo->h = viewport.height();
+
+ return NO_ERROR;
+}
+
+/*
* Runs the MediaCodec encoder, sending the output to the MediaMuxer. The
* input frames are coming from the virtual display as fast as SurfaceFlinger
* wants to send them.
@@ -403,14 +400,22 @@
// useful stuff is hard to get at without a Dalvik VM.
err = SurfaceComposerClient::getDisplayInfo(mainDpy,
&mainDpyInfo);
- if (err != NO_ERROR) {
+ if (err == NO_ERROR) {
+ err = getActualDisplaySize(mainDpy, &mainDpyInfo);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to set actual display size\n");
+ return err;
+ }
+
+ if (orientation != mainDpyInfo.orientation) {
+ ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
+ SurfaceComposerClient::Transaction t;
+ setDisplayProjection(t, virtualDpy, mainDpyInfo);
+ t.apply();
+ orientation = mainDpyInfo.orientation;
+ }
+ } else {
ALOGW("getDisplayInfo(main) failed: %d", err);
- } else if (orientation != mainDpyInfo.orientation) {
- ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
- SurfaceComposerClient::Transaction t;
- setDisplayProjection(t, virtualDpy, mainDpyInfo);
- t.apply();
- orientation = mainDpyInfo.orientation;
}
}
@@ -552,6 +557,10 @@
return rawFp;
}
+static inline uint32_t floorToEven(uint32_t num) {
+ return num & ~1;
+}
+
/*
* Main "do work" start point.
*
@@ -579,6 +588,13 @@
fprintf(stderr, "ERROR: unable to get display characteristics\n");
return err;
}
+
+ err = getActualDisplaySize(mainDpy, &mainDpyInfo);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: unable to set actual display size\n");
+ return err;
+ }
+
if (gVerbose) {
printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
@@ -586,12 +602,12 @@
fflush(stdout);
}
- bool rotated = isDeviceRotated(mainDpyInfo.orientation);
+ // Encoder can't take odd number as config
if (gVideoWidth == 0) {
- gVideoWidth = rotated ? mainDpyInfo.h : mainDpyInfo.w;
+ gVideoWidth = floorToEven(mainDpyInfo.w);
}
if (gVideoHeight == 0) {
- gVideoHeight = rotated ? mainDpyInfo.w : mainDpyInfo.h;
+ gVideoHeight = floorToEven(mainDpyInfo.h);
}
// Configure and start the encoder.
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 73ed8c3..1558e8b 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -118,9 +118,9 @@
status_t ClearKeyCasPlugin::closeSession(const CasSessionId &sessionId) {
ALOGV("closeSession: sessionId=%s", sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
@@ -132,9 +132,9 @@
const CasSessionId &sessionId, const CasData & /*data*/) {
ALOGV("setSessionPrivateData: sessionId=%s",
sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
return OK;
@@ -143,9 +143,9 @@
status_t ClearKeyCasPlugin::processEcm(
const CasSessionId &sessionId, const CasEcm& ecm) {
ALOGV("processEcm: sessionId=%s", sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
@@ -418,15 +418,15 @@
const CasSessionId &sessionId) {
ALOGV("setMediaCasSession: sessionId=%s", sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
ALOGE("ClearKeyDescramblerPlugin: session not found");
return ERROR_CAS_SESSION_NOT_OPENED;
}
- mCASSession = session;
+ std::atomic_store(&mCASSession, session);
return OK;
}
@@ -447,12 +447,14 @@
subSamplesToString(subSamples, numSubSamples).string(),
srcPtr, dstPtr, srcOffset, dstOffset);
- if (mCASSession == NULL) {
+ std::shared_ptr<ClearKeyCasSession> session = std::atomic_load(&mCASSession);
+
+ if (session.get() == nullptr) {
ALOGE("Uninitialized CAS session!");
return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
}
- return mCASSession->decrypt(
+ return session->decrypt(
secure, scramblingControl,
numSubSamples, subSamples,
(uint8_t*)srcPtr + srcOffset,
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
index 42cfb8f..389e172 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
@@ -120,7 +120,7 @@
AString *errorDetailMsg) override;
private:
- sp<ClearKeyCasSession> mCASSession;
+ std::shared_ptr<ClearKeyCasSession> mCASSession;
String8 subSamplesToString(
SubSample const *subSamples,
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
index 4b4051d..3bb1176 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
@@ -56,7 +56,7 @@
Mutex::Autolock lock(mSessionsLock);
- sp<ClearKeyCasSession> session = new ClearKeyCasSession(plugin);
+ std::shared_ptr<ClearKeyCasSession> session(new ClearKeyCasSession(plugin));
uint8_t *byteArray = (uint8_t *) &mNextSessionId;
sessionId->push_back(byteArray[3]);
@@ -69,7 +69,7 @@
return OK;
}
-sp<ClearKeyCasSession> ClearKeySessionLibrary::findSession(
+std::shared_ptr<ClearKeyCasSession> ClearKeySessionLibrary::findSession(
const CasSessionId& sessionId) {
Mutex::Autolock lock(mSessionsLock);
@@ -88,7 +88,7 @@
return;
}
- sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
+ std::shared_ptr<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
mIDToSessionMap.removeItemsAt(index);
}
@@ -96,7 +96,7 @@
Mutex::Autolock lock(mSessionsLock);
for (ssize_t index = (ssize_t)mIDToSessionMap.size() - 1; index >= 0; index--) {
- sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
+ std::shared_ptr<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
if (session->getPlugin() == plugin) {
mIDToSessionMap.removeItemsAt(index);
}
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
index 01f5f47..a537e63 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
@@ -32,6 +32,10 @@
class ClearKeyCasSession : public RefBase {
public:
+ explicit ClearKeyCasSession(CasPlugin *plugin);
+
+ virtual ~ClearKeyCasSession();
+
ssize_t decrypt(
bool secure,
DescramblerPlugin::ScramblingControl scramblingControl,
@@ -58,8 +62,6 @@
friend class ClearKeySessionLibrary;
- explicit ClearKeyCasSession(CasPlugin *plugin);
- virtual ~ClearKeyCasSession();
CasPlugin* getPlugin() const { return mPlugin; }
status_t decryptPayload(
const AES_KEY& key, size_t length, size_t offset, char* buffer) const;
@@ -73,7 +75,7 @@
status_t addSession(CasPlugin *plugin, CasSessionId *sessionId);
- sp<ClearKeyCasSession> findSession(const CasSessionId& sessionId);
+ std::shared_ptr<ClearKeyCasSession> findSession(const CasSessionId& sessionId);
void destroySession(const CasSessionId& sessionId);
@@ -85,7 +87,7 @@
Mutex mSessionsLock;
uint32_t mNextSessionId;
- KeyedVector<CasSessionId, sp<ClearKeyCasSession>> mIDToSessionMap;
+ KeyedVector<CasSessionId, std::shared_ptr<ClearKeyCasSession>> mIDToSessionMap;
ClearKeySessionLibrary();
DISALLOW_EVIL_CONSTRUCTORS(ClearKeySessionLibrary);
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
index e48e388..e215965 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2CCDecoder.cpp
@@ -372,10 +372,16 @@
timeUs, mDTVCCPacket->data(), mDTVCCPacket->size());
mDTVCCPacket->setRange(0, 0);
}
+ if (mDTVCCPacket->size() + 2 > mDTVCCPacket->capacity()) {
+ return false;
+ }
memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2);
mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2);
br.skipBits(16);
} else if (mDTVCCPacket->size() > 0 && cc_type == 2) {
+ if (mDTVCCPacket->size() + 2 > mDTVCCPacket->capacity()) {
+ return false;
+ }
memcpy(mDTVCCPacket->data() + mDTVCCPacket->size(), br.data(), 2);
mDTVCCPacket->setRange(0, mDTVCCPacket->size() + 2);
br.skipBits(16);
@@ -403,6 +409,9 @@
line21CCBuf = new ABuffer((cc_count - i) * sizeof(CCData));
line21CCBuf->setRange(0, 0);
}
+ if (line21CCBuf->size() + sizeof(cc) > line21CCBuf->capacity()) {
+ return false;
+ }
memcpy(line21CCBuf->data() + line21CCBuf->size(), &cc, sizeof(cc));
line21CCBuf->setRange(0, line21CCBuf->size() + sizeof(CCData));
}
@@ -464,6 +473,9 @@
size_t trackIndex = getTrackIndex(kTrackTypeCEA708, service_number, &trackAdded);
if (mSelectedTrack == (ssize_t)trackIndex) {
sp<ABuffer> ccPacket = new ABuffer(block_size);
+ if (ccPacket->capacity() == 0) {
+ return false;
+ }
memcpy(ccPacket->data(), br.data(), block_size);
mCCMap.add(timeUs, ccPacket);
}
@@ -527,10 +539,12 @@
ccBuf = new ABuffer(size);
ccBuf->setRange(0, 0);
- for (ssize_t i = 0; i <= index; ++i) {
- sp<ABuffer> buf = mCCMap.valueAt(i);
- memcpy(ccBuf->data() + ccBuf->size(), buf->data(), buf->size());
- ccBuf->setRange(0, ccBuf->size() + buf->size());
+ if (ccBuf->capacity() > 0) {
+ for (ssize_t i = 0; i <= index; ++i) {
+ sp<ABuffer> buf = mCCMap.valueAt(i);
+ memcpy(ccBuf->data() + ccBuf->size(), buf->data(), buf->size());
+ ccBuf->setRange(0, ccBuf->size() + buf->size());
+ }
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index a5f5fc6..0784939 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1120,6 +1120,7 @@
} else if (what == DecoderBase::kWhatShutdownCompleted) {
ALOGV("%s shutdown completed", audio ? "audio" : "video");
if (audio) {
+ Mutex::Autolock autoLock(mDecoderLock);
mAudioDecoder.clear();
mAudioDecoderError = false;
++mAudioDecoderGeneration;
@@ -1127,6 +1128,7 @@
CHECK_EQ((int)mFlushingAudio, (int)SHUTTING_DOWN_DECODER);
mFlushingAudio = SHUT_DOWN;
} else {
+ Mutex::Autolock autoLock(mDecoderLock);
mVideoDecoder.clear();
mVideoDecoderError = false;
++mVideoDecoderGeneration;
@@ -1447,29 +1449,6 @@
break;
}
- case kWhatGetStats:
- {
- ALOGV("kWhatGetStats");
-
- Vector<sp<AMessage>> *trackStats;
- CHECK(msg->findPointer("trackstats", (void**)&trackStats));
-
- trackStats->clear();
- if (mVideoDecoder != NULL) {
- trackStats->push_back(mVideoDecoder->getStats());
- }
- if (mAudioDecoder != NULL) {
- trackStats->push_back(mAudioDecoder->getStats());
- }
-
- // respond for synchronization
- sp<AMessage> response = new AMessage;
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
- break;
- }
-
default:
TRESPASS();
break;
@@ -1817,6 +1796,7 @@
(long long)currentPositionUs, forceNonOffload, needsToCreateAudioDecoder);
if (mAudioDecoder != NULL) {
mAudioDecoder->pause();
+ Mutex::Autolock autoLock(mDecoderLock);
mAudioDecoder.clear();
mAudioDecoderError = false;
++mAudioDecoderGeneration;
@@ -1935,6 +1915,8 @@
}
}
+ Mutex::Autolock autoLock(mDecoderLock);
+
if (audio) {
sp<AMessage> notify = new AMessage(kWhatAudioNotify, this);
++mAudioDecoderGeneration;
@@ -2236,13 +2218,15 @@
void NuPlayer::getStats(Vector<sp<AMessage> > *trackStats) {
CHECK(trackStats != NULL);
- ALOGV("NuPlayer::getStats()");
- sp<AMessage> msg = new AMessage(kWhatGetStats, this);
- msg->setPointer("trackstats", trackStats);
+ trackStats->clear();
- sp<AMessage> response;
- (void) msg->postAndAwaitResponse(&response);
- // response is for synchronization, ignore contents
+ Mutex::Autolock autoLock(mDecoderLock);
+ if (mVideoDecoder != NULL) {
+ trackStats->push_back(mVideoDecoder->getStats());
+ }
+ if (mAudioDecoder != NULL) {
+ trackStats->push_back(mAudioDecoder->getStats());
+ }
}
sp<MetaData> NuPlayer::getFileMeta() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index e400d16..9f5be06 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -159,7 +159,6 @@
kWhatPrepareDrm = 'pDrm',
kWhatReleaseDrm = 'rDrm',
kWhatMediaClockNotify = 'mckN',
- kWhatGetStats = 'gSts',
};
wp<NuPlayerDriver> mDriver;
@@ -175,6 +174,7 @@
sp<DecoderBase> mVideoDecoder;
bool mOffloadAudio;
sp<DecoderBase> mAudioDecoder;
+ Mutex mDecoderLock; // guard |mAudioDecoder| and |mVideoDecoder|.
sp<CCDecoder> mCCDecoder;
sp<Renderer> mRenderer;
sp<ALooper> mRendererLooper;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index db37021..41f5db0 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -217,6 +217,7 @@
mNumFramesReceived(0),
mLastFrameTimestampUs(0),
mStarted(false),
+ mEos(false),
mNumFramesEncoded(0),
mTimeBetweenFrameCaptureUs(0),
mFirstFrameTimeUs(0),
@@ -880,6 +881,7 @@
{
Mutex::Autolock autoLock(mLock);
mStarted = false;
+ mEos = false;
mStopSystemTimeUs = -1;
mFrameAvailableCondition.signal();
@@ -1075,7 +1077,7 @@
{
Mutex::Autolock autoLock(mLock);
- while (mStarted && mFramesReceived.empty()) {
+ while (mStarted && !mEos && mFramesReceived.empty()) {
if (NO_ERROR !=
mFrameAvailableCondition.waitRelative(mLock,
mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
@@ -1091,6 +1093,9 @@
if (!mStarted) {
return OK;
}
+ if (mFramesReceived.empty()) {
+ return ERROR_END_OF_STREAM;
+ }
frame = *mFramesReceived.begin();
mFramesReceived.erase(mFramesReceived.begin());
@@ -1129,6 +1134,8 @@
if (mStopSystemTimeUs != -1 && timestampUs >= mStopSystemTimeUs) {
ALOGV("Drop Camera frame at %lld stop time: %lld us",
(long long)timestampUs, (long long)mStopSystemTimeUs);
+ mEos = true;
+ mFrameAvailableCondition.signal();
return true;
}
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index 29a219f..6e94517 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -301,10 +301,13 @@
err = mSource->read(&mediaBuffer, &mReadOptions);
mReadOptions.clearSeekTo();
if (err != OK) {
- ALOGW("Input Error or EOS");
mHaveMoreInputs = false;
if (!mFirstSample && err == ERROR_END_OF_STREAM) {
+ (void)mDecoder->queueInputBuffer(
+ index, 0, 0, 0, MediaCodec::BUFFER_FLAG_EOS);
err = OK;
+ } else {
+ ALOGW("Input Error: err=%d", err);
}
break;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 72eff94..353e407 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -860,7 +860,15 @@
}
//static
-sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
+sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, const char *owner) {
+ if (owner) {
+ if (strncmp(owner, "default", 8) == 0) {
+ return new ACodec;
+ } else if (strncmp(owner, "codec2", 7) == 0) {
+ return CreateCCodec();
+ }
+ }
+
if (name.startsWithIgnoreCase("c2.")) {
return CreateCCodec();
} else if (name.startsWithIgnoreCase("omx.")) {
@@ -884,11 +892,6 @@
// we need to invest in an extra looper to free the main event
// queue.
- mCodec = GetCodecBase(name);
- if (mCodec == NULL) {
- return NAME_NOT_FOUND;
- }
-
mCodecInfo.clear();
bool secureCodec = false;
@@ -922,6 +925,11 @@
return NAME_NOT_FOUND;
}
+ mCodec = GetCodecBase(name, mCodecInfo->getOwnerName());
+ if (mCodec == NULL) {
+ return NAME_NOT_FOUND;
+ }
+
if (mIsVideo) {
// video codec needs dedicated looper
if (mCodecLooper == NULL) {
@@ -1935,7 +1943,9 @@
mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());
}
- if (mComponentName.startsWith("OMX.google.")) {
+ const char *owner = mCodecInfo->getOwnerName();
+ if (mComponentName.startsWith("OMX.google.")
+ && (owner == nullptr || strncmp(owner, "default", 8) == 0)) {
mFlags |= kFlagUsesSoftwareRenderer;
} else {
mFlags &= ~kFlagUsesSoftwareRenderer;
diff --git a/media/libstagefright/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp
index 6819bba..9020fc1 100644
--- a/media/libstagefright/VideoFrameScheduler.cpp
+++ b/media/libstagefright/VideoFrameScheduler.cpp
@@ -475,7 +475,16 @@
nextVsyncTime += mVsyncPeriod;
if (vsyncsForLastFrame < ULONG_MAX)
++vsyncsForLastFrame;
+ } else if (mTimeCorrection < -correctionLimit * 2
+ || mTimeCorrection > correctionLimit * 2) {
+ ALOGW("correction beyond limit: %lld vs %lld (vsyncs for last frame: %zu, min: %zu)"
+ " restarting. render=%lld",
+ (long long)mTimeCorrection, (long long)correctionLimit,
+ vsyncsForLastFrame, minVsyncsPerFrame, (long long)origRenderTime);
+ restart();
+ return origRenderTime;
}
+
ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame);
}
mLastVsyncTime = nextVsyncTime;
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index 26bc25c..df6cd03 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -299,7 +299,11 @@
}
- bytes_to_discard = pVars->frame_start - pVars->sideInfo.main_data_begin - main_data_end;
+ // force signed computation; buffer sizes and offsets are all going to be
+ // well within the constraints of 32-bit signed math.
+ bytes_to_discard = pVars->frame_start
+ - ((int32)pVars->sideInfo.main_data_begin)
+ - ((int32)main_data_end);
if (main_data_end > BUFSIZE) /* check overflow on the buffer */
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp
index 7eaa860..e55c2e7 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_get_side_info.cpp
@@ -154,7 +154,7 @@
tmp = getbits_crc(inputStream, 22, crc, info->error_protection);
si->ch[ch].gran[gr].big_values = (tmp << 10) >> 23; /* 9 */
- si->ch[ch].gran[gr].global_gain = ((tmp << 19) >> 24) - 210; /* 8 */
+ si->ch[ch].gran[gr].global_gain = (int32)((tmp << 19) >> 24) - 210; /* 8 */
si->ch[ch].gran[gr].scalefac_compress = (tmp << 27) >> 28; /* 4 */
si->ch[ch].gran[gr].window_switching_flag = tmp & 1; /* 1 */
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_stereo_proc.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_stereo_proc.cpp
index 10edfc3..4338c43 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_stereo_proc.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_stereo_proc.cpp
@@ -178,6 +178,10 @@
; FUNCTION CODE
----------------------------------------------------------------------------*/
+#if __has_attribute(no_sanitize)
+// deliberately playing near overflow points of int32
+__attribute__((no_sanitize("integer")))
+#endif
void pvmp3_st_mid_side(int32 xr[SUBBANDS_NUMBER*FILTERBANK_BANDS],
int32 xl[SUBBANDS_NUMBER*FILTERBANK_BANDS],
int32 Start,
diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
index f173e0f..06b15b3 100644
--- a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
+++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp
@@ -689,7 +689,6 @@
notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL);
return;
}
- mIsCodecConfigFlushRequired = true;
}
if (!mSampFreq || !mNumChannels) {
@@ -713,10 +712,14 @@
signed int bytesConsumed = 0;
int errorCode = 0;
if (mIsCodecInitialized) {
+ mIsCodecConfigFlushRequired = true;
errorCode =
decodeXAACStream(inBuffer, inBufferLength, &bytesConsumed, &numOutBytes);
- } else {
+ } else if (!mIsCodecConfigFlushRequired) {
ALOGW("Assumption that first frame after header initializes decoder failed!");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, -1, NULL);
+ return;
}
inHeader->nFilledLen -= bytesConsumed;
inHeader->nOffset += bytesConsumed;
diff --git a/media/libstagefright/include/media/stagefright/CameraSource.h b/media/libstagefright/include/media/stagefright/CameraSource.h
index 475976b..3037b72 100644
--- a/media/libstagefright/include/media/stagefright/CameraSource.h
+++ b/media/libstagefright/include/media/stagefright/CameraSource.h
@@ -204,6 +204,7 @@
int32_t mNumFramesReceived;
int64_t mLastFrameTimestampUs;
bool mStarted;
+ bool mEos;
int32_t mNumFramesEncoded;
// Time between capture of two frames.
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index ad02004..7f6aae6 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -377,7 +377,7 @@
MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
- static sp<CodecBase> GetCodecBase(const AString &name);
+ static sp<CodecBase> GetCodecBase(const AString &name, const char *owner = nullptr);
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index a0f2a13..32113c2 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1610,12 +1610,15 @@
}
BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+ // Invalidate buffers in the client side first before calling OMX_FreeBuffer.
+ // If not, pending events in the client side might access the buffers after free.
+ invalidateBufferID(buffer);
+
OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header);
CLOG_IF_ERROR(freeBuffer, err, "%s:%u %#x", portString(portIndex), portIndex, buffer);
delete buffer_meta;
buffer_meta = NULL;
- invalidateBufferID(buffer);
return StatusFromOMXError(err);
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
index d34214b..2206526 100644
--- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "APM::SessionRoute"
+#define LOG_TAG "APM_SessionRoute"
//#define LOG_NDEBUG 0
#include "SessionRoute.h"
@@ -122,19 +122,17 @@
audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType,
const DeviceVector& availableDevices)
{
- audio_devices_t device = AUDIO_DEVICE_NONE;
-
for (size_t index = 0; index < size(); index++) {
sp<SessionRoute> route = valueAt(index);
if (streamType == route->mStreamType && route->isActiveOrChanged()
&& route->mDeviceDescriptor != 0) {
- device = route->mDeviceDescriptor->type();
+ audio_devices_t device = route->mDeviceDescriptor->type();
if (!availableDevices.getDevicesFromType(device).isEmpty()) {
- break;
+ return device;
}
}
}
- return device;
+ return AUDIO_DEVICE_NONE;
}
} // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index d00d5fa..9d618b0 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4882,11 +4882,15 @@
// use device for strategy DTMF
// 9: the strategy for beacon, a.k.a. "transmitted through speaker" is active on the output:
// use device for strategy t-t-s
+
+ // FIXME: extend use of isStrategyActiveOnSameModule() to all strategies
+ // with a refined rule considering mutually exclusive devices (using same backend)
+ // as opposed to all streams on the same audio HAL module.
if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE) &&
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
} else if (isInCall() ||
- isStrategyActive(outputDesc, STRATEGY_PHONE)) {
+ isStrategyActiveOnSameModule(outputDesc, STRATEGY_PHONE)) {
device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
} else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION)) {
device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
@@ -5894,6 +5898,20 @@
return false;
}
+bool AudioPolicyManager::isStrategyActiveOnSameModule(const sp<AudioOutputDescriptor>& outputDesc,
+ routing_strategy strategy, uint32_t inPastMs,
+ nsecs_t sysTime) const
+{
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ if (outputDesc->sharesHwModuleWith(desc)
+ && isStrategyActive(desc, strategy, inPastMs, sysTime)) {
+ return true;
+ }
+ }
+ return false;
+}
+
audio_policy_forced_cfg_t AudioPolicyManager::getForceUse(audio_policy_force_use_t usage)
{
return mEngine->getForceUse(usage);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index b954714..48e0472 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -321,6 +321,10 @@
bool isStrategyActive(const sp<AudioOutputDescriptor>& outputDesc, routing_strategy strategy,
uint32_t inPastMs = 0, nsecs_t sysTime = 0) const;
+ bool isStrategyActiveOnSameModule(const sp<AudioOutputDescriptor>& outputDesc,
+ routing_strategy strategy, uint32_t inPastMs = 0,
+ nsecs_t sysTime = 0) const;
+
// change the route of the specified output. Returns the number of ms we have slept to
// allow new routing to take effect in certain cases.
virtual uint32_t setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index c41de82..2bf42b6 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -2434,7 +2434,8 @@
return isUidActiveLocked(uid, callingPackage);
}
-static const int kPollUidActiveTimeoutMillis = 50;
+static const int64_t kPollUidActiveTimeoutTotalMillis = 300;
+static const int64_t kPollUidActiveTimeoutMillis = 50;
bool CameraService::UidPolicy::isUidActiveLocked(uid_t uid, String16 callingPackage) {
// Non-app UIDs are considered always active
@@ -2462,7 +2463,8 @@
// activity being resumed. The proper fix is very risky, so we temporary add
// some polling which should happen pretty rarely anyway as the race is hard
// to hit.
- active = am.isUidActive(uid, callingPackage);
+ active = mActiveUids.find(uid) != mActiveUids.end();
+ if (!active) active = am.isUidActive(uid, callingPackage);
if (active) {
break;
}
@@ -2470,11 +2472,15 @@
startTimeMillis = uptimeMillis();
}
int64_t ellapsedTimeMillis = uptimeMillis() - startTimeMillis;
- int64_t remainingTimeMillis = kPollUidActiveTimeoutMillis - ellapsedTimeMillis;
+ int64_t remainingTimeMillis = kPollUidActiveTimeoutTotalMillis - ellapsedTimeMillis;
if (remainingTimeMillis <= 0) {
break;
}
+ remainingTimeMillis = std::min(kPollUidActiveTimeoutMillis, remainingTimeMillis);
+
+ mUidLock.unlock();
usleep(remainingTimeMillis * 1000);
+ mUidLock.lock();
} while (true);
if (active) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 65faac9..d59b313 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -102,7 +102,7 @@
{
SharedParameters::Lock l(mParameters);
- res = l.mParameters.initialize(&(mDevice->info()), mDeviceVersion);
+ res = l.mParameters.initialize(mDevice.get(), mDeviceVersion);
if (res != OK) {
ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
@@ -250,6 +250,7 @@
switch (p.sceneMode) {
case ANDROID_CONTROL_SCENE_MODE_DISABLED:
result.append("AUTO\n"); break;
+ CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY)
CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_ACTION)
CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PORTRAIT)
CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_LANDSCAPE)
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index d66dec4..28d186a 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -41,23 +41,29 @@
int cameraFacing) :
cameraId(cameraId),
cameraFacing(cameraFacing),
- info(NULL) {
+ info(NULL),
+ mDefaultSceneMode(ANDROID_CONTROL_SCENE_MODE_DISABLED) {
}
Parameters::~Parameters() {
}
-status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) {
+status_t Parameters::initialize(CameraDeviceBase *device, int deviceVersion) {
status_t res;
+ if (device == nullptr) {
+ ALOGE("%s: device is null!", __FUNCTION__);
+ return BAD_VALUE;
+ }
- if (info->entryCount() == 0) {
+ const CameraMetadata& info = device->info();
+ if (info.entryCount() == 0) {
ALOGE("%s: No static information provided!", __FUNCTION__);
return BAD_VALUE;
}
- Parameters::info = info;
+ Parameters::info = &info;
mDeviceVersion = deviceVersion;
- res = buildFastInfo();
+ res = buildFastInfo(device);
if (res != OK) return res;
res = buildQuirks();
@@ -557,6 +563,10 @@
noSceneModes = true;
break;
case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY:
+ // Face priority can be used as alternate default if supported.
+ // Per API contract it shouldn't override the user set flash,
+ // white balance and focus modes.
+ mDefaultSceneMode = availableSceneModes.data.u8[i];
// Not in old API
addComma = false;
break;
@@ -761,17 +771,7 @@
focusingAreas.clear();
focusingAreas.add(Parameters::Area(0,0,0,0,0));
- if (fastInfo.isExternalCamera) {
- params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, -1.0);
- } else {
- camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, false);
- if (!availableFocalLengths.count) return NO_INIT;
-
- float minFocalLength = availableFocalLengths.data.f[0];
- params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength);
- }
-
+ params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, fastInfo.defaultFocalLength);
float horizFov, vertFov;
res = calculatePictureFovs(&horizFov, &vertFov);
@@ -993,7 +993,7 @@
return paramsFlattened;
}
-status_t Parameters::buildFastInfo() {
+status_t Parameters::buildFastInfo(CameraDeviceBase *device) {
camera_metadata_ro_entry_t activeArraySize =
staticInfo(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, 2, 4);
@@ -1109,20 +1109,12 @@
focusDistanceCalibration.data.u8[0] !=
ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
-
- camera_metadata_ro_entry_t hwLevel = staticInfo(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
- if (!hwLevel.count) return NO_INIT;
- fastInfo.isExternalCamera =
- hwLevel.data.u8[0] == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
-
- camera_metadata_ro_entry_t availableFocalLengths =
- staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, /*required*/false);
- if (!availableFocalLengths.count && !fastInfo.isExternalCamera) return NO_INIT;
+ res = getDefaultFocalLength(device);
+ if (res != OK) return res;
SortedVector<int32_t> availableFormats = getAvailableOutputFormats();
if (!availableFormats.size()) return NO_INIT;
-
if (sceneModeOverrides.count > 0) {
// sceneModeOverrides is defined to have 3 entries for each scene mode,
// which are AE, AWB, and AF override modes the HAL wants for that scene
@@ -1200,19 +1192,6 @@
fastInfo.bestFaceDetectMode = bestFaceDetectMode;
fastInfo.maxFaces = maxFaces;
- // Find smallest (widest-angle) focal length to use as basis of still
- // picture FOV reporting.
- if (fastInfo.isExternalCamera) {
- fastInfo.minFocalLength = -1.0;
- } else {
- fastInfo.minFocalLength = availableFocalLengths.data.f[0];
- for (size_t i = 1; i < availableFocalLengths.count; i++) {
- if (fastInfo.minFocalLength > availableFocalLengths.data.f[i]) {
- fastInfo.minFocalLength = availableFocalLengths.data.f[i];
- }
- }
- }
-
// Check if the HAL supports HAL_PIXEL_FORMAT_YCbCr_420_888
fastInfo.useFlexibleYuv = false;
for (size_t i = 0; i < availableFormats.size(); i++) {
@@ -1760,7 +1739,7 @@
// SCENE_MODE
validatedParams.sceneMode = sceneModeStringToEnum(
- newParams.get(CameraParameters::KEY_SCENE_MODE) );
+ newParams.get(CameraParameters::KEY_SCENE_MODE), mDefaultSceneMode);
if (validatedParams.sceneMode != sceneMode &&
validatedParams.sceneMode !=
ANDROID_CONTROL_SCENE_MODE_DISABLED) {
@@ -1778,7 +1757,7 @@
}
}
bool sceneModeSet =
- validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_DISABLED;
+ validatedParams.sceneMode != mDefaultSceneMode;
// FLASH_MODE
if (sceneModeSet) {
@@ -2157,7 +2136,7 @@
uint8_t reqSceneMode =
sceneModeActive ? sceneMode :
enableFaceDetect ? (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY :
- (uint8_t)ANDROID_CONTROL_SCENE_MODE_DISABLED;
+ mDefaultSceneMode;
res = request->update(ANDROID_CONTROL_SCENE_MODE,
&reqSceneMode, 1);
if (res != OK) return res;
@@ -2446,6 +2425,50 @@
return true;
}
+status_t Parameters::getDefaultFocalLength(CameraDeviceBase *device) {
+ if (device == nullptr) {
+ ALOGE("%s: Camera device is nullptr", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ camera_metadata_ro_entry_t hwLevel = staticInfo(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL);
+ if (!hwLevel.count) return NO_INIT;
+ fastInfo.isExternalCamera =
+ hwLevel.data.u8[0] == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
+
+ camera_metadata_ro_entry_t availableFocalLengths =
+ staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS, 0, 0, /*required*/false);
+ if (!availableFocalLengths.count && !fastInfo.isExternalCamera) return NO_INIT;
+
+ // Find focal length in PREVIEW template to use as default focal length.
+ if (fastInfo.isExternalCamera) {
+ fastInfo.defaultFocalLength = -1.0;
+ } else {
+ // Find smallest (widest-angle) focal length to use as basis of still
+ // picture FOV reporting.
+ fastInfo.defaultFocalLength = availableFocalLengths.data.f[0];
+ for (size_t i = 1; i < availableFocalLengths.count; i++) {
+ if (fastInfo.defaultFocalLength > availableFocalLengths.data.f[i]) {
+ fastInfo.defaultFocalLength = availableFocalLengths.data.f[i];
+ }
+ }
+
+ // Use focal length in preview template if it exists
+ CameraMetadata previewTemplate;
+ status_t res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW, &previewTemplate);
+ if (res != OK) {
+ ALOGE("%s: Failed to create default PREVIEW request: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ camera_metadata_entry entry = previewTemplate.find(ANDROID_LENS_FOCAL_LENGTH);
+ if (entry.count != 0) {
+ fastInfo.defaultFocalLength = entry.data.f[0];
+ }
+ }
+ return OK;
+}
+
const char* Parameters::getStateName(State state) {
#define CASE_ENUM_TO_CHAR(x) case x: return(#x); break;
switch(state) {
@@ -2589,12 +2612,12 @@
-1;
}
-int Parameters::sceneModeStringToEnum(const char *sceneMode) {
+int Parameters::sceneModeStringToEnum(const char *sceneMode, uint8_t defaultSceneMode) {
return
!sceneMode ?
- ANDROID_CONTROL_SCENE_MODE_DISABLED :
+ defaultSceneMode :
!strcmp(sceneMode, CameraParameters::SCENE_MODE_AUTO) ?
- ANDROID_CONTROL_SCENE_MODE_DISABLED :
+ defaultSceneMode :
!strcmp(sceneMode, CameraParameters::SCENE_MODE_ACTION) ?
ANDROID_CONTROL_SCENE_MODE_ACTION :
!strcmp(sceneMode, CameraParameters::SCENE_MODE_PORTRAIT) ?
@@ -3241,12 +3264,12 @@
if (horizFov != NULL) {
*horizFov = 180 / M_PI * 2 *
atanf(horizCropFactor * sensorSize.data.f[0] /
- (2 * fastInfo.minFocalLength));
+ (2 * fastInfo.defaultFocalLength));
}
if (vertFov != NULL) {
*vertFov = 180 / M_PI * 2 *
atanf(vertCropFactor * sensorSize.data.f[1] /
- (2 * fastInfo.minFocalLength));
+ (2 * fastInfo.defaultFocalLength));
}
return OK;
}
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 97f8ea7..42e7a47 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -30,6 +30,8 @@
#include <camera/CameraParameters2.h>
#include <camera/CameraMetadata.h>
+#include "common/CameraDeviceBase.h"
+
namespace android {
namespace camera2 {
@@ -241,7 +243,7 @@
};
DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides;
bool isExternalCamera;
- float minFocalLength;
+ float defaultFocalLength;
bool useFlexibleYuv;
Size maxJpegSize;
Size maxZslSize;
@@ -264,10 +266,10 @@
~Parameters();
// Sets up default parameters
- status_t initialize(const CameraMetadata *info, int deviceVersion);
+ status_t initialize(CameraDeviceBase *device, int deviceVersion);
// Build fast-access device static info from static info
- status_t buildFastInfo();
+ status_t buildFastInfo(CameraDeviceBase *device);
// Query for quirks from static info
status_t buildQuirks();
@@ -300,6 +302,9 @@
// whether zero shutter lag should be used for non-recording operation
bool useZeroShutterLag() const;
+ // Get default focal length
+ status_t getDefaultFocalLength(CameraDeviceBase *camera);
+
// Calculate the crop region rectangle, either tightly about the preview
// resolution, or a region just based on the active array; both take
// into account the current zoom level.
@@ -326,7 +331,7 @@
static const char* wbModeEnumToString(uint8_t wbMode);
static int effectModeStringToEnum(const char *effectMode);
static int abModeStringToEnum(const char *abMode);
- static int sceneModeStringToEnum(const char *sceneMode);
+ static int sceneModeStringToEnum(const char *sceneMode, uint8_t defaultScene);
static flashMode_t flashModeStringToEnum(const char *flashMode);
static const char* flashModeEnumToString(flashMode_t flashMode);
static focusMode_t focusModeStringToEnum(const char *focusMode);
@@ -434,6 +439,7 @@
Size getMaxSize(const Vector<Size>& sizes);
int mDeviceVersion;
+ uint8_t mDefaultSceneMode;
};
// This class encapsulates the Parameters class so that it can only be accessed
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 543914e..28ffc8b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2143,8 +2143,14 @@
res = stream->finishConfiguration();
if (res != OK) {
- SET_ERR_L("Can't finish configuring output stream %d: %s (%d)",
- stream->getId(), strerror(-res), res);
+ // If finishConfiguration fails due to abandoned surface, do not set
+ // device to error state.
+ bool isSurfaceAbandoned =
+ (res == NO_INIT || res == DEAD_OBJECT) && stream->isAbandoned();
+ if (!isSurfaceAbandoned) {
+ SET_ERR_L("Can't finish configuring output stream %d: %s (%d)",
+ stream->getId(), strerror(-res), res);
+ }
return res;
}
}
@@ -2361,9 +2367,16 @@
//present streams end up with outstanding buffers that will
//not get drained.
internalUpdateStatusLocked(STATUS_ACTIVE);
+ } else if (rc == DEAD_OBJECT) {
+ // DEAD_OBJECT can be returned if either the consumer surface is
+ // abandoned, or the HAL has died.
+ // - If the HAL has died, configureStreamsLocked call will set
+ // device to error state,
+ // - If surface is abandoned, we should not set device to error
+ // state.
+ ALOGE("Failed to re-configure camera due to abandoned surface");
} else {
- setErrorStateLocked("%s: Failed to re-configure camera: %d",
- __FUNCTION__, rc);
+ SET_ERR_L("Failed to re-configure camera: %d", rc);
}
} else {
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
@@ -2497,6 +2510,9 @@
CLOGE("Can't finish configuring input stream %d: %s (%d)",
mInputStream->getId(), strerror(-res), res);
cancelStreamsConfigurationLocked();
+ if ((res == NO_INIT || res == DEAD_OBJECT) && mInputStream->isAbandoned()) {
+ return DEAD_OBJECT;
+ }
return BAD_VALUE;
}
}
@@ -2510,6 +2526,9 @@
CLOGE("Can't finish configuring output stream %d: %s (%d)",
outputStream->getId(), strerror(-res), res);
cancelStreamsConfigurationLocked();
+ if ((res == NO_INIT || res == DEAD_OBJECT) && outputStream->isAbandoned()) {
+ return DEAD_OBJECT;
+ }
return BAD_VALUE;
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index b3c3717..2c020a2 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -564,7 +564,7 @@
// Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
// let prepareNextBuffer handle the error.)
- if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ if ((res == NO_INIT || res == DEAD_OBJECT) && mState == STATE_CONFIGURED) {
mState = STATE_ABANDONED;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 1105b75..6030d15 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -331,7 +331,14 @@
status_t res;
res = configureQueueLocked();
- if (res != OK) {
+ // configureQueueLocked could return error in case of abandoned surface.
+ // Treat as non-fatal error.
+ if (res == NO_INIT || res == DEAD_OBJECT) {
+ ALOGE("%s: Unable to configure stream %d queue (non-fatal): %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ mState = STATE_ABANDONED;
+ return res;
+ } else if (res != OK) {
ALOGE("%s: Unable to configure stream %d queue: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
mState = STATE_ERROR;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index a60cb56..4ddcf1a 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -482,6 +482,7 @@
// after the HAL has provided usage and max_buffers values. After this call,
// the stream must be ready to produce all buffers for registration with
// HAL.
+ // Returns NO_INIT or DEAD_OBJECT if the queue has been abandoned.
virtual status_t configureQueueLocked() = 0;
// Get the total number of buffers in the queue
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 59ac636..8a9402e 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -493,6 +493,10 @@
SP_LOGV("acquired buffer %" PRId64 " from input at slot %d",
bufferItem.mGraphicBuffer->getId(), bufferItem.mSlot);
+ if (bufferItem.mTransformToDisplayInverse) {
+ bufferItem.mTransform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ }
+
// Attach and queue the buffer to each of the outputs
BufferTracker& tracker = *(mBuffers[bufferId]);
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
index eef6658..4dafefd 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.cpp
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -49,7 +49,7 @@
};
// Only for capture result
-constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrect = {
+constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrectNoClamp = {
ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points
ANDROID_STATISTICS_FACE_LANDMARKS,
};
@@ -79,12 +79,21 @@
array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
if (array.count != 4) return BAD_VALUE;
- mArrayWidth = array.data.i32[2];
- mArrayHeight = array.data.i32[3];
+ float arrayX = static_cast<float>(array.data.i32[0]);
+ float arrayY = static_cast<float>(array.data.i32[1]);
+ mArrayWidth = static_cast<float>(array.data.i32[2]);
+ mArrayHeight = static_cast<float>(array.data.i32[3]);
array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- mActiveWidth = array.data.i32[2];
- mActiveHeight = array.data.i32[3];
+ if (array.count != 4) return BAD_VALUE;
+
+ float activeX = static_cast<float>(array.data.i32[0]);
+ float activeY = static_cast<float>(array.data.i32[1]);
+ mActiveWidth = static_cast<float>(array.data.i32[2]);
+ mActiveHeight = static_cast<float>(array.data.i32[3]);
+
+ mArrayDiffX = activeX - arrayX;
+ mArrayDiffY = activeY - arrayY;
return updateCalibration(deviceInfo);
}
@@ -111,22 +120,13 @@
if (weight == 0) {
continue;
}
- res = mapCorrectedToRaw(e.data.i32 + j, 2);
+ res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true);
if (res != OK) return res;
- for (size_t k = 0; k < 4; k+=2) {
- int32_t& x = e.data.i32[j + k];
- int32_t& y = e.data.i32[j + k + 1];
- // Clamp to within active array
- x = std::max(0, x);
- x = std::min(mActiveWidth - 1, x);
- y = std::max(0, y);
- y = std::min(mActiveHeight - 1, y);
- }
}
}
for (auto rect : kRequestRectsToCorrect) {
e = request->find(rect);
- res = mapCorrectedRectToRaw(e.data.i32, e.count / 4);
+ res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true);
if (res != OK) return res;
}
}
@@ -156,27 +156,18 @@
if (weight == 0) {
continue;
}
- res = mapRawToCorrected(e.data.i32 + j, 2);
+ res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true);
if (res != OK) return res;
- for (size_t k = 0; k < 4; k+=2) {
- int32_t& x = e.data.i32[j + k];
- int32_t& y = e.data.i32[j + k + 1];
- // Clamp to within active array
- x = std::max(0, x);
- x = std::min(mActiveWidth - 1, x);
- y = std::max(0, y);
- y = std::min(mActiveHeight - 1, y);
- }
}
}
for (auto rect : kResultRectsToCorrect) {
e = result->find(rect);
- res = mapRawRectToCorrected(e.data.i32, e.count / 4);
+ res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true);
if (res != OK) return res;
}
- for (auto pts : kResultPointsToCorrect) {
+ for (auto pts : kResultPointsToCorrectNoClamp) {
e = result->find(pts);
- res = mapRawToCorrected(e.data.i32, e.count / 2);
+ res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false);
if (res != OK) return res;
}
}
@@ -232,9 +223,12 @@
return OK;
}
-status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount) {
+status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount,
+ bool clamp, bool simple) {
if (!mValidMapping) return INVALID_OPERATION;
+ if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp);
+
if (!mValidGrids) {
status_t res = buildGrids();
if (res != OK) return res;
@@ -275,6 +269,12 @@
// Interpolate along left edge of corrected quad (which are axis-aligned) for y
float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]);
+ // Clamp to within active array
+ if (clamp) {
+ corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ }
+
coordPairs[i] = static_cast<int32_t>(std::round(corrX));
coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
}
@@ -282,7 +282,30 @@
return OK;
}
-status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) {
+status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
+ bool clamp) const {
+ if (!mValidMapping) return INVALID_OPERATION;
+
+ float scaleX = mActiveWidth / mArrayWidth;
+ float scaleY = mActiveHeight / mArrayHeight;
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ float x = coordPairs[i];
+ float y = coordPairs[i + 1];
+ float corrX = x * scaleX;
+ float corrY = y * scaleY;
+ if (clamp) {
+ corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ }
+ coordPairs[i] = static_cast<int32_t>(std::round(corrX));
+ coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
+ }
+
+ return OK;
+}
+
+status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
+ bool simple) {
if (!mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, r, b)
@@ -293,7 +316,7 @@
rects[i + 1] + rects[i + 3]
};
- mapRawToCorrected(coords, 2);
+ mapRawToCorrected(coords, 2, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -305,14 +328,24 @@
return OK;
}
+status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp,
+ bool simple) const {
+ return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple);
+}
+
template<typename T>
-status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) const {
+status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp,
+ bool simple) const {
if (!mValidMapping) return INVALID_OPERATION;
+ if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp);
+
+ float activeCx = mCx - mArrayDiffX;
+ float activeCy = mCy - mArrayDiffY;
for (int i = 0; i < coordCount * 2; i += 2) {
- // Move to normalized space
- float ywi = (coordPairs[i + 1] - mCy) * mInvFy;
- float xwi = (coordPairs[i] - mCx - mS * ywi) * mInvFx;
+ // Move to normalized space from active array space
+ float ywi = (coordPairs[i + 1] - activeCy) * mInvFy;
+ float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx;
// Apply distortion model to calculate raw image coordinates
float rSq = xwi * xwi + ywi * ywi;
float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq);
@@ -321,6 +354,11 @@
// Move back to image space
float xr = mFx * xc + mS * yc + mCx;
float yr = mFy * yc + mCy;
+ // Clamp to within pre-correction active array
+ if (clamp) {
+ xr = std::min(mArrayWidth - 1, std::max(0.f, xr));
+ yr = std::min(mArrayHeight - 1, std::max(0.f, yr));
+ }
coordPairs[i] = static_cast<T>(std::round(xr));
coordPairs[i + 1] = static_cast<T>(std::round(yr));
@@ -329,10 +367,32 @@
return OK;
}
-template status_t DistortionMapper::mapCorrectedToRaw(int32_t*, int) const;
-template status_t DistortionMapper::mapCorrectedToRaw(float*, int) const;
+template<typename T>
+status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount,
+ bool clamp) const {
+ if (!mValidMapping) return INVALID_OPERATION;
-status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) const {
+ float scaleX = mArrayWidth / mActiveWidth;
+ float scaleY = mArrayHeight / mActiveHeight;
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ float x = coordPairs[i];
+ float y = coordPairs[i + 1];
+ float rawX = x * scaleX;
+ float rawY = y * scaleY;
+ if (clamp) {
+ rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX));
+ rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY));
+ }
+ coordPairs[i] = static_cast<T>(std::round(rawX));
+ coordPairs[i + 1] = static_cast<T>(std::round(rawY));
+ }
+
+ return OK;
+}
+
+
+status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
+ bool simple) const {
if (!mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
@@ -344,7 +404,7 @@
rects[i + 1] + rects[i + 3]
};
- mapCorrectedToRaw(coords, 2);
+ mapCorrectedToRaw(coords, 2, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -380,7 +440,8 @@
};
mDistortedGrid[index].src = &mCorrectedGrid[index];
mDistortedGrid[index].coords = mCorrectedGrid[index].coords;
- status_t res = mapCorrectedToRaw(mDistortedGrid[index].coords.data(), 4);
+ status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4,
+ /*clamp*/false, /*simple*/false);
if (res != OK) return res;
}
}
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.h b/services/camera/libcameraservice/device3/DistortionMapper.h
index 00cbd32..4c0a1a6 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.h
+++ b/services/camera/libcameraservice/device3/DistortionMapper.h
@@ -73,8 +73,11 @@
*
* coordPairs: A pointer to an array of consecutive (x,y) points
* coordCount: Number of (x,y) pairs to transform
+ * clamp: Whether to clamp the result to the bounds of the active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawToCorrected(int32_t *coordPairs, int coordCount);
+ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp,
+ bool simple = true);
/**
* Transform from distorted (original) to corrected (warped) coordinates.
@@ -82,8 +85,11 @@
*
* rects: A pointer to an array of consecutive (x,y, w, h) rectangles
* rectCount: Number of rectangles to transform
+ * clamp: Whether to clamp the result to the bounds of the active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawRectToCorrected(int32_t *rects, int rectCount);
+ status_t mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
+ bool simple = true);
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -91,9 +97,11 @@
*
* coordPairs: A pointer to an array of consecutive (x,y) points
* coordCount: Number of (x,y) pairs to transform
+ * clamp: Whether to clamp the result to the bounds of the precorrection active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- template<typename T>
- status_t mapCorrectedToRaw(T* coordPairs, int coordCount) const;
+ status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount, bool clamp,
+ bool simple = true) const;
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -101,8 +109,11 @@
*
* rects: A pointer to an array of consecutive (x,y, w, h) rectangles
* rectCount: Number of rectangles to transform
+ * clamp: Whether to clamp the result to the bounds of the precorrection active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount) const;
+ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
+ bool simple = true) const;
struct GridQuad {
// Source grid quad, or null
@@ -150,8 +161,18 @@
// Only capture result
static const std::array<uint32_t, 1> kResultRectsToCorrect;
- // Only for capture results
- static const std::array<uint32_t, 2> kResultPointsToCorrect;
+ // Only for capture results; don't clamp
+ static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp;
+
+ // Single implementation for various mapCorrectedToRaw methods
+ template<typename T>
+ status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const;
+
+ // Simple linear interpolation option
+ template<typename T>
+ status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount, bool clamp) const;
+
+ status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const;
// Utility to create reverse mapping grids
status_t buildGrids();
@@ -168,9 +189,11 @@
float mK[5];
// pre-correction active array dimensions
- int mArrayWidth, mArrayHeight;
+ float mArrayWidth, mArrayHeight;
// active array dimensions
- int mActiveWidth, mActiveHeight;
+ float mActiveWidth, mActiveHeight;
+ // corner offsets between pre-correction and active arrays
+ float mArrayDiffX, mArrayDiffY;
std::vector<GridQuad> mCorrectedGrid;
std::vector<GridQuad> mDistortedGrid;
diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
index b489931..2a689c6 100644
--- a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
+++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
@@ -30,6 +30,7 @@
int32_t testActiveArray[] = {100, 100, 1000, 750};
+int32_t testPreCorrActiveArray[] = {90, 90, 1020, 770};
float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f };
@@ -45,14 +46,19 @@
};
-void setupTestMapper(DistortionMapper *m, float distortion[5]) {
+void setupTestMapper(DistortionMapper *m,
+ float distortion[5], float intrinsics[5],
+ int32_t activeArray[4], int32_t preCorrectionActiveArray[4]) {
CameraMetadata deviceInfo;
deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
- testActiveArray, 4);
+ preCorrectionActiveArray, 4);
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ activeArray, 4);
deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
- testICal, 5);
+ intrinsics, 5);
deviceInfo.update(ANDROID_LENS_DISTORTION,
distortion, 5);
@@ -89,6 +95,9 @@
ASSERT_FALSE(m.calibrationValid());
deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ testPreCorrActiveArray, 4);
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
testActiveArray, 4);
deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
@@ -118,17 +127,19 @@
status_t res;
DistortionMapper m;
- setupTestMapper(&m, identityDistortion);
+ setupTestMapper(&m, identityDistortion, testICal,
+ /*activeArray*/ testActiveArray,
+ /*preCorrectionActiveArray*/ testActiveArray);
auto coords = basicCoords;
- res = m.mapCorrectedToRaw(coords.data(), 5);
+ res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], basicCoords[i]);
}
- res = m.mapRawToCorrected(coords.data(), 5);
+ res = m.mapRawToCorrected(coords.data(), 5, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
@@ -137,18 +148,18 @@
std::array<int32_t, 8> rects = {
0, 0, 100, 100,
- testActiveArray[2] - 100, testActiveArray[3]-100, 100, 100
+ testActiveArray[2] - 101, testActiveArray[3] - 101, 100, 100
};
auto rectsOrig = rects;
- res = m.mapCorrectedRectToRaw(rects.data(), 2);
+ res = m.mapCorrectedRectToRaw(rects.data(), 2, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
EXPECT_EQ(rects[i], rectsOrig[i]);
}
- res = m.mapRawRectToCorrected(rects.data(), 2);
+ res = m.mapRawRectToCorrected(rects.data(), 2, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
@@ -156,23 +167,39 @@
}
}
-TEST(DistortionMapperTest, LargeTransform) {
+TEST(DistortionMapperTest, SimpleTransform) {
+ status_t res;
+
+ DistortionMapper m;
+ setupTestMapper(&m, identityDistortion, testICal,
+ /*activeArray*/ testActiveArray,
+ /*preCorrectionActiveArray*/ testPreCorrActiveArray);
+
+ auto coords = basicCoords;
+ res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true, /*simple*/true);
+ ASSERT_EQ(res, OK);
+
+ ASSERT_EQ(coords[0], 0); ASSERT_EQ(coords[1], 0);
+ ASSERT_EQ(coords[2], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[3], 0);
+ ASSERT_EQ(coords[4], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[5], testPreCorrActiveArray[3] - 1);
+ ASSERT_EQ(coords[6], 0); ASSERT_EQ(coords[7], testPreCorrActiveArray[3] - 1);
+ ASSERT_EQ(coords[8], testPreCorrActiveArray[2] / 2); ASSERT_EQ(coords[9], testPreCorrActiveArray[3] / 2);
+}
+
+
+void RandomTransformTest(::testing::Test *test,
+ int32_t* activeArray, DistortionMapper &m, bool clamp, bool simple) {
status_t res;
constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed
constexpr int bucketsPerPixel = 3; // Histogram granularity
unsigned int seed = 1234; // Ensure repeatability for debugging
- const size_t coordCount = 1e6; // Number of random test points
-
- float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01};
-
- DistortionMapper m;
- setupTestMapper(&m, bigDistortion);
+ const size_t coordCount = 1e5; // Number of random test points
std::default_random_engine gen(seed);
- std::uniform_int_distribution<int> x_dist(0, testActiveArray[2] - 1);
- std::uniform_int_distribution<int> y_dist(0, testActiveArray[3] - 1);
+ std::uniform_int_distribution<int> x_dist(0, activeArray[2] - 1);
+ std::uniform_int_distribution<int> y_dist(0, activeArray[3] - 1);
std::vector<int32_t> randCoords(coordCount * 2);
@@ -186,12 +213,12 @@
auto origCoords = randCoords;
base::Timer correctedToRawTimer;
- res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2);
+ res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, clamp, simple);
auto correctedToRawDurationMs = correctedToRawTimer.duration();
EXPECT_EQ(res, OK);
base::Timer rawToCorrectedTimer;
- res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2);
+ res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, clamp, simple);
auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration();
EXPECT_EQ(res, OK);
@@ -202,9 +229,9 @@
(std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(
rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count();
- RecordProperty("CorrectedToRawDurationPerCoordUs",
+ test->RecordProperty("CorrectedToRawDurationPerCoordUs",
base::StringPrintf("%f", correctedToRawDurationPerCoordUs));
- RecordProperty("RawToCorrectedDurationPerCoordUs",
+ test->RecordProperty("RawToCorrectedDurationPerCoordUs",
base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs));
// Calculate mapping errors after round trip
@@ -239,17 +266,61 @@
}
float rmsError = std::sqrt(totalErrorSq / randCoords.size());
- RecordProperty("RmsError", base::StringPrintf("%f", rmsError));
+ test->RecordProperty("RmsError", base::StringPrintf("%f", rmsError));
for (size_t i = 0; i < histogram.size(); i++) {
std::string label = base::StringPrintf("HistogramBin[%f,%f)",
(float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel);
- RecordProperty(label, histogram[i]);
+ test->RecordProperty(label, histogram[i]);
}
- RecordProperty("HistogramOutOfRange", outOfHistogram);
+ test->RecordProperty("HistogramOutOfRange", outOfHistogram);
+}
+
+// Test a realistic distortion function with matching calibration values, enforcing
+// clamping.
+TEST(DistortionMapperTest, DISABLED_SmallTransform) {
+ int32_t activeArray[] = {0, 8, 3278, 2450};
+ int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464};
+
+ float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431};
+ float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000};
+
+ DistortionMapper m;
+ setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray);
+
+ RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/false);
+}
+
+// Test a realistic distortion function with matching calibration values, enforcing
+// clamping, but using the simple linear transform
+TEST(DistortionMapperTest, SmallSimpleTransform) {
+ int32_t activeArray[] = {0, 8, 3278, 2450};
+ int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464};
+
+ float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431};
+ float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000};
+
+ DistortionMapper m;
+ setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray);
+
+ RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/true);
+}
+
+// Test a very large distortion function; the regions aren't valid for such a big transform,
+// so disable clamping. This test is just to verify round-trip math accuracy for big transforms
+TEST(DistortionMapperTest, LargeTransform) {
+ float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01};
+
+ DistortionMapper m;
+ setupTestMapper(&m, bigDistortion, testICal,
+ /*activeArray*/testActiveArray,
+ /*preCorrectionActiveArray*/testPreCorrActiveArray);
+
+ RandomTransformTest(this, testActiveArray, m, /*clamp*/false, /*simple*/false);
}
// Compare against values calculated by OpenCV
// undistortPoints() method, which is the same as mapRawToCorrected
+// Ignore clamping
// See script DistortionMapperComp.py
#include "DistortionMapperTest_OpenCvData.h"
@@ -262,11 +333,14 @@
const int32_t maxSqError = 2;
DistortionMapper m;
- setupTestMapper(&m, bigDistortion);
+ setupTestMapper(&m, bigDistortion, testICal,
+ /*activeArray*/testActiveArray,
+ /*preCorrectionActiveArray*/testActiveArray);
using namespace openCvData;
- res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2);
+ res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, /*clamp*/false,
+ /*simple*/false);
for (size_t i = 0; i < rawCoords.size(); i+=2) {
int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) +