Linux环境下 通过V4L2读取视频+UDP发送图片文件

2023-11-19

 

该图为整个项目的流程图

其中左边的流程为总流程,包括通过中断读取摄像头的帧数据,通过内存映射将内核态的数据映射到用户态,读取用户态的数据,采用循环发送图片数据。

右边是发送图片的流程图,将用户态的缓冲区的数据通过内存映射到用户态,通过内存拷贝将用户态的指针地址对应的数据保存在一个字符数组中,然后通过循环读取字符数组中的数据并通过socket 将数据发送给服务器。

项目场景:

该项目是为了在linux arm开发板上通过linux内置的v4l2接口读取摄像头数据,并通过网络协议将图片数据发送给服务器。因为之前用过opencv  ffmpeg等开源工具读取过linux三的usb摄像头,可以看到这两种接口都是封装了v4l2的接口,所以我想在底层深入了解怎么采用v4l2查看usb摄像头的数据。


问题描述

如何正确的读取用户态下的数据:


原因分析:

如何正确的读取用户态下的数据:

我在通过ioctl 接口调用读取到单帧的数据,得到的是一个指针地址,以及该指针地址后面length个字节。

eg:       ret = ioctl(fd,VIDIOC_DQBUF,&readbuffer);

得到的数据首地址在 mptr[readbuffer.index] 保存的地址中,并且数据长度为 readbuffer.length

这里我需要怎样读取 mptr[readbuffer.index]地址里面的数据


解决方案:

初始化一个指针,指向数据的首地址,通过移动指针的位置,来访问数据的不同位置:

void *ptr = mptr[readbuffer.index];

int send_num = sendto(sockfd, ptr,1024 ,0,(SA)&saddr,sizeof(saddr));

ptr += 1024;

问题描述

UDP发送数据时,会出现数据丢失的情况:

比如 char sent[614400] 总共是614400大小的一个字符数组,但是udp接收端接收到小于614400大小的数据


原因分析:

通过查阅资料,发现,是由于udp发送端发送数据速度较快,而接收端不仅要接收数据还要处理数据,导致接收端处理数据较慢,由于接收端有一个接收缓冲区,当发送端发送的数据超过了缓冲区的大小,那么缓冲区里面的数据就会被覆盖掉:

udp发送端发送数据速度较快,而接收端不仅要接收数据还要处理数据,导致接收端处理数据较慢,由于接收端有一个接收缓冲区,当发送端发送的数据超过了缓冲区的大小,那么缓冲区里面的数据就会被覆盖掉:


解决方案:

两个解决方案,接收端采用多线程读写两个线程进行处理,或者发送端发送数据后,休眠1微秒:

这里我采用的发送端在发送完数据后,休眠一微秒,然后再发送剩下的数据,如此循环往复。

以上就是我在做项目中遇到的两个问题,及解决办法。总共耗时两天。

接下来我给出整体代码。

server端

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <time.h>
#include <fcntl.h>

typedef struct sockaddr * (SA);

int main(int argc, const char *argv[])
{
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字描述符
	if(-1 == sockfd)
	{
		perror("sockfd");
		exit(1);
	}

	struct sockaddr_in ser,cli;	
	bzero(&ser,sizeof(ser));
	bzero(&cli,sizeof(cli));
	ser.sin_family =AF_INET;
	ser.sin_port = htons(50000);
	ser.sin_addr.s_addr = inet_addr("192.168.0.89");
	int ret = bind(sockfd,(SA)&ser,sizeof(ser));
	if(-1 == ret)
	{
		perror("bind");
		exit(1);
	}
	int len = sizeof(cli);
	char save_name[10];
	int save_idx = 0 ; 
	while(1)
	{
	char buf1[1024]={0};
	int rd_num1 = recvfrom(sockfd,buf1,1024,0,(SA)&cli,&len);
	if(0 == strcmp(buf1,"^_^"))
	{
	sprintf(save_name,"my_%d.jpg",save_idx++);
	}
	int fd = open(save_name,  O_CREAT |O_WRONLY|O_TRUNC,0666);
	if(-1 == fd)
	{
		perror("open error!\n");
		exit(1);
	}
	int cnt = 0 ; 
	while(1)
	{
		char buf[1024]={0};int left = 614400 - cnt;int rd_num ;
		//if(left < 1024 )
		//rd_num = recvfrom(sockfd,buf,left,0,(SA)&cli,&len);
		//else
		rd_num = recvfrom(sockfd,buf,1024,0,(SA)&cli,&len);
		//printf("%d \n ",rd_num);
		cnt += rd_num;
		//if(cnt >= 614400){printf("%d \n",cnt);break;}
		//收到发送完毕的信号,退出 
		if(0 == strcmp(buf,"^_^"))
		{
			printf("file end  %d \n",cnt);
			break;
		}
		write(fd,buf,rd_num);
		bzero(buf,sizeof(buf));
                //usleep(0.01);
	}
	close(fd);
	}
	close(sockfd);
	return 0;
}


client端

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef struct sockaddr * (SA);
//如下函数read_JPEG_file为封装好的函数,需要转换格式的时候直接调用即可,不用深入理解
int read_JPEG_file (const char *jpegData, char *rgbdata, int size)
{
	struct jpeg_error_mgr jerr;
	struct jpeg_decompress_struct cinfo;
	cinfo.err = jpeg_std_error(&jerr);
	//1创建解码对象并且初始化
	jpeg_create_decompress(&cinfo);
	//2.装备解码的数据
	//jpeg_stdio_src(&cinfo, infile);
	jpeg_mem_src(&cinfo,jpegData, size);
	//3.获取jpeg图片文件的参数
	(void) jpeg_read_header(&cinfo, TRUE);
	/* Step 4: set parameters for decompression */
	//5.开始解码
	(void) jpeg_start_decompress(&cinfo);
	//6.申请存储一行数据的内存空间
	int row_stride = cinfo.output_width * cinfo.output_components;
	unsigned char *buffer = malloc(row_stride);
	int i=0;
	while (cinfo.output_scanline < cinfo.output_height) {
		//printf("****%d\n",i);
		(void) jpeg_read_scanlines(&cinfo, &buffer, 1); 
		memcpy(rgbdata+i*640*3, buffer, row_stride );
		i++;
	}
	//7.解码完成
	(void) jpeg_finish_decompress(&cinfo);
	//8.释放解码对象
	jpeg_destroy_decompress(&cinfo);
	return 1;
}

int fd_fb;                                                    
static struct fb_var_screeninfo var; /* LCD可变参数 */
static unsigned int *fb_base = NULL; /* Framebuffer映射基地址 */                    
int lcd_w = 800 ,lcd_h= 480; //定义显示器分辨率

//将数据流以3字节为单位拷贝到rgb显存中
void lcd_show_rgb(unsigned char *rgbdata, int w ,int h)
{
    unsigned int *ptr = fb_base;
    for(int i = 0; i <h; i++) {
        for(int j = 0; j < w; j++) {
                memcpy(ptr+j,rgbdata+j*3,3);//
        }
        ptr += lcd_w;
        rgbdata += w*3;
    }
}

int main(void) 
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    assert( sockfd != -1 );
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(50000);
    saddr.sin_addr.s_addr = inet_addr("192.168.0.89");
    char buff[614400] = {0};
    //虚拟机-ubuntu
   lcd_w = var.xres_virtual; //xres_virtual参数可以自动获取当前虚拟机显示器分辨率
   lcd_h = var.yres_virtual;

   //建立显示器fb内存映射 方便控制
   fb_base = (unsigned int*)mmap(NULL,lcd_w*lcd_h*4,PROT_READ|PROT_WRITE,MAP_SHARED, fd_fb,0);
   if(fb_base == NULL)
   {
      printf("can't mmap Framebuffer\n");
      goto err1;
   }

    int fd = open("/dev/video0",O_RDWR); //打开摄像头设备
    if (fd < 0)
    {
        perror("打开设备失败");
        return -1;
    }

    struct v4l2_format vfmt;

    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //摄像头采集
    vfmt.fmt.pix.width = 640; //设置摄像头采集参数,不可以任意设置
    vfmt.fmt.pix.height = 480;
    vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; //设置视频采集格式为mjpg格式
    
    int ret = ioctl(fd,VIDIOC_S_FMT,&vfmt);
    if (ret < 0)
    {
        perror("设置格式失败1");
    }

    //申请内核空间
    struct v4l2_requestbuffers reqbuffer;
    reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuffer.count = 4; //申请4个缓冲区
    reqbuffer.memory = V4L2_MEMORY_MMAP;  //映射方式

    ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuffer);
    if (ret < 0)
    {
        perror("申请空间失败");
    }
   
    //映射
    unsigned char *mptr[4];//保存映射后用户空间的首地址
    unsigned int size[4];
    struct v4l2_buffer mapbuffer;
    //初始化type和index
    mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    for(int i = 0; i <4;i++) {
        mapbuffer.index = i;
        ret = ioctl(fd,VIDIOC_QUERYBUF,&mapbuffer); //从内核空间中查询一个空间作映射
        if (ret < 0)
        {
            perror("查询内核空间失败");
        }
        //映射到用户空间
        mptr[i] = (unsigned char *)mmap(NULL,mapbuffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,mapbuffer.m.offset);
        size[i] = mapbuffer.length; //保存映射长度用于后期释放
        //查询后通知内核已经放回
        ret = ioctl(fd,VIDIOC_QBUF,&mapbuffer); 
        if (ret < 0)
        {
            perror("放回失败");
        }
    }
    //开始采集
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_STREAMON,&type); 
    if (ret < 0){ perror("开启失败");}

    //定义一个空间存储解码后的rgb
    unsigned char rgbdata[640*480*3];
    int jpg_idx = 0 ; 
    char jpg_name[10];
    char * buf = malloc(1024);
    int sum = 100;//这里是用来控制读取的帧的数量 实际场景下 可以不要这个限制
    while(1)
    {
	if(sum -- < 0 ){return -1 ; }
        //strcpy(buf,"^_^");
        //sendto(sockfd,buf,strlen(buf),0,(SA)&saddr,sizeof(saddr));
        //从队列中提取一帧数据
        struct v4l2_buffer readbuffer;
        readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //每个结构体都需要设置type为这个参赛要记住
        ret = ioctl(fd,VIDIOC_DQBUF,&readbuffer); 
    	int read_len = mapbuffer.length;
    	int send_len = 1024;
    	printf("%d \n",read_len);
        if (ret < 0)
         {
         perror("读取数据失败");
         }
	if(1)
	{
        void *ptr = mptr[readbuffer.index];
        strcpy(buf,"^_^");//发送UDP头 方便接收方确认数据开始
        sendto(sockfd,buf,strlen(buf),0,(SA)&saddr,sizeof(saddr));
	int cnt = 0 ; //用来保存已经发送的字节数量
        while(1)
        {
                if(cnt >= readbuffer.length)
                {
                        break;
                }
		cnt += 1024; 
                int send_num = sendto(sockfd, ptr,1024 ,0,(SA)&saddr,sizeof(saddr));
		ptr += 1024;
		usleep(1);
                bzero(buf,sizeof(buf));
        }
        strcpy(buf,"^_^");//发送UDP尾巴 方便接收方确认数据结束
        sendto(sockfd,buf,strlen(buf),0,(SA)&saddr,sizeof(saddr));
        usleep(5);
        }

        //通知内核使用完毕
        ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
        if(ret < 0)
            {
                perror("放回队列失败");
            }
        }
        //停止采集
        ret = ioctl(fd,VIDIOC_STREAMOFF,&type);

        //释放映射
        for(int i=0; i<4; i)munmap(mptr[i], size[i]);

        close(fd); //关闭文件
        return 0;

    err1:
       close(fd_fb);
       return -1;
}

最后 server端和client端 编译源代码的命令分别为

gcc serv.c -o serv -ljpeg

gcc cli.c -o cli -ljpeg

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux环境下 通过V4L2读取视频+UDP发送图片文件 的相关文章

  • 如何用C语言创建UDP服务器?

    我正在尝试用 C 语言 在 Linux 下 编写一个 UDP 服务器 我知道在socket 我必须使用的功能SOCK DGRAM并不是SOCK STREAM if list s socket AF INET SOCK DGRAM IPPRO
  • 我应该对 UDP 使用(非阻塞)NIO 吗?

    根据这个帖子 https stackoverflow com questions 569555 non blocking udp i o vs blocking udp i o in java UDP 只是不阻塞 使用 非阻塞 NIO AP
  • Golang编写的UDP客户端无法接收来自服务器的消息

    我写了一个Java客户端 它向广播地址发送消息 我还编写了一个Java服务器 它接受所有发送的消息并将消息发送回客户端 现在我想尝试在 Go 中做同样的事情 只是为了获得一些经验 服务器工作正常 正在接收消息并对 Java 客户端做出响应
  • 播放 UDP 数据包中收到的原始 PCM 音频

    远程设备正在 UDP 数据包中发送实时原始 PCM 音频 不包含标头 我需要在 java 中实现一个程序来接收这些数据包并在 PC 上实时播放它们 据我所知 原始 PCM 的属性是 16 位 单声道 采样率 24KHz 因此我尝试向该原始
  • 对 C# 中 UDP 协议的套接字感到困惑

    我刚刚开始通过各种 Google 搜索学习套接字 但在弄清楚如何在 C 中正确使用套接字时遇到一些问题 我需要一些帮助 我有一个测试应用程序 Windows 窗体 和一个不同的类 实际上在它自己的 dll 中 但这无关紧要 我有我的套接字代
  • 使用 Winsock 通过单个 UDP 服务器处理 10 个客户端

    我已经使用 UDP 套接字建立了一个服务器 客户端应用程序 但我的服务器无法一次处理多个客户端 现在我想修改我的应用程序 让 10 个客户端分别运行在不同的计算机上 而我的服务器运行在单独的计算机上 我希望我的服务器能够与 10 个不同机器
  • 什么是 STUN?它是否需要端口转发服务器?

    我对没有基础服务器的 p2p 通信进行了一些研究 并通过了 STUN 据我所知 STUN 是 NAT 打孔 的一种方式 不需要对等方进行端口转发即可连接 这是正确的吗 打孔到底是什么意思 这一切看起来都很脆弱 因为如果不需要端口转发 它就会
  • UDP 服务器套接字缓冲区溢出

    我正在 Linux 上编写 C 应用程序 我的应用程序有一个 UDP 服务器 它在某些事件上向客户端发送数据 UDP 服务器还接收来自客户端的一些反馈 确认 为了实现这个应用程序 我使用了一个 UDP 套接字 例如int fdSocket
  • 如何使用 ZeroMQ 处理原始 UDP?

    我有一个客户 我无法更改其代码 但我想使用 重新 编写ZeroMQ插座 客户使用原始TCP和原始的UDP插座 我知道我可以使用ZMQ ROUTER RAW对于生的TCP插座 但是原始的怎么样 UDP数据流 ZeroMQ 中对 UDP 的支持
  • 我应该使用哪个高级 API 来管理 iOS 上的 UDP 套接字? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 在章节 使用套接字和流 https developer apple com library mac d
  • 移动提供商无法进行 UDP 打洞

    实际上 我正在编写一个 Android 应用程序 该应用程序接收连接到 PC 的网络摄像头的图片 为了获得更多的 fps 我使用 udp 协议而不是 tcp 这个想法是 电脑将图片发送到手机的 IP 和端口 但电话提供商有不同的公共端口 所
  • 为什么UDP服务器上的UDP客户端端口会改变

    我一直在关注一个简 单的 UDP 服务器 客户端教程 发现here http www binarytides com udp socket programming in winsock 我有一个关于客户端连接到服务器的端口的快速问题 仅从代
  • HTTP 是否使用 UDP?

    这可能是一个愚蠢的问题 HTTP 是否使用过用户数据报协议 例如 如果使用 HTTP 传输 MP3 或视频 它内部是否使用 UDP 进行传输 From RFC 2616 http www ietf org rfc rfc2616 txt 通
  • 如何在QT中发送和接收UDP数据包

    我正在 QT 中编写一个小型应用程序 它通过本地网络发送广播 UDP 数据包 并等待来自网络上的一个或多个设备的 UDP 响应数据包 创建套接字并发送广播数据包 udpSocketSend new QUdpSocket this udpSo
  • netty 4.x.x 中的 UDP 广播

    我们需要使用 Netty 4 0 0 二进制文件通过 UDP 通道广播对象 Pojo 在 Netty 4 0 0 中 它允许我们仅使用 DatagramPacket 类来发送 UDP 数据包 此类仅接受 ByteBuf 作为参数 还有其他方
  • C++ UDP Socket端口复用

    如何在 C 中创建客户端 UDP 套接字 以便它可以侦听另一个应用程序正在侦听的端口 换句话说 如何在 C 中应用端口复用 我只想监听一个端口 您可以使用嗅探器来做到这一点 只需忽略来自不同端口的数据包即可 我可能需要阻止它发送一些特定的数
  • 对等网络应用程序的网络发现

    我希望有两个类 一个服务器类和一个客户端类 服务器类应该接收每个新客户端的 IP 地址和端口号并将它们存储在列表中 它应该为每个客户端提供已连接客户端及其 IP 地址的列表 然后 客户端可以使用 TCP 连接相互通信 问题是客户端不知道服务
  • Windows 操作系统中无法访问的 IP 套接字关闭时间

    这些代码通过用户数据报协议提供发送数据 下面有两个代码 当我使用第一个代码来处理无法访问的 IP 地址时 我得到了三秒的延迟 请查看新结果标题 只需打开新的 C 控制台应用程序并将这些代码粘贴到其中 第一个代码 using System u
  • 提高UDP可靠性

    我正在构建一个基于 UDP 的小型服务器 服务器基于 Net并使用它自己的Socket类 我通过 ReceiveMessageFromAsync 和异步发送使用完成端口 我的问题是我失去了大约 5 10 的流量 现在我明白这是正常的 但是有
  • Rails 是否支持侦听 UDP 套接字的简洁方式?

    在 Rails 中 集成更新模型某些元素的 UDP 侦听过程的最佳方式是什么 特别是向其中一个表添加行 简单的答案似乎是在同一进程中使用 UDP 套接字对象启动一个线程 但不清楚我应该在哪里执行适合 Rails 方式的操作 有没有一种巧妙的

随机推荐

  • html js c 代码大全,js常用汇总

    javascript 代码库JS函数修改html的元素内容 及修改属性内容 document getElementById aid innerHTML World document getElementById aid href http
  • CBAM——即插即用的注意力模块(附代码)

    论文 CBAM Convolutional Block Attention Module 代码 code 目录 前言 1 什么是CBAM 1 Channel attention module CAM 2 Spatial attention
  • hexo的美化——拓展篇

    基础知识 css样式 hexo themes next source css 是next主题的样式文件 决定主题的外观 hexo themes next source css main styl 汇总css文件夹中所有的样式 hexo th
  • 一段有意思的异步代码片段

    毫不夸张的说 下面的代码会有一半的人输出错误 上代码 async function getCount id return id let count 0 async function addCount num count await getC
  • 深度学习入坑笔记之二---手写体图像识别问题

    深度学习入坑笔记之二 手写体图像识别问题 目录 前言 通过softmax进行手写体图像建模及识别 数据导入 softmax建模 训练模型 模型评估 通过卷积网络进行手写体图像建模及识别 初始化权重 定义卷积层及池化层 添加层 训练及评估模型
  • golang1.9编译openwrt运行程序 ,window7下liteide编译

    网上看了好多资料发现都很过时了 基本都是用的https github com gomini go mips32编译的 但是go1 9早就支持mips了 设置好编译参数 开始build 这时在go pkg下会出现linux mips目录 就是
  • 本地镜像发布到私有库

    情景 涉及机密的文件 公司不可能提供镜像给公网 所以需要创建一个私有仓库用于存放敏感的镜像 Docker Registry帮助我们搭建私有的仓库供团队使用 相当于一个私有的hub仓库 本地拉取registry镜像 运行私有库 相当于自己本地
  • BugkuCTF-MISC题FileStoragedat

    知识点 FileStorage是微信存储数据的一个文件夹 该文件夹下存放的是经过加密后微信里发送 接受的图片而形成的文件后缀为dat的文件 就是微信dat文件 想要做出此题 就得先弄懂微信dat文件形成的原因 微信的dat文件 将微信图片的
  • Java Elasticsearch多条件分组聚合查询

    需求 在项目开发中 需要从elasticsearch中查询日志数据 先统计每一天的日志调用量 然后在每一天的分组聚合基础上 再分组聚合统计成功和失败的日志调用量 代码 DateHistogramAggregationBuilder aggr
  • Python爬虫——多线程爬虫如何实现?

    Python爬虫 多线程爬虫 1 多任务 2 主线程与子线程 2 1 何谓线程 主线程及子线程 2 2 查看线程数量 2 3 创建子线程 2 4 线程间的通信 3 线程间的资源竞争 4 互斥锁与死锁 4 1 互斥锁 4 2 死锁 4 3 避
  • 我的第一个Imx6ULL应用《百度图像识别》

    Imx6ULL填坑计划 此次用到的所有资料我都放到了奶牛快传里 下载的话速度极快 https c t work s fe0b4a22171342 我买这个板子已经很久了 跟着野火正点原子的教程踉踉跄跄学了一段儿 对很多基础知识也是一知半解
  • SSHDroid(SSH Server for Android)通过PC或命令连接android

    1 下载berserker android apps sshdroid apk 如果你懒的下载 给我留言 我会发给你 2 安装到手机 显示如图 简单解释一下 一般android系统没有root权限 Wifi Connection 是你连接的
  • JavaWeb项目相关的依赖(Maven仓库)

    Maven仓库 SSM整合 依赖 junit
  • Springboot自动装配原理详解

    Springboot自动装配原理 主程序类 主入口类 SpringBootApplication public class MysteelEnglishWebApplication public static void main Strin
  • 怎么通过SPSS的神经网络模型预测结果

    神经网络模型是数据分析常用的模型 它广泛应用于众多领域 比如 医疗 人工智能 深度学习 语音 机器人等 它能通过现有数据经过神经网络模型训练得到训练模型 再将模型运用于预测数据集 进而得到预测结果 并且将预测趋势应用于各个领域 IBM SP
  • 元字符的详细解析

    上一篇文章介绍了正则的用处以及正则中这些元字符的基本含义 但是如果我们只知道那些元字符的含义 不知道怎么使用和加以练习 那么对于正则我们还只是看见了门槛 并没有踏入 那么本篇文章就让我们迈起脚步正式走入正则的世界吧 let s go 我的学
  • 714阿里巴巴模拟面试

    介绍一下数据库分页 https www nowcoder com questionTerminal 3577280c810546658f06f19c01ff0345 给定一棵树 求出这棵树的直径 即两个节点距离的最大值 应该是左右子树遍历深
  • Combox的GetLBText();遇到的问题和解决方法

    最近有个项目要结束但是测试的时候发现一个问题 就是点击Combox的时候偶尔出现程序卡死 找了半天最后定位到GetLBText 的函数上 发现当你在选择索引或者Combox获得焦点的时候GetLBText都会执行到 正常的值肯定没有我问题
  • linux下安装awk

    sudo apt get install gawk 注意名称
  • Linux环境下 通过V4L2读取视频+UDP发送图片文件

    该图为整个项目的流程图 其中左边的流程为总流程 包括通过中断读取摄像头的帧数据 通过内存映射将内核态的数据映射到用户态 读取用户态的数据 采用循环发送图片数据 右边是发送图片的流程图 将用户态的缓冲区的数据通过内存映射到用户态 通过内存拷贝