关键字: 正点原子,i.MX6UL,linux, framebuffer
- 主机环境:ubuntu16.04-64bit
- 硬件版本:正点原子i.MX6UL emmc
固件帖在本站:[[正点原子i.MX6UL开发板] 编译uboot、linux、buildroot-rootfs](https://whycan.cn/t_3550.html)
---
#### 一、本篇目标
接上篇- [[正点原子i.MX6UL]v4l2+framebuffer预览USB摄像头(三):实时预览](https://whycan.cn/t_3909.html)
撕裂问题是由于同时进行读写操作,整帧数据被破坏造成的。
如题,本篇主要实现在LCD上使用framebuffer双缓冲,实现无撕裂的实时预览USB摄像头画面。
由于本篇并不属于原定 三分天下 三步走计划,所以定为番外。
#### 二、相关概念
概念尽量简短不占篇幅,主要记录实践的过程。
framegbuffer双缓冲的资料少有完整可靠的介绍,也可能是我没有找到~
下面是我收集到的信息:
framebuffer驱动框架原生支持双缓冲,如果fb底层驱动实现了双缓冲,应用层可以直接使用,非常方便。
framebuffer双缓冲原理是基于 virtual screen 平移实现,平移实际上就是重新配置了显存的首地址。当定义的显存大于LCD实际尺寸两倍以上时,就可以通过 fb_var_screeninfo 中的 xoffset 和 yoffset 控制 x 轴或 y 轴方向的平移,一般来说只实现 y 轴上的平移就够了。
在应用代码中通过 ioctl 的 FBIOPAN_DISPLAY 指令实现平移。
- 当 yoffset = 1 时,表示显示内容向上平移一行。
vinfo.yoffset = 1;
ret = ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);
- 当 yoffset = var->yres时,表示向上平移一个屏幕帧,即相当于切换缓冲区了!
vinfo.yoffset = vinfo.yres;
ret = ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);
- 双缓冲切换核心代码
// ok 双缓冲测试成功,没有撕裂了!
void framebuffer_doublebuf_update(int fb_size)
{
int ret;
static int buf_index = 0;
if (buf_index)
{
// --更新整屏 offline
vinfo.yoffset = vinfo.yres;
memcpy(fb_addr + vinfo.yoffset * finfo.line_length, fb_buf1, fb_size);
ret = ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);
if (ret)
{
printf("----FBIOPAN_DISPLAY-failed: %d \n", ret);
}
// printf("fb online buf : %d \n", buf_index);
buf_index = 0;
}
else
{
// --更新整屏 offline
memcpy(fb_addr, fb_buf1, fb_size);
vinfo.yoffset = 0;
ret = ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);
if (ret)
{
printf("----FBIOPAN_DISPLAY-failed: %d \n", ret);
}
// printf("fb online buf : %d \n", buf_index);
buf_index = 1;
}
}
使用双缓冲的必要条件:
1. fb底层驱动显存大于等于LCD实际显存的2倍。
2. fb底层驱动实现fb_pan_display函数。
3. fbinfo->fix.ypanstep = 1,表示支持y轴平移。
4. var->yres_virtual >= vinfo.yres * 2(由于是可变信息,应用层修改也行)。
下面使用framebuffer版的"hello world"查看下驱动的主要信息,代码来自-[[正点原子i.MX6UL]v4l2+framebuffer预览USB摄像头(二):显示照片](https://whycan.cn/t_3888.html):
# ./fb-getinfo
success open fb dev : /dev/fb0
fb_fix.id="mxs-lcdif"
fb_fix.smem_start=0x98100000
fb_fix.mem_len=33554432 // 显存32M,远远满足双缓冲使用
...
fb_fix.ypanstep=1 // 支持y轴平移
fb_fix.line_length=2048
fb_var.xres=1024
fb_var.yres=600
fb_var.xres_virtual=1024
fb_var.yres_virtual=600 // 不是yres的2倍,由于是可变信息,可以在应用层自己改成2倍。
fb_var.xoffset=0
fb_var.yoffset=0
...
#
可以看到已经支持双缓冲了,只用关心应用层代码。
最后需要说明一点:单纯的双缓冲并不能有效解决撕裂问题,必须配合vsync才能彻底消灭撕裂!
好消息是,NXP的fb驱动在fb_pan_display里已经实现vsync切换显存(值得新手学习),可以在应用层直接使用双缓冲。
#### 三、代码实现
本代码把采集到的USB摄像头数据,先由YUYV转成RGB888,然后由RGB888再转换成LCD需要的RGB565,最终把数据复制到显存完成显示。
重复这个过程即可实现简单的USB摄像头实时预览。
显存更新使用了带vsync的双缓冲,已经彻底消灭撕裂问题。
v4l2grap-uvc-bmponfb-dbuf-run-x.c:
/*
* @FilePath: /0321/v4l2grap-uvc-bmponfb-dbuf-run-x.c
* @Version: 1.0.0
* @Author: zys
* @LastAuthor : zys
* @CreationDate: 2020-02-04 11:33:25
* @LastEditTime : 2020-03-21 23:13:34
* @Description : 在LCD上实时预览USB摄像头画面,双缓冲模式。
*/
/*----------------------------------------------------------------------------*/
/* change log ----------------------------------------------------------------*/
/*
0321
yz imx6ul test ok.
-build:
$ARMGCC v4l2grap-uvc-bmponfb-dbuf-run-x.c -o v4l2grap-uvc-bmponfb-dbuf-run-x
-tftp get:
cp v4l2grap-uvc-bmponfb-dbuf-run-x /tftpboot/
tftp -g -r v4l2grap-uvc-bmponfb-dbuf-run-x 192.168.1.133
*/
/*----------------------------------------------------------------------------*/
/* Includes ------------------------------------------------------------------*/
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* Private define ------------------------------------------------------------*/
#define _UVC_CAM_HEIGHT (480)
#define _UVC_CAM_WIDTH (640)
#define IMAGEHEIGHT _UVC_CAM_HEIGHT
#define IMAGEWIDTH _UVC_CAM_WIDTH
#define NB_BUFFER 4
/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
struct vdIn {
int fd;
char* videodevice;
// v4l2
struct v4l2_capability cap;
struct v4l2_format fmt;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_streamparm setfps;
struct v4l2_requestbuffers rb;
void* mem[NB_BUFFER];
int memlength[NB_BUFFER];
unsigned char* framebuffer;
int framesizeIn;
int width;
int height;
int fps;
FILE* fp_bmp;
};
/*_attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐 */
//14byte文件头
typedef struct
{
unsigned char cfType[2]; //文件类型,"BM"(0x4D42)
unsigned int cfSize; //文件大小(字节)
unsigned int cfReserved; //保留,值为0
unsigned int cfoffBits; //数据区相对于文件头的偏移量(字节)
} __attribute__((packed)) BITMAPFILEHEADER;
//40byte信息头
typedef struct
{
unsigned int ciSize; //BITMAPFILEHEADER所占的字节数
unsigned int ciWidth; //宽度
unsigned int ciHeight; //高度
unsigned short int ciPlanes; //目标设备的位平面数,值为1
unsigned short int ciBitCount; //每个像素的位数
char ciCompress[4]; //压缩说明
unsigned int ciSizeImage; //用字节表示的图像大小,该数据必须是4的倍数
unsigned int ciXPelsPerMeter; //目标设备的水平像素数/米
unsigned int ciYPelsPerMeter; //目标设备的垂直像素数/米
unsigned int ciClrUsed; //位图使用调色板的颜色数
unsigned intciClrImportant; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要
} __attribute__((packed)) BITMAPINFOHEADER;
typedef struct
{
unsigned char blue;
unsigned char green;
unsigned char red;
} __attribute__((packed)) PIXEL; //颜色模式RGB
/* Private function prototypes -----------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static struct vdIn uvc_cam;
static unsigned char rgb888_buffer[IMAGEWIDTH * IMAGEHEIGHT * 3];
static int xres = 0;
static int yres = 0;
static int bits_per_pixel = 0;
static char* fb_addr = NULL;
static char* fb_buf1 = NULL;
int fbfd;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
/* Global variables ---------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void yuyv_to_rgb888(void)
{
int i, j;
unsigned char y1, y2, u, v;
int r1, g1, b1, r2, g2, b2;
unsigned char* pointer;
double rbase = 0;
double gbase = 0;
double bbase = 0;
pointer = uvc_cam.framebuffer;
for (i = 0; i < IMAGEHEIGHT; i++) {
for (j = 0; j < (IMAGEWIDTH / 2); j++) {
y1 = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2));
u = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2) + 1);
y2 = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2) + 2);
v = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2) + 3);
rbase = 1.042 * (v - 128);
gbase = 0.34414 * (u - 128) + 0.71414 * (v - 128);
bbase = 1.772 * (u - 128);
r1 = y1 + rbase;
g1 = y1 - gbase;
b1 = y1 + bbase;
r2 = y2 + rbase;
g2 = y2 - gbase;
b2 = y2 + bbase;
if (r1 > 255)
r1 = 255;
else if (r1 < 0)
r1 = 0;
if (b1 > 255)
b1 = 255;
else if (b1 < 0)
b1 = 0;
if (g1 > 255)
g1 = 255;
else if (g1 < 0)
g1 = 0;
if (r2 > 255)
r2 = 255;
else if (r2 < 0)
r2 = 0;
if (b2 > 255)
b2 = 255;
else if (b2 < 0)
b2 = 0;
if (g2 > 255)
g2 = 255;
else if (g2 < 0)
g2 = 0;
*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6) = (unsigned char)b1;
*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 1) = (unsigned char)g1;
*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 2) = (unsigned char)r1;
*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 3) = (unsigned char)b2;
*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 4) = (unsigned char)g2;
*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 5) = (unsigned char)r2;
}
}
// printf("yuyv to rgb888 done\n");
}
int v4l2_init(void)
{
int i = 0;
int ret;
struct v4l2_buffer buf;
// 1. open cam
if ((uvc_cam.fd = open(uvc_cam.videodevice, O_RDWR)) == -1) {
printf("ERROR opening V4L interface\n");
return -1;
}
// 2. querycap
memset(&uvc_cam.cap, 0, sizeof(struct v4l2_capability));
ret = ioctl(uvc_cam.fd, VIDIOC_QUERYCAP, &uvc_cam.cap);
if (ret < 0) {
printf("Error opening device %s: unable to query device.\n", uvc_cam.videodevice);
return -1;
}
else {
printf("driver:\t\t%s\n", uvc_cam.cap.driver);
printf("card:\t\t%s\n", uvc_cam.cap.card);
printf("bus_info:\t%s\n", uvc_cam.cap.bus_info);
printf("version:\t%d\n", uvc_cam.cap.version);
printf("capabilities:\t%x\n", uvc_cam.cap.capabilities);
if ((uvc_cam.cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) {
printf("%s: \tsupports capture.\n", uvc_cam.videodevice);
}
if ((uvc_cam.cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) {
printf("%s: \tsupports streaming.\n", uvc_cam.videodevice);
}
}
// 3. set format in
// 3.1 enum fmt
printf("\nSupport format:\n");
memset(&uvc_cam.fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
uvc_cam.fmtdesc.index = 0;
uvc_cam.fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (ioctl(uvc_cam.fd, VIDIOC_ENUM_FMT, &uvc_cam.fmtdesc) != -1) {
printf("\t%d.%s\n", uvc_cam.fmtdesc.index + 1, uvc_cam.fmtdesc.description);
uvc_cam.fmtdesc.index++;
}
// 3.2 set fmt
memset(&uvc_cam.fmt, 0, sizeof(struct v4l2_format));
uvc_cam.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
uvc_cam.fmt.fmt.pix.width = uvc_cam.width;
uvc_cam.fmt.fmt.pix.height = uvc_cam.height;
uvc_cam.fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
uvc_cam.fmt.fmt.pix.field = V4L2_FIELD_ANY;
ret = ioctl(uvc_cam.fd, VIDIOC_S_FMT, &uvc_cam.fmt);
if (ret < 0) {
printf("Unable to set format\n");
return -1;
}
// 3.3 get fmt
ret = ioctl(uvc_cam.fd, VIDIOC_G_FMT, &uvc_cam.fmt);
if (ret < 0) {
printf("Unable to get format\n");
return -1;
}
else {
printf("\nfmt.type:\t\t%d\n", uvc_cam.fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n", uvc_cam.fmt.fmt.pix.pixelformat & 0xFF, (uvc_cam.fmt.fmt.pix.pixelformat >> 8) & 0xFF, (uvc_cam.fmt.fmt.pix.pixelformat >> 16) & 0xFF, (uvc_cam.fmt.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.height:\t\t%d\n", uvc_cam.fmt.fmt.pix.height);
printf("pix.width:\t\t%d\n", uvc_cam.fmt.fmt.pix.width);
printf("pix.field:\t\t%d\n", uvc_cam.fmt.fmt.pix.field);
}
// 4. set fps
memset(&uvc_cam.setfps, 0, sizeof(struct v4l2_streamparm));
uvc_cam.setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
uvc_cam.setfps.parm.capture.timeperframe.numerator = 1;
uvc_cam.setfps.parm.capture.timeperframe.denominator = 25;
ret = ioctl(uvc_cam.fd, VIDIOC_S_PARM, &uvc_cam.setfps);
if (ret < 0) {
printf("Unable to set frame rate\n");
return -1;
}
else {
printf("set fps OK!\n");
}
ret = ioctl(uvc_cam.fd, VIDIOC_G_PARM, &uvc_cam.setfps);
if (ret < 0) {
printf("Unable to get frame rate\n");
return -1;
}
else {
printf("get fps OK:\n");
printf("timeperframe.numerator : %d\n", uvc_cam.setfps.parm.capture.timeperframe.numerator);
printf("timeperframe.denominator: %d\n", uvc_cam.setfps.parm.capture.timeperframe.denominator);
printf("set fps : %d\n", 1 * uvc_cam.setfps.parm.capture.timeperframe.denominator / uvc_cam.setfps.parm.capture.timeperframe.numerator);
}
// 5. enum framesizes
while (1) {
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
fmtdesc.index = i++;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(uvc_cam.fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0) {
break;
}
printf("Supported format: %s\n", fmtdesc.description);
struct v4l2_frmsizeenum fsenum;
memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));
fsenum.pixel_format = uvc_cam.fmtdesc.pixelformat;
int j = 0;
while (1) {
fsenum.index = j;
j++;
if (ioctl(uvc_cam.fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0) {
if (uvc_cam.fmt.fmt.pix.pixelformat == fmtdesc.pixelformat) {
printf("\tSupported size with the current format: %dx%d\n", fsenum.discrete.width, fsenum.discrete.height);
}
else {
printf("\tSupported size: %dx%d\n", fsenum.discrete.width, fsenum.discrete.height);
}
}
else {
break;
}
}
}
// 6. request buffers
memset(&uvc_cam.rb, 0, sizeof(struct v4l2_requestbuffers));
uvc_cam.rb.count = NB_BUFFER;
uvc_cam.rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
uvc_cam.rb.memory = V4L2_MEMORY_MMAP;
ret = ioctl(uvc_cam.fd, VIDIOC_REQBUFS, &uvc_cam.rb);
if (ret < 0) {
printf("Unable to allocate buffers\n");
return -1;
}
// 6.1 map the buffers
for (i = 0; i < NB_BUFFER; i++) {
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(uvc_cam.fd, VIDIOC_QUERYBUF, &buf);
if (ret < 0) {
printf("Unable to query buffer\n");
return -1;
}
uvc_cam.mem[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, uvc_cam.fd, buf.m.offset);
if (uvc_cam.mem[i] == MAP_FAILED) {
printf("Unable to map buffer\n");
return -1;
}
uvc_cam.memlength[i] = buf.length;
}
// 6.2 queue the buffers.
for (i = 0; i < NB_BUFFER; i++) {
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.index = i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(uvc_cam.fd, VIDIOC_QBUF, &buf);
if (ret < 0) {
printf("Unable to queue buffer\n");
return -1;
}
}
// 7. malloc yuyv buf
uvc_cam.framesizeIn = uvc_cam.width * uvc_cam.height << 1; // w * h * 2
uvc_cam.framebuffer = (unsigned char*)calloc(1, (size_t)uvc_cam.framesizeIn);
if (uvc_cam.framebuffer == NULL) {
printf("err calloc memory\n");
return -1;
}
printf("init %s \t[OK]\n", uvc_cam.videodevice);
return 0;
}
void v4l2_exit(void)
{
free(uvc_cam.framebuffer);
close(uvc_cam.fd);
}
int v4l2_enable(void)
{
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int ret;
ret = ioctl(uvc_cam.fd, VIDIOC_STREAMON, &type);
if (ret < 0) {
printf("Unable to start capture\n");
return ret;
}
printf("start capture\n");
return 0;
}
int v4l2_disable(void)
{
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int ret;
ret = ioctl(uvc_cam.fd, VIDIOC_STREAMOFF, &type);
if (ret < 0) {
printf("Unable to stop capture\n");
return ret;
}
printf("stop capture\n");
return 0;
}
int v4l2_uvc_grap(void)
{
int ret;
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(uvc_cam.fd, VIDIOC_DQBUF, &buf);
if (ret < 0) {
printf("Unable to dequeue buffer\n");
exit(1);
}
memcpy(uvc_cam.framebuffer, uvc_cam.mem[buf.index], uvc_cam.framesizeIn);
ioctl(uvc_cam.fd, VIDIOC_QBUF, &buf);
// printf("buf index: %d\n", buf.index);
return 0;
}
int save_bmp(char* bmp_name)
{
FILE* fp;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
printf("save bmp function\n");
fp = fopen(bmp_name, "wb");
if (fp == NULL) {
printf("open errror\n");
return (-1);
}
//Set BITMAPINFOHEADER
memset(&bi, 0, sizeof(BITMAPINFOHEADER));
bi.ciSize = 40;
bi.ciWidth = IMAGEWIDTH;
bi.ciHeight = IMAGEHEIGHT;
bi.ciPlanes = 1;
bi.ciBitCount = 24;
bi.ciSizeImage = IMAGEWIDTH * IMAGEHEIGHT * 3;
//Set BITMAPFILEHEADER
memset(&bf, 0, sizeof(BITMAPFILEHEADER));
bf.cfType[0] = 'B';
bf.cfType[1] = 'M';
bf.cfSize = 54 + bi.ciSizeImage;
bf.cfReserved = 0;
bf.cfoffBits = 54;
fwrite(&bf, 14, 1, fp);
fwrite(&bi, 40, 1, fp);
fwrite(rgb888_buffer, bi.ciSizeImage, 1, fp);
printf("save %s done\n", bmp_name);
fclose(fp);
return 0;
}
int fb_init(void)
{
int fb_size;
fbfd = open("/dev/fb0", O_RDWR | O_CREAT, S_IRWXU);
if (fbfd < 0) {
printf("open error\n");
exit(1);
}
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
printf("Error reading variable information.\n");
exit(1);
}
vinfo.yres_virtual = vinfo.yres * 2;
if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)) {
printf("Error put variable information.\n");
exit(1);
}
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
printf("Error reading variable information.\n");
exit(1);
}
printf("R:%d,G:%d,B:%d \n", vinfo.red, vinfo.green, vinfo.blue);
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
xres = vinfo.xres;
yres = vinfo.yres;
bits_per_pixel = vinfo.bits_per_pixel;
fb_size = vinfo.yres * finfo.line_length;
printf("yres_virtual\t: %d \n", vinfo.yres_virtual);
printf("smem_len\t: %d \n", finfo.smem_len);
printf("fbsize\t\t: %d \n", fb_size);
if ((fb_addr = (char*)mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0)) < 0) {
printf("fb mmap error\n");
exit(1);
}
fb_buf1 = (char*)calloc(1, fb_size);
if (fb_buf1 == NULL) {
printf("fb buf1 calloc err!\n");
}
printf("fb init ok!\n");
return fb_size;
}
void bmp_data_to_fbbuf(char* pbuf, int fb_size)
{
int line_x, line_y;
long int location = 0;
static unsigned short b, g, r;
static unsigned short rgb; /*2个字节*/
PIXEL pix;
int pixcnt = 0;
char* psrc = pbuf;
pixcnt = 0;
// --每次处理一个像素
for (line_y = 0; line_y < IMAGEHEIGHT; line_y++) {
for (line_x = 0; line_x < IMAGEWIDTH; line_x++) {
//-- ok
pix.blue = psrc[pixcnt++];
pix.green = psrc[pixcnt++];
pix.red = psrc[pixcnt++];
r = ((pix.red >> 3) << 11) & 0xf800;
g = ((pix.green >> 2) << 5) & 0x07e0;
b = (pix.blue >> 3) & 0x001f;
rgb = r | g | b;
location = line_x * 2 + (IMAGEHEIGHT - 1 - line_y) * xres * 2;
//--先放到缓存里面
*((unsigned short*)(fb_buf1 + location)) = rgb;
}
}
}
void fb_update(int fb_size)
{
// --更新整屏
memcpy(fb_addr, fb_buf1, fb_size);
}
// ok 双缓冲测试成功,没有撕裂了!
void framebuffer_doublebuf_update(int fb_size)
{
int ret;
static int buf_index = 0;
if (buf_index) {
// --更新整屏 offline
vinfo.yoffset = vinfo.yres;
memcpy(fb_addr + vinfo.yoffset * finfo.line_length, fb_buf1, fb_size);
ret = ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);
if (ret) {
printf("----FBIOPAN_DISPLAY-failed: %d \n", ret);
}
printf("fb online buf : %d \n", buf_index);
buf_index = 0;
}
else {
// --更新整屏 offline
memcpy(fb_addr, fb_buf1, fb_size);
vinfo.yoffset = 0;
ret = ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);
if (ret) {
printf("----FBIOPAN_DISPLAY-failed: %d \n", ret);
}
printf("fb online buf : %d \n", buf_index);
buf_index = 1;
}
}
int main(int argc, char const* argv[])
{
char vdname[15];
int fb_size;
printf("\n----- v4l2 run on fb doublebuf app start ----- \n");
if (argc < 2) {
printf("need:/dev/videox\n");
printf("like:%s /dev/video1\n", argv[0]);
printf("app exit.\n\n");
exit(1);
}
snprintf(vdname, 12, argv[1]);
memset(&uvc_cam, 0, sizeof(struct vdIn));
uvc_cam.videodevice = vdname;
printf("using: \t\t%s\n", uvc_cam.videodevice);
uvc_cam.width = _UVC_CAM_WIDTH;
uvc_cam.height = _UVC_CAM_HEIGHT;
// 1. init cam
if (v4l2_init() < 0) {
goto app_exit;
}
fb_size = fb_init();
v4l2_enable();
usleep(5 * 1000);
while (1) {
// 2. grap uvc
v4l2_uvc_grap();
yuyv_to_rgb888();
// 3. show on fb
bmp_data_to_fbbuf(rgb888_buffer, fb_size);
framebuffer_doublebuf_update(fb_size);
}
app_exit:
printf("app exit.\n\n");
v4l2_exit();
return 0;
}
/*************************** (C) COPYRIGHT 2020 ZYS ************END OF FILE****/
#### 四、测试程序
- 运行程序
先把程序复制到开发板,推荐用NFS或TFTP。
# ./v4l2grap-uvc-bmponfb-dbuf-run-x /dev/video1
----- v4l2 run on fb doublebuf app start -----
using: /dev/video1
driver: uvcvideo
card: Aoni HD Camera
bus_info: usb-ci_hdrc.1-1.4
version: 262415
capabilities: 84200001
/dev/video1: supports capture.
/dev/video1: supports streaming.
Support format:
1.MJPEG
2.YUV 4:2:2 (YUYV)
fmt.type: 1
pix.pixelformat: YUYV
pix.height: 480
pix.width: 640
pix.field: 1
set fps OK!
get fps OK:
timeperframe.numerator : 1
timeperframe.denominator: 25
set fps : 25
Supported format: MJPEG
Supported size: 640x480
Supported size: 1280x720
Supported size: 1184x656
Supported size: 1024x576
Supported size: 960x720
Supported size: 960x540
Supported size: 864x486
Supported size: 800x600
Supported size: 752x423
Supported size: 640x360
Supported size: 320x240
Supported format: YUV 4:2:2 (YUYV)
Supported size with the current format: 640x480
Supported size with the current format: 1280x720
Supported size with the current format: 1184x656
Supported size with the current format: 1024x576
Supported size with the current format: 960x720
Supported size with the current format: 960x540
Supported size with the current format: 864x486
Supported size with the current format: 800x600
Supported size with the current format: 752x423
Supported size with the current format: 640x360
Supported size with the current format: 320x240
init /dev/video1 [OK]
R:11,G:5,B:0
1024x600, 16bpp
yres_virtual : 1200
smem_len : 33554432
fbsize : 1228800
fb init ok!
start capture
fb online buf : 0
fb online buf : 1
fb online buf : 0
fb online buf : 1
fb online buf : 0
fb online buf : 1
fb online buf : 0
fb online buf : 1
fb online buf : 0
fb online buf : 1
fb online buf : 0
fb online buf : 1
^C
#
- 预览效果
只是没了撕裂现象,此处略。
本篇完。
离线
这么好的帖子没人要顶一下吗?
离线
厉害,感觉以后会用上。
离线
很好的帖子顶一下
离线
楼主MD用得炉火纯青
离线
占楼
离线
有时间实践验证一下,手上有块韦的板。
离线