本帖最后由 donatello1996 于 2021-10-3 19:39 编辑
从USB摄像头获取图像并显示,这个是 ARM Linux应用开发非常常见的例子,而其中最为简单,能让小白学会的方式自然就是V4L2驱动,这个驱动库都有写好的代码可以直接调用,我之前的帖子也发过类似的内容,这种方式虽然简单但是效率低下(不知道是不是USB接口 通信的瓶颈导致的),实际输出效果即帧数低得可怜,后续还需要对驱动代码继续改进,或者是拿一套现成的MIPI CSI设备使用相同驱动代码进行空白对照即可。本帖不侧重V4L2驱动代码,直接一笔带过,重点讲不同的帧数据格式和不同的GPU输出格式之间的转化,这个知识点相比起直接用轮子的V4L2驱动可重要多了(我个人是这样觉得的)。
- V4L2驱动代码如下,看得懂就看,看不懂的看网上解读即可:
- int V4l2_Grab()
- {
- unsigned int n_buffers;
- buffers = (buffer* )malloc(req.count*sizeof(*buffers));
- if (buffers == NULL)
- {
- printf ("Out of memoryn");
- return 0;
- }
- for (n_buffers = 0; n_buffers < req.count; n_buffers++)
- {
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = n_buffers;
- //query buffers
- if (ioctl (fd_video,VIDIOC_QUERYBUF, &buf) == -1)
- {
- printf("query buffer errorn");
- return(0);
- }
- buffers[n_buffers].length = buf.length;
- buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED,
- fd_video, buf.m.offset);
- if (buffers[n_buffers].start == MAP_FAILED)
- {
- printf("buffer map errorn");
- return 0;
- }
- }
- for (n_buffers = 0; n_buffers < req.count; n_buffers++)
- {
- buf.index = n_buffers;
- ioctl(fd_video, VIDIOC_QBUF, &buf);
- }
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ioctl (fd_video, VIDIOC_STREAMON, &type);
- ioctl(fd_video, VIDIOC_DQBUF, &buf);
- // printf("grab yuyv OK!n");
- return 1;
- }
复制代码
重点要讲不同的帧数据格式和不同的GPU输出格式之间的转化,在实际项目中, 开发板的HDMI输出 电路五花八门,有16位 24位 32位输出,16位输出最常见的当然是RGB565格式,24位的RGB888格式,32的ARGB8888格式,这个很好理解,HDMI驱动部分要根据项目要求进行调整,涉及到成本/性能/规定选材等诸多方面,对此进行造轮子是非常有意义的,作为开发者,应该常备多套输出代码,以便能适应以上输出格式排列组合的任意情况,值得注意的是,虽然数据源有u8/u16/u24/u32四种格式,但是GPU芯片一般只有16/32两种输出格式,数据转换这块属实给玩明白了(抗吧腔),但是必须玩明白,虽然枯燥但是不能出错:
u8格式的RGB888数据转为u16格式的RGB565数据:
- 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;
- }
复制代码
u8格式的RGB888数据转为u32格式的ARGB8888数据:
- int RGB888_u8_2_u32_ARGB8888(unsigned char * input_buffer , unsigned int output_buffer[])
- {
- int i , j = 0;
- for(i = 0 ; i < 640 * 480 * 4 ; i += 4)
- {
- output_buffer[i / 4] = (unsigned int)((input_buffer[j] << 16) | (input_buffer[j + 1] << 8) | input_buffer[j + 2]);
- j += 3;
- }
- return 0;
- }
复制代码
16位显示格式的LCD显示16位格式的buffer:
- int LCD16_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;
- }
复制代码
32位显示格式的LCD显示16位格式的buffer:
- int LCD32_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 * 4);
- close(fd_lcd);
- return 0;
- }
复制代码
32位显示格式的LCD显示24/32位格式的buffer,因为buffer一定为u32格式,所以不需要区分:
- int LCD32_Show_Buffer2432(char *dev_name , int xpos , int ypos , int width , int height ,unsigned int frame_buffer2432[])
- {
- 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_buffer2432[i * width + j];
- }
- }
- }
- write(fd_lcd , framebuffer_lcd , LCD_WIDTH * LCD_HEIGHT * 4);
- close(fd_lcd);
- return 0;
- }
复制代码
32位显示格式的LCD显示8位格式的buffer,其中buffer为RGB888格式:
- int LCD32_Show_Buffer8_RGB888(char *dev_name , int xpos , int ypos , int width , int height ,unsigned char frame_buffer8_rgb888[])
- {
- 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_buffer8_rgb888[(i * width + j) * 3 ] << 16 |
- frame_buffer8_rgb888[(i * width + j) * 3 + 1] << 8 |
- frame_buffer8_rgb888[(i * width + j) * 3 + 2];
- }
- }
- }
- write(fd_lcd,framebuffer_lcd,LCD_WIDTH * LCD_HEIGHT * 2);
- close(fd_lcd);
- return 0;
- }
复制代码
32位显示格式的LCD显示8位格式的buffer,其中buffer为RGB565格式:
- int LCD32_Show_Buffer8_RGB565(char *dev_name , int xpos , int ypos , int width , int height ,unsigned char frame_buffer8_rgb565[])
- {
- 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_buffer8_rgb565[(i * width + j) * 2 ] << 8 |
- frame_buffer8_rgb565[(i * width + j) * 2 + 1];
- }
- }
- }
- write(fd_lcd,framebuffer_lcd,LCD_WIDTH * LCD_HEIGHT * 2);
- close(fd_lcd);
- return 0;
- }
复制代码
那现在就来用两种方式读取USB摄像头数据并显示,一种方式为buffer直接转换显示,另一种方式为JPG文件读取显示:
- while(1)
- {
- V4l2_Grab();
- if(buffers != NULL)
- {
- Yuyv_2_RGB888_u8(buffers , frame_buffer_video);
- Encode_Jpeg(frame_buffer_video , 640 , 480 , (char*)"/home/proj/1.jpg");
- // UDP_Send_Picture(socklen_udp_camera_send,sockaddr_udp_camera_send,(char*)"/home/proj/1.jpg");
- RGB888_u8_2_ARGB8888_u32(frame_buffer_video , frame_buffer_video32);
- LCD32_Show_Buffer2432(FB_DEV, 100 , 260 , 640 , 480 , frame_buffer_video32);
- LCD_RGB888_Show_JPG_File(FB_DEV, 800 , 260 , "/home/proj/1.jpg");
- }
- }
复制代码
0
|
|
|
|