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