/* ** Copyright (c) 2011 The Linux Foundation. All rights reserved. ** ** 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. */ /*#error uncomment this for compiler test!*/ //#define ALOG_NDEBUG 0 #define ALOG_NIDEBUG 0 #define LOG_TAG "QCameraHWI_Record" #include #include #include #include #include #include "QCameraStream.h" #define LIKELY(exp) __builtin_expect(!!(exp), 1) #define UNLIKELY(exp) __builtin_expect(!!(exp), 0) /* QCameraStream_record class implementation goes here*/ /* following code implement the video streaming capture & encoding logic of this class*/ // --------------------------------------------------------------------------- // QCameraStream_record createInstance() // --------------------------------------------------------------------------- namespace android { QCameraStream* QCameraStream_record::createInstance(int cameraId, camera_mode_t mode) { ALOGV("%s: BEGIN", __func__); QCameraStream* pme = new QCameraStream_record(cameraId, mode); ALOGV("%s: END", __func__); return pme; } // --------------------------------------------------------------------------- // QCameraStream_record deleteInstance() // --------------------------------------------------------------------------- void QCameraStream_record::deleteInstance(QCameraStream *ptr) { ALOGV("%s: BEGIN", __func__); if (ptr){ ptr->release(); delete ptr; ptr = NULL; } ALOGV("%s: END", __func__); } // --------------------------------------------------------------------------- // QCameraStream_record Constructor // --------------------------------------------------------------------------- QCameraStream_record::QCameraStream_record(int cameraId, camera_mode_t mode) :QCameraStream(cameraId,mode), mDebugFps(false) { mHalCamCtrl = NULL; char value[PROPERTY_VALUE_MAX]; ALOGV("%s: BEGIN", __func__); property_get("persist.debug.sf.showfps", value, "0"); mDebugFps = atoi(value); ALOGV("%s: END", __func__); } // --------------------------------------------------------------------------- // QCameraStream_record Destructor // --------------------------------------------------------------------------- QCameraStream_record::~QCameraStream_record() { ALOGV("%s: BEGIN", __func__); if(mActive) { stop(); } if(mInit) { release(); } mInit = false; mActive = false; ALOGV("%s: END", __func__); } // --------------------------------------------------------------------------- // QCameraStream_record Callback from mm_camera // --------------------------------------------------------------------------- static void record_notify_cb(mm_camera_ch_data_buf_t *bufs_new, void *user_data) { QCameraStream_record *pme = (QCameraStream_record *)user_data; mm_camera_ch_data_buf_t *bufs_used = 0; ALOGV("%s: BEGIN", __func__); /* * Call Function Process Video Data */ pme->processRecordFrame(bufs_new); ALOGV("%s: END", __func__); } // --------------------------------------------------------------------------- // QCameraStream_record // --------------------------------------------------------------------------- status_t QCameraStream_record::init() { status_t ret = NO_ERROR; ALOGV("%s: BEGIN", __func__); /* * Acquiring Video Channel */ ret = QCameraStream::initChannel (mCameraId, MM_CAMERA_CH_VIDEO_MASK); if (NO_ERROR!=ret) { ALOGE("%s ERROR: Can't init native cammera preview ch\n",__func__); return ret; } /* * Register the Callback with camera */ (void) cam_evt_register_buf_notify(mCameraId, MM_CAMERA_CH_VIDEO, record_notify_cb, MM_CAMERA_REG_BUF_CB_INFINITE, 0, this); mInit = true; ALOGV("%s: END", __func__); return ret; } // --------------------------------------------------------------------------- // QCameraStream_record // --------------------------------------------------------------------------- status_t QCameraStream_record::start() { status_t ret = NO_ERROR; ALOGV("%s: BEGIN", __func__); Mutex::Autolock lock(mStopCallbackLock); if(!mInit) { ALOGE("%s ERROR: Record buffer not registered",__func__); return BAD_VALUE; } setFormat(MM_CAMERA_CH_VIDEO_MASK , (cam_format_t)0); //mRecordFreeQueueLock.lock(); //mRecordFreeQueue.clear(); //mRecordFreeQueueLock.unlock(); /* * Allocating Encoder Frame Buffers */ ret = initEncodeBuffers(); if (NO_ERROR!=ret) { ALOGE("%s ERROR: Buffer Allocation Failed\n",__func__); goto error; } ret = cam_config_prepare_buf(mCameraId, &mRecordBuf); if(ret != MM_CAMERA_OK) { ALOGE("%s ERROR: Reg Record buf err=%d\n", __func__, ret); ret = BAD_VALUE; goto error; }else{ ret = NO_ERROR; } /* * Start Video Streaming */ ret = cam_ops_action(mCameraId, true, MM_CAMERA_OPS_VIDEO, 0); if (MM_CAMERA_OK != ret) { ALOGE ("%s ERROR: Video streaming start err=%d\n", __func__, ret); ret = BAD_VALUE; goto error; }else{ ALOGV("%s : Video streaming Started",__func__); ret = NO_ERROR; } mActive = true; ALOGV("%s: END", __func__); return ret; error: releaseEncodeBuffer(); ALOGV("%s: END", __func__); return ret; } void QCameraStream_record::releaseEncodeBuffer() { for(int cnt = 0; cnt < mHalCamCtrl->mRecordingMemory.buffer_count; cnt++) { if (NO_ERROR != mHalCamCtrl->sendUnMappingBuf(MSM_V4L2_EXT_CAPTURE_MODE_VIDEO, cnt, mCameraId, CAM_SOCK_MSG_TYPE_FD_UNMAPPING)) ALOGE("%s: Unmapping Video Data Failed", __func__); if (mHalCamCtrl->mStoreMetaDataInFrame) { struct encoder_media_buffer_type * packet = (struct encoder_media_buffer_type *) mHalCamCtrl->mRecordingMemory.metadata_memory[cnt]->data; native_handle_delete(const_cast(packet->meta_handle)); mHalCamCtrl->mRecordingMemory.metadata_memory[cnt]->release( mHalCamCtrl->mRecordingMemory.metadata_memory[cnt]); } mHalCamCtrl->mRecordingMemory.camera_memory[cnt]->release( mHalCamCtrl->mRecordingMemory.camera_memory[cnt]); close(mHalCamCtrl->mRecordingMemory.fd[cnt]); mHalCamCtrl->mRecordingMemory.fd[cnt] = -1; #ifdef USE_ION mHalCamCtrl->deallocate_ion_memory(&mHalCamCtrl->mRecordingMemory, cnt); #endif } memset(&mHalCamCtrl->mRecordingMemory, 0, sizeof(mHalCamCtrl->mRecordingMemory)); //mNumRecordFrames = 0; delete[] recordframes; if (mRecordBuf.video.video.buf.mp) delete[] mRecordBuf.video.video.buf.mp; } // --------------------------------------------------------------------------- // QCameraStream_record // --------------------------------------------------------------------------- void QCameraStream_record::stop() { status_t ret = NO_ERROR; ALOGV("%s: BEGIN", __func__); if(!mActive) { ALOGE("%s : Record stream not started",__func__); return; } mActive = false; Mutex::Autolock lock(mStopCallbackLock); #if 0 //mzhu, when stop recording, all frame will be dirty. no need to queue frame back to kernel any more mRecordFreeQueueLock.lock(); while(!mRecordFreeQueue.isEmpty()) { ALOGV("%s : Pre-releasing of Encoder buffers!\n", __FUNCTION__); mm_camera_ch_data_buf_t releasedBuf = mRecordFreeQueue.itemAt(0); mRecordFreeQueue.removeAt(0); mRecordFreeQueueLock.unlock(); ALOGV("%s (%d): releasedBuf.idx = %d\n", __FUNCTION__, __LINE__, releasedBuf.video.video.idx); if(MM_CAMERA_OK != cam_evt_buf_done(mCameraId,&releasedBuf)) ALOGE("%s : Buf Done Failed",__func__); } mRecordFreeQueueLock.unlock(); #if 0 while (!mRecordFreeQueue.isEmpty()) { ALOGE("%s : Waiting for Encoder to release all buffer!\n", __FUNCTION__); } #endif #endif // mzhu /* unregister the notify fn from the mmmm_camera_t object * call stop() in parent class to stop the monitor thread */ ret = cam_ops_action(mCameraId, false, MM_CAMERA_OPS_VIDEO, 0); if (MM_CAMERA_OK != ret) { ALOGE ("%s ERROR: Video streaming Stop err=%d\n", __func__, ret); } ret = cam_config_unprepare_buf(mCameraId, MM_CAMERA_CH_VIDEO); if(ret != MM_CAMERA_OK){ ALOGE("%s ERROR: Ureg video buf \n", __func__); } releaseEncodeBuffer(); mActive = false; ALOGV("%s: END", __func__); } // --------------------------------------------------------------------------- // QCameraStream_record // --------------------------------------------------------------------------- void QCameraStream_record::release() { status_t ret = NO_ERROR; ALOGV("%s: BEGIN", __func__); if(mActive) { stop(); } if(!mInit) { ALOGE("%s : Record stream not initialized",__func__); return; } ret= QCameraStream::deinitChannel(mCameraId, MM_CAMERA_CH_VIDEO); if(ret != MM_CAMERA_OK) { ALOGE("%s:Deinit Video channel failed=%d\n", __func__, ret); } (void)cam_evt_register_buf_notify(mCameraId, MM_CAMERA_CH_VIDEO, NULL, (mm_camera_register_buf_cb_type_t)NULL, NULL, NULL); mInit = false; ALOGV("%s: END", __func__); } status_t QCameraStream_record::processRecordFrame(void *data) { ALOGV("%s : BEGIN",__func__); mm_camera_ch_data_buf_t* frame = (mm_camera_ch_data_buf_t*) data; Mutex::Autolock lock(mStopCallbackLock); if(!mActive) { ALOGE("Recording Stopped. Returning callback"); return NO_ERROR; } if (UNLIKELY(mDebugFps)) { debugShowVideoFPS(); } mHalCamCtrl->dumpFrameToFile(frame->video.video.frame, HAL_DUMP_FRM_VIDEO); mHalCamCtrl->mCallbackLock.lock(); camera_data_timestamp_callback rcb = mHalCamCtrl->mDataCbTimestamp; void *rdata = mHalCamCtrl->mCallbackCookie; mHalCamCtrl->mCallbackLock.unlock(); nsecs_t timeStamp = nsecs_t(frame->video.video.frame->ts.tv_sec)*1000000000LL + \ frame->video.video.frame->ts.tv_nsec; ALOGV("Send Video frame to services/encoder TimeStamp : %lld",timeStamp); mRecordedFrames[frame->video.video.idx] = *frame; #ifdef USE_ION struct ion_flush_data cache_inv_data; int ion_fd; ion_fd = frame->video.video.frame->ion_dev_fd; cache_inv_data.vaddr = (void *)frame->video.video.frame->buffer; cache_inv_data.fd = frame->video.video.frame->fd; cache_inv_data.handle = frame->video.video.frame->fd_data.handle; cache_inv_data.length = frame->video.video.frame->ion_alloc.len; if (mHalCamCtrl->cache_ops(ion_fd, &cache_inv_data, ION_IOC_CLEAN_CACHES) < 0) ALOGE("%s: Cache clean for Video buffer %p fd = %d failed", __func__, cache_inv_data.vaddr, cache_inv_data.fd); #endif if (mHalCamCtrl->mStoreMetaDataInFrame) { mStopCallbackLock.unlock(); if(mActive && (rcb != NULL) && (mHalCamCtrl->mMsgEnabled & CAMERA_MSG_VIDEO_FRAME)) { rcb(timeStamp, CAMERA_MSG_VIDEO_FRAME, mHalCamCtrl->mRecordingMemory.metadata_memory[frame->video.video.idx], 0, mHalCamCtrl->mCallbackCookie); } } else { mStopCallbackLock.unlock(); if(mActive && (rcb != NULL) && (mHalCamCtrl->mMsgEnabled & CAMERA_MSG_VIDEO_FRAME)) { rcb(timeStamp, CAMERA_MSG_VIDEO_FRAME, mHalCamCtrl->mRecordingMemory.camera_memory[frame->video.video.idx], 0, mHalCamCtrl->mCallbackCookie); } } ALOGV("%s : END",__func__); return NO_ERROR; } //Record Related Functions status_t QCameraStream_record::initEncodeBuffers() { ALOGV("%s : BEGIN",__func__); status_t ret = NO_ERROR; const char *pmem_region; uint32_t frame_len; uint8_t num_planes; uint32_t planes[VIDEO_MAX_PLANES]; //cam_ctrl_dimension_t dim; int width = 0; /* width of channel */ int height = 0; /* height of channel */ int buf_cnt; pmem_region = "/dev/pmem_adsp"; memset(&mHalCamCtrl->mRecordingMemory, 0, sizeof(mHalCamCtrl->mRecordingMemory)); memset(&dim, 0, sizeof(cam_ctrl_dimension_t)); ret = cam_config_get_parm(mCameraId, MM_CAMERA_PARM_DIMENSION, &dim); if (MM_CAMERA_OK != ret) { ALOGE("%s: ERROR - can't get camera dimension!", __func__); return BAD_VALUE; } else { width = dim.video_width; height = dim.video_height; } num_planes = 2; planes[0] = dim.video_frame_offset.mp[0].len; planes[1] = dim.video_frame_offset.mp[1].len; frame_len = dim.video_frame_offset.frame_len; buf_cnt = VIDEO_BUFFER_COUNT; if(mHalCamCtrl->isLowPowerCamcorder()) { ALOGV("%s: lower power camcorder selected", __func__); buf_cnt = VIDEO_BUFFER_COUNT_LOW_POWER_CAMCORDER; } recordframes = new msm_frame[buf_cnt]; memset(recordframes,0,sizeof(struct msm_frame) * buf_cnt); mRecordBuf.video.video.buf.mp = new mm_camera_mp_buf_t[buf_cnt * sizeof(mm_camera_mp_buf_t)]; if (!mRecordBuf.video.video.buf.mp) { ALOGE("%s Error allocating memory for mplanar struct ", __func__); return BAD_VALUE; } memset(mRecordBuf.video.video.buf.mp, 0, buf_cnt * sizeof(mm_camera_mp_buf_t)); memset(&mHalCamCtrl->mRecordingMemory, 0, sizeof(mHalCamCtrl->mRecordingMemory)); for (int i=0; imRecordingMemory.main_ion_fd[i] = -1; mHalCamCtrl->mRecordingMemory.fd[i] = -1; } mHalCamCtrl->mRecordingMemory.buffer_count = buf_cnt; mHalCamCtrl->mRecordingMemory.size = frame_len; mHalCamCtrl->mRecordingMemory.cbcr_offset = planes[0]; for (int cnt = 0; cnt < mHalCamCtrl->mRecordingMemory.buffer_count; cnt++) { #ifdef USE_ION if(mHalCamCtrl->allocate_ion_memory(&mHalCamCtrl->mRecordingMemory, cnt, ((0x1 << CAMERA_ION_HEAP_ID) | (0x1 << CAMERA_ION_FALLBACK_HEAP_ID))) < 0) { ALOGE("%s ION alloc failed\n", __func__); return UNKNOWN_ERROR; } #else mHalCamCtrl->mRecordingMemory.fd[cnt] = open("/dev/pmem_adsp", O_RDWR|O_SYNC); if(mHalCamCtrl->mRecordingMemory.fd[cnt] <= 0) { ALOGE("%s: no pmem for frame %d", __func__, cnt); return UNKNOWN_ERROR; } #endif mHalCamCtrl->mRecordingMemory.camera_memory[cnt] = mHalCamCtrl->mGetMemory(mHalCamCtrl->mRecordingMemory.fd[cnt], mHalCamCtrl->mRecordingMemory.size, 1, (void *)this); if (mHalCamCtrl->mStoreMetaDataInFrame) { mHalCamCtrl->mRecordingMemory.metadata_memory[cnt] = mHalCamCtrl->mGetMemory(-1, sizeof(struct encoder_media_buffer_type), 1, (void *)this); struct encoder_media_buffer_type * packet = (struct encoder_media_buffer_type *) mHalCamCtrl->mRecordingMemory.metadata_memory[cnt]->data; packet->meta_handle = native_handle_create(1, 2); //1 fd, 1 offset and 1 size packet->buffer_type = kMetadataBufferTypeCameraSource; native_handle_t * nh = const_cast(packet->meta_handle); nh->data[0] = mHalCamCtrl->mRecordingMemory.fd[cnt]; nh->data[1] = 0; nh->data[2] = mHalCamCtrl->mRecordingMemory.size; } recordframes[cnt].fd = mHalCamCtrl->mRecordingMemory.fd[cnt]; recordframes[cnt].buffer = (uint32_t)mHalCamCtrl->mRecordingMemory.camera_memory[cnt]->data; recordframes[cnt].y_off = 0; recordframes[cnt].cbcr_off = mHalCamCtrl->mRecordingMemory.cbcr_offset; recordframes[cnt].path = OUTPUT_TYPE_V; recordframes[cnt].fd_data = mHalCamCtrl->mRecordingMemory.ion_info_fd[cnt]; recordframes[cnt].ion_alloc = mHalCamCtrl->mRecordingMemory.alloc[cnt]; recordframes[cnt].ion_dev_fd = mHalCamCtrl->mRecordingMemory.main_ion_fd[cnt]; if (NO_ERROR != mHalCamCtrl->sendMappingBuf(MSM_V4L2_EXT_CAPTURE_MODE_VIDEO, cnt, recordframes[cnt].fd, mHalCamCtrl->mRecordingMemory.size, mCameraId, CAM_SOCK_MSG_TYPE_FD_MAPPING)) ALOGE("%s: sending mapping data Msg Failed", __func__); ALOGV("initRecord : record heap , video buffers buffer=%lu fd=%d y_off=%d cbcr_off=%d\n", (unsigned long)recordframes[cnt].buffer, recordframes[cnt].fd, recordframes[cnt].y_off, recordframes[cnt].cbcr_off); //mNumRecordFrames++; mRecordBuf.video.video.buf.mp[cnt].frame = recordframes[cnt]; mRecordBuf.video.video.buf.mp[cnt].frame_offset = 0; mRecordBuf.video.video.buf.mp[cnt].num_planes = num_planes; /* Plane 0 needs to be set seperately. Set other planes * in a loop. */ mRecordBuf.video.video.buf.mp[cnt].planes[0].reserved[0] = mRecordBuf.video.video.buf.mp[cnt].frame_offset; mRecordBuf.video.video.buf.mp[cnt].planes[0].length = planes[0]; mRecordBuf.video.video.buf.mp[cnt].planes[0].m.userptr = recordframes[cnt].fd; for (int j = 1; j < num_planes; j++) { mRecordBuf.video.video.buf.mp[cnt].planes[j].length = planes[j]; mRecordBuf.video.video.buf.mp[cnt].planes[j].m.userptr = recordframes[cnt].fd; mRecordBuf.video.video.buf.mp[cnt].planes[j].reserved[0] = mRecordBuf.video.video.buf.mp[cnt].planes[j-1].reserved[0] + mRecordBuf.video.video.buf.mp[cnt].planes[j-1].length; } } //memset(&mRecordBuf, 0, sizeof(mRecordBuf)); mRecordBuf.ch_type = MM_CAMERA_CH_VIDEO; mRecordBuf.video.video.num = mHalCamCtrl->mRecordingMemory.buffer_count;//kRecordBufferCount; //mRecordBuf.video.video.frame_offset = &record_offset[0]; //mRecordBuf.video.video.frame = &recordframes[0]; ALOGV("%s : END",__func__); return NO_ERROR; } void QCameraStream_record::releaseRecordingFrame(const void *opaque) { ALOGV("%s : BEGIN, opaque = 0x%p",__func__, opaque); if(!mActive) { ALOGE("%s : Recording already stopped!!! Leak???",__func__); return; } for(int cnt = 0; cnt < mHalCamCtrl->mRecordingMemory.buffer_count; cnt++) { if (mHalCamCtrl->mStoreMetaDataInFrame) { if(mHalCamCtrl->mRecordingMemory.metadata_memory[cnt] && mHalCamCtrl->mRecordingMemory.metadata_memory[cnt]->data == opaque) { /* found the match */ if(MM_CAMERA_OK != cam_evt_buf_done(mCameraId, &mRecordedFrames[cnt])) ALOGE("%s : Buf Done Failed",__func__); ALOGV("%s : END",__func__); return; } } else { if(mHalCamCtrl->mRecordingMemory.camera_memory[cnt] && mHalCamCtrl->mRecordingMemory.camera_memory[cnt]->data == opaque) { /* found the match */ if(MM_CAMERA_OK != cam_evt_buf_done(mCameraId, &mRecordedFrames[cnt])) ALOGE("%s : Buf Done Failed",__func__); ALOGV("%s : END",__func__); return; } } } ALOGE("%s: cannot find the matched frame with opaue = 0x%p", __func__, opaque); } void QCameraStream_record::debugShowVideoFPS() const { static int mFrameCount; static int mLastFrameCount = 0; static nsecs_t mLastFpsTime = 0; static float mFps = 0; mFrameCount++; nsecs_t now = systemTime(); nsecs_t diff = now - mLastFpsTime; if (diff > ms2ns(250)) { mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; ALOGV("Video Frames Per Second: %.4f", mFps); mLastFpsTime = now; mLastFrameCount = mFrameCount; } } #if 0 sp QCameraStream_record::getHeap() const { return mRecordHeap != NULL ? mRecordHeap->mHeap : NULL; } #endif status_t QCameraStream_record::takeLiveSnapshot(){ return true; } }//namespace android