0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

DS小龙哥-嵌入式技术 2022-09-17 15:45 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

【摘要】 介绍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客户端连接(浏览器),处理与浏览器之间的交互过程。

pYYBAGMlO2GAdjHYAADUqpM_-IU324.png

​云服务器: 本身就是一个虚拟电脑

1.​登录: 使用ssh远程登录。

2.​买云服务器: 送一个公网IP地址。

3.​也可以购买一个域名。www.1234.com

今天的代码基础之上实现:

跨网段网页视频监控。

poYBAGMlO2GABXVEAADqi_-m3-g598.pngpoYBAGMlO2KAd5ufAAGLE_1ScZA202.png

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

    文章

    1740

    浏览量

    68023
  • Linux
    +关注

    关注

    88

    文章

    11854

    浏览量

    219819
  • 服务器
    +关注

    关注

    14

    文章

    10438

    浏览量

    91846
  • HTTP
    +关注

    关注

    0

    文章

    539

    浏览量

    35638
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    动车组受电弓视频监控系统

    与智能算法推出了动车组受电弓视频监控系统,该可实时监测弓网状态,主动预警,为列车安全运行提供了保障。动车组受电弓视频监控系统用于运行途中实时监视车顶受电及接触网工作状态,
    的头像 发表于 04-23 17:45 140次阅读
    动车组受电弓<b class='flag-5'>视频</b><b class='flag-5'>监控</b>系统

    受电弓视频监控系统保障高铁安全运行

    志强视觉的动车组受电弓视频监控系统用于运行途中实时监视车顶受电及接触网工作状态,并兼顾受电弓附近高压设备工作状态。通过智能分析自动实时识别受电弓异常状态,为随车机械师处理异常降弓等弓网故障提供辅助的监视视频和分析图像。各车型动车
    的头像 发表于 03-30 17:17 607次阅读
    受电弓<b class='flag-5'>视频</b><b class='flag-5'>监控</b>系统保障高铁安全运行

    探索LTC2901:可编程四电源监控器的卓越性能与应用

    探索LTC2901:可编程四电源监控器的卓越性能与应用 在电子系统设计中,电源监控至关重要,它能确保系统在各种电源条件下稳定运行。今天,我们将深入探讨一款高性能的可编程四电源
    的头像 发表于 02-27 14:50 345次阅读

    深度剖析LTC2900可编程四路电源监控

    深度剖析LTC2900可编程四路电源监控器 在电子系统设计中,电源监控是确保系统稳定运行的关键环节。今天我们要深入探讨的是 Linear Technology 公司的 LTC2900 可编程
    的头像 发表于 02-27 14:40 283次阅读

    送4本!206 张手绘图+源码+视频Linux 老兵呕心之作,让小白也能吃透底层逻辑

    作为开发者,你是否也有过这样的困扰?想学Linux网络编程,却被枯燥的理论、复杂的内核源码劝退;照搬别人的代码能跑通项目,但遇到问题不知如何排查,更谈不上架构优化;市面上的教材要么只讲API用法,要么满
    的头像 发表于 02-05 08:08 213次阅读
    送4本!206 张手绘图+源码+<b class='flag-5'>视频</b>!<b class='flag-5'>Linux</b> 老兵呕心之作,让小白也能吃透底层逻辑

    ESP32开发板创建同步WebServer网页服务器

    ESP32 内置了 Wi-Fi 功能,能够作为网页服务器(Web Server)向网络中的其他设备提供服务。通过在 ESP32 上运行网页服务器,可以创建基于浏览器的用户界面,用于监控传感器数据或控制设备状态,是实现物联网(Io
    的头像 发表于 01-30 11:36 496次阅读
    ESP32开发板创建同步WebServer<b class='flag-5'>网页</b>服务器

    开源 | 60余套STM32单片机、嵌入式Linux、物联网、人工智能项目(开发板+教程+源码)

    嵌入式实战项目推荐15个嵌入式Linux+Qt综合应用项目,涉及家居、医疗、农业等多种应用领域,案例中使用了嵌入式、物联网、人工智能多技术,包括Linux应用开发、Q
    的头像 发表于 12-04 11:42 1084次阅读
    开源 | 60余套STM32单片机、嵌入式<b class='flag-5'>Linux</b>、物联网、人工智能<b class='flag-5'>项目</b>(开发板+教程+源码)

    Linux 编程语言盘点:从内核到AI的全栈选择

    在工控圈和嵌入式圈里,有一个常年被讨论的问题:  “在 Linux 上,到底该用什么语言编程?” 有人坚信:C 才是真正的工业语言。有人反驳:Python 才是效率王者。还有人推崇 Go、Rust
    的头像 发表于 11-06 17:05 876次阅读

    【上海晶珩睿莓1开发板试用体验】2、视频监控监控系统

    感谢电子发烧友论坛 1、系统说明 硬件准备:睿莓板卡、网线、路由器、摄像头、用于远程看监控的手机 系统功能:睿莓板卡驱动摄像头获取监控流媒体,手机远程查看监控视频 2、硬件连接 如下图
    发表于 09-14 19:16

    物联网平台应用环境监控:低代码零编程简化开发,组态应用

    传统环境监控系统开发常陷入两难困境,企业开发周期长、开发维护成本贵,基层运维人员不懂技术,遇到参数调整只能依赖IT团队。而物联网平台驱动的环境监控系统,以低代码零编程为核心,通过“可视化组态应用”将
    的头像 发表于 08-29 15:33 1163次阅读

    【「Yocto项目实战教程:高效定制嵌入式Linux系统」阅读体验】+基础概念学习理解

    。为了对珠峰更了解些,开始接触 Linux 系统开发,并逐渐认识到 Yocto 项目在定制嵌入式 Linux 系统方面的重要性。所以很想拜读下此书。 二、书籍内容概述 基础知识 书中首先回顾了
    发表于 08-04 22:29

    是否可以仅使用 Bootloader Host 来实现可引导加载项目的相同编程结果?

    你好 我想使用 Bootloader Host 在我的 CY8CKIT-059 上对 CY8C5888LTI-LP097 芯片进行编程,并将项目类型设置为可引导加载。我的目标是实现与通过使用 SWD
    发表于 07-18 07:39

    明达技术MG-PNS-MR协议转换器在行车能耗监控项目中的应用

    在某大型行车能耗监控项目中大显身手,以“免编程、高扩展”的卓越性能,助力客户轻松实现多电表数据无缝接入西门子 PLC系统,为智能化能耗管理树立新标杆。 本期案例使用的明达技术产品 MG-PNS-MR  
    的头像 发表于 07-02 15:36 709次阅读
    明达技术MG-PNS-MR协议转换器在行车能耗<b class='flag-5'>监控</b><b class='flag-5'>项目</b>中的应用

    【「Yocto项目实战教程:高效定制嵌入式Linux系统」阅读体验】01初读体验

    顺序为从基础到进阶,从进阶到实战,也就是说前面的部分偏理论知识学习,后端知识偏向于项目应用 Yocto可能能解决目前linux源码中的无用代码过多的问题(我一直觉得linux,zepyhr这种框架和驱动
    发表于 06-30 21:49

    明远智睿SSD2351开发板:视频监控领域的卓越之选

    源的开发资料让开发者可以根据实际需求对视频监控系统的软件进行定制和优化,如开发个性化的视频编码算法、监控界面等。一对一的技术支持则为开发者解决开发过程中遇到的技术难题,确保
    发表于 05-30 10:24