Camera1 源码解析系列—— Camera1 takePicture() 流程解析
Posted ByteSaid
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Camera1 源码解析系列—— Camera1 takePicture() 流程解析相关的知识,希望对你有一定的参考价值。
前言
前面几篇笔记已经把 Camera
控制流的部分梳理得比较清楚了。在 Camera
流程中,还有一个重要的部分,即数据流。
Camera API 1
中,数据流主要是通过函数回调的方式,依照从下往上的方向,逐层 return
到 Applications
中。
本篇将数据流与 Camera
的控制流结合起来,从 takePicture()
方法切入,追踪一个比较完整的 Camera
流程。
1 Open 流程
Camera Open
的流程,在之前的一篇文章中已经比较详细地描述了。在这里,再关注一下这个流程中 Libraries
和 HAL
层的部分。
1.1 CameraService.cpp
- 路径:
frameworks/av/services/camera/libcameraservice/CameraService.cpp
connect()
:- 注意这里真正实现逻辑是在
connectHelper()
函数中。
- 注意这里真正实现逻辑是在
connectHelper()
:- 这个函数实现比较长,截取其中的一段。
- 首先,如果客户端实例已经存在于
MediaRecorder
,则直接将其取出返回。 - 若不存在,则先获取
deviceVersion
,然后再调用makeClient()
函数创建一个客户端。
template<class CALLBACK, class CLIENT>
Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
int api1CameraId, int halVersion, const String16& clientPackageName, int clientUid,
int clientPid, apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
/*out*/sp<CLIENT>& device)
binder::Status ret = binder::Status::ok();
...
sp<BasicClient> tmp = nullptr;
if(!(ret = makeClient(this, cameraCb, clientPackageName,
cameraId, api1CameraId, facing,
clientPid, clientUid, getpid(), legacyMode,
halVersion, deviceVersion, effectiveApiLevel,
/*out*/&tmp)).isOk())
return ret;
client = static_cast<CLIENT*>(tmp.get());
...
return ret;
makeClient()
:- 主要是根据
API
版本以及HAL
版本来选择生成具体的Client
实例。 - 如果是
HAL1
+Camera1
,则创建的是CameraClient
,如果是HAL3
+Camera1
,则创建的是Camera2Client
。
- 主要是根据
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
int api1CameraId, int facing, int clientPid, uid_t clientUid, int servicePid,
bool legacyMode, int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp<BasicClient>* client)
if (halVersion < 0 || halVersion == deviceVersion)
// Default path: HAL version is unspecified by caller, create CameraClient
// based on device version reported by the HAL.
switch(deviceVersion)
case CAMERA_DEVICE_API_VERSION_1_0:
if (effectiveApiLevel == API_1) // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName,
api1CameraId, facing, clientPid, clientUid,
getpid(), legacyMode);
else // Camera2 API route
ALOGW("Camera using old HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_DEPRECATED_HAL,
"Camera device \\"%s\\" HAL version %d does not support camera2 API",
cameraId.string(), deviceVersion);
break;
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
case CAMERA_DEVICE_API_VERSION_3_2:
case CAMERA_DEVICE_API_VERSION_3_3:
case CAMERA_DEVICE_API_VERSION_3_4:
if (effectiveApiLevel == API_1) // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new Camera2Client(cameraService, tmp, packageName,
cameraId, api1CameraId,
facing, clientPid, clientUid,
servicePid, legacyMode);
else // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
*client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
facing, clientPid, clientUid, servicePid);
break;
default:
// Should not be reachable
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
"Camera device \\"%s\\" has unknown HAL version %d",
cameraId.string(), deviceVersion);
else
// A particular HAL version is requested by caller. Create CameraClient
// based on the requested HAL version.
if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
halVersion == CAMERA_DEVICE_API_VERSION_1_0)
// Only support higher HAL version device opened as HAL1.0 device.
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
*client = new CameraClient(cameraService, tmp, packageName,
api1CameraId, facing, clientPid, clientUid,
servicePid, legacyMode);
else
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
" opened as HAL %x device", halVersion, deviceVersion,
CAMERA_DEVICE_API_VERSION_1_0);
return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
"Camera device \\"%s\\" (HAL version %d) cannot be opened as HAL version %d",
cameraId.string(), deviceVersion, halVersion);
return Status::ok();
1.2 CameraHardwareInterface.h
- 路径:
frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h
setCallback()
:- 设置
notify
回调,这用来通知数据已经更新。 - 设置
data
回调以及dataTimestamp
回调,对应的是函数指针mDataCb
与mDataCvTimestamp
。 - 注意到,设置
mDevice->ops
对应回调函数时,传入的不是之前设置的函数指针,而是__data_cb
这样的函数。在该文件中,实现了__data_cb
,将回调函数做了一层封装。
- 设置
/** Set the notification and data callbacks */
void setCallbacks(notify_callback notify_cb,
data_callback data_cb,
data_callback_timestamp data_cb_timestamp,
void* user)
mNotifyCb = notify_cb;
mDataCb = data_cb;
mDataCbTimestamp = data_cb_timestamp;
mCbUser = user;
ALOGV("%s(%s)", __FUNCTION__, mName.string());
if (mDevice->ops->set_callbacks)
mDevice->ops->set_callbacks(mDevice,
__notify_cb,
__data_cb,
__data_cb_timestamp,
__get_memory,
this);
__data_cb()
:- 对原
callback
函数简单封装,附加了一个防止数组越界判断。
- 对原
static void __data_cb(int32_t msg_type,
const camera_memory_t *data, unsigned int index,
camera_frame_metadata_t *metadata,
void *user)
ALOGV("%s", __FUNCTION__);
CameraHardwareInterface *__this =
static_cast<CameraHardwareInterface *>(user);
sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
if (index >= mem->mNumBufs)
ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
index, mem->mNumBufs);
return;
__this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser);
2 控制流
2.1 Framework
2.1.1 Camera.java
- 路径:
frameworks/base/core/java/android/hardware/Camera.java
takePicture()
:- 设置快门回调。
- 设置各种类型的图片数据回调。
- 调用
JNI takePicture
方法。 - 注意,传入的参数
msgType
是根据相应CallBack
是否存在而确定的,每种Callback
应该对应一个二进制中的数位(如 1,10,100 中 1 的位置),于是这里采用|=
操作给它赋值。
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback postview, PictureCallback jpeg)
mShutterCallback = shutter;
mRawImageCallback = raw;
mPostviewCallback = postview;
mJpegCallback = jpeg;
// If callback is not set, do not send me callbacks.
int msgType = 0;
if (mShutterCallback != null)
msgType |= CAMERA_MSG_SHUTTER;
if (mRawImageCallback != null)
msgType |= CAMERA_MSG_RAW_IMAGE;
if (mPostviewCallback != null)
msgType |= CAMERA_MSG_POSTVIEW_FRAME;
if (mJpegCallback != null)
msgType |= CAMERA_MSG_COMPRESSED_IMAGE;
native_takePicture(msgType);
mFaceDetectionRunning = false;
2.2 Android Runtime
2.2.1 android_hardware_Camera.cpp
- 路径:
frameworks/base/core/jni/android_hardware_Camera.cpp
takePicture()
:- 获取已经打开的
camera
实例,调用其takePicture()
接口。 - 注意,在这个函数中,对于
RAW_IMAGE
有一些附加操作:- 如果设置了
RAW
的callback
,则要检查上下文中,是否能找到对应Buffer
。 - 若无法找到
Buffer
,则将CAMERA_MSG_RAW_IMAGE
的信息去掉,换成CAMERA_MSG_RAW_IMAGE_NOTIFY
。 - 替换后,就只会获得
notification
的消息,而没有对应的图像数据。
- 如果设置了
static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz, jint msgType)
ALOGV("takePicture");
JNICameraContext* context;
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
/*
* When CAMERA_MSG_RAW_IMAGE is requested, if the raw image callback
* buffer is available, CAMERA_MSG_RAW_IMAGE is enabled to get the
* notification _and_ the data; otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY
* is enabled to receive the callback notification but no data.
*
* Note that CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the
* Java application.
*/
if (msgType & CAMERA_MSG_RAW_IMAGE)
ALOGV("Enable raw image callback buffer");
if (!context->isRawImageCallbackBufferAvailable())
ALOGV("Enable raw image notification, since no callback buffer exists");
msgType &= ~CAMERA_MSG_RAW_IMAGE;
msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;
if (camera->takePicture(msgType) != NO_ERROR)
jniThrowRuntimeException(env, "takePicture failed");
return;
2.3 C/C++ Libraries
2.3.1 Camera.cpp
- 路径:
frameworks/av/camera/Camera.cpp
takePicture()
:- 获取一个
ICamera
,调用其takePicture
接口。
- 获取一个
// take a picture
status_t Camera::takePicture(int msgType)
ALOGV("takePicture: 0x%x", msgType);
sp <::android::hardware::ICamera> c = mCamera;
if (c == 0) return NO_INIT;
return c->takePicture(msgType);
2.3.2 ICamera.cpp
- 路径:
frameworks/av/camera/ICamera.cpp
takePicture()
:- 利用
Binder
机制发送相应指令到服务端。 - 如果是
HAL1
+Camera1
,实际调用到的是CameraClient::takePicture()
函数。 - 如果是
HAL3
+Camera1
,实际调用到的是Camera2Client::takePicture()
函数。
- 利用
// take a picture - returns an IMemory (ref-counted mmap)
status_t takePicture(int msgType)
ALOGV("takePicture: 0x%x", msgType);
Parcel data, reply;
data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
data.writeInt32(msgType);
remote()->transact(TAKE_PICTURE, data, &reply);
status_t ret = reply.readInt32();
return ret;
2.3.3 CameraClient.cpp
- 路径:
frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp
takePicture()
:- 注意,
CAMERA_MSG_RAW_IMAGE
指令与CAMERA_MSG_RAW_IMAGE_NOTIFY
指令不能同时有效,需要进行对应的检查。 - 对传入的指令过滤,只留下与
takePicture()
操作相关的。 - 调用
CameraHardwareInterface
中的takePicture()
接口。
- 注意,
// take a picture - image is returned in callback
status_t CameraClient::takePicture(int msgType)
LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);
Mutex::Autolock lock(mLock);
status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
(msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY))
ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
" cannot be both enabled");
return BAD_VALUE;
// We only accept picture related message types
// and ignore other types of messages for takePicture().
int picMsgType = msgType
& (CAMERA_MSG_SHUTTER |
CAMERA_MSG_POSTVIEW_FRAME |
CAMERA_MSG_RAW_IMAGE |
CAMERA_MSG_RAW_IMAGE_NOTIFY |
CAMERA_MSG_COMPRESSED_IMAGE);
enableMsgType(picMsgType);
return mHardware->takePicture();
2.3.4 Camera2Client.cpp
- 路径:
frameworks/av/services/camera/libcameraservice/api1/Camera2Client.cpp
takePicture()
:- 调用了
mCaptureSequencer->startCapture()
。
- 调用了
status_t Camera2Client::takePicture(int msgType)
ATRACE_CALL();
Mutex::Autolock icl(mBinderSerializationLock);
status_t res;
if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
SharedParameters::Lock l(mParameters);
switch (l.mParameters.state)
case Parameters::DISCONNECTED:
case Parameters::STOPPED:
case Parameters::WAITING_FOR_PREVIEW_WINDOW:
ALOGE("%s: Camera %d: Cannot take picture without preview enabled",
__FUNCTION__, mCameraId);
return INVALID_OPERATION;
case Parameters::PREVIEW:
// Good to go for takePicture
res = commandStopFaceDetectionL(l.mParameters);
if (res != OK)
ALOGE("%s: Camera %d: Unable to stop face detection for still capture",
__FUNCTION__, mCameraId);
return res;
l.mParameters.state = Parameters::STILL_CAPTURE;
break;
case Parameters::RECORD:
// Good to go for video snapshot
l.mParameters.state = Parameters::VIDEO_SNAPSHOT;
break;
case Parameters::STILL_CAPTURE:
case Parameters::VIDEO_SNAPSHOT:
ALOGE("%s: Camera %d: Already taking a picture",
__FUNCTION__, mCameraId);
return INVALID_OPERATION;
ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId);
res = updateProcessorStream(mJpegProcessor, l.mParameters);
if (res != OK)
ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
// Need HAL to have correct settings before (possibly) triggering precapture
syncWithDevice();
res = mCaptureSequencer->startCapture(msgType);
if (res != OK)
ALOGE("%s: Camera %d: Unable to start capture: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
return res;
2.3.4.1 CaptureSequencer.cpp
- 路径:
frameworks/av/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
startCapture()
:- 这里
mStartCaptureSignal.signal()
会激活mStartCaptureSignal
条件。
- 这里
status_t CaptureSequencer::startCapture(int msgType)
ALOGV("%s", __FUNCTION__);
ATRACE_CALL();
Mutex::Autolock l(mInputMutex);
if (mBusy)
ALOGE("%s: Already busy capturing!", __FUNCTION__);
return INVALID_OPERATION;
if (!mStartCapture)
mMsgType = msgType;
mStartCapture = true;
mStartCaptureSignal.signal();
return OK;
CaptureSequencer
模块是 take picture
操作的重点,在 Camera2Client
中进行了创建,首先来看 CaptureSequencer
线程的 threadLoop
函数:
bool CaptureSequencer::threadLoop()
sp<Camera2Client> client = mClient.promote();
if (client == 0) return false;
CaptureState currentState;
Mutex::Autolock l(mStateMutex);
currentState = mCaptureState;
currentState = (this->*kStateManagers[currentState])(client);
Mutex::Autolock l(<以上是关于Camera1 源码解析系列—— Camera1 takePicture() 流程解析的主要内容,如果未能解决你的问题,请参考以下文章
Camera1 源码解析系列—— Camera1 startPreview() 流程解析
Camera1 源码解析系列—— Camera1 Open() 流程解析
Camera1 源码解析系列—— Camera1 takePicture() 流程解析