Qualcomm Camera HAL 2.0

可紊 提交于 2019-12-05 09:17:57

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 that@_@


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!