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;
96 format.fmt.pix.width = 720;
97 format.fmt.pix.height = 240;
98 ALOGI("Requesting format %c%c%c%c (0x%08X)",
99 ((char*)&format.fmt.pix.pixelformat)[0],
100 ((char*)&format.fmt.pix.pixelformat)[1],
101 ((char*)&format.fmt.pix.pixelformat)[2],
102 ((char*)&format.fmt.pix.pixelformat)[3],
103 format.fmt.pix.pixelformat);
104 if (ioctl(mDeviceFd, VIDIOC_S_FMT, &format) < 0) {
105 ALOGE("VIDIOC_S_FMT: %s", strerror(errno));
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 ALOGI("Current output format: fmt=0x%X, %dx%d, pitch=%d",
118 format.fmt.pix.pixelformat,
119 format.fmt.pix.width,
120 format.fmt.pix.height,
121 format.fmt.pix.bytesperline
122 );
123 } else {
124 ALOGE("VIDIOC_G_FMT: %s", strerror(errno));
125 return false;
126 }
127
128 // Make sure we're initialized to the STOPPED state
129 mRunMode = STOPPED;
130 mFrameReady = false;
131
132 // Ready to go!
133 return true;
134 }
135
136
close()137 void VideoCapture::close() {
138 ALOGD("VideoCapture::close");
139 // Stream should be stopped first!
140 assert(mRunMode == STOPPED);
141
142 if (isOpen()) {
143 ALOGD("closing video device file handled %d", mDeviceFd);
144 ::close(mDeviceFd);
145 mDeviceFd = -1;
146 }
147 }
148
149
startStream(std::function<void (VideoCapture *,imageBuffer *,void *)> callback)150 bool VideoCapture::startStream(std::function<void(VideoCapture*, imageBuffer*, void*)> callback) {
151 // Set the state of our background thread
152 int prevRunMode = mRunMode.fetch_or(RUN);
153 if (prevRunMode & RUN) {
154 // The background thread is already running, so we can't start a new stream
155 ALOGE("Already in RUN state, so we can't start a new streaming thread");
156 return false;
157 }
158
159 // Tell the L4V2 driver to prepare our streaming buffers
160 v4l2_requestbuffers bufrequest;
161 bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
162 bufrequest.memory = V4L2_MEMORY_MMAP;
163 bufrequest.count = 1;
164 if (ioctl(mDeviceFd, VIDIOC_REQBUFS, &bufrequest) < 0) {
165 ALOGE("VIDIOC_REQBUFS: %s", strerror(errno));
166 return false;
167 }
168
169 // Get the information on the buffer that was created for us
170 memset(&mBufferInfo, 0, sizeof(mBufferInfo));
171 mBufferInfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
172 mBufferInfo.memory = V4L2_MEMORY_MMAP;
173 mBufferInfo.index = 0;
174 if (ioctl(mDeviceFd, VIDIOC_QUERYBUF, &mBufferInfo) < 0) {
175 ALOGE("VIDIOC_QUERYBUF: %s", strerror(errno));
176 return false;
177 }
178
179 ALOGI("Buffer description:");
180 ALOGI(" offset: %d", mBufferInfo.m.offset);
181 ALOGI(" length: %d", mBufferInfo.length);
182
183 // Get a pointer to the buffer contents by mapping into our address space
184 mPixelBuffer = mmap(
185 NULL,
186 mBufferInfo.length,
187 PROT_READ | PROT_WRITE,
188 MAP_SHARED,
189 mDeviceFd,
190 mBufferInfo.m.offset
191 );
192 if( mPixelBuffer == MAP_FAILED) {
193 ALOGE("mmap: %s", strerror(errno));
194 return false;
195 }
196 memset(mPixelBuffer, 0, mBufferInfo.length);
197 ALOGI("Buffer mapped at %p", mPixelBuffer);
198
199 // Queue the first capture buffer
200 if (ioctl(mDeviceFd, VIDIOC_QBUF, &mBufferInfo) < 0) {
201 ALOGE("VIDIOC_QBUF: %s", strerror(errno));
202 return false;
203 }
204
205 // Start the video stream
206 int type = mBufferInfo.type;
207 if (ioctl(mDeviceFd, VIDIOC_STREAMON, &type) < 0) {
208 ALOGE("VIDIOC_STREAMON: %s", strerror(errno));
209 return false;
210 }
211
212 // Remember who to tell about new frames as they arrive
213 mCallback = callback;
214
215 // Fire up a thread to receive and dispatch the video frames
216 mCaptureThread = std::thread([this](){ collectFrames(); });
217
218 ALOGD("Stream started.");
219 return true;
220 }
221
222
stopStream()223 void VideoCapture::stopStream() {
224 // Tell the background thread to stop
225 int prevRunMode = mRunMode.fetch_or(STOPPING);
226 if (prevRunMode == STOPPED) {
227 // The background thread wasn't running, so set the flag back to STOPPED
228 mRunMode = STOPPED;
229 } else if (prevRunMode & STOPPING) {
230 ALOGE("stopStream called while stream is already stopping. 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 ALOGE("VIDIOC_STREAMOFF: %s", strerror(errno));
242 }
243
244 ALOGD("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 ALOGE("VIDIOC_QBUF: %s", strerror(errno));
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 ALOGE("VIDIOC_DQBUF: %s", strerror(errno));
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 ALOGD("VideoCapture thread ending");
301 mRunMode = STOPPED;
302 }
303