【ALINX AXU2CGB试用】使用V4L2函数库驱动USB摄像头&UDP网络摄像头实现
所有Linux设备,只要是有USB接口,都可以实现USB摄像头的读取,这是每一个嵌入式开发者都必须掌握的技能。 首先是打开支持V4L2的设备,通常会被系统映射为/dev/videox设备:
读写方式同样是ioctl,非常简单:
if ((fd_video = open(filename,O_RDWR)) == -1)
{
printf("Error opening V4L interface\n");
return 1;
}
if (ioctl(fd_video,VIDIOC_QUERYCAP,&cap) == -1)
{
printf("Error opening device %s: unable to query device.\n",filename);
return 2;
} 复制代码 使用format结构体设置相关参数:
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.height = IMAGEHEIGHT;
fmt.fmt.pix.width = IMAGEWIDTH;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(ioctl(fd_video,VIDIOC_S_FMT, &fmt) == -1)
{
printf("Unable to set format\n");
return 3;
}
if(ioctl(fd_video,VIDIOC_G_FMT, &fmt) == -1)
{
printf("Unable to get format\n");
return 4;
} 复制代码 使用requestbuffers结构体配置申请内存:
struct v4l2_requestbuffers req;
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if(ioctl(fd_video,VIDIOC_REQBUFS,&req)==-1)
{
printf("request for buffers error\n");
return 5;
} 复制代码 抓取时,使用队列缓存进行数据搬运:
if (ioctl (fd_video,VIDIOC_QUERYBUF, &buf) == -1)
{
printf("query buffer error\n");
return(0);
}
ioctl (fd_video, VIDIOC_STREAMON, &type);
ioctl(fd_video, VIDIOC_DQBUF, &buf);
int RGB888_u8_2_RGB565_u16(unsigned char * input_buffer,unsigned short output_buffer[])
{
int i;
for(i = 0 ; i < 640 * 480 * 3 ; i += 3)
{
output_buffer[i/3] = (unsigned short)(((input_buffer[i] << 8) & 0xF800) | ((input_buffer[i+1] << 3) & 0x7E0) | input_buffer[i+2] >> 3);
}
return 0;
} 复制代码 显示到framebuffer设备需要进行格式转换,数据格式取决于开发板的GPU显示芯片所支持的格式:
int LCD_Show_Buffer16(char *dev_name , int xpos , int ypos , int width , int height ,unsigned short frame_buffer16[])
{
int fd_lcd,i,j,count;
fd_lcd = open(dev_name,O_RDWR);
if(fd_lcd==-1)
{
printf("open LCD failed!\n");
return -1;
}
for(i = 0 ; i < LCD_HEIGHT ; i++)
{
for(j = 0 ; j < LCD_WIDTH ; j++)
{
if(i <= height && j <= width)
{
framebuffer_lcd[(i + ypos) * LCD_WIDTH + j + xpos] = frame_buffer16[i * width + j];
}
}
}
write(fd_lcd,framebuffer_lcd,LCD_WIDTH * LCD_HEIGHT * 2);
close(fd_lcd);
return 0;
}
复制代码uint16_t ConvertRGB32_or_24_To16(uint32_t rgb)
{
uint32_t red = (rgb & 0x00F80000) >> 8 ;
uint32_t green = (rgb & 0x0000FC00) >> 5 ;
uint32_t blue = (rgb & 0x000000F8) >> 3;
return red | green | blue;
} 复制代码
然后是UDP传输摄像头图片的实现,需要设置目标IP和端口号:
int UDP_Send_Found(socklen_t* socket_found , struct sockaddr_in *addr , char* ip , int port)
{
*socket_found = socket(AF_INET, SOCK_DGRAM, 0);
if(*socket_found == (~0))
{
printf("Create udp send socket failed!\n");
return -1;
}
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(ip);
addr->sin_port = htons(port);
memset(addr->sin_zero, 0, 8);
return 0;
} 复制代码int UDP_Send_Picture(socklen_t socket_send , struct sockaddr_in addr , char* filename)
{
struct Package
{
int length;
char data[UDP_FRAME_LEN];
int fin;
}picture;
socklen_t addr_len = sizeof(struct sockaddr_in);
FILE *fp;
fp = fopen(filename, "rb+");
printf("fp = %d\n",fp);
fseek(fp, 0, SEEK_END);
int fend = ftell(fp);
fseek(fp, 0, 0);
int sendbytes;
printf("fend = %d\n",fend);
while(fend > 0)
{
memset(picture.data, 0, sizeof(picture.data));
fread(picture.data, UDP_FRAME_LEN, 1, fp);
if(fend >= UDP_FRAME_LEN)
{
picture.length = UDP_FRAME_LEN;
picture.fin = 0;
}
else
{
picture.length = fend;
picture.fin = 1;
}
//printf("sendbytes = %d \n",sendbytes);
sendbytes = sendto(socket_send, (char *)&picture, sizeof(struct Package), 0, (struct sockaddr*)&addr,addr_len);
if(sendbytes == -1)
{
printf("Send Picture Failed!d\n");
return -1;
}
else
{
fend -= UDP_FRAME_LEN;
}
}
fclose(fp);
return 0;
} 复制代码
电脑端使用QT上位机搭建一个接收UDP的界面:
UDPImage1.zip (24.5 MB, 下载次数: 0) |