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. 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 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 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 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 263 void VideoCapture::markFrameReady() { 264 mFrameReady = true; 265 }; 266 267 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 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