项目三. 基于图像压缩的视频监控系统

Posted free-1122

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目三. 基于图像压缩的视频监控系统相关的知识,希望对你有一定的参考价值。

项目三. 基于图像压缩的视频监控系统

Sprint0-产品设计与规划

基于图像压缩的视频监控系统

1. 产品功能演示

在linux系统上运行程序,弹出登录界面,输入地址、端口信息,弹出视频监控界面,实时传出视频信息。

2. 功能模块分析

采集端:

图像采集子系统

图像编码子系统

传输子系统

主程序

监控端:

传输子系统

图像编码子系统

传输子系统

主程序

 

Sprint1-基于Epoll架构的采集端程序框架设计

第1课-Epoll机制精通

大纲:

v  为什么用Epoll?

  • 阻塞型IO与多路复用
  • select
  • epoll

v  怎么用Epoll?

  • epoll_creat
  • epoll_ctl
  • epoll_wait

 

1. 为什么要用Epoll?

(1)阻塞型IO

阻塞是指没有获得资源则挂起进程,直到获得资源为止。被挂起的进程进入休眠状态,被调度器的运行队列移走,直到等待条件被满足。

非阻塞是不能进行设备操作时不挂起,或放弃,或反复查询,直到可以进行操作为止。

驱动程序常需要这种能力:当应用程序进行read(),write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应该在设备驱动程序的xxx_read(), xxx_write()等操作中将进程阻直到资源可以获取,以后,应用程序read(),write()等调用返回,整个过程仍然进行了正确的设备访问,用户并没有感知到;若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read(),xxx_write()等操作应立即返回,read(),write()等系统调用也随即被访问。         

阻塞不是低效率,如果设备驱动不阻塞, 用户想获取设备资源只能不断查询,消耗CPU资源,阻塞访问时,不能获取资源的进程将进入休眠,将CPU资源让给其他资源。阻塞的进程会进入休眠状态,因此,必须确保有一个地方能唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断。

(2)多路复用

多路复用是指以同一传输媒质(线路)承载多路信号进行通信的方式。各路信号在送往传输媒质以前,需按一定的规则进行调制,以利于各路已调信号在媒质中传输,并不致混淆,从而在传到对方时使信号具有足够能量,且可用反调制的方法加以区分、恢复成原信号。多路复用常用的方法有频分多路复用和时分多路复用,码分多路复用的应用也在不断扩大。

频分多路复用FDM (Frequency Division Multiplexing)和时分多路复用TDM (Time Division Multiplexing)是两种最常用的多路复用技术(Multiplexing)。

 

目前我们使用的函数,有那个可以实现阻塞型IO和多路复用呢?当然即使select函数。

 

(3)select函数

select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他 文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执 行了select()的进程哪一Socket或文件可读或可写。主要用于Socket通信当中!

我们看一下这个函数的一般使用过程:

需要头文件#include <sys/select.h>

while(1)

       FD_ZERO(&set); //1. 初始化文件描述符集合

       foreach(需要监控的文件)

      

              fd大于maxfd,则maxfd-fd

              FD_SET(fd,&set)   //2.将文件的描述符一个一个的加到文件中去

      

       res=select(maxfd+1,&set,0,0,0);  /*3. 调用select开始监控,要是没有文件满足要求,就会等待在这个地方,

       等到要么超时,要么就等待在这个地方*/

       if(FD_ISSET(listen_fd,&set))   

      

              newfd=accept(listen_fd);

              array[nsock]=newfd;

              if(--res<=0) continue;

      

       foreach(需要监控的文件)  /*4. 遍历文件,看看是哪个的变化导致的这种退出,很占用资源*/

      

              if(FD_ISSET(fd,&tyle="COLOR: #ff0000">set))

              执行读等相关的操作

              如果错误或者关闭,则要删除fd,将array中相应位置和最后而定一个元素相互调换就好,nsock减一

              if(--res<=0) continue;

      

介于select函数在使用的时候,由于遍历性使得运行的时间很长,很浪费资源,所以我们引出了下面的epoll函数。

 

(4)Epoll分析

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

无序遍历,没有上线,是linux系统中最优秀的多路复用机制。

 

2. 怎么使用Epoll?

Epoll支持管道,FIFO,套接字,POSIX消息队列,终端,设备等等,但就是不支持普通文件(文本文件、可执行程序等)。Epoll的使用分为3个环节。

epoll_creat/epoll_creat1(创建epoll监听池)

epoll_ctl(添加要监听的事件)

epoll_wait(等待事件的发生)

(1)epoll_creat/epoll_creat1

创建epoll监听池

synopsis:(梗要)

#include <sys/epoll.h>

int epoll_creat(int size);  //老的版本中size表示监听池的大小,现在没用了

int epoll_creat1(int flags); /*flags表示创建标志,一般的情况下,我么让它等于0,当它等于0的时候,这个函数和epoll_creat()函数的作用是一模一样的 */

description:

epoll_creat() craets an epoll “instance”, requesting the kernel to allocate an event backing store dimensioned for size descriptors. The size is not the maxmum size of the backing store but just a hint to the kernel about how to dimension internal structures.

epoll_creat() returns a file descriptor referring to the new epoll instance. This file descriptor is used for all the subsequent calls to the epoll interface.

返回值:

成功,无;失败,-1。

 

(2)epoll_ctl

添加要监听的事件, select是以文件作为监听对象的,但是epoll_ctl是以事件为监听对象的。A文件(可读)和A文件(可写)算是两个文件

synopsis:(梗要)

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

description:

This system call performs control operations on the epoll instance referred toby the file descriptor epfd. It requests that the operation op be performed for the target file dercriptor, fd.

epfd我们要监听的监听池的fd;

op表示我们对监听池进行的操作:

EPOLL_CTL_ADD:加入事件

EPOLL_CTL_MOD:更改事件

EPOLL_CTL_DEL:删除事件

fd表示我们要操作的事件是哪一个文件

*event表示我们操作的类型:

EPOLLIN:可读

EPOLLOUT:可写

 

返回值:成功0,失败-1.

 

(3)epoll_wait

等待事件的发生

synopsis:(梗要)

#include <sys/epoll.h>

int epoll_wait(int epfd struct epoll_event *event, int maxevent, int timeout);

description:

epfd:等待的事件

*event:当事件被监听到的时候,就会被放到这个数组里面

maxevent:最多的时间数

timeout:等待的最长时间

返回值:

成功,表示有多少个事件发生;失败,-1。

 

3. 程序-监听两个FIFO

创建文件ep.c,ew1.c和ew2.c。文件ep.c是我们创建的epoll监控文件,ew1.c和ew2.c文件分别是我们对创建的两个FIFO文件的写入文件。程序如下:

ep.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

#include<sys/epoll.h>

int main()

    int fd1, fd2;

    int efd;

    int i;

    char c;

    int n;

    struct epoll_event event;

    struct epoll_event *events;

    //创建FIO

    mkfifo("/tmp/fifo1",0666);

    mkfifo("/tmp/fifo2",0666);

    fd1=open("/tmp/fifo1",O_RDONLY);

    fd2=open("/tmp/fifo2",O_RDONLY);

    //创建监听池

    efd=epoll_create1(0);

    //构造监听事件,加入监听池

    event.events = EPOLLIN|EPOLLET; /*EPOLLET表示边沿触发*/

    event.data.fd = fd1;

    epoll_ctl(efd,EPOLL_CTL_ADD,fd1,&event);

    event.events = EPOLLIN;

    event.data.fd = fd2;

    epoll_ctl(efd,EPOLL_CTL_ADD,fd2,&event);

    //开始监听

    events = calloc(100,sizeof(event)); //给要保存的数组分配空间,可以保存100个事件

    n = epoll_wait(efd,events,100,-1);

    for(i=0;i<n;i++)

   

        if(events[i].events & EPOLLIN) //如果数据类型是EPOLLIN

       

            read(events[i].data.fd,&c,1);

        //假设我们读的是个字符,长度当然就是1,要读的文件在events[i].data.fd中

            printf("file %d can be read\n",events[i].data.fd);

       

 

        if(events[i].events & EPOLLOUT) //如果数据类型是EPOLLOUT

       

            //

       

        if(events[i].events & EPOLLERR) //如果数据类型是EPOLLERR,出错的

       

            //

       

   

    free(events); //释放我们使用的空间,必须得有

    close(fd1);

    close(fd2);  //关闭打开的文件

ew1.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

int main()

    int fd;

    char c1 = ‘c‘;

    fd = open("/tmp/fifo1", O_WRONLY);  //用只写的方式打开

    write(fd,&c1,1);

    close(fd);

    return 0;

ew2.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

int main()

    int fd;

    char c1 = ‘c‘;

    fd = open("/tmp/fifo2", O_WRONLY);  //用只写的方式打开

    write(fd,&c1,1);

    close(fd);

    return 0;

分别用gcc编译成ep,ew1和ew2三个.o文件。在一个终端中运行./ep会是程序卡住,使用ctrl+shift+t生成另一个相同的中端,运行./ew1和./ew2后,第一个中断可以继续运行,结果为:

file 3 can be read

file 4 can be read

顺序运行不对的话,显示不出来正确结果。

 

 

 

 

第2课-基于Epoll的采集端程序框架设计

epoll的使用流程

(1)创建

(2)加入事件

(3)等待和处理

 

步骤:

1. 创建源文件

我们的软件体系到底能不能完成,关键的事情就是加入事件(摄像头)。

这里我们区分,主程序是第一个模块,摄像头采集系统是第二个模块,传输系统是第三个模块,配置子系统是第四个模块。每个模块对应一个原文件,我们要创建四个原文件。分别为:main.c           cam.c      net.c      cfg.c

 

2. 模型代码化

我们创建一个include文件夹,用来存放我们要用到的头文件。创建头文件main.h。在这里建立结构体struct server,在cam.c中建立结构体struct cam,在net.c中建立struct tcp,在cfg.c中建立struct cfg,具体内容先不去填写。今天我们的任务就是将main.h中的内容进行完善即可。

主程序对应着与配置子系统、采集子系统、传输子系统和epoll框架这四个内容的关系。我们就建立四个成员对应这四个关系。我们可以进行函数下放,就要下放接口。子程序同时也提供函数接口,这样就完成了函数的下放。我们看看有哪些接口:

(1)添加事件到EPOLL

(2)从EPOLL中删除事件

现在我们在想为什么不直接用系统带的函数epoll_ctl,还要自己写呢?因为我们用的epoll_ctl是依赖于struct epoll_event结构来的,但是这个结果里面的信息有限:

typedef union epoll_data

    void *ptr;

    int fd;

    uint32_t u32;

    uint64_4 u64;

epoll_data_t;

stryct epoll_event

    unint32_t events;   /*Epoll events*/

    epoll_data_t data;  /*User data variable*/

;

也就只有一个文件类型和一个联合(包含4项),我们要是想多保存点信息就不行了。我们可以使这个指针再加一些附加结构,这里面保存我们想要有的其他信息。我们在main函数中定义就可以。

struct server

    int epfd;   //指向epoll

    struct cam *cam;  //指向采集子系统

    struct tcp_srv *srv; //指向传输子系统

    struct cfg* cfg;   //指向配置子系统

;

3. 程序

main.c

#include<stdio.h>

#include<unistd.h>

#include<fcntl.h>

#include<sys/epoll.h>

#include<main.h>

#include<stdbool.h>

struct event_ext    //我们的附加的结构体,用来补充epoll_ctl函数

    int fd;

    bool epolled;   //表示我们的时间是否已经加入到了我们监控池之中

    uint32_t events;  //事件的类型

    void (*handler)(int fd,void *arg);  //函数指针,其中有两个成员

    void *arg;  //提供给handler使用的指针

;

/*由于我们附加的函数的作用,我们还得需要自己手写我们对人epoll事件的添加和删除函数*/

/*基于新的结构体而定创造函数,结构体中有的内容,这里面都该有,第一个结构*/

struct event_ext *epoll_event_creat(int fd,uint32_t type,void (*handler)(void *arg),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;

;

//添加epoll,第二个结构

int epoll_add_event(int epfd, struct event_ext *ev)  

 /*创建一个事件附加结构,并将参数传进来*/

    struct epoll_event epv;   //先创建一个结构体

    int op; //定义原本的epoll_ctl中的第二个参数

    //2. 初始化epoll_event(将附加结构挂载到epoll_event中)

    epv.data.ptr = ev;   //将附加的结构挂载原来系统带的结构体上

    epv.events =  ev->events;  //初始化数据类型,原本的epoll_event中就这两个成员

    if(ev->epolled)  //文件已经存在的时候,就是进行修改,否则进行加入

   

        op = EPOLL_CTL_MOD; //修改的操作  

   

    else

   

        op = EPOLL_CTL_ADD;//添加的操作

        ev->epolled = true; //第一次添加进去之后,标识就变成了真

   

    //3. 将epoll_even加入到epoll中

    epoll_ctl(epfd,op,ev->fd,&epv);

    return 0;

;

//从epoll池中删除

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));  //为结构体分配空间

    //1. 创建epoll

    srv_main->epfd = epoll_create(512);

    //2. 加入等事件,将这个程序进行下放-交给子系统,这之后就要设置子系统的函数接口

    srv_main->cam = cam_sys_init();

    srv_main->srv = net_sys_init();

    //3. 等待事件发生且处理

    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里面的内容类型与event里面的一致*/

           

                //处理

                e->handler(e->fd,e->arg);

           

            if((event&EPOLLOUT)&&(e->events&EPOLLOUT)) /*若是读的事件,附加条件要e里面的内容类型与event里面的一致*/

           

                //处理

                e->handler(e->fd,e->arg);

           

            if((event&EPOLLERR)&&(e->events&EPOLLERR))  /*若是读的事件,附加条件要e里面的内容类型与event里面的一致*/

           

                //处理

                e->handler(e->fd,e->arg);

           

       

   

    return -1;   //按道理来讲我们而定循环是不应该结束的,要是结束了就说明程序出错了

cam.c

#include<main.h>

struct cam

;

struct cam *cam_sys_init()

    //1. 初始化采集子系统

    //2. 将采集子系统中的事件加入epoll池

    return NULL;

;

net.c

#include<main.h>

struct tcp_srv

;

struct tcp_srv *net_sys_init()

    //1. 初始化传输子系统

    //2. 将传输子系统中的事件加入epoll池

    return NULL;

;

cfg.c

struct cfg

 

;

4. 编译

创建一个Makefile文件,touch Makefile。通过samb服务器打开这个文件我们写下如下的文件:

BIN = wcamsrv                                                          //输出的文件名字

INC = -Iinclude/     /*表示需要引用的是当前文件下的include文 件,-I表示的对应的操作*/

SRC = $(wildcard *.c)                         //表示寻找的该路径下的所有.c文件,都给SRC

OBJ = $(patsubst %.c, %.o, $(SRC) )     /*表示输出的格式是.o的形式的,对应的三个参数,第一个参数表示所有的.c文件,第二个参数表示输出都是.o文件,第三个参数表示来自SCR变量*/

CC = arm-linux-gcc                                                   //表示编译器的选择

CFLAGS = $(INC) -g                                                           //编译选项

$(BIN):$(OBJS)                                                               //编译依赖

    $(CC) -o $@ $^                                                          //编译输出

clean:                                                                      //清除选项

    rm $(OBJS) $(BIN)                                                       

 

 

Sprint2-采集端图像采集子系统设计

 

第3课-采集端图像采集子系统设计

流程:

1. 初始化-初始化摄像头

2. 注册事件到epoll

3. 开始采集

4. 保存图像

 

我们打开camera.c,进行书写

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/epoll.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <linux/types.h>

#include <linux/videodev2.h>

 

#include <main.h>

 

struct cam

    struct v4l2_dev *v4_dev;

   

;

 

struct buf

    void *start;

    int len;

;

 

struct v4l2_dev

    int fd;

    __u8 name[32];

    __u8 drv[16];

    struct buf *buf;

    struct event_ext *ev;

;

 

 

/*事件处理函数*/

void cam_handler(int fd,void *arg)

     struct v4l2_buffer buf;

     struct v4l2_dev *v = arg;

     int file_fd;

    

     file_fd = open("test.jpg",O_RDWR|O_CREAT,0777);

   

     /*帧出列*/

     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     buf.memory = V4L2_MEMORY_MMAP;

     ioctl (v->fd, VIDIOC_DQBUF, &buf);

 

     write(file_fd,v->buf[buf.index].start,v->buf[buf.index].len);

     close(file_fd);

    

     /*buf入列*/

     ioctl(v->fd, VIDIOC_QBUF, &buf); 

 

struct v4l2_dev * v4l2_init()

     struct v4l2_capability cap;

     struct v4l2_dev *v;

     struct v4l2_format fmt;

     struct v4l2_requestbuffers req;

     int i;

     struct v4l2_buffer buf; 

 

    

     //1. 初始化摄像头

     v = calloc(1,sizeof(struct v4l2_dev));

     v->fd = open("/dev/video3",O_RDWR|O_NONBLOCK);

    

     //1.1 获取驱动信息

     ioctl (v->fd, VIDIOC_QUERYCAP, &cap);

    

     if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))

    

          printf("this is not a video device\n");

          return -1; 

    

     strcpy((char *)v->name,(char *)cap.card);

     strcpy((char *)v->drv,(char *)cap.driver);

    

     //1.2 设置图像格式

     fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     fmt.fmt.pix.width       = 1280;

     fmt.fmt.pix.height      = 1024;

     fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;

 

     ioctl (v->fd, VIDIOC_S_FMT, &fmt) ;

       

     //1.3 申请图像缓冲区

     req.count               = 4;

     req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     req.memory              = V4L2_MEMORY_MMAP;

     ioctl (v->fd, VIDIOC_REQBUFS, &req);

    

     //1.4 把内核空间中的图像缓冲区映射到用户空间

     v->buf = calloc(4,sizeof(struct buf));

    

     for (i = 0; i < req.count; i++)

    

           /*获取图像缓冲区的信息*/

           buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

           buf.memory      = V4L2_MEMORY_MMAP;

           buf.index       = i;

 

           ioctl (v->fd, VIDIOC_QUERYBUF, &buf);

            

           v->buf[i].len = buf.length;

          

           // 把内核空间中的图像缓冲区映射到用户空间

          v->buf[i].start = mmap (NULL ,    //通过mmap建立映射关系

                                        buf.length,

                                        PROT_READ | PROT_WRITE ,

                                        MAP_SHARED ,

                                        v->fd,

                                        buf.m.offset);

    

    

     //1.5 图像缓冲入队

       for (i = 0; i < 4; ++i)

      

               buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

               buf.memory      = V4L2_MEMORY_MMAP;

               buf.index       = i;

               ioctl (v->fd, VIDIOC_QBUF, &buf);    

      

    

     //2. 注册事件到epoll

     v->ev = epoll_event_create(v->fd,EPOLLIN,cam_handler,v);

     epoll_add_event(srv_main->epfd,v->ev);

    

     return v;

   

 

void v4l2_start_capture(struct v4l2_dev *v)

    enum v4l2_buf_type type;

   

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ioctl (v->fd, VIDIOC_STREAMON, &type);

 

struct cam * cam_sys_init()

     struct cam *cam = calloc(1,sizeof(struct cam));

    

     //1. 初始化

     cam->v4_dev = v4l2_init();

    

     //2. 开始采集

     v4l2_start_capture(cam->v4_dev);

       

 

我们在以前就编辑好的文件夹中运行命令:make,会生成一些子文件,我们将运行文件倒进开发板。运行./wcamsrv ,会在主界面上生成一个test。jpg文件,打开文件,发现捕捉成功。

 

Sprint3-采集端传输子系统设计(未完成)

第4课-采集端传输子系统设计

我们再分析一遍流程:由采集端(摄像头)传来的图像给主程序,主程序将图像进行编码压缩,将压缩后的推向传给播放器进行播放。我们之前采集到的图像是jpg格式的,而对于jpg格式的图像,他本身就是一个编码压缩后的图像了,我们没有必要再对它进行编码。

我们直接将编码的步骤省去,直接去做网络传输子系统。它的作用也就是将图像文件进行传输。我们先看该系统的流程。

(1)初始化

socket初始化(TCP)

创建socket

初始化地址

bind地址

listen

事件注册到epoll

(2)事件的处理

收到网络包的时候进行处理

发送网络包的时候进行处理

 

我们先编写初始化代码,打开net.c文件,程序编写如下:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <stdbool.h>

#include <unistd.h>

#include <fcntl.h>

#include <time.h>

#include <errno.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <pthread.h>

#include <sys/epoll.h>

 

#include <main.h>

 

void net_send(struct tcp_cli * tc, void *buf, int len)

    struct tcp_cli *c = (struct tcp_cli*)tc;

    struct tcp_srv *s = c->srv;

    epoll_del_event(s->epfd, c->ev_rx);

    c->buf = buf;

    c->len = len;

    epoll_add_event(s->epfd, c->ev_tx);

 

static void rx_app_handler(int sock, void *arg)

    struct tcp_cli *c = arg; 

    int res = 0;  

    unsigned char            *pbuf;

    pbuf  = &c->req[0];

 

        res = read(c->sock, pbuf, FRAME_HDR_SZ);

        process_incoming(c);

static void tx_app_handler(int sock, void *arg)

    struct tcp_cli *c = arg;

    struct tcp_srv *s = c->srv;

    int res = 0;

    res = send(sock, c->buf, c->len, 0);

    if (res > 0)

        c->len -= res;

        if (c->len == 0)

            epoll_del_event(s->epfd, c->ev_tx);

            epoll_add_event(s->epfd, c->ev_rx);

       

   

int build_ack(unsigned char *rsp, unsigned char type, unsigned char id,

                         unsigned char len, unsigned char *data)

    rsp[LEN_POS]  = len;

    rsp[CMD0_POS] = type;

    rsp[CMD1_POS] = id;

    memcpy(&rsp[DAT_POS], data, len);

    return len + FRAME_HDR_SZ;

int net_sys_init( )

     struct sockaddr_in addr;

     struct sockaddr_in sin;

     struct tcp_cli *c;

         int new_sock;

     int len;

        struct tcp_srv *s = calloc(1, sizeof(struct tcp_srv));

 

        s->epfd = srv_main->epfd;

   

     s->sock = socket(AF_INET, SOCK_STREAM, 0);

    addr.sin_addr.s_addr = INADDR_ANY;

    addr.sin_family = AF_INET;

    addr.sin_port = htons (DEF_TCP_SRV_PORT);

    bind(s->sock, (struct sockaddr*)&addr, sizeof(struct sockaddr));

    listen(s->sock, 5); 

       new_sock = accept(s->sock, (struct sockaddr*)&sin, &len);

      c = calloc(1, sizeof(struct tcp_cli));

      c->sock = new_sock;

      memcpy(&c->addr, &sin, len);

      c->srv = s;

//      c->req_total = FRAME_HDR_SZ;

      

      c->ev_rx = epoll_event_create(c->sock,EPOLLIN,rx_app_handler, c);

      c->ev_tx = epoll_event_create(c->sock,EPOLLOUT,tx_app_handler, c);

 

       epoll_add_event(c->srv->epfd, c->ev_rx);

 

    //return s;

    

       srv_main->srv = s;

      

       return 0;

 

在linclude文件夹中的,net.h中的内容是;

#ifndef __TCP_SRV_H__

#define __TCP_SRV_H__

#include <netinet/in.h>

#include <protocol.h>

#define DEF_TCP_SRV_PORT        19868

struct tcp_srv

    int                     sock;          

    int                     epfd;

    void                    *arg;          

;

 

struct tcp_cli

    int                     sock;          

    struct sockaddr_in      addr;                   

    struct event_ext *             ev_rx;         

    struct event_ext *             ev_tx;         

    char                    *buf;          

    int                     len;           

    struct tcp_srv          *srv;  

    unsigned char        req[FRAME_MAX_SZ];

    unsigned char        rsp[FRAME_MAX_SZ + VID_FRAME_MAX_SZ];       

;

#endif

 

采集端与播放器之间进行通信,肯定是基于一定的协议的,我们使用的最简单的方法就是“申请”,即发送一个请求,回复一个请求,我们为了完成这个内容,在camera.c中做如下的修改:

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/epoll.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <linux/types.h>

#include <linux/videodev2.h>

 

#include <main.h>

 

struct buf

    void *start;

    int len;

;

 

struct cam

    struct v4l2_dev *v4_dev;

    struct buf              tran_frm;           /* frame to transfer */

    __u32                   tran_frm_max_size;

   

;

 

 

struct v4l2_dev

    int fd;

    __u8 name[32];

    __u8 drv[16];

    struct buf *buf;

    struct event_ext *ev;

   

    void*                   arg;

;

 

static void handle_jpeg_img_proc(const void *p, int size, void *arg)

    struct cam *v = arg;

 

    v->tran_frm.start = (void*)p;

    v->tran_frm.len   = size;

 

/*事件处理函数*/

void cam_handler(int fd,void *arg)

     struct v4l2_buffer buf;

     struct v4l2_dev *v = arg;

     int file_fd;

    

     file_fd = open("test.jpg",O_RDWR|O_CREAT,0777);

   

     /*帧出列*/

     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     buf.memory = V4L2_MEMORY_MMAP;

     ioctl (v->fd, VIDIOC_DQBUF, &buf);

 

     handle_jpeg_img_proc(v->buf[buf.index].start, buf.bytesused, v->arg);

  

     /*buf入列*/

     ioctl(v->fd, VIDIOC_QBUF, &buf); 

 

struct v4l2_dev * v4l2_init()

     struct v4l2_capability cap;

     struct v4l2_dev *v;

     struct v4l2_format fmt;

     struct v4l2_requestbuffers req;

     int i;

     struct v4l2_buffer buf; 

 

    

     //1. 初始化摄像头

     v = calloc(1,sizeof(struct v4l2_dev));

     v->fd = open("/dev/video3",O_RDWR|O_NONBLOCK);

    

     //1.1 获取驱动信息

     ioctl (v->fd, VIDIOC_QUERYCAP, &cap);

    

     if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))

    

          printf("this is not a video device\n");

          return -1; 

    

     strcpy((char *)v->name,(char *)cap.card);

     strcpy((char *)v->drv,(char *)cap.driver);

    

     //1.2 设置图像格式

     fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     fmt.fmt.pix.width       = 1280;

     fmt.fmt.pix.height      = 1024;

     fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;

     ioctl (v->fd, VIDIOC_S_FMT, &fmt) ;

     //1.3 申请图像缓冲区

     req.count               = 4;

     req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     req.memory              = V4L2_MEMORY_MMAP;

     ioctl (v->fd, VIDIOC_REQBUFS, &req);

     //1.4 把内核空间中的图像缓冲区映射到用户空间

     v->buf = calloc(4,sizeof(struct buf));

    

     for (i = 0; i < req.count; i++)

    

           /*获取图像缓冲区的信息*/

           buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

           buf.memory      = V4L2_MEMORY_MMAP;

           buf.index       = i;

           ioctl (v->fd, VIDIOC_QUERYBUF, &buf);

            

           v->buf[i].len = buf.length;

           // 把内核空间中的图像缓冲区映射到用户空间

          v->buf[i].start = mmap (NULL ,    //通过mmap建立映射关系

                                        buf.length,

                                        PROT_READ | PROT_WRITE ,

                                        MAP_SHARED ,

                                        v->fd,

                                        buf.m.offset);

    

     //1.5 图像缓冲入队

       for (i = 0; i < 4; ++i)

      

               buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

               buf.memory      = V4L2_MEMORY_MMAP;

               buf.index       = i;

               ioctl (v->fd, VIDIOC_QBUF, &buf);    

      

     //2. 注册事件到epoll

     v->ev = epoll_event_create(v->fd,EPOLLIN,cam_handler,v);

     epoll_add_event(srv_main->epfd,v->ev);

     return v;

   

void v4l2_start_capture(struct v4l2_dev *v)

    enum v4l2_buf_type type;

   

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ioctl (v->fd, VIDIOC_STREAMON, &type);

struct cam * cam_sys_init()

     struct cam *cam = calloc(1,sizeof(struct cam));

     //1. 初始化

     cam->v4_dev = v4l2_init();

     cam->v4_dev->arg = cam;

     //2. 开始采集

     v4l2_start_capture(cam->v4_dev);  

     //3. 第2课加入

     return cam;

void cam_get_fmt(struct cam *v, __u8 *rsp)

    __u32 fmt = V4L2_PIX_FMT_JPEG;

    memcpy(rsp, &fmt, 4);

__u32 cam_get_trans_frame(struct cam *v, __u8 *rsp)

    memcpy(rsp, v->tran_frm.start, v->tran_frm.len);

    return v->tran_frm.len;

int process_incoming(struct tcp_cli * c)

    struct cam      *v      = srv_main->cam;

    __u8            *req    = c->req;

    __u8            *rsp    = c->rsp;

    __u8            id      = req[CMD1_POS];

    __u8            status  = ERR_SUCCESS;

    __u8            data[FRAME_DAT_MAX];

    __u32           pos, len, size;

    switch (id)

    case REQUEST_ID(VID_GET_FMT):

        cam_get_fmt(v, data);       

        build_ack(rsp, (TYPE_SRSP << TYPE_BIT_POS) | SUBS_VID, id, 4, data);

        net_send(c, rsp, 4 + FRAME_HDR_SZ);

        break;

    case REQUEST_ID(VID_REQ_FRAME):

        pos = FRAME_HDR_SZ + 4;

        size = cam_get_trans_frame(v, &rsp[pos]);   

        build_ack(rsp, (TYPE_SRSP << TYPE_BIT_POS) | SUBS_VID, id, 4, (__u8*)&size);

        net_send(c, rsp, pos + size);

        break;

    default:

        status = ERR_CMD_ID;

        break;

   

    return status;

 

 

为了完成申请内容,我们编写protocol.h,放在include文件夹中,内容如下:

 

 

#ifndef __PROTOCOL_H__

#define __PROTOCOL_H__

 

#define VID_FRAME_MAX_SZ    (0xFFFFF - FRAME_MAX_SZ)

 

#define FRAME_MAX_SZ    253

#define FRAME_DAT_MAX   253

#define FRAME_HDR_SZ    3

 

#define FRAME_ERR_SZ    3

 

#define TYPE_MASK       0xE0

#define TYPE_BIT_POS    5

#define SUBS_MASK       0x1F

 

#define LEN_POS         0

#define CMD0_POS        1

#define CMD1_POS        2

#define DAT_POS         3

 

/* Error codes */

#define ERR_SUCCESS     0   /* success */

#define ERR_SUBS        1   /* invalid subsystem */

#define ERR_CMD_ID      2   /* invalid command ID */

#define ERR_PARAM       3   /* invalid parameter */

#define ERR_LEN         4   /* invalid length */

 

#define TYPE_SREQ    0x1

#define TYPE_SRSP     0x2

 

   

#define SUBS_ERR          0x0

#define SUBS_SYS          0x1

#define SUBS_VID          0x3

#define SUBS_MAX          0x4

   

   

#define REQUEST(len, type, subs, id)   (((len) << (8*LEN_POS)) | \

    (((type) << TYPE_BIT_POS | (subs)) << (8*CMD0_POS)) | ((id) << (8*CMD1_POS)))

   

 

enum request

 

    SYS_VERSION      =   REQUEST(0x0, TYPE_SREQ, SUBS_SYS, 0x0),

 

    /**

     * VID SubSystem

     */

    VID_GET_UCTLS =   REQUEST(0x0, TYPE_SREQ, SUBS_VID, 0x0),

    VID_GET_UCTL =   REQUEST(0x4, TYPE_SREQ, SUBS_VID, 0x1),

    VID_SET_UCTL =   REQUEST(0x8, TYPE_SREQ, SUBS_VID, 0x2),

    VID_SET_UCS2DEF  =   REQUEST(0x0, TYPE_SREQ, SUBS_VID, 0x3),

 

    VID_GET_FRMSIZ   =   REQUEST(0x0, TYPE_SREQ, SUBS_VID, 0x10),

    VID_GET_FMT      =   REQUEST(0x0, TYPE_SREQ, SUBS_VID, 0x11),

 

    VID_REQ_FRAME =   REQUEST(0x0, TYPE_SREQ, SUBS_VID, 0x20),

;

 

#define REQUEST_ID(req)     (((req) >> (8*CMD1_POS)) & 0xFF)

#define REQUEST_TYPE(req)   (((req) >> (8*CMD0_POS + TYPE_BIT_POS)) & TYPE_MASK)

#define REQUEST_SUBS(req)   (((req) >> (8*CMD0_POS)) & SUBS_MASK)

#define REQUEST_LEN(req)    (((req) >> (8*LEN_POS)) & 0xFF)

#endif

 

 

我们也对应的修改main.h中的内容,如下:

#include <stdio.h>

#include <net.h>

struct server

    int epfd; //指向epoll

    struct cam *cam; //指向采集子系统

    struct tcp_srv *srv; //指向传输子系统

    struct cfg* cfg; //指向配置子系统

;

int process_incoming(struct tcp_cli * c);

 

struct server *srv_main;

 

我们重新编写的camera.c文件如下;

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/epoll.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <linux/types.h>

#include <linux/videodev2.h>

 

#include <main.h>

 

struct buf

    void *start;

    int len;

;

 

struct cam

    struct v4l2_dev *v4_dev;

    struct buf              tran_frm;           /* frame to transfer */

    __u32                   tran_frm_max_size;

   

;

 

 

struct v4l2_dev

    int fd;

    __u8 name[32];

    __u8 drv[16];

    struct buf *buf;

    struct event_ext *ev;

   

    void*                   arg;

;

 

static void handle_jpeg_img_proc(const void *p, int size, void *arg)

    struct cam *v = arg;

 

    v->tran_frm.start = (void*)p;

    v->tran_frm.len   = size;

 

/*事件处理函数*/

void cam_handler(int fd,void *arg)

     struct v4l2_buffer buf;

     struct v4l2_dev *v = arg;

     int file_fd;

    

     file_fd = open("test.jpg",O_RDWR|O_CREAT,0777);

   

     /*帧出列*/

     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     buf.memory = V4L2_MEMORY_MMAP;

     ioctl (v->fd, VIDIOC_DQBUF, &buf);

 

     handle_jpeg_img_proc(v->buf[buf.index].start, buf.bytesused, v->arg);

  

     /*buf入列*/

     ioctl(v->fd, VIDIOC_QBUF, &buf); 

 

struct v4l2_dev * v4l2_init()

     struct v4l2_capability cap;

     struct v4l2_dev *v;

     struct v4l2_format fmt;

     struct v4l2_requestbuffers req;

     int i;

     struct v4l2_buffer buf; 

 

    

     //1. 初始化摄像头

     v = calloc(1,sizeof(struct v4l2_dev));

     v->fd = open("/dev/video3",O_RDWR|O_NONBLOCK);

    

     //1.1 获取驱动信息

     ioctl (v->fd, VIDIOC_QUERYCAP, &cap);

    

     if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))

    

          printf("this is not a video device\n");

          return -1; 

    

     strcpy((char *)v->name,(char *)cap.card);

     strcpy((char *)v->drv,(char *)cap.driver);

    

     //1.2 设置图像格式

     fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     fmt.fmt.pix.width       = 1280;

     fmt.fmt.pix.height      = 1024;

     fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;

 

     ioctl (v->fd, VIDIOC_S_FMT, &fmt) ;

       

     //1.3 申请图像缓冲区

     req.count               = 4;

     req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;

     req.memory              = V4L2_MEMORY_MMAP;

     ioctl (v->fd, VIDIOC_REQBUFS, &req);

    

     //1.4 把内核空间中的图像缓冲区映射到用户空间

     v->buf = calloc(4,sizeof(struct buf));

    

     for (i = 0; i < req.count; i++)

    

           /*获取图像缓冲区的信息*/

           buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

           buf.memory      = V4L2_MEMORY_MMAP;

           buf.index       = i;

 

           ioctl (v->fd, VIDIOC_QUERYBUF, &buf);

            

           v->buf[i].len = buf.length;

          

           // 把内核空间中的图像缓冲区映射到用户空间

          v->buf[i].start = mmap (NULL ,    //通过mmap建立映射关系

                                        buf.length,

                                        PROT_READ | PROT_WRITE ,

                                        MAP_SHARED ,

                                        v->fd,

                                        buf.m.offset);

    

    

     //1.5 图像缓冲入队

       for (i = 0; i < 4; ++i)

      

               buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;

               buf.memory      = V4L2_MEMORY_MMAP;

               buf.index       = i;

               ioctl (v->fd, VIDIOC_QBUF, &buf);    

      

    

     //2. 注册事件到epoll

     v->ev = epoll_event_create(v->fd,EPOLLIN,cam_handler,v);

     epoll_add_event(srv_main->epfd,v->ev);

    

     return v;

   

 

void v4l2_start_capture(struct v4l2_dev *v)

    enum v4l2_buf_type type;

   

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ioctl (v->fd, VIDIOC_STREAMON, &type);

 

struct cam * cam_sys_init()

     struct cam *cam = calloc(1,sizeof(struct cam));

    

     //1. 初始化

     cam->v4_dev = v4l2_init();

     cam->v4_dev->arg = cam;

    

     //2. 开始采集

     v4l2_start_capture(cam->v4_dev);  

    

     //3. 第2课加入

     return cam;

 

void cam_get_fmt(struct cam *v, __u8 *rsp)

    __u32 fmt = V4L2_PIX_FMT_JPEG;

    memcpy(rsp, &fmt, 4);

 

 

__u32 cam_get_trans_frame(struct cam *v, __u8 *rsp)

    memcpy(rsp, v->tran_frm.start, v->tran_frm.len);

    return v->tran_frm.len;

 

int process_incoming(struct tcp_cli * c)

    struct cam      *v      = srv_main->cam;

    __u8            *req    = c->req;

    __u8            *rsp    = c->rsp;

    __u8            id      = req[CMD1_POS];

    __u8            status  = ERR_SUCCESS;

    __u8            data[FRAME_DAT_MAX];

    __u32           pos, len, size;

 

    switch (id)

 

    case REQUEST_ID(VID_GET_FMT):

        cam_get_fmt(v, data);       

        build_ack(rsp, (TYPE_SRSP << TYPE_BIT_POS) | SUBS_VID, id, 4, data);

        net_send(c, rsp, 4 + FRAME_HDR_SZ);

        break;

 

 

    case REQUEST_ID(VID_REQ_FRAME):

 

        pos = FRAME_HDR_SZ + 4;

   

        size = cam_get_trans_frame(v, &rsp[pos]);

           

        build_ack(rsp, (TYPE_SRSP << TYPE_BIT_POS) | SUBS_VID, id, 4, (__u8*)&size);

        net_send(c, rsp, pos + size);

        break;

 

    default:

        status = ERR_CMD_ID;

        break;

   

   

    return status;

 

 

Sprint4-监控端播放器通讯子系统设计(未完成)

 

第5课-监控端播放器通讯子系统设计

我们在前面的章节完成了采集端的开发,现在我们开始进行视频端的开发。

大纲

v  监控端系统框架分析

v  监控端通讯子系统设计

  • 网络初始化
  • 构建工作线程:发送图形请求、接收一帧图像、提交一帧图像给图形显示子系统

 

1. 监控端系统架构分析

我们运行监控端程序,会弹出一个界面来填写我们需要链接的地址和端口。完成连接后就会将视频显示出来。

 

显示端通过网络连接将信号传递给采集端,采集端将图像传递给监控端。需要做的就是两个子系统的编写:

ü  通讯子系统

ü  图像显示子系统

 

²  监控端通讯子系统设计

我们在sprint4文件夹中创建net.c和cli_main.c两个文件,修改它们的权限:chmod 777

-R ./

我们首先编写cli_main.c,这是我们要编写的主函数,也是总入口

 

Sprint5-监控端播放器显示子系统设计(未完成)

第6课-监控端播放器显示子系统设计

大纲:

GTK图形程序设计

GTK简介

GTK实例分析

移植GTK图形程序

1. GTK图形程序设计

在嵌入式Linux下有很多图形界面系统GUI包括Qt、GTK+、miniGUI等。其中,最优秀的应该是QT,但是GTK+去世最适合新手快速上手的,原因很简单:

GTK+采用C语言,而QT采用C++编程。其中QT是最优秀的图形系统,但是GTK+是相对简单上手的。

我们将纯图形化界面文件(有三个:wacam_win.c,Makefile,子文件夹icons用于存放图形)导进我们的linux系统,输入make编译一下。

 

生成了我们要的wcamclient执行文件,./wcamclient运行程序,出现我们要的连接界面,如下:

 

连接好网址后,显示如下的界面:

 

这里由于我们没有连接,所以显示的是一个纯图形的界面。下面我们看一下,我们的程序,wacam_win.c:

/**gtk编程参考

http://blog.csdn.net/lianghe_work/article/details/47087109

http://blog.csdn.net/exbob/article/details/6932864

http://blog.csdn.net/fykhlp/article/details/5985131

*/

 

#include <gtk/gtk.h>

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <stdbool.h>

#include <unistd.h>

#include <fcntl.h>

#include <time.h>

#include <errno.h>

#include <pthread.h>

#include <sys/types.h>

#include <sys/socket.h>

 

 

#define DEF_CONN_IP   "192.168.1.230"

#define DEF_PORT     "19868"

#define LOGO_IMG          "./icons/webcam_icon.png"

#define LOGO_IMG1         "./icons/wcamclient.png"

 

#define WIN_TITLE             "Web Camera"

#define WIN_ICON         "./icons/icon.png"

 

#define SNAP_BUTTON_IMG       "./icons/snap-icon-small.xpm"

#define SETTING_BUTTON_IMG "./icons/settings.png"

 

#define WCAM_VERSION     "Web Camera 2.0"

#define WCAM_HOMEPAGE     "http://www.enjoylinux.cn"

#define SNAPSHOT_PATH       IMGDIR

 

    #define FRAME_MAX_SZ    253

    #define VID_FRAME_MAX_SZ    (0xFFFFF - FRAME_MAX_SZ)

 

struct entry_win

    GtkWidget   *win;

    GtkWidget   *ip_entry;

    GtkWidget   *port_entry;

    GtkWidget   *connect_button;

    gboolean    connected;

    int         sock;

;

 

typedef struct entry_win *entry_win_t;

 

typedef void (*wcc_img_proc_t)(const void *p, int size, void *arg);

 

struct wcam_cli

    int                     sock;           /* 涓庢湇鍔″櫒閫氫俊鐨勫缁撳瓧 */

    pthread_t               tid;            /* 澶勭悊绾跨▼ */     

    bool                    need_stop;

   

    __u8                    req[FRAME_MAX_SZ];

    __u8                    rsp[FRAME_MAX_SZ + VID_FRAME_MAX_SZ];

 

    wcc_img_proc_t          proc;           /* 鍥惧儚澶勭悊鍑芥暟 */

    void                    *arg;           /* 鍥惧儚澶勭悊鍑芥暟浼犲叆鍙傛暟 */

;

 

typedef struct wcam_cli *wcc_t;

 

 

struct wcam_win

    GtkWidget           *win;

    wcc_t               client;

    entry_win_t         entry_win;

 

    GtkWidget           *video_area;

 

    guint32             video_format;

    guint32             video_width;

    guint32             video_height;

    gboolean            video_fullscreen;

    gboolean            need_snapshot;

 

    //struct wcam_uctls   *uctls;  

 

    gchar               ipaddr[24];     /* format: "ip :port" */

 

    GtkWidget           *fps_label;

    GtkWidget           *frmsize_label;

    guint32             frm_cnt;

    guint64             last_twenty_frm_us;

 

    GtkWidget           *info_area;

    GtkWidget           *button_area;

    GtkWidget           *control_area_button;

    GtkWidget           *control_area;

 

;

 

__u64 clock_get_time_us()

    struct timespec ts;

    clock_gettime(CLOCK_REALTIME, &ts);

    return (__u64)ts.tv_sec * 1000000LL + (__u64)ts.tv_nsec / 1000LL;

 

void draw_video_frame(const void *p, int size, void *arg)

    struct wcam_win *c      = arg;

    GdkPixbufLoader *pixbuf_loader;

    GdkPixbuf       *pixbuf;

    GdkPixbuf       *pixbuf4fullscreen = NULL;

    GdkScreen       *screen;

    cairo_t         *cr;

    gchar           outstr[100];

    gfloat          fps;

    guint64         now_us;

 

    gdk_threads_enter();

   

    /* update frame size label */

    sprintf(outstr, "%d 瀛楄妭", size);

    gtk_label_set_text(GTK_LABEL(c->frmsize_label), outstr);

 

    /* update fps label */

    c->frm_cnt++;

    if (c->frm_cnt == 1)

        c->last_twenty_frm_us = clock_get_time_us();

    else if (c->frm_cnt > 10)

        now_us = clock_get_time_us();

        fps = 10.0 * 1000000.0 / (now_us - c->last_twenty_frm_us);

        c->frm_cnt = 0;

        sprintf(outstr, "%2.1f 甯?绉?, fps);

        gtk_label_set_text(GTK_LABEL(c->fps_label), outstr);

   

 

    /* update video */

    pixbuf_loader = gdk_pixbuf_loader_new();       

    gdk_pixbuf_loader_write(pixbuf_loader, p, size, NULL);

    gdk_pixbuf_loader_close(pixbuf_loader, NULL);

    pixbuf = gdk_pixbuf_loader_get_pixbuf(pixbuf_loader);

 

    /*创建cairo*/

    cr = gdk_cairo_create(c->video_area->window);

   

    /*导入图像*/

    gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);

   

    /*绘制图像*/

    cairo_paint(cr);

    cairo_destroy(cr);

 

    g_object_unref(pixbuf_loader);

    if (pixbuf4fullscreen)

        g_object_unref(pixbuf4fullscreen);

       

    gdk_threads_leave();

 

    if (c->need_snapshot)      /* Maybe we should open a file_save_dialog here */

        char img_path[100];     /* or maybe we should make the image_storage_path Configurable */

        FILE *fp;              

        time_t t;

        struct tm *tmp;

        t = time(NULL);

        tmp = localtime(&t);

 

        strftime(outstr, sizeof(outstr), "%F-%T", tmp);

        printf(SNAPSHOT_PATH"/webcam-%s.jpg\n", outstr);

        printf(SNAPSHOT_PATH"/webcam-%s.jpg", outstr);

        sprintf(img_path, SNAPSHOT_PATH"/webcam-%s.jpg", outstr);

        fp = fopen(img_path, "w");       

        fwrite(p, size, 1, fp);

        fclose(fp);

        c->need_snapshot = FALSE;

   

 

static void connect_handler(GtkButton *button, gpointer data)

    struct entry_win *c = data;

    const gchar      *ip;

    gint16           port;

   

    /*读取IP和端口*/

    ip   = gtk_entry_get_text(GTK_ENTRY(c->ip_entry));

    port = atoi(gtk_entry_get_text(GTK_ENTRY(c->port_entry)));

   

        c->connected = TRUE;

        gtk_main_quit();  

 

static gboolean logo_draw(struct entry_win *c, GtkWidget* box)

    gtk_box_pack_start_defaults(GTK_BOX(box), gtk_image_new_from_file(LOGO_IMG));

    gtk_box_pack_start_defaults(GTK_BOX(box), gtk_image_new_from_file(LOGO_IMG1));

    return TRUE;

 

static gboolean entry_area_draw(struct entry_win *c, GtkWidget* box)

    GtkWidget *label;

    GtkWidget *entry;

 

        /*创建标签*/

    label = gtk_label_new("Server Ip:");

    gtk_box_pack_start_defaults(GTK_BOX(box), label);

   

    /*创建文本输入框*/

    entry = gtk_entry_new();

    gtk_entry_set_text(GTK_ENTRY(entry), DEF_CONN_IP);

    gtk_entry_set_max_length(GTK_ENTRY(entry), 15);

 

    gtk_box_pack_start_defaults(GTK_BOX(box), entry);

        c->ip_entry = entry; 

   

        /*创建标签*/

    label = gtk_label_new("Server Port:");

        gtk_box_pack_start_defaults(GTK_BOX(box), label);

       

        /*创建文本输入框*/

    entry = gtk_entry_new();

    gtk_entry_set_text(GTK_ENTRY(entry), DEF_PORT);

    gtk_entry_set_max_length(GTK_ENTRY(entry), 5);

 

    gtk_box_pack_start_defaults(GTK_BOX(box), entry);  

    c->port_entry = entry;

 

    return TRUE;

 

static gboolean button_area_draw(struct entry_win *c, GtkWidget* box)

    GtkWidget *button;

   

    /* 创建带文字的按钮 */

    button = gtk_button_new_with_label("OK"); 

    gtk_box_pack_start_defaults(GTK_BOX(box), button);

    c->connect_button = button;

   

    /*关联按钮点击事件*/

    g_signal_connect(button, "clicked", G_CALLBACK(connect_handler), c);     

 

    /* 创建带文字的按钮 */

    button = gtk_button_new_with_label("Cancel"); 

    gtk_box_pack_start_defaults(GTK_BOX(box), button);

   

    g_signal_connect(button, "clicked", G_CALLBACK(gtk_main_quit), NULL);

 

    return TRUE;

 

 

static gboolean entry_win_draw_face(struct entry_win *c)

    GtkWidget *vbox;

    GtkWidget *hbox;

 

    /*创建垂直布局容器*/

    vbox = gtk_vbox_new(FALSE, 5);

    gtk_container_add(GTK_CONTAINER(c->win), vbox);         

   

    /*创建水平布局容器*/

    hbox = gtk_hbox_new(FALSE, 5);

    gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);

   

    /*绘制logo*/

    logo_draw(c, hbox);

 

    /*输入ip和端口*/

    hbox = gtk_hbox_new(FALSE, 5);

    gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);

    entry_area_draw(c, hbox);

 

    /*创建水平按键布局容器*/

    hbox = gtk_hbutton_box_new();

    /*设置按钮间歇*/

    gtk_box_set_spacing(GTK_BOX(hbox), 5);

    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);  

    gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);

    button_area_draw(c, hbox);

 

    return TRUE;

 

static gboolean

key_press_func(GtkWidget* widget, GdkEventKey* key, gpointer data)

#define KEY_ENTER 0xff0d

#define KEY_ESC   0xff1b

    struct entry_win *c = data;

    if (KEY_ENTER == key->keyval)

        connect_handler(GTK_BUTTON(c->connect_button), c);

        return TRUE;

    else if (KEY_ESC == key->keyval)

        c->connected = FALSE;

        gtk_main_quit();

        return TRUE;

   

    return FALSE;

#undef  KEY_ENTER

#undef  KEY_ESC

 

entry_win_t login_create()

    struct entry_win *c = calloc(1, sizeof(struct entry_win));

    if (!c)

        perror("entry_win_create");

        return NULL;

   

 

    /*---创建新窗口---*/

    c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);    

 

    /*---设置窗口标题---*/

    gtk_window_set_title(GTK_WINDOW(c->win), WIN_TITLE);

   

    /*---设置窗口图标---*/             

    gtk_window_set_icon(GTK_WINDOW(c->win), gdk_pixbuf_new_from_file(WIN_ICON, NULL));

   

    /*---设置窗口位置---*/ 

    gtk_window_set_position(GTK_WINDOW(c->win), GTK_WIN_POS_CENTER);

   

    /*---设置窗口是否可伸缩---*/

    gtk_window_set_resizable(GTK_WINDOW(c->win), FALSE);   

   

    /*---设置窗口边框宽度---*/

    gtk_container_set_border_width(GTK_CONTAINER(c->win), 0);

   

    /*---关联退出信号与处理函数---*/

    g_signal_connect(GTK_OBJECT(c->win), "destroy", G_CALLBACK(gtk_main_quit), NULL);

   

    /*---关联按键信号与处理函数---*/

    g_signal_connect(G_OBJECT(c->win), "key_press_event", G_CALLBACK(key_press_func), c);

   

    entry_win_draw_face(c);

   

    gtk_widget_show_all(c->win);

    return c;

 

int login_run(entry_win_t win)

    struct entry_win *c = win;

    /*启动该窗口*/

    gtk_main();

    return c->connected == TRUE ? 0 : -1;

 

void login_hide(entry_win_t win)

    struct entry_win *c = win;

    gtk_widget_hide_all(c->win);

 

 

 

/*------------主窗口-------------------*/

 

int entry_win_get_ip(entry_win_t win, char *ip)

    struct entry_win *c = win;

    if (c->connected == FALSE)

        return -1;

    strcpy(ip, gtk_entry_get_text(GTK_ENTRY(c->ip_entry)));

    return 0;

 

int entry_win_get_port(entry_win_t win, char *port)

    struct entry_win *c = win;

    if (c->connected == FALSE)

        return -1;

    strcpy(port, gtk_entry_get_text(GTK_ENTRY(c->port_entry)));

    return 0;

 

void main_quit(GtkWidget *Object, gpointer data)

    gtk_main_quit();

 

static gboolean

draw_area_draw(struct wcam_win *c, GtkWidget *box)

    /*创建图像显示区域*/

    c->video_area = gtk_drawing_area_new();

    gtk_widget_set_size_request(c->video_area, c->video_width, c->video_height);

    gtk_box_pack_start(GTK_BOX(box), c->video_area, FALSE, FALSE, 0);

   

    gtk_widget_add_events(c->video_area, GDK_BUTTON_PRESS_MASK);

 

    return TRUE;

 

static gboolean

info_area_draw(struct wcam_win *c, GtkWidget *box)

    GtkWidget   *frame;

    GtkWidget   *table;

    GtkWidget   *label;

    GtkWidget   *align;

    GtkWidget   *separator;

    gchar       buf[256];

 

    frame = gtk_frame_new("淇℃伅鍖?);

    c->info_area = frame;

    gtk_box_pack_start_defaults(GTK_BOX(box), frame);

 

    table = gtk_table_new(9, 2, FALSE);

    gtk_container_add(GTK_CONTAINER(frame), table);

   

    /* VERSION & HOMEPAGE */

    label = gtk_label_new("涓婚〉:");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    label = gtk_link_button_new_with_label(WCAM_HOMEPAGE, "www.enjoylinux.cn");

    align = gtk_alignment_new(0, 0, 0, 0);              /* 宸﹀榻?*/

    gtk_container_add(GTK_CONTAINER(align), label);

    gtk_table_attach(GTK_TABLE(table), align, 1, 2, 0, 1,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    label = gtk_label_new("鐗堟湰:");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    label = gtk_label_new(WCAM_VERSION);

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 1, 2,

                     GTK_FILL, GTK_SHRINK, 8, 1);

 

    /* IP & PORT */

    label = gtk_label_new("鏈嶅姟鍣?");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    label = gtk_label_new(c->ipaddr);

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 2, 3,

                     GTK_FILL, GTK_SHRINK, 8, 1);

 

    separator = gtk_hseparator_new();

    gtk_table_attach(GTK_TABLE(table), separator, 0, 1, 3, 4,

                     GTK_FILL, GTK_SHRINK, 1, 1);

    separator = gtk_hseparator_new();

    gtk_table_attach(GTK_TABLE(table), separator, 1, 2, 3, 4,

                     GTK_FILL | GTK_EXPAND, GTK_SHRINK, 1, 1);

 

    /* Frame Format */

    label = gtk_label_new("甯ф牸寮?");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    sprintf(buf, "%c%c%c%c", (c->video_format)&0xFF,

                             (c->video_format>>8)&0xFF,

                             (c->video_format>>16)&0xFF,

                             (c->video_format>>24)&0xFF);

    label = gtk_label_new(buf);

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 4, 5,

                     GTK_FILL, GTK_SHRINK, 8, 1);

 

    /* Frame width x height */

    label = gtk_label_new("甯у昂瀵?");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    sprintf(buf, "%d x %d", c->video_width, c->video_height);

    label = gtk_label_new(buf);

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 5, 6,

                     GTK_FILL, GTK_SHRINK, 8, 1);

 

    /* Frame Size */

    label = gtk_label_new("甯уぇ灏?");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 6, 7,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    label = gtk_label_new("0");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 6, 7,

                     GTK_FILL, GTK_SHRINK, 8, 1);

    c->frmsize_label = label;

 

    /* FPS */

    label = gtk_label_new("甯ч€熺巼:");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 0, 1, 7, 8,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    label = gtk_label_new("0");

    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);

    gtk_table_attach(GTK_TABLE(table), label, 1, 2, 7, 8,

                     GTK_FILL, GTK_SHRINK, 8, 1);

    c->fps_label = label;

 

    /* hseparator */

    separator = gtk_hseparator_new();

    gtk_table_attach(GTK_TABLE(table), separator, 0, 1, 8, 9,

                     GTK_FILL, GTK_SHRINK, 1, 1);

    separator = gtk_hseparator_new();

    gtk_table_attach(GTK_TABLE(table), separator, 1, 2, 8, 9,

                     GTK_FILL, GTK_SHRINK, 1, 1);

 

    return TRUE;

 

static gboolean main_button_area_draw(struct wcam_win *c, GtkWidget *box)

    GtkWidget *buttonbox;    

    GtkWidget *button;      

    GtkWidget *hbox;    

    GtkWidget *label;

    GtkWidget *image;

 

    buttonbox = gtk_hbutton_box_new();

    gtk_box_pack_start(GTK_BOX(box), buttonbox, FALSE, FALSE, 0);

    c->button_area = buttonbox;

    /* control button */

    image = gtk_image_new_from_file(SETTING_BUTTON_IMG);

    label = gtk_label_new("鏄剧ず鎺у埗椤?");

    hbox = gtk_hbox_new(FALSE, 5);

    gtk_box_pack_start_defaults(GTK_BOX(hbox), image);

    gtk_box_pack_start_defaults(GTK_BOX(hbox), label);

 

    button = gtk_check_button_new();  

    gtk_container_add(GTK_CONTAINER(button), hbox);

    c->control_area_button = button;

   

    gtk_box_pack_start_defaults(GTK_BOX(buttonbox), button);

   

    /* snapshot button */

    image = gtk_image_new_from_file(SNAP_BUTTON_IMG);

    label = gtk_label_new("蹇収");

    hbox  = gtk_hbox_new(FALSE, 5);

    gtk_box_pack_start_defaults(GTK_BOX(hbox), image);

    gtk_box_pack_start_defaults(GTK_BOX(hbox), label);

 

    button = gtk_button_new();

    gtk_container_add(GTK_CONTAINER(button), hbox);

   

    gtk_box_pack_start_defaults(GTK_BOX(buttonbox), button);

 

 

    return TRUE;

 

static gboolean main_win_draw_face(struct wcam_win *c)

    GtkWidget *box;

    GtkWidget *hbox;

    GtkWidget *hseparator;

 

    box = gtk_vbox_new(FALSE, 5);

    gtk_container_add(GTK_CONTAINER(c->win), box);         

 

    /* draw_area & info_area */

    hbox = gtk_hbox_new(FALSE, 5);

    gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);

    draw_area_draw(c, hbox);

    info_area_draw(c, hbox); 

 

    /* hseparator */

    hseparator=gtk_hseparator_new();

    gtk_box_pack_start(GTK_BOX(box), hseparator, FALSE, TRUE, 0);

   

    /* button_area */

    main_button_area_draw(c, box);

 

    /* control_area */

   // control_area_draw(c, box);

 

    return TRUE;

 

static gboolean main_create(struct wcam_win *c)

    int len;

    /*------------初始化----------------*/

    c->video_width = 640;

    c->video_height = 480;

 

    /* fullscreen */

    c->video_fullscreen = FALSE;

   

    /* ip & port */

    entry_win_get_ip(c->entry_win, c->ipaddr);

 

    len = strlen(c->ipaddr);

 

    c->ipaddr[len] = ‘:‘;

    entry_win_get_port(c->entry_win, &c->ipaddr[len+1]);

   

   

    c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); 

   

    /* 璁剧疆绐楀彛鏍囬 */

    gtk_window_set_title(GTK_WINDOW(c->win), WIN_TITLE);               

 

    gtk_window_set_icon(GTK_WINDOW(c->win),

                        gdk_pixbuf_new_from_file(WIN_ICON, NULL));

   

    /* 绐楀彛杈规澶у皬璁剧疆涓? */

    gtk_container_set_border_width(GTK_CONTAINER(c->win), 0);

   

    g_signal_connect(c->win, "destroy",

                     G_CALLBACK(main_quit), c);

    gtk_widget_set_app_paintable(c->win, TRUE);

 

    main_win_draw_face(c);

 

    gtk_widget_show_all(c->win);

    //toggle_control_area(GTK_TOGGLE_BUTTON(c->control_area_button), c);

   

    gtk_widget_hide(c->win);

    gtk_window_set_position(GTK_WINDOW(c->win), GTK_WIN_POS_CENTER);

    gtk_widget_show(c->win);

 

    return TRUE;

 

void main_run( )

    gtk_main();

 

gint main(gint argc,gchar* argv[])

  

    int res;

   

    /*---GTK初始化----*/

    gtk_init(&argc, &argv);  

    g_thread_init(NULL);

    gdk_threads_init();

 

    struct wcam_win *c = calloc(1, sizeof(struct wcam_win));

 

    c->entry_win = login_create();

   

    res = login_run(c->entry_win);

    if (res == -1)

        goto err_win;

   

   

    login_hide(c->entry_win);

 

    main_create(c);

   

    main_run();

 

err_win:

    free(c->entry_win);

    free(c);

 

    return 0;

 

它的大致步骤是,先初始化GTK程序,再创建窗体。

 

2. QT移植以及程序

现在我们的界面已经编辑完成,我们要做的是使得各个部分的按键具有对应的功能。

(1)首先是登录的界面的“ok”按键,当我们按这个按键的时候,应该进行网络的连接;(2)再就是我们的主界面,没有视频的传输。

 

我们现在所缺少的内容在,我们之前编程的过程中都是实现了的。

 

以上是关于项目三. 基于图像压缩的视频监控系统的主要内容,如果未能解决你的问题,请参考以下文章

基于视频压缩的实时监控系统

嵌入式-项目1-基于视频压缩的实时监控系统-1.EPOLL框架

基于SocketOpenCV和MFC实现的网络实时视频监控

基于Matlab通用视频处理系统的设计-含Matlab代码

调用opencv的函数cvCreateVideoWriter生成的视频太大(几G),怎么生成小的视频?

20155114许星霖《远程安防监控系统》课程设计个人报告