Merge "Fix detected integer sanitization" into pi-dev
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 e90b8f8..c1f5ddc 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2312,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
@@ -2324,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
@@ -2418,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>
@@ -2446,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
@@ -3059,7 +3071,7 @@
* outputs will crop horizontally (pillarbox), and 16:9
* streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>If the coordinate system is android.sensor.info.activeArraysSize, the width and height
+ * <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>
@@ -4675,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>
*
@@ -4686,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,
@@ -4717,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
*/
@@ -4738,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
*/
@@ -5094,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>
@@ -5113,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
@@ -5416,16 +5458,34 @@
* 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>
+ * 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.</p>
+ * 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
*/
@@ -7157,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/drm/libmediadrm/ICrypto.cpp b/drm/libmediadrm/ICrypto.cpp
index 73ecda1..a2594aa 100644
--- a/drm/libmediadrm/ICrypto.cpp
+++ b/drm/libmediadrm/ICrypto.cpp
@@ -225,8 +225,13 @@
void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
uint32_t size = data.readInt32();
- vector.insertAt((size_t)0, size);
- data.read(vector.editArray(), size);
+ if (vector.insertAt((size_t)0, size) < 0) {
+ vector.clear();
+ }
+ if (data.read(vector.editArray(), size) != NO_ERROR) {
+ vector.clear();
+ android_errorWriteWithInfoLog(0x534e4554, "62872384", -1, NULL, 0);
+ }
}
void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
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/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/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/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 52791b9..8ab33f7 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -234,7 +234,11 @@
if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) {
const Media &item = mMediaItems.itemAt(i);
- *uri = item.makeURL(baseURL);
+ if (item.mURI.empty()) {
+ *uri = "";
+ } else {
+ *uri = item.makeURL(baseURL);
+ }
return true;
}
}
@@ -465,7 +469,7 @@
}
if ((*uri).empty()) {
- *uri = mItems.itemAt(index).mURI;
+ *uri = mItems.itemAt(index).makeURL(mBaseURI.c_str());
}
}
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 08ced79..32113c2 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1085,7 +1085,8 @@
}
case OMXBuffer::kBufferTypeANWBuffer: {
- if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer) {
+ if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer
+ && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
break;
}
return useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer);
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 6b20bca..42285f8 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -811,7 +811,13 @@
size_t *encryptedbytes) {
// size needed to store all the crypto data
- size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
+ size_t cryptosize;
+ // = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
+ if (__builtin_mul_overflow(sizeof(size_t) * 2, numsubsamples, &cryptosize) ||
+ __builtin_add_overflow(cryptosize, sizeof(AMediaCodecCryptoInfo), &cryptosize)) {
+ ALOGE("crypto size overflow");
+ return NULL;
+ }
AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
if (!ret) {
ALOGE("couldn't allocate %zu bytes", cryptosize);
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 4b767b5..9d618b0 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -861,6 +861,21 @@
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
}
+ // Set incall music only if device was explicitly set, and fallback to the device which is
+ // chosen by the engine if not.
+ // FIXME: provide a more generic approach which is not device specific and move this back
+ // to getOutputForDevice.
+ if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX &&
+ *stream == AUDIO_STREAM_MUSIC &&
+ audio_is_linear_pcm(config->format) &&
+ isInCall()) {
+ if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
+ } else {
+ device = mEngine->getDeviceForStrategy(strategy);
+ }
+ }
+
ALOGV("getOutputForAttr() device 0x%x, sampling rate %d, format %#x, channel mask %#x, "
"flags %#x",
device, config->sample_rate, config->format, config->channel_mask, *flags);
@@ -916,11 +931,6 @@
*flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_VOIP_RX |
AUDIO_OUTPUT_FLAG_DIRECT);
ALOGV("Set VoIP and Direct output flags for PCM format");
- } else if (device == AUDIO_DEVICE_OUT_TELEPHONY_TX &&
- stream == AUDIO_STREAM_MUSIC &&
- audio_is_linear_pcm(config->format) &&
- isInCall()) {
- *flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
}
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/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/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]) +