-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathCameraCaptureThread.cpp
159 lines (133 loc) · 5.03 KB
/
CameraCaptureThread.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "CameraCaptureThread.h"
#include <QImage>
#include <QMutex>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <QDebug>
#include <libv4lconvert.h>
#include <stdint.h>
static const int v4l2BufferNum = 2;
struct CameraCaptureThread::Private {
QMutex mutex;
QImage image;
int fd;
void *v4l2Buffer[v4l2BufferNum];
uint32_t v4l2BufferSize[v4l2BufferNum];
v4l2_format srcfmt;
v4l2_format dstfmt;
v4lconvert_data *convert_data = nullptr;
};
CameraCaptureThread::CameraCaptureThread()
: m(new Private)
{
}
CameraCaptureThread::~CameraCaptureThread()
{
delete m;
}
void CameraCaptureThread::startCapture()
{
m->fd = open("/dev/video0", O_RDWR);
/* 1. フォーマット指定。640x480のRGB24形式でキャプチャしてください */
v4l2_format tmpfmt;
memset(&tmpfmt, 0, sizeof(tmpfmt));
tmpfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(m->fd, VIDIOC_G_FMT, &tmpfmt);
m->dstfmt = m->srcfmt = tmpfmt;
m->dstfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
m->convert_data = v4lconvert_create(m->fd);
v4lconvert_try_format(m->convert_data, &m->dstfmt, &m->srcfmt);
m->srcfmt = tmpfmt;
/* 2. バッファリクエスト。バッファを2面確保してください */
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(req));
req.count = v4l2BufferNum;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(m->fd, VIDIOC_REQBUFS, &req);
/* 3. 確保されたバッファをユーザプログラムからアクセスできるようにmmapする */
struct v4l2_buffer buf;
for (uint32_t i = 0; i < v4l2BufferNum; i++) {
/* 3.1 確保したバッファ情報を教えてください */
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl(m->fd, VIDIOC_QUERYBUF, &buf);
/* 3.2 取得したバッファ情報を基にmmapして、後でアクセスできるようにアドレスを保持っておく */
m->v4l2Buffer[i] = mmap(NULL, buf.length, PROT_READ, MAP_SHARED, m->fd, buf.m.offset);
m->v4l2BufferSize[i] = buf.length;
}
/* 4. バッファのエンキュー。指定するバッファをキャプチャするときに使ってください */
for (uint32_t i = 0; i < v4l2BufferNum; i++) {
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl(m->fd, VIDIOC_QBUF, &buf);
}
/* 5. ストリーミング開始。キャプチャを開始してください */
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(m->fd, VIDIOC_STREAMON, &type);
/* この例だと2面しかないので、2フレームのキャプチャ(1/30*2秒?)が終わった後、新たにバッファがエンキューされるまでバッファへの書き込みは行われない、はず */
}
void CameraCaptureThread::copyBuffer()
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(m->fd, &fds);
/* 6. バッファに画データが書き込まれるまで待つ */
while (select(m->fd + 1, &fds, NULL, NULL, NULL) < 0) {
if (isInterruptionRequested()) return;
}
if (FD_ISSET(m->fd, &fds)) {
/* 7. バッファのデキュー。もっとも古くキャプチャされたバッファをデキューして、そのインデックス番号を教えてください */
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(m->fd, VIDIOC_DQBUF, &buf);
/* 8. デキューされたバッファのインデックス(buf.index)と書き込まれたサイズ(buf.byteused)が返ってくる */
/* 9. create QImage object */
{
QMutexLocker lock(&m->mutex);
m->image = QImage(m->dstfmt.fmt.pix.width, m->dstfmt.fmt.pix.height, QImage::Format_RGB888);
v4lconvert_convert(m->convert_data, &m->srcfmt, &m->dstfmt, (unsigned char *)m->v4l2Buffer[buf.index], buf.bytesused, m->image.bits(), m->dstfmt.fmt.pix.sizeimage);
// m->image = QImage((uchar const *)m->v4l2Buffer[buf.index], m->dstfmt.fmt.pix.width, m->dstfmt.fmt.pix.height, m->dstfmt.fmt.pix.width * 3, QImage::Format_RGB888);
}
/* 10. 先ほどデキューしたバッファを、再度エンキューする。カメラデバイスはこのバッファに対して再びキャプチャした画を書き込む */
ioctl(m->fd, VIDIOC_QBUF, &buf);
}
}
void CameraCaptureThread::stopCapture()
{
/* 11. ストリーミング終了。キャプチャを停止してください */
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(m->fd, VIDIOC_STREAMOFF, &type);
/* 12. リソース解放 */
for (uint32_t i = 0; i < v4l2BufferNum; i++) munmap(m->v4l2Buffer[i], m->v4l2BufferSize[i]);
/* 13. デバイスファイルを閉じる */
v4lconvert_destroy(m->convert_data);
close(m->fd);
}
void CameraCaptureThread::run()
{
startCapture();
while (1) {
if (isInterruptionRequested()) break;
copyBuffer();
}
stopCapture();
}
QImage CameraCaptureThread::image()
{
QMutexLocker lock(&m->mutex);
QImage im;
std::swap(im, m->image);
return im;
}