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

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

CameraService: Create a dummy stream when 0 streams are requested.

A workaround for a camera device HAL v3.2 or older specification hole - it's
not acceptable to configure_streams with 0 output streams. However, we allow for
this at the public API level, to allow an application to release all output streams.

So in this case, create a dummy stream that doesn't actually do anything as a placeholder.

Bug: 17220694
Change-Id: Ib25242ffc2c9f2b2f619fd5fe6d652266579da85
parent 1754351d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ LOCAL_SRC_FILES:= \
    device3/Camera3InputStream.cpp \
    device3/Camera3OutputStream.cpp \
    device3/Camera3ZslStream.cpp \
    device3/Camera3DummyStream.cpp \
    device3/StatusTracker.cpp \
    gui/RingBufferConsumer.cpp \
    utils/CameraTraces.cpp \
+75 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@
#include "device3/Camera3OutputStream.h"
#include "device3/Camera3InputStream.h"
#include "device3/Camera3ZslStream.h"
#include "device3/Camera3DummyStream.h"
#include "CameraService.h"

using namespace android::camera3;
@@ -181,6 +182,7 @@ status_t Camera3Device::initialize(camera_module_t *module)
    mHal3Device = device;
    mStatus = STATUS_UNCONFIGURED;
    mNextStreamId = 0;
    mDummyStreamId = NO_STREAM;
    mNeedConfig = true;
    mPauseStateNotify = false;

@@ -1418,6 +1420,15 @@ status_t Camera3Device::configureStreamsLocked() {
        return OK;
    }

    // Workaround for device HALv3.2 or older spec bug - zero streams requires
    // adding a dummy stream instead.
    // TODO: Bug: 17321404 for fixing the HAL spec and removing this workaround.
    if (mOutputStreams.size() == 0) {
        addDummyStreamLocked();
    } else {
        tryRemoveDummyStreamLocked();
    }

    // Start configuring the streams
    ALOGV("%s: Camera %d: Starting stream configuration", __FUNCTION__, mId);

@@ -1540,7 +1551,7 @@ status_t Camera3Device::configureStreamsLocked() {

    mNeedConfig = false;

    if (config.num_streams > 0) {
    if (mDummyStreamId == NO_STREAM) {
        mStatus = STATUS_CONFIGURED;
    } else {
        mStatus = STATUS_UNCONFIGURED;
@@ -1554,6 +1565,69 @@ status_t Camera3Device::configureStreamsLocked() {
    return OK;
}

status_t Camera3Device::addDummyStreamLocked() {
    ATRACE_CALL();
    status_t res;

    if (mDummyStreamId != NO_STREAM) {
        // Should never be adding a second dummy stream when one is already
        // active
        SET_ERR_L("%s: Camera %d: A dummy stream already exists!",
                __FUNCTION__, mId);
        return INVALID_OPERATION;
    }

    ALOGV("%s: Camera %d: Adding a dummy stream", __FUNCTION__, mId);

    sp<Camera3OutputStreamInterface> dummyStream =
            new Camera3DummyStream(mNextStreamId);

    res = mOutputStreams.add(mNextStreamId, dummyStream);
    if (res < 0) {
        SET_ERR_L("Can't add dummy stream to set: %s (%d)", strerror(-res), res);
        return res;
    }

    mDummyStreamId = mNextStreamId;
    mNextStreamId++;

    return OK;
}

status_t Camera3Device::tryRemoveDummyStreamLocked() {
    ATRACE_CALL();
    status_t res;

    if (mDummyStreamId == NO_STREAM) return OK;
    if (mOutputStreams.size() == 1) return OK;

    ALOGV("%s: Camera %d: Removing the dummy stream", __FUNCTION__, mId);

    // Ok, have a dummy stream and there's at least one other output stream,
    // so remove the dummy

    sp<Camera3StreamInterface> deletedStream;
    ssize_t outputStreamIdx = mOutputStreams.indexOfKey(mDummyStreamId);
    if (outputStreamIdx == NAME_NOT_FOUND) {
        SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId);
        return INVALID_OPERATION;
    }

    deletedStream = mOutputStreams.editValueAt(outputStreamIdx);
    mOutputStreams.removeItemsAt(outputStreamIdx);

    // Free up the stream endpoint so that it can be used by some other stream
    res = deletedStream->disconnect();
    if (res != OK) {
        SET_ERR_L("Can't disconnect deleted dummy stream %d", mDummyStreamId);
        // fall through since we want to still list the stream as deleted.
    }
    mDeletedStreams.add(deletedStream);
    mDummyStreamId = NO_STREAM;

    return res;
}

void Camera3Device::setErrorState(const char *fmt, ...) {
    Mutex::Autolock l(mLock);
    va_list args;
+15 −0
Original line number Diff line number Diff line
@@ -151,6 +151,8 @@ class Camera3Device :
    struct                     RequestTrigger;
    // minimal jpeg buffer size: 256KB + blob header
    static const ssize_t       kMinJpegBufferSize = 256 * 1024 + sizeof(camera3_jpeg_blob);
    // Constant to use for stream ID when one doesn't exist
    static const int           NO_STREAM = -1;

    // A lock to enforce serialization on the input/configure side
    // of the public interface.
@@ -196,6 +198,8 @@ class Camera3Device :
    int                        mNextStreamId;
    bool                       mNeedConfig;

    int                        mDummyStreamId;

    // Whether to send state updates upstream
    // Pause when doing transparent reconfiguration
    bool                       mPauseStateNotify;
@@ -290,6 +294,17 @@ class Camera3Device :
     */
    status_t           configureStreamsLocked();

    /**
     * Add a dummy stream to the current stream set as a workaround for
     * not allowing 0 streams in the camera HAL spec.
     */
    status_t           addDummyStreamLocked();

    /**
     * Remove a dummy stream if the current config includes real streams.
     */
    status_t           tryRemoveDummyStreamLocked();

    /**
     * Set device into an error state due to some fatal failure, and set an
     * error message to indicate why. Only the first call's message will be
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "Camera3-DummyStream"
#define ATRACE_TAG ATRACE_TAG_CAMERA
//#define LOG_NDEBUG 0

#include <utils/Log.h>
#include <utils/Trace.h>
#include "Camera3DummyStream.h"

namespace android {

namespace camera3 {

Camera3DummyStream::Camera3DummyStream(int id) :
        Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
                /*maxSize*/0, DUMMY_FORMAT) {

}

Camera3DummyStream::~Camera3DummyStream() {

}

status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *buffer) {
    ATRACE_CALL();
    ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", mId);
    return INVALID_OPERATION;
}

status_t Camera3DummyStream::returnBufferLocked(
        const camera3_stream_buffer &buffer,
        nsecs_t timestamp) {
    ATRACE_CALL();
    ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId);
    return INVALID_OPERATION;
}

status_t Camera3DummyStream::returnBufferCheckedLocked(
            const camera3_stream_buffer &buffer,
            nsecs_t timestamp,
            bool output,
            /*out*/
            sp<Fence> *releaseFenceOut) {
    ATRACE_CALL();
    ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId);
    return INVALID_OPERATION;
}

void Camera3DummyStream::dump(int fd, const Vector<String16> &args) const {
    (void) args;
    String8 lines;
    lines.appendFormat("    Stream[%d]: Dummy\n", mId);
    write(fd, lines.string(), lines.size());

    Camera3IOStreamBase::dump(fd, args);
}

status_t Camera3DummyStream::setTransform(int transform) {
    ATRACE_CALL();
    // Do nothing
    return OK;
}

status_t Camera3DummyStream::configureQueueLocked() {
    // Do nothing
    return OK;
}

status_t Camera3DummyStream::disconnectLocked() {
    mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG
                                           : STATE_CONSTRUCTED;
    return OK;
}

status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) {
    *usage = DUMMY_USAGE;
    return OK;
}

}; // namespace camera3

}; // namespace android
+98 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H
#define ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H

#include <utils/RefBase.h>
#include <gui/Surface.h>

#include "Camera3Stream.h"
#include "Camera3IOStreamBase.h"
#include "Camera3OutputStreamInterface.h"

namespace android {
namespace camera3 {

/**
 * A dummy output stream class, to be used as a placeholder when no valid
 * streams are configured by the client.
 * This is necessary because camera HAL v3.2 or older disallow configuring
 * 0 output streams, while the public camera2 API allows for it.
 */
class Camera3DummyStream :
        public Camera3IOStreamBase,
        public Camera3OutputStreamInterface {

  public:
    /**
     * Set up a dummy stream; doesn't actually connect to anything, and uses
     * a default dummy format and size.
     */
    Camera3DummyStream(int id);

    virtual ~Camera3DummyStream();

    /**
     * Camera3Stream interface
     */

    virtual void     dump(int fd, const Vector<String16> &args) const;

    status_t         setTransform(int transform);

  protected:

    /**
     * Note that we release the lock briefly in this function
     */
    virtual status_t returnBufferCheckedLocked(
            const camera3_stream_buffer &buffer,
            nsecs_t timestamp,
            bool output,
            /*out*/
            sp<Fence> *releaseFenceOut);

    virtual status_t disconnectLocked();

  private:

    // Default dummy parameters; 320x240 is a required size for all devices,
    // otherwise act like a SurfaceView would.
    static const int DUMMY_WIDTH = 320;
    static const int DUMMY_HEIGHT = 240;
    static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
    static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;

    /**
     * Internal 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 configureQueueLocked();

    virtual status_t getEndpointUsage(uint32_t *usage);

}; // class Camera3DummyStream

} // namespace camera3

} // namespace android

#endif