TEST_P(CameraFrameTest, GetFrame) { TEST_EXTENSION_FORKING_INIT; /* Submit a PREVIEW type request, then wait until we get the frame back */ CameraMetadata previewRequest; ASSERT_EQ(OK, mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, &previewRequest)); { Vector<uint8_t> outputStreamIds; outputStreamIds.push(mStreamId); ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, outputStreamIds)); if (CAMERA_FRAME_DEBUGGING) { int frameCount = 0; ASSERT_EQ(OK, previewRequest.update(ANDROID_REQUEST_FRAME_COUNT, &frameCount, 1)); } } if (CAMERA_FRAME_DEBUGGING) { previewRequest.dump(STDOUT_FILENO); } for (int i = 0; i < GetParam(); ++i) { ALOGV("Submitting capture request %d", i); CameraMetadata tmpRequest = previewRequest; ASSERT_EQ(OK, mDevice->capture(tmpRequest)); } for (int i = 0; i < GetParam(); ++i) { ALOGV("Reading capture request %d", i); ASSERT_EQ(OK, mDevice->waitForNextFrame(CAMERA_FRAME_TIMEOUT)); CameraMetadata frameMetadata; ASSERT_EQ(OK, mDevice->getNextFrame(&frameMetadata)); // wait for buffer to be available ASSERT_EQ(OK, mFrameListener->waitForFrame(CAMERA_FRAME_TIMEOUT)); ALOGV("We got the frame now"); // mark buffer consumed so producer can re-dequeue it CpuConsumer::LockedBuffer imgBuffer; ASSERT_EQ(OK, mCpuConsumer->lockNextBuffer(&imgBuffer)); ASSERT_EQ(OK, mCpuConsumer->unlockBuffer(imgBuffer)); } }
// TODO: move to Camera2ClientBase bool CameraDeviceClient::enforceRequestPermissions(CameraMetadata& metadata) { const int pid = IPCThreadState::self()->getCallingPid(); const int selfPid = getpid(); camera_metadata_entry_t entry; /** * Mixin default important security values * - android.led.transmit = defaulted ON */ CameraMetadata staticInfo = mDevice->info(); entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS); for(size_t i = 0; i < entry.count; ++i) { uint8_t led = entry.data.u8[i]; switch(led) { case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: { uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON; if (!metadata.exists(ANDROID_LED_TRANSMIT)) { metadata.update(ANDROID_LED_TRANSMIT, &transmitDefault, 1); } break; } } } // We can do anything! if (pid == selfPid) { return true; } /** * Permission check special fields in the request * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT */ entry = metadata.find(ANDROID_LED_TRANSMIT); if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) { String16 permissionString = String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED"); if (!checkCallingPermission(permissionString)) { const int uid = IPCThreadState::self()->getCallingUid(); ALOGE("Permission Denial: " "can't disable transmit LED pid=%d, uid=%d", pid, uid); return false; } } return true; }
status_t ZslProcessor::updateRequestWithDefaultStillRequest(CameraMetadata &request) const { sp<Camera2Client> client = mClient.promote(); if (client == 0) { ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId); return INVALID_OPERATION; } sp<Camera3Device> device = static_cast<Camera3Device*>(client->getCameraDevice().get()); if (device == 0) { ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); return INVALID_OPERATION; } CameraMetadata stillTemplate; device->createDefaultRequest(CAMERA3_TEMPLATE_STILL_CAPTURE, &stillTemplate); // Find some of the post-processing tags, and assign the value from template to the request. // Only check the aberration mode and noise reduction mode for now, as they are very important // for image quality. uint32_t postProcessingTags[] = { ANDROID_NOISE_REDUCTION_MODE, ANDROID_COLOR_CORRECTION_ABERRATION_MODE, ANDROID_COLOR_CORRECTION_MODE, ANDROID_TONEMAP_MODE, ANDROID_SHADING_MODE, ANDROID_HOT_PIXEL_MODE, ANDROID_EDGE_MODE }; camera_metadata_entry_t entry; for (size_t i = 0; i < sizeof(postProcessingTags) / sizeof(uint32_t); i++) { entry = stillTemplate.find(postProcessingTags[i]); if (entry.count > 0) { request.update(postProcessingTags[i], entry.data.u8, 1); } } return OK; }
status_t ZslProcessor3::pushToReprocess(int32_t requestId) { ALOGV("%s: Send in reprocess request with id %d", __FUNCTION__, requestId); Mutex::Autolock l(mInputMutex); status_t res; sp<Camera2Client> client = mClient.promote(); if (client == 0) { ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId); return INVALID_OPERATION; } IF_ALOGV() { dumpZslQueue(-1); } size_t metadataIdx; nsecs_t candidateTimestamp = getCandidateTimestampLocked(&metadataIdx); if (candidateTimestamp == -1) { ALOGE("%s: Could not find good candidate for ZSL reprocessing", __FUNCTION__); return NOT_ENOUGH_DATA; } res = mZslStream->enqueueInputBufferByTimestamp(candidateTimestamp, /*actualTimestamp*/NULL); if (res == mZslStream->NO_BUFFER_AVAILABLE) { ALOGV("%s: No ZSL buffers yet", __FUNCTION__); return NOT_ENOUGH_DATA; } else if (res != OK) { ALOGE("%s: Unable to push buffer for reprocessing: %s (%d)", __FUNCTION__, strerror(-res), res); return res; } { CameraMetadata request = mFrameList[metadataIdx]; // Verify that the frame is reasonable for reprocessing camera_metadata_entry_t entry; entry = request.find(ANDROID_CONTROL_AE_STATE); if (entry.count == 0) { ALOGE("%s: ZSL queue frame has no AE state field!", __FUNCTION__); return BAD_VALUE; } if (entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_CONVERGED && entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_LOCKED) { ALOGV("%s: ZSL queue frame AE state is %d, need full capture", __FUNCTION__, entry.data.u8[0]); return NOT_ENOUGH_DATA; } uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS; res = request.update(ANDROID_REQUEST_TYPE, &requestType, 1); int32_t inputStreams[1] = { mZslStreamId }; if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS, inputStreams, 1); // TODO: Shouldn't we also update the latest preview frame? int32_t outputStreams[1] = { client->getCaptureStreamId() }; if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS, outputStreams, 1); res = request.update(ANDROID_REQUEST_ID, &requestId, 1); if (res != OK ) { ALOGE("%s: Unable to update frame to a reprocess request", __FUNCTION__); return INVALID_OPERATION; } res = client->stopStream(); if (res != OK) { ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: " "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); return INVALID_OPERATION; } // Update JPEG settings { SharedParameters::Lock l(client->getParameters()); res = l.mParameters.updateRequestJpeg(&request); if (res != OK) { ALOGE("%s: Camera %d: Unable to update JPEG entries of ZSL " "capture request: %s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); return res; } } mLatestCapturedRequest = request; res = client->getCameraDevice()->capture(request); if (res != OK ) { ALOGE("%s: Unable to send ZSL reprocess request to capture: %s" " (%d)", __FUNCTION__, strerror(-res), res); return res; } mState = LOCKED; } return OK; }
void CameraModule::deriveCameraCharacteristicsKeys( uint32_t deviceVersion, CameraMetadata &chars) { // HAL1 devices should not reach here if (deviceVersion < CAMERA_DEVICE_API_VERSION_2_0) { ALOGV("%s: Cannot derive keys for HAL version < 2.0"); return; } // Keys added in HAL3.3 if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) { const size_t NUM_DERIVED_KEYS_HAL3_3 = 5; Vector<uint8_t> controlModes; uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE; chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1); data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE; chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1); controlModes.push(ANDROID_CONTROL_MODE_AUTO); camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) { controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE); } // Only advertise CONTROL_OFF mode if 3A manual controls are supported. bool isManualAeSupported = false; bool isManualAfSupported = false; bool isManualAwbSupported = false; entry = chars.find(ANDROID_CONTROL_AE_AVAILABLE_MODES); if (entry.count > 0) { for (size_t i = 0; i < entry.count; i++) { if (entry.data.u8[i] == ANDROID_CONTROL_AE_MODE_OFF) { isManualAeSupported = true; break; } } } entry = chars.find(ANDROID_CONTROL_AF_AVAILABLE_MODES); if (entry.count > 0) { for (size_t i = 0; i < entry.count; i++) { if (entry.data.u8[i] == ANDROID_CONTROL_AF_MODE_OFF) { isManualAfSupported = true; break; } } } entry = chars.find(ANDROID_CONTROL_AWB_AVAILABLE_MODES); if (entry.count > 0) { for (size_t i = 0; i < entry.count; i++) { if (entry.data.u8[i] == ANDROID_CONTROL_AWB_MODE_OFF) { isManualAwbSupported = true; break; } } } if (isManualAeSupported && isManualAfSupported && isManualAwbSupported) { controlModes.push(ANDROID_CONTROL_MODE_OFF); } chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes); entry = chars.find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS); // HAL3.2 devices passing existing CTS test should all support all LSC modes and LSC map bool lensShadingModeSupported = false; if (entry.count > 0) { for (size_t i = 0; i < entry.count; i++) { if (entry.data.i32[i] == ANDROID_SHADING_MODE) { lensShadingModeSupported = true; break; } } } Vector<uint8_t> lscModes; Vector<uint8_t> lscMapModes; lscModes.push(ANDROID_SHADING_MODE_FAST); lscModes.push(ANDROID_SHADING_MODE_HIGH_QUALITY); lscMapModes.push(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF); if (lensShadingModeSupported) { lscModes.push(ANDROID_SHADING_MODE_OFF); lscMapModes.push(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_ON); } chars.update(ANDROID_SHADING_AVAILABLE_MODES, lscModes); chars.update(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, lscMapModes); entry = chars.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS); Vector<int32_t> availableCharsKeys; availableCharsKeys.setCapacity(entry.count + NUM_DERIVED_KEYS_HAL3_3); for (size_t i = 0; i < entry.count; i++) { availableCharsKeys.push(entry.data.i32[i]); } availableCharsKeys.push(ANDROID_CONTROL_AE_LOCK_AVAILABLE); availableCharsKeys.push(ANDROID_CONTROL_AWB_LOCK_AVAILABLE); availableCharsKeys.push(ANDROID_CONTROL_AVAILABLE_MODES); availableCharsKeys.push(ANDROID_SHADING_AVAILABLE_MODES); availableCharsKeys.push(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES); chars.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, availableCharsKeys); // Need update android.control.availableHighSpeedVideoConfigurations since HAL3.3 // adds batch size to this array. entry = chars.find(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS); if (entry.count > 0) { Vector<int32_t> highSpeedConfig; for (size_t i = 0; i < entry.count; i += 4) { highSpeedConfig.add(entry.data.i32[i]); // width highSpeedConfig.add(entry.data.i32[i + 1]); // height highSpeedConfig.add(entry.data.i32[i + 2]); // fps_min highSpeedConfig.add(entry.data.i32[i + 3]); // fps_max highSpeedConfig.add(1); // batchSize_max. default to 1 for HAL3.2 } chars.update(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS, highSpeedConfig); } } // Always add a default for the pre-correction active array if the vendor chooses to omit this camera_metadata_entry entry = chars.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); if (entry.count == 0) { Vector<int32_t> preCorrectionArray; entry = chars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); preCorrectionArray.appendArray(entry.data.i32, entry.count); chars.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, preCorrectionArray); } return; }
status_t ZslProcessor::pushToReprocess(int32_t requestId) { ALOGV("%s: Send in reprocess request with id %d", __FUNCTION__, requestId); Mutex::Autolock l(mInputMutex); status_t res; sp<Camera2Client> client = mClient.promote(); if (client == 0) { ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId); return INVALID_OPERATION; } IF_ALOGV() { dumpZslQueue(-1); } if (mZslQueueTail != mZslQueueHead) { CameraMetadata request; size_t index = mZslQueueTail; while (index != mZslQueueHead) { if (!mZslQueue[index].frame.isEmpty()) { request = mZslQueue[index].frame; break; } index = (index + 1) % kZslBufferDepth; } if (index == mZslQueueHead) { ALOGV("%s: ZSL queue has no valid frames to send yet.", __FUNCTION__); return NOT_ENOUGH_DATA; } // Verify that the frame is reasonable for reprocessing camera_metadata_entry_t entry; entry = request.find(ANDROID_CONTROL_AE_STATE); if (entry.count == 0) { ALOGE("%s: ZSL queue frame has no AE state field!", __FUNCTION__); return BAD_VALUE; } if (entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_CONVERGED && entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_LOCKED) { ALOGV("%s: ZSL queue frame AE state is %d, need full capture", __FUNCTION__, entry.data.u8[0]); return NOT_ENOUGH_DATA; } buffer_handle_t *handle = &(mZslQueue[index].buffer.mGraphicBuffer->handle); uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS; res = request.update(ANDROID_REQUEST_TYPE, &requestType, 1); int32_t inputStreams[1] = { mZslReprocessStreamId }; if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS, inputStreams, 1); int32_t outputStreams[1] = { client->getCaptureStreamId() }; if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS, outputStreams, 1); res = request.update(ANDROID_REQUEST_ID, &requestId, 1); if (res != OK ) { ALOGE("%s: Unable to update frame to a reprocess request", __FUNCTION__); return INVALID_OPERATION; } res = client->stopStream(); if (res != OK) { ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: " "%s (%d)", __FUNCTION__, mId, strerror(-res), res); return INVALID_OPERATION; } // TODO: have push-and-clear be atomic res = client->getCameraDevice()->pushReprocessBuffer(mZslReprocessStreamId, handle, this); if (res != OK) { ALOGE("%s: Unable to push buffer for reprocessing: %s (%d)", __FUNCTION__, strerror(-res), res); return res; } // Update JPEG settings { SharedParameters::Lock l(client->getParameters()); res = l.mParameters.updateRequestJpeg(&request); if (res != OK) { ALOGE("%s: Camera %d: Unable to update JPEG entries of ZSL " "capture request: %s (%d)", __FUNCTION__, mId, strerror(-res), res); return res; } } mLatestCapturedRequest = request; res = client->getCameraDevice()->capture(request); if (res != OK ) { ALOGE("%s: Unable to send ZSL reprocess request to capture: %s (%d)", __FUNCTION__, strerror(-res), res); return res; } mState = LOCKED; } else { ALOGV("%s: No ZSL buffers yet", __FUNCTION__); return NOT_ENOUGH_DATA; } return OK; }