summaryrefslogtreecommitdiffstats
path: root/services/camera/libcameraservice/Camera2Client.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/camera/libcameraservice/Camera2Client.cpp')
-rw-r--r--services/camera/libcameraservice/Camera2Client.cpp404
1 files changed, 379 insertions, 25 deletions
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index c90f81a..8d4add4 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -55,7 +55,9 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
mPreviewStreamId(NO_STREAM),
mPreviewRequest(NULL),
mCaptureStreamId(NO_STREAM),
- mCaptureRequest(NULL)
+ mCaptureRequest(NULL),
+ mRecordingStreamId(NO_STREAM),
+ mRecordingRequest(NULL)
{
ATRACE_CALL();
@@ -341,23 +343,59 @@ void Camera2Client::disconnect() {
status_t Camera2Client::connect(const sp<ICameraClient>& client) {
ATRACE_CALL();
+
Mutex::Autolock icl(mICameraLock);
- return BAD_VALUE;
+ if (mClientPid != 0 && getCallingPid() != mClientPid) {
+ ALOGE("%s: Camera %d: Connection attempt from pid %d; "
+ "current locked to pid %d", __FUNCTION__,
+ mCameraId, getCallingPid(), mClientPid);
+ return BAD_VALUE;
+ }
+
+ mClientPid = getCallingPid();
+ mCameraClient = client;
+
+ return OK;
}
status_t Camera2Client::lock() {
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
+ ALOGV("%s: Camera %d: Lock call from pid %d; current client pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
- return BAD_VALUE;
+ if (mClientPid == 0) {
+ mClientPid = getCallingPid();
+ return OK;
+ }
+
+ if (mClientPid != getCallingPid()) {
+ ALOGE("%s: Camera %d: Lock call from pid %d; currently locked to pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
+ return EBUSY;
+ }
+
+ return OK;
}
status_t Camera2Client::unlock() {
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
+ ALOGV("%s: Camera %d: Unlock call from pid %d; current client pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
- return BAD_VALUE;
+ // TODO: Check for uninterruptable conditions
+
+ if (mClientPid == getCallingPid()) {
+ mClientPid = 0;
+ mCameraClient.clear();
+ return OK;
+ }
+
+ ALOGE("%s: Camera %d: Unlock call from pid %d; currently locked to pid %d",
+ __FUNCTION__, mCameraId, getCallingPid(), mClientPid);
+ return EBUSY;
}
status_t Camera2Client::setPreviewDisplay(
@@ -365,8 +403,6 @@ status_t Camera2Client::setPreviewDisplay(
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
- if (mState >= PREVIEW) return INVALID_OPERATION;
-
sp<IBinder> binder;
sp<ANativeWindow> window;
if (surface != 0) {
@@ -382,8 +418,6 @@ status_t Camera2Client::setPreviewTexture(
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
- if (mState >= PREVIEW) return INVALID_OPERATION;
-
sp<IBinder> binder;
sp<ANativeWindow> window;
if (surfaceTexture != 0) {
@@ -402,6 +436,27 @@ status_t Camera2Client::setPreviewWindowLocked(const sp<IBinder>& binder,
return NO_ERROR;
}
+ switch (mState) {
+ case NOT_INITIALIZED:
+ case RECORD:
+ case STILL_CAPTURE:
+ case VIDEO_SNAPSHOT:
+ ALOGE("%s: Camera %d: Cannot set preview display while in state %s",
+ __FUNCTION__, mCameraId, getStateName(mState));
+ return INVALID_OPERATION;
+ case STOPPED:
+ case WAITING_FOR_PREVIEW_WINDOW:
+ // OK
+ break;
+ case PREVIEW:
+ // Already running preview - need to stop and create a new stream
+ // TODO: Optimize this so that we don't wait for old stream to drain
+ // before spinning up new stream
+ mDevice->setStreamingRequest(NULL);
+ mState = WAITING_FOR_PREVIEW_WINDOW;
+ break;
+ }
+
if (mPreviewStreamId != NO_STREAM) {
res = mDevice->waitUntilDrained();
if (res != OK) {
@@ -454,6 +509,8 @@ status_t Camera2Client::startPreviewLocked() {
}
mState = STOPPED;
+ Mutex::Autolock pl(mParamsLock);
+
res = updatePreviewStream();
if (res != OK) {
ALOGE("%s: Camera %d: Unable to update preview stream: %s (%d)",
@@ -544,23 +601,125 @@ status_t Camera2Client::storeMetaDataInBuffers(bool enabled) {
status_t Camera2Client::startRecording() {
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
- return BAD_VALUE;
+ status_t res;
+ switch (mState) {
+ case STOPPED:
+ res = startPreviewLocked();
+ if (res != OK) return res;
+ break;
+ case PREVIEW:
+ // Ready to go
+ break;
+ case RECORD:
+ case VIDEO_SNAPSHOT:
+ // OK to call this when recording is already on
+ return OK;
+ break;
+ default:
+ ALOGE("%s: Camera %d: Can't start recording in state %s",
+ __FUNCTION__, mCameraId, getStateName(mState));
+ return INVALID_OPERATION;
+ };
+
+ Mutex::Autolock pl(mParamsLock);
+
+ res = updateRecordingStream();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ if (mRecordingRequest == NULL) {
+ res = updateRecordingRequest();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create recording request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+
+ uint8_t outputStreams[2] = { mPreviewStreamId, mRecordingStreamId };
+ res = updateEntry(mRecordingRequest,
+ ANDROID_REQUEST_OUTPUT_STREAMS,
+ outputStreams, 2);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set up recording request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ res = sort_camera_metadata(mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error sorting recording request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
+ res = mDevice->setStreamingRequest(mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to set recording request to start "
+ "recording: %s (%d)", __FUNCTION__, mCameraId,
+ strerror(-res), res);
+ return res;
+ }
+ mState = RECORD;
+
+ return OK;
}
void Camera2Client::stopRecording() {
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
+ status_t res;
+ switch (mState) {
+ case RECORD:
+ // OK to stop
+ break;
+ case STOPPED:
+ case PREVIEW:
+ case STILL_CAPTURE:
+ case VIDEO_SNAPSHOT:
+ default:
+ ALOGE("%s: Camera %d: Can't stop recording in state %s",
+ __FUNCTION__, mCameraId, getStateName(mState));
+ return;
+ };
+
+ // Back to preview. Since record can only be reached through preview,
+ // all preview stream setup should be up to date.
+ res = mDevice->setStreamingRequest(mPreviewRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to switch back to preview request: "
+ "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+ return;
+ }
+
+ // TODO: Should recording heap be freed? Can't do it yet since requests
+ // could still be in flight.
+
+ mState = PREVIEW;
}
bool Camera2Client::recordingEnabled() {
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
- return BAD_VALUE;
+ return (mState == RECORD || mState == VIDEO_SNAPSHOT);
}
void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) {
ATRACE_CALL();
Mutex::Autolock icl(mICameraLock);
+ // Make sure this is for the current heap
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) {
+ ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release "
+ "(got %x, expected %x)", __FUNCTION__, mCameraId,
+ heap->getHeapID(), mRecordingHeap->mHeap->getHeapID());
+ return;
+ }
+ mRecordingHeapFree++;
}
status_t Camera2Client::autoFocus() {
@@ -616,11 +775,18 @@ status_t Camera2Client::takePicture(int msgType) {
}
}
- // TODO: For video snapshot, will need 3 streams here
camera_metadata_entry_t outputStreams;
- uint8_t streamIds[2] = { mPreviewStreamId, mCaptureStreamId };
- res = updateEntry(mCaptureRequest, ANDROID_REQUEST_OUTPUT_STREAMS,
- &streamIds, 2);
+ if (mState == PREVIEW) {
+ uint8_t streamIds[2] = { mPreviewStreamId, mCaptureStreamId };
+ res = updateEntry(mCaptureRequest, ANDROID_REQUEST_OUTPUT_STREAMS,
+ &streamIds, 2);
+ } else if (mState == RECORD) {
+ uint8_t streamIds[3] = { mPreviewStreamId, mRecordingStreamId,
+ mCaptureStreamId };
+ res = updateEntry(mCaptureRequest, ANDROID_REQUEST_OUTPUT_STREAMS,
+ &streamIds, 3);
+ }
+
if (res != OK) {
ALOGE("%s: Camera %d: Unable to set up still image capture request: "
"%s (%d)",
@@ -650,7 +816,7 @@ status_t Camera2Client::takePicture(int msgType) {
return res;
}
}
-
+ // TODO: Capture should be atomic with setStreamingRequest here
res = mDevice->capture(captureCopy);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to submit still image capture request: "
@@ -699,7 +865,10 @@ status_t Camera2Client::setParameters(const String8& params) {
previewHeight != mParameters.previewHeight) {
if (mState >= PREVIEW) {
ALOGE("%s: Preview size cannot be updated when preview "
- "is active!", __FUNCTION__);
+ "is active! (Currently %d x %d, requested %d x %d",
+ __FUNCTION__,
+ mParameters.previewWidth, mParameters.previewHeight,
+ previewWidth, previewHeight);
return BAD_VALUE;
}
camera_metadata_entry_t availablePreviewSizes =
@@ -1127,6 +1296,7 @@ status_t Camera2Client::setParameters(const String8& params) {
}
/** Update internal parameters */
+
mParameters.previewWidth = previewWidth;
mParameters.previewHeight = previewHeight;
mParameters.previewFpsRange[0] = previewFpsRange[0];
@@ -1183,6 +1353,13 @@ status_t Camera2Client::setParameters(const String8& params) {
return res;
}
+ res = updateRecordingRequest();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+
if (mState == PREVIEW) {
res = mDevice->setStreamingRequest(mPreviewRequest);
if (res != OK) {
@@ -1190,8 +1367,17 @@ status_t Camera2Client::setParameters(const String8& params) {
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
}
+ } else if (mState == RECORD || mState == VIDEO_SNAPSHOT) {
+ res = mDevice->setStreamingRequest(mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
}
+ mParamsFlattened = params;
+
return OK;
}
@@ -1240,6 +1426,8 @@ void Camera2Client::onCaptureAvailable() {
ATRACE_CALL();
status_t res;
sp<ICameraClient> currentClient;
+ ALOGV("%s: Camera %d: Still capture available", __FUNCTION__, mCameraId);
+
CpuConsumer::LockedBuffer imgBuffer;
{
Mutex::Autolock icl(mICameraLock);
@@ -1268,8 +1456,8 @@ void Camera2Client::onCaptureAvailable() {
}
// TODO: Optimize this to avoid memcopy
- void* captureMemory = mCaptureHeap->getBase();
- size_t size = mCaptureHeap->getSize();
+ void* captureMemory = mCaptureHeap->mHeap->getBase();
+ size_t size = mCaptureHeap->mHeap->getSize();
memcpy(captureMemory, imgBuffer.data, size);
mCaptureConsumer->unlockBuffer(imgBuffer);
@@ -1291,7 +1479,100 @@ void Camera2Client::onCaptureAvailable() {
// Call outside mICameraLock to allow re-entrancy from notification
if (currentClient != 0) {
currentClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE,
- mCaptureMemory, NULL);
+ mCaptureHeap->mBuffers[0], NULL);
+ }
+}
+
+void Camera2Client::onRecordingFrameAvailable() {
+ ATRACE_CALL();
+ status_t res;
+ sp<ICameraClient> currentClient;
+ size_t heapIdx = 0;
+ nsecs_t timestamp;
+ {
+ Mutex::Autolock icl(mICameraLock);
+ // TODO: Signal errors here upstream
+ if (mState != RECORD && mState != VIDEO_SNAPSHOT) {
+ ALOGE("%s: Camera %d: Recording image buffer produced unexpectedly!",
+ __FUNCTION__, mCameraId);
+ return;
+ }
+
+ CpuConsumer::LockedBuffer imgBuffer;
+ res = mRecordingConsumer->lockNextBuffer(&imgBuffer);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return;
+ }
+
+ if (imgBuffer.format != (int)kRecordingFormat) {
+ ALOGE("%s: Camera %d: Unexpected recording format: %x",
+ __FUNCTION__, mCameraId, imgBuffer.format);
+ mRecordingConsumer->unlockBuffer(imgBuffer);
+ return;
+ }
+ size_t bufferSize = imgBuffer.width * imgBuffer.height * 3 / 2;
+
+ if (mRecordingHeap == 0 ||
+ bufferSize >
+ mRecordingHeap->mHeap->getSize() / kRecordingHeapCount) {
+ ALOGV("%s: Camera %d: Creating recording heap with %d buffers of "
+ "size %d bytes", __FUNCTION__, mCameraId,
+ kRecordingHeapCount, bufferSize);
+ if (mRecordingHeap != 0) {
+ ALOGV("%s: Camera %d: Previous heap has size %d "
+ "(new will be %d) bytes", __FUNCTION__, mCameraId,
+ mRecordingHeap->mHeap->getSize(),
+ bufferSize * kRecordingHeapCount);
+ }
+ // Need to allocate memory for heap
+ mRecordingHeap.clear();
+
+ mRecordingHeap = new Camera2Heap(bufferSize, kRecordingHeapCount,
+ "Camera2Client::RecordingHeap");
+ if (mRecordingHeap->mHeap->getSize() == 0) {
+ ALOGE("%s: Camera %d: Unable to allocate memory for recording",
+ __FUNCTION__, mCameraId);
+ mRecordingConsumer->unlockBuffer(imgBuffer);
+ return;
+ }
+ mRecordingHeapHead = 0;
+ mRecordingHeapFree = kRecordingHeapCount;
+ }
+
+ // TODO: Optimize this to avoid memcopy
+ if ( mRecordingHeapFree == 0) {
+ ALOGE("%s: Camera %d: No free recording buffers, dropping frame",
+ __FUNCTION__, mCameraId);
+ mRecordingConsumer->unlockBuffer(imgBuffer);
+ return;
+ }
+ heapIdx = mRecordingHeapHead;
+ timestamp = imgBuffer.timestamp;
+ mRecordingHeapHead = (mRecordingHeapHead + 1) % kRecordingHeapCount;
+ mRecordingHeapFree--;
+
+ ALOGV("%s: Camera %d: Timestamp %lld",
+ __FUNCTION__, mCameraId, timestamp);
+
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap =
+ mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset,
+ &size);
+
+ memcpy((uint8_t*)heap->getBase() + offset, imgBuffer.data, size);
+
+ mRecordingConsumer->unlockBuffer(imgBuffer);
+
+ currentClient = mCameraClient;
+ }
+ // Call outside mICameraLock to allow re-entrancy from notification
+ if (currentClient != 0) {
+ currentClient->dataCallbackTimestamp(timestamp,
+ CAMERA_MSG_VIDEO_FRAME,
+ mRecordingHeap->mBuffers[heapIdx]);
}
}
@@ -1999,7 +2280,7 @@ status_t Camera2Client::buildDefaultParameters() {
0);
params.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
- formatEnumToString(HAL_PIXEL_FORMAT_YCrCb_420_SP));
+ formatEnumToString(kRecordingFormat));
params.set(CameraParameters::KEY_RECORDING_HINT,
CameraParameters::FALSE);
@@ -2126,15 +2407,13 @@ status_t Camera2Client::updateCaptureStream() {
mCaptureWindow = new SurfaceTextureClient(
mCaptureConsumer->getProducerInterface());
// Create memory for API consumption
- mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0,
- "Camera2Client::CaptureHeap");
- if (mCaptureHeap->getSize() == 0) {
+ mCaptureHeap = new Camera2Heap(maxJpegSize.data.i32[0], 1,
+ "Camera2Client::CaptureHeap");
+ if (mCaptureHeap->mHeap->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) {
@@ -2243,6 +2522,81 @@ status_t Camera2Client::updateCaptureRequest() {
return OK;
}
+status_t Camera2Client::updateRecordingRequest() {
+ ATRACE_CALL();
+ status_t res;
+ if (mRecordingRequest == NULL) {
+ res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD,
+ &mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to create default recording request:"
+ " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+
+ res = updateRequestCommon(mRecordingRequest);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update common entries of recording "
+ "request: %s (%d)", __FUNCTION__, mCameraId,
+ strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t Camera2Client::updateRecordingStream() {
+ status_t res;
+
+ if (mRecordingConsumer == 0) {
+ // Create CPU buffer queue endpoint
+ mRecordingConsumer = new CpuConsumer(1);
+ mRecordingConsumer->setFrameAvailableListener(new RecordingWaiter(this));
+ mRecordingConsumer->setName(String8("Camera2Client::RecordingConsumer"));
+ mRecordingWindow = new SurfaceTextureClient(
+ mRecordingConsumer->getProducerInterface());
+ // Allocate memory later, since we don't know buffer size until receipt
+ }
+
+ if (mRecordingStreamId != NO_STREAM) {
+ // Check if stream parameters have to change
+ uint32_t currentWidth, currentHeight;
+ res = mDevice->getStreamInfo(mRecordingStreamId,
+ &currentWidth, &currentHeight, 0);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying recording output stream info: "
+ "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ if (currentWidth != (uint32_t)mParameters.videoWidth ||
+ currentHeight != (uint32_t)mParameters.videoHeight) {
+ // TODO: Should wait to be sure previous recording has finished
+ res = mDevice->deleteStream(mRecordingStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to delete old output stream "
+ "for recording: %s (%d)", __FUNCTION__, mCameraId,
+ strerror(-res), res);
+ return res;
+ }
+ mRecordingStreamId = NO_STREAM;
+ }
+ }
+
+ if (mRecordingStreamId == NO_STREAM) {
+ res = mDevice->createStream(mRecordingWindow,
+ mParameters.videoWidth, mParameters.videoHeight,
+ kRecordingFormat, 0, &mRecordingStreamId);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't create output stream for recording: "
+ "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+
+ return OK;
+}
+
status_t Camera2Client::updateRequestCommon(camera_metadata_t *request) {
ATRACE_CALL();
status_t res;