Linux下的视频传输
利用网线在电脑与开发板之间组成一个局域网,能够让电脑与开发板之间的网络互通,(记着要IP地址固定哟!!)
在电脑上挂载摄像头并作为服务端,开发板在显示屏上显示摄像头所拍摄的画面。
服务端
利用V4L2来采集视频
V4L2 是 Video for linux two 的简称,是 Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范。
- 首先是打开摄像头设备;
/* 初始化摄像头 */
static int v4l2_dev_init(char *path)
{v4l2_fd = open(path, O_RDWR);if(v4l2_fd < 0) {perror("open_video error");exit(1);}return 1;
}
- 枚举出摄像头所支持的所有视频像素格式\采集分辨率\帧率
/* 枚举出摄像头所支持的所有视频像素格式\采集分辨率\帧率 */
static void v4l2_print_formats(void)
{struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum frmsize;struct v4l2_frmivalenum frmival;fmtdesc.index = 0;fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集/*查看支持的像素格式*/while(ioctl(v4l2_fd, VIDIOC_ENUM_FMT, &fmtdesc)==0){printf("fmt: \" %s \" <0x%d>\n", fmtdesc.description, fmtdesc.pixelformat);fmtdesc.index++;frmsize.index = 0;frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;frmsize.pixel_format = fmtdesc.pixelformat;/*查看支持的分辨率*/while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0){printf("frm_size<%d*%d> ", frmsize.discrete.width, frmsize.discrete.height);frmsize.index++;frmival.index = 0;frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;frmival.pixel_format = fmtdesc.pixelformat;frmival.width = frmsize.discrete.width;frmival.height = frmsize.discrete.height;/*查看支持的帧率*/while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0){printf("<%dfps>\n", (frmival.discrete.denominator / frmival.discrete.numerator));frmival.index++;}}printf("\r\n");}
}
- 设置设备的参数,譬如像素格式、 帧大小
/* 设置格式 */
static int v4l2_set_format(unsigned int format, int width, int height)
{struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集fmt.fmt.pix.width = width;//设置分辨率的宽fmt.fmt.pix.height = height;//设置分辨率的高fmt.fmt.pix.pixelformat = format;//设置视频输出格式if(ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt) < 0){printf("Error: v4l2_set_format\r\n");return 0;}/*查询当前摄像头的工作模式*/ioctl(v4l2_fd, VIDIOC_G_FMT, &fmt);printf("width:%d, height:%d \n", fmt.fmt.pix.width, fmt.fmt.pix.height);return 1;
}
- 申请帧缓冲、内存映射
/*申请帧缓冲、内存映射*/
static int v4l2_init_buffer(void)
{struct v4l2_requestbuffers reqbuf;struct v4l2_buffer buf;reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;reqbuf.count = 4; //帧缓冲的数量reqbuf.memory = V4L2_MEMORY_MMAP;/*申请帧缓冲*/if(ioctl(v4l2_fd, VIDIOC_REQBUFS, &reqbuf) < 0){printf("error: v4l2_init_buffer\r\n");return 0;}buf.index = 0;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;/*内存映射*/for(buf.index = 0;buf.index < 4;buf.index++){ioctl(v4l2_fd, VIDIOC_QUERYBUF, &buf);frm_base[buf.index] = mmap(NULL, buf.length, \PROT_READ | PROT_WRITE, MAP_SHARED, \v4l2_fd, buf.m.offset);if (MAP_FAILED == frm_base[buf.index]) {perror("mmap error");return -1;}printf("查询内存成功 buf[%d]==%d长度\r\n",buf.index,buf.length);if(ioctl(v4l2_fd,VIDIOC_QBUF,&buf) < 0){//加入缓冲队列printf("error: VIDIOC_QBUF\r\n");return 0;}}return 1;
}
- 打开摄像头
static int v4l2_stream_on(void)
{enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;/* 打开摄像头、摄像头开始采集数据 */if(ioctl(v4l2_fd, VIDIOC_STREAMON, &type) < 0){printf("error: v4l2_stream_on\r\n");return 0;}printf("Camera_open : success\r\n");return 1;
}
- 读取数据
static void v4l2_read_data(void)
{struct v4l2_buffer buf;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 1;while(1){for(buf.index = 1; buf.index < 4; buf.index++){if(ioctl(v4l2_fd, VIDIOC_DQBUF, &buf)!=0)//出队{printf("提取数据失败\r\n");break;}Send_Video_Data(connfd,frm_base[buf.index],buf.length);if(ioctl(v4l2_fd, VIDIOC_QBUF, &buf)!=0)//入队{printf("放回队列失败\r\n");exit(0);}usleep(33000);}}
}
服务端整体代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #define SERVER_PORT 55555static int sockfd, connfd;
static int v4l2_fd = -1; //摄像头设备文件描述符
static unsigned char *frm_base[4] = {NULL}; //帧缓冲起始地址/* 初始化摄像头 */
static int v4l2_dev_init(char *path)
{v4l2_fd = open(path, O_RDWR);if(v4l2_fd < 0) {perror("open_video error");exit(1);}return 1;
}
/* 枚举出摄像头所支持的所有视频像素格式\采集分辨率\帧率 */
static void v4l2_print_formats(void)
{struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum frmsize;struct v4l2_frmivalenum frmival;fmtdesc.index = 0;fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集/*查看支持的像素格式*/while(ioctl(v4l2_fd, VIDIOC_ENUM_FMT, &fmtdesc)==0){printf("fmt: \" %s \" <0x%d>\n", fmtdesc.description, fmtdesc.pixelformat);fmtdesc.index++;frmsize.index = 0;frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;frmsize.pixel_format = fmtdesc.pixelformat;/*查看支持的分辨率*/while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0){printf("frm_size<%d*%d> ", frmsize.discrete.width, frmsize.discrete.height);frmsize.index++;frmival.index = 0;frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;frmival.pixel_format = fmtdesc.pixelformat;frmival.width = frmsize.discrete.width;frmival.height = frmsize.discrete.height;/*查看支持的帧率*/while(ioctl(v4l2_fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0){printf("<%dfps>\n", (frmival.discrete.denominator / frmival.discrete.numerator));frmival.index++;}}printf("\r\n");}
}
/* 设置格式 */
static int v4l2_set_format(unsigned int format, int width, int height)
{struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//视频采集fmt.fmt.pix.width = width;//设置分辨率的宽fmt.fmt.pix.height = height;//设置分辨率的高fmt.fmt.pix.pixelformat = format;//设置视频输出格式if(ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt) < 0){printf("Error: v4l2_set_format\r\n");return 0;}/*查询当前摄像头的工作模式*/ioctl(v4l2_fd, VIDIOC_G_FMT, &fmt);printf("width:%d, height:%d \n", fmt.fmt.pix.width, fmt.fmt.pix.height);return 1;
}
/*申请帧缓冲、内存映射*/
static int v4l2_init_buffer(void)
{struct v4l2_requestbuffers reqbuf;struct v4l2_buffer buf;reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;reqbuf.count = 4; //帧缓冲的数量reqbuf.memory = V4L2_MEMORY_MMAP;/*申请帧缓冲*/if(ioctl(v4l2_fd, VIDIOC_REQBUFS, &reqbuf) < 0){printf("error: v4l2_init_buffer\r\n");return 0;}buf.index = 0;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;/*内存映射*/for(buf.index = 0;buf.index < 4;buf.index++){ioctl(v4l2_fd, VIDIOC_QUERYBUF, &buf);frm_base[buf.index] = mmap(NULL, buf.length, \PROT_READ | PROT_WRITE, MAP_SHARED, \v4l2_fd, buf.m.offset);if (MAP_FAILED == frm_base[buf.index]) {perror("mmap error");return -1;}printf("查询内存成功 buf[%d]==%d长度\r\n",buf.index,buf.length);if(ioctl(v4l2_fd,VIDIOC_QBUF,&buf) < 0){//加入缓冲队列printf("error: VIDIOC_QBUF\r\n");return 0;}}return 1;
}static int v4l2_stream_on(void)
{enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;/* 打开摄像头、摄像头开始采集数据 */if(ioctl(v4l2_fd, VIDIOC_STREAMON, &type) < 0){printf("error: v4l2_stream_on\r\n");return 0;}printf("Camera_open : success\r\n");return 1;
}void Send_Video_Data(int fd,uint8_t * data,int data_len)
{int tmp = 0;int current = 0;if(fd > 0){while(1){tmp = write(fd,data,data_len);if(tmp < 0){perror("write buf");close("fd");exit(1);}current = data_len - tmp;//还有多少字节没有写入data+=tmp;//指针位移data_len = current;//写入剩下的字节if(data_len == 0){break;}} }
}unsigned char rgbdatabuf[640*480*4]={0};
static void v4l2_read_data(void)
{struct v4l2_buffer buf;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 1;while(1){for(buf.index = 1; buf.index < 4; buf.index++){if(ioctl(v4l2_fd, VIDIOC_DQBUF, &buf)!=0)//出队{printf("提取数据失败\r\n");break;}Send_Video_Data(connfd,frm_base[buf.index],buf.length);if(ioctl(v4l2_fd, VIDIOC_QBUF, &buf)!=0)//入队{printf("放回队列失败\r\n");exit(0);}usleep(33000);}}
}void tcp_server(void)
{struct sockaddr_in server_addr = {0};struct sockaddr_in client_addr = {0};char ip_str[20] = {'\0'};//客户端IPint addrlen = sizeof(struct sockaddr_in);//结构体 struct sockaddr_in 所占字节int ret;/* 打开套接字,得到套接字描述符 */sockfd = socket(AF_INET, SOCK_STREAM, 0);if (0 > sockfd) {perror("socket error");exit(1);}/*配置监听描述符地址复用属性*/ int opt = 1;//opt 只有(关闭(0)/打开(1))ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));if(ret < 0) {perror("setsockopt error");exit(1);}/* 将套接字与指定端口号进行绑定 */server_addr.sin_family = AF_INET;//协议族为IPV4server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定IPserver_addr.sin_port = htons(SERVER_PORT);//绑定端口ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (0 > ret) {perror("bind error");close(sockfd);exit(1);}/* 使服务器进入监听状态 */ret = listen(sockfd, 50);if (0 > ret) {perror("listen error");close(sockfd);exit(1);}/* 阻塞等待客户端连接 */connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);if (0 > connfd) {printf("\n有客户端连接失败\n");exit(1);}printf("有客户端接入...\n");//将二进制 Ipv4 地址转换成 点分十进制inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));printf("客户端主机的 IP 地址: %s\n", ip_str);printf("客户端进程的端口号: %d\n", client_addr.sin_port);
}int main(int argc, char *argv[])
{v4l2_dev_init(argv[1]);v4l2_print_formats();v4l2_set_format(V4L2_PIX_FMT_YUYV, 640, 480);v4l2_init_buffer();v4l2_stream_on();tcp_server();v4l2_read_data();
}
客户端
初始化 LCD
/* 初始化 LCD */
static int fb_dev_init(void)
{struct fb_var_screeninfo fb_var = {0};struct fb_fix_screeninfo fb_fix = {0};unsigned long screen_size;/* 打开framebuffer设备 */fb_fd = open(FB_DEV, O_RDWR);if (0 > fb_fd) {perror("open_fb error");return -1;}/* 获取framebuffer设备信息 */ioctl(fb_fd, FBIOGET_VSCREENINFO, &fb_var);ioctl(fb_fd, FBIOGET_FSCREENINFO, &fb_fix);screen_size = fb_fix.line_length * fb_var.yres;width = fb_var.xres_virtual;height = fb_var.yres_virtual;printf("%d*%d\n", width, height);/* 内存映射 */screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);if (MAP_FAILED == (void *)screen_base) {perror("mmap error");close(fb_fd);return -1;}/* LCD背景刷白 */memset(screen_base, 0xFF, screen_size);return 0;
}
显示图片
void show_pic(unsigned char * pic_data, int w, int h)
{int i,j;unsigned int * p = screen_base;//LCD起始地址unsigned int tmpdata;for(i=0;i<h;i++)//对某一行{for(j=0;j<w;j++)//一行中的一个像素点{//三个十六进制表示一个像素点//j*3表示当前行的第j+1个十六进制的数据//i*w*3表示第i+1行tmpdata = (pic_data[j*3+i*w*3]<<16 | pic_data[j*3+i*w*3+1]<<8 | pic_data[(j*3)+(i*w*3)+2]<<0);* (p+j) = tmpdata;}p+=width;}
}
YUYV422 转RGB 数据
//YUYV422 转RGB 数据
void yuyv_to_rgb(unsigned char *yuyvdata, unsigned char *rgbdata, int w, int h)
{//码流Y0 U0 Y1 V1 Y2 U2 Y3 V3 --》YUYV像素[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]--》RGB像素int r1, g1, b1; int r2, g2, b2;int i =0 ;for(i=0; i<w*h/2; i++){char data[4];memcpy(data, yuyvdata+i*4, 4);unsigned char Y0=data[0];unsigned char U0=data[1];unsigned char Y1=data[2];unsigned char V1=data[3]; //Y0U0Y1V1 -->[Y0 U0 V1] [Y1 U0 V1]r1 = Y0+1.4075*(V1-128); if(r1>255)r1=255; if(r1<0)r1=0;g1 =Y0- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g1>255)g1=255; if(g1<0)g1=0;b1 = Y0 + 1.779 * (U0-128); if(b1>255)b1=255; if(b1<0)b1=0;r2 = Y1+1.4075*(V1-128);if(r2>255)r2=255; if(r2<0)r2=0;g2 = Y1- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g2>255)g2=255; if(g2<0)g2=0;b2 = Y1 + 1.779 * (U0-128); if(b2>255)b2=255; if(b2<0)b2=0;rgbdata[i*6+0]=r1;rgbdata[i*6+1]=g1;rgbdata[i*6+2]=b1;rgbdata[i*6+3]=r2;rgbdata[i*6+4]=g2;rgbdata[i*6+5]=b2;}
}
彩色图像水平镜像
/**
* @desc 彩色图像水平镜像
* @param pImg 图像缓存,3个像素一个字节,RGB888格式
* @param w 图像宽度
* @param h 图像高度
**/
void rotateColorImgMirrorH(unsigned char *pImg,unsigned int w,unsigned int h)
{unsigned char tmp;unsigned int i=0,j=0,m=0; unsigned char *t = NULL,*tt = NULL; unsigned int w3 = w*3; tt = pImg;for(i=0;i<h;i++){t = tt + ((w-1)*3);for(j=0;j<(w>>1);j++){for(m=0;m<3;m++){tmp = *tt;*tt = *t;*t = tmp;tt++;t++;}t -= 6;}tt += ((w>>1)*3);}
}
客户端整体程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #define SERVER_PORT 55555
#define FB_DEV "/dev/fb0"int sockfd;
static int width; //LCD宽度
static int height; //LCD高度
static int fb_fd = -1; //LCD设备文件描述符
static int v4l2_fd = -1; //摄像头设备文件描述符
static unsigned int *screen_base = NULL;//LCD显存基地址
static unsigned char *frm_base[4] = {NULL}; //帧缓冲起始地址
unsigned char rgbdatabuf[640*480*4]={0};
char yuyvbuf[614400]={0};/* 初始化 LCD */
static int fb_dev_init(void)
{struct fb_var_screeninfo fb_var = {0};struct fb_fix_screeninfo fb_fix = {0};unsigned long screen_size;/* 打开framebuffer设备 */fb_fd = open(FB_DEV, O_RDWR);if (0 > fb_fd) {perror("open_fb error");return -1;}/* 获取framebuffer设备信息 */ioctl(fb_fd, FBIOGET_VSCREENINFO, &fb_var);ioctl(fb_fd, FBIOGET_FSCREENINFO, &fb_fix);screen_size = fb_fix.line_length * fb_var.yres;width = fb_var.xres_virtual;height = fb_var.yres_virtual;printf("%d*%d\n", width, height);/* 内存映射 */screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);if (MAP_FAILED == (void *)screen_base) {perror("mmap error");close(fb_fd);return -1;}/* LCD背景刷白 */memset(screen_base, 0xFF, screen_size);return 0;
}
//YUYV422 转RGB 数据
void yuyv_to_rgb(unsigned char *yuyvdata, unsigned char *rgbdata, int w, int h)
{//码流Y0 U0 Y1 V1 Y2 U2 Y3 V3 --》YUYV像素[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]--》RGB像素int r1, g1, b1; int r2, g2, b2;int i =0 ;for(i=0; i<w*h/2; i++){char data[4];memcpy(data, yuyvdata+i*4, 4);unsigned char Y0=data[0];unsigned char U0=data[1];unsigned char Y1=data[2];unsigned char V1=data[3]; //Y0U0Y1V1 -->[Y0 U0 V1] [Y1 U0 V1]r1 = Y0+1.4075*(V1-128); if(r1>255)r1=255; if(r1<0)r1=0;g1 =Y0- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g1>255)g1=255; if(g1<0)g1=0;b1 = Y0 + 1.779 * (U0-128); if(b1>255)b1=255; if(b1<0)b1=0;r2 = Y1+1.4075*(V1-128);if(r2>255)r2=255; if(r2<0)r2=0;g2 = Y1- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g2>255)g2=255; if(g2<0)g2=0;b2 = Y1 + 1.779 * (U0-128); if(b2>255)b2=255; if(b2<0)b2=0;rgbdata[i*6+0]=r1;rgbdata[i*6+1]=g1;rgbdata[i*6+2]=b1;rgbdata[i*6+3]=r2;rgbdata[i*6+4]=g2;rgbdata[i*6+5]=b2;}
}
/**
* @desc 彩色图像水平镜像
* @param pImg 图像缓存,3个像素一个字节,RGB888格式
* @param w 图像宽度
* @param h 图像高度
**/
void rotateColorImgMirrorH(unsigned char *pImg,unsigned int w,unsigned int h)
{unsigned char tmp;unsigned int i=0,j=0,m=0; unsigned char *t = NULL,*tt = NULL; unsigned int w3 = w*3; tt = pImg;for(i=0;i<h;i++){t = tt + ((w-1)*3);for(j=0;j<(w>>1);j++){for(m=0;m<3;m++){tmp = *tt;*tt = *t;*t = tmp;tt++;t++;}t -= 6;}tt += ((w>>1)*3);}
}
void show_pic(unsigned char * pic_data, int w, int h)
{int i,j;unsigned int * p = screen_base;//LCD起始地址unsigned int tmpdata;for(i=0;i<h;i++)//对某一行{for(j=0;j<w;j++)//一行中的一个像素点{//三个十六进制表示一个像素点//j*3表示当前行的第j+1个十六进制的数据//i*w*3表示第i+1行tmpdata = (pic_data[j*3+i*w*3]<<16 | pic_data[j*3+i*w*3+1]<<8 | pic_data[(j*3)+(i*w*3)+2]<<0);* (p+j) = tmpdata;}p+=width;}
}
void Recv_Video_Data(int fd,uint8_t * data,int data_len)
{int tmp = 0;int current = 0;if(fd > 0){while(1){tmp = read(fd,data,data_len);if(tmp < 0){perror("read buf");close("fd");exit(1);}current = data_len - tmp;data+=tmp;data_len = current;if(data_len == 0){ break;}}}
}
void tcp_client(char * SERVER_IP)
{struct sockaddr_in server_addr = {0};int ret;/* 打开套接字,得到套接字描述符 */sockfd = socket(AF_INET, SOCK_STREAM, 0);if (0 > sockfd) {perror("socket error");exit(EXIT_FAILURE);}/* 调用 connect 连接远端服务器 */server_addr.sin_family = AF_INET;//IPV4server_addr.sin_port = htons(SERVER_PORT); //端口号server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);// inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP 地址/* 请求与服务器建立连接 */ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (0 > ret) {perror("connect error");close(sockfd);exit(EXIT_FAILURE);}printf("服务器连接成功...\n\n");/* 向服务器发送数据 */while(1) {Recv_Video_Data(sockfd,yuyvbuf,sizeof(yuyvbuf));yuyv_to_rgb(yuyvbuf, rgbdatabuf ,640, 480);rotateColorImgMirrorH(rgbdatabuf,640,480);show_pic(rgbdatabuf,640,480);usleep(66000);}close(sockfd);exit(EXIT_SUCCESS);
}int main(int argc, char **argv)
{fb_dev_init();tcp_client("192.168.88.88");
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
