概述
goldfish camera主要是创建android端虚拟摄像头,和qemu通信,并且配置摄像头相关参数以及提供camera相关操作的接口。
- QemuClient.cpp:调用qemu_pipe_open创建和qemu通信的qemu pipe,提供queryConnect、queryDisconnect、queryStart、queryStop、queryFrame和qemu通信接口
- EmulatedQemuCameraDevice.cpp:主要是调用QemuClient.cpp中的接口,实现camera和qemu通信的接口。Initialize、connectDevice、disconnectDevice、startDevice、stopDevice、produceFrame等。Initialize中会创建属于camera的qemu pipe
- EmulatedCameraDevice.cpp : 主要是创建摄像头数据生产者和消费者线程Camera_FrameProducer Camera_CameraThread
- EmulatedQemuCamera.cpp:主要初始化时设置摄像头相关参数
- EmulatedCamera.cpp:提供camera相关操作接口,比如start_preview、stop_preview、set_parameters、get_parameters、take_picture等
- CallbackNotifier.cpp:定义相关回调函数
- PreviewWindow.cpp:定义预览功能的一些函数
初始化
EmulatedQemuCamera::Initialize
---EmulatedQemuCameraDevice::Initialize
--EmulatedCamera::Initialize
---mParameters.set 获取camera的分辨率,并且设置相关摄像头相关参数,这个函数主要设置了ORIENTATION_KEY、KEY_SUPPORTED_PICTURE_SIZES、KEY_SUPPORTED_PREVIEW_SIZES、KEY_SUPPORTED_VIDEO_SIZES。
status_t EmulatedQemuCamera::Initialize(const char* device_name,
const char* frame_dims,
const char* facing_dir)
{
...
/* Initialize camera device. */
status_t res = mQemuCameraDevice.Initialize(device_name);
if (res != NO_ERROR) {
return res;
}
/* Initialize base class. */
res = EmulatedCamera::Initialize();
if (res != NO_ERROR) {
return res;
}
mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, sizes.c_str());
....
}
EmulatedQemuCameraDevice::Initialize
---QemuClient::connectClient
---EmulatedCameraDevice::Initialize
status_t EmulatedQemuCameraDevice::Initialize(const char* device_name)
{
/* Connect to the service. */
char connect_str[256];
snprintf(connect_str, sizeof(connect_str), "name=%s", device_name);
status_t res = mQemuClient.connectClient(connect_str);
/* Initialize base class. */
res = EmulatedCameraDevice::Initialize();
if (res == NO_ERROR) {
ALOGV("%s: Connected to the emulated camera service '%s'",
__FUNCTION__, device_name);
mDeviceName = device_name;
} else {
mQemuClient.queryDisconnect();
}
return res;
}
QemuClient::connectClient
调用qemu_pipe_open打开qemu pipe,用于和qemu通信
EmulatedCameraDevice::Initialize
只是设置状态变量mState = ECDS_INITIALIZED
EmulatedCamera::Initialize
设置camera参数
---设置预览支持的格式
---设置预览帧率大小
---拍照之后的图片格式
等等
摄像头预览调用流程
通过摄像头预览的调用流程,可以完整了解goldfish camera下打开摄像头、预览画面数据帧处理、拍照等流程。
EmulatedCamera::set_preview_window
EmulatedCamera提供了一系列调用的接口,set_preview_window就是其中一个,设置预览画面窗口。最终调用PreviewWindow::setPreviewWindow
打开摄像头预览
PreviewWindow::setPreviewWindow
设置传进来的window,并且赋值给mPreviewWindow
status_t PreviewWindow::setPreviewWindow(struct preview_stream_ops* window,
int preview_fps)
{
...
if (window != NULL) {
...
res = window->set_usage(window, GRALLOC_USAGE_SW_WRITE_OFTEN);
...
}
mPreviewWindow = window;
return res;
}
EmulatedCamera::start_preview
--EmulatedCamera::startPreview
EmulatedCamera::startPreview
--doStartPreview
EmulatedCamera::doStartPreview
--getCameraDevice 获取EmulatedQemuCameraDevice
--PreviewWindow::startPreview 设置mPreviewEnabled为true
--EmulatedQemuCameraDevice::connectDevice 连接摄像头
--EmulatedQemuCameraDevice::startDevice 获取数据格式和预览画面的帧率,并且作为参数传入startDevice
--startDeliveringFrames 启动从qemu中获取摄像头数据帧的线程
连接和打开摄像头
EmulatedQemuCameraDevice::connectDevice
---QemuClient::queryConnect 主要是盗用QemuClient::queryConnect,通过qemu pipe向qemu发送连接摄像头的命令
CameraQemuClient::queryConnect
---构造query信息
---doQuery 执行query
---设置qery完成状态
status_t CameraQemuClient::queryConnect()
{
QemuQuery query(mQueryConnect);
doQuery(&query);
const status_t res = query.getCompletionStatus();
ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
__FUNCTION__, query.mReplyData ? query.mReplyData :
"No error message");
return res;
}
QemuClient::doQuery
---sendMessage 向qemu pipe中写query命令,发送给qemu
---receiveMessage 从qemu pipe中获取qemu返回的数据
EmulatedQemuCameraDevice::startDevice
----EmulatedCameraDevice::commonStartDevice
初始化mFrameBuffers数组,size为2 的uint8_t数组,存放对端传过来的摄像头数据。因为传过来的是raw数据,所以8位,其中一个给摄像头数据生产者使用,一个给摄像头数据消费者使用
初始化mFrameWidth、mFrameHeight、mPixelFormat和mTotalPixels
---初始化mPreviewFrames。size 为2的uint32_t数组,存放预览画面的数据。数据是RGB32格式,所以是32位
---初始化mFrameBufferPairs。size为2的pair数组,记录mFrameBuffers和mPreviewFrames
----QemuClient.queryStart
调用doQuery,向qemu发送start命令,qemu中会调用camera_cloud_device_start_capturing,具体可以看《qemu camera简介》
摄像头数据生产线程和消费线程创建
EmulatedCameraDevice::startDeliveringFrames
---startWorkerThread 启动处理摄像头数据的工作线程
EmulatedCameraDevice::startWorkerThread
---new CameraThread,名字为Camera_CameraThread,定义生产数据帧的函数ProduceFrameFunc staticProduceFrame
---CameraThread::startThread 启动线程。在线程循环处理函数threadLoop中,调用了inWorkerThread
WorkerThread::readyToRun
--onThreadStart
EmulatedCameraDevice::CameraThread::onThreadStart
--调用getPrimaryBuffer和getSecondaryBuffer 分别获取mFrameBufferPairs[0]和mFrameBufferPairs[1]
--new FrameProducer ,线程名字为Camera_FrameProducer,mProducerFunc 就是上面的staticProduceFrame,赋值mPrimaryBuffer和mSecondaryBuffer
--启动线程
status_t EmulatedCameraDevice::CameraThread::onThreadStart() {
void* primaryBuffer = mCameraDevice->getPrimaryBuffer();
void* secondaryBuffer = mCameraDevice->getSecondaryBuffer();
mFrameProducer = new FrameProducer(mCameraDevice,
mProducerFunc, mProducerOpaque,
primaryBuffer, secondaryBuffer);
if (mFrameProducer.get() == nullptr) {
ALOGE("%s: Could not instantiate FrameProducer object", __FUNCTION__);
return ENOMEM;
}
return mFrameProducer->startThread(mOneBurst);
}
获取摄像头预览画面数据
Camera_FrameProducer
此线程主要是生产摄像头数据帧,也就是向qemu获取摄像头数据
WorkerThread::threadLoop
--inWorkerThread
EmulatedCameraDevice::CameraThread::FrameProducer::inWorkerThread
--mProducer,即staticProduceFrame 生产摄像头预览数据帧。获取到的数据存放在mSecondaryBuffer中
--mSecondaryBuffer数据已经准备好,把mSecondaryBuffer交换给mPrimaryBuffer
--mHasFrame设置true
EmulatedCameraDevice::staticProduceFrame
---EmulatedQemuCameraDevice::produceFrame
EmulatedQemuCameraDevice::produceFrame
---QemuClient::queryFrame 向qemu发送获取摄像头数据帧的命令。传进来的是mFrameBuffers和mPreviewFrames的数组,获取到数据会填充到这两个数组中
bool EmulatedQemuCameraDevice::produceFrame(void* buffer, int64_t* timestamp)
{
auto frameBufferPair = reinterpret_cast<FrameBufferPair*>(buffer);
uint8_t* rawFrame = frameBufferPair->first;
uint32_t* previewFrame = frameBufferPair->second;
status_t query_res = mQemuClient.queryFrame(rawFrame, previewFrame,
mFrameBufferSize,
mTotalPixels * 4,
mWhiteBalanceScale[0],
mWhiteBalanceScale[1],
mWhiteBalanceScale[2],
mExposureCompensation,
timestamp);
...
return true;
}
CameraQemuClient::queryFrame
--构造QueryFrame的query字符串。
char query_str[256];
snprintf(query_str, sizeof(query_str), "%s video=%zu preview=%zu whiteb=%g,%g,%g expcomp=%g time=%d",
mQueryFrame, (vframe && vframe_size) ? vframe_size : 0,
(pframe && pframe_size) ? pframe_size : 0, r_scale, g_scale, b_scale,
exposure_comp, frame_time != nullptr ? 1 : 0);
QemuQuery query(query_str);
--调用doQuery向qemu发送query frame的命令,并且获取从qemu pipe中传回来的摄像数据
--处理传回来的摄像数据,memcpy给vframe和pframe,也就是mFrameBuffers和mPreviewFrames
处理并显示摄像头预览画面数据
Camera_CameraThread
摄像头预览画面数据帧的消费线程,也就是处理并显示摄像头预览画面数据到预览窗口。
WorkerThread::threadLoop
--inWorkerThread
EmulatedCameraDevice::CameraThread::inWorkerThread
--waitForFrameOrTimeout 等待frame准备好,通过mHasFrame变量判断。如果等待超时,则返回false
---EmulatedCamera::onNextFrameAvailable
EmulatedCamera::onNextFrameAvailable
void EmulatedCamera::onNextFrameAvailable(nsecs_t timestamp,
EmulatedCameraDevice* camera_dev)
{
/* Notify the preview window first. */
mPreviewWindow.onNextFrameAvailable(timestamp, camera_dev);
/* Notify callback notifier next. */
mCallbackNotifier.onNextFrameAvailable(timestamp, camera_dev);
}
PreviewWindow::onNextFrameAvailable
---adjustPreviewDimensions 确保预览窗口的大小和camera数据帧宽高一样
---mPreviewWindow->dequeue_buffer 给预览画面数据申请preview window buffer
---mPreviewWindow->lock_buffer lock出队列的buffer
---graphics framework 锁住该buffer,并且提供framebuffer的数据地址
---EmulatedCameraDevice::getCurrentPreviewFrame 获取从qemu中获取到的数据
---mPreviewWindow->enqueue_buffer 在预览窗口上显示getCurrentPreviewFrame获取的预览画面
---GrallocModule::getInstance().unlock(*buffer);
EmulatedCameraDevice::getCurrentPreviewFrame
---getPrimaryBuffer 获取mFrameBufferPairs[0],也就是Camera_FrameProducer线程从qemu中获取保存在mPrimaryBuffer中的数据
---根据不同数据格式,对获取到数据进行不同处理。
CallbackNotifier::onNextFrameAvailable
---当mTakingPicture为true,则进行拍照