Невозможно направить видео с веб-камеры на виртуальное видеоустройство в Linux (через OpenCV)

Я хотел бы поместить наложение видео на входящий поток веб-камеры через OpenCV. В качестве первого шага я пытаюсь транслировать входящее видео с веб-камеры на /dev/video0 к виртуальному видеоустройству в /dev/video1 (V4l2loopback). К сожалению, я не могу переслать поток веб-камеры на устройство v4l2loopback.

Я нашел эту ветку: Как записать / передать в виртуальную веб-камеру, созданную модулем V4L2loopback? но ссылки там действительно не помогли мне.

Получение и просмотр потока с веб-камеры хорошо работает с небольшим демонстрационным кодом со страницы OpenCV. А также пример кода со страницы v4l2loopback, например, для воспроизведения статического видеофайла на /dev/video1 работает хорошо. Я могу смотреть видео при подключении VLC к /dev/video1

Я уже читал, что устройством v4l2loopback нужно управлять с помощью обычных команд драйвера Linux (open, ioctl, write и т. Д.). Таким образом, в OpenCV нет класса-оболочки для записи в устройство обратной связи. Моя веб-камера транслируется с разрешением 640x480 и в формате MJPG. Интересно то, что я могу подключиться к устройству обратной связи с VLC и увидеть правильное разрешение, кодек и FPS, отображаемые при нажатии кнопки воспроизведения. Счетчик времени на индикаторе выполнения также начинает работать. Но экран остается черным (с логотипом VLC).

Что я примерно делаю, так это (кстати: код может не скомпилироваться... я не хочу, чтобы все было здесь загромождено... дайте мне знать, если вам нужно больше подробностей):

int main ( int argc, char **argv ) {
    cv::VideoCapture cap;
    struct v4l2_format vid_format;
    size_t framesize = 640 * 480 * 3; // 3 Bytes per  pixel
    __u8 *buffer = null;
    int fd = null;

    cap.open  ( "/dev/video0" );
    fd = open ( "/dev/video1", O_RDWR );

    memset ( &vid_format, 0, sizeof(vid_format) );
    vid_format.type                = V4L2_BUF_TYPE_VIDEO_OUTPUT;
    vid_format.fmt.pix.width       = cap.get(CV_CAP_PROP_FRAME_WIDTH);
    vid_format.fmt.pix.height      = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
    vid_format.fmt.pix.pixelformat = { 'M', 'J', 'P', 'G' };
    vid_format.fmt.pix.sizeimage   = framesize;
    vid_format.fmt.pix.field       = V4L2_FIELD_NONE;
    vid_format.fmt.pix.colorspace  = V4L2_COLORSPACE_SRGB;
    ioctl ( fd, VIDIOC_S_FMT, &vid_format );

    buffer = (__u8*) malloc ( sizeof(__u8) *framesize );
    memset ( buffer, 0, framesize );

    for(;;) {
        cv::Mat frame;
        cap >> frame;
        write ( fd, &frame.data, framesize );
    }
}

Было бы здорово, если бы кто-нибудь дал мне подсказку, как мне нужно преобразовать данные камеры, чтобы они были приняты VLC.

1 ответ

Решение

После долгих исследований я наконец-то смог разработать рабочее решение. Есть много шагов, которые необходимо выполнить и о которых я подробно расскажу ниже:

генеральный

Как описано в моем вопросе выше, цель состоит в том, чтобы иметь возможность принимать входящий поток веб-камеры и пересылать его на виртуальное видеоустройство, которое, в свою очередь, можно открыть с помощью таких инструментов, как VLC. Это считается первым шагом для того, чтобы иметь возможность выполнять дальнейшие манипуляции с изображениями.

1) v4l2loopback

v4l2loopback - это виртуальное видеоустройство (модуль ядра) для Linux. Источники можно скачать здесь https://github.com/umlaeute/v4l2loopback. Для загрузки необходимо выполнить следующие шаги:

make
sudo make install
sudo modprobe v4l2loopback

Если вы хотите использовать это видеоустройство в Chrome (WebRTC), вам нужно выполнить последнюю строку с дополнительным параметром:

sudo modprobe v4l2loopback exclusive_caps=1

Информация: важно отметить, что для устройства v4l2loopback должно быть установлено то же разрешение, что и для разрешения, которое вы хотите использовать в приведенном ниже примере. Я установил определения в образце FullHD, как вы можете видеть. Если вы хотите, например, 800x600, вам нужно либо изменить значение по умолчанию в коде v4l2loopback перед компиляцией, либо изменить разрешение при вставке модуля через дополнительные параметры строки cmd max_width а также max_height, Модуль ядра работает по умолчанию с разрешением 640х480. Вы можете получить более подробную информацию и все поддерживаемые параметры, используя:

modinfo v4l2loopback

2) OpenCV

OpenCV - это библиотека, которая поддерживает захват и манипулирование живым видео. Для сборки OpenCV перейдите на эту страницу http://docs.opencv.org/3.0-beta/doc/tutorials/introduction/linux_install/linux_install.html где подробно описаны все шаги.

3) Пример кода

Вы можете создать / запустить приведенный ниже пример кода следующим образом:

g++ -ggdb `pkg-config --cflags --libs opencv` sample.cpp -o sample
./sample

Вот код:

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include "opencv2/opencv.hpp"

#define VIDEO_OUT "/dev/video0" // V4L2 Loopack
#define VIDEO_IN  "/dev/video1" // Webcam

#define WIDTH  1920
#define HEIGHT 1080


int main ( int argc, char **argv ) {
    cv::VideoCapture cap;
    struct v4l2_format vid_format;
    size_t framesize = WIDTH * HEIGHT * 3;
    int fd = 0;

    if( cap.open ( VIDEO_IN ) ) {
        cap.set ( cv::CAP_PROP_FRAME_WIDTH , WIDTH  );
        cap.set ( cv::CAP_PROP_FRAME_HEIGHT, HEIGHT );
    } else {
        std::cout << "Unable to open video input!" << std::endl;
    }

    if ( (fd = open ( VIDEO_OUT, O_RDWR )) == -1 )
        printf ("Unable to open video output!");

    memset ( &vid_format, 0, sizeof(vid_format) );
    vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;

    if ( ioctl ( fd, VIDIOC_G_FMT, &vid_format ) == -1 )
        printf ( "Unable to get video format data. Errro: %d\n", errno );

    vid_format.fmt.pix.width       = cap.get ( CV_CAP_PROP_FRAME_WIDTH  );
    vid_format.fmt.pix.height      = cap.get ( CV_CAP_PROP_FRAME_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 )
        printf ( "Unable to set video format! Errno: %d\n", errno );

    cv::Mat frame ( cap.get(CV_CAP_PROP_FRAME_HEIGHT), 
    cap.get(CV_CAP_PROP_FRAME_WIDTH), CV_8UC3 );

    printf ( "Please open the virtual video device (/dev/video<x>) e.g. with VLC\n" );

    while (1) {
        cap >> frame;
        cv::cvtColor ( frame, frame, cv::COLOR_BGR2RGB ); // Webcams sometimes deliver video in BGR not RGB. so we need to convert
        write ( fd, frame.data, framesize );
    }
}
Другие вопросы по тегам