diff options
author | Eino-Ville Talvala <etalvala@google.com> | 2012-06-07 17:12:38 -0700 |
---|---|---|
committer | Eino-Ville Talvala <etalvala@google.com> | 2012-06-13 12:03:28 -0700 |
commit | d4bcfde6bf3e7b28e36f6ec66e6d9e5adebfa949 (patch) | |
tree | 9a7809d649c25ee5bb30bd7dd46da7753b59fa1a /services/camera/libcameraservice/Camera2Client.cpp | |
parent | a9300fd3d84cc07bbfa702e2fad3b32a5ebfd337 (diff) | |
download | frameworks_av-d4bcfde6bf3e7b28e36f6ec66e6d9e5adebfa949.zip frameworks_av-d4bcfde6bf3e7b28e36f6ec66e6d9e5adebfa949.tar.gz frameworks_av-d4bcfde6bf3e7b28e36f6ec66e6d9e5adebfa949.tar.bz2 |
Camera2: Still image support
- Add stream / request creation for still image capture
- Add takePicture call
- Add callback handler
- Fix shutdown bugs (wait until requests have drained)
Bug: 6243944
Change-Id: Id73eb7090e61b40b90348d1eb262f641ea5f3229
Diffstat (limited to 'services/camera/libcameraservice/Camera2Client.cpp')
-rw-r--r-- | services/camera/libcameraservice/Camera2Client.cpp | 297 |
1 files changed, 280 insertions, 17 deletions
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp index 1cea906..05a54b7 100644 --- a/services/camera/libcameraservice/Camera2Client.cpp +++ b/services/camera/libcameraservice/Camera2Client.cpp @@ -52,8 +52,10 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService, Client(cameraService, cameraClient, cameraId, cameraFacing, clientPid), mState(NOT_INITIALIZED), - mPreviewStreamId(NO_PREVIEW_STREAM), - mPreviewRequest(NULL) + mPreviewStreamId(NO_STREAM), + mPreviewRequest(NULL), + mCaptureStreamId(NO_STREAM), + mCaptureRequest(NULL) { ATRACE_CALL(); @@ -280,9 +282,16 @@ void Camera2Client::disconnect() { stopPreviewLocked(); - if (mPreviewStreamId != NO_PREVIEW_STREAM) { + mDevice->waitUntilDrained(); + + if (mPreviewStreamId != NO_STREAM) { mDevice->deleteStream(mPreviewStreamId); - mPreviewStreamId = NO_PREVIEW_STREAM; + mPreviewStreamId = NO_STREAM; + } + + if (mCaptureStreamId != NO_STREAM) { + mDevice->deleteStream(mCaptureStreamId); + mCaptureStreamId = NO_STREAM; } CameraService::Client::disconnect(); @@ -323,7 +332,7 @@ status_t Camera2Client::setPreviewDisplay( window = surface; } - return setPreviewWindow(binder,window); + return setPreviewWindowLocked(binder,window); } status_t Camera2Client::setPreviewTexture( @@ -339,10 +348,10 @@ status_t Camera2Client::setPreviewTexture( binder = surfaceTexture->asBinder(); window = new SurfaceTextureClient(surfaceTexture); } - return setPreviewWindow(binder, window); + return setPreviewWindowLocked(binder, window); } -status_t Camera2Client::setPreviewWindow(const sp<IBinder>& binder, +status_t Camera2Client::setPreviewWindowLocked(const sp<IBinder>& binder, const sp<ANativeWindow>& window) { ATRACE_CALL(); status_t res; @@ -351,7 +360,9 @@ status_t Camera2Client::setPreviewWindow(const sp<IBinder>& binder, return NO_ERROR; } - if (mPreviewStreamId != NO_PREVIEW_STREAM) { + // TODO: Should wait until HAL has no remaining requests + + if (mPreviewStreamId != NO_STREAM) { res = mDevice->deleteStream(mPreviewStreamId); if (res != OK) { return res; @@ -359,7 +370,7 @@ status_t Camera2Client::setPreviewWindow(const sp<IBinder>& binder, } res = mDevice->createStream(window, mParameters.previewWidth, mParameters.previewHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mPreviewStreamId); if (res != OK) { return res; @@ -368,7 +379,7 @@ status_t Camera2Client::setPreviewWindow(const sp<IBinder>& binder, mPreviewSurface = binder; if (mState == WAITING_FOR_PREVIEW_WINDOW) { - return startPreview(); + return startPreviewLocked(); } return OK; @@ -382,11 +393,15 @@ void Camera2Client::setPreviewCallbackFlag(int flag) { status_t Camera2Client::startPreview() { ATRACE_CALL(); Mutex::Autolock icl(mICameraLock); + return startPreviewLocked(); +} +status_t Camera2Client::startPreviewLocked() { + ATRACE_CALL(); status_t res; - if (mState == PREVIEW) return INVALID_OPERATION; + if (mState >= PREVIEW) return INVALID_OPERATION; - if (mPreviewStreamId == NO_PREVIEW_STREAM) { + if (mPreviewStreamId == NO_STREAM) { mState = WAITING_FOR_PREVIEW_WINDOW; return OK; } @@ -438,10 +453,28 @@ void Camera2Client::stopPreview() { void Camera2Client::stopPreviewLocked() { ATRACE_CALL(); - if (mState != PREVIEW) return; - - mDevice->setStreamingRequest(NULL); - mState = STOPPED; + switch (mState) { + case NOT_INITIALIZED: + ALOGE("%s: Camera %d: Call before initialized", + __FUNCTION__, mCameraId); + break; + case STOPPED: + break; + case STILL_CAPTURE: + ALOGE("%s: Camera %d: Cannot stop preview during still capture.", + __FUNCTION__, mCameraId); + break; + case RECORD: + // TODO: Handle record stop here + case PREVIEW: + mDevice->setStreamingRequest(NULL); + case WAITING_FOR_PREVIEW_WINDOW: + mState = STOPPED; + break; + default: + ALOGE("%s: Camera %d: Unknown state %d", __FUNCTION__, mCameraId, + mState); + } } bool Camera2Client::previewEnabled() { @@ -493,7 +526,95 @@ status_t Camera2Client::cancelAutoFocus() { status_t Camera2Client::takePicture(int msgType) { ATRACE_CALL(); Mutex::Autolock icl(mICameraLock); - return BAD_VALUE; + status_t res; + + switch (mState) { + case NOT_INITIALIZED: + case STOPPED: + case WAITING_FOR_PREVIEW_WINDOW: + ALOGE("%s: Camera %d: Cannot take picture without preview enabled", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + case PREVIEW: + case RECORD: + // Good to go for takePicture + break; + case STILL_CAPTURE: + case VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Already taking a picture", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + Mutex::Autolock pl(mParamsLock); + + res = updateCaptureStream(); + + if (mCaptureRequest == NULL) { + updateCaptureRequest(); + } + + // TODO: For video snapshot, need 3 streams here + camera_metadata_entry_t outputStreams; + uint8_t streamIds[2] = { mPreviewStreamId, mCaptureStreamId }; + res = find_camera_metadata_entry(mCaptureRequest, + ANDROID_REQUEST_OUTPUT_STREAMS, + &outputStreams); + if (res == NAME_NOT_FOUND) { + res = add_camera_metadata_entry(mCaptureRequest, + ANDROID_REQUEST_OUTPUT_STREAMS, + streamIds, 2); + } else if (res == OK) { + res = update_camera_metadata_entry(mCaptureRequest, + outputStreams.index, streamIds, 2, NULL); + } + + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up still image capture request: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + camera_metadata_t *captureCopy = clone_camera_metadata(mCaptureRequest); + if (captureCopy == NULL) { + ALOGE("%s: Camera %d: Unable to copy capture request for HAL device", + __FUNCTION__, mCameraId); + return NO_MEMORY; + } + + if (mState == PREVIEW) { + res = mDevice->setStreamingRequest(NULL); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to stop preview for still capture: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + res = mDevice->capture(captureCopy); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to submit still image capture request: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + switch (mState) { + case PREVIEW: + mState = STILL_CAPTURE; + break; + case RECORD: + mState = VIDEO_SNAPSHOT; + break; + default: + ALOGE("%s: Camera %d: Unknown state for still capture!", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + return OK; } status_t Camera2Client::setParameters(const String8& params) { @@ -991,6 +1112,7 @@ status_t Camera2Client::setParameters(const String8& params) { mParameters.videoStabilization = videoStabilization; updatePreviewRequest(); + updateCaptureRequest(); return OK; } @@ -1013,6 +1135,65 @@ status_t Camera2Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { /** Device-related methods */ +void Camera2Client::onCaptureAvailable() { + ATRACE_CALL(); + status_t res; + sp<ICameraClient> currentClient; + CpuConsumer::LockedBuffer imgBuffer; + { + Mutex::Autolock icl(mICameraLock); + + // TODO: Signal errors here upstream + if (mState != STILL_CAPTURE && mState != VIDEO_SNAPSHOT) { + ALOGE("%s: Camera %d: Still image produced unexpectedly!", + __FUNCTION__, mCameraId); + return; + } + + res = mCaptureConsumer->lockNextBuffer(&imgBuffer); + if (res != OK) { + ALOGE("%s: Camera %d: Error receiving still image buffer: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return; + } + + if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Camera %d: Unexpected format for still image: " + "%x, expected %x", __FUNCTION__, mCameraId, + imgBuffer.format, + HAL_PIXEL_FORMAT_BLOB); + mCaptureConsumer->unlockBuffer(imgBuffer); + return; + } + + // TODO: Optimize this to avoid memcopy + void* captureMemory = mCaptureHeap->getBase(); + size_t size = mCaptureHeap->getSize(); + memcpy(captureMemory, imgBuffer.data, size); + + mCaptureConsumer->unlockBuffer(imgBuffer); + + currentClient = mCameraClient; + switch (mState) { + case STILL_CAPTURE: + mState = STOPPED; + break; + case VIDEO_SNAPSHOT: + mState = RECORD; + break; + default: + ALOGE("%s: Camera %d: Unexpected state %d", __FUNCTION__, + mCameraId, mState); + break; + } + } + // Call outside mICameraLock to allow re-entrancy from notification + if (currentClient != 0) { + currentClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, + mCaptureMemory, NULL); + } +} + camera_metadata_entry_t Camera2Client::staticInfo(uint32_t tag, size_t minCount, size_t maxCount) { status_t res; @@ -1752,6 +1933,88 @@ status_t Camera2Client::updatePreviewRequest() { return OK; } +status_t Camera2Client::updateCaptureStream() { + status_t res; + // Find out buffer size for JPEG + camera_metadata_entry_t maxJpegSize = + staticInfo(ANDROID_JPEG_MAX_SIZE); + if (maxJpegSize.count == 0) { + ALOGE("%s: Camera %d: Can't find ANDROID_JPEG_MAX_SIZE!", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + if (mCaptureConsumer == 0) { + // Create CPU buffer queue endpoint + mCaptureConsumer = new CpuConsumer(1); + mCaptureConsumer->setFrameAvailableListener(new CaptureWaiter(this)); + mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer")); + mCaptureWindow = new SurfaceTextureClient( + mCaptureConsumer->getProducerInterface()); + // Create memory for API consumption + mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0, + "Camera2Client::CaptureHeap"); + if (mCaptureHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for capture", + __FUNCTION__, mCameraId); + return NO_MEMORY; + } + mCaptureMemory = new MemoryBase(mCaptureHeap, + 0, maxJpegSize.data.i32[0]); + } + if (mCaptureStreamId == NO_STREAM) { + // Create stream for HAL production + res = mDevice->createStream(mCaptureWindow, + mParameters.pictureWidth, mParameters.pictureHeight, + HAL_PIXEL_FORMAT_BLOB, maxJpegSize.data.i32[0], + &mCaptureStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create output stream for capture: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + } else { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = mDevice->getStreamInfo(mCaptureStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying capture output stream info: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)mParameters.pictureWidth || + currentHeight != (uint32_t)mParameters.pictureHeight) { + res = mDevice->deleteStream(mCaptureStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for capture: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + mCaptureStreamId = NO_STREAM; + return updateCaptureStream(); + } + } + return OK; +} +status_t Camera2Client::updateCaptureRequest() { + ATRACE_CALL(); + status_t res; + if (mCaptureRequest == NULL) { + res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_STILL_CAPTURE, + &mCaptureRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default still image request:" + " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + // TODO: Adjust for params changes + return OK; +} + int Camera2Client::formatStringToEnum(const char *format) { return !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422SP) ? |