/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
/*
* Contains implementation of a class CallbackNotifier that manages callbacks
* set via set_callbacks, enable_msg_type, and disable_msg_type camera HAL API.
*/
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_CallbackNotifier"
#include "CallbackNotifier.h"
#include <MetadataBufferType.h>
#include <cutils/log.h>
#include "EmulatedCameraDevice.h"
#include "JpegCompressor.h"
#include "Exif.h"
#include "Thumbnail.h"
namespace android {
/* String representation of camera messages. */
static const char* lCameraMessages[] = {"CAMERA_MSG_ERROR",
"CAMERA_MSG_SHUTTER",
"CAMERA_MSG_FOCUS",
"CAMERA_MSG_ZOOM",
"CAMERA_MSG_PREVIEW_FRAME",
"CAMERA_MSG_VIDEO_FRAME",
"CAMERA_MSG_POSTVIEW_FRAME",
"CAMERA_MSG_RAW_IMAGE",
"CAMERA_MSG_COMPRESSED_IMAGE",
"CAMERA_MSG_RAW_IMAGE_NOTIFY",
"CAMERA_MSG_PREVIEW_METADATA"};
static const int lCameraMessagesNum = sizeof(lCameraMessages) / sizeof(char*);
/* Builds an array of strings for the given set of messages.
* Param:
* msg - Messages to get strings for,
* strings - Array where to save strings
* max - Maximum number of entries in the array.
* Return:
* Number of strings saved into the 'strings' array.
*/
static int GetMessageStrings(uint32_t msg, const char** strings, int max) {
int index = 0;
int out = 0;
while (msg != 0 && out < max && index < lCameraMessagesNum) {
while ((msg & 0x1) == 0 && index < lCameraMessagesNum) {
msg >>= 1;
index++;
}
if ((msg & 0x1) != 0 && index < lCameraMessagesNum) {
strings[out] = lCameraMessages[index];
out++;
msg >>= 1;
index++;
}
}
return out;
}
/* Logs messages, enabled by the mask. */
static void PrintMessages(uint32_t msg) {
const char* strs[lCameraMessagesNum];
const int translated = GetMessageStrings(msg, strs, lCameraMessagesNum);
for (int n = 0; n < translated; n++) {
ALOGV(" %s", strs[n]);
}
}
CallbackNotifier::CallbackNotifier()
: mNotifyCB(NULL),
mDataCB(NULL),
mDataCBTimestamp(NULL),
mGetMemoryCB(NULL),
mCBOpaque(NULL),
mLastFrameTimestamp(0),
mFrameRefreshFreq(0),
mMessageEnabler(0),
mJpegQuality(90),
mVideoRecEnabled(false),
mTakingPicture(false) {}
CallbackNotifier::~CallbackNotifier() {}
/****************************************************************************
* Camera API
***************************************************************************/
void CallbackNotifier::setCallbacks(
camera_notify_callback notify_cb, camera_data_callback data_cb,
camera_data_timestamp_callback data_cb_timestamp,
camera_request_memory get_memory, void* user) {
ALOGV("%s: %p, %p, %p, %p (%p)", __FUNCTION__, notify_cb, data_cb,
data_cb_timestamp, get_memory, user);
Mutex::Autolock locker(&mObjectLock);
mNotifyCB = notify_cb;
mDataCB = data_cb;
mDataCBTimestamp = data_cb_timestamp;
mGetMemoryCB = get_memory;
mCBOpaque = user;
}
void CallbackNotifier::enableMessage(uint msg_type) {
ALOGV("%s: msg_type = 0x%x", __FUNCTION__, msg_type);
PrintMessages(msg_type);
Mutex::Autolock locker(&mObjectLock);
mMessageEnabler |= msg_type;
ALOGV("**** Currently enabled messages:");
PrintMessages(mMessageEnabler);
}
void CallbackNotifier::disableMessage(uint msg_type) {
ALOGV("%s: msg_type = 0x%x", __FUNCTION__, msg_type);
PrintMessages(msg_type);
Mutex::Autolock locker(&mObjectLock);
mMessageEnabler &= ~msg_type;
ALOGV("**** Currently enabled messages:");
PrintMessages(mMessageEnabler);
}
status_t CallbackNotifier::enableVideoRecording(int fps) {
ALOGV("%s: FPS = %d", __FUNCTION__, fps);
Mutex::Autolock locker(&mObjectLock);
mVideoRecEnabled = true;
mLastFrameTimestamp = 0;
mFrameRefreshFreq = 1000000000LL / fps;
return NO_ERROR;
}
void CallbackNotifier::disableVideoRecording() {
ALOGV("%s:", __FUNCTION__);
Mutex::Autolock locker(&mObjectLock);
mVideoRecEnabled = false;
mLastFrameTimestamp = 0;
mFrameRefreshFreq = 0;
}
void CallbackNotifier::releaseRecordingFrame(const void* opaque) {
List<camera_memory_t*>::iterator it = mCameraMemoryTs.begin();
for (; it != mCameraMemoryTs.end(); ++it) {
if ((*it)->data == opaque) {
(*it)->release(*it);
mCameraMemoryTs.erase(it);
break;
}
}
}
status_t CallbackNotifier::storeMetaDataInBuffers(bool enable) {
// Return error if metadata is request, otherwise silently agree.
return enable ? INVALID_OPERATION : NO_ERROR;
}
/****************************************************************************
* Public API
***************************************************************************/
void CallbackNotifier::cleanupCBNotifier() {
Mutex::Autolock locker(&mObjectLock);
mMessageEnabler = 0;
mNotifyCB = NULL;
mDataCB = NULL;
mDataCBTimestamp = NULL;
mGetMemoryCB = NULL;
mCBOpaque = NULL;
mLastFrameTimestamp = 0;
mFrameRefreshFreq = 0;
mJpegQuality = 90;
mVideoRecEnabled = false;
mTakingPicture = false;
}
void CallbackNotifier::onNextFrameAvailable(const void* frame,
nsecs_t timestamp,
EmulatedCameraDevice* camera_dev) {
if (isMessageEnabled(CAMERA_MSG_VIDEO_FRAME) && isVideoRecordingEnabled() &&
isNewVideoFrameTime(timestamp)) {
camera_memory_t* cam_buff =
mGetMemoryCB(-1, camera_dev->getFrameBufferSize(), 1, mCBOpaque);
if (NULL != cam_buff && NULL != cam_buff->data) {
memcpy(cam_buff->data, frame, camera_dev->getFrameBufferSize());
mDataCBTimestamp(timestamp, CAMERA_MSG_VIDEO_FRAME, cam_buff, 0,
mCBOpaque);
mCameraMemoryTs.push_back(cam_buff);
} else {
ALOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
}
}
if (isMessageEnabled(CAMERA_MSG_PREVIEW_FRAME)) {
camera_memory_t* cam_buff =
mGetMemoryCB(-1, camera_dev->getFrameBufferSize(), 1, mCBOpaque);
if (NULL != cam_buff && NULL != cam_buff->data) {
memcpy(cam_buff->data, frame, camera_dev->getFrameBufferSize());
mDataCB(CAMERA_MSG_PREVIEW_FRAME, cam_buff, 0, NULL, mCBOpaque);
cam_buff->release(cam_buff);
} else {
ALOGE("%s: Memory failure in CAMERA_MSG_PREVIEW_FRAME", __FUNCTION__);
}
}
if (mTakingPicture) {
/* This happens just once. */
mTakingPicture = false;
/* The sequence of callbacks during picture taking is:
* - CAMERA_MSG_SHUTTER
* - CAMERA_MSG_RAW_IMAGE_NOTIFY
* - CAMERA_MSG_COMPRESSED_IMAGE
*/
if (isMessageEnabled(CAMERA_MSG_SHUTTER)) {
mNotifyCB(CAMERA_MSG_SHUTTER, 0, 0, mCBOpaque);
}
if (isMessageEnabled(CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
mNotifyCB(CAMERA_MSG_RAW_IMAGE_NOTIFY, 0, 0, mCBOpaque);
}
if (isMessageEnabled(CAMERA_MSG_COMPRESSED_IMAGE)) {
/* Compress the frame to JPEG. Note that when taking pictures, we
* have requested camera device to provide us with NV21 frames. */
NV21JpegCompressor compressor;
const CameraParameters* cameraParameters = camera_dev->getCameraParameters();
if (cameraParameters == nullptr) {
ALOGE("%s: Could not get camera parameters to take picture.", __FUNCTION__);
return;
}
ExifData* exifData = createExifData(*cameraParameters);
// Create a thumbnail and place the pointer and size in the EXIF
// data structure. This transfers ownership to the EXIF data and
// the memory will be deallocated in the freeExifData call below.
int width = camera_dev->getFrameWidth();
int height = camera_dev->getFrameHeight();
int thumbWidth = cameraParameters->getInt(
CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
int thumbHeight = cameraParameters->getInt(
CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
if (thumbWidth > 0 && thumbHeight > 0) {
if (!createThumbnail(static_cast<const unsigned char*>(frame),
width, height, thumbWidth, thumbHeight,
mJpegQuality, exifData)) {
// Not really a fatal error, we'll just keep going
ALOGE("%s: Failed to create thumbnail for image",
__FUNCTION__);
}
}
status_t res = compressor.compressRawImage(frame, exifData, mJpegQuality, width, height);
if (res == NO_ERROR) {
camera_memory_t* jpeg_buff =
mGetMemoryCB(-1, compressor.getCompressedSize(), 1, mCBOpaque);
if (NULL != jpeg_buff && NULL != jpeg_buff->data) {
compressor.getCompressedImage(jpeg_buff->data);
mDataCB(CAMERA_MSG_COMPRESSED_IMAGE, jpeg_buff, 0, NULL, mCBOpaque);
jpeg_buff->release(jpeg_buff);
} else {
ALOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
}
} else {
ALOGE("%s: Compression failure in CAMERA_MSG_VIDEO_FRAME",
__FUNCTION__);
}
freeExifData(exifData);
}
}
}
void CallbackNotifier::onCameraDeviceError(int err) {
if (isMessageEnabled(CAMERA_MSG_ERROR) && mNotifyCB != NULL) {
mNotifyCB(CAMERA_MSG_ERROR, err, 0, mCBOpaque);
}
}
void CallbackNotifier::onCameraFocusAcquired() {
if (isMessageEnabled(CAMERA_MSG_FOCUS) && mNotifyCB != NULL) {
mNotifyCB(CAMERA_MSG_FOCUS, 1, 0, mCBOpaque);
}
}
/****************************************************************************
* Private API
***************************************************************************/
bool CallbackNotifier::isNewVideoFrameTime(nsecs_t timestamp) {
Mutex::Autolock locker(&mObjectLock);
if ((timestamp - mLastFrameTimestamp) >= mFrameRefreshFreq) {
mLastFrameTimestamp = timestamp;
return true;
}
return false;
}
}; /* namespace android */