| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "Camera2-StreamingProcessor" |
| #define ATRACE_TAG ATRACE_TAG_CAMERA |
| //#define LOG_NDEBUG 0 |
| //#define LOG_NNDEBUG 0 // Per-frame verbose logging |
| |
| #ifdef LOG_NNDEBUG |
| #define ALOGVV(...) ALOGV(__VA_ARGS__) |
| #else |
| #define ALOGVV(...) ((void)0) |
| #endif |
| |
| #include <cutils/properties.h> |
| #include <utils/Log.h> |
| #include <utils/Trace.h> |
| #include <gui/BufferItem.h> |
| #include <gui/Surface.h> |
| #include <camera/ICameraRecordingProxy.h> |
| #include <media/hardware/HardwareAPI.h> |
| |
| #include "common/CameraDeviceBase.h" |
| #include "api1/Camera2Client.h" |
| #include "api1/client2/StreamingProcessor.h" |
| #include "api1/client2/Camera2Heap.h" |
| |
| namespace android { |
| namespace camera2 { |
| |
| StreamingProcessor::StreamingProcessor(sp<Camera2Client> client): |
| mClient(client), |
| mDevice(client->getCameraDevice()), |
| mId(client->getCameraId()), |
| mActiveRequest(NONE), |
| mPaused(false), |
| mPreviewRequestId(Camera2Client::kPreviewRequestIdStart), |
| mPreviewStreamId(NO_STREAM), |
| mRecordingRequestId(Camera2Client::kRecordingRequestIdStart), |
| mRecordingStreamId(NO_STREAM), |
| mRecordingFrameAvailable(false), |
| mRecordingHeapCount(kDefaultRecordingHeapCount), |
| mRecordingHeapFree(kDefaultRecordingHeapCount), |
| mRecordingFormat(kDefaultRecordingFormat), |
| mRecordingDataSpace(kDefaultRecordingDataSpace), |
| mRecordingGrallocUsage(kDefaultRecordingGrallocUsage) |
| { |
| } |
| |
| StreamingProcessor::~StreamingProcessor() { |
| deletePreviewStream(); |
| deleteRecordingStream(); |
| } |
| |
| status_t StreamingProcessor::setPreviewWindow(sp<Surface> window) { |
| ATRACE_CALL(); |
| status_t res; |
| |
| res = deletePreviewStream(); |
| if (res != OK) return res; |
| |
| Mutex::Autolock m(mMutex); |
| |
| mPreviewWindow = window; |
| |
| return OK; |
| } |
| |
| bool StreamingProcessor::haveValidPreviewWindow() const { |
| Mutex::Autolock m(mMutex); |
| return mPreviewWindow != 0; |
| } |
| |
| status_t StreamingProcessor::updatePreviewRequest(const Parameters ¶ms) { |
| ATRACE_CALL(); |
| status_t res; |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| Mutex::Autolock m(mMutex); |
| if (mPreviewRequest.entryCount() == 0) { |
| sp<Camera2Client> client = mClient.promote(); |
| if (client == 0) { |
| ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| // Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case. |
| if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) { |
| if (params.zslMode && !params.recordingHint) { |
| res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG, |
| &mPreviewRequest); |
| } else { |
| res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW, |
| &mPreviewRequest); |
| } |
| } else { |
| res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, |
| &mPreviewRequest); |
| } |
| |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to create default preview request: " |
| "%s (%d)", __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| } |
| |
| res = params.updateRequest(&mPreviewRequest); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to update common entries of preview " |
| "request: %s (%d)", __FUNCTION__, mId, |
| strerror(-res), res); |
| return res; |
| } |
| |
| res = mPreviewRequest.update(ANDROID_REQUEST_ID, |
| &mPreviewRequestId, 1); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to update request id for preview: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| |
| return OK; |
| } |
| |
| status_t StreamingProcessor::updatePreviewStream(const Parameters ¶ms) { |
| ATRACE_CALL(); |
| Mutex::Autolock m(mMutex); |
| |
| status_t res; |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| if (mPreviewStreamId != NO_STREAM) { |
| // Check if stream parameters have to change |
| uint32_t currentWidth, currentHeight; |
| res = device->getStreamInfo(mPreviewStreamId, |
| ¤tWidth, ¤tHeight, 0, 0); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Error querying preview stream info: " |
| "%s (%d)", __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| if (currentWidth != (uint32_t)params.previewWidth || |
| currentHeight != (uint32_t)params.previewHeight) { |
| ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d", |
| __FUNCTION__, mId, currentWidth, currentHeight, |
| params.previewWidth, params.previewHeight); |
| res = device->waitUntilDrained(); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Error waiting for preview to drain: " |
| "%s (%d)", __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| res = device->deleteStream(mPreviewStreamId); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to delete old output stream " |
| "for preview: %s (%d)", __FUNCTION__, mId, |
| strerror(-res), res); |
| return res; |
| } |
| mPreviewStreamId = NO_STREAM; |
| } |
| } |
| |
| if (mPreviewStreamId == NO_STREAM) { |
| res = device->createStream(mPreviewWindow, |
| params.previewWidth, params.previewHeight, |
| CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN, |
| CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| } |
| |
| res = device->setStreamTransform(mPreviewStreamId, |
| params.previewTransform); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to set preview stream transform: " |
| "%s (%d)", __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| |
| return OK; |
| } |
| |
| status_t StreamingProcessor::deletePreviewStream() { |
| ATRACE_CALL(); |
| status_t res; |
| |
| Mutex::Autolock m(mMutex); |
| |
| if (mPreviewStreamId != NO_STREAM) { |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| ALOGV("%s: for cameraId %d on streamId %d", |
| __FUNCTION__, mId, mPreviewStreamId); |
| |
| res = device->waitUntilDrained(); |
| if (res != OK) { |
| ALOGE("%s: Error waiting for preview to drain: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| res = device->deleteStream(mPreviewStreamId); |
| if (res != OK) { |
| ALOGE("%s: Unable to delete old preview stream: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| mPreviewStreamId = NO_STREAM; |
| } |
| return OK; |
| } |
| |
| int StreamingProcessor::getPreviewStreamId() const { |
| Mutex::Autolock m(mMutex); |
| return mPreviewStreamId; |
| } |
| |
| status_t StreamingProcessor::setRecordingBufferCount(size_t count) { |
| ATRACE_CALL(); |
| // Make sure we can support this many buffer slots |
| if (count > BufferQueue::NUM_BUFFER_SLOTS) { |
| ALOGE("%s: Camera %d: Too many recording buffers requested: %zu, max %d", |
| __FUNCTION__, mId, count, BufferQueue::NUM_BUFFER_SLOTS); |
| return BAD_VALUE; |
| } |
| |
| Mutex::Autolock m(mMutex); |
| |
| ALOGV("%s: Camera %d: New recording buffer count from encoder: %zu", |
| __FUNCTION__, mId, count); |
| |
| // Need to re-size consumer and heap |
| if (mRecordingHeapCount != count) { |
| ALOGV("%s: Camera %d: Resetting recording heap and consumer", |
| __FUNCTION__, mId); |
| |
| if (isStreamActive(mActiveStreamIds, mRecordingStreamId)) { |
| ALOGE("%s: Camera %d: Setting recording buffer count when " |
| "recording stream is already active!", __FUNCTION__, |
| mId); |
| return INVALID_OPERATION; |
| } |
| |
| releaseAllRecordingFramesLocked(); |
| |
| if (mRecordingHeap != 0) { |
| mRecordingHeap.clear(); |
| } |
| mRecordingHeapCount = count; |
| mRecordingHeapFree = count; |
| |
| mRecordingConsumer.clear(); |
| } |
| |
| return OK; |
| } |
| |
| status_t StreamingProcessor::setRecordingFormat(int format, |
| android_dataspace dataSpace) { |
| ATRACE_CALL(); |
| |
| Mutex::Autolock m(mMutex); |
| |
| ALOGV("%s: Camera %d: New recording format/dataspace from encoder: %X, %X", |
| __FUNCTION__, mId, format, dataSpace); |
| |
| mRecordingFormat = format; |
| mRecordingDataSpace = dataSpace; |
| int prevGrallocUsage = mRecordingGrallocUsage; |
| if (mRecordingFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { |
| mRecordingGrallocUsage = GRALLOC_USAGE_HW_VIDEO_ENCODER; |
| } else { |
| mRecordingGrallocUsage = GRALLOC_USAGE_SW_READ_OFTEN; |
| } |
| |
| ALOGV("%s: Camera %d: New recording gralloc usage: %08X", __FUNCTION__, mId, |
| mRecordingGrallocUsage); |
| |
| if (prevGrallocUsage != mRecordingGrallocUsage) { |
| ALOGV("%s: Camera %d: Resetting recording consumer for new usage", |
| __FUNCTION__, mId); |
| |
| if (isStreamActive(mActiveStreamIds, mRecordingStreamId)) { |
| ALOGE("%s: Camera %d: Changing recording format when " |
| "recording stream is already active!", __FUNCTION__, |
| mId); |
| return INVALID_OPERATION; |
| } |
| |
| releaseAllRecordingFramesLocked(); |
| |
| mRecordingConsumer.clear(); |
| } |
| |
| return OK; |
| } |
| |
| status_t StreamingProcessor::updateRecordingRequest(const Parameters ¶ms) { |
| ATRACE_CALL(); |
| status_t res; |
| Mutex::Autolock m(mMutex); |
| |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| if (mRecordingRequest.entryCount() == 0) { |
| res = device->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD, |
| &mRecordingRequest); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to create default recording request:" |
| " %s (%d)", __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| } |
| |
| res = params.updateRequest(&mRecordingRequest); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to update common entries of recording " |
| "request: %s (%d)", __FUNCTION__, mId, |
| strerror(-res), res); |
| return res; |
| } |
| |
| res = mRecordingRequest.update(ANDROID_REQUEST_ID, |
| &mRecordingRequestId, 1); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to update request id for request: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| |
| return OK; |
| } |
| |
| status_t StreamingProcessor::recordingStreamNeedsUpdate( |
| const Parameters ¶ms, bool *needsUpdate) { |
| status_t res; |
| |
| if (needsUpdate == 0) { |
| ALOGE("%s: Camera %d: invalid argument", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| if (mRecordingStreamId == NO_STREAM) { |
| *needsUpdate = true; |
| return OK; |
| } |
| |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| uint32_t currentWidth, currentHeight, currentFormat; |
| android_dataspace currentDataSpace; |
| res = device->getStreamInfo(mRecordingStreamId, |
| ¤tWidth, ¤tHeight, ¤tFormat, ¤tDataSpace); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Error querying recording output stream info: " |
| "%s (%d)", __FUNCTION__, mId, |
| strerror(-res), res); |
| return res; |
| } |
| |
| if (mRecordingConsumer == 0 || |
| currentWidth != (uint32_t)params.videoWidth || |
| currentHeight != (uint32_t)params.videoHeight || |
| currentFormat != (uint32_t)mRecordingFormat || |
| currentDataSpace != mRecordingDataSpace) { |
| *needsUpdate = true; |
| } |
| *needsUpdate = false; |
| return res; |
| } |
| |
| status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { |
| ATRACE_CALL(); |
| status_t res; |
| Mutex::Autolock m(mMutex); |
| |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| bool newConsumer = false; |
| if (mRecordingConsumer == 0) { |
| ALOGV("%s: Camera %d: Creating recording consumer with %zu + 1 " |
| "consumer-side buffers", __FUNCTION__, mId, mRecordingHeapCount); |
| // Create CPU buffer queue endpoint. We need one more buffer here so that we can |
| // always acquire and free a buffer when the heap is full; otherwise the consumer |
| // will have buffers in flight we'll never clear out. |
| sp<IGraphicBufferProducer> producer; |
| sp<IGraphicBufferConsumer> consumer; |
| BufferQueue::createBufferQueue(&producer, &consumer); |
| mRecordingConsumer = new BufferItemConsumer(consumer, |
| mRecordingGrallocUsage, |
| mRecordingHeapCount + 1); |
| mRecordingConsumer->setFrameAvailableListener(this); |
| mRecordingConsumer->setName(String8("Camera2-RecordingConsumer")); |
| mRecordingWindow = new Surface(producer); |
| newConsumer = true; |
| // 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; |
| uint32_t currentFormat; |
| android_dataspace currentDataSpace; |
| res = device->getStreamInfo(mRecordingStreamId, |
| ¤tWidth, ¤tHeight, |
| ¤tFormat, ¤tDataSpace); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Error querying recording output stream info: " |
| "%s (%d)", __FUNCTION__, mId, |
| strerror(-res), res); |
| return res; |
| } |
| if (currentWidth != (uint32_t)params.videoWidth || |
| currentHeight != (uint32_t)params.videoHeight || |
| currentFormat != (uint32_t)mRecordingFormat || |
| currentDataSpace != mRecordingDataSpace || |
| newConsumer) { |
| // TODO: Should wait to be sure previous recording has finished |
| res = device->deleteStream(mRecordingStreamId); |
| |
| if (res == -EBUSY) { |
| ALOGV("%s: Camera %d: Device is busy, call " |
| "updateRecordingStream after it becomes idle", |
| __FUNCTION__, mId); |
| return res; |
| } else if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to delete old output stream " |
| "for recording: %s (%d)", __FUNCTION__, |
| mId, strerror(-res), res); |
| return res; |
| } |
| mRecordingStreamId = NO_STREAM; |
| } |
| } |
| |
| if (mRecordingStreamId == NO_STREAM) { |
| mRecordingFrameCount = 0; |
| res = device->createStream(mRecordingWindow, |
| params.videoWidth, params.videoHeight, |
| mRecordingFormat, mRecordingDataSpace, |
| CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Can't create output stream for recording: " |
| "%s (%d)", __FUNCTION__, mId, |
| strerror(-res), res); |
| return res; |
| } |
| } |
| |
| return OK; |
| } |
| |
| status_t StreamingProcessor::deleteRecordingStream() { |
| ATRACE_CALL(); |
| status_t res; |
| |
| Mutex::Autolock m(mMutex); |
| |
| if (mRecordingStreamId != NO_STREAM) { |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| res = device->waitUntilDrained(); |
| if (res != OK) { |
| ALOGE("%s: Error waiting for HAL to drain: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| res = device->deleteStream(mRecordingStreamId); |
| if (res != OK) { |
| ALOGE("%s: Unable to delete recording stream: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| return res; |
| } |
| mRecordingStreamId = NO_STREAM; |
| } |
| return OK; |
| } |
| |
| int StreamingProcessor::getRecordingStreamId() const { |
| return mRecordingStreamId; |
| } |
| |
| status_t StreamingProcessor::startStream(StreamType type, |
| const Vector<int32_t> &outputStreams) { |
| ATRACE_CALL(); |
| status_t res; |
| |
| if (type == NONE) return INVALID_OPERATION; |
| |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| ALOGV("%s: Camera %d: type = %d", __FUNCTION__, mId, type); |
| |
| Mutex::Autolock m(mMutex); |
| |
| // If a recording stream is being started up and no recording |
| // stream is active yet, free up any outstanding buffers left |
| // from the previous recording session. There should never be |
| // any, so if there are, warn about it. |
| bool isRecordingStreamIdle = !isStreamActive(mActiveStreamIds, mRecordingStreamId); |
| bool startRecordingStream = isStreamActive(outputStreams, mRecordingStreamId); |
| if (startRecordingStream && isRecordingStreamIdle) { |
| releaseAllRecordingFramesLocked(); |
| } |
| |
| ALOGV("%s: Camera %d: %s started, recording heap has %zu free of %zu", |
| __FUNCTION__, mId, (type == PREVIEW) ? "preview" : "recording", |
| mRecordingHeapFree, mRecordingHeapCount); |
| |
| CameraMetadata &request = (type == PREVIEW) ? |
| mPreviewRequest : mRecordingRequest; |
| |
| res = request.update( |
| ANDROID_REQUEST_OUTPUT_STREAMS, |
| outputStreams); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to set up preview request: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| |
| res = request.sort(); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Error sorting preview request: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| |
| res = device->setStreamingRequest(request); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to set preview request to start preview: " |
| "%s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| mActiveRequest = type; |
| mPaused = false; |
| mActiveStreamIds = outputStreams; |
| return OK; |
| } |
| |
| status_t StreamingProcessor::togglePauseStream(bool pause) { |
| ATRACE_CALL(); |
| status_t res; |
| |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| ALOGV("%s: Camera %d: toggling pause to %d", __FUNCTION__, mId, pause); |
| |
| Mutex::Autolock m(mMutex); |
| |
| if (mActiveRequest == NONE) { |
| ALOGE("%s: Camera %d: Can't toggle pause, streaming was not started", |
| __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| if (mPaused == pause) { |
| return OK; |
| } |
| |
| if (pause) { |
| res = device->clearStreamingRequest(); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Can't clear stream request: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| } else { |
| CameraMetadata &request = |
| (mActiveRequest == PREVIEW) ? mPreviewRequest |
| : mRecordingRequest; |
| res = device->setStreamingRequest(request); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to set preview request to resume: " |
| "%s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| } |
| |
| mPaused = pause; |
| return OK; |
| } |
| |
| status_t StreamingProcessor::stopStream() { |
| ATRACE_CALL(); |
| status_t res; |
| |
| Mutex::Autolock m(mMutex); |
| |
| sp<CameraDeviceBase> device = mDevice.promote(); |
| if (device == 0) { |
| ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId); |
| return INVALID_OPERATION; |
| } |
| |
| res = device->clearStreamingRequest(); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Can't clear stream request: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| return res; |
| } |
| |
| mActiveRequest = NONE; |
| mActiveStreamIds.clear(); |
| mPaused = false; |
| |
| return OK; |
| } |
| |
| int32_t StreamingProcessor::getActiveRequestId() const { |
| Mutex::Autolock m(mMutex); |
| switch (mActiveRequest) { |
| case NONE: |
| return 0; |
| case PREVIEW: |
| return mPreviewRequestId; |
| case RECORD: |
| return mRecordingRequestId; |
| default: |
| ALOGE("%s: Unexpected mode %d", __FUNCTION__, mActiveRequest); |
| return 0; |
| } |
| } |
| |
| status_t StreamingProcessor::incrementStreamingIds() { |
| ATRACE_CALL(); |
| Mutex::Autolock m(mMutex); |
| |
| mPreviewRequestId++; |
| if (mPreviewRequestId >= Camera2Client::kPreviewRequestIdEnd) { |
| mPreviewRequestId = Camera2Client::kPreviewRequestIdStart; |
| } |
| mRecordingRequestId++; |
| if (mRecordingRequestId >= Camera2Client::kRecordingRequestIdEnd) { |
| mRecordingRequestId = Camera2Client::kRecordingRequestIdStart; |
| } |
| return OK; |
| } |
| |
| void StreamingProcessor::onFrameAvailable(const BufferItem& /*item*/) { |
| ATRACE_CALL(); |
| Mutex::Autolock l(mMutex); |
| if (!mRecordingFrameAvailable) { |
| mRecordingFrameAvailable = true; |
| mRecordingFrameAvailableSignal.signal(); |
| } |
| |
| } |
| |
| bool StreamingProcessor::threadLoop() { |
| status_t res; |
| |
| { |
| Mutex::Autolock l(mMutex); |
| while (!mRecordingFrameAvailable) { |
| res = mRecordingFrameAvailableSignal.waitRelative( |
| mMutex, kWaitDuration); |
| if (res == TIMED_OUT) return true; |
| } |
| mRecordingFrameAvailable = false; |
| } |
| |
| do { |
| res = processRecordingFrame(); |
| } while (res == OK); |
| |
| return true; |
| } |
| |
| status_t StreamingProcessor::processRecordingFrame() { |
| ATRACE_CALL(); |
| status_t res; |
| sp<Camera2Heap> recordingHeap; |
| size_t heapIdx = 0; |
| nsecs_t timestamp; |
| |
| sp<Camera2Client> client = mClient.promote(); |
| if (client == 0) { |
| // Discard frames during shutdown |
| BufferItem imgBuffer; |
| res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); |
| if (res != OK) { |
| if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { |
| ALOGE("%s: Camera %d: Can't acquire recording buffer: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| } |
| return res; |
| } |
| mRecordingConsumer->releaseBuffer(imgBuffer); |
| return OK; |
| } |
| |
| { |
| /* acquire SharedParameters before mMutex so we don't dead lock |
| with Camera2Client code calling into StreamingProcessor */ |
| SharedParameters::Lock l(client->getParameters()); |
| Mutex::Autolock m(mMutex); |
| BufferItem imgBuffer; |
| res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); |
| if (res != OK) { |
| if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { |
| ALOGE("%s: Camera %d: Can't acquire recording buffer: %s (%d)", |
| __FUNCTION__, mId, strerror(-res), res); |
| } |
| return res; |
| } |
| timestamp = imgBuffer.mTimestamp; |
| |
| mRecordingFrameCount++; |
| ALOGVV("OnRecordingFrame: Frame %d", mRecordingFrameCount); |
| |
| if (l.mParameters.state != Parameters::RECORD && |
| l.mParameters.state != Parameters::VIDEO_SNAPSHOT) { |
| ALOGV("%s: Camera %d: Discarding recording image buffers " |
| "received after recording done", __FUNCTION__, |
| mId); |
| mRecordingConsumer->releaseBuffer(imgBuffer); |
| return INVALID_OPERATION; |
| } |
| |
| if (mRecordingHeap == 0) { |
| size_t payloadSize = sizeof(VideoNativeMetadata); |
| ALOGV("%s: Camera %d: Creating recording heap with %zu buffers of " |
| "size %zu bytes", __FUNCTION__, mId, |
| mRecordingHeapCount, payloadSize); |
| |
| mRecordingHeap = new Camera2Heap(payloadSize, mRecordingHeapCount, |
| "Camera2Client::RecordingHeap"); |
| if (mRecordingHeap->mHeap->getSize() == 0) { |
| ALOGE("%s: Camera %d: Unable to allocate memory for recording", |
| __FUNCTION__, mId); |
| mRecordingConsumer->releaseBuffer(imgBuffer); |
| return NO_MEMORY; |
| } |
| for (size_t i = 0; i < mRecordingBuffers.size(); i++) { |
| if (mRecordingBuffers[i].mBuf != |
| BufferItemConsumer::INVALID_BUFFER_SLOT) { |
| ALOGE("%s: Camera %d: Non-empty recording buffers list!", |
| __FUNCTION__, mId); |
| } |
| } |
| mRecordingBuffers.clear(); |
| mRecordingBuffers.setCapacity(mRecordingHeapCount); |
| mRecordingBuffers.insertAt(0, mRecordingHeapCount); |
| |
| mRecordingHeapHead = 0; |
| mRecordingHeapFree = mRecordingHeapCount; |
| } |
| |
| if (mRecordingHeapFree == 0) { |
| ALOGE("%s: Camera %d: No free recording buffers, dropping frame", |
| __FUNCTION__, mId); |
| mRecordingConsumer->releaseBuffer(imgBuffer); |
| return NO_MEMORY; |
| } |
| |
| heapIdx = mRecordingHeapHead; |
| mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount; |
| mRecordingHeapFree--; |
| |
| ALOGVV("%s: Camera %d: Timestamp %lld", |
| __FUNCTION__, mId, timestamp); |
| |
| ssize_t offset; |
| size_t size; |
| sp<IMemoryHeap> heap = |
| mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset, |
| &size); |
| |
| VideoNativeMetadata *payload = reinterpret_cast<VideoNativeMetadata*>( |
| (uint8_t*)heap->getBase() + offset); |
| payload->eType = kMetadataBufferTypeANWBuffer; |
| payload->pBuffer = imgBuffer.mGraphicBuffer->getNativeBuffer(); |
| // b/28466701 |
| payload->pBuffer = (ANativeWindowBuffer*)((uint8_t*)payload->pBuffer - |
| ICameraRecordingProxy::getCommonBaseAddress()); |
| payload->nFenceFd = -1; |
| |
| ALOGVV("%s: Camera %d: Sending out ANWBuffer %p", |
| __FUNCTION__, mId, payload->pBuffer); |
| |
| mRecordingBuffers.replaceAt(imgBuffer, heapIdx); |
| recordingHeap = mRecordingHeap; |
| } |
| |
| // Call outside locked parameters to allow re-entrancy from notification |
| Camera2Client::SharedCameraCallbacks::Lock l(client->mSharedCameraCallbacks); |
| if (l.mRemoteCallback != 0) { |
| l.mRemoteCallback->dataCallbackTimestamp(timestamp, |
| CAMERA_MSG_VIDEO_FRAME, |
| recordingHeap->mBuffers[heapIdx]); |
| } else { |
| ALOGW("%s: Camera %d: Remote callback gone", __FUNCTION__, mId); |
| } |
| |
| return OK; |
| } |
| |
| void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) { |
| ATRACE_CALL(); |
| status_t res; |
| |
| Mutex::Autolock m(mMutex); |
| // 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__, mId, |
| heap->getHeapID(), mRecordingHeap->mHeap->getHeapID()); |
| return; |
| } |
| |
| VideoNativeMetadata *payload = reinterpret_cast<VideoNativeMetadata*>( |
| (uint8_t*)heap->getBase() + offset); |
| |
| if (payload->eType != kMetadataBufferTypeANWBuffer) { |
| ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)", |
| __FUNCTION__, mId, payload->eType, |
| kMetadataBufferTypeANWBuffer); |
| return; |
| } |
| |
| // b/28466701 |
| payload->pBuffer = (ANativeWindowBuffer*)(((uint8_t*)payload->pBuffer) + |
| ICameraRecordingProxy::getCommonBaseAddress()); |
| |
| // Release the buffer back to the recording queue |
| size_t itemIndex; |
| for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { |
| const BufferItem item = mRecordingBuffers[itemIndex]; |
| if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT && |
| item.mGraphicBuffer->getNativeBuffer() == payload->pBuffer) { |
| break; |
| } |
| } |
| |
| if (itemIndex == mRecordingBuffers.size()) { |
| ALOGE("%s: Camera %d: Can't find returned ANW Buffer %p in list of " |
| "outstanding buffers", __FUNCTION__, mId, |
| payload->pBuffer); |
| return; |
| } |
| |
| ALOGVV("%s: Camera %d: Freeing returned ANW buffer %p index %d", __FUNCTION__, |
| mId, payload->pBuffer, itemIndex); |
| |
| res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to free recording frame " |
| "(Returned ANW buffer: %p): %s (%d)", __FUNCTION__, |
| mId, payload->pBuffer, strerror(-res), res); |
| return; |
| } |
| mRecordingBuffers.replaceAt(itemIndex); |
| |
| mRecordingHeapFree++; |
| ALOGV_IF(mRecordingHeapFree == mRecordingHeapCount, |
| "%s: Camera %d: All %d recording buffers returned", |
| __FUNCTION__, mId, mRecordingHeapCount); |
| } |
| |
| void StreamingProcessor::releaseAllRecordingFramesLocked() { |
| ATRACE_CALL(); |
| status_t res; |
| |
| if (mRecordingConsumer == 0) { |
| return; |
| } |
| |
| ALOGV("%s: Camera %d: Releasing all recording buffers", __FUNCTION__, |
| mId); |
| |
| size_t releasedCount = 0; |
| for (size_t itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { |
| const BufferItem item = mRecordingBuffers[itemIndex]; |
| if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT) { |
| res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]); |
| if (res != OK) { |
| ALOGE("%s: Camera %d: Unable to free recording frame " |
| "(buffer_handle_t: %p): %s (%d)", __FUNCTION__, |
| mId, item.mGraphicBuffer->handle, strerror(-res), res); |
| } |
| mRecordingBuffers.replaceAt(itemIndex); |
| releasedCount++; |
| } |
| } |
| |
| if (releasedCount > 0) { |
| ALOGW("%s: Camera %d: Force-freed %zu outstanding buffers " |
| "from previous recording session", __FUNCTION__, mId, releasedCount); |
| ALOGE_IF(releasedCount != mRecordingHeapCount - mRecordingHeapFree, |
| "%s: Camera %d: Force-freed %zu buffers, but expected %zu", |
| __FUNCTION__, mId, releasedCount, mRecordingHeapCount - mRecordingHeapFree); |
| } |
| |
| mRecordingHeapHead = 0; |
| mRecordingHeapFree = mRecordingHeapCount; |
| } |
| |
| bool StreamingProcessor::isStreamActive(const Vector<int32_t> &streams, |
| int32_t recordingStreamId) { |
| for (size_t i = 0; i < streams.size(); i++) { |
| if (streams[i] == recordingStreamId) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| status_t StreamingProcessor::dump(int fd, const Vector<String16>& /*args*/) { |
| String8 result; |
| |
| result.append(" Current requests:\n"); |
| if (mPreviewRequest.entryCount() != 0) { |
| result.append(" Preview request:\n"); |
| write(fd, result.string(), result.size()); |
| mPreviewRequest.dump(fd, 2, 6); |
| result.clear(); |
| } else { |
| result.append(" Preview request: undefined\n"); |
| } |
| |
| if (mRecordingRequest.entryCount() != 0) { |
| result = " Recording request:\n"; |
| write(fd, result.string(), result.size()); |
| mRecordingRequest.dump(fd, 2, 6); |
| result.clear(); |
| } else { |
| result = " Recording request: undefined\n"; |
| } |
| |
| const char* streamTypeString[] = { |
| "none", "preview", "record" |
| }; |
| result.append(String8::format(" Active request: %s (paused: %s)\n", |
| streamTypeString[mActiveRequest], |
| mPaused ? "yes" : "no")); |
| |
| write(fd, result.string(), result.size()); |
| |
| return OK; |
| } |
| |
| }; // namespace camera2 |
| }; // namespace android |