本文共 5502 字,大约阅读时间需要 18 分钟。
【倒车影像分流需求 一】- 需求分解 及 进程间通信共享内存原理
本需求系列文章:
- 《》
- 《》
- 《》
- 《》
- 《》
- 《》
- 《》
- 《》
- 《》
前阵子接到 xxx 的需求,要在车机上实现倒车影像功能,简单来说,就是要将倒车影像的Camera 数据,分流发送到xxx SDK上。
一、 需求分解
需求分解主要三步:
- 从哪拿到倒车影像视频数据?
- 拿到视频数据后,要发到哪里?
- 以什么形式跨进程发送数据?
接下来一个个看下:
1.1 从哪拿到倒车影像视频数据?
结合我们之前写的《 》,
当有视频数据到来时,会回调 RearCamera::dataCallback
void RearCamera::dataCallback(int32_t msgType, const sp& data, camera_frame_metadata_t *) { static int once = 0; switch (msgType) { case CAMERA_MSG_PREVIEW_FRAME: { if (data != nullptr) { reverse_video::getInstance()->notifyCameraPreviewDataArrived();//need optmized dls_camera_NewPreviewDataArrived((unsigned char *)data->pointer(), (ssize_t)data->size()); } Mutex::Autolock l(mPreviewLock); mPreviewBufferCount++; mPreviewCondition.broadcast(); break; } }};
最终处理完的数据在 OnNewCameraPreviewData 的 mPreviewBuffer 中,所以,我们只需要在此处,将 previewbuffer 的数据拷贝发出去就好了。
# /hardware/libhardware/modules/reverse/reversing_video/reverse_ui/CameraView.cpp/******************************************************** * use: callback when new camera preview data arrived ******/ void CameraView::OnNewCameraPreviewData(unsigned char * aData, int aDataLen){ //ALOGD("%s new preview data+++", __FUNCTION__); //mNewCameraDataLock.lock(); memcpy(mPreviewBuffer, aData, aDataLen); mNewCameraData = true; mNewCameraDataLock.unlock(); // cielee post data to dmsdp +++ Camera_Shared_Memory* Camera_View_Shared_Memory = Camera_Shared_Memory::getCamera_Shared_Memory(); unsigned char *tmp_mPreviewBuffer = NULL; tmp_mPreviewBuffer= mPreviewBuffer; // 在此处进行数据分流,写共享内存 Camera_View_Shared_Memory->Write_Shared_Memory(tmp_mPreviewBuffer,sizeof(mPreviewBuffer)); // cielee post data to dmsdp --- mYuvTexBuffer_timestamp = uptimeMillis(); //ALOGD("%s new data done!!",__FUNCTION__); //ALOGD("%s new preview data---", __FUNCTION__);}
1.2 拿到视频数据后,要发到哪里?
根据 xxx 的需求:
厂商使用上述头文件,实现Camera相关接口,并生成“libdmsdpcamerahandler.so”文件,放置到“/system/lib”或/“system/lib64”目录下。 xxx SDK会在运行时,动态加载该so链接库,并通过so链接库中实现的接口从车机获取Camera数据。也就是说,根据 xxx 提供的接口,写一个lib库出来,xxx 启动时,就会自动打开 so库。
根据接口,对应的Camera 数据在DMSDPSendBackDataCB
回调函数据发出去。
/** * function callback for send data back */ typedef int32_t (*DMSDPSendBackDataCB)( const char* id, uint32_t idLen, const uint8_t* buffer, uint32_t len, int32_t type);
1.3 以什么形式跨进程发送数据?
因为libdmsdpcamerahandler.so 是在xxx SDK 进程中被打开的,所以它运行在 xxx 的进程中。
而倒车视频数据是在 service服务dls_vehicle_signal_service 中, 所以需要将 service服务dls_vehicle_signal_service 中的Camera 数据跨进程实时发送给 libdmsdpcamerahandler.so。实现方法有两种,各自实现方法如下:
-
共享内存
在service服务dls_vehicle_signal_service 中初始化一块共享buff,当有数据到在时,dls_vehicle_signal_service往buff写数据, 数据写完后, libdmsdpcamerahandler.so 中再来读数据。 注意需要考虑数据同步问题。 -
AIDL ----> 如果想搞这块可以参考我之前的博客《》
在 libdmsdpcamerahandler.so 中实现 AIDL 的Server 端,打开xxx 投屏时,会初始化 Server 在 dls_vehicle_signal_service 中,实现AIDL 的Client 端,当有视频数据到来时,Client 发送数据给到 Server
最终,我还是选择使用 共享内存 来实现,因为它实现简单且效率快。
二、共享内存原理
以下内容出自:《》
因为这篇文章写的非常好,我就没有自已写,摘抄些笔记,欢迎去阅读原文共享内存是System V版本的最后一个进程间通信方式。
共享内存,就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。但共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
网上看到一幅图画的特别好,我们copy 下:
当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥。
2.1 共享内存的接口函数以及指令
1.查看系统中的共享存储段 ipcs -m2.删除系统中的共享存储段 ipcrm -m [shmid]3.shmget ( ):创建共享内存 int shmget(key_t key, size_t size, int shmflg); [参数key]:由ftok生成的key标识,标识系统的唯一IPC资源。 [参数size]:需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍。 [参数shmflg]:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,如果是已经存在的,可以使用IPC_CREAT或直接传0。 [返回值]:成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败返回-1并设置错误码。4.shmat ( ):挂接共享内存 void *shmat(int shmid, const void *shmaddr, int shmflg); [参数shmid]:共享存储段的标识符。 [参数*shmaddr]:shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上(推荐使用)。 [参数shmflg]:若指定了SHM_RDONLY位,则以只读方式连接此段,否则以读写方式连接此段。 [返回值]:成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1。5.shmdt ( ):去关联共享内存 当一个进程不需要共享内存的时候,就需要去关联。该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。 int shmdt(const void *shmaddr); [参数*shmaddr]:连接以后返回的地址。 [返回值]:成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1。6.shmctl ( ):销毁共享内存 int shmctl(int shmid, int cmd, struct shmid_ds *buf); [参数shmid]:共享存储段标识符。 [参数cmd]:指定的执行操作,设置为IPC_RMID时表示可以删除共享内存。 [参数*buf]:设置为NULL即可。 [返回值]:成功返回0,失败返回-1。
2.2创建共享内存
#include#include #include #include #define PATHNAME "."#define PROJ_ID 0x6666static int CommShm(int size,int flags){ key_t key = ftok(PATHNAME,PROJ_ID); if(key < 0){ perror("ftok"); return -1; } int shmid = 0; if((shmid = shmget(key,size,flags)) < 0){ perror("shmget"); return -2; } return shmid;}
2.3 销毁共享内存
int DestroyShm(int shmid){ if(shmctl(shmid,IPC_RMID,NULL) < 0) { perror("shmctl"); return -1; } return 0;}
2.4 使用共享内存
服务端创建 和 挂接 共享内存:
int main(){ int shmid = CommShm( 4096 ,IPC_CREAT | IPC_EXCL | 0666); // 获取 share memory id char *addr = shmat(shmid,NULL,0); // 根据 share memory id 挂接地址 sleep(2); int i = 0; while(i++ < 26) { printf("client# %s\n",addr); sleep(1); } shmdt(addr); // 当不在使用时,取消共享内存地址关联 sleep(2); DestroyShm(shmid); // 销毁共享内存 return 0;}
客户端写共享内存:
int main(){ int shmid = CommShm(4096 ,IPC_CREAT);; // 获取 share memory id sleep(1); char *addr = shmat(shmid,NULL,0); // 根据 share memory id 挂接地址 sleep(2); int i = 0; while(i < 26) { addr[i] = 'A' + i; i++; addr[i] = 0; sleep(1); } shmdt(addr); // 当不在使用时,取消共享内存地址关联 sleep(2); return 0;}
转载地址:https://ciellee.blog.csdn.net/article/details/105989168 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!