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