I am working on an embedded Linux system (kernel-5.10.24), where there is a UVC (connected to USB 2.0 port of the system) and a LCD in the system.
Now I am trying to show the UVC image onto the LCD in real-time, so I tried following project of v4l2_framebuffer. It captured the UVC image in YUYV format, converted the image to RGB and wrote the RGB data into framebuffer directly.
It showed the UVC image on LCD smoothly if the image is small (UVC is configured like 640x480 or 800x600), but it performed bad when the image is bigger, such as 1280*720.
I checked its code to convert YUYV to RGB and show RGB to framebuffer, as follows.
static void parse_im(const unsigned char *im_yuv, unsigned char *dst, int width, int height) {
const int IM_SIZE = width * height;
unsigned char Y = 0;
unsigned char U = 0;
unsigned char V = 0;
int B = 0;
int G = 0;
int R = 0;
int i;
for(i = 0; i < IM_SIZE; ++i){
if(!(i & 1)){
U = im_yuv[2 * i + 1];
V = im_yuv[2 * i + 3];
}
Y = im_yuv[2 * i];
B = Y + 1.773 * (U - 128);
G = Y - 0.344 * (U - 128) - (0.714 * (V - 128));
R = Y + 1.403 * (V - 128);
if(B > UCHAR_MAX){
B = UCHAR_MAX;
}
if(G > UCHAR_MAX){
G = UCHAR_MAX;
}
if(R > UCHAR_MAX){
R = UCHAR_MAX;
}
dst[3*i] = B;
dst[3*i+1] = G;
dst[3*i+2] = R;
}
}
....
void draw_framebuffer(unsigned char* src, int width, int height)
{
int x, y;
unsigned int location = 0;
int i = 0;
for(y = 0; y < height; y++)
{
for(x = 0; x < width; x++)
{
location = (x + vinfo.xoffset) * (vinfo.bits_per_pixel >> 3) + (y + vinfo.yoffset) * finfo.line_length;
*(fbp + location) = src[i*3]; //B
*(fbp + location + 1) = src[i*3 + 1]; //G
*(fbp + location + 2) = src[i*3 + 2]; //R
i++;
}
}
}
parse_im() converts the YUYV to RGB, and draw_framebuffer() writes RGB data into framebuffer. I am not sure if it is the optimized way to do that, but it performed worse with larger image.
Then, I tried another way by capturing UVC image in MJPEG format and showing the decoded JPEG image into framebuffer (using libjpeg to decode the MJPEG in memory).
To my surprise, it performed better (more smoothly) than 1st implementation.
Here is the code to decode and show JPEG onto framebuffer.
int show_jpeg_on_fb(unsigned char *jpeg_data, size_t jpeg_size)
{
// Initialize JPEG decompression
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr err;
cinfo.err = jpeg_std_error(&err);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, jpeg_data, jpeg_size);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
// Allocate input buffer
JSAMPARRAY input_buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW) * cinfo.output_height);
for (int i = 0; i < cinfo.output_height; i++) {
input_buffer[i] = (JSAMPROW)malloc(sizeof(JSAMPLE) * cinfo.output_width * cinfo.output_components);
}
// Allocate output buffer
uint32_t *output_buffer = (uint32_t *)malloc(sizeof(uint32_t) * varinfo.xres * varinfo.yres);
// Read image data row by row
while (cinfo.output_scanline < cinfo.output_height) {
int i = cinfo.output_scanline;
jpeg_read_scanlines(&cinfo, &input_buffer[i], 1);
}
// Convert image data to ARGB format
for (int j = 0; j < varinfo.yres; j++) {
for (int i = 0; i < varinfo.xres; i++) {
int src_i = i * cinfo.output_width / varinfo.xres;
int src_j = j * cinfo.output_height / varinfo.yres;
int r = input_buffer[src_j][src_i * cinfo.output_components];
int g = input_buffer[src_j][src_i * cinfo.output_components + 1];
int b = input_buffer[src_j][src_i * cinfo.output_components + 2];
output_buffer[j * varinfo.xres + i] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
// Write output buffer to framebuffer memory
memcpy(framebuffer, output_buffer, screensize);
....
The jpeg_data and jpeg_size are the MJPEG image and its size in memory captured by UVC.
The testing results are out of my expectation, my assumption is using MJPEG will involve decoding process in software, so its performance should be worse than using YUYV (plus YUYVtoRGB).
I am wondering what causes the difference, and is it possible to optimize the codes, esp. the v4l2_framebuffer project??
Some one told me the slowness of showing bigger YUYV image in LCD may come from the limitation of USB 2.0 port, whose data rate is about 20MB, which is not quick enough to deliver 1280 * 960 YUYV4:2:2 image. I am not quite sure about it, I will run testing to check how fast the CPU can get and save a YUYV image (of 1280*960), and compare that with MJPEG image.