• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <sys/ioctl.h>
24 #include <sys/mman.h>
25 #include <cutils/log.h>
26 
27 #include "assert.h"
28 
29 #include "VideoCapture.h"
30 
31 
32 // NOTE:  This developmental code does not properly clean up resources in case of failure
33 //        during the resource setup phase.  Of particular note is the potential to leak
34 //        the file descriptor.  This must be fixed before using this code for anything but
35 //        experimentation.
open(const char * deviceName)36 bool VideoCapture::open(const char* deviceName) {
37     // If we want a polling interface for getting frames, we would use O_NONBLOCK
38 //    int mDeviceFd = open(deviceName, O_RDWR | O_NONBLOCK, 0);
39     mDeviceFd = ::open(deviceName, O_RDWR, 0);
40     if (mDeviceFd < 0) {
41         ALOGE("failed to open device %s (%d = %s)", deviceName, errno, strerror(errno));
42         return false;
43     }
44 
45     v4l2_capability caps;
46     {
47         int result = ioctl(mDeviceFd, VIDIOC_QUERYCAP, &caps);
48         if (result  < 0) {
49             ALOGE("failed to get device caps for %s (%d = %s)", deviceName, errno, strerror(errno));
50             return false;
51         }
52     }
53 
54     // Report device properties
55     ALOGI("Open Device: %s (fd=%d)", deviceName, mDeviceFd);
56     ALOGI("  Driver: %s", caps.driver);
57     ALOGI("  Card: %s", caps.card);
58     ALOGI("  Version: %u.%u.%u",
59             (caps.version >> 16) & 0xFF,
60             (caps.version >> 8)  & 0xFF,
61             (caps.version)       & 0xFF);
62     ALOGI("  All Caps: %08X", caps.capabilities);
63     ALOGI("  Dev Caps: %08X", caps.device_caps);
64 
65     // Enumerate the available capture formats (if any)
66     ALOGI("Supported capture formats:");
67     v4l2_fmtdesc formatDescriptions;
68     formatDescriptions.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
69     for (int i=0; true; i++) {
70         formatDescriptions.index = i;
71         if (ioctl(mDeviceFd, VIDIOC_ENUM_FMT, &formatDescriptions) == 0) {
72             ALOGI("  %2d: %s 0x%08X 0x%X",
73                    i,
74                    formatDescriptions.description,
75                    formatDescriptions.pixelformat,
76                    formatDescriptions.flags
77             );
78         } else {
79             // No more formats available
80             break;
81         }
82     }
83 
84     // Verify we can use this device for video capture
85     if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) ||
86         !(caps.capabilities & V4L2_CAP_STREAMING)) {
87         // Can't do streaming capture.
88         ALOGE("Streaming capture not supported by %s.", deviceName);
89         return false;
90     }
91 
92     // Set our desired output format
93     v4l2_format format;
94     format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
95     format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; // Could/should we request V4L2_PIX_FMT_NV21?
96     format.fmt.pix.width = 720;                     // TODO:  Can we avoid hard coding dimensions?
97     format.fmt.pix.height = 240;                    // For now, this works with available hardware
98     format.fmt.pix.field = V4L2_FIELD_ALTERNATE;    // TODO:  Do we need to specify this?
99     ALOGI("Requesting format %c%c%c%c (0x%08X)",
100           ((char*)&format.fmt.pix.pixelformat)[0],
101           ((char*)&format.fmt.pix.pixelformat)[1],
102           ((char*)&format.fmt.pix.pixelformat)[2],
103           ((char*)&format.fmt.pix.pixelformat)[3],
104           format.fmt.pix.pixelformat);
105     if (ioctl(mDeviceFd, VIDIOC_S_FMT, &format) < 0) {
106         ALOGE("VIDIOC_S_FMT: %s", strerror(errno));
107     }
108 
109     // Report the current output format
110     format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
111     if (ioctl(mDeviceFd, VIDIOC_G_FMT, &format) == 0) {
112 
113         mFormat = format.fmt.pix.pixelformat;
114         mWidth  = format.fmt.pix.width;
115         mHeight = format.fmt.pix.height;
116         mStride = format.fmt.pix.bytesperline;
117 
118         ALOGI("Current output format:  fmt=0x%X, %dx%d, pitch=%d",
119                format.fmt.pix.pixelformat,
120                format.fmt.pix.width,
121                format.fmt.pix.height,
122                format.fmt.pix.bytesperline
123         );
124     } else {
125         ALOGE("VIDIOC_G_FMT: %s", strerror(errno));
126         return false;
127     }
128 
129     // Make sure we're initialized to the STOPPED state
130     mRunMode = STOPPED;
131     mFrameReady = false;
132 
133     // Ready to go!
134     return true;
135 }
136 
137 
close()138 void VideoCapture::close() {
139     ALOGD("VideoCapture::close");
140     // Stream should be stopped first!
141     assert(mRunMode == STOPPED);
142 
143     if (isOpen()) {
144         ALOGD("closing video device file handled %d", mDeviceFd);
145         ::close(mDeviceFd);
146         mDeviceFd = -1;
147     }
148 }
149 
150 
startStream(std::function<void (VideoCapture *,imageBuffer *,void *)> callback)151 bool VideoCapture::startStream(std::function<void(VideoCapture*, imageBuffer*, void*)> callback) {
152     // Set the state of our background thread
153     int prevRunMode = mRunMode.fetch_or(RUN);
154     if (prevRunMode & RUN) {
155         // The background thread is already running, so we can't start a new stream
156         ALOGE("Already in RUN state, so we can't start a new streaming thread");
157         return false;
158     }
159 
160     // Tell the L4V2 driver to prepare our streaming buffers
161     v4l2_requestbuffers bufrequest;
162     bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
163     bufrequest.memory = V4L2_MEMORY_MMAP;
164     bufrequest.count = 1;
165     if (ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest) < 0) {
166         ALOGE("VIDIOC_REQBUFS: %s", strerror(errno));
167         return false;
168     }
169 
170     // Get the information on the buffer that was created for us
171     memset(&mBufferInfo, 0, sizeof(mBufferInfo));
172     mBufferInfo.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
173     mBufferInfo.memory   = V4L2_MEMORY_MMAP;
174     mBufferInfo.index    = 0;
175     if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfo) < 0) {
176         ALOGE("VIDIOC_QUERYBUF: %s", strerror(errno));
177         return false;
178     }
179 
180     ALOGI("Buffer description:");
181     ALOGI("  offset: %d", mBufferInfo.m.offset);
182     ALOGI("  length: %d", mBufferInfo.length);
183 
184     // Get a pointer to the buffer contents by mapping into our address space
185     mPixelBuffer = mmap(
186             NULL,
187             mBufferInfo.length,
188             PROT_READ | PROT_WRITE,
189             MAP_SHARED,
190             mDeviceFd,
191             mBufferInfo.m.offset
192     );
193     if( mPixelBuffer == MAP_FAILED) {
194         ALOGE("mmap: %s", strerror(errno));
195         return false;
196     }
197     memset(mPixelBuffer, 0, mBufferInfo.length);
198     ALOGI("Buffer mapped at %p", mPixelBuffer);
199 
200     // Queue the first capture buffer
201     if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
202         ALOGE("VIDIOC_QBUF: %s", strerror(errno));
203         return false;
204     }
205 
206     // Start the video stream
207     int type = mBufferInfo.type;
208     if (ioctl(mDeviceFd, VIDIOC_STREAMON, &type) < 0) {
209         ALOGE("VIDIOC_STREAMON: %s", strerror(errno));
210         return false;
211     }
212 
213     // Remember who to tell about new frames as they arrive
214     mCallback = callback;
215 
216     // Fire up a thread to receive and dispatch the video frames
217     mCaptureThread = std::thread([this](){ collectFrames(); });
218 
219     ALOGD("Stream started.");
220     return true;
221 }
222 
223 
stopStream()224 void VideoCapture::stopStream() {
225     // Tell the background thread to stop
226     int prevRunMode = mRunMode.fetch_or(STOPPING);
227     if (prevRunMode == STOPPED) {
228         // The background thread wasn't running, so set the flag back to STOPPED
229         mRunMode = STOPPED;
230     } else if (prevRunMode & STOPPING) {
231         ALOGE("stopStream called while stream is already stopping.  Reentrancy is not supported!");
232         return;
233     } else {
234         // Block until the background thread is stopped
235         if (mCaptureThread.joinable()) {
236             mCaptureThread.join();
237         }
238 
239         // Stop the underlying video stream (automatically empties the buffer queue)
240         int type = mBufferInfo.type;
241         if (ioctl(mDeviceFd, VIDIOC_STREAMOFF, &type) < 0) {
242             ALOGE("VIDIOC_STREAMOFF: %s", strerror(errno));
243         }
244 
245         ALOGD("Capture thread stopped.");
246     }
247 
248     // Unmap the buffers we allocated
249     munmap(mPixelBuffer, mBufferInfo.length);
250 
251     // Tell the L4V2 driver to release our streaming buffers
252     v4l2_requestbuffers bufrequest;
253     bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
254     bufrequest.memory = V4L2_MEMORY_MMAP;
255     bufrequest.count = 0;
256     ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest);
257 
258     // Drop our reference to the frame delivery callback interface
259     mCallback = nullptr;
260 }
261 
262 
markFrameReady()263 void VideoCapture::markFrameReady() {
264     mFrameReady = true;
265 };
266 
267 
returnFrame()268 bool VideoCapture::returnFrame() {
269     // We're giving the frame back to the system, so clear the "ready" flag
270     mFrameReady = false;
271 
272     // Requeue the buffer to capture the next available frame
273     if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
274         ALOGE("VIDIOC_QBUF: %s", strerror(errno));
275         return false;
276     }
277 
278     return true;
279 }
280 
281 
282 // This runs on a background thread to receive and dispatch video frames
collectFrames()283 void VideoCapture::collectFrames() {
284     // Run until our atomic signal is cleared
285     while (mRunMode == RUN) {
286         // Wait for a buffer to be ready
287         if (ioctl(mDeviceFd, VIDIOC_DQBUF, &mBufferInfo) < 0) {
288             ALOGE("VIDIOC_DQBUF: %s", strerror(errno));
289             break;
290         }
291 
292         markFrameReady();
293 
294         // If a callback was requested per frame, do that now
295         if (mCallback) {
296             mCallback(this, &mBufferInfo, mPixelBuffer);
297         }
298     }
299 
300     // Mark ourselves stopped
301     ALOGD("VideoCapture thread ending");
302     mRunMode = STOPPED;
303 }
304