Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a691ff3c authored by Eino-Ville Talvala's avatar Eino-Ville Talvala
Browse files

Camera2/3: Don't allow recording and callbacks to coexist.

- Tear down conflicting streams when necessary.

- Shut down callbacks if recording starts

- Do not allow callbacks to start if recording is active

Per the current camera API, recording and preview callbacks cannot be
active simultaneously. However, the framework did not explicitly
disallow this, and in fact left the streams configured once they were
created, even if switching between the two operational modes.

In addition, no guards existed for trying to enable both recording and
callbacks at the same time.

Bug: 9423825

Change-Id: I7d6e6114c2e14fcfb5299b4c72ad557895cbf4b8
parent 209bbbcf
Loading
Loading
Loading
Loading
+67 −25
Original line number Diff line number Diff line
@@ -611,29 +611,37 @@ void Camera2Client::setPreviewCallbackFlag(int flag) {

void Camera2Client::setPreviewCallbackFlagL(Parameters &params, int flag) {
    status_t res = OK;

    switch(params.state) {
        case Parameters::STOPPED:
        case Parameters::WAITING_FOR_PREVIEW_WINDOW:
        case Parameters::PREVIEW:
            // OK
            break;
        default:
            if (flag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) {
                ALOGE("%s: Camera %d: Can't use preview callbacks "
                        "in state %d", __FUNCTION__, mCameraId, params.state);
                return;
            }
    }

    if (flag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) {
        ALOGV("%s: setting oneshot", __FUNCTION__);
        params.previewCallbackOneShot = true;
    }
    if (params.previewCallbackFlags != (uint32_t)flag) {
        params.previewCallbackFlags = flag;
        switch(params.state) {
        case Parameters::PREVIEW:

        if (params.state == Parameters::PREVIEW) {
            res = startPreviewL(params, true);
            break;
        case Parameters::RECORD:
        case Parameters::VIDEO_SNAPSHOT:
            res = startRecordingL(params, true);
            break;
        default:
            break;
        }
            if (res != OK) {
                ALOGE("%s: Camera %d: Unable to refresh request in state %s",
                        __FUNCTION__, mCameraId,
                        Parameters::getStateName(params.state));
            }
        }
    }

}

@@ -702,6 +710,26 @@ status_t Camera2Client::startPreviewL(Parameters &params, bool restart) {
    bool callbacksEnabled = params.previewCallbackFlags &
        CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
    if (callbacksEnabled) {
        // Can't have recording stream hanging around when enabling callbacks,
        // since it exceeds the max stream count on some devices.
        if (mStreamingProcessor->getRecordingStreamId() != NO_STREAM) {
            ALOGV("%s: Camera %d: Clearing out recording stream before "
                    "creating callback stream", __FUNCTION__, mCameraId);
            res = mStreamingProcessor->stopStream();
            if (res != OK) {
                ALOGE("%s: Camera %d: Can't stop streaming to delete "
                        "recording stream", __FUNCTION__, mCameraId);
                return res;
            }
            res = mStreamingProcessor->deleteRecordingStream();
            if (res != OK) {
                ALOGE("%s: Camera %d: Unable to delete recording stream before "
                        "enabling callbacks: %s (%d)", __FUNCTION__, mCameraId,
                        strerror(-res), res);
                return res;
            }
        }

        res = mCallbackProcessor->updateStream(params);
        if (res != OK) {
            ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)",
@@ -898,6 +926,29 @@ status_t Camera2Client::startRecordingL(Parameters &params, bool restart) {
        }
    }

    // Not all devices can support a preview callback stream and a recording
    // stream at the same time, so assume none of them can.
    if (mCallbackProcessor->getStreamId() != NO_STREAM) {
        ALOGV("%s: Camera %d: Clearing out callback stream before "
                "creating recording stream", __FUNCTION__, mCameraId);
        res = mStreamingProcessor->stopStream();
        if (res != OK) {
            ALOGE("%s: Camera %d: Can't stop streaming to delete callback stream",
                    __FUNCTION__, mCameraId);
            return res;
        }
        res = mCallbackProcessor->deleteStream();
        if (res != OK) {
            ALOGE("%s: Camera %d: Unable to delete callback stream before "
                    "record: %s (%d)", __FUNCTION__, mCameraId,
                    strerror(-res), res);
            return res;
        }
    }
    // Disable callbacks if they're enabled; can't record and use callbacks,
    // and we can't fail record start without stagefright asserting.
    params.previewCallbackFlags = 0;

    res = updateProcessorStream<
            StreamingProcessor,
            &StreamingProcessor::updateRecordingStream>(mStreamingProcessor,
@@ -909,17 +960,6 @@ status_t Camera2Client::startRecordingL(Parameters &params, bool restart) {
    }

    Vector<uint8_t> outputStreams;
    bool callbacksEnabled = params.previewCallbackFlags &
        CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK;
    if (callbacksEnabled) {
        res = mCallbackProcessor->updateStream(params);
        if (res != OK) {
            ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)",
                    __FUNCTION__, mCameraId, strerror(-res), res);
            return res;
        }
        outputStreams.push(getCallbackStreamId());
    }
    outputStreams.push(getPreviewStreamId());
    outputStreams.push(getRecordingStreamId());

@@ -1651,6 +1691,8 @@ status_t Camera2Client::updateProcessorStream(sp<ProcessorT> processor,
     * queue) and then try again. Resume streaming once we're done.
     */
    if (res == -EBUSY) {
        ALOGV("%s: Camera %d: Pausing to update stream", __FUNCTION__,
                mCameraId);
        res = mStreamingProcessor->togglePauseStream(/*pause*/true);
        if (res != OK) {
            ALOGE("%s: Camera %d: Can't pause streaming: %s (%d)",
+18 −4
Original line number Diff line number Diff line
@@ -74,8 +74,10 @@ status_t CallbackProcessor::updateStream(const Parameters &params) {
    }

    if (mCallbackConsumer == 0) {
        // Create CPU buffer queue endpoint
        mCallbackConsumer = new CpuConsumer(kCallbackHeapCount);
        // Create CPU buffer queue endpoint. Make it async to avoid disconnect
        // deadlocks.
        mCallbackConsumer = new CpuConsumer(kCallbackHeapCount,
                /*synchronized*/ false);
        mCallbackConsumer->setFrameAvailableListener(this);
        mCallbackConsumer->setName(String8("Camera2Client::CallbackConsumer"));
        mCallbackWindow = new Surface(
@@ -133,7 +135,7 @@ status_t CallbackProcessor::updateStream(const Parameters &params) {
status_t CallbackProcessor::deleteStream() {
    ATRACE_CALL();
    sp<CameraDeviceBase> device;

    status_t res;
    {
        Mutex::Autolock l(mInputMutex);

@@ -146,7 +148,19 @@ status_t CallbackProcessor::deleteStream() {
            return INVALID_OPERATION;
        }
    }
    device->deleteStream(mCallbackStreamId);
    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(mCallbackStreamId);
    if (res != OK) {
        ALOGE("%s: Unable to delete callback stream: %s (%d)",
                __FUNCTION__, strerror(-res), res);
        return res;
    }

    {
        Mutex::Autolock l(mInputMutex);