diff options
| author | Eino-Ville Talvala <etalvala@google.com> | 2015-04-11 13:15:45 -0700 | 
|---|---|---|
| committer | Eino-Ville Talvala <etalvala@google.com> | 2015-04-15 10:51:33 -0700 | 
| commit | 4d44cad22ea925a651463f2d51d6586c14d4b787 (patch) | |
| tree | 3e9fa49f6f98a8deee591f58054488ddc6e46505 /services/camera | |
| parent | 4123d6db0642cd13e69230705b12d6b6fee6f73f (diff) | |
| download | frameworks_av-4d44cad22ea925a651463f2d51d6586c14d4b787.zip frameworks_av-4d44cad22ea925a651463f2d51d6586c14d4b787.tar.gz frameworks_av-4d44cad22ea925a651463f2d51d6586c14d4b787.tar.bz2  | |
Camera2: Add prepare for output streams
The prepare call asynchronously pre-allocates buffers for a given
output stream, and then fires the onPrepared callback.
Not implemented for Camera2Device or used in Camera2Client.
Change-Id: I1cccdfff846dd6985133c591dbdceed823929ade
Diffstat (limited to 'services/camera')
20 files changed, 641 insertions, 37 deletions
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 0016174..bf1692d 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -671,6 +671,42 @@ status_t CameraDeviceClient::flush(int64_t* lastFrameNumber) {      return mDevice->flush(lastFrameNumber);  } +status_t CameraDeviceClient::prepare(int streamId) { +    ATRACE_CALL(); +    ALOGV("%s", __FUNCTION__); + +    status_t res = OK; +    if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + +    Mutex::Autolock icl(mBinderSerializationLock); + +    // Guard against trying to prepare non-created streams +    ssize_t index = NAME_NOT_FOUND; +    for (size_t i = 0; i < mStreamMap.size(); ++i) { +        if (streamId == mStreamMap.valueAt(i)) { +            index = i; +            break; +        } +    } + +    if (index == NAME_NOT_FOUND) { +        ALOGW("%s: Camera %d: Invalid stream ID (%d) specified, no stream " +              "created yet", __FUNCTION__, mCameraId, streamId); +        return BAD_VALUE; +    } + +    // Also returns BAD_VALUE if stream ID was not valid +    res = mDevice->prepare(streamId); + +    if (res == BAD_VALUE) { +        ALOGE("%s: Camera %d: Unexpected BAD_VALUE when preparing stream, but we" +              " already checked and the stream ID (%d) should be valid.", +              __FUNCTION__, mCameraId, streamId); +    } + +    return res; +} +  status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {      String8 result;      result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n", @@ -730,6 +766,14 @@ void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras,      }  } +void CameraDeviceClient::notifyPrepared(int streamId) { +    // Thread safe. Don't bother locking. +    sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback(); +    if (remoteCb != 0) { +        remoteCb->onPrepared(streamId); +    } +} +  void CameraDeviceClient::detachDevice() {      if (mDevice == 0) return; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index f2d8899..b8d8bea 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -109,6 +109,9 @@ public:      virtual status_t      flush(/*out*/                                  int64_t* lastFrameNumber = NULL); +    // Prepare stream by preallocating its buffers +    virtual status_t      prepare(int streamId); +      /**       * Interface used by CameraService       */ @@ -135,6 +138,7 @@ public:      virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,                               const CaptureResultExtras& resultExtras);      virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp); +    virtual void notifyPrepared(int streamId);      /**       * Interface used by independent components of CameraDeviceClient. diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index c0c2314..ba0b264 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -280,6 +280,14 @@ void Camera2ClientBase<TClientBase>::notifyAutoWhitebalance(uint8_t newState,  }  template <typename TClientBase> +void Camera2ClientBase<TClientBase>::notifyPrepared(int streamId) { +    (void)streamId; + +    ALOGV("%s: Stream %d now prepared", +            __FUNCTION__, streamId); +} + +template <typename TClientBase>  int Camera2ClientBase<TClientBase>::getCameraId() const {      return TClientBase::mCameraId;  } diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index 168ea0a..f1cacdf 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -72,7 +72,7 @@ public:      virtual void          notifyAutoExposure(uint8_t newState, int triggerId);      virtual void          notifyAutoWhitebalance(uint8_t newState,                                                   int triggerId); - +    virtual void          notifyPrepared(int streamId);      int                   getCameraId() const;      const sp<CameraDeviceBase>& diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index 6ece359..f02fc32 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -199,6 +199,7 @@ class CameraDeviceBase : public virtual RefBase {          virtual void notifyIdle() = 0;          virtual void notifyShutter(const CaptureResultExtras &resultExtras,                  nsecs_t timestamp) = 0; +        virtual void notifyPrepared(int streamId) = 0;          // Required only for API1          virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0; @@ -281,6 +282,12 @@ class CameraDeviceBase : public virtual RefBase {      virtual status_t flush(int64_t *lastFrameNumber = NULL) = 0;      /** +     * Prepare stream by preallocating buffers for it asynchronously. +     * Calls notifyPrepared() once allocation is complete. +     */ +    virtual status_t prepare(int streamId) = 0; + +    /**       * Get the HAL device version.       */      virtual uint32_t getDeviceVersion() = 0; diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index 3c5ea9d..f6645f3 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -618,6 +618,12 @@ status_t Camera2Device::flush(int64_t* /*lastFrameNumber*/) {      return waitUntilDrained();  } +status_t Camera2Device::prepare(int streamId) { +    ATRACE_CALL(); +    ALOGE("%s: Camera %d: unimplemented", __FUNCTION__, mId); +    return NO_INIT; +} +  uint32_t Camera2Device::getDeviceVersion() {      ATRACE_CALL();      return mDeviceVersion; diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index 9972606..fd1240a 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -84,6 +84,9 @@ class Camera2Device: public CameraDeviceBase {              buffer_handle_t *buffer, wp<BufferReleasedListener> listener);      // Flush implemented as just a wait      virtual status_t flush(int64_t *lastFrameNumber = NULL); +    // Prepare is a no-op +    virtual status_t prepare(int streamId); +      virtual uint32_t getDeviceVersion();      virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index dc752a6..ec9c70c 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -175,6 +175,8 @@ status_t Camera3Device::initialize(CameraModule *module)          return res;      } +    mPreparerThread = new PreparerThread(); +      /** Everything is good to go */      mDeviceVersion = device->common.version; @@ -1190,7 +1192,8 @@ status_t Camera3Device::setNotifyCallback(NotificationListener *listener) {          ALOGW("%s: Replacing old callback listener", __FUNCTION__);      }      mListener = listener; -    mRequestThread->setNotifyCallback(listener); +    mRequestThread->setNotificationListener(listener); +    mPreparerThread->setNotificationListener(listener);      return OK;  } @@ -1336,6 +1339,34 @@ status_t Camera3Device::flush(int64_t *frameNumber) {      return res;  } +status_t Camera3Device::prepare(int streamId) { +    ATRACE_CALL(); +    ALOGV("%s: Camera %d: Preparing stream %d", __FUNCTION__, mId, streamId); + +    sp<Camera3StreamInterface> stream; +    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId); +    if (outputStreamIdx == NAME_NOT_FOUND) { +        CLOGE("Stream %d does not exist", streamId); +        return BAD_VALUE; +    } + +    stream = mOutputStreams.editValueAt(outputStreamIdx); + +    if (stream->isUnpreparable() || stream->hasOutstandingBuffers() ) { +        ALOGE("%s: Camera %d: Stream %d has already been a request target", +                __FUNCTION__, mId, streamId); +        return BAD_VALUE; +    } + +    if (mRequestThread->isStreamPending(stream)) { +        ALOGE("%s: Camera %d: Stream %d is already a target in a pending request", +                __FUNCTION__, mId, streamId); +        return BAD_VALUE; +    } + +    return mPreparerThread->prepare(stream); +} +  uint32_t Camera3Device::getDeviceVersion() {      ATRACE_CALL();      Mutex::Autolock il(mInterfaceLock); @@ -1409,6 +1440,11 @@ sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(                  return NULL;              }          } +        // Check if stream is being prepared +        if (mInputStream->isPreparing()) { +            CLOGE("Request references an input stream that's being prepared!"); +            return NULL; +        }          newRequest->mInputStream = mInputStream;          newRequest->mSettings.erase(ANDROID_REQUEST_INPUT_STREAMS); @@ -1441,6 +1477,11 @@ sp<Camera3Device::CaptureRequest> Camera3Device::createCaptureRequest(                  return NULL;              }          } +        // Check if stream is being prepared +        if (stream->isPreparing()) { +            CLOGE("Request references an output stream that's being prepared!"); +            return NULL; +        }          newRequest->mOutputStreams.push(stream);      } @@ -1916,7 +1957,6 @@ bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag,      return true;  } -  void Camera3Device::returnOutputBuffers(          const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,          nsecs_t timestamp) { @@ -2416,7 +2456,7 @@ CameraMetadata Camera3Device::getLatestRequestLocked() {  Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,          sp<StatusTracker> statusTracker,          camera3_device_t *hal3Device) : -        Thread(false), +        Thread(/*canCallJava*/false),          mParent(parent),          mStatusTracker(statusTracker),          mHal3Device(hal3Device), @@ -2432,7 +2472,7 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,      mStatusId = statusTracker->addComponent();  } -void Camera3Device::RequestThread::setNotifyCallback( +void Camera3Device::RequestThread::setNotificationListener(          NotificationListener *listener) {      Mutex::Autolock l(mRequestLock);      mListener = listener; @@ -2846,6 +2886,26 @@ CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {      return mLatestRequest;  } +bool Camera3Device::RequestThread::isStreamPending( +        sp<Camera3StreamInterface>& stream) { +    Mutex::Autolock l(mRequestLock); + +    for (const auto& request : mRequestQueue) { +        for (const auto& s : request->mOutputStreams) { +            if (stream == s) return true; +        } +        if (stream == request->mInputStream) return true; +    } + +    for (const auto& request : mRepeatingRequests) { +        for (const auto& s : request->mOutputStreams) { +            if (stream == s) return true; +        } +        if (stream == request->mInputStream) return true; +    } + +    return false; +}  void Camera3Device::RequestThread::cleanUpFailedRequest(          camera3_capture_request_t &request, @@ -3193,6 +3253,138 @@ status_t Camera3Device::RequestThread::addDummyTriggerIds(      return OK;  } +/** + * PreparerThread inner class methods + */ + +Camera3Device::PreparerThread::PreparerThread() : +        Thread(/*canCallJava*/false), mActive(false), mCancelNow(false) { +} + +Camera3Device::PreparerThread::~PreparerThread() { +    Thread::requestExitAndWait(); +    if (mCurrentStream != nullptr) { +        mCurrentStream->cancelPrepare(); +        ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId()); +        mCurrentStream.clear(); +    } +    clear(); +} + +status_t Camera3Device::PreparerThread::prepare(sp<Camera3StreamInterface>& stream) { +    status_t res; + +    Mutex::Autolock l(mLock); + +    res = stream->startPrepare(); +    if (res == OK) { +        // No preparation needed, fire listener right off +        ALOGV("%s: Stream %d already prepared", __FUNCTION__, stream->getId()); +        if (mListener) { +            mListener->notifyPrepared(stream->getId()); +        } +        return OK; +    } else if (res != NOT_ENOUGH_DATA) { +        return res; +    } + +    // Need to prepare, start up thread if necessary +    if (!mActive) { +        // mRunning will change to false before the thread fully shuts down, so wait to be sure it +        // isn't running +        Thread::requestExitAndWait(); +        res = Thread::run("C3PrepThread", PRIORITY_BACKGROUND); +        if (res != OK) { +            ALOGE("%s: Unable to start preparer stream: %d (%s)", __FUNCTION__, res, strerror(-res)); +            if (mListener) { +                mListener->notifyPrepared(stream->getId()); +            } +            return res; +        } +        mCancelNow = false; +        mActive = true; +        ALOGV("%s: Preparer stream started", __FUNCTION__); +    } + +    // queue up the work +    mPendingStreams.push_back(stream); +    ALOGV("%s: Stream %d queued for preparing", __FUNCTION__, stream->getId()); + +    return OK; +} + +status_t Camera3Device::PreparerThread::clear() { +    status_t res; + +    Mutex::Autolock l(mLock); + +    for (const auto& stream : mPendingStreams) { +        stream->cancelPrepare(); +    } +    mPendingStreams.clear(); +    mCancelNow = true; + +    return OK; +} + +void Camera3Device::PreparerThread::setNotificationListener(NotificationListener *listener) { +    Mutex::Autolock l(mLock); +    mListener = listener; +} + +bool Camera3Device::PreparerThread::threadLoop() { +    status_t res; +    { +        Mutex::Autolock l(mLock); +        if (mCurrentStream == nullptr) { +            // End thread if done with work +            if (mPendingStreams.empty()) { +                ALOGV("%s: Preparer stream out of work", __FUNCTION__); +                // threadLoop _must not_ re-acquire mLock after it sets mActive to false; would +                // cause deadlock with prepare()'s requestExitAndWait triggered by !mActive. +                mActive = false; +                return false; +            } + +            // Get next stream to prepare +            auto it = mPendingStreams.begin(); +            mCurrentStream = *it; +            mPendingStreams.erase(it); +            ATRACE_ASYNC_BEGIN("stream prepare", mCurrentStream->getId()); +            ALOGV("%s: Preparing stream %d", __FUNCTION__, mCurrentStream->getId()); +        } else if (mCancelNow) { +            mCurrentStream->cancelPrepare(); +            ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId()); +            ALOGV("%s: Cancelling stream %d prepare", __FUNCTION__, mCurrentStream->getId()); +            mCurrentStream.clear(); +            mCancelNow = false; +            return true; +        } +    } + +    res = mCurrentStream->prepareNextBuffer(); +    if (res == NOT_ENOUGH_DATA) return true; +    if (res != OK) { +        // Something bad happened; try to recover by cancelling prepare and +        // signalling listener anyway +        ALOGE("%s: Stream %d returned error %d (%s) during prepare", __FUNCTION__, +                mCurrentStream->getId(), res, strerror(-res)); +        mCurrentStream->cancelPrepare(); +    } + +    // This stream has finished, notify listener +    Mutex::Autolock l(mLock); +    if (mListener) { +        ALOGV("%s: Stream %d prepare done, signaling listener", __FUNCTION__, +                mCurrentStream->getId()); +        mListener->notifyPrepared(mCurrentStream->getId()); +    } + +    ATRACE_ASYNC_END("stream prepare", mCurrentStream->getId()); +    mCurrentStream.clear(); + +    return true; +}  /**   * Static callback forwarding methods from HAL to instance diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index b08ba81..4fbcb2e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -138,6 +138,8 @@ class Camera3Device :      virtual status_t flush(int64_t *lastFrameNumber = NULL); +    virtual status_t prepare(int streamId); +      virtual uint32_t getDeviceVersion();      virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; @@ -374,7 +376,7 @@ class Camera3Device :                  sp<camera3::StatusTracker> statusTracker,                  camera3_device_t *hal3Device); -        void     setNotifyCallback(NotificationListener *listener); +        void     setNotificationListener(NotificationListener *listener);          /**           * Call after stream (re)-configuration is completed. @@ -438,6 +440,12 @@ class Camera3Device :           */          CameraMetadata getLatestRequest() const; +        /** +         * Returns true if the stream is a target of any queued or repeating +         * capture request +         */ +        bool isStreamPending(sp<camera3::Camera3StreamInterface>& stream); +        protected:          virtual bool threadLoop(); @@ -559,7 +567,6 @@ class Camera3Device :          Vector<camera3_stream_buffer_t> pendingOutputBuffers; -          // Fields used by the partial result only          struct PartialResultInFlight {              // Set by process_capture_result once 3A has been sent to clients @@ -610,7 +617,8 @@ class Camera3Device :                  resultExtras(extras),                  hasInputBuffer(hasInput){          } -}; +    }; +      // Map from frame number to the in-flight request state      typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap; @@ -642,6 +650,45 @@ class Camera3Device :      sp<camera3::StatusTracker> mStatusTracker;      /** +     * Thread for preparing streams +     */ +    class PreparerThread : private Thread, public virtual RefBase { +      public: +        PreparerThread(); +        ~PreparerThread(); + +        void setNotificationListener(NotificationListener *listener); + +        /** +         * Queue up a stream to be prepared. Streams are processed by +         * a background thread in FIFO order +         */ +        status_t prepare(sp<camera3::Camera3StreamInterface>& stream); + +        /** +         * Cancel all current and pending stream preparation +         */ +        status_t clear(); + +      private: +        Mutex mLock; + +        virtual bool threadLoop(); + +        // Guarded by mLock + +        NotificationListener *mListener; +        List<sp<camera3::Camera3StreamInterface> > mPendingStreams; +        bool mActive; +        bool mCancelNow; + +        // Only accessed by threadLoop and the destructor + +        sp<camera3::Camera3StreamInterface> mCurrentStream; +    }; +    sp<PreparerThread> mPreparerThread; + +    /**       * Output result queue and current HAL device 3A state       */ diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp index 01edfff..ecb8ac8 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp @@ -87,7 +87,7 @@ status_t Camera3DummyStream::disconnectLocked() {      return OK;  } -status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) { +status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) const {      *usage = DUMMY_USAGE;      return OK;  } diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h index d023c57..3a3dbf4 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.h +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h @@ -89,7 +89,7 @@ class Camera3DummyStream :      virtual status_t configureQueueLocked(); -    virtual status_t getEndpointUsage(uint32_t *usage); +    virtual status_t getEndpointUsage(uint32_t *usage) const;  }; // class Camera3DummyStream diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp index 8696413..23b1c45 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -67,13 +67,18 @@ bool Camera3IOStreamBase::hasOutstandingBuffersLocked() const {  void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const {      (void) args;      String8 lines; + +    uint32_t consumerUsage = 0; +    status_t res = getEndpointUsage(&consumerUsage); +    if (res != OK) consumerUsage = 0; +      lines.appendFormat("      State: %d\n", mState); -    lines.appendFormat("      Dims: %d x %d, format 0x%x\n", +    lines.appendFormat("      Dims: %d x %d, format 0x%x, dataspace 0x%x\n",              camera3_stream::width, camera3_stream::height, -            camera3_stream::format); +            camera3_stream::format, camera3_stream::data_space);      lines.appendFormat("      Max size: %zu\n", mMaxSize); -    lines.appendFormat("      Usage: %d, max HAL buffers: %d\n", -            camera3_stream::usage, camera3_stream::max_buffers); +    lines.appendFormat("      Combined usage: %d, max HAL buffers: %d\n", +            camera3_stream::usage | consumerUsage, camera3_stream::max_buffers);      lines.appendFormat("      Frames produced: %d, last timestamp: %" PRId64 " ns\n",              mFrameCount, mLastTimestamp);      lines.appendFormat("      Total buffers: %zu, currently dequeued: %zu\n", @@ -156,13 +161,11 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer,      // Inform tracker about becoming busy      if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG && -            mState != STATE_IN_RECONFIG) { +            mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) {          /**           * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers           * before/after register_stream_buffers during initial configuration -         * or re-configuration. -         * -         * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2 +         * or re-configuration, or during prepare pre-allocation           */          sp<StatusTracker> statusTracker = mStatusTracker.promote();          if (statusTracker != 0) { @@ -177,9 +180,11 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer,  }  status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const { -    // Allow dequeue during IN_[RE]CONFIG for registration +    // Allow dequeue during IN_[RE]CONFIG for registration, in +    // PREPARING for pre-allocation      if (mState != STATE_CONFIGURED && -            mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { +            mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG && +            mState != STATE_PREPARING) {          ALOGE("%s: Stream %d: Can't get buffers in unconfigured state %d",                  __FUNCTION__, mId, mState);          return INVALID_OPERATION; @@ -240,13 +245,11 @@ status_t Camera3IOStreamBase::returnAnyBufferLocked(      mHandoutTotalBufferCount--;      if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG && -            mState != STATE_IN_RECONFIG) { +            mState != STATE_IN_RECONFIG && mState != STATE_PREPARING) {          /**           * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers           * before/after register_stream_buffers during initial configuration -         * or re-configuration. -         * -         * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2 +         * or re-configuration, or during prepare pre-allocation           */          ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__,                  mId); diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h index abcf2b1..f5727e8 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h @@ -84,7 +84,7 @@ class Camera3IOStreamBase :      virtual size_t   getHandoutInputBufferCountLocked(); -    virtual status_t getEndpointUsage(uint32_t *usage) = 0; +    virtual status_t getEndpointUsage(uint32_t *usage) const = 0;      status_t getBufferPreconditionCheckLocked() const;      status_t returnBufferPreconditionCheckLocked() const; diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index fa97e57..84c5754 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -275,7 +275,7 @@ status_t Camera3InputStream::configureQueueLocked() {      return OK;  } -status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) { +status_t Camera3InputStream::getEndpointUsage(uint32_t *usage) const {      // Per HAL3 spec, input streams have 0 for their initial usage field.      *usage = 0;      return OK; diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h index 7ba36c9..9f3de10 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.h +++ b/services/camera/libcameraservice/device3/Camera3InputStream.h @@ -75,7 +75,7 @@ class Camera3InputStream : public Camera3IOStreamBase {      virtual status_t configureQueueLocked(); -    virtual status_t getEndpointUsage(uint32_t *usage); +    virtual status_t getEndpointUsage(uint32_t *usage) const;  }; // class Camera3InputStream diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 8d9b360..7a0331b 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -209,6 +209,13 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(          }      }      mLock.lock(); + +    // Once a valid buffer has been returned to the queue, can no longer +    // dequeue all buffers for preallocation. +    if (buffer.status != CAMERA3_BUFFER_STATUS_ERROR) { +        mStreamUnpreparable = true; +    } +      if (res != OK) {          close(anwReleaseFence);      } @@ -390,7 +397,7 @@ status_t Camera3OutputStream::disconnectLocked() {      return OK;  } -status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) { +status_t Camera3OutputStream::getEndpointUsage(uint32_t *usage) const {      status_t res;      int32_t u = 0; diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h index 12b2ebb..513b695 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.h +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -99,7 +99,7 @@ class Camera3OutputStream :      virtual status_t configureQueueLocked(); -    virtual status_t getEndpointUsage(uint32_t *usage); +    virtual status_t getEndpointUsage(uint32_t *usage) const;  }; // class Camera3OutputStream diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index d3c5cc3..3821da1 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -194,6 +194,11 @@ status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) {          return OK;      } +    // Reset prepared state, since buffer config has changed, and existing +    // allocations are no longer valid +    mPrepared = false; +    mStreamUnpreparable = false; +      status_t res;      res = configureQueueLocked();      if (res != OK) { @@ -244,6 +249,125 @@ status_t Camera3Stream::cancelConfiguration() {      return OK;  } +bool Camera3Stream::isUnpreparable() { +    ATRACE_CALL(); + +    Mutex::Autolock l(mLock); +    return mStreamUnpreparable; +} + +status_t Camera3Stream::startPrepare() { +    ATRACE_CALL(); + +    Mutex::Autolock l(mLock); +    status_t res = OK; + +    // This function should be only called when the stream is configured already. +    if (mState != STATE_CONFIGURED) { +        ALOGE("%s: Stream %d: Can't prepare stream if stream is not in CONFIGURED " +                "state %d", __FUNCTION__, mId, mState); +        return INVALID_OPERATION; +    } + +    // This function can't be called if the stream has already received filled +    // buffers +    if (mStreamUnpreparable) { +        ALOGE("%s: Stream %d: Can't prepare stream that's already in use", +                __FUNCTION__, mId); +        return INVALID_OPERATION; +    } + +    if (getHandoutOutputBufferCountLocked() > 0) { +        ALOGE("%s: Stream %d: Can't prepare stream that has outstanding buffers", +                __FUNCTION__, mId); +        return INVALID_OPERATION; +    } + +    if (mPrepared) return OK; + +    size_t bufferCount = getBufferCountLocked(); + +    mPreparedBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount); +    mPreparedBufferIdx = 0; + +    mState = STATE_PREPARING; + +    return NOT_ENOUGH_DATA; +} + +bool Camera3Stream::isPreparing() const { +    Mutex::Autolock l(mLock); +    return mState == STATE_PREPARING; +} + +status_t Camera3Stream::prepareNextBuffer() { +    ATRACE_CALL(); + +    Mutex::Autolock l(mLock); +    status_t res = OK; + +    // This function should be only called when the stream is preparing +    if (mState != STATE_PREPARING) { +        ALOGE("%s: Stream %d: Can't prepare buffer if stream is not in PREPARING " +                "state %d", __FUNCTION__, mId, mState); +        return INVALID_OPERATION; +    } + +    // Get next buffer - this may allocate, and take a while for large buffers +    res = getBufferLocked( &mPreparedBuffers.editItemAt(mPreparedBufferIdx) ); +    if (res != OK) { +        ALOGE("%s: Stream %d: Unable to allocate buffer %d during preparation", +                __FUNCTION__, mId, mPreparedBufferIdx); +        return NO_INIT; +    } + +    mPreparedBufferIdx++; + +    // Check if we still have buffers left to allocate +    if (mPreparedBufferIdx < mPreparedBuffers.size()) { +        return NOT_ENOUGH_DATA; +    } + +    // Done with prepare - mark stream as such, and return all buffers +    // via cancelPrepare +    mPrepared = true; + +    return cancelPrepareLocked(); +} + +status_t Camera3Stream::cancelPrepare() { +    ATRACE_CALL(); + +    Mutex::Autolock l(mLock); + +    return cancelPrepareLocked(); +} + +status_t Camera3Stream::cancelPrepareLocked() { +    status_t res = OK; + +    // This function should be only called when the stream is mid-preparing. +    if (mState != STATE_PREPARING) { +        ALOGE("%s: Stream %d: Can't cancel prepare stream if stream is not in " +                "PREPARING state %d", __FUNCTION__, mId, mState); +        return INVALID_OPERATION; +    } + +    // Return all valid buffers to stream, in ERROR state to indicate +    // they weren't filled. +    for (size_t i = 0; i < mPreparedBufferIdx; i++) { +        mPreparedBuffers.editItemAt(i).release_fence = -1; +        mPreparedBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR; +        returnBufferLocked(mPreparedBuffers[i], 0); +    } +    mPreparedBuffers.clear(); +    mPreparedBufferIdx = 0; + +    mState = STATE_CONFIGURED; + +    return res; +} +  status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) {      ATRACE_CALL();      Mutex::Autolock l(mLock); @@ -427,15 +551,13 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) {              ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "                      "must be set to NULL in camera3_device::ops", __FUNCTION__);              return INVALID_OPERATION; -        } else { -            ALOGD("%s: Skipping NULL check for deprecated register_stream_buffers", __FUNCTION__);          }          return OK; -    } else { -        ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);      } +    ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__); +      status_t res;      size_t bufferCount = getBufferCountLocked(); @@ -491,6 +613,8 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) {          returnBufferLocked(streamBuffers[i], 0);      } +    mPrepared = true; +      return res;  } diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index e89361e..0543c66 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -57,8 +57,15 @@ namespace camera3 {   *    re-registering buffers with HAL.   *   *  STATE_CONFIGURED: Stream is configured, and has registered buffers with the - *    HAL. The stream's getBuffer/returnBuffer work. The priv pointer may still be - *    modified. + *    HAL (if necessary). The stream's getBuffer/returnBuffer work. The priv + *    pointer may still be modified. + * + *  STATE_PREPARING: The stream's buffers are being pre-allocated for use.  On + *    older HALs, this is done as part of configuration, but in newer HALs + *    buffers may be allocated at time of first use. But some use cases require + *    buffer allocation upfront, to minmize disruption due to lengthy allocation + *    duration.  In this state, only prepareNextBuffer() and cancelPrepare() + *    may be called.   *   * Transition table:   * @@ -82,6 +89,12 @@ namespace camera3 {   *    STATE_CONFIGURED     => STATE_CONSTRUCTED:   *        When disconnect() is called after making sure stream is idle with   *        waitUntilIdle(). + *    STATE_CONFIGURED     => STATE_PREPARING: + *        When startPrepare is called before the stream has a buffer + *        queued back into it for the first time. + *    STATE_PREPARING      => STATE_CONFIGURED: + *        When sufficient prepareNextBuffer calls have been made to allocate + *        all stream buffers, or cancelPrepare is called.   *   * Status Tracking:   *    Each stream is tracked by StatusTracker as a separate component, @@ -167,6 +180,73 @@ class Camera3Stream :      status_t         cancelConfiguration();      /** +     * Determine whether the stream has already become in-use (has received +     * a valid filled buffer), which determines if a stream can still have +     * prepareNextBuffer called on it. +     */ +    bool             isUnpreparable(); + +    /** +     * Start stream preparation. May only be called in the CONFIGURED state, +     * when no valid buffers have yet been returned to this stream. +     * +     * If no prepartion is necessary, returns OK and does not transition to +     * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions +     * to PREPARING. +     * +     * This call performs no allocation, so is quick to call. +     * +     * Returns: +     *    OK if no more buffers need to be preallocated +     *    NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish +     *        buffer pre-allocation, and transitions to the PREPARING state. +     *    NO_INIT in case of a serious error from the HAL device +     *    INVALID_OPERATION if called when not in CONFIGURED state, or a +     *        valid buffer has already been returned to this stream. +     */ +    status_t         startPrepare(); + +    /** +     * Check if the stream is mid-preparing. +     */ +    bool             isPreparing() const; + +    /** +     * Continue stream buffer preparation by allocating the next +     * buffer for this stream.  May only be called in the PREPARED state. +     * +     * Returns OK and transitions to the CONFIGURED state if all buffers +     * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA. +     * +     * This call allocates one buffer, which may take several milliseconds for +     * large buffers. +     * +     * Returns: +     *    OK if no more buffers need to be preallocated, and transitions +     *        to the CONFIGURED state. +     *    NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish +     *        buffer pre-allocation. +     *    NO_INIT in case of a serious error from the HAL device +     *    INVALID_OPERATION if called when not in CONFIGURED state, or a +     *        valid buffer has already been returned to this stream. +     */ +    status_t         prepareNextBuffer(); + +    /** +     * Cancel stream preparation early. In case allocation needs to be +     * stopped, this method transitions the stream back to the CONFIGURED state. +     * Buffers that have been allocated with prepareNextBuffer remain that way, +     * but a later use of prepareNextBuffer will require just as many +     * calls as if the earlier prepare attempt had not existed. +     * +     * Returns: +     *    OK if cancellation succeeded, and transitions to the CONFIGURED state +     *    INVALID_OPERATION if not in the PREPARING state +     *    NO_INIT in case of a serious error from the HAL device +     */ +    status_t        cancelPrepare(); + +    /**       * Fill in the camera3_stream_buffer with the next valid buffer for this       * stream, to hand over to the HAL.       * @@ -263,7 +343,8 @@ class Camera3Stream :          STATE_CONSTRUCTED,          STATE_IN_CONFIG,          STATE_IN_RECONFIG, -        STATE_CONFIGURED +        STATE_CONFIGURED, +        STATE_PREPARING      } mState;      mutable Mutex mLock; @@ -312,13 +393,17 @@ class Camera3Stream :      // Get the usage flags for the other endpoint, or return      // INVALID_OPERATION if they cannot be obtained. -    virtual status_t getEndpointUsage(uint32_t *usage) = 0; +    virtual status_t getEndpointUsage(uint32_t *usage) const = 0;      // Tracking for idle state      wp<StatusTracker> mStatusTracker;      // Status tracker component ID      int mStatusId; +    // Tracking for stream prepare - whether this stream can still have +    // prepareNextBuffer called on it. +    bool mStreamUnpreparable; +    private:      uint32_t oldUsage;      uint32_t oldMaxBuffers; @@ -333,6 +418,18 @@ class Camera3Stream :                                    bool acquired, bool output);      List<wp<Camera3StreamBufferListener> > mBufferListenerList; +    status_t        cancelPrepareLocked(); + +    // Tracking for PREPARING state + +    // State of buffer preallocation. Only true if either prepareNextBuffer +    // has been called sufficient number of times, or stream configuration +    // had to register buffers with the HAL +    bool mPrepared; + +    Vector<camera3_stream_buffer_t> mPreparedBuffers; +    size_t mPreparedBufferIdx; +  }; // class Camera3Stream  }; // namespace camera3 diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index ea90dd9..d177b57 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -89,6 +89,68 @@ class Camera3StreamInterface : public virtual RefBase {      virtual status_t cancelConfiguration() = 0;      /** +     * Determine whether the stream has already become in-use (has received +     * a valid filled buffer), which determines if a stream can still have +     * prepareNextBuffer called on it. +     */ +    virtual bool     isUnpreparable() = 0; + +    /** +     * Start stream preparation. May only be called in the CONFIGURED state, +     * when no valid buffers have yet been returned to this stream. +     * +     * If no prepartion is necessary, returns OK and does not transition to +     * PREPARING state. Otherwise, returns NOT_ENOUGH_DATA and transitions +     * to PREPARING. +     * +     * Returns: +     *    OK if no more buffers need to be preallocated +     *    NOT_ENOUGH_DATA if calls to prepareNextBuffer are needed to finish +     *        buffer pre-allocation, and transitions to the PREPARING state. +     *    NO_INIT in case of a serious error from the HAL device +     *    INVALID_OPERATION if called when not in CONFIGURED state, or a +     *        valid buffer has already been returned to this stream. +     */ +    virtual status_t startPrepare() = 0; + +    /** +     * Check if the stream is mid-preparing. +     */ +    virtual bool     isPreparing() const = 0; + +    /** +     * Continue stream buffer preparation by allocating the next +     * buffer for this stream.  May only be called in the PREPARED state. +     * +     * Returns OK and transitions to the CONFIGURED state if all buffers +     * are allocated after the call concludes. Otherwise returns NOT_ENOUGH_DATA. +     * +     * Returns: +     *    OK if no more buffers need to be preallocated, and transitions +     *        to the CONFIGURED state. +     *    NOT_ENOUGH_DATA if more calls to prepareNextBuffer are needed to finish +     *        buffer pre-allocation. +     *    NO_INIT in case of a serious error from the HAL device +     *    INVALID_OPERATION if called when not in CONFIGURED state, or a +     *        valid buffer has already been returned to this stream. +     */ +    virtual status_t prepareNextBuffer() = 0; + +    /** +     * Cancel stream preparation early. In case allocation needs to be +     * stopped, this method transitions the stream back to the CONFIGURED state. +     * Buffers that have been allocated with prepareNextBuffer remain that way, +     * but a later use of prepareNextBuffer will require just as many +     * calls as if the earlier prepare attempt had not existed. +     * +     * Returns: +     *    OK if cancellation succeeded, and transitions to the CONFIGURED state +     *    INVALID_OPERATION if not in the PREPARING state +     *    NO_INIT in case of a serious error from the HAL device +     */ +    virtual status_t cancelPrepare() = 0; + +    /**       * Fill in the camera3_stream_buffer with the next valid buffer for this       * stream, to hand over to the HAL.       *  | 
