searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

goldfish camera简介

2023-12-19 03:35:44
154
0

概述

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,则进行拍照
0条评论
0 / 1000
黄****养
3文章数
0粉丝数
黄****养
3 文章 | 0 粉丝
黄****养
3文章数
0粉丝数
黄****养
3 文章 | 0 粉丝
原创

goldfish camera简介

2023-12-19 03:35:44
154
0

概述

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,则进行拍照
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0