]> git.sesse.net Git - nageru/blob - nageru/v4l_output.cpp
f6c4e856bd9a830cda93f5837e57032203506cbd
[nageru] / nageru / v4l_output.cpp
1 #include "v4l_output.h"
2
3 #include <assert.h>
4 #include <fcntl.h>
5 #include <linux/videodev2.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/ioctl.h>
10 #include <unistd.h>
11
12 #include "shared/memcpy_interleaved.h"
13
14 V4LOutput::V4LOutput(const char *device_path, unsigned width, unsigned height)
15         : width(width), height(height),
16           image_size_bytes(width * height + (width / 2) * (height / 2) * 2),
17           yuv420_buf(new uint8_t[image_size_bytes])
18 {
19         video_fd = open(device_path, O_WRONLY);
20         if (video_fd == -1) {
21                 perror(device_path);
22                 exit(1);
23         }
24
25         v4l2_format fmt;
26         memset(&fmt, 0, sizeof(fmt));
27         fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
28         fmt.fmt.pix.width = width;
29         fmt.fmt.pix.height = width;
30         fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
31         fmt.fmt.pix.field = V4L2_FIELD_NONE;
32         fmt.fmt.pix.bytesperline = 0;
33         fmt.fmt.pix.sizeimage = image_size_bytes;
34         fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
35         int err = ioctl(video_fd, VIDIOC_S_FMT, &fmt);
36         if (err == -1) {
37                 perror("ioctl(VIDIOC_S_FMT)");
38                 exit(1);
39         }
40 }
41
42 V4LOutput::~V4LOutput()
43 {
44         close(video_fd);
45 }
46
47 void V4LOutput::send_frame(const uint8_t *data)
48 {
49         // Seemingly NV12 isn't a very common format among V4L2 consumers,
50         // so we convert from our usual NV12 to YUV420. We get an unneeded
51         // memcpy() of the luma data, but hopefully, we'll manage.
52         const size_t luma_size = width * height;
53         const size_t chroma_size = (width / 2) * (height / 2);
54         memcpy(yuv420_buf.get(), data, luma_size);
55         memcpy_interleaved(
56                 yuv420_buf.get() + luma_size,
57                 yuv420_buf.get() + luma_size + chroma_size,
58                 data + luma_size, 2 * chroma_size);
59
60         const uint8_t *ptr = yuv420_buf.get();
61         size_t bytes_left = image_size_bytes;
62         while (bytes_left > 0) {
63                 int err = write(video_fd, ptr, bytes_left);
64                 if (err == -1) {
65                         perror("V4L write");
66                         exit(1);
67                 }
68                 if (err == 0) {
69                         fprintf(stderr, "WARNING: Short V4L write() (only wrote %zu of %zu bytes), skipping rest of frame.\n",
70                                 image_size_bytes - bytes_left, image_size_bytes);
71                         return;
72                 }
73                 assert(err > 0);
74                 bytes_left -= err;
75                 ptr += err;
76         }
77 }