diff options
author | Igor Murashkin <iam@google.com> | 2013-04-15 14:59:22 -0700 |
---|---|---|
committer | Igor Murashkin <iam@google.com> | 2013-04-26 11:13:43 -0700 |
commit | 5a269fa72b419e7fe4bf6bf9b27eec8782b3a963 (patch) | |
tree | 74ba220ec1c528a724112152406d8b6a590f3661 /services | |
parent | 02f8457cf788e09e4f0c302dda453f13293009e7 (diff) | |
download | frameworks_av-5a269fa72b419e7fe4bf6bf9b27eec8782b3a963.zip frameworks_av-5a269fa72b419e7fe4bf6bf9b27eec8782b3a963.tar.gz frameworks_av-5a269fa72b419e7fe4bf6bf9b27eec8782b3a963.tar.bz2 |
Camera3: Add input stream support
- Untested with actual CAMERA3_STREAM_INPUT streams.
Bug: 8629088
Change-Id: Ia0c21ef0a2c951e401ea8babd15d3cceb4bb25a1
Diffstat (limited to 'services')
7 files changed, 473 insertions, 28 deletions
diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp index e53dbb5..3437130 100644 --- a/services/camera/libcameraservice/Camera3Device.cpp +++ b/services/camera/libcameraservice/Camera3Device.cpp @@ -30,6 +30,7 @@ #include <utils/Timers.h> #include "Camera3Device.h" #include "camera3/Camera3OutputStream.h" +#include "camera3/Camera3InputStream.h" using namespace android::camera3; @@ -369,6 +370,69 @@ status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time return mRequestThread->waitUntilRequestProcessed(requestId, timeout); } +status_t Camera3Device::createInputStream( + uint32_t width, uint32_t height, int format, int *id) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + + status_t res; + bool wasActive = false; + + switch (mStatus) { + case STATUS_ERROR: + ALOGE("%s: Device has encountered a serious error", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_UNINITIALIZED: + ALOGE("%s: Device not initialized", __FUNCTION__); + return INVALID_OPERATION; + case STATUS_IDLE: + // OK + break; + case STATUS_ACTIVE: + ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__); + mRequestThread->setPaused(true); + res = waitUntilDrainedLocked(); + if (res != OK) { + ALOGE("%s: Can't pause captures to reconfigure streams!", + __FUNCTION__); + mStatus = STATUS_ERROR; + return res; + } + wasActive = true; + break; + default: + ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus); + return INVALID_OPERATION; + } + assert(mStatus == STATUS_IDLE); + + if (mInputStream != 0) { + ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__); + return INVALID_OPERATION; + } + + sp<Camera3InputStream> newStream = new Camera3InputStream(mNextStreamId, + width, height, format); + + mInputStream = newStream; + + *id = mNextStreamId++; + + // Continue captures if active at start + if (wasActive) { + ALOGV("%s: Restarting activity to reconfigure streams", __FUNCTION__); + res = configureStreamsLocked(); + if (res != OK) { + ALOGE("%s: Can't reconfigure device for new stream %d: %s (%d)", + __FUNCTION__, mNextStreamId, strerror(-res), res); + return res; + } + mRequestThread->setPaused(false); + } + + return OK; +} + status_t Camera3Device::createStream(sp<ANativeWindow> consumer, uint32_t width, uint32_t height, int format, size_t size, int *id) { ATRACE_CALL(); @@ -1287,7 +1351,7 @@ bool Camera3Device::RequestThread::threadLoop() { if (nextRequest->mInputStream != NULL) { request.input_buffer = &inputBuffer; - res = nextRequest->mInputStream->getBuffer(&inputBuffer); + res = nextRequest->mInputStream->getInputBuffer(&inputBuffer); if (res != OK) { ALOGE("RequestThread: Can't get input buffer, skipping request:" " %s (%d)", strerror(-res), res); @@ -1358,6 +1422,23 @@ bool Camera3Device::RequestThread::threadLoop() { mLatestRequestSignal.signal(); } + // Return input buffer back to framework + if (request.input_buffer != NULL) { + Camera3Stream *stream = + Camera3Stream::cast(request.input_buffer->stream); + res = stream->returnInputBuffer(*(request.input_buffer)); + // Note: stream may be deallocated at this point, if this buffer was the + // last reference to it. + if (res != OK) { + ALOGE("%s: RequestThread: Can't return input buffer for frame %d to" + " its stream:%s (%d)", __FUNCTION__, + request.frame_number, strerror(-res), res); + // TODO: Report error upstream + } + } + + + return true; } @@ -1371,7 +1452,7 @@ void Camera3Device::RequestThread::cleanUpFailedRequest( } if (request.input_buffer != NULL) { request.input_buffer->status = CAMERA3_BUFFER_STATUS_ERROR; - nextRequest->mInputStream->returnBuffer(*(request.input_buffer), 0); + nextRequest->mInputStream->returnInputBuffer(*(request.input_buffer)); } for (size_t i = 0; i < request.num_output_buffers; i++) { outputBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h index 7f294e6..5c5faeb 100644 --- a/services/camera/libcameraservice/Camera3Device.h +++ b/services/camera/libcameraservice/Camera3Device.h @@ -82,6 +82,9 @@ class Camera3Device : virtual status_t createStream(sp<ANativeWindow> consumer, uint32_t width, uint32_t height, int format, size_t size, int *id); + virtual status_t createInputStream( + uint32_t width, uint32_t height, int format, + int *id); virtual status_t createReprocessStreamFromStream(int outputId, int *id); virtual status_t getStreamInfo(int id, diff --git a/services/camera/libcameraservice/camera3/Camera3InputStream.cpp b/services/camera/libcameraservice/camera3/Camera3InputStream.cpp index 8a48ee5..c7dd12a 100644 --- a/services/camera/libcameraservice/camera3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/camera3/Camera3InputStream.cpp @@ -18,6 +18,9 @@ #define ATRACE_TAG ATRACE_TAG_CAMERA //#define LOG_NDEBUG 0 +// This is needed for stdint.h to define INT64_MAX in C++ +#define __STDC_LIMIT_MACROS + #include <utils/Log.h> #include <utils/Trace.h> #include "Camera3InputStream.h" @@ -28,38 +31,262 @@ namespace camera3 { Camera3InputStream::Camera3InputStream(int id, uint32_t width, uint32_t height, int format) : - Camera3Stream(id, CAMERA3_STREAM_INPUT, width, height, 0, format) { + Camera3Stream(id, CAMERA3_STREAM_INPUT, width, height, 0, format), + mTotalBufferCount(0), + mDequeuedBufferCount(0), + mFrameCount(0), + mLastTimestamp(0) { + mCombinedFence = new Fence(); + + if (format == HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__); + mState = STATE_ERROR; + } +} + +Camera3InputStream::~Camera3InputStream() { + disconnectLocked(); } -status_t Camera3InputStream::getBufferLocked(camera3_stream_buffer *buffer) { - (void) buffer; - ALOGE("%s: Not implemented", __FUNCTION__); - return INVALID_OPERATION; +status_t Camera3InputStream::getInputBufferLocked( + camera3_stream_buffer *buffer) { + ATRACE_CALL(); + status_t res; + + // FIXME: will not work in (re-)registration + if (mState == STATE_IN_CONFIG || mState == STATE_IN_RECONFIG) { + ALOGE("%s: Stream %d: Buffer registration for input streams" + " not implemented (state %d)", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Allow acquire during IN_[RE]CONFIG for registration + if (mState != STATE_CONFIGURED && + mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { + ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Only limit acquire amount when fully configured + if (mState == STATE_CONFIGURED && + mDequeuedBufferCount == camera3_stream::max_buffers) { + ALOGE("%s: Stream %d: Already acquired maximum number of simultaneous" + " buffers (%d)", __FUNCTION__, mId, + camera3_stream::max_buffers); + return INVALID_OPERATION; + } + + ANativeWindowBuffer* anb; + int fenceFd; + + assert(mConsumer != 0); + + BufferItem bufferItem; + res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false); + + if (res != OK) { + ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + anb = bufferItem.mGraphicBuffer->getNativeBuffer(); + assert(anb != NULL); + fenceFd = bufferItem.mFence->dup(); + /** + * FenceFD now owned by HAL except in case of error, + * in which case we reassign it to acquire_fence + */ + + // Handing out a raw pointer to this object. Increment internal refcount. + incStrong(this); + buffer->stream = this; + buffer->buffer = &(anb->handle); + buffer->acquire_fence = fenceFd; + buffer->release_fence = -1; + buffer->status = CAMERA3_BUFFER_STATUS_OK; + + mDequeuedBufferCount++; + + mBuffersInFlight.push_back(bufferItem); + + return OK; } -status_t Camera3InputStream::returnBufferLocked( - const camera3_stream_buffer &buffer, - nsecs_t timestamp) { - (void) timestamp; - (void) buffer; - ALOGE("%s: Not implemented", __FUNCTION__); - return INVALID_OPERATION; +status_t Camera3InputStream::returnInputBufferLocked( + const camera3_stream_buffer &buffer) { + ATRACE_CALL(); + status_t res; + + // returnBuffer may be called from a raw pointer, not a sp<>, and we'll be + // decrementing the internal refcount next. In case this is the last ref, we + // might get destructed on the decStrong(), so keep an sp around until the + // end of the call - otherwise have to sprinkle the decStrong on all exit + // points. + sp<Camera3InputStream> keepAlive(this); + decStrong(this); + + // Allow buffers to be returned in the error state, to allow for disconnect + // and in the in-config states for registration + if (mState == STATE_CONSTRUCTED) { + ALOGE("%s: Stream %d: Can't return buffers in unconfigured state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + if (mDequeuedBufferCount == 0) { + ALOGE("%s: Stream %d: No buffers outstanding to return", __FUNCTION__, + mId); + return INVALID_OPERATION; + } + + bool bufferFound = false; + BufferItem bufferItem; + { + // Find the buffer we are returning + Vector<BufferItem>::iterator it, end; + for (it = mBuffersInFlight.begin(), end = mBuffersInFlight.end(); + it != end; + ++it) { + + const BufferItem& tmp = *it; + ANativeWindowBuffer *anb = tmp.mGraphicBuffer->getNativeBuffer(); + if (anb != NULL && &(anb->handle) == buffer.buffer) { + bufferFound = true; + bufferItem = tmp; + mBuffersInFlight.erase(it); + mDequeuedBufferCount--; + } + } + } + if (!bufferFound) { + ALOGE("%s: Stream %d: Can't return buffer that wasn't sent to HAL", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + + if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { + if (buffer.release_fence != -1) { + ALOGE("%s: Stream %d: HAL should not set release_fence(%d) when " + "there is an error", __FUNCTION__, mId, buffer.release_fence); + close(buffer.release_fence); + } + + /** + * Reassign release fence as the acquire fence incase of error + */ + const_cast<camera3_stream_buffer*>(&buffer)->release_fence = + buffer.acquire_fence; + } + + /** + * Unconditionally return buffer to the buffer queue. + * - Fwk takes over the release_fence ownership + */ + sp<Fence> releaseFence = new Fence(buffer.release_fence); + res = mConsumer->releaseBuffer(bufferItem, releaseFence); + if (res != OK) { + ALOGE("%s: Stream %d: Error releasing buffer back to buffer queue:" + " %s (%d)", __FUNCTION__, mId, strerror(-res), res); + return res; + } + + mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence); + + mBufferReturnedSignal.signal(); + + return OK; + } bool Camera3InputStream::hasOutstandingBuffersLocked() const { - ALOGE("%s: Not implemented", __FUNCTION__); + nsecs_t signalTime = mCombinedFence->getSignalTime(); + ALOGV("%s: Stream %d: Has %d outstanding buffers," + " buffer signal time is %lld", + __FUNCTION__, mId, mDequeuedBufferCount, signalTime); + if (mDequeuedBufferCount > 0 || signalTime == INT64_MAX) { + return true; + } return false; } status_t Camera3InputStream::waitUntilIdle(nsecs_t timeout) { - (void) timeout; - ALOGE("%s: Not implemented", __FUNCTION__); - return INVALID_OPERATION; + status_t res; + { + Mutex::Autolock l(mLock); + while (mDequeuedBufferCount > 0) { + if (timeout != TIMEOUT_NEVER) { + nsecs_t startTime = systemTime(); + res = mBufferReturnedSignal.waitRelative(mLock, timeout); + if (res == TIMED_OUT) { + return res; + } else if (res != OK) { + ALOGE("%s: Error waiting for outstanding buffers: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + nsecs_t deltaTime = systemTime() - startTime; + if (timeout <= deltaTime) { + timeout = 0; + } else { + timeout -= deltaTime; + } + } else { + res = mBufferReturnedSignal.wait(mLock); + if (res != OK) { + ALOGE("%s: Error waiting for outstanding buffers: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + } + } + } + + // No lock + + unsigned int timeoutMs; + if (timeout == TIMEOUT_NEVER) { + timeoutMs = Fence::TIMEOUT_NEVER; + } else if (timeout == 0) { + timeoutMs = 0; + } else { + // Round up to wait at least 1 ms + timeoutMs = (timeout + 999999) / 1000000; + } + + return mCombinedFence->wait(timeoutMs); +} + +size_t Camera3InputStream::getBufferCountLocked() { + return mTotalBufferCount; } status_t Camera3InputStream::disconnectLocked() { - ALOGE("%s: Not implemented", __FUNCTION__); - return INVALID_OPERATION; + switch (mState) { + case STATE_IN_RECONFIG: + case STATE_CONFIGURED: + // OK + break; + default: + // No connection, nothing to do + return OK; + } + + if (mDequeuedBufferCount > 0) { + ALOGE("%s: Can't disconnect with %d buffers still acquired!", + __FUNCTION__, mDequeuedBufferCount); + return INVALID_OPERATION; + } + + assert(mBuffersInFlight.size() == 0); + + /** + * no-op since we can't disconnect the producer from the consumer-side + */ + + mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG : STATE_CONSTRUCTED; + return OK; } sp<IGraphicBufferProducer> Camera3InputStream::getProducerInterface() const { @@ -67,9 +294,71 @@ sp<IGraphicBufferProducer> Camera3InputStream::getProducerInterface() const { } void Camera3InputStream::dump(int fd, const Vector<String16> &args) const { - (void) fd; (void) args; - ALOGE("%s: Not implemented", __FUNCTION__); + String8 lines; + lines.appendFormat(" Stream[%d]: Input\n", mId); + lines.appendFormat(" State: %d\n", mState); + lines.appendFormat(" Dims: %d x %d, format 0x%x\n", + camera3_stream::width, camera3_stream::height, + camera3_stream::format); + lines.appendFormat(" Max size: %d\n", mMaxSize); + lines.appendFormat(" Usage: %d, max HAL buffers: %d\n", + camera3_stream::usage, camera3_stream::max_buffers); + lines.appendFormat(" Frames produced: %d, last timestamp: %lld ns\n", + mFrameCount, mLastTimestamp); + lines.appendFormat(" Total buffers: %d, currently acquired: %d\n", + mTotalBufferCount, mDequeuedBufferCount); + write(fd, lines.string(), lines.size()); +} + +status_t Camera3InputStream::configureQueueLocked() { + status_t res; + + switch (mState) { + case STATE_IN_RECONFIG: + res = disconnectLocked(); + if (res != OK) { + return res; + } + break; + case STATE_IN_CONFIG: + // OK + break; + default: + ALOGE("%s: Bad state: %d", __FUNCTION__, mState); + return INVALID_OPERATION; + } + + assert(mMaxSize == 0); + assert(camera3_stream::format != HAL_PIXEL_FORMAT_BLOB); + + mTotalBufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS + + camera3_stream::max_buffers; + mDequeuedBufferCount = 0; + mFrameCount = 0; + + if (mConsumer.get() == 0) { + mConsumer = new BufferItemConsumer(camera3_stream::usage, + mTotalBufferCount, + /*synchronousMode*/true); + mConsumer->setName(String8::format("Camera3-InputStream-%d", mId)); + } + + res = mConsumer->setDefaultBufferSize(camera3_stream::width, + camera3_stream::height); + if (res != OK) { + ALOGE("%s: Stream %d: Could not set buffer dimensions %dx%d", + __FUNCTION__, mId, camera3_stream::width, camera3_stream::height); + return res; + } + res = mConsumer->setDefaultBufferFormat(camera3_stream::format); + if (res != OK) { + ALOGE("%s: Stream %d: Could not set buffer format %d", + __FUNCTION__, mId, camera3_stream::format); + return res; + } + + return OK; } }; // namespace camera3 diff --git a/services/camera/libcameraservice/camera3/Camera3InputStream.h b/services/camera/libcameraservice/camera3/Camera3InputStream.h index c4b5dd9..fd9f464 100644 --- a/services/camera/libcameraservice/camera3/Camera3InputStream.h +++ b/services/camera/libcameraservice/camera3/Camera3InputStream.h @@ -29,6 +29,10 @@ namespace camera3 { /** * A class for managing a single stream of input data to the camera device. + * + * This class serves as a consumer adapter for the HAL, and will consume the + * buffers by feeding them into the HAL, as well as releasing the buffers back + * the buffers once the HAL is done with them. */ class Camera3InputStream : public Camera3Stream { public: @@ -36,6 +40,7 @@ class Camera3InputStream : public Camera3Stream { * Set up a stream for formats that have fixed size, such as RAW and YUV. */ Camera3InputStream(int id, uint32_t width, uint32_t height, int format); + ~Camera3InputStream(); virtual status_t waitUntilIdle(nsecs_t timeout); virtual void dump(int fd, const Vector<String16> &args) const; @@ -49,18 +54,32 @@ class Camera3InputStream : public Camera3Stream { private: + typedef BufferItemConsumer::BufferItem BufferItem; + sp<BufferItemConsumer> mConsumer; + Vector<BufferItem> mBuffersInFlight; + size_t mTotalBufferCount; + size_t mDequeuedBufferCount; + Condition mBufferReturnedSignal; + uint32_t mFrameCount; + nsecs_t mLastTimestamp; + + // The merged release fence for all returned buffers + sp<Fence> mCombinedFence; /** * Camera3Stream interface */ - virtual status_t getBufferLocked(camera3_stream_buffer *buffer); - virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer, - nsecs_t timestamp); + virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnInputBufferLocked( + const camera3_stream_buffer &buffer); virtual bool hasOutstandingBuffersLocked() const; virtual status_t disconnectLocked(); + virtual status_t configureQueueLocked(); + virtual size_t getBufferCountLocked(); + }; // class Camera3InputStream }; // namespace camera3 diff --git a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp index 276b940..ec8cf0d 100644 --- a/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/camera3/Camera3OutputStream.cpp @@ -298,7 +298,7 @@ status_t Camera3OutputStream::configureQueueLocked() { switch (mState) { case STATE_IN_RECONFIG: - res = disconnect(); + res = disconnectLocked(); if (res != OK) { return res; } diff --git a/services/camera/libcameraservice/camera3/Camera3Stream.cpp b/services/camera/libcameraservice/camera3/Camera3Stream.cpp index cf3072b..bc259b6 100644 --- a/services/camera/libcameraservice/camera3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/camera3/Camera3Stream.cpp @@ -188,6 +188,18 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, return returnBufferLocked(buffer, timestamp); } +status_t Camera3Stream::getInputBuffer(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return getInputBufferLocked(buffer); +} + +status_t Camera3Stream::returnInputBuffer(const camera3_stream_buffer &buffer) { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + return returnInputBufferLocked(buffer); +} + bool Camera3Stream::hasOutstandingBuffers() const { ATRACE_CALL(); Mutex::Autolock l(mLock); @@ -259,6 +271,25 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { return res; } +status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *) { + ALOGE("%s: This type of stream does not support output", __FUNCTION__); + return INVALID_OPERATION; +} +status_t Camera3Stream::returnBufferLocked(const camera3_stream_buffer &, + nsecs_t) { + ALOGE("%s: This type of stream does not support output", __FUNCTION__); + return INVALID_OPERATION; +} +status_t Camera3Stream::getInputBufferLocked(camera3_stream_buffer *) { + ALOGE("%s: This type of stream does not support input", __FUNCTION__); + return INVALID_OPERATION; +} +status_t Camera3Stream::returnInputBufferLocked( + const camera3_stream_buffer &) { + ALOGE("%s: This type of stream does not support input", __FUNCTION__); + return INVALID_OPERATION; +} + }; // namespace camera3 }; // namespace android diff --git a/services/camera/libcameraservice/camera3/Camera3Stream.h b/services/camera/libcameraservice/camera3/Camera3Stream.h index 2364cfd..46a3872 100644 --- a/services/camera/libcameraservice/camera3/Camera3Stream.h +++ b/services/camera/libcameraservice/camera3/Camera3Stream.h @@ -157,6 +157,25 @@ class Camera3Stream : nsecs_t timestamp); /** + * Fill in the camera3_stream_buffer with the next valid buffer for this + * stream, to hand over to the HAL. + * + * This method may only be called once finishConfiguration has been called. + * For bidirectional streams, this method applies to the input-side + * buffers. + * + */ + status_t getInputBuffer(camera3_stream_buffer *buffer); + + /** + * Return a buffer to the stream after use by the HAL. + * + * This method may only be called for buffers provided by getBuffer(). + * For bidirectional streams, this method applies to the input-side buffers + */ + status_t returnInputBuffer(const camera3_stream_buffer &buffer); + + /** * Whether any of the stream's buffers are currently in use by the HAL, * including buffers that have been returned but not yet had their * release fence signaled. @@ -215,9 +234,12 @@ class Camera3Stream : // cast to camera3_stream*, implementations must increment the // refcount of the stream manually in getBufferLocked, and decrement it in // returnBufferLocked. - virtual status_t getBufferLocked(camera3_stream_buffer *buffer) = 0; + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); virtual status_t returnBufferLocked(const camera3_stream_buffer &buffer, - nsecs_t timestamp) = 0; + nsecs_t timestamp); + virtual status_t getInputBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnInputBufferLocked( + const camera3_stream_buffer &buffer); virtual bool hasOutstandingBuffersLocked() const = 0; virtual status_t disconnectLocked() = 0; |