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.
36bool 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
138void 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
151bool 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
224void 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
263void VideoCapture::markFrameReady() {
264    mFrameReady = true;
265};
266
267
268bool 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
283void 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