Невозможно направить видео с веб-камеры на виртуальное видеоустройство в 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 );
}
}