From: Steinar H. Gunderson Date: Sun, 5 Apr 2020 15:34:02 +0000 (+0200) Subject: Add a little program to proxy from bmusb to a V4L2 output. X-Git-Tag: 0.7.5~5 X-Git-Url: https://git.sesse.net/?p=bmusb;a=commitdiff_plain;h=92bbe6548d1681d7cfae66e1c42f9770f3eec857 Add a little program to proxy from bmusb to a V4L2 output. --- diff --git a/Makefile b/Makefile index 23b458c..1c66611 100644 --- a/Makefile +++ b/Makefile @@ -11,13 +11,16 @@ SODEV := libbmusb.so SONAME := libbmusb.so.6 SOLIB := libbmusb.so.6.0.0 -all: $(LIB) $(SOLIB) main +all: $(LIB) $(SOLIB) main bmusb-v4l2proxy %.pic.o : %.cpp $(CXX) $(CPPFLAGS) $(CXXFLAGS) -fPIC -o $@ -c $^ main: bmusb.o main.o - $(CXX) -o main $^ $(LDFLAGS) + $(CXX) -o $@ $^ $(LDFLAGS) + +bmusb-v4l2proxy: bmusb.o v4l2proxy.o + $(CXX) -o $@ $^ $(LDFLAGS) # Static library. $(LIB): bmusb.o fake_capture.o diff --git a/v4l2proxy.cpp b/v4l2proxy.cpp new file mode 100644 index 0000000..2bb40aa --- /dev/null +++ b/v4l2proxy.cpp @@ -0,0 +1,108 @@ +// +// A helper proxy to send data from bmusb to a V4L2 output. +// To get it as a V4L2 _input_, you can use v4l2loopback: +// +// sudo apt install v4l2loopback-dkms v4l2loopback-utils +// sudo modprobe v4l2loopback video_nr=2 card_label='Intensity Shuttle (bmusb)' max_width=1280 max_height=720 exclusive_caps=1 +// ./bmusb-v4l2proxy /dev/video2 +// +// There is currently no audio support. + +#include +#include +#include +#include +#include +#include +#include +#if __SSE2__ +#include +#endif + +using namespace bmusb; + +BMUSBCapture *usb; +int video_fd; + +void frame_callback(uint16_t timecode, + FrameAllocator::Frame video_frame, size_t video_offset, VideoFormat video_format, + FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format) +{ + printf("0x%04x: %zu video bytes (format 0x%04x, %d x %d)\n", + timecode, + video_frame.len - video_offset, video_format.id, video_format.width, video_format.height); + + static unsigned last_width = 0, last_height = 0, last_stride = 0; + + if (video_format.width != last_width || + video_format.height != last_height || + video_format.stride != last_stride) { + v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fmt.fmt.pix.width = video_format.width; + fmt.fmt.pix.height = video_format.height; + + // Chrome accepts YUYV, but not our native UYVY. We byteswap below. + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + + fmt.fmt.pix.field = V4L2_FIELD_NONE; + fmt.fmt.pix.bytesperline = video_format.stride; + fmt.fmt.pix.sizeimage = video_format.stride * video_format.height; + fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + int err = ioctl(video_fd, VIDIOC_S_FMT, &fmt); + if (err == -1) { + perror("ioctl(VIDIOC_S_FMT)"); + usb->get_video_frame_allocator()->release_frame(video_frame); + usb->get_audio_frame_allocator()->release_frame(audio_frame); + return; + } else { + last_width = video_format.width; + last_height = video_format.height; + last_stride = video_format.stride; + } + } + + if (video_frame.data != nullptr) { + uint8_t *origptr = video_frame.data + video_offset + video_format.extra_lines_top * video_format.stride; +#if __SSE2__ + __m128i *ptr = (__m128i *)origptr; + for (unsigned i = 0; i < video_format.width * video_format.height / 8; ++i) { + __m128i val = _mm_loadu_si128(ptr); + val = _mm_slli_epi16(val, 8) | _mm_srli_epi16(val, 8); + _mm_storeu_si128(ptr, val); + ++ptr; + } +#else + uint8_t *ptr = origptr; + for (unsigned i = 0; i < video_format.width * video_format.height; ++i) { + swap(ptr[0], ptr[1]); + swap(ptr[2], ptr[3]); + ptr += 4; + } +#endif + + write(video_fd, origptr, video_frame.len); + } + + usb->get_video_frame_allocator()->release_frame(video_frame); + usb->get_audio_frame_allocator()->release_frame(audio_frame); +} + +int main(int argc, char **argv) +{ + const char *filename = (argc >= 2) ? argv[1] : "/dev/video2"; + video_fd = open(filename, O_RDWR); + if (video_fd == -1) { + perror(filename); + exit(1); + } + + usb = new BMUSBCapture(0); // First card. + usb->set_frame_callback(frame_callback); + usb->configure_card(); + BMUSBCapture::start_bm_thread(); + usb->start_bm_capture(); + sleep(1000000); +} +