| /* Copyright (c) 2012, The Linux Foundataion. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #define LOG_TAG "QCameraStream" |
| |
| #include <utils/Log.h> |
| #include <utils/Errors.h> |
| #include "QCamera2HWI.h" |
| #include "QCameraStream.h" |
| |
| namespace qcamera { |
| |
| int32_t QCameraStream::get_bufs( |
| cam_frame_len_offset_t *offset, |
| uint8_t *num_bufs, |
| uint8_t **initial_reg_flag, |
| mm_camera_buf_def_t **bufs, |
| mm_camera_map_unmap_ops_tbl_t *ops_tbl, |
| void *user_data) |
| { |
| QCameraStream *stream = reinterpret_cast<QCameraStream *>(user_data); |
| if (!stream) { |
| ALOGE("getBufs invalid stream pointer"); |
| return NO_MEMORY; |
| } |
| return stream->getBufs(offset, num_bufs, initial_reg_flag, bufs, ops_tbl); |
| } |
| |
| int32_t QCameraStream::put_bufs( |
| mm_camera_map_unmap_ops_tbl_t *ops_tbl, |
| void *user_data) |
| { |
| QCameraStream *stream = reinterpret_cast<QCameraStream *>(user_data); |
| if (!stream) { |
| ALOGE("putBufs invalid stream pointer"); |
| return NO_MEMORY; |
| } |
| return stream->putBufs(ops_tbl); |
| } |
| |
| QCameraStream::QCameraStream(QCameraAllocator &allocator, |
| uint32_t camHandle, |
| uint32_t chId, |
| mm_camera_ops_t *camOps, |
| cam_padding_info_t *paddingInfo) : |
| mCamHandle(camHandle), |
| mChannelHandle(chId), |
| mHandle(0), |
| mCamOps(camOps), |
| mStreamInfo(NULL), |
| mNumBufs(0), |
| mDataCB(NULL), |
| mStreamInfoBuf(NULL), |
| mStreamBufs(NULL), |
| mAllocator(allocator), |
| mBufDefs(NULL) |
| { |
| mMemVtbl.user_data = this; |
| mMemVtbl.get_bufs = get_bufs; |
| mMemVtbl.put_bufs = put_bufs; |
| memset(&mFrameLenOffset, 0, sizeof(mFrameLenOffset)); |
| memcpy(&mPaddingInfo, paddingInfo, sizeof(cam_padding_info_t)); |
| } |
| |
| QCameraStream::~QCameraStream() |
| { |
| int rc = mCamOps->unmap_stream_buf(mCamHandle, |
| mChannelHandle, mHandle, CAM_MAPPING_BUF_TYPE_STREAM_INFO, 0, -1); |
| if (rc < 0) { |
| ALOGE("Failed to map stream info buffer"); |
| } |
| mStreamInfoBuf->deallocate(); |
| delete mStreamInfoBuf; |
| |
| // delete stream |
| if (mHandle > 0) { |
| mCamOps->delete_stream(mCamHandle, mChannelHandle, mHandle); |
| mHandle = 0; |
| } |
| } |
| |
| int32_t QCameraStream::init(cam_stream_type_t stream_type, |
| stream_cb_routine stream_cb, void *userdata) |
| { |
| int32_t rc = OK; |
| mm_camera_stream_config_t stream_config; |
| |
| mHandle = mCamOps->add_stream(mCamHandle, mChannelHandle); |
| if (!mHandle) { |
| ALOGE("add_stream failed"); |
| rc = UNKNOWN_ERROR; |
| goto done; |
| } |
| |
| // Allocate and map stream info memory |
| mStreamInfoBuf = mAllocator.allocateStreamInfoBuf(stream_type); |
| if (!mStreamInfoBuf) { |
| ALOGE("Failed to allocate stream info object"); |
| goto err1; |
| } |
| mStreamInfo = reinterpret_cast<cam_stream_info_t *>(mStreamInfoBuf->getPtr(0)); |
| |
| rc = mCamOps->map_stream_buf(mCamHandle, |
| mChannelHandle, mHandle, CAM_MAPPING_BUF_TYPE_STREAM_INFO, |
| 0, -1, mStreamInfoBuf->getFd(0), mStreamInfoBuf->getSize(0)); |
| if (rc < 0) { |
| ALOGE("Failed to map stream info buffer"); |
| goto err2; |
| } |
| |
| // Configure the stream |
| mStreamInfo->bundle_id = mChannelHandle; |
| stream_config.stream_info = mStreamInfo; |
| stream_config.mem_vtbl = mMemVtbl; |
| stream_config.stream_cb = dataNotifyCB; |
| stream_config.padding_info = mPaddingInfo; |
| stream_config.userdata = this; |
| rc = mCamOps->config_stream(mCamHandle, |
| mChannelHandle, mHandle, &stream_config); |
| if (rc < 0) { |
| ALOGE("Failed to config stream, rc = %d", rc); |
| goto err3; |
| } |
| |
| mDataCB = stream_cb; |
| mUserData = userdata; |
| return 0; |
| |
| err3: |
| mCamOps->unmap_stream_buf(mCamHandle, |
| mChannelHandle, mHandle, CAM_MAPPING_BUF_TYPE_STREAM_INFO, 0, -1); |
| |
| err2: |
| mStreamInfoBuf->deallocate(); |
| delete mStreamInfoBuf; |
| err1: |
| mCamOps->delete_stream(mCamHandle, mChannelHandle, mHandle); |
| done: |
| return rc; |
| } |
| |
| int32_t QCameraStream::start() |
| { |
| int32_t rc = 0; |
| rc = mProcTh.launch(dataProcRoutine, this); |
| return rc; |
| } |
| |
| int32_t QCameraStream::stop() |
| { |
| int32_t rc = 0; |
| rc = mProcTh.exit(); |
| return rc; |
| } |
| |
| int32_t QCameraStream::processZoomDone(preview_stream_ops_t *previewWindow) |
| { |
| int32_t rc = 0; |
| |
| // get stream param for crop info |
| mStreamInfo->parm_buf.is_crop_valid = TRUE; |
| rc = mCamOps->get_stream_parms(mCamHandle, |
| mChannelHandle, |
| mHandle, |
| &mStreamInfo->parm_buf); |
| |
| |
| // update preview window crop if it's preview/postview stream |
| if ( (previewWindow != NULL) && |
| (mStreamInfo->stream_type == CAM_STREAM_TYPE_PREVIEW || |
| mStreamInfo->stream_type == CAM_STREAM_TYPE_POSTVIEW) ) { |
| rc = previewWindow->set_crop(previewWindow, |
| mStreamInfo->parm_buf.crop.left, |
| mStreamInfo->parm_buf.crop.top, |
| mStreamInfo->parm_buf.crop.width, |
| mStreamInfo->parm_buf.crop.height); |
| } |
| |
| return rc; |
| } |
| |
| int32_t QCameraStream::processDataNotify(mm_camera_super_buf_t *frame) |
| { |
| ALOGI("%s:\n", __func__); |
| mDataQ.enqueue((void *)frame); |
| return mProcTh.sendCmd(CAMERA_CMD_TYPE_DO_NEXT_JOB, FALSE, FALSE); |
| } |
| |
| void QCameraStream::dataNotifyCB(mm_camera_super_buf_t *recvd_frame, |
| void *userdata) |
| { |
| ALOGI("%s:\n", __func__); |
| QCameraStream* stream = (QCameraStream *)userdata; |
| if (stream == NULL || |
| recvd_frame == NULL || |
| recvd_frame->bufs[0] == NULL || |
| recvd_frame->bufs[0]->stream_id != stream->getMyHandle()) { |
| ALOGE("%s: Not a valid stream to handle buf", __func__); |
| return; |
| } |
| |
| mm_camera_super_buf_t *frame = |
| (mm_camera_super_buf_t *)malloc(sizeof(mm_camera_super_buf_t)); |
| if (frame == NULL) { |
| ALOGE("%s: No mem for mm_camera_buf_def_t", __func__); |
| stream->bufDone(recvd_frame->bufs[0]->buf_idx); |
| return; |
| } |
| *frame = *recvd_frame; |
| stream->processDataNotify(frame); |
| return; |
| } |
| |
| void *QCameraStream::dataProcRoutine(void *data) |
| { |
| int running = 1; |
| int ret; |
| QCameraStream *pme = (QCameraStream *)data; |
| QCameraCmdThread *cmdThread = &pme->mProcTh; |
| |
| ALOGI("%s: E", __func__); |
| do { |
| do { |
| ret = cam_sem_wait(&cmdThread->cmd_sem); |
| if (ret != 0 && errno != EINVAL) { |
| ALOGE("%s: cam_sem_wait error (%s)", |
| __func__, strerror(errno)); |
| return NULL; |
| } |
| } while (ret != 0); |
| |
| // we got notified about new cmd avail in cmd queue |
| camera_cmd_type_t cmd = cmdThread->getCmd(); |
| ALOGD("%s: get cmd %d", __func__, cmd); |
| switch (cmd) { |
| case CAMERA_CMD_TYPE_DO_NEXT_JOB: |
| { |
| mm_camera_super_buf_t *frame = |
| (mm_camera_super_buf_t *)pme->mDataQ.dequeue(); |
| if (NULL != frame) { |
| if (pme->mDataCB != NULL) { |
| pme->mDataCB(frame, pme, pme->mUserData); |
| } else { |
| // no data cb routine, return buf here |
| pme->bufDone(frame->bufs[0]->buf_idx); |
| free(frame); |
| } |
| } |
| } |
| break; |
| case CAMERA_CMD_TYPE_EXIT: |
| /* flush data buf queue */ |
| pme->mDataQ.flush(); |
| running = 0; |
| break; |
| default: |
| break; |
| } |
| } while (running); |
| ALOGD("%s: X", __func__); |
| return NULL; |
| } |
| |
| int32_t QCameraStream::bufDone(int index) |
| { |
| int32_t rc = NO_ERROR; |
| |
| if (index >= mNumBufs || mBufDefs == NULL) |
| return BAD_INDEX; |
| |
| rc = mCamOps->qbuf(mCamHandle, mChannelHandle, &mBufDefs[index]); |
| if (rc < 0) |
| return rc; |
| |
| mStreamBufs->invalidateCache(index); |
| return rc; |
| } |
| |
| int32_t QCameraStream::bufDone(const void *opaque, bool isMetaData) |
| { |
| int32_t rc = NO_ERROR; |
| |
| int index = mStreamBufs->getMatchBufIndex(opaque, isMetaData); |
| if (index == -1 || index >= mNumBufs) { |
| return BAD_INDEX; |
| } |
| |
| rc = bufDone(index); |
| return rc; |
| } |
| |
| int32_t QCameraStream::getBufs(cam_frame_len_offset_t *offset, |
| uint8_t *num_bufs, |
| uint8_t **initial_reg_flag, |
| mm_camera_buf_def_t **bufs, |
| mm_camera_map_unmap_ops_tbl_t *ops_tbl) |
| { |
| int rc = NO_ERROR; |
| uint8_t *regFlags; |
| |
| if (!ops_tbl) { |
| ALOGE("getBufs: ops_tbl is NULL"); |
| return INVALID_OPERATION; |
| } |
| |
| mFrameLenOffset = *offset; |
| |
| //Allocate and map stream info buffer |
| mStreamBufs = mAllocator.allocateStreamBuf(mStreamInfo->stream_type, |
| mFrameLenOffset.frame_len); |
| if (!mStreamBufs) { |
| ALOGE("Failed to allocate stream buffers"); |
| return NO_MEMORY; |
| } |
| |
| mNumBufs = mStreamBufs->getCnt(); |
| for (int i = 0; i < mNumBufs; i++) { |
| rc = ops_tbl->map_ops(i, -1, mStreamBufs->getFd(i), |
| mStreamBufs->getSize(i), ops_tbl->userdata); |
| if (rc < 0) { |
| ALOGE("getBufs: map_stream_buf failed: %d", rc); |
| for (int j = 0; j < i; j++) { |
| ops_tbl->unmap_ops(j, -1, ops_tbl->userdata); |
| } |
| mStreamBufs->deallocate(); |
| delete mStreamBufs; |
| mStreamBufs = NULL; |
| return INVALID_OPERATION; |
| } |
| } |
| |
| //regFlags array is allocated by us, but consumed and freed by mm-camera-interface |
| regFlags = (uint8_t *)malloc(sizeof(uint8_t) * mNumBufs); |
| if (!regFlags) { |
| ALOGE("Out of memory"); |
| for (int i = 0; i < mNumBufs; i++) { |
| ops_tbl->unmap_ops(i, -1, ops_tbl->userdata); |
| } |
| mStreamBufs->deallocate(); |
| delete mStreamBufs; |
| mStreamBufs = NULL; |
| return NO_MEMORY; |
| } |
| |
| mBufDefs = (mm_camera_buf_def_t *)malloc(mNumBufs * sizeof(mm_camera_buf_def_t)); |
| if (mBufDefs == NULL) { |
| ALOGE("getBufs: getRegFlags failed %d", rc); |
| for (int i = 0; i < mNumBufs; i++) { |
| ops_tbl->unmap_ops(i, -1, ops_tbl->userdata); |
| } |
| mStreamBufs->deallocate(); |
| delete mStreamBufs; |
| mStreamBufs = NULL; |
| return INVALID_OPERATION; |
| } |
| for (int i = 0; i < mNumBufs; i++) { |
| mStreamBufs->getBufDef(mFrameLenOffset, mBufDefs[i], i); |
| } |
| |
| rc = mStreamBufs->getRegFlags(regFlags); |
| if (rc < 0) { |
| ALOGE("getBufs: getRegFlags failed %d", rc); |
| for (int i = 0; i < mNumBufs; i++) { |
| ops_tbl->unmap_ops(i, -1, ops_tbl->userdata); |
| } |
| mStreamBufs->deallocate(); |
| delete mStreamBufs; |
| mStreamBufs = NULL; |
| free(mBufDefs); |
| mBufDefs = NULL; |
| return INVALID_OPERATION; |
| } |
| |
| *num_bufs = mNumBufs; |
| *initial_reg_flag = regFlags; |
| *bufs = mBufDefs; |
| return NO_ERROR; |
| } |
| |
| int32_t QCameraStream::putBufs(mm_camera_map_unmap_ops_tbl_t *ops_tbl) |
| { |
| int rc = NO_ERROR; |
| for (int i = 0; i < mNumBufs; i++) { |
| rc = ops_tbl->unmap_ops(i, -1, ops_tbl->userdata); |
| if (rc < 0) { |
| ALOGE("getBufs: map_stream_buf failed: %d", rc); |
| } |
| } |
| mBufDefs = NULL; // mBufDefs just keep a ptr to the buffer |
| // mm-camera-interface own the buffer, so no need to free |
| memset(&mFrameLenOffset, 0, sizeof(mFrameLenOffset)); |
| mStreamBufs->deallocate(); |
| delete mStreamBufs; |
| |
| return rc; |
| } |
| |
| bool QCameraStream::isTypeOf(cam_stream_type_t type) |
| { |
| if (mStreamInfo != NULL && (mStreamInfo->stream_type == type)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| int32_t QCameraStream::getFrameOffset(cam_frame_len_offset_t &offset) |
| { |
| offset = mFrameLenOffset; |
| return 0; |
| } |
| |
| int32_t QCameraStream::getCropInfo(cam_rect_t &crop) |
| { |
| if (mStreamInfo != NULL) { |
| crop = mStreamInfo->parm_buf.crop; |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t QCameraStream::getFrameDimension(cam_dimension_t &dim) |
| { |
| if (mStreamInfo != NULL) { |
| dim = mStreamInfo->dim; |
| return 0; |
| } |
| return -1; |
| } |
| |
| int32_t QCameraStream::getFormat(cam_format_t &fmt) |
| { |
| if (mStreamInfo != NULL) { |
| fmt = mStreamInfo->fmt; |
| return 0; |
| } |
| return -1; |
| } |
| |
| }; // namespace qcamera |