【摘要】 介紹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)break;
}while((read_len=read(file_fd,buffer,sizeof(buffer)))>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_state<=0)break; /*數據接收完畢就退出*/
recv_len=read(Clientfd,p,1024);
p+=recv_len;
if(p-buffer>1024)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)break;
}while((read_len=read(file_fd,buffer,sizeof(buffer)))>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_state<=0)break; /*數據接收完畢就退出*/
recv_len=read(Clientfd,p,1024);
p+=recv_len;
if(p-buffer>1024)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
-
視頻監控
+關注
關注
17文章
1723瀏覽量
65992 -
Linux
+關注
關注
87文章
11496瀏覽量
213247 -
服務器
+關注
關注
13文章
9753瀏覽量
87563 -
HTTP
+關注
關注
0文章
524瀏覽量
32607
發布評論請先 登錄
明遠智睿SSD2351開發板:視頻監控領域的卓越之選
視頻監控設備市場規模猛漲,高清網絡監控攝像頭帶來創新

低空視頻傳輸在望獲實時linux系統上的應用
雙絞線在視頻監控系統中的應用 雙絞線連接器的類型與選擇
【畢設項目參考】嵌入式Linux 10+項目推薦,附項目文檔/源碼/視頻

根據ip地址查網頁怎么查詢?

【免費分享】嵌入式Linux開發板【入門+項目,應用+底層】資料包一網打盡,附教程/視頻/源碼...

智慧園區視頻監控分析系統 YOLO

AI行為識別視頻監控系統 Python

評論