Qualcomm Camera HAL 2.0

We know that in the HAL Vendor implementation of dynamic to load a name for the camera.$platform$.so file, then to define the load Android HAL, here to Camera HAL 2 and Qualcomm msm8960 for example, an article with before( ).

(Note: This article is the draft is long, but has not been made, because this version code hand no device can run, also cannot determine whether the code is completely correct, at least found some places are stub implementation, this paper may have some mistakes, such as finding incorrect welcome that, I will try to find the mistakes and correct!)

We know that a lot of methods defined in the camera2.h, then in msm8960 HAL is in the following areas
/path/to/qcam-hal/QCamera/HAL2
This compiler is a camera.$platform$.so, please see its implementation
The first is HAL2/wrapper/QualcommCamera.h|cpp

/**
 * The functions need to be provided by the camera HAL.
 *
 * If getNumberOfCameras() returns N, the valid cameraId for getCameraInfo()
 * and openCameraHardware() is 0 to N-1.
 */
 
static hw_module_methods_t camera_module_methods = {
    open: camera_device_open,
};
 
static hw_module_t camera_common  = {
    tag: HARDWARE_MODULE_TAG,
    module_api_version: CAMERA_MODULE_API_VERSION_2_0, // So Camera Service to initialize the Camera2Client series
    hal_api_version: HARDWARE_HAL_API_VERSION,
    id: CAMERA_HARDWARE_MODULE_ID,
    name: "Qcamera",
    author:"Qcom",
    methods: &camera_module_methods,
    dso: NULL,
    reserved:  {0},
};
 
camera_module_t HAL_MODULE_INFO_SYM = { // The HMI, each HAL module must have
    common: camera_common,
    get_number_of_cameras: get_number_of_cameras,
    get_camera_info: get_camera_info,
};
 
camera2_device_ops_t camera_ops = { // Pay attention to the function of these binding
    set_request_queue_src_ops:           android::set_request_queue_src_ops,
    notify_request_queue_not_empty:      android::notify_request_queue_not_empty,
    set_frame_queue_dst_ops:             android::set_frame_queue_dst_ops,
    get_in_progress_count:               android::get_in_progress_count,
    flush_captures_in_progress:          android::flush_captures_in_progress,
    construct_default_request:           android::construct_default_request,
 
    allocate_stream:                     android::allocate_stream,
    register_stream_buffers:             android::register_stream_buffers,
    release_stream:                      android::release_stream,
 
    allocate_reprocess_stream:           android::allocate_reprocess_stream,
    allocate_reprocess_stream_from_stream: android::allocate_reprocess_stream_from_stream,
    release_reprocess_stream:            android::release_reprocess_stream,
 
    trigger_action:                      android::trigger_action,
    set_notify_callback:                 android::set_notify_callback,
    get_metadata_vendor_tag_ops:         android::get_metadata_vendor_tag_ops,
    dump:                                android::dump,
};
 
typedef struct { // Note that this is a wrap structure defined in Qualcomm itself
  camera2_device_t hw_dev; // Here is the standard
  QCameraHardwareInterface *hardware;
  int camera_released;
  int cameraId;
} camera_hardware_t;
 
/* HAL should return NULL if it fails to open camera hardware. */
extern "C" int  camera_device_open(
  const struct hw_module_t* module, const char* id,
          struct hw_device_t** hw_device)
{
    int rc = -1;
    int mode = 0;
    camera2_device_t *device = NULL;
    if (module && id && hw_device) {
        int cameraId = atoi(id);
 
        if (!strcmp(module->name, camera_common.name)) {
            camera_hardware_t *camHal =
                (camera_hardware_t *) malloc(sizeof (camera_hardware_t));
            if (!camHal) {
                *hw_device = NULL;
                ALOGE("%s:  end in no mem", __func__);
                return rc;
            }
            /* we have the camera_hardware obj malloced */
            memset(camHal, 0, sizeof (camera_hardware_t));
            camHal->hardware = new QCameraHardwareInterface(cameraId, mode);
            if (camHal->hardware && camHal->hardware->isCameraReady()) {
                camHal->cameraId = cameraId;
                device = &camHal->hw_dev; // Here camera2_device_t
                device->common.close = close_camera_device; // The initialization of camera2_device_t
                device->common.version = CAMERA_DEVICE_API_VERSION_2_0;
                device->ops = &camera_ops;
                device->priv = (void *)camHal;
                rc =  0;
            } else {
                if (camHal->hardware) {
                    delete camHal->hardware;
                    camHal->hardware = NULL;
                }
                free(camHal);
                device = NULL;
            }
        }
    }
    /* pass actual hw_device ptr to framework. This amkes that we actally be use memberof() macro */
    *hw_device = (hw_device_t*)&device->common; // This is kernel or Android native framework commonly used
    return rc;
}

Have a look allocate stream

int allocate_stream(const struct camera2_device *device,
        uint32_t width,
        uint32_t height,
        int      format,
        const camera2_stream_ops_t *stream_ops,
        uint32_t *stream_id,
        uint32_t *format_actual,
        uint32_t *usage,
        uint32_t *max_buffers)
{
    QCameraHardwareInterface *hardware = util_get_Hal_obj(device);
    hardware->allocate_stream(width, height, format, stream_ops,
            stream_id, format_actual, usage, max_buffers);
    return rc;
}

Note QCameraHardwareInterface in QCameraHWI.h|cpp

int QCameraHardwareInterface::allocate_stream(
    uint32_t width,
    uint32_t height, int format,
    const camera2_stream_ops_t *stream_ops,
    uint32_t *stream_id,
    uint32_t *format_actual,
    uint32_t *usage,
    uint32_t *max_buffers)
{
    int ret = OK;
    QCameraStream *stream = NULL;
    camera_mode_t myMode = (camera_mode_t)(CAMERA_MODE_2D|CAMERA_NONZSL_MODE);
 
    stream = QCameraStream_preview::createInstance(
                        mCameraHandle->camera_handle,
                        mChannelId,
                        width,
                        height,
                        format,
                        mCameraHandle,
                        myMode);
 
    stream->setPreviewWindow(stream_ops); // Here, it is only created by the method of stream, will have the corresponding ANativeWindow
    *stream_id = stream->getStreamId();
    *max_buffers= stream->getMaxBuffers(); // Get from HAL
    *usage = GRALLOC_USAGE_HW_CAMERA_WRITE | CAMERA_GRALLOC_HEAP_ID
        | CAMERA_GRALLOC_FALLBACK_HEAP_ID;
    /* Set to an arbitrary format SUPPORTED by gralloc */
    *format_actual = HAL_PIXEL_FORMAT_YCrCb_420_SP;
 
    return ret;
}

QCameraStream_preview:: createInstance to directly call structure method, also is the following  
 (related to class in QCameraStream.h|cpp and QCameraStream_Preview.cpp)

QCameraStream_preview::QCameraStream_preview(uint32_t CameraHandle,
                        uint32_t ChannelId,
                        uint32_t Width,
                        uint32_t Height,
                        int requestedFormat,
                        mm_camera_vtbl_t *mm_ops,
                        camera_mode_t mode) :
                 QCameraStream(CameraHandle,
                        ChannelId,
                        Width,
                        Height,
                        mm_ops,
                        mode),
                 mLastQueuedFrame(NULL),
                 mDisplayBuf(NULL),
                 mNumFDRcvd(0)
{
    mStreamId = allocateStreamId(); // Distribution of stream ID (according to mStreamTable)
 
    switch (requestedFormat) { // max buffer number
    case CAMERA2_HAL_PIXEL_FORMAT_OPAQUE:
        mMaxBuffers = 5;
        break;
    case HAL_PIXEL_FORMAT_BLOB:
        mMaxBuffers = 1;
        break;
    default:
        ALOGE("Unsupported requested format %d", requestedFormat);
        mMaxBuffers = 1;
        break;
    }
    /*TODO: There has to be a better way to do this*/
}

Have a look again  
/path/to/qcam-hal/QCamera/stack/mm-camera-interface/
mm_camera_interface.h
 Among

typedef struct {
    uint32_t camera_handle;        /* camera object handle */
    mm_camera_info_t *camera_info; /* reference pointer of camear info */
    mm_camera_ops_t *ops;          /* API call table */
} mm_camera_vtbl_t;

mm_camera_interface.c
 Among

/* camera ops v-table */
static mm_camera_ops_t mm_camera_ops = {
    .sync = mm_camera_intf_sync,
    .is_event_supported = mm_camera_intf_is_event_supported,
    .register_event_notify = mm_camera_intf_register_event_notify,
    .qbuf = mm_camera_intf_qbuf,
    .camera_close = mm_camera_intf_close,
    .query_2nd_sensor_info = mm_camera_intf_query_2nd_sensor_info,
    .is_parm_supported = mm_camera_intf_is_parm_supported,
    .set_parm = mm_camera_intf_set_parm,
    .get_parm = mm_camera_intf_get_parm,
    .ch_acquire = mm_camera_intf_add_channel,
    .ch_release = mm_camera_intf_del_channel,
    .add_stream = mm_camera_intf_add_stream,
    .del_stream = mm_camera_intf_del_stream,
    .config_stream = mm_camera_intf_config_stream,
    .init_stream_bundle = mm_camera_intf_bundle_streams,
    .destroy_stream_bundle = mm_camera_intf_destroy_bundle,
    .start_streams = mm_camera_intf_start_streams,
    .stop_streams = mm_camera_intf_stop_streams,
    .async_teardown_streams = mm_camera_intf_async_teardown_streams,
    .request_super_buf = mm_camera_intf_request_super_buf,
    .cancel_super_buf_request = mm_camera_intf_cancel_super_buf_request,
    .start_focus = mm_camera_intf_start_focus,
    .abort_focus = mm_camera_intf_abort_focus,
    .prepare_snapshot = mm_camera_intf_prepare_snapshot,
    .set_stream_parm = mm_camera_intf_set_stream_parm,
    .get_stream_parm = mm_camera_intf_get_stream_parm
};

Taking start stream as an example

mm_camera_intf_start_streams(mm_camera_interface
    mm_camera_start_streams(mm_camera
        mm_channel_fsm_fn(mm_camera_channel
            mm_channel_fsm_fn_active(mm_camera_channel
                mm_channel_start_streams(mm_camera_channel
                    mm_stream_fsm_fn(mm_camera_stream
                        mm_stream_fsm_reg(mm_camera_stream
                            mm_camera_cmd_thread_launch(mm_camera_data
                            mm_stream_streamon(mm_camera_stream

Note: in this paper, the gradient representation is put, call, if the gradient is the same, that these methods are invoked in the same method

int32_t mm_stream_streamon(mm_stream_t *my_obj)
{
    int32_t rc;
    enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 
    /* Add fd to data poll thread */
    rc = mm_camera_poll_thread_add_poll_fd(&my_obj->ch_obj->poll_thread[0],
                                           my_obj->my_hdl,
                                           my_obj->fd,
                                           mm_stream_data_notify,
                                           (void*)my_obj);
    if (rc <0) {
        return rc;
    }
    rc = ioctl(my_obj->fd, VIDIOC_STREAMON, &buf_type);
    if (rc <0) {
        CDBG_ERROR("%s: ioctl VIDIOC_STREAMON failed: rc=%d\n",
                   __func__, rc);
        /* remove fd from data poll thread in case of failure */
        mm_camera_poll_thread_del_poll_fd(&my_obj->ch_obj->poll_thread[0], my_obj->my_hdl);
    }
    return rc;
}

See IOCTL, VIDIOC_STREAMON, can be happy, method of user space and kernel space communication which is V4L2 norms, V4L2 (Video for Linux Two) is a classical and mature video communication protocol, and V4L before it, do not know can go to download the code, and Video4Linux2 (The) is also very good data.  
 Here introduce:

open(VIDEO_DEVICE_NAME, ...) // Open the video equipment, general call when the initialization procedure

ioctl(...) // Mainly is some need of data transmission quantity is very little control operation
Here many parameters can be used, and usually we will follow the way to use, for example
VIDIOC_QUERYCAP // Query device capable of what
VIDIOC_CROPCAP // The ability to query crop equipment
VIDIOC_S_* // set/The get method, set / get parameter
VIDIOC_G_*
VIDIOC_REQBUFS // Distribution of buffer, can have a variety of ways
VIDIOC_QUERYBUF // The distribution of buffer information query
VIDIOC_QBUF // QUEUE BUFFER buffer into the DRV queue (when the buffer is empty)
VIDIOC_STREAMON // Start the video data transmission
VIDIOC_DQBUF // DEQUEUE BUFFER take buffer out from the DRV in the buffer queue (when buffer is data)

[0...n]
QBUF -> DQBUF // You can repeat this action

VIDIOC_STREAMOFF // Stop video data transmission

close(VIDEO_DEVICE_FD) // Closing device
The above is the main function and simple sequence of calls, and other functions

select() // Wait for events to occur, mainly used in after we save frame buffer to DRV, wait for its reaction
mmap/munmap // Mainly deal with the US request buffer, buffer distribution in the device memory space needs

And have a look mm_camera_stream this file is also realized.

Read here, we come back to continue to look at the QCam HAL, which of course implementation details are not my start stream listed above is so simple, but in fact is not complicated, feel is important and the structure of the state.

The first is channel, currently only supports 1 channel, but can have multiple streams (will be introduced, back and now supports up to 8 streams)

/* mm_channel */
typedef enum {
    MM_CHANNEL_STATE_NOTUSED = 0,   /* not used */
    MM_CHANNEL_STATE_STOPPED,       /* stopped */
    MM_CHANNEL_STATE_ACTIVE,        /* active, at least one stream active */
    MM_CHANNEL_STATE_PAUSED,        /* paused */
    MM_CHANNEL_STATE_MAX
} mm_channel_state_type_t;

It can perform event

typedef enum {
    MM_CHANNEL_EVT_ADD_STREAM,
    MM_CHANNEL_EVT_DEL_STREAM,
    MM_CHANNEL_EVT_START_STREAM,
    MM_CHANNEL_EVT_STOP_STREAM,
    MM_CHANNEL_EVT_TEARDOWN_STREAM,
    MM_CHANNEL_EVT_CONFIG_STREAM,
    MM_CHANNEL_EVT_PAUSE,
    MM_CHANNEL_EVT_RESUME,
    MM_CHANNEL_EVT_INIT_BUNDLE,
    MM_CHANNEL_EVT_DESTROY_BUNDLE,
    MM_CHANNEL_EVT_REQUEST_SUPER_BUF,
    MM_CHANNEL_EVT_CANCEL_REQUEST_SUPER_BUF,
    MM_CHANNEL_EVT_START_FOCUS,
    MM_CHANNEL_EVT_ABORT_FOCUS,
    MM_CHANNEL_EVT_PREPARE_SNAPSHOT,
    MM_CHANNEL_EVT_SET_STREAM_PARM,
    MM_CHANNEL_EVT_GET_STREAM_PARM,
    MM_CHANNEL_EVT_DELETE,
    MM_CHANNEL_EVT_MAX
} mm_channel_evt_type_t;

/* mm_stream */
typedef enum { // The state should carefully, every method, the state will need to change
    MM_STREAM_STATE_NOTUSED = 0,      /* not used */
    MM_STREAM_STATE_INITED,           /* inited  */
    MM_STREAM_STATE_ACQUIRED,         /* acquired, fd opened  */
    MM_STREAM_STATE_CFG,              /* fmt & dim configured */
    MM_STREAM_STATE_BUFFED,           /* buf allocated */
    MM_STREAM_STATE_REG,              /* buf regged, stream off */
    MM_STREAM_STATE_ACTIVE_STREAM_ON, /* active with stream on */
    MM_STREAM_STATE_ACTIVE_STREAM_OFF, /* active with stream off */
    MM_STREAM_STATE_MAX
} mm_stream_state_type_t;

Similarly, stream can perform event

typedef enum {
    MM_STREAM_EVT_ACQUIRE,
    MM_STREAM_EVT_RELEASE,
    MM_STREAM_EVT_SET_FMT,
    MM_STREAM_EVT_GET_BUF,
    MM_STREAM_EVT_PUT_BUF,
    MM_STREAM_EVT_REG_BUF,
    MM_STREAM_EVT_UNREG_BUF,
    MM_STREAM_EVT_START,
    MM_STREAM_EVT_STOP,
    MM_STREAM_EVT_QBUF,
    MM_STREAM_EVT_SET_PARM,
    MM_STREAM_EVT_GET_PARM,
    MM_STREAM_EVT_MAX
} mm_stream_evt_type_t;

Every time when the executive function here need to check the channel/stream state, only the correct time will state to perform

For example, you can observe
Mm_channel mm_channel_state_type_t state;
Mm_stream mm_stream_state_type_t state;
All said the current structure of the state

In addition
struct mm_camera_obj
struct mm_channel
struct mm_stream
The three is to contain, and stream and channel will also hold the parent structure (as it is called, the actual container relationship) reference.

In fact, Vendor HAL each have their own ways to achieve, may also contain many unique things, such as where it will feed IOCTL some special commands or data structure, which we only when doing the platform specific to consider. These are probably the myriads of changes, such as the OMAP4 with the DRV communication is through rpmsg, and use of a standard OpenMAX to achieve.

Theory on so much, then look at a case, for example, we‘re going to start preview in Camera Service:

Camera2Client::startPreviewL
    StreamingProcessor->updatePreviewStream
        Camera2Device->createStream
            StreamAdapter->connectToDevice
                camera2_device_t->ops->allocate_stream // Analysis of the above
                Native_window_api_* or native_window_*
 
    StreamingProcessor->startStream
        Camera2Device->setStreamingRequest
            Camera2Device::RequestQueue->setStreamSlot // Create a stream slot
                Camera2Device::RequestQueue->signalConsumerLocked
status_t Camera2Device::MetadataQueue::signalConsumerLocked() {
    status_t res = OK;
    notEmpty.signal();
    if (mSignalConsumer && mDevice != NULL) {
        mSignalConsumer = false;
        mMutex.unlock();
        res = mDevice->ops->notify_request_queue_not_empty(mDevice); // Notice Vendor HAL run command thread to run, 
                                                                     // The notify_request_queue_not_empty event is not always trigger, only the initialization time
                                                                     // Or run command thread in the dequeue when the data for NULL, 
                                                                     // Camera Service change and new request came to trigger
                                                                     // Can be said to be a burden, do not request, thread has also been there
                                                                     // But often encounter this situation is the use of a thread stop there
        mMutex.lock();
    }
    return res;
}

However, in the Qualcomm HAL

int notify_request_queue_not_empty(const struct camera2_device *device) // This method is registered to the camera2_device_ops_t
    QCameraHardwareInterface->notify_request_queue_not_empty()
        pthread_create(&mCommandThread, &attr, command_thread, (void *)this) != 0)
void *command_thread(void *obj)
{
    ...
    pme->runCommandThread(obj);
}
void QCameraHardwareInterface::runCommandThread(void *data)
{
    /**
     * This function implements the main service routine for the incoming
     * frame requests, this thread routine is started everytime we get a
     * notify_request_queue_not_empty trigger, this thread makes the
     * assumption that once it receives a NULL on a dequest_request call
     * there will be a fresh notify_request_queue_not_empty call that is
     * invoked thereby launching a new instance of this thread. Therefore,
     * once we get a NULL on a dequeue request we simply let this thread die
     */
    int res;
    camera_metadata_t *request=NULL;
    mPendingRequests=0;
 
    while (mRequestQueueSrc) { // MRequestQueueSrc is provided by set_request_queue_src_ops
                               // See Camera2Device:: MetadataQueue:: setConsumerDevice
                               // In the Camera2Device:: initialize was called
        ALOGV("%s:Dequeue request using mRequestQueueSrc:%p",__func__,mRequestQueueSrc);
        mRequestQueueSrc->dequeue_request(mRequestQueueSrc, &request); // Framework request
        if (request==NULL) {
            ALOGE("%s:No more requests available from src command                     thread dying",__func__);
            return;
        }
        mPendingRequests++;
 
        /* Set the metadata values */
 
        /* Wait for the SOF for the new metadata values to be applied */
 
        /* Check the streams that need to be active in the stream request */
        sort_camera_metadata(request);
 
        camera_metadata_entry_t streams;
        res = find_camera_metadata_entry(request,
                ANDROID_REQUEST_OUTPUT_STREAMS,
                &streams);
        if (res != NO_ERROR) {
            ALOGE("%s: error reading output stream tag", __FUNCTION__);
            return;
        }
 
        res = tryRestartStreams(streams); // Go to prepareStream and streamOn, a detailed code behind
        if (res != NO_ERROR) {
            ALOGE("error tryRestartStreams %d", res);
            return;
        }
 
        /* 3rd pass: Turn on all streams requested */
        for (uint32_t i = 0; i <streams.count; i++) {
            int streamId = streams.data.u8[i];
            QCameraStream *stream = QCameraStream::getStreamAtId(streamId);
 
            /* Increment the frame pending count in each stream class */
 
            /* Assuming we will have the stream obj in had at this point may be
             * may be multiple objs in which case we loop through array of streams */
            stream->onNewRequest();
        }
        ALOGV("%s:Freeing request using mRequestQueueSrc:%p",__func__,mRequestQueueSrc);
        /* Free the request buffer */
        mRequestQueueSrc->free_request(mRequestQueueSrc,request);
        mPendingRequests--;
        ALOGV("%s:Completed request",__func__);
    }
 
    QCameraStream::streamOffAll();
}

Following this method explain where mRequestQueueSrc from

// Connect to camera2 HAL as consumer (input requests/reprocessing)
status_t Camera2Device::MetadataQueue::setConsumerDevice(camera2_device_t *d) {
    ATRACE_CALL();
    status_t res;
    res = d->ops->set_request_queue_src_ops(d,
            this);
    if (res != OK) return res;
    mDevice = d;
    return OK;
}

Because

QCameraStream_preview->prepareStream
    QCameraStream->initStream
        mm_camera_vtbl_t->ops->add_stream(... stream_cb_routine ...) // This is used to return data in callback, mm_camera_super_buf_t* and void* with two parameters
            mm_camera_add_stream
                mm_channel_fsm_fn(..., MM_CHANNEL_EVT_ADD_STREAM, ..., mm_evt_paylod_add_stream_t)
                    mm_channel_fsm_fn_stopped
                        mm_channel_add_stream(..., mm_camera_buf_notify_t, ...)
                            mm_stream_fsm_inited

In mm_channel_add_stream, the mm_camera_buf_notify_t package to mm_stream_t

mm_stream_t *stream_obj = NULL;
/* initialize stream object */
memset(stream_obj, 0, sizeof(mm_stream_t));
/* cd through intf always palced at idx 0 of buf_cb */
stream_obj->buf_cb[0].cb = buf_cb; // callback
stream_obj->buf_cb[0].user_data = user_data;
stream_obj->buf_cb[0].cb_count = -1; /* infinite by default */// The default unlimited

The event parameter and mm_stream_fsm_inited, incoming is MM_STREAM_EVT_ACQUIRE

int32_t mm_stream_fsm_inited(mm_stream_t *my_obj,
                             mm_stream_evt_type_t evt,
                             void * in_val,
                             void * out_val)
{
    int32_t rc = 0;
    char dev_name[MM_CAMERA_DEV_NAME_LEN];
 
    switch (evt) {
    case MM_STREAM_EVT_ACQUIRE:
        if ((NULL == my_obj->ch_obj) || (NULL == my_obj->ch_obj->cam_obj)) {
            CDBG_ERROR("%s: NULL channel or camera obj\n", __func__);
            rc = -1;
            break;
        }
 
        snprintf(dev_name, sizeof(dev_name), "/dev/%s",
                 mm_camera_util_get_dev_name(my_obj->ch_obj->cam_obj->my_hdl));
 
        my_obj->fd = open(dev_name, O_RDWR | O_NONBLOCK); // Open the video equipment
        if (my_obj->fd <= 0) {
            CDBG_ERROR("%s: open dev returned %d\n", __func__, my_obj->fd);
            rc = -1;
            break;
        }
        rc = mm_stream_set_ext_mode(my_obj);
        if (0 == rc) {
            my_obj->state = MM_STREAM_STATE_ACQUIRED; // mm_stream_state_type_t
        } else {
            /* failed setting ext_mode
             * close fd */
            if(my_obj->fd > 0) {
                close(my_obj->fd);
                my_obj->fd = -1;
            }
            break;
        }
        rc = get_stream_inst_handle(my_obj);
        if(rc) {
            if(my_obj->fd > 0) {
                close(my_obj->fd);
                my_obj->fd = -1;
            }
        }
        break;
    default:
        CDBG_ERROR("%s: Invalid evt=%d, stream_state=%d",
                   __func__,evt,my_obj->state);
        rc = -1;
        break;
    }
    return rc;
}

Also

QCameraStream->streamOn
    mm_camera_vtbl_t->ops->start_streams
        mm_camera_intf_start_streams
            mm_camera_start_streams
                mm_channel_fsm_fn(..., MM_CHANNEL_EVT_START_STREAM, ...)
                    mm_stream_fsm_fn(..., MM_STREAM_EVT_START, ...)
                        mm_camera_cmd_thread_launch // Start the CB thread
                        mm_stream_streamon(mm_stream_t)
                            mm_camera_poll_thread_add_poll_fd(..., mm_stream_data_notify , ...)

While

static void mm_stream_data_notify(void* user_data)
{
    mm_stream_t *my_obj = (mm_stream_t*)user_data;
    int32_t idx = -1, i, rc;
    uint8_t has_cb = 0;
    mm_camera_buf_info_t buf_info;
 
    if (NULL == my_obj) {
        return;
    }
 
    if (MM_STREAM_STATE_ACTIVE_STREAM_ON != my_obj->state) {
        /* this Cb will only received in active_stream_on state
         * if not so, return here */
        CDBG_ERROR("%s: ERROR!! Wrong state (%d) to receive data notify!",
                   __func__, my_obj->state);
        return;
    }
 
    memset(&buf_info, 0, sizeof(mm_camera_buf_info_t));
 
    pthread_mutex_lock(&my_obj->buf_lock);
    rc = mm_stream_read_msm_frame(my_obj, &buf_info); // Through IOCTL (VIDIOC_DQBUF,...,...) to read frame data
    if (rc != 0) {
        pthread_mutex_unlock(&my_obj->buf_lock);
        return;
    }
    idx = buf_info.buf->buf_idx;
 
    /* update buffer location */
    my_obj->buf_status[idx].in_kernel = 0;
 
    /* update buf ref count */
    if (my_obj->is_bundled) {
        /* need to add into super buf since bundled, add ref count */
        my_obj->buf_status[idx].buf_refcnt++;
    }
 
    for (i=0; i <MM_CAMERA_STREAM_BUF_CB_MAX; i++) {
        if(NULL != my_obj->buf_cb[i].cb) {
            /* for every CB, add ref count */
            my_obj->buf_status[idx].buf_refcnt++;
            has_cb = 1;
        }
    }
    pthread_mutex_unlock(&my_obj->buf_lock);
 
    mm_stream_handle_rcvd_buf(my_obj, &buf_info); // mm_camera_queue_enq, Lost frame data into queue(
                                                  // The premise is a registered callback), and through the sem_post notification queue
                                                  // Then mm_camera_cmd_thread_launch start thread
                                                  // Round robin read data, then the implementation of CB
}

This leads to stream_cb_routine in the stream on (implemented in QCameraStream) will always execute

void stream_cb_routine(mm_camera_super_buf_t *bufs,
                       void *userdata)
{
    QCameraStream *p_obj=(QCameraStream*) userdata;
    switch (p_obj->mExtImgMode) { // This mode will be determined at the time of prepareStream
    case MM_CAMERA_PREVIEW:
        ALOGE("%s : callback for MM_CAMERA_PREVIEW", __func__);
        ((QCameraStream_preview *)p_obj)->dataCallback(bufs); // CAMERA_PREVIEW and CAMERA_VIDEO are the same?
        break;
    case MM_CAMERA_VIDEO:
        ALOGE("%s : callback for MM_CAMERA_VIDEO", __func__);
        ((QCameraStream_preview *)p_obj)->dataCallback(bufs);
        break;
    case MM_CAMERA_SNAPSHOT_MAIN:
        ALOGE("%s : callback for MM_CAMERA_SNAPSHOT_MAIN", __func__);
        p_obj->p_mm_ops->ops->qbuf(p_obj->mCameraHandle,
                                   p_obj->mChannelId,
                                   bufs->bufs[0]);
        break;
    case MM_CAMERA_SNAPSHOT_THUMBNAIL:
        break;
    default:
        break;
    }
}
void QCameraStream::dataCallback(mm_camera_super_buf_t *bufs)
{
    if (mPendingCount != 0) { // The dataCallback is always in the back?
                               // And set down from the code of callback times the default is -1, -1 said infinite. 
                               // It seems only this way can explain, otherwise no one trigger words, even if the mPendingCount in onNewRequest 1
                               // Here also do not perceive
        ALOGD("Got frame request");
        pthread_mutex_lock(&mFrameDeliveredMutex);
        mPendingCount--;
        ALOGD("Completed frame request");
        pthread_cond_signal(&mFrameDeliveredCond);
        pthread_mutex_unlock(&mFrameDeliveredMutex);
        processPreviewFrame(bufs);
    } else {
        p_mm_ops->ops->qbuf(mCameraHandle,
                mChannelId, bufs->bufs[0]); // If there is no need to data, directly to the buffer into the DRV queue, call to V4L2 QBUF
    }
}

More curious is in the hands of this version of QCam HAL code camera2_frame_queue_dst_ops_t has not been used

int QCameraHardwareInterface::set_frame_queue_dst_ops(
    const camera2_frame_queue_dst_ops_t *frame_dst_ops)
{
    mFrameQueueDst = frame_dst_ops; // This now seems to be no use
    return OK;
}

So Camera Service FrameProcessor Camera2Device-> getNextFrame would not obtain data, do not know is this version of the code is not my problem, but also in the latest Qualcomm Camera HAL code is not in the AOSP trees, but directly to the proprietary form for the so file, it‘s just I.

So overall, there may be several QCameraStream, each stream is responsible for their own things.
Also the relationship between them, for example, may be new come stream will lead to other stream-on stream restart.

In Camera HAL 2, we have a key is re-process stream
Simple said is to output stream as input stream again to add to the BufferQueue, let the other consumer, is similar to a chain.
At present in the ZslProcessor were useful to.

ZslProcessor->updateStream
    Camera2Device->createStream
    Camera2Device->createReprocessStreamFromStream // When the release is the first delete re-process
        new ReprocessStreamAdapter
        ReprocessStreamAdapter->connectToDevice
            camera2_device_t->ops->allocate_reprocess_stream_from_stream

Where ReprocessStreamAdapter is the actual camera2_stream_in_ops_t, responsible for the management of re-process stream.

But this version of the code Qualcomm seems not to achieve, so for the time being so far behind, if find the corresponding code, see.

So after so many don‘t feel surprised, standing in the Camera Service position, it owns two of MetadataQueue, mRequestQueue and mFrameQueue.
App the requested action, such as the set parameter/start preview/start recording directly into request, into the mRequestQueue, then restart Preview/recording stream.
For example, the capture will be converted to request, to mRequestQueue.
If necessary, through the notify_request_queue_not_empty to inform the QCam HAL request processing, then QCam HAL will start a thread (QCameraHardwareInterface:: runCommandThread) to do with. Until all request processed exit thread.
In the process of transfer to each of the stream processPreviewFrame, if necessary, it will call themselves each subsequent callback.
There is an implementation detail is, Stream_cb_routine is from start stream started registered on the same channel, While the stream_cb_routine indirect call QCameraStream:: dataCallback stream_cb_routine (of course have to specify the callback back what is, It calls the corresponding dataCallback), The callback is always in the back, So after each new request mPendingCount plus 1, DataCallback processPreviewFrame will call back, Or directly to the buffer again pressed back into DRV queue.

void QCameraStream::dataCallback(mm_camera_super_buf_t *bufs)
{
    if (mPendingCount != 0) { // The dataCallback is always in the back?
                               // And set down from the code of callback times the default is -1, -1 said infinite. 
                               // It seems only this way can explain, otherwise no one trigger words, even if the mPendingCount in onNewRequest 1
                               // Here also do not perceive
        ALOGD("Got frame request");
        pthread_mutex_lock(&mFrameDeliveredMutex);
        mPendingCount--;
        ALOGD("Completed frame request");
        pthread_cond_signal(&mFrameDeliveredCond);
        pthread_mutex_unlock(&mFrameDeliveredMutex);
        processPreviewFrame(bufs);
    } else {
        p_mm_ops->ops->qbuf(mCameraHandle,
                mChannelId, bufs->bufs[0]); // If there is no need to data, directly to the buffer into the DRV queue, call to V4L2 QBUF
    }
}

void QCameraStream::onNewRequest()
{
    ALOGI("%s:E",__func__);
    pthread_mutex_lock(&mFrameDeliveredMutex);
    ALOGI("Sending Frame request");
    mPendingCount++;
    pthread_cond_wait(&mFrameDeliveredCond, &mFrameDeliveredMutex); // Such a request is processed, then do the next request
    ALOGV("Got frame");
    pthread_mutex_unlock(&mFrameDeliveredMutex);
    ALOGV("%s:X",__func__);
}

The processPreviewFrame call to the enqueue_buffer method to create the stream connection in the BufferQueue, put the data into BufferQueue, then the corresponding consumer will receive.
For example, in the Android Camera HAL currently has 2
camera2/BurstCapture.h
camera2/CallbackProcessor.h
camera2/JpegProcessor.h
camera2/StreamingProcessor.h
camera2/ZslProcessor.h
The corresponding Consumer:: FrameAvailableListener burst-capture, but now can not consider, because have only stub.

ZslProcessor.h and CaptureSequencer.h have to realize FrameProcessor:: FilteredListener onFrameAvailable(...)
But we said before this version of QCam HAL does not implement, so FrameProcessor is unable to gain access to the meta data.
So onFrameAbailable will not be notified. (I believe this version of the code. I have a question)

We have QCam HAL before anything is not achieved, so mFrameQueue won‘t have the data, but it was supposed to be the metadata DRV come back queue to this.

In addition
CaptureSequencer.h and onCaptureAvailable to realize, when JpegProcessor will notice it.

Curious? Multiple stream (s) is not the same return, so if CPU processing speed different will have the time difference? There is a curious how DRV handles Video snapshot, if buffer is of the order of Video, there will be less of a frame, if not the sequence of DRV, that is a return to more buffer? I really haven‘t thought about [email protected][email protected]

时间: 2024-08-29 18:24:26

Qualcomm Camera HAL 2.0的相关文章

Android Camera HAL浅析

1.Camera成像原理介绍 Camera工作流程图 Camera的成像原理可以简单概括如下: 景物(SCENE)通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为电信号,经过A/D(模数转换)转换后变为数字 图像信号,再送到数字信号处理芯片(DSP)中加工处理,再通过IO接口传输到CPU中处理,通过DISPLAY就可以看到图像了. 电荷耦合器件(CCD)或互补金属氧化物半导体(CMOS)接收光学镜头传递来的影像,经模/数转换器(A/D)转换成数字信号,经过编码后

Qualcomm Camera基础

高通将android的camera模块重新修改了一下,与原生的方式存在一些差异.这里将前段时间学习的一些零散知识进行一下总结,便于以后查阅. 1.整个模块主要巡行三个主线程:control.config及frame,control用来执行总的控制,是上层控制接口(这个线程还未去了 解)?config主要进行一些配置,这个线程里面主要进行3A的工作,另外还有一些跟效果有关的设置:至于frame线程好像主要用来做预览吧.目前还 只是大致了解config线程. 2.在Qualcomm执行初始化时就会调

Android Camera API2.0下全新的Camera FW/HAL架构简述

本文均属自己阅读源码的点滴总结,转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2[email protected] Software:系统源码Android5.1 前沿: 前面博文大多少总结的是Camera HAL1到HAL3的系统架构,但这些架构对于Camera APP开发来说依旧还是处于Camera API1.0的标准.而随着Camera3.HAL3.0等的不断更新,Google先是在Framework中更改了整个架构从而去匹配Camera A

sc7731 Android 5.1 Camera 学习之二 framework 到 HAL接口整理

前面已经分析过,Client端发起远程调用,而实际完成处理任务的,是Server端的 CameraClient 实例.远程client 和 server是两个不同的进程,它们使用binder作为通信工具,完成进程间的通信. 注:CameraClient定义如下: 1 class CameraClient : public CameraService::Client 2 { 3 //... 4 }; App需要对Camera进行各种操作,framework-java 和framework-c++

android从应用到驱动之—camera(2)---cameraHAL的实现

本来想用这一篇博客把cameraHAL的实现和流程都给写完的.搞了半天,东西实在是太多了.这篇先写cameraHAL的基本实现框架,下一篇在具体写camerahal的流程吧. cameraHAL的实现: 对于初学者来说,最大的疑问是系统是如何调用hardware的.这里就以camera来举例说明.调用hardware的程序是cameraservice,我们就去它里面看看它是如何找到hardware的 先把源码贴上来: /* ** ** Copyright (C) 2008, The Androi

android 如何实现前置camera自拍镜像功能

默认的前置camera, 文字"XI"在preview时显示为"IX"(前置camera preview时默认会有mirror效果), 拍摄出来的照片为"XI",如何让拍摄出来的照片也是"IX" , 也就是和preview时保持一致? 对于普通单拍(非ZSD或其他拍照模式), 需要修改的代码为normalShot.cpp文件中的onCmd_capture()方法, 将原来的 bool NormalShot:: onCmd_ca

(转)android从应用到驱动之—camera(1)---程序调用流程

一.开篇 写博客还得写开篇介绍,可惜,这个不是我所擅长的.就按我自己的想法写吧. 话说camera模块,从上层到底层一共包含着这么几个部分: 1.apk------java语言 2.camera的java接口----java语言 3.camera的java接口的具体实现,即所谓的JNI-----(java—>C++) 4.camera客户端-----C++语言 5.camera服务器----C++语言 6.camera硬件抽象层,即所谓的HAL------C++语言 7.camera驱动 如上也

Android HAL模块实现(转)

Android的HAL(Hardware Abstract Layer硬件抽象层)是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚.思路是把控制硬件的动作都放到了Android HAL中,而linux driver仅仅完成一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space.而Android是基于Aparch的license,因此硬件厂商可以只提供二进制代码,所以说Android只是一个开放的平台,并不是 一个开源的平台. 总结下来,Androi

android从应用到驱动之—camera(2)---cameraHAL的实现(转)

cameraHAL的实现: 对于初学者来说,最大的疑问是系统是如何调用hardware的.这里就以camera来举例说明.调用hardware的程序是cameraservice,我们就去它里面看看它是如何找到hardware的 先把源码贴上来: /* ** ** Copyright (C) 2008, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License