Merge "QCamera2: Removes obsolete code for AF, zoom events"
diff --git a/QCamera2/HAL/QCamera2HWI.cpp b/QCamera2/HAL/QCamera2HWI.cpp
index 9b128d0..98cc249 100644
--- a/QCamera2/HAL/QCamera2HWI.cpp
+++ b/QCamera2/HAL/QCamera2HWI.cpp
@@ -854,14 +854,16 @@
int QCamera2HardwareInterface::close_camera_device(hw_device_t *hw_dev)
{
int ret = NO_ERROR;
+ ALOGD("[KPI Perf] %s: E",__func__);
QCamera2HardwareInterface *hw =
reinterpret_cast<QCamera2HardwareInterface *>(
reinterpret_cast<camera_device_t *>(hw_dev)->priv);
if (!hw) {
- ALOGE("NULL camera device");
+ ALOGE("%s: NULL camera device", __func__);
return BAD_VALUE;
}
delete hw;
+ ALOGD("[KPI Perf] %s: X",__func__);
return ret;
}
@@ -1124,7 +1126,7 @@
}
/* Allocate memory for capability buffer */
- capabilityHeap = new QCameraHeapMemory();
+ capabilityHeap = new QCameraHeapMemory(QCAMERA_ION_USE_CACHE);
rc = capabilityHeap->allocate(1, sizeof(cam_capability_t));
if(rc != OK) {
ALOGE("%s: No memory for cappability", __func__);
@@ -1340,16 +1342,18 @@
{
int rc = NO_ERROR;
QCameraMemory *mem = NULL;
+ bool bCachedMem = QCAMERA_ION_USE_CACHE;
// Allocate stream buffer memory object
switch (stream_type) {
case CAM_STREAM_TYPE_PREVIEW:
{
if (isNoDisplayMode()) {
- mem = new QCameraStreamMemory(mGetMemory);
+ mem = new QCameraStreamMemory(mGetMemory, bCachedMem);
} else {
cam_dimension_t dim;
- QCameraGrallocMemory *grallocMemory = new QCameraGrallocMemory(mGetMemory);
+ QCameraGrallocMemory *grallocMemory =
+ new QCameraGrallocMemory(mGetMemory);
mParameters.getStreamDimension(stream_type, dim);
if (grallocMemory)
@@ -1362,7 +1366,8 @@
case CAM_STREAM_TYPE_POSTVIEW:
{
cam_dimension_t dim;
- QCameraGrallocMemory *grallocMemory = new QCameraGrallocMemory(mGetMemory);
+ QCameraGrallocMemory *grallocMemory =
+ new QCameraGrallocMemory(mGetMemory);
mParameters.getStreamDimension(stream_type, dim);
if (grallocMemory)
@@ -1375,10 +1380,18 @@
case CAM_STREAM_TYPE_RAW:
case CAM_STREAM_TYPE_METADATA:
case CAM_STREAM_TYPE_OFFLINE_PROC:
- mem = new QCameraStreamMemory(mGetMemory);
+ mem = new QCameraStreamMemory(mGetMemory, bCachedMem);
break;
case CAM_STREAM_TYPE_VIDEO:
- mem = new QCameraVideoMemory(mGetMemory);
+ {
+ char value[32];
+ property_get("persist.camera.mem.usecache", value, "1");
+ if (atoi(value) == 0) {
+ bCachedMem = QCAMERA_ION_USE_NOCACHE;
+ }
+ ALOGD("%s: vidoe buf using cached memory = %d", __func__, bCachedMem);
+ mem = new QCameraVideoMemory(mGetMemory, bCachedMem);
+ }
break;
case CAM_STREAM_TYPE_DEFAULT:
case CAM_STREAM_TYPE_MAX:
@@ -1415,7 +1428,7 @@
{
int rc = NO_ERROR;
- QCameraHeapMemory *streamInfoBuf = new QCameraHeapMemory();
+ QCameraHeapMemory *streamInfoBuf = new QCameraHeapMemory(QCAMERA_ION_USE_CACHE);
if (!streamInfoBuf) {
ALOGE("allocateStreamInfoBuf: Unable to allocate streamInfo object");
return NULL;
@@ -2156,7 +2169,7 @@
}
// allocate ion memory for source image
- QCameraHeapMemory *imgBuf = new QCameraHeapMemory();
+ QCameraHeapMemory *imgBuf = new QCameraHeapMemory(QCAMERA_ION_USE_CACHE);
if (imgBuf == NULL) {
ALOGE("%s: Unable to new heap memory obj for image buf", __func__);
return NO_MEMORY;
@@ -3021,8 +3034,6 @@
return rc;
}
- // TODO: commented out for now
-#if 0
rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_POSTVIEW,
postview_stream_cb_routine, this);
@@ -3031,7 +3042,6 @@
delete pChannel;
return rc;
}
-#endif
rc = addStreamToChannel(pChannel, CAM_STREAM_TYPE_SNAPSHOT,
NULL, this);
@@ -3151,6 +3161,10 @@
pp_config.denoise2d.denoise_enable = 1;
pp_config.denoise2d.process_plates = mParameters.getWaveletDenoiseProcessPlate();
}
+
+ if (isCACEnabled()) {
+ pp_config.feature_mask |= CAM_QCOM_FEATURE_CAC;
+ }
}
if (needRotationReprocess()) {
pp_config.feature_mask |= CAM_QCOM_FEATURE_ROTATION;
@@ -3852,6 +3866,25 @@
}
/*===========================================================================
+ * FUNCTION : isCACEnabled
+ *
+ * DESCRIPTION: if CAC is enabled
+ *
+ * PARAMETERS : none
+ *
+ * RETURN : true: needed
+ * false: no need
+ *==========================================================================*/
+bool QCamera2HardwareInterface::isCACEnabled()
+{
+ char prop[PROPERTY_VALUE_MAX];
+ memset(prop, 0, sizeof(prop));
+ property_get("persist.camera.feature.cac", prop, "0");
+ int enableCAC = atoi(prop);
+ return enableCAC == 1;
+}
+
+/*===========================================================================
* FUNCTION : needReprocess
*
* DESCRIPTION: if reprocess is needed
@@ -3870,7 +3903,7 @@
if (mParameters.isZSLMode() &&
((gCamCapability[mCameraId]->min_required_pp_mask > 0) ||
- mParameters.isWNREnabled())) {
+ mParameters.isWNREnabled() || isCACEnabled())) {
// TODO: add for ZSL HDR later
// pp module has min requirement for zsl reprocess, or WNR in ZSL mode
ALOGD("%s: need do reprocess for ZSL WNR or min PP reprocess", __func__);
@@ -4167,6 +4200,7 @@
*==========================================================================*/
int32_t QCamera2HardwareInterface::prepareHardwareForSnapshot(int32_t afNeeded)
{
+ ALOGD("[KPI Perf] %s: Prepare hardware such as LED",__func__);
return mCameraHandle->ops->prepare_snapshot(mCameraHandle->camera_handle,
afNeeded);
}
diff --git a/QCamera2/HAL/QCamera2HWI.h b/QCamera2/HAL/QCamera2HWI.h
index a1b217d..bc4db20 100644
--- a/QCamera2/HAL/QCamera2HWI.h
+++ b/QCamera2/HAL/QCamera2HWI.h
@@ -101,6 +101,9 @@
#define QCAMERA_DUMP_FRM_MASK_ALL 0x000000ff
+#define QCAMERA_ION_USE_CACHE true
+#define QCAMERA_ION_USE_NOCACHE false
+
typedef enum {
QCAMERA_NOTIFY_CALLBACK,
QCAMERA_DATA_CALLBACK,
@@ -272,6 +275,7 @@
int commitParameterChanges();
bool needDebugFps();
+ bool isCACEnabled();
bool needReprocess();
bool needRotationReprocess();
bool needOnlineRotation();
diff --git a/QCamera2/HAL/QCamera2HWICallbacks.cpp b/QCamera2/HAL/QCamera2HWICallbacks.cpp
index 40fffa6..ca534a4 100644
--- a/QCamera2/HAL/QCamera2HWICallbacks.cpp
+++ b/QCamera2/HAL/QCamera2HWICallbacks.cpp
@@ -684,6 +684,7 @@
__func__, pMetaData->faces_data.num_faces_detected);
} else {
// process face detection result
+ ALOGD("[KPI Perf] %s: Number of faces detected %d",__func__,pMetaData->faces_data.num_faces_detected);
pme->processFaceDetectionResult(&pMetaData->faces_data);
}
}
@@ -987,12 +988,9 @@
qcamera_callback_argm_t *arg = ( qcamera_callback_argm_t * ) data;
if ( ( NULL != arg ) && ( NULL != user_data ) ) {
-
if ( arg->release_cb ) {
arg->release_cb(arg->user_data, arg->cookie);
}
-
- delete arg;
}
}
diff --git a/QCamera2/HAL/QCameraMem.cpp b/QCamera2/HAL/QCameraMem.cpp
index 03da59f..2b846ae 100644
--- a/QCamera2/HAL/QCameraMem.cpp
+++ b/QCamera2/HAL/QCameraMem.cpp
@@ -53,11 +53,13 @@
*
* DESCRIPTION: default constructor of QCameraMemory
*
- * PARAMETERS : none
+ * PARAMETERS :
+ * @cached : flag indicates if using cached memory
*
* RETURN : None
*==========================================================================*/
-QCameraMemory::QCameraMemory()
+QCameraMemory::QCameraMemory(bool cached)
+ :m_bCached(cached)
{
mBufferCount = 0;
for (int i = 0; i < MM_CAMERA_MAX_NUM_FRAMES; i++) {
@@ -97,6 +99,12 @@
*==========================================================================*/
int QCameraMemory::cacheOpsInternal(int index, unsigned int cmd, void *vaddr)
{
+ if (!m_bCached) {
+ // Memory is not cached, no need for cache ops
+ ALOGV("%s: No cache ops here for uncached memory", __func__);
+ return OK;
+ }
+
struct ion_flush_data cache_inv_data;
struct ion_custom_data custom_data;
int ret = OK;
@@ -293,7 +301,7 @@
int main_ion_fd = 0;
main_ion_fd = open("/dev/ion", O_RDONLY);
- if (main_ion_fd <= 0) {
+ if (main_ion_fd < 0) {
ALOGE("Ion dev open failed: %s\n", strerror(errno));
goto ION_OPEN_FAILED;
}
@@ -303,7 +311,9 @@
/* to make it page size aligned */
alloc.len = (alloc.len + 4095) & (~4095);
alloc.align = 4096;
- alloc.flags = ION_FLAG_CACHED;
+ if (m_bCached) {
+ alloc.flags = ION_FLAG_CACHED;
+ }
alloc.heap_mask = heap_id;
rc = ioctl(main_ion_fd, ION_IOC_ALLOC, &alloc);
if (rc < 0) {
@@ -370,12 +380,13 @@
*
* DESCRIPTION: constructor of QCameraHeapMemory for ion memory used internally in HAL
*
- * PARAMETERS : none
+ * PARAMETERS :
+ * @cached : flag indicates if using cached memory
*
* RETURN : none
*==========================================================================*/
-QCameraHeapMemory::QCameraHeapMemory()
- : QCameraMemory()
+QCameraHeapMemory::QCameraHeapMemory(bool cached)
+ : QCameraMemory(cached)
{
for (int i = 0; i < MM_CAMERA_MAX_NUM_FRAMES; i ++)
mPtr[i] = NULL;
@@ -563,11 +574,14 @@
*
* PARAMETERS :
* @getMemory : camera memory request ops table
+ * @cached : flag indicates if using cached memory
*
* RETURN : none
*==========================================================================*/
-QCameraStreamMemory::QCameraStreamMemory(camera_request_memory getMemory) :
- mGetMemory(getMemory)
+QCameraStreamMemory::QCameraStreamMemory(camera_request_memory getMemory,
+ bool cached)
+ :QCameraMemory(cached),
+ mGetMemory(getMemory)
{
for (int i = 0; i < MM_CAMERA_MAX_NUM_FRAMES; i ++)
mCameraMemory[i] = NULL;
@@ -745,11 +759,13 @@
*
* PARAMETERS :
* @getMemory : camera memory request ops table
+ * @cached : flag indicates if using cached ION memory
*
* RETURN : none
*==========================================================================*/
-QCameraVideoMemory::QCameraVideoMemory(camera_request_memory getMemory)
- : QCameraStreamMemory(getMemory)
+QCameraVideoMemory::QCameraVideoMemory(camera_request_memory getMemory,
+ bool cached)
+ : QCameraStreamMemory(getMemory, cached)
{
memset(mMetadata, 0, sizeof(mMetadata));
}
@@ -894,7 +910,7 @@
* RETURN : none
*==========================================================================*/
QCameraGrallocMemory::QCameraGrallocMemory(camera_request_memory getMemory)
- : QCameraMemory()
+ : QCameraMemory(true)
{
mMinUndequeuedBuffers = 0;
mWindow = NULL;
@@ -1091,7 +1107,7 @@
mPrivateHandle[cnt] =
(struct private_handle_t *)(*mBufferHandle[cnt]);
mMemInfo[cnt].main_ion_fd = open("/dev/ion", O_RDONLY);
- if (mMemInfo[cnt].main_ion_fd <= 0) {
+ if (mMemInfo[cnt].main_ion_fd < 0) {
ALOGE("%s: failed: could not open ion device", __func__);
for(int i = 0; i < cnt; i++) {
struct ion_handle_data ion_handle;
diff --git a/QCamera2/HAL/QCameraMem.h b/QCamera2/HAL/QCameraMem.h
index 8fc7028..2890c9d 100644
--- a/QCamera2/HAL/QCameraMem.h
+++ b/QCamera2/HAL/QCameraMem.h
@@ -60,7 +60,7 @@
virtual int getMatchBufIndex(const void *opaque, bool metadata) const = 0;
virtual void *getPtr(int index) const= 0;
- QCameraMemory();
+ QCameraMemory(bool cached);
virtual ~QCameraMemory();
void getBufDef(const cam_frame_len_offset_t &offset,
@@ -80,6 +80,7 @@
void deallocOneBuffer(struct QCameraMemInfo &memInfo);
int cacheOpsInternal(int index, unsigned int cmd, void *vaddr);
+ bool m_bCached;
int mBufferCount;
struct QCameraMemInfo mMemInfo[MM_CAMERA_MAX_NUM_FRAMES];
};
@@ -88,7 +89,7 @@
// They are allocated from /dev/ion.
class QCameraHeapMemory : public QCameraMemory {
public:
- QCameraHeapMemory();
+ QCameraHeapMemory(bool cached);
virtual ~QCameraHeapMemory();
virtual int allocate(int count, int size);
@@ -107,7 +108,7 @@
// framework. They are allocated from /dev/ion or gralloc.
class QCameraStreamMemory : public QCameraMemory {
public:
- QCameraStreamMemory(camera_request_memory getMemory);
+ QCameraStreamMemory(camera_request_memory getMemory, bool cached);
virtual ~QCameraStreamMemory();
virtual int allocate(int count, int size);
@@ -127,7 +128,7 @@
// framework. They are allocated from /dev/ion or gralloc.
class QCameraVideoMemory : public QCameraStreamMemory {
public:
- QCameraVideoMemory(camera_request_memory getMemory);
+ QCameraVideoMemory(camera_request_memory getMemory, bool cached);
virtual ~QCameraVideoMemory();
virtual int allocate(int count, int size);
diff --git a/QCamera2/HAL/QCameraParameters.cpp b/QCamera2/HAL/QCameraParameters.cpp
index e4bf31a..72b44a6 100644
--- a/QCamera2/HAL/QCameraParameters.cpp
+++ b/QCamera2/HAL/QCameraParameters.cpp
@@ -816,6 +816,30 @@
}
/*===========================================================================
+ * FUNCTION : compareFPSValues
+ *
+ * DESCRIPTION: helper function for fps sorting
+ *
+ * PARAMETERS :
+ * @p1 : first array element
+ * @p2 : second array element
+ *
+ * RETURN : -1 - left element is greater than right
+ * 0 - elements are equals
+ * 1 - left element is less than right
+ *==========================================================================*/
+int QCameraParameters::compareFPSValues(const void *p1, const void *p2)
+{
+ if ( *( (int *) p1) > *( (int *) p2) ) {
+ return -1;
+ } else if ( *( (int *) p1) < *( (int *) p2) ) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*===========================================================================
* FUNCTION : createFpsString
*
* DESCRIPTION: create string obj contains array of FPS rates
@@ -830,13 +854,29 @@
{
String8 str;
char buffer[32];
+ int duplicate = INT_MAX;
+
+ int *fpsValues = new int[len];
for (int i = 0; i < len; i++ ) {
- snprintf(buffer, sizeof(buffer), "%d", int(fps[i].max_fps * 1000));
- str.append(buffer);
- if (i < len-1)
- str.append(",");
+ fpsValues[i] = int(fps[i].max_fps);
}
+
+ qsort(fpsValues, len, sizeof(int), compareFPSValues);
+
+ for (int i = 0; i < len; i++ ) {
+ if ( duplicate != fpsValues[i] ) {
+ snprintf(buffer, sizeof(buffer), "%d", fpsValues[i]);
+ str.append(buffer);
+ if (i < len-1) {
+ str.append(",");
+ }
+ duplicate = fpsValues[i];
+ }
+ }
+
+ delete [] fpsValues;
+
return str;
}
@@ -958,10 +998,7 @@
}
// set the new value
- char val[32];
- sprintf(val, "%dx%d", width, height);
- updateParamEntry(KEY_PREVIEW_SIZE, val);
- ALOGV("%s: %s", __func__, val);
+ CameraParameters::setPreviewSize(width, height);
return NO_ERROR;
}
}
@@ -1000,10 +1037,7 @@
}
// set the new value
- char val[32];
- sprintf(val, "%dx%d", width, height);
- updateParamEntry(KEY_PICTURE_SIZE, val);
- ALOGV("%s: %s", __func__, val);
+ CameraParameters::setPictureSize(width, height);
return NO_ERROR;
}
}
@@ -1050,10 +1084,7 @@
}
// set the new value
- char val[32];
- sprintf(val, "%dx%d", width, height);
- updateParamEntry(KEY_VIDEO_SIZE, val);
- ALOGV("%s: %s", __func__, val);
+ CameraParameters::setVideoSize(width, height);
return NO_ERROR;
}
}
@@ -1179,7 +1210,7 @@
if (previewFormat != NAME_NOT_FOUND) {
mPreviewFormat = (cam_format_t)previewFormat;
- updateParamEntry(KEY_PREVIEW_FORMAT, str);
+ CameraParameters::setPreviewFormat(str);
ALOGV("%s: format %d\n", __func__, mPreviewFormat);
return NO_ERROR;
}
@@ -1209,7 +1240,7 @@
if (pictureFormat != NAME_NOT_FOUND) {
mPictureFormat = pictureFormat;
- updateParamEntry(KEY_PICTURE_FORMAT, str);
+ CameraParameters::setPictureFormat(str);
ALOGE("%s: format %d\n", __func__, mPictureFormat);
return NO_ERROR;
}
@@ -1271,11 +1302,8 @@
}
}
- char val[16];
- sprintf(val, "%d", optimalWidth);
- updateParamEntry(KEY_JPEG_THUMBNAIL_WIDTH, val);
- sprintf(val, "%d", optimalHeight);
- updateParamEntry(KEY_JPEG_THUMBNAIL_HEIGHT, val);
+ set(KEY_JPEG_THUMBNAIL_WIDTH, optimalWidth);
+ set(KEY_JPEG_THUMBNAIL_HEIGHT, optimalHeight);
return NO_ERROR;
}
@@ -1304,9 +1332,7 @@
quality = params.getInt(KEY_JPEG_THUMBNAIL_QUALITY);
if (quality >= 0 && quality <= 100) {
- char val[16];
- sprintf(val, "%d", quality);
- updateParamEntry(KEY_JPEG_THUMBNAIL_QUALITY, val);
+ set(KEY_JPEG_THUMBNAIL_QUALITY, quality);
} else {
ALOGE("%s: Invalid jpeg thumbnail quality=%d", __func__, quality);
rc = BAD_VALUE;
@@ -1334,7 +1360,7 @@
if (strcmp(str, portrait) == 0 || strcmp(str, landscape) == 0) {
// Camera service needs this to decide if the preview frames and raw
// pictures should be rotated.
- updateParamEntry(KEY_QC_ORIENTATION, str);
+ set(KEY_QC_ORIENTATION, str);
} else {
ALOGE("%s: Invalid orientation value: %s", __func__, str);
return BAD_VALUE;
@@ -2477,7 +2503,7 @@
if(str_val && strlen(str_val) > 0) {
if (prev_str == NULL || strcmp(str_val, prev_str) != 0) {
m_bNoDisplayMode = atoi(str_val);
- updateParamEntry(KEY_QC_NO_DISPLAY_MODE, str_val);
+ set(KEY_QC_NO_DISPLAY_MODE, str_val);
m_bNeedRestart = true;
}
} else {
@@ -2510,7 +2536,7 @@
sizeof(ON_OFF_MODES_MAP)/sizeof(QCameraMap),
str_val);
if (value != NAME_NOT_FOUND) {
- updateParamEntry(KEY_QC_ZSL, str_val);
+ set(KEY_QC_ZSL, str_val);
m_bZslMode = (value > 0)? true : false;
// ZSL mode changed, need restart preview
@@ -2945,7 +2971,7 @@
String8 fpsValues = createFpsString(m_pCapability->fps_ranges_tbl,
m_pCapability->fps_ranges_tbl_cnt);
set(KEY_SUPPORTED_PREVIEW_FRAME_RATES, fpsValues.string());
- CameraParameters::setPreviewFrameRate(max_fps);
+ CameraParameters::setPreviewFrameRate(int(m_pCapability->fps_ranges_tbl[default_fps_index].max_fps));
} else {
ALOGE("%s: supported fps ranges cnt is 0 or exceeds max!!!", __func__);
}
@@ -3260,7 +3286,7 @@
m_pCamOpsTbl = mmOps;
//Allocate Set Param Buffer
- m_pParamHeap = new QCameraHeapMemory();
+ m_pParamHeap = new QCameraHeapMemory(QCAMERA_ION_USE_CACHE);
rc = m_pParamHeap->allocate(1, sizeof(parm_buffer_t));
if(rc != OK) {
rc = NO_MEMORY;
@@ -5568,6 +5594,8 @@
fd_set_parm.fd_mode = faceProcMask;
fd_set_parm.num_fd = requested_faces;
+ ALOGD("[KPI Perf] %s: Face detection value = %d num_fd = %d",
+ __func__, faceProcMask,requested_faces);
if(initBatchUpdate(m_pParamBuf) < 0 ) {
ALOGE("%s:Failed to initialize group update table", __func__);
return BAD_TYPE;
diff --git a/QCamera2/HAL/QCameraParameters.h b/QCamera2/HAL/QCameraParameters.h
index 711f9e6..3b6a9a6 100644
--- a/QCamera2/HAL/QCameraParameters.h
+++ b/QCamera2/HAL/QCameraParameters.h
@@ -520,6 +520,7 @@
String8 createFpsRangeString(const cam_fps_range_t *fps,
int len,
int &default_fps_index);
+ static int compareFPSValues(const void *p1, const void *p2);
String8 createFpsString(const cam_fps_range_t *fps, int len);
String8 createZoomRatioValuesString(int *zoomRatios, int length);
int lookupAttr(const QCameraMap arr[], int len, const char *name);
diff --git a/QCamera2/HAL/QCameraPostProc.cpp b/QCamera2/HAL/QCameraPostProc.cpp
index d77de9e..a2e6999 100644
--- a/QCamera2/HAL/QCameraPostProc.cpp
+++ b/QCamera2/HAL/QCameraPostProc.cpp
@@ -243,7 +243,7 @@
cam_dimension_t thumbnailSize;
memset(&thumbnailSize, 0, sizeof(cam_dimension_t));
m_parent->getThumbnailSize(thumbnailSize);
- if (thumbnailSize.width == 0 && thumbnailSize.height == 0) {
+ if (thumbnailSize.width == 0 || thumbnailSize.height == 0) {
// (0,0) means no thumbnail
m_bThumbnailNeeded = FALSE;
}
@@ -328,7 +328,8 @@
delete m_pJpegOutputMem;
m_pJpegOutputMem = NULL;
}
- m_pJpegOutputMem = new QCameraStreamMemory(m_parent->mGetMemory);
+ m_pJpegOutputMem = new QCameraStreamMemory(m_parent->mGetMemory,
+ QCAMERA_ION_USE_CACHE);
if (NULL == m_pJpegOutputMem) {
ret = NO_MEMORY;
ALOGE("%s : No memory for m_pJpegOutputMem", __func__);
@@ -633,6 +634,13 @@
return BAD_VALUE;
}
+ if (m_parent->mParameters.isNV16PictureFormat()) {
+ releaseSuperBuf(job->src_frame);
+ free(job->src_frame);
+ free(job);
+ return processRawData(frame);
+ }
+
qcamera_jpeg_data_t *jpeg_job =
(qcamera_jpeg_data_t *)malloc(sizeof(qcamera_jpeg_data_t));
if (jpeg_job == NULL) {
@@ -1349,13 +1357,14 @@
}
} else {
// not active, simply return buf and do no op
- mm_camera_super_buf_t *super_buf =
- (mm_camera_super_buf_t *)pme->m_inputJpegQ.dequeue();
- if (NULL != super_buf) {
- pme->releaseSuperBuf(super_buf);
- free(super_buf);
+ qcamera_jpeg_data_t *jpeg_data =
+ (qcamera_jpeg_data_t *)pme->m_inputJpegQ.dequeue();
+ if (NULL != jpeg_data) {
+ pme->releaseJpegJobData(jpeg_data);
+ free(jpeg_data);
}
- super_buf = (mm_camera_super_buf_t *)pme->m_inputRawQ.dequeue();
+ mm_camera_super_buf_t *super_buf =
+ (mm_camera_super_buf_t *)pme->m_inputRawQ.dequeue();
if (NULL != super_buf) {
pme->releaseSuperBuf(super_buf);
free(super_buf);
diff --git a/QCamera2/HAL/QCameraThermalAdapter.cpp b/QCamera2/HAL/QCameraThermalAdapter.cpp
index 16d8a42..abdae3e 100644
--- a/QCamera2/HAL/QCameraThermalAdapter.cpp
+++ b/QCamera2/HAL/QCameraThermalAdapter.cpp
@@ -63,7 +63,7 @@
int rc = NO_ERROR;
ALOGV("%s E", __func__);
- mHandle = dlopen("/system/lib/libthermalclient.so", RTLD_NOW);
+ mHandle = dlopen("/vendor/lib/libthermalclient.so", RTLD_NOW);
if (!mHandle) {
error = dlerror();
ALOGE("%s: dlopen failed with error %s",
diff --git a/QCamera2/stack/common/cam_intf.h b/QCamera2/stack/common/cam_intf.h
index d3f50b1..ba555e1 100644
--- a/QCamera2/stack/common/cam_intf.h
+++ b/QCamera2/stack/common/cam_intf.h
@@ -174,6 +174,8 @@
uint8_t meta_present; /* if there is meta data associated with this reprocess frame */
uint32_t meta_stream_handle; /* meta data stream ID. only valid if meta_present != 0 */
uint8_t meta_buf_index; /* buf index to meta data buffer. only valid if meta_present != 0 */
+
+ cam_per_frame_pp_config_t frame_pp_config; /* per frame post-proc configuration */
} cam_reprocess_param;
typedef struct {
diff --git a/QCamera2/stack/common/cam_types.h b/QCamera2/stack/common/cam_types.h
index 0d3388f..dba418f 100644
--- a/QCamera2/stack/common/cam_types.h
+++ b/QCamera2/stack/common/cam_types.h
@@ -801,4 +801,16 @@
cam_pp_feature_config_t pp_feature_config;
} cam_stream_reproc_config_t;
+typedef struct {
+ uint8_t crop_enabled;
+ cam_rect_t input_crop;
+} cam_crop_param_t;
+
+typedef struct {
+ cam_denoise_param_t denoise;
+ cam_crop_param_t crop;
+ uint32_t flip; /* 0 means no flip */
+ int32_t sharpness; /* 0 means no sharpness */
+} cam_per_frame_pp_config_t;
+
#endif /* __QCAMERA_TYPES_H__ */
diff --git a/QCamera2/stack/common/mm_camera_interface.h b/QCamera2/stack/common/mm_camera_interface.h
index 7ccc130..eb8a458 100644
--- a/QCamera2/stack/common/mm_camera_interface.h
+++ b/QCamera2/stack/common/mm_camera_interface.h
@@ -608,6 +608,18 @@
**/
int32_t (*flush_super_buf_queue) (uint32_t camera_handle,
uint32_t ch_id, uint32_t frame_idx);
+
+ /** configure_notify_mode: function definition for configuring the
+ * notification mode of channel
+ * @camera_handle : camera handler
+ * @ch_id : channel handler
+ * @notify_mode : notification mode
+ * Return value: 0 -- success
+ * -1 -- failure
+ **/
+ int32_t (*configure_notify_mode) (uint32_t camera_handle,
+ uint32_t ch_id,
+ mm_camera_super_buf_notify_mode_t notify_mode);
} mm_camera_ops_t;
/** mm_camera_vtbl_t: virtual table for camera operations
diff --git a/QCamera2/stack/mm-camera-interface/inc/mm_camera.h b/QCamera2/stack/mm-camera-interface/inc/mm_camera.h
index 4b535e5..02f4e34 100644
--- a/QCamera2/stack/mm-camera-interface/inc/mm_camera.h
+++ b/QCamera2/stack/mm-camera-interface/inc/mm_camera.h
@@ -67,6 +67,7 @@
MM_CAMERA_CMD_TYPE_EXIT, /* EXIT */
MM_CAMERA_CMD_TYPE_REQ_DATA_CB,/* request data */
MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB, /* superbuf dataB CMD */
+ MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY, /* configure notify mode */
MM_CAMERA_CMD_TYPE_FLUSH_QUEUE, /* flush queue */
MM_CAMERA_CMD_TYPE_MAX
} mm_camera_cmdcb_type_t;
@@ -89,6 +90,7 @@
mm_camera_super_buf_t superbuf; /* superbuf if superbuf dataCB*/
mm_camera_req_buf_t req_buf; /* num of buf requested */
uint32_t frame_idx; /* frame idx boundary for flush superbuf queue*/
+ mm_camera_super_buf_notify_mode_t notify_mode; /* notification mode */
} u;
} mm_camera_cmdcb_t;
@@ -245,6 +247,7 @@
MM_CHANNEL_EVT_REQUEST_SUPER_BUF,
MM_CHANNEL_EVT_CANCEL_REQUEST_SUPER_BUF,
MM_CHANNEL_EVT_FLUSH_SUPER_BUF_QUEUE,
+ MM_CHANNEL_EVT_CONFIG_NOTIFY_MODE,
MM_CHANNEL_EVT_MAP_STREAM_BUF,
MM_CHANNEL_EVT_UNMAP_STREAM_BUF,
MM_CHANNEL_EVT_SET_STREAM_PARM,
@@ -375,6 +378,8 @@
pthread_mutex_t evt_lock;
pthread_cond_t evt_cond;
mm_camera_event_t evt_rcvd;
+
+ pthread_mutex_t msg_lock; /* lock for sending msg through socket */
} mm_camera_obj_t;
typedef struct {
@@ -461,6 +466,9 @@
extern int32_t mm_camera_flush_super_buf_queue(mm_camera_obj_t *my_obj,
uint32_t ch_id,
uint32_t frame_idx);
+extern int32_t mm_camera_config_channel_notify(mm_camera_obj_t *my_obj,
+ uint32_t ch_id,
+ mm_camera_super_buf_notify_mode_t notify_mode);
extern int32_t mm_camera_set_stream_parms(mm_camera_obj_t *my_obj,
uint32_t ch_id,
uint32_t s_id,
diff --git a/QCamera2/stack/mm-camera-interface/src/mm_camera.c b/QCamera2/stack/mm-camera-interface/src/mm_camera.c
index 38c7197..deac8b8 100644
--- a/QCamera2/stack/mm-camera-interface/src/mm_camera.c
+++ b/QCamera2/stack/mm-camera-interface/src/mm_camera.c
@@ -289,6 +289,7 @@
rc = -1;
goto on_error;
}
+ pthread_mutex_init(&my_obj->msg_lock, NULL);
pthread_mutex_init(&my_obj->cb_lock, NULL);
pthread_mutex_init(&my_obj->evt_lock, NULL);
@@ -359,6 +360,7 @@
mm_camera_socket_close(my_obj->ds_fd);
my_obj->ds_fd = 0;
}
+ pthread_mutex_destroy(&my_obj->msg_lock);
pthread_mutex_destroy(&my_obj->cb_lock);
pthread_mutex_destroy(&my_obj->evt_lock);
@@ -1102,6 +1104,43 @@
}
/*===========================================================================
+ * FUNCTION : mm_camera_config_channel_notify
+ *
+ * DESCRIPTION: configures the channel notification mode
+ *
+ * PARAMETERS :
+ * @my_obj : camera object
+ * @ch_id : channel handle
+ * @notify_mode : notification mode
+ *
+ * RETURN : int32_t type of status
+ * 0 -- success
+ * -1 -- failure
+ *==========================================================================*/
+int32_t mm_camera_config_channel_notify(mm_camera_obj_t *my_obj,
+ uint32_t ch_id,
+ mm_camera_super_buf_notify_mode_t notify_mode)
+{
+ int32_t rc = -1;
+ mm_channel_t * ch_obj =
+ mm_camera_util_get_channel_by_handler(my_obj, ch_id);
+
+ if (NULL != ch_obj) {
+ pthread_mutex_lock(&ch_obj->ch_lock);
+ pthread_mutex_unlock(&my_obj->cam_lock);
+
+ rc = mm_channel_fsm_fn(ch_obj,
+ MM_CHANNEL_EVT_CONFIG_NOTIFY_MODE,
+ (void *)notify_mode,
+ NULL);
+ } else {
+ pthread_mutex_unlock(&my_obj->cam_lock);
+ }
+
+ return rc;
+}
+
+/*===========================================================================
* FUNCTION : mm_camera_set_stream_parms
*
* DESCRIPTION: set parameters per stream
@@ -1460,6 +1499,9 @@
{
int32_t rc = -1;
int32_t status;
+
+ /* need to lock msg_lock, since sendmsg until reposonse back is deemed as one operation*/
+ pthread_mutex_lock(&my_obj->msg_lock);
if(mm_camera_socket_sendmsg(my_obj->ds_fd, msg, buf_size, sendfd) > 0) {
/* wait for event that mapping/unmapping is done */
mm_camera_util_wait_for_event(my_obj, CAM_EVENT_TYPE_MAP_UNMAP_DONE, &status);
@@ -1467,6 +1509,7 @@
rc = 0;
}
}
+ pthread_mutex_unlock(&my_obj->msg_lock);
return rc;
}
diff --git a/QCamera2/stack/mm-camera-interface/src/mm_camera_channel.c b/QCamera2/stack/mm-camera-interface/src/mm_camera_channel.c
index 58a9254..9aa117a 100644
--- a/QCamera2/stack/mm-camera-interface/src/mm_camera_channel.c
+++ b/QCamera2/stack/mm-camera-interface/src/mm_camera_channel.c
@@ -67,6 +67,8 @@
int32_t mm_channel_cancel_super_buf_request(mm_channel_t *my_obj);
int32_t mm_channel_flush_super_buf_queue(mm_channel_t *my_obj,
uint32_t frame_idx);
+int32_t mm_channel_config_notify_mode(mm_channel_t *my_obj,
+ mm_camera_super_buf_notify_mode_t notify_mode);
int32_t mm_channel_superbuf_flush(mm_channel_t* my_obj, mm_channel_queue_t * queue);
int32_t mm_channel_set_stream_parm(mm_channel_t *my_obj,
mm_evt_paylod_set_get_stream_parms_t *payload);
@@ -203,12 +205,13 @@
/* skip frames if needed */
ch_obj->pending_cnt = cmd_cb->u.req_buf.num_buf_requested;
mm_channel_superbuf_skip(ch_obj, &ch_obj->bundle.superbuf_queue);
+ } else if (MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY == cmd_cb->cmd_type) {
+ ch_obj->bundle.superbuf_queue.attr.notify_mode = cmd_cb->u.notify_mode;
} else if (MM_CAMERA_CMD_TYPE_FLUSH_QUEUE == cmd_cb->cmd_type) {
ch_obj->bundle.superbuf_queue.expected_frame_id = cmd_cb->u.frame_idx;
mm_channel_superbuf_flush(ch_obj, &ch_obj->bundle.superbuf_queue);
return;
}
-
notify_mode = ch_obj->bundle.superbuf_queue.attr.notify_mode;
/* bufdone for overflowed bufs */
@@ -518,6 +521,12 @@
rc = mm_channel_flush_super_buf_queue(my_obj, frame_idx);
}
break;
+ case MM_CHANNEL_EVT_CONFIG_NOTIFY_MODE:
+ {
+ mm_camera_super_buf_notify_mode_t notify_mode = ( mm_camera_super_buf_notify_mode_t ) in_val;
+ rc = mm_channel_config_notify_mode(my_obj, notify_mode);
+ }
+ break;
case MM_CHANNEL_EVT_SET_STREAM_PARM:
{
mm_evt_paylod_set_get_stream_parms_t *payload =
@@ -1154,6 +1163,44 @@
}
/*===========================================================================
+ * FUNCTION : mm_channel_config_notify_mode
+ *
+ * DESCRIPTION: configure notification mode
+ *
+ * PARAMETERS :
+ * @my_obj : channel object
+ * @notify_mode : notification mode
+ *
+ * RETURN : int32_t type of status
+ * 0 -- success
+ * -1 -- failure
+ *==========================================================================*/
+int32_t mm_channel_config_notify_mode(mm_channel_t *my_obj,
+ mm_camera_super_buf_notify_mode_t notify_mode)
+{
+ int32_t rc = 0;
+ mm_camera_cmdcb_t* node = NULL;
+
+ node = (mm_camera_cmdcb_t *)malloc(sizeof(mm_camera_cmdcb_t));
+ if (NULL != node) {
+ memset(node, 0, sizeof(mm_camera_cmdcb_t));
+ node->u.notify_mode = notify_mode;
+ node->cmd_type = MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY;
+
+ /* enqueue to cmd thread */
+ cam_queue_enq(&(my_obj->cmd_thread.cmd_queue), node);
+
+ /* wake up cmd thread */
+ cam_sem_post(&(my_obj->cmd_thread.cmd_sem));
+ } else {
+ CDBG_ERROR("%s: No memory for mm_camera_node_t", __func__);
+ rc = -1;
+ }
+
+ return rc;
+}
+
+/*===========================================================================
* FUNCTION : mm_channel_qbuf
*
* DESCRIPTION: enqueue buffer back to kernel
diff --git a/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c b/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c
index 3919fc4..f043bfb 100644
--- a/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c
+++ b/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c
@@ -923,6 +923,43 @@
}
/*===========================================================================
+ * FUNCTION : mm_camera_intf_configure_notify_mode
+ *
+ * DESCRIPTION: Configures channel notification mode
+ *
+ * PARAMETERS :
+ * @camera_handle: camera handle
+ * @ch_id : channel handle
+ * @notify_mode : notification mode
+ *
+ * RETURN : int32_t type of status
+ * 0 -- success
+ * -1 -- failure
+ *==========================================================================*/
+static int32_t mm_camera_intf_configure_notify_mode(uint32_t camera_handle,
+ uint32_t ch_id,
+ mm_camera_super_buf_notify_mode_t notify_mode)
+{
+ int32_t rc = -1;
+ mm_camera_obj_t * my_obj = NULL;
+
+ CDBG("%s :E camera_handler = %d,ch_id = %d",
+ __func__, camera_handle, ch_id);
+ pthread_mutex_lock(&g_intf_lock);
+ my_obj = mm_camera_util_get_camera_by_handler(camera_handle);
+
+ if(my_obj) {
+ pthread_mutex_lock(&my_obj->cam_lock);
+ pthread_mutex_unlock(&g_intf_lock);
+ rc = mm_camera_config_channel_notify(my_obj, ch_id, notify_mode);
+ } else {
+ pthread_mutex_unlock(&g_intf_lock);
+ }
+ CDBG("%s :X rc = %d", __func__, rc);
+ return rc;
+}
+
+/*===========================================================================
* FUNCTION : mm_camera_intf_map_buf
*
* DESCRIPTION: mapping camera buffer via domain socket to server
@@ -1304,7 +1341,8 @@
.stop_channel = mm_camera_intf_stop_channel,
.request_super_buf = mm_camera_intf_request_super_buf,
.cancel_super_buf_request = mm_camera_intf_cancel_super_buf_request,
- .flush_super_buf_queue = mm_camera_intf_flush_super_buf_queue
+ .flush_super_buf_queue = mm_camera_intf_flush_super_buf_queue,
+ .configure_notify_mode = mm_camera_intf_configure_notify_mode
};
/*===========================================================================
diff --git a/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c b/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c
index 42ee99f..91c61bc 100644
--- a/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c
+++ b/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c
@@ -2281,8 +2281,10 @@
{
int32_t rc = 0;
if (stream_info->reprocess_config.pp_type == CAM_OFFLINE_REPROCESS_TYPE) {
- // take offset from input source
- *buf_planes = stream_info->reprocess_config.offline.input_buf_planes;
+ if (buf_planes->plane_info.frame_len == 0) {
+ // take offset from input source
+ *buf_planes = stream_info->reprocess_config.offline.input_buf_planes;
+ }
return rc;
}
diff --git a/QCamera2/stack/mm-camera-interface/src/mm_camera_thread.c b/QCamera2/stack/mm-camera-interface/src/mm_camera_thread.c
index d889b05..3e691ca 100755
--- a/QCamera2/stack/mm-camera-interface/src/mm_camera_thread.c
+++ b/QCamera2/stack/mm-camera-interface/src/mm_camera_thread.c
@@ -92,6 +92,9 @@
len = write(poll_cb->pfds[1], &cmd_evt, sizeof(cmd_evt));
if(len < 1) {
CDBG_ERROR("%s: len = %d, errno = %d", __func__, len, errno);
+ /* Avoid waiting for the signal */
+ pthread_mutex_unlock(&poll_cb->mutex);
+ return 0;
}
CDBG("%s: begin IN mutex write done, len = %d", __func__, len);
/* wait till worker task gives positive signal */
@@ -482,6 +485,7 @@
case MM_CAMERA_CMD_TYPE_DATA_CB:
case MM_CAMERA_CMD_TYPE_REQ_DATA_CB:
case MM_CAMERA_CMD_TYPE_SUPER_BUF_DATA_CB:
+ case MM_CAMERA_CMD_TYPE_CONFIG_NOTIFY:
case MM_CAMERA_CMD_TYPE_FLUSH_QUEUE:
if (NULL != cmd_thread->cb) {
cmd_thread->cb(node, cmd_thread->user_data);
diff --git a/QCamera2/stack/mm-jpeg-interface/inc/mm_jpeg.h b/QCamera2/stack/mm-jpeg-interface/inc/mm_jpeg.h
index 2496cfe..593c7d4 100644
--- a/QCamera2/stack/mm-jpeg-interface/inc/mm_jpeg.h
+++ b/QCamera2/stack/mm-jpeg-interface/inc/mm_jpeg.h
@@ -42,7 +42,7 @@
#define MM_JPEG_MAX_THREADS 30
#define MM_JPEG_CIRQ_SIZE 30
#define MM_JPEG_MAX_SESSION 10
-#define MAX_EXIF_TABLE_ENTRIES 30
+#define MAX_EXIF_TABLE_ENTRIES 50
typedef struct {
struct cam_list list;
diff --git a/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg.c b/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg.c
index c13b51d..91ec30c 100644
--- a/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg.c
+++ b/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg.c
@@ -698,13 +698,24 @@
return ret;
}
- // Enable thumbnail port
- ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandPortEnable,
- p_session->inputTmbPort.nPortIndex, NULL);
+ if (p_session->params.encode_thumbnail) {
+ // Enable thumbnail port
+ ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandPortEnable,
+ p_session->inputTmbPort.nPortIndex, NULL);
- if (ret) {
- CDBG_ERROR("%s:%d] failed", __func__, __LINE__);
- return ret;
+ if (ret) {
+ CDBG_ERROR("%s:%d] failed", __func__, __LINE__);
+ return ret;
+ }
+ } else {
+ // Disable thumbnail port
+ ret = OMX_SendCommand(p_session->omx_handle, OMX_CommandPortDisable,
+ p_session->inputTmbPort.nPortIndex, NULL);
+
+ if (ret) {
+ CDBG_ERROR("%s:%d] failed", __func__, __LINE__);
+ return ret;
+ }
}
p_session->outputPort.nBufferSize =
@@ -1013,6 +1024,7 @@
OMX_CONFIG_ROTATIONTYPE rotate;
mm_jpeg_encode_params_t *p_params = &p_session->params;
mm_jpeg_encode_job_t *p_jobparams = &p_session->encode_job;
+ QOMX_EXIF_INFO exif_info;
/* set rotation */
memset(&rotate, 0, sizeof(rotate));
@@ -1028,22 +1040,22 @@
(int)p_jobparams->rotation, (int)rotate.nPortIndex);
/* Set Exif data*/
- memset(&p_session->exif_info_all, 0, sizeof(p_session->exif_info_all));
+ memset(&p_session->exif_info_all[0], 0, sizeof(p_session->exif_info_all));
+ exif_info.numOfEntries = p_params->exif_info.numOfEntries;
+ exif_info.exif_data = &p_session->exif_info_all[0];
/*If Exif data has been passed copy it*/
if (p_params->exif_info.numOfEntries > 0) {
CDBG("%s:%d] Num of exif entries passed from HAL: %d", __func__, __LINE__,
p_params->exif_info.numOfEntries);
- memcpy(&p_session->exif_info_all, &p_params->exif_info.exif_data,
- sizeof(p_params->exif_info.exif_data));
+ memcpy(exif_info.exif_data, p_params->exif_info.exif_data,
+ sizeof(QEXIF_INFO_DATA) * p_params->exif_info.numOfEntries);
}
- p_params->exif_info.exif_data = p_session->exif_info_all;
-
- if (p_params->exif_info.numOfEntries > 0) {
+ if (exif_info.numOfEntries > 0) {
/* set exif tags */
CDBG("%s:%d] Set exif tags count %d", __func__, __LINE__,
- (int)p_params->exif_info.numOfEntries);
+ (int)exif_info.numOfEntries);
rc = OMX_GetExtensionIndex(p_session->omx_handle, QOMX_IMAGE_EXT_EXIF_NAME,
&exif_idx);
if (OMX_ErrorNone != rc) {
@@ -1052,7 +1064,7 @@
}
rc = OMX_SetParameter(p_session->omx_handle, exif_idx,
- &p_params->exif_info);
+ &exif_info);
if (OMX_ErrorNone != rc) {
CDBG_ERROR("%s:%d] Error %d", __func__, __LINE__, rc);
return rc;
@@ -1345,11 +1357,13 @@
goto error;
}
- ret = OMX_EmptyThisBuffer(p_session->omx_handle,
- p_session->p_in_omx_thumb_buf[p_jobparams->thumb_index]);
- if (ret) {
- CDBG_ERROR("%s:%d] Error", __func__, __LINE__);
- goto error;
+ if (p_session->params.encode_thumbnail) {
+ ret = OMX_EmptyThisBuffer(p_session->omx_handle,
+ p_session->p_in_omx_thumb_buf[p_jobparams->thumb_index]);
+ if (ret) {
+ CDBG_ERROR("%s:%d] Error", __func__, __LINE__);
+ goto error;
+ }
}
ret = OMX_FillThisBuffer(p_session->omx_handle,
diff --git a/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg_exif.c b/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg_exif.c
index 2ab2b4e..016c9ae 100644
--- a/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg_exif.c
+++ b/QCamera2/stack/mm-jpeg-interface/src/mm_jpeg_exif.c
@@ -266,5 +266,3 @@
return 0;
}
-
-