[英]How to render images to /dev/video0 using v4l2loopback?
I've been trying to render images to /dev/video.我一直在尝试将图像渲染到 /dev/video。 I can get something to sort of display but it's somewhat scrambled.我可以得到一些东西来展示,但它有点乱。
I first started off trying to render a normal RGB24 image (based off this example https://stackoverflow.com/a/44648382/3818491 ), but the result (below) was a scrambled image.我首先开始尝试渲染普通的 RGB24 图像(基于此示例https://stackoverflow.com/a/44648382/3818491 ),但结果(下图)是一个打乱的图像。
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <iostream>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <CImg.h>
#define VIDEO_OUT "/dev/video0" // V4L2 Loopack
#define WIDTH 1280
#define HEIGHT 720
int main() {
using namespace cimg_library;
CImg<uint8_t> canvas(WIDTH, HEIGHT, 1, 3);
const uint8_t red[] = {255, 0, 0};
const uint8_t purple[] = {255, 0, 255};
int fd;
if ((fd = open(VIDEO_OUT, O_RDWR)) == -1) {
std::cerr << "Unable to open video output!\n";
return 1;
}
struct v4l2_format vid_format;
vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
if (ioctl(fd, VIDIOC_G_FMT, &vid_format) == -1) {
std::cerr << "Unable to get video format data. Errro: " << errno << '\n';
return 1;
}
size_t framesize = canvas.size();
int width = canvas.width(), height = canvas.height();
vid_format.fmt.pix.width = width;
vid_format.fmt.pix.height = height;
vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
vid_format.fmt.pix.sizeimage = framesize;
vid_format.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &vid_format) == -1) {
std::cerr << "Unable to set video format! Errno: " << errno << '\n';
return 1;
}
std::cout << "Stream running!\n";
while (true) {
canvas.draw_plasma();
canvas.draw_rectangle(
100, 100, 100 + 100, 100 + 100, red, 1);
canvas.draw_text(5,5, "Hello World!", purple);
canvas.draw_text(5, 20, "Image freshly rendered with the CImg Library!", red);
write(fd, canvas.data(), framesize);
}
}
So I checked what (I think) /dev/video expects which seems to be YUV420P.所以我检查了(我认为)/dev/video 所期望的似乎是 YUV420P。
v4l2-ctl --list-formats-ext 130 ↵
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'YU12' (Planar YUV 4:2:0)
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
So I attempted to convert the frame that format (using this code to quickly test).所以我尝试将帧转换为该格式(使用此代码快速测试)。
Adjusting the spec to:将规格调整为:
vid_format.fmt.pix.width = width;
vid_format.fmt.pix.height = height;
vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
vid_format.fmt.pix.sizeimage = width*height*3/2; // size of yuv buffer
vid_format.fmt.pix.field = V4L2_FIELD_NONE;
That results in this (which seems to be from what I've gathered the structure of a yuv420 image but still rendered incorrectly).这导致了这个(这似乎来自我收集的 yuv420 图像的结构,但仍然渲染不正确)。
What does /dev/video0 expect? /dev/video0 期望什么?
After a lot of hacking around, I've managed to generate a valid YUYV video/image to send to /dev/video0.经过大量的修改后,我设法生成了一个有效的 YUYV 视频/图像以发送到 /dev/video0。
First I make a buffer to hold the frame:首先,我制作一个缓冲区来保存框架:
// Allocate buffer for the YUUV frame
std::vector<uint8_t> buffer;
buffer.resize(vid_format.fmt.pix.sizeimage);
Then I write the current canvas to the buffer in YUYV format.然后我将当前的 canvas 以 YUYV 格式写入缓冲区。
bool skip = true;
cimg_forXY(canvas, cx, cy) {
size_t row = cy * width * 2;
uint8_t r, g, b, y;
r = canvas(cx, cy, 0);
g = canvas(cx, cy, 1);
b = canvas(cx, cy, 2);
y = std::clamp<uint8_t>(r * .299000 + g * .587000 + b * .114000, 0, 255);
buffer[row + cx * 2] = y;
if (!skip) {
uint8_t u, v;
u = std::clamp<uint8_t>(r * -.168736 + g * -.331264 + b * .500000 + 128, 0, 255);
v = std::clamp<uint8_t>(r * .500000 + g * -.418688 + b * -.081312 + 128, 0, 255);
buffer[row + (cx - 1) * 2 + 1] = u;
buffer[row + (cx - 1) * 2 + 3] = v;
}
skip = !skip;
}
Note: CImg has RGBtoYUV
has an in-place RGB to YUV conversion, but for some reason calling this on a uint8_t canvas just zeros it.注意:CImg 具有RGBtoYUV
具有就地 RGB 到 YUV 的转换,但由于某种原因,在 uint8_t canvas 上调用它只是将其归零。
It also has get_YUVtoRGB
which (allocates and) returns a CImg<float>
canvas, which I think you multiply each value by 255 to scale to a byte, however, whatever I tried that did not give the correct colour.它还具有get_YUVtoRGB
,它(分配并)返回一个CImg<float>
canvas,我认为您将每个值乘以 255 以缩放为一个字节,但是,无论我尝试什么都没有给出正确的颜色。 Edit: I likely forgot the +128 bias (though I still prefer not reallocating for each frame)编辑:我可能忘记了 +128 偏差(尽管我仍然不喜欢为每一帧重新分配)
My full code is here (if anyone wants to do something similar)https://gist.github.com/MacDue/36199c3f3ca04bd9fd40a1bc2067ef72我的完整代码在这里(如果有人想做类似的事情)https://gist.github.com/MacDue/36199c3f3ca04bd9fd40a1bc2067ef72
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.