Linux编程_网页视频监控项目

【摘要】 介绍linux下http服务器搭建,完成网页图片显示,网页视频显示。
任务1: 网页视频监控项目 目的: 使用浏览器访问开发板的usb摄像头图像数据,实时刷新到达视频的效果。
1.​ http协议: 如何传输数据,让浏览器显示?
2.​ 线程的并发执行: 多个浏览器同时访问摄像头数据。
3.​ usb摄像头编程: 如果获取摄像头的数据。
1.1 如何显示一张静态的图片 http协议: 文本协议-----报文: 字符串。
http服务器基本的交互的步骤:
1.​ 先创建http服务器
2.​ 使用浏览器(http客户端)访问http服务器:
(1)​ 第一次请求的路径是: / :表示询问: 你需要我做什么?
(2)​ http服务器收到请求之后,先向http客户端发送应答报文。
再发送需要浏览器处理的数据: 数据类型、数据长度。 :表示分配给浏览器需要做的任务
如果需要浏览器显示一张图片,浏览器在收到任务之后,会解析任务,再次向服务器发送请求:
请求图片(图片的资源路径):
http服务器收到请求之后,先向http客户端发送应答报文。
再发送需要浏览器处理的数据: 数据类型、数据长度。
1.2 采集摄像头数据、显示动态图片 1. 采集摄像头数据: 开一个新的线程
2. 需要将摄像头的数据编码为jpg格式—jpglib只能将rgb数据压缩成jpg格式保存到文件。
需要使用改进的算法,将jpg图像压缩存放到内存里。
3.​ 需要考虑资源共享: 线程互斥锁+条件变量
(1)​ 线程1: 负责采集摄像头的数据,并进行编码压缩jpg图像
(2)​ 线程2(主线程): 负责等待http客户端连接(浏览器),处理与浏览器之间的交互过程。
​ 云服务器: 本身就是一个虚拟电脑。
1.​ 登录: 使用ssh远程登录。
2.​ 买云服务器: 送一个公网ip地址。
3.​ 也可以购买一个域名。www.1234.com
今天的代码基础之上实现:
跨网段网页视频监控。
int on = 1;
if(setsockopt(http_server_fd, sol_socket, so_reuseaddr, &on, sizeof(on)) < 0)
{
printf(setsockopt(so_reuseaddr) 设置错误!\n);
exit(-1);
}
//这样可以保证: 端口关闭之后,立即可以再次使用
1.3 解决tcp服务器退出时,产生退出信号终止进程 signal.h中的宏定义sig_dfl及sig_ign
sig_dfl,sig_ign 分别表示无返回值的函数指针,指针值分别是0和1,
这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值。
sig_dfl:默认信号处理程序
sig_ign:忽略信号的处理程序
/*
往一个已经接收到fin的套接中写是允许的,接收到的fin仅仅代表对方不再发送数据。
并不能代表我不能发送数据给对方。
往一个fin结束的进程中写(write),对方会发送一个rst字段过来,tcp重置。
如果再调用write就会产生sigpipe信号
*/
signal(sigpipe,sig_ign);
1.4  http服务器搭建_显示一静态jpg图片#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define http_server_port 1237 /*http服务器端口号*/int http_server_fd; /*http服务器套接字*//*函数功能: 处理退出的信号*/void exit_sighandler(int sig){ /*关闭服务器套接字*/ close(http_server_fd); sleep(2); //退出进程 exit(1);}/*函数功能: 向http客户端发送文件数据*/void httpclient_sendfiledata(int client_fd,char *type,char *file){ int file_fd; int read_len; struct stat file_buf; unsigned char buffer[1024]; file_fd=open(file,o_rdonly); if(file_fd<0) { printf(%s文件打开失败!\n,file); return; } /*1. 获取文件的状态信息*/ stat(file,&file_buf); //printf(%d\n,file_buf.st_size); /*2. 构造报文头*/ sprintf(buffer,http/1.1 200 ok\r\n \ content-type:%s\r\n \ content-length:%d\r\n server: wbyq\r\n \ \r\n,type,file_buf.st_size); read_len=strlen(buffer); /*2. 发送数据*/ do { if(write(client_fd,buffer,read_len)0);}/*函数功能: 处理http客户端的线程*/void *pthread_handler_http_client(void *dev){ int clientfd; unsigned char buffer[1024]; unsigned char *p=buffer; struct pollfd fds; int poll_state; /*poll函数的状态值*/ int recv_len; /*接收的数据长度*/ if(dev==null) { pthread_exit(null); /*终止线程*/ } clientfd=*(int*)dev; /*保存客户端套接字描述符*/ free(dev); /*释放空间*/ /*1. 接收客户端的请求报文*/ fds.fd=clientfd; fds.events=pollin; while(1) { /*等待数据*/ poll_state=poll(&fds,1,100); if(poll_state1024)break; } //printf(buffer=%s\n,buffer); /*1. 判断请求的路径*/ if(strstr(buffer,get / http/1.1)) { httpclient_sendfiledata(clientfd,text/html,index.html); } else if(strstr(buffer,get /image.jpg http/1.1)) { httpclient_sendfiledata(clientfd,image/jpeg,123.jpg); } else if(strstr(buffer,get /favicon.ico http/1.1)) { httpclient_sendfiledata(clientfd,image/x-icon,123.ico); } close(clientfd);}/*http服务器创建:1. 创建socket套接字2. 绑定端口号: 服务器创建3. 设置监听端口的数量: 服务器最大等待连接的客户端总数量4. 等待客户端连接*/int main(int argc,char **argv){ /*1. 绑定将要捕获的信号*/ signal(sigint,exit_sighandler); signal(sigsegv,exit_sighandler); /*2. 创建套接字*/ http_server_fd=socket(af_inet,sock_stream,0); if(http_server_fd<0) { printf(http服务器:创建套接字创建失败!\n); return -1; } /*3. 绑定端口号*/ struct sockaddr_in server_addr; memset(&server_addr,0,sizeof(struct sockaddr_in)); server_addr.sin_family=af_inet; //ipv4 server_addr.sin_port=htons(http_server_port); //需要填大端格式的端口号数据 server_addr.sin_addr.s_addr=0;//inet_addr(192.168.18.3); /*0=inet_addr(0.0.0.0) ---表示本地所有ip地址*/ if(bind(http_server_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in))!=0) { printf(http服务器:绑定端口号失败!\n); return -2; } /*4. 设置监听客户端连接的数量*/ listen(http_server_fd,50); /*5. 等待客户端连接:阻塞*/ struct sockaddr_in client_addr; int addrlen=sizeof(struct sockaddr_in); pthread_t thread_id; /*线程的id*/ int *client_fd=null; /*保存客户端的套接字描述符*/ while(1) { client_fd=(int*)malloc(sizeof(int)); if(client_fd==null) { printf(存放客户端的套接字描述符,空间申请失败!\n); break; } *client_fd=accept(http_server_fd,(struct sockaddr *)&client_addr,&addrlen); if(*client_fd<0) { break; } /*6. 创建新的线程*/ if(pthread_create(&thread_id,null,pthread_handler_http_client,(void*)client_fd)!=0) { printf(创建处理http客户端线程失败!\n); break; } } /*7. 关闭服务器套接字*/ close(http_server_fd); return 0;}  
1.5 网页视频监控的项目代码_多线程处理#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include yuv_to_jpeg.h#define uvc_video_device /dev/video15 /*uvc摄像头设备节点*/int uvc_video_fd; /*存放摄像头设备节点的文件描述符*/int video_stop_stat=1; /*视频停止状态: 1表示正常执行,0表示退出*/unsigned char *video_memaddr_buffer[4]; /*存放的是摄像头映射出来的缓冲区首地址*/int image_width; /*图像的宽度*/int image_height; /*图像的高度*/unsigned char *jpg_video_buffer=null; /*转换之后的jpg数据缓冲区首地址*/unsigned int jpg_video_size; /*存放当前jpg数据缓冲区的大小*/pthread_mutex_t mutex; /*互斥锁*/pthread_cond_t cond; /*条件变量*/#define http_server_port 1235 /*http服务器端口号*/int http_server_fd; /*http服务器套接字*//*函数功能: 处理退出的信号*/void exit_sighandler(int sig){ video_stop_stat=0; //让摄像头采集线程自动退出 sleep(2); /*关闭服务器套接字*/ close(http_server_fd); //退出进程 exit(1);}/*函数功能: 向http客户端发送文件数据*/void httpclient_sendfiledata(int client_fd,char *type,char *file){ int file_fd; int read_len; struct stat file_buf; unsigned char buffer[1024]; file_fd=open(file,o_rdonly); if(file_fd<0) { printf(%s文件打开失败!\n,file); return; } /*1. 获取文件的状态信息*/ stat(file,&file_buf); //printf(%d\n,file_buf.st_size); /*2. 构造报文头*/ sprintf(buffer,http/1.1 200 ok\r\n \ content-type:%s\r\n \ content-length:%d\r\n server: wbyq\r\n \ \r\n,type,file_buf.st_size); read_len=strlen(buffer); /*2. 发送数据*/ do { if(write(client_fd,buffer,read_len)0);}/*函数功能: 发送数据流*/void sendvideodata(int clientfd){ int image_size; unsigned char *image_data; unsigned char buffer[1024]; /*1. 构造报文头: 回应浏览器请求,并告诉浏览器接下来需要使用长连接*/ sprintf(buffer, http/1.0 200 ok\r\n \ server: wbyq\r\n \ content-type: multipart/x-mixed-replace;boundary= boundarydonotcross \r\n \ \r\n \ -- boundarydonotcross \r\n); if(write(clientfd,buffer,strlen(buffer))<0) { return; } /*2. 循环发送数据流: jpg图片*/ image_data=malloc(image_width*image_height*3); if(image_data==null) { printf(循环发送数据流缓冲区申请失败!\n); return; } while(video_stop_stat) { //阻塞方式等待条件变量,等待成功并上锁 pthread_cond_wait(&cond,&mutex); image_size=jpg_video_size; //保存图片的大小 memcpy(image_data,jpg_video_buffer,image_size); //互斥锁解锁 pthread_mutex_unlock(&mutex); /*2.1 构造报文头: 告诉浏览器发送数据类型和数据的长度*/ sprintf(buffer,content-type:%s\r\n \ content-length:%d\r\n\ \r\n,image/jpeg,image_size); if(write(clientfd,buffer,strlen(buffer))<0) { break; } /*2.2 发送实际的数据*/ if(write(clientfd,image_data,image_size)<0)break; /*2.3 发送间隔符号*/ sprintf(buffer,\r\n-- boundarydonotcross \r\n); //间隔符号 if(write(clientfd,buffer,strlen(buffer))<0) { break; } } free(image_data); //释放空间}/*函数功能: 处理http客户端的线程*/void *pthread_handler_http_client(void *dev){ int clientfd; unsigned char buffer[1024]; unsigned char *p=buffer; struct pollfd fds; int poll_state; /*poll函数的状态值*/ int recv_len; /*接收的数据长度*/ if(dev==null) { pthread_exit(null); /*终止线程*/ } clientfd=*(int*)dev; /*保存客户端套接字描述符*/ free(dev); /*释放空间*/ /*1. 接收客户端的请求报文*/ fds.fd=clientfd; fds.events=pollin; while(1) { /*等待数据*/ poll_state=poll(&fds,1,100); if(poll_state1024)break; } //printf(buffer=%s\n,buffer); /*1. 判断请求的路径*/ if(strstr(buffer,get / http/1.1)) { httpclient_sendfiledata(clientfd,text/html,index.html); } else if(strstr(buffer,get /?action=stream http/1.1)) { sendvideodata(clientfd); //发送视频流数据 } else if(strstr(buffer,get /favicon.ico http/1.1)) { httpclient_sendfiledata(clientfd,image/x-icon,123.ico); } close(clientfd);}/*函数功能: uvc摄像头初始化返回值: 0表示成功*/int uvcvideoinit(void){ /*1. 打开摄像头设备*/ uvc_video_fd=open(uvc_video_device,o_rdwr); if(uvc_video_fd<0) { printf(%s 摄像头设备打开失败!\n,uvc_video_device); return -1; } /*2. 设置摄像头的属性*/ struct v4l2_format format; memset(&format,0,sizeof(struct v4l2_format)); format.type=v4l2_buf_type_video_capture; /*表示视频捕获设备*/ format.fmt.pix.width=320; /*预设的宽度*/ format.fmt.pix.height=240; /*预设的高度*/ format.fmt.pix.pixelformat=v4l2_pix_fmt_yuyv; /*预设的格式*/ format.fmt.pix.field=v4l2_field_any; /*系统自动设置: 帧属性*/ if(ioctl(uvc_video_fd,vidioc_s_fmt,&format)) /*设置摄像头的属性*/ { printf(摄像头格式设置失败!\n); return -2; } image_width=format.fmt.pix.width; image_height=format.fmt.pix.height; printf(摄像头实际输出的图像尺寸:x=%d,y=%d\n,format.fmt.pix.width,format.fmt.pix.height); if(format.fmt.pix.pixelformat==v4l2_pix_fmt_yuyv) { printf(当前摄像头支持yuv格式图像输出!\n); } else { printf(当前摄像头不支持yuv格式图像输出!\n); return -3; } /*3. 请求缓冲区: 申请摄像头数据采集的缓冲区*/ struct v4l2_requestbuffers req_buff; memset(&req_buff,0,sizeof(struct v4l2_requestbuffers)); req_buff.count=4; /*预设要申请4个缓冲区*/ req_buff.type=v4l2_buf_type_video_capture; /*视频捕获设备*/ req_buff.memory=v4l2_memory_mmap; /*支持mmap内存映射*/ if(ioctl(uvc_video_fd,vidioc_reqbufs,&req_buff)) /*申请缓冲区*/ { printf(申请摄像头数据采集的缓冲区失败!\n); return -4; } printf(摄像头缓冲区申请的数量: %d\n,req_buff.count); /*4. 获取缓冲区的详细信息: 地址,编号*/ struct v4l2_buffer buff_info; memset(&buff_info,0,sizeof(struct v4l2_buffer)); int i; for(i=0;i
深圳有望成为世界首个地铁内免费使用高速WiFi城市
模拟器件国产化及行业前景调研分析
全世界“最安全”汽车品牌又出好车 沃尔沃V90让奔驰眼红
魅族Pro7什么时候上市?最新消息:魅族Pro7即将发布真机曝光?网友喊话李楠辟谣!
简单的电子制作能激发对电子学的兴趣
Linux编程_网页视频监控项目
iOS10.3 Beta4怎么样?一分钟升级iOS10.3 Beta4教程?
铝基板输出纹波噪声的测试方法
搭乘智慧零售,天天快递在革新中蜕变
米粉请放心,小米6将搭载满血版骁龙835 没有降频版!
条形音响设计上的限制和困难解决方案
为什么要使用激光雷达?与其传感器优劣对比
佰维存储强化产业链布局,发力高端消费电子、工业车规等领域
金立破产清算一案第一次债权人会议召开,认定债权总额173.59亿元
Facebook正在计划构建一个加密支付平台允许用户使用数字硬币进行购物
三分钟看懂高温超导滤波器构成
人脸识别门禁系统在写字楼大厦中如何应用?
蚂蚁金服入股TMB 欲打造巴基斯坦版支付宝
华进入选国家级专精特新“小巨人”企业
英飞凌与高通联袂打造面向3D认证的高质量标准解决方案