采集端分为以下几个子系统:
1、采集端子系统
2、存储子系统
3、压缩子系统
4、传输子系统
5、配置子系统
6、主程序
首先对整体的程序框架做一个划分,对于每个子系统都建立一个.c文件,同时对每个子系统都建立一个结构体来描述,然后把所有需要用到的头文件都放到include文件夹中。
然后提炼出哪些事件是需要等待的,可以把这些事件加入Epoll池中,分析如下:
1、摄像头可读(摄像头的设备文件已经有了一帧图像)
2、socket可写(可以通过socket发送数据了)
3、存储设备可写(可以往SD卡中存放数据)
可能分析的不够全面,会把有些事件漏掉,想起了以后再加好了。
对于Epoll的事件结构是这样的:
struct epoll_event{
__uint32_t events;
epoll_data_t data;
};
如果需要保存一些信息,比如说设备文件fd,对应的处理函数和其他标志位,就是采用data里面的结构来保存的:
typedef union epoll_data{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
};
由于结构里的数据只可以用一个,因此为了方便起见,我们自己定义一个结构来保存设备文件fd、一些标志位和处理函数,并把这个结构的指针挂接早ptr指针中:
struct event_ext
{
int fd;//监控文件的fd
bool epolled;//事件是否在池中的标志
uint32_t events;//事件类型
void (*handler)(int fd, void *arg);//处理函数
void *arg;//处理函数的参数
};
这个结构定义在main.c中,然后在main.h中定义:
struct server
{
int epfd;//指向创建的Epoll
struct cam *cam;//指向摄像头子系统
struct tcp_srv *tcp_srv;//指向网络子系统
struct cfg *cfg;//指向配置子系统
};
struct server *srv_main;
在cam.c中定义:
struct cam
{
};
struct cam *cam_sys_init()
{
//初始化采集子系统
//将采集子系统中的事件加入Epoll池
return NULL;
}
在net.c中定义:
struct tcp_srv
{
};
struct tcp_srv *net_sys_init()
{
//初始化传输子系统
//将传输子系统的事件加入Epoll池
}
在配置文件中定义:
struct cfg
{
};
定义好每个子系统的结构后开始设计主程序的框架,在主程序中主要实现Epoll的初始化,各个子模块的初始化,然后添加事件,最后等待事件的发生,具体事件的处理交给各个子系统。同时为了使整个框架更灵活,把Epoll事件的添加交给子系统完成,具体需要添加/修改哪个事件有子系统觉得,同时事件的添加放在子系统初始化中完成。然后给子系统提供事件初始化函数、事件添加/修改、事件删除的接口。最后main.c文件如下:
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include "main.h"
struct event_ext
{
int fd;//监控文件的fd
bool epolled;//事件是否在池中的标志
uint32_t events;//事件类型
void (*handler)(int fd, void *arg);//处理函数
void *arg;//处理函数的参数
};
//初始化事件的接口
struct event_ext *epoll_event_create(int fd, uint32_t type, void (*handler)(int , void *), void *arg)
{
struct event_ext *e = calloc(1,sizeof(struct event_ext));
e->fd = fd;
e->events = type;
e->handler = handler;
e->arg = arg;
return e;
}
//添加事件的接口
int epoll_add_event(int epfd, struct event_ext *ev)
{
struct epoll_event epv;
int op;
//初始化epoll_event
epv.data.ptr = ev;
epv.events = ev->events;
if(ev->epolled)
{
op = EPOLL_CTL_MOD;
}
else
{
op = EPOLL_CTL_ADD;
ev->epolled = true;
}
//将epoll_event加入epoll
epoll_ctl(epfd, op, ev->fd, &epv);
return 0;
}
//删除事件
int epoll_del_event(int epfd, struct event_ext *ev)
{
epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, NULL);
ev->epolled = false;
return 0;
}
int main()
{
struct epoll_event events[512];
int fds;
int i;
uint32_t event;
struct event_ext *e;
srv_main = calloc(1,sizeof(struct server));
//创建Epoll
srv_main->epfd = epoll_create(512);
//子系统初始化
srv_main->cam = cam_sys_init();
srv_main->tcp_srv = net_sys_init();
//等待事件发生并处理
while(1)
{
fds = epoll_wait(srv_main->epfd, events, 512, 1000);
for(i=0; i<fds; i++)
{
event = events[i].events;
e = events[i].data.ptr;
if((event & EPOLLIN) && (e->events & EPOLLIN))
{
e->handler(e->fd, e->arg);
}
if((event & EPOLLOUT) && (e->events & EPOLLOUT))
{
e->handler(e->fd, e->arg);
}
if((event & EPOLLERR) && (e->events & EPOLLERR))
{
e->handler(e->fd, e->arg);
}
}
}
return -1;
}
然后是Makefile的编写:
BIN = wcamsrv
INC = -Iinclude/
SRC = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRC))
CC = arm-linux-gcc
CFLAGS = $(INC) -g
$(BIN):$(OBJS)
$(CC) -o $@ $^
clean:
rm $(OBJS) $(BIN)
BIN一个变量,保存目标的二进制文件
INC是头文件的路径,包含include下的所有文件
SRC是需要编译的源文件,使用wildcard函数来寻找所有的.c文件
OBJS保存生成的.o文件
CC是编译工具,它是arm-linux-gcc
下面2行是编译和清除的命令。
更多Linux资料及视频教程点击这里