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_UYVY;
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 mFrameReady = false;
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 // Get the information on the buffer that was created for us
168 memset(&mBufferInfo, 0, sizeof(mBufferInfo));
169 mBufferInfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
170 mBufferInfo.memory = V4L2_MEMORY_MMAP;
171 mBufferInfo.index = 0;
172 if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfo) < 0) {
173 PLOG(ERROR) << "VIDIOC_QUERYBUF failed";
174 return false;
175 }
176
177 LOG(INFO) << "Buffer description:";
178 LOG(INFO) << " offset: " << mBufferInfo.m.offset;
179 LOG(INFO) << " length: " << mBufferInfo.length;
180 LOG(INFO) << " flags : " << std::hex << mBufferInfo.flags;
181
182 // Get a pointer to the buffer contents by mapping into our address space
183 mPixelBuffer = mmap(
184 NULL,
185 mBufferInfo.length,
186 PROT_READ | PROT_WRITE,
187 MAP_SHARED,
188 mDeviceFd,
189 mBufferInfo.m.offset
190 );
191 if( mPixelBuffer == MAP_FAILED) {
192 PLOG(ERROR) << "mmap() failed";
193 return false;
194 }
195 memset(mPixelBuffer, 0, mBufferInfo.length);
196 LOG(INFO) << "Buffer mapped at " << mPixelBuffer;
197
198 // Queue the first capture buffer
199 if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
200 PLOG(ERROR) << "VIDIOC_QBUF failed";
201 return false;
202 }
203
204 // Start the video stream
205 int type = mBufferInfo.type;
206 if (ioctl(mDeviceFd, VIDIOC_STREAMON, &type) < 0) {
207 PLOG(ERROR) << "VIDIOC_STREAMON failed";
208 return false;
209 }
210
211 // Remember who to tell about new frames as they arrive
212 mCallback = callback;
213
214 // Fire up a thread to receive and dispatch the video frames
215 mCaptureThread = std::thread([this](){ collectFrames(); });
216
217 LOG(DEBUG) << "Stream started.";
218 return true;
219 }
220
221
stopStream()222 void VideoCapture::stopStream() {
223 // Tell the background thread to stop
224 int prevRunMode = mRunMode.fetch_or(STOPPING);
225 if (prevRunMode == STOPPED) {
226 // The background thread wasn't running, so set the flag back to STOPPED
227 mRunMode = STOPPED;
228 } else if (prevRunMode & STOPPING) {
229 LOG(ERROR) << "stopStream called while stream is already stopping. "
230 << "Reentrancy is not supported!";
231 return;
232 } else {
233 // Block until the background thread is stopped
234 if (mCaptureThread.joinable()) {
235 mCaptureThread.join();
236 }
237
238 // Stop the underlying video stream (automatically empties the buffer queue)
239 int type = mBufferInfo.type;
240 if (ioctl(mDeviceFd, VIDIOC_STREAMOFF, &type) < 0) {
241 PLOG(ERROR) << "VIDIOC_STREAMOFF failed";
242 }
243
244 LOG(DEBUG) << "Capture thread stopped.";
245 }
246
247 // Unmap the buffers we allocated
248 munmap(mPixelBuffer, mBufferInfo.length);
249
250 // Tell the L4V2 driver to release our streaming buffers
251 v4l2_requestbuffers bufrequest;
252 bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
253 bufrequest.memory = V4L2_MEMORY_MMAP;
254 bufrequest.count = 0;
255 ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest);
256
257 // Drop our reference to the frame delivery callback interface
258 mCallback = nullptr;
259 }
260
261
markFrameReady()262 void VideoCapture::markFrameReady() {
263 mFrameReady = true;
264 };
265
266
returnFrame()267 bool VideoCapture::returnFrame() {
268 // We're giving the frame back to the system, so clear the "ready" flag
269 mFrameReady = false;
270
271 // Requeue the buffer to capture the next available frame
272 if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
273 PLOG(ERROR) << "VIDIOC_QBUF failed";
274 return false;
275 }
276
277 return true;
278 }
279
280
281 // This runs on a background thread to receive and dispatch video frames
collectFrames()282 void VideoCapture::collectFrames() {
283 // Run until our atomic signal is cleared
284 while (mRunMode == RUN) {
285 // Wait for a buffer to be ready
286 if (ioctl(mDeviceFd, VIDIOC_DQBUF, &mBufferInfo) < 0) {
287 PLOG(ERROR) << "VIDIOC_DQBUF failed";
288 break;
289 }
290
291 markFrameReady();
292
293 // If a callback was requested per frame, do that now
294 if (mCallback) {
295 mCallback(this, &mBufferInfo, mPixelBuffer);
296 }
297 }
298
299 // Mark ourselves stopped
300 LOG(DEBUG) << "VideoCapture thread ending";
301 mRunMode = STOPPED;
302 }
303
304
setParameter(v4l2_control & control)305 int VideoCapture::setParameter(v4l2_control& control) {
306 int status = ioctl(mDeviceFd, VIDIOC_S_CTRL, &control);
307 if (status < 0) {
308 PLOG(ERROR) << "Failed to program a parameter value "
309 << "id = " << std::hex << control.id;
310 }
311
312 return status;
313 }
314
315
getParameter(v4l2_control & control)316 int VideoCapture::getParameter(v4l2_control& control) {
317 int status = ioctl(mDeviceFd, VIDIOC_G_CTRL, &control);
318 if (status < 0) {
319 PLOG(ERROR) << "Failed to read a parameter value"
320 << " fd = " << std::hex << mDeviceFd
321 << " id = " << control.id;
322 }
323
324 return status;
325 }
326
327
enumerateCameraControls()328 std::set<uint32_t> VideoCapture::enumerateCameraControls() {
329 // Retrieve available camera controls
330 struct v4l2_queryctrl ctrl = {
331 .id = V4L2_CTRL_FLAG_NEXT_CTRL
332 };
333
334 std::set<uint32_t> ctrlIDs;
335 while (0 == ioctl(mDeviceFd, VIDIOC_QUERYCTRL, &ctrl)) {
336 if (!(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) {
337 ctrlIDs.emplace(ctrl.id);
338 }
339
340 ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
341 }
342
343 if (errno != EINVAL) {
344 PLOG(WARNING) << "Failed to run VIDIOC_QUERYCTRL";
345 }
346
347 return std::move(ctrlIDs);
348 }
349