/*
** 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 <utils/Log.h>
#include <utils/threads.h>
#include <cutils/properties.h>
#include <fcntl.h>
#include <sys/mman.h>
#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<native_handle_t *>(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; i<MM_CAMERA_MAX_NUM_FRAMES;i++) {
mHalCamCtrl->mRecordingMemory.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<native_handle_t *>(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<IMemoryHeap> QCameraStream_record::getHeap() const
{
return mRecordHeap != NULL ? mRecordHeap->mHeap : NULL;
}
#endif
status_t QCameraStream_record::takeLiveSnapshot(){
return true;
}
}//namespace android