Как визуализировать изображения в / dev / video0 с помощью v4l2loopback? - PullRequest
1 голос
/ 04 мая 2020

Я пытался рендерить изображения в / dev / video. Я могу получить что-то для отображения, но это немного зашифровано.

Сначала я попытался отрисовать нормальное изображение RGB24 (на основе этого примера { ссылка }), но результат ( ниже) было зашифрованное изображение.

#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);
    }
}

rgb24 broken

Итак, я проверил, что (я думаю) / 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)

Поэтому я попытался преобразовать этот формат кадра (используя этот код для быстрого тестирования).

Настройка spe c на:

    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;

Это приводит к этому (что, по-видимому, связано с тем, что я собрал структуру изображения yuv420, но по-прежнему отображается неправильно).

yuv420p broken

Что ожидает / dev / video0?

1 Ответ

0 голосов
/ 04 мая 2020

После долгих взломов мне удалось создать действительное видео / изображение YUYV для отправки в /dev/video0.

Сначала я создаю буфер для хранения кадра:

// Allocate buffer for the YUUV frame
std::vector<uint8_t> buffer;
buffer.resize(vid_format.fmt.pix.sizeimage);

Затем я записываю текущий холст в буфер в формате 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;
}

Примечание: CImg имеет RGBtoYUV, имеет преобразование RGB в YUV на месте, но по какой-то причине вызывает его на холсте uint8_t просто обнуляет его.

У него также есть get_YUVtoRGB, который (выделяет и) возвращает холст CImg<float>, который, я думаю, вы умножаете каждое значение на 255, чтобы масштабировать до байта, независимо от того, Я старался, чтобы не дать правильный цвет. Изменить: Я, вероятно, забыл +128 смещения (хотя я все еще предпочитаю не перераспределять для каждого кадра)

Мой полный код здесь (если кто-то хочет сделать что-то подобное) https://gist.github.com/MacDue/36199c3f3ca04bd9fd40a1bc2067ef72

...