您的位置:首页 > 运维架构

[V4L2+RTP] FireWRT远程视频监控-2

2016-01-24 00:57 549 查看


[V4L2+RTP] FireWRT远程视频监控-2

发表于 2015-3-26 10:01:02 浏览:1193 | 回复:5 打印 只看该作者 [复制链接]楼主

本帖最后由 代祥军 于 2015-4-16 09:13 编辑

1.准备

涉及的知识

V4L2采集视频数据

YUYV转化JPEG

RTP打包JPEG图片数据

socket发送

程序调用流程:

main.c:

usage->init_sock/*判断参数,初始化套接字*/

/*初始化V4L2*/

start_engine

-->cam_init /*给struct cam 分配内存,并且初始化cam结构体*/

--> v4l2_init

-->open_camera /*打开摄像头*/

-->init_camera /*初始化摄像头*/

-->start_capturing /*开始采集图像*/

-->create_cam_pthread

-->pthread_create:capture_encode_thread 创建线程

-->capture_encode_thread

-->while: 循环

-->read_frame 读一帧数据

--> jpeg_rtp 如果采集的数据时jpeg直接rtp打包发送

-->send_jpeg_rtp_for_fmt_mjpeg 设rtp某些动态参数

-->SendFrame rtp打包 socket发送VLC

-->jpeg_encode_yuyv422_rtp 如果采集的视频数据时yuyv先转化为jepg在rtp打包发送

-->yuyv422torgb 将yuyv数据装化为RGB

-->encode_rgb_to_jpeg_mem RGB转化为jpeg放到内存

-->send_jpeg_rtp 设置rtp参数

-->SendFrame rtp打包数据发送

当按下ctrl+c时:stop_engine()

engine.c:

2.V4L2采集视频数据

video_capture.c

涉及的结构体:

struct buffer{

void *start;

size_t length;

};

struct camera{

char *device_name;//设备名字 /dev/videoX

int fd;//摄像头句柄

int width;//输出的视频宽度

int height;//高度

int display_depth;

int image_size;//一帧数据大小

int frame_number;

int support_fmt;//摄像头支持的格式如果支持Jpeg就以此获得数据,如果只支持yuyv就输出yuyv在用程序转化为jpeg

unsigned int n_buffers;//缓冲区个数

struct v4l2_capability v4l2_cap;

struct v4l2_cropcap v4l2_cropcap;

struct v4l2_format v4l2_fmt;

struct v4l2_crop v4l2_crop;

struct v4l2_fmtdesc v4l2_fmtdesc;

struct v4l2_streamparm v4l2_setfps;

struct buffer *buffers;

};

/*上面参数fmt_select选择*/

#define FMT_JPEG 0x101

#define FMT_YUYV422 0x102

#define FMT_YUYV420 0x104

复制代码
涉及的函数:




/*外界接口*/

void v4l2_init(struct camera *cam)

{

open_camera(cam);

init_camera(cam);

start_capturing(cam);

}

复制代码
2.1 open_camera 打开摄像头

/*摄像头操作函数*/

static void open_camera(struct camera *cam)

{

struct stat st;

if (-1 == stat(cam->device_name, &st)) {

fprintf(stderr, "Cannot identify '%s': %d, %s\n", cam->device_name,errno, strerror(errno));

exit(EXIT_FAILURE);

}

if (!S_ISCHR(st.st_mode)) {

fprintf(stderr, "%s is no device\n", cam->device_name);

exit(EXIT_FAILURE);

}

cam->fd = open(cam->device_name, O_RDWR, 0); // | O_NONBLOCK 打开摄像头 记录fd

if (-1 == cam->fd) {

fprintf(stderr, "Cannot open '%s': %d, %s\n", cam->device_name, errno,strerror(errno));

exit(EXIT_FAILURE);

}

}

复制代码

2.2 init_camera 初始化摄像头

static void init_camera(struct camera*cam)

{

struct v4l2_capability *cap = &(cam->v4l2_cap);

struct v4l2_cropcap *cropcap = &(cam->v4l2_cropcap);

struct v4l2_crop *crop = &(cam->v4l2_crop);

struct v4l2_format *fmt = &(cam->v4l2_fmt);

struct v4l2_fmtdesc *fmtdesc = &(cam->v4l2_fmtdesc);

struct v4l2_streamparm *streamparm = &(cam->v4l2_setfps);



struct v4l2_requestbuffers req;

unsigned int min;

int pos = -1;

/*1.查询设备属性*/

if (-1 == xioctl(cam->fd, VIDIOC_QUERYCAP, cap)) {

if (EINVAL == errno) {

fprintf(stderr, "%s is no V4L2 device\n", cam->device_name);

exit(EXIT_FAILURE);

}else

errno_exit("VIDIOC_QUERYCAP");

}

/*2.是否支持图像获取*/

if (!(cap->capabilities & V4L2_CAP_VIDEO_CAPTURE)) {

fprintf(stderr, "%s is no video capture device\n", cam->device_name);

exit(EXIT_FAILURE);

}

/*3.查询设备是否支持流输出*/

if (!(cap->capabilities & V4L2_CAP_STREAMING)) {

fprintf(stderr, "%s does not support streaming i/o\n",cam->device_name);

exit(EXIT_FAILURE);

}

#ifdef OUTPUT_CAMINFO

printf("VIDOOC_QUERYCAP\n");

printf("driver:\t\t%s\n",cap->driver);

printf("card:\t\t%s\n",cap->card);

printf("bus_info:\t%s\n",cap->bus_info);

printf("version:\t%d\n",cap->version);

printf("capabilities:\t%x\n",cap->capabilities);

#endif

/*打印摄像头支持的格式*/

fmtdesc->index = 0;

fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

#ifdef OUTPUT_CAMINFO

printf("support format:\n");

#endif

while(ioctl(cam->fd,VIDIOC_ENUM_FMT,fmtdesc) != -1){

#ifdef OUTPUT_CAMINFO

printf("\t%d,%s\n",fmtdesc->index+1,fmtdesc->description);

#endif

/*4.查看摄像头输出的 视频格式 jpeg yuyv420 yuyv422*/

pos = find_string(fmtdesc->description,"JPEG");

if(pos != -1){

cam->support_fmt |= FMT_JPEG;

goto CON;

}

pos = find_string(fmtdesc->description,"4:2:2");

if(pos!=-1){

cam->support_fmt |= FMT_YUYV422;

goto CON;

}

pos = find_string(fmtdesc->description,"4:2:0");

if(pos!=-1){

cam->support_fmt |= FMT_YUYV420;

goto CON;

}

CON:

fmtdesc->index++;

}

/*5.设置输出格式*/

CLEAR(*cropcap);

cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

/*v4l2_cropcap 结构体用来设置摄像头的捕捉能力,在捕捉上视频时应先先设置v4l2_cropcap 的 type 域,再通过 VIDIO_CROPCAP 操作命令获取设备捕捉能力的参数,保存于 v4l2_cropcap 结构体中,包括 bounds(最大捕捉方框的左上角坐标和宽高),defrect(默认捕捉方框的左上角坐标和宽高)等。v4l2_format 结构体用来设置摄像头的视频制式、帧格式等,在设置这个参数时应先填 好 v4l2_format 的各个域,如 type(传输流类型),fmt.pix.width(宽),fmt.pix.heigth(高),fmt.pix.field(采样区域,如隔行采样),fmt.pix.pixelformat(采

样类型,如 YUV4:2:2),然后通过 VIDIO_S_FMT 操作命令设置视频捕捉格式*/

crop->c.width = cam->width;

crop->c.height = cam->height;

crop->c.left = 0;

crop->c.top = 0;

crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;



CLEAR(*fmt);

fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt->fmt.pix.width = cam->width;

fmt->fmt.pix.height = cam->height;

if( (cam->support_fmt & FMT_JPEG) == FMT_JPEG){

fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;

cam->support_fmt = FMT_JPEG;/*记录设备输出的图像格式*/

}

else if((cam->support_fmt & FMT_YUYV422) == FMT_YUYV422){

fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

cam->support_fmt = FMT_YUYV422;

}

else if( (cam->support_fmt & FMT_YUYV420 ) == FMT_YUYV420 ){

fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;

cam->support_fmt = FMT_YUYV420;

}

else

errno_exit("no support fmt");

fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;



if (-1 == xioctl(cam->fd,VIDIOC_S_FMT,fmt))

errno_exit("VIDIOC_S_FMT fail");



#if 1

CLEAR(*streamparm);

streamparm->type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

streamparm->parm.capture.timeperframe.numerator = 1;

streamparm->parm.capture.timeperframe.denominator = 25;

if(-1==xioctl(cam->fd,VIDIOC_S_PARM,streamparm))

errno_exit("VIDIOC_S_PARM");

#endif

min = fmt->fmt.pix.width * 2;/*YUYV 每个像素占用2个字节*/

if (fmt->fmt.pix.bytesperline < min)

fmt->fmt.pix.bytesperline = min;



min = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height;

if (fmt->fmt.pix.sizeimage < min)

fmt->fmt.pix.sizeimage = min;





/*6.分配内存,映射内存*/

CLEAR(req);

req.count = 4;

req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

req.memory = V4L2_MEMORY_MMAP;



if (-1 == xioctl(cam->fd, VIDIOC_REQBUFS, &req)) {

if (EINVAL == errno) {

fprintf(stderr, "%s does not support "

"memory mapping\n", cam->device_name);

exit(EXIT_FAILURE);

} else

errno_exit("VIDIOC_REQBUFS");

}

if (req.count < 2) {

fprintf(stderr, "Insufficient buffer memory on %s\n", cam->device_name);

exit(EXIT_FAILURE);

}

cam->buffers = calloc(req.count, sizeof(*(cam->buffers)));

if (!cam->buffers) {

fprintf(stderr, "Out of memory\n");

exit(EXIT_FAILURE);

}

for(cam->n_buffers = 0;cam->n_buffers < req.count;cam->n_buffers++){

struct v4l2_buffer buf;

CLEAR(buf);

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = cam->n_buffers;

if (-1 == xioctl(cam->fd, VIDIOC_QUERYBUF, &buf))

errno_exit("VIDIOC_QUERYBUF");



cam->buffers[cam->n_buffers].length = buf.length;

/*映射内存*/

cam->buffers[cam->n_buffers].start = mmap(NULL /* start anywhere */,buf.length, PROT_READ | PROT_WRITE /* required */,MAP_SHARED /* recommended */, cam->fd, buf.m.offset);



if (MAP_FAILED == cam->buffers[cam->n_buffers].start)

errno_exit("mmap");

}

}

复制代码
2.3 start_capturing 开始图像采集

static void start_capturing(struct camera *cam)

{

unsigned int i;

enum v4l2_buf_type type;

for (i = 0; i < cam->n_buffers; ++i) {

struct v4l2_buffer buf;

CLEAR(buf);

/*1.将内存放入缓冲区*/

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

buf.index = i;

if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))

errno_exit("VIDIOC_QBUF");

}

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

/*2.开始传输采集图像*/

if (-1 == xioctl(cam->fd, VIDIOC_STREAMON, &type))

errno_exit("VIDIOC_STREAMON");

}

复制代码
2.3 read_frame 读一帧数据

int read_frame(struct camera *cam,unsigned char *buffer,int *len/*数据大小*/)

{

fd_set fds;

struct timeval tv;

int r;

struct v4l2_buffer buf;





FD_ZERO(&fds);

FD_SET(cam->fd,&fds);

tv.tv_sec = 2;

tv.tv_usec = 0;

r = select(cam->fd+1,&fds,NULL,NULL,&tv);/*查询是否有数据*/

if(-1 == r){

if(EINTR == errno)

return 1;//表示应该继续读

errno_exit("select");

}

if(0 == r){

fprintf(stderr,"select timeout");

exit(EXIT_FAILURE);

}



CLEAR(buf);



buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

if (-1 == xioctl(cam->fd, VIDIOC_DQBUF, &buf)) {/*从内核环形缓冲区取出一帧数据*/

switch (errno) {

case EAGAIN:

return -1;

case EIO:

default:

errno_exit("VIDIOC_DQBUF");

}

}

memcpy(buffer,(unsigned char *)cam->buffers[buf.index].start,buf.bytesused);/*将采集的一帧数据放到自定义的buffer中*/

//printf("cam->n_buffers=%d\nbuf.index=%d\nbuf.bytesused=%d\n",cam->n_buffers,buf.index,buf.bytesused);

*len = buf.bytesused;

if (-1 == xioctl(cam->fd, VIDIOC_QBUF, &buf))/*放回环形缓冲区*/

errno_exit("VIDIOC_QBUF");

dbug("video QBUF");

return 0;//成功

}

复制代码

本主题由 暴走的阿Sai 于 2015-8-20 11:26 设置高亮
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: