OSDN Git Service

Camera3: Add input stream support
authorIgor Murashkin <iam@google.com>
Mon, 15 Apr 2013 21:59:22 +0000 (14:59 -0700)
committerIgor Murashkin <iam@google.com>
Fri, 26 Apr 2013 18:13:43 +0000 (11:13 -0700)
- Untested with actual CAMERA3_STREAM_INPUT streams.

Bug: 8629088

Change-Id: Ia0c21ef0a2c951e401ea8babd15d3cceb4bb25a1

services/camera/libcameraservice/Camera3Device.cpp
services/camera/libcameraservice/Camera3Device.h
services/camera/libcameraservice/camera3/Camera3InputStream.cpp
services/camera/libcameraservice/camera3/Camera3InputStream.h
services/camera/libcameraservice/camera3/Camera3OutputStream.cpp
services/camera/libcameraservice/camera3/Camera3Stream.cpp
services/camera/libcameraservice/camera3/Camera3Stream.h

index e53dbb5..3437130 100644 (file)
@@ -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;
index 7f294e6..5c5faeb 100644 (file)
@@ -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,
index 8a48ee5..c7dd12a 100644 (file)
@@ -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
index c4b5dd9..fd9f464 100644 (file)
@@ -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
index 276b940..ec8cf0d 100644 (file)
@@ -298,7 +298,7 @@ status_t Camera3OutputStream::configureQueueLocked() {
 
     switch (mState) {
         case STATE_IN_RECONFIG:
-            res = disconnect();
+            res = disconnectLocked();
             if (res != OK) {
                 return res;
             }
index cf3072b..bc259b6 100644 (file)
@@ -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
index 2364cfd..46a3872 100644 (file)
@@ -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;