1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <error.h>
19 #include <errno.h>
20 #include <iomanip>
21 #include <memory.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26
27 #include <android-base/logging.h>
28
29 #include "assert.h"
30
31 #include "VideoCapture.h"
32
33
34 // NOTE: This developmental code does not properly clean up resources in case of failure
35 // during the resource setup phase. Of particular note is the potential to leak
36 // the file descriptor. This must be fixed before using this code for anything but
37 // experimentation.
open(const char * deviceName,const int32_t width,const int32_t height)38 bool VideoCapture::open(const char* deviceName, const int32_t width, const int32_t height) {
39 // If we want a polling interface for getting frames, we would use O_NONBLOCK
40 // int mDeviceFd = open(deviceName, O_RDWR | O_NONBLOCK, 0);
41 mDeviceFd = ::open(deviceName, O_RDWR, 0);
42 if (mDeviceFd < 0) {
43 PLOG(ERROR) << "failed to open device " << deviceName;
44 return false;
45 }
46
47 v4l2_capability caps;
48 {
49 int result = ioctl(mDeviceFd, VIDIOC_QUERYCAP, &caps);
50 if (result < 0) {
51 PLOG(ERROR) << "failed to get device caps for " << deviceName;
52 return false;
53 }
54 }
55
56 // Report device properties
57 LOG(INFO) << "Open Device: " << deviceName << " (fd = " << mDeviceFd << ")";
58 LOG(INFO) << " Driver: " << caps.driver;
59 LOG(INFO) << " Card: " << caps.card;
60 LOG(INFO) << " Version: " << ((caps.version >> 16) & 0xFF)
61 << "." << ((caps.version >> 8) & 0xFF)
62 << "." << (caps.version & 0xFF);
63 LOG(INFO) << " All Caps: " << std::hex << std::setw(8) << caps.capabilities;
64 LOG(INFO) << " Dev Caps: " << std::hex << caps.device_caps;
65
66 // Enumerate the available capture formats (if any)
67 LOG(INFO) << "Supported capture formats:";
68 v4l2_fmtdesc formatDescriptions;
69 formatDescriptions.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
70 for (int i=0; true; i++) {
71 formatDescriptions.index = i;
72 if (ioctl(mDeviceFd, VIDIOC_ENUM_FMT, &formatDescriptions) == 0) {
73 LOG(INFO) << " " << std::setw(2) << i
74 << ": " << formatDescriptions.description
75 << " " << std::hex << std::setw(8) << formatDescriptions.pixelformat
76 << " " << std::hex << formatDescriptions.flags;
77 } else {
78 // No more formats available
79 break;
80 }
81 }
82
83 // Verify we can use this device for video capture
84 if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) ||
85 !(caps.capabilities & V4L2_CAP_STREAMING)) {
86 // Can't do streaming capture.
87 LOG(ERROR) << "Streaming capture not supported by " << deviceName;
88 return false;
89 }
90
91 // Set our desired output format
92 v4l2_format format;
93 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
94 format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
95 format.fmt.pix.width = width;
96 format.fmt.pix.height = height;
97 LOG(INFO) << "Requesting format: "
98 << ((char*)&format.fmt.pix.pixelformat)[0]
99 << ((char*)&format.fmt.pix.pixelformat)[1]
100 << ((char*)&format.fmt.pix.pixelformat)[2]
101 << ((char*)&format.fmt.pix.pixelformat)[3]
102 << "(" << std::hex << std::setw(8) << format.fmt.pix.pixelformat << ")";
103
104 if (ioctl(mDeviceFd, VIDIOC_S_FMT, &format) < 0) {
105 PLOG(ERROR) << "VIDIOC_S_FMT failed";
106 }
107
108 // Report the current output format
109 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
110 if (ioctl(mDeviceFd, VIDIOC_G_FMT, &format) == 0) {
111
112 mFormat = format.fmt.pix.pixelformat;
113 mWidth = format.fmt.pix.width;
114 mHeight = format.fmt.pix.height;
115 mStride = format.fmt.pix.bytesperline;
116
117 LOG(INFO) << "Current output format: "
118 << "fmt=0x" << std::hex << format.fmt.pix.pixelformat
119 << ", " << std::dec << format.fmt.pix.width << " x " << format.fmt.pix.height
120 << ", pitch=" << format.fmt.pix.bytesperline;
121 } else {
122 PLOG(ERROR) << "VIDIOC_G_FMT failed";
123 return false;
124 }
125
126 // Make sure we're initialized to the STOPPED state
127 mRunMode = STOPPED;
128 mFrames.clear();
129
130 // Ready to go!
131 return true;
132 }
133
134
close()135 void VideoCapture::close() {
136 LOG(DEBUG) << __FUNCTION__;
137 // Stream should be stopped first!
138 assert(mRunMode == STOPPED);
139
140 if (isOpen()) {
141 LOG(DEBUG) << "closing video device file handle " << mDeviceFd;
142 ::close(mDeviceFd);
143 mDeviceFd = -1;
144 }
145 }
146
147
startStream(std::function<void (VideoCapture *,imageBuffer *,void *)> callback)148 bool VideoCapture::startStream(std::function<void(VideoCapture*, imageBuffer*, void*)> callback) {
149 // Set the state of our background thread
150 int prevRunMode = mRunMode.fetch_or(RUN);
151 if (prevRunMode & RUN) {
152 // The background thread is already running, so we can't start a new stream
153 LOG(ERROR) << "Already in RUN state, so we can't start a new streaming thread";
154 return false;
155 }
156
157 // Tell the L4V2 driver to prepare our streaming buffers
158 v4l2_requestbuffers bufrequest;
159 bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
160 bufrequest.memory = V4L2_MEMORY_MMAP;
161 bufrequest.count = 1;
162 if (ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest) < 0) {
163 PLOG(ERROR) << "VIDIOC_REQBUFS failed";
164 return false;
165 }
166
167 mNumBuffers = bufrequest.count;
168 mBufferInfos = std::make_unique<v4l2_buffer[]>(mNumBuffers);
169 mPixelBuffers = std::make_unique<void *[]>(mNumBuffers);
170
171 for (int i = 0; i < mNumBuffers; ++i) {
172 // Get the information on the buffer that was created for us
173 memset(&mBufferInfos[i], 0, sizeof(v4l2_buffer));
174 mBufferInfos[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
175 mBufferInfos[i].memory = V4L2_MEMORY_MMAP;
176 mBufferInfos[i].index = i;
177
178 if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfos[i]) < 0) {
179 PLOG(ERROR) << "VIDIOC_QUERYBUF failed";
180 return false;
181 }
182
183 LOG(INFO) << "Buffer description:";
184 LOG(INFO) << " offset: " << mBufferInfos[i].m.offset;
185 LOG(INFO) << " length: " << mBufferInfos[i].length;
186 LOG(INFO) << " flags : " << std::hex << mBufferInfos[i].flags;
187
188 // Get a pointer to the buffer contents by mapping into our address space
189 mPixelBuffers[i] = mmap(
190 NULL,
191 mBufferInfos[i].length,
192 PROT_READ | PROT_WRITE,
193 MAP_SHARED,
194 mDeviceFd,
195 mBufferInfos[i].m.offset
196 );
197
198 if(mPixelBuffers[i] == MAP_FAILED) {
199 PLOG(ERROR) << "mmap() failed";
200 return false;
201 }
202
203 memset(mPixelBuffers[i], 0, mBufferInfos[i].length);
204 LOG(INFO) << "Buffer mapped at " << mPixelBuffers[i];
205
206 // Queue the first capture buffer
207 if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfos[i]) < 0) {
208 PLOG(ERROR) << "VIDIOC_QBUF failed";
209 return false;
210 }
211 }
212
213 // Start the video stream
214 const int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
215 if (ioctl(mDeviceFd, VIDIOC_STREAMON, &type) < 0) {
216 PLOG(ERROR) << "VIDIOC_STREAMON failed";
217 return false;
218 }
219
220 // Remember who to tell about new frames as they arrive
221 mCallback = callback;
222
223 // Fire up a thread to receive and dispatch the video frames
224 mCaptureThread = std::thread([this](){ collectFrames(); });
225
226 LOG(DEBUG) << "Stream started.";
227 return true;
228 }
229
230
stopStream()231 void VideoCapture::stopStream() {
232 // Tell the background thread to stop
233 int prevRunMode = mRunMode.fetch_or(STOPPING);
234 if (prevRunMode == STOPPED) {
235 // The background thread wasn't running, so set the flag back to STOPPED
236 mRunMode = STOPPED;
237 } else if (prevRunMode & STOPPING) {
238 LOG(ERROR) << "stopStream called while stream is already stopping. "
239 << "Reentrancy is not supported!";
240 return;
241 } else {
242 // Block until the background thread is stopped
243 if (mCaptureThread.joinable()) {
244 mCaptureThread.join();
245 }
246
247 // Stop the underlying video stream (automatically empties the buffer queue)
248 const int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
249 if (ioctl(mDeviceFd, VIDIOC_STREAMOFF, &type) < 0) {
250 PLOG(ERROR) << "VIDIOC_STREAMOFF failed";
251 }
252
253 LOG(DEBUG) << "Capture thread stopped.";
254 }
255
256 for (int i = 0; i < mNumBuffers; ++i) {
257 // Unmap the buffers we allocated
258 munmap(mPixelBuffers[i], mBufferInfos[i].length);
259 }
260
261 // Tell the L4V2 driver to release our streaming buffers
262 v4l2_requestbuffers bufrequest;
263 bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
264 bufrequest.memory = V4L2_MEMORY_MMAP;
265 bufrequest.count = 0;
266 ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest);
267
268 // Drop our reference to the frame delivery callback interface
269 mCallback = nullptr;
270
271 // Release capture buffers
272 mNumBuffers = 0;
273 mBufferInfos = nullptr;
274 mPixelBuffers = nullptr;
275 }
276
277
returnFrame(int id)278 bool VideoCapture::returnFrame(int id) {
279 if (mFrames.find(id) == mFrames.end()) {
280 LOG(WARNING) << "Invalid request to return a buffer " << id << " is ignored.";
281 return false;
282 }
283
284 // Requeue the buffer to capture the next available frame
285 if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfos[id]) < 0) {
286 PLOG(ERROR) << "VIDIOC_QBUF failed";
287 return false;
288 }
289
290 // Remove ID of returned buffer from the set
291 mFrames.erase(id);
292
293 return true;
294 }
295
296
297 // This runs on a background thread to receive and dispatch video frames
collectFrames()298 void VideoCapture::collectFrames() {
299 // Run until our atomic signal is cleared
300 while (mRunMode == RUN) {
301 struct v4l2_buffer buf = {
302 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
303 .memory = V4L2_MEMORY_MMAP
304 };
305
306 // Wait for a buffer to be ready
307 if (ioctl(mDeviceFd, VIDIOC_DQBUF, &buf) < 0) {
308 PLOG(ERROR) << "VIDIOC_DQBUF failed";
309 break;
310 }
311
312 mFrames.insert(buf.index);
313
314 // Update a frame metadata
315 mBufferInfos[buf.index] = buf;
316
317 // If a callback was requested per frame, do that now
318 if (mCallback) {
319 mCallback(this, &mBufferInfos[buf.index], mPixelBuffers[buf.index]);
320 }
321 }
322
323 // Mark ourselves stopped
324 LOG(DEBUG) << "VideoCapture thread ending";
325 mRunMode = STOPPED;
326 }
327
328
setParameter(v4l2_control & control)329 int VideoCapture::setParameter(v4l2_control& control) {
330 int status = ioctl(mDeviceFd, VIDIOC_S_CTRL, &control);
331 if (status < 0) {
332 PLOG(ERROR) << "Failed to program a parameter value "
333 << "id = " << std::hex << control.id;
334 }
335
336 return status;
337 }
338
339
getParameter(v4l2_control & control)340 int VideoCapture::getParameter(v4l2_control& control) {
341 int status = ioctl(mDeviceFd, VIDIOC_G_CTRL, &control);
342 if (status < 0) {
343 PLOG(ERROR) << "Failed to read a parameter value"
344 << " fd = " << std::hex << mDeviceFd
345 << " id = " << control.id;
346 }
347
348 return status;
349 }
350
351
enumerateCameraControls()352 std::set<uint32_t> VideoCapture::enumerateCameraControls() {
353 // Retrieve available camera controls
354 struct v4l2_queryctrl ctrl = {
355 .id = V4L2_CTRL_FLAG_NEXT_CTRL
356 };
357
358 std::set<uint32_t> ctrlIDs;
359 while (0 == ioctl(mDeviceFd, VIDIOC_QUERYCTRL, &ctrl)) {
360 if (!(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
361 ctrlIDs.emplace(ctrl.id);
362 }
363
364 ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
365 }
366
367 if (errno != EINVAL) {
368 PLOG(WARNING) << "Failed to run VIDIOC_QUERYCTRL";
369 }
370
371 return std::move(ctrlIDs);
372 }
373