Home | History | Annotate | Download | only in sampleDriver
      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