]> git.sesse.net Git - bmusb/blob - v4l2proxy.cpp
Release 0.7.8.
[bmusb] / v4l2proxy.cpp
1 //
2 // A helper proxy to send data from bmusb to a V4L2 output.
3 // To get it as a V4L2 _input_, you can use v4l2loopback:
4 //
5 //   sudo apt install v4l2loopback-dkms v4l2loopback-utils
6 //   sudo modprobe v4l2loopback video_nr=2 card_label='Intensity Shuttle (bmusb)' max_width=1280 max_height=720 exclusive_caps=1
7 //   ./bmusb-v4l2proxy /dev/video2
8 //
9 // There is currently no audio support.
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <sys/ioctl.h>
17 #include <linux/videodev2.h>
18 #include <bmusb/bmusb.h>
19 #if __SSE2__
20 #include <immintrin.h>
21 #endif
22 #include <algorithm>
23
24 using namespace std;
25 using namespace bmusb;
26
27 BMUSBCapture *usb;
28 int video_fd;
29
30 void frame_callback(uint16_t timecode,
31                     FrameAllocator::Frame video_frame, size_t video_offset, VideoFormat video_format,
32                     FrameAllocator::Frame audio_frame, size_t audio_offset, AudioFormat audio_format)
33 {
34         printf("0x%04x: %zu video bytes (format 0x%04x, %d x %d)\n",
35                 timecode,
36                 video_frame.len - video_offset, video_format.id, video_format.width, video_format.height);
37
38         static unsigned last_width = 0, last_height = 0, last_stride = 0;
39
40         if (video_format.width != last_width ||
41             video_format.height != last_height ||
42             video_format.stride != last_stride) {
43                 v4l2_format fmt;
44                 memset(&fmt, 0, sizeof(fmt));
45                 fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
46                 fmt.fmt.pix.width = video_format.width;
47                 fmt.fmt.pix.height = video_format.height;
48
49                 // Chrome accepts YUYV, but not our native UYVY. We byteswap below.
50                 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
51
52                 fmt.fmt.pix.field = V4L2_FIELD_NONE;
53                 fmt.fmt.pix.bytesperline = video_format.stride;
54                 fmt.fmt.pix.sizeimage = video_format.stride * video_format.height;
55                 fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
56                 int err = ioctl(video_fd, VIDIOC_S_FMT, &fmt);
57                 if (err == -1) {
58                         perror("ioctl(VIDIOC_S_FMT)");
59                         usb->get_video_frame_allocator()->release_frame(video_frame);
60                         usb->get_audio_frame_allocator()->release_frame(audio_frame);
61                         return;
62                 } else {
63                         last_width = video_format.width;
64                         last_height = video_format.height;
65                         last_stride = video_format.stride;
66                 }
67         }
68
69         if (video_frame.data != nullptr) {
70                 uint8_t *origptr = video_frame.data + video_offset + video_format.extra_lines_top * video_format.stride;
71 #if __SSE2__
72                 __m128i *ptr = (__m128i *)origptr;
73                 for (unsigned i = 0; i < video_format.stride * video_format.height / 16; ++i) {
74                         __m128i val = _mm_loadu_si128(ptr);
75                         val = _mm_slli_epi16(val, 8) | _mm_srli_epi16(val, 8);
76                         _mm_storeu_si128(ptr, val);
77                         ++ptr;
78                 }
79 #else
80                 uint8_t *ptr = origptr;
81                 for (unsigned i = 0; i < video_format.stride * video_format.height / 4; ++i) {
82                         swap(ptr[0], ptr[1]);
83                         swap(ptr[2], ptr[3]);
84                         ptr += 4;
85                 }
86 #endif
87
88                 size_t len = video_frame.len;
89                 while (len > 0) {
90                         ssize_t ret = write(video_fd, origptr, len);
91                         if (ret == -1) {
92                                 if (errno == EINTR) {
93                                         continue;
94                                 } else {
95                                         perror("write");
96                                         break;  // Hope for better luck next frame.
97                                 }
98                         }
99                         origptr += ret;
100                         len -= ret;
101                 }
102         }
103
104         usb->get_video_frame_allocator()->release_frame(video_frame);
105         usb->get_audio_frame_allocator()->release_frame(audio_frame);
106 }
107
108 int main(int argc, char **argv)
109 {
110         const char *filename = (argc >= 2) ? argv[1] : "/dev/video2";
111         video_fd = open(filename, O_RDWR);
112         if (video_fd == -1) {
113                 perror(filename);
114                 exit(1);
115         }
116
117         usb = new BMUSBCapture(0);  // First card.
118         usb->set_frame_callback(frame_callback);
119         usb->configure_card();
120         BMUSBCapture::start_bm_thread();
121         usb->start_bm_capture();
122         sleep(1000000);
123 }
124