Add my own fork of acatcher

This commit is contained in:
Donald Burr 2015-02-17 20:50:46 -08:00
parent 62f85ee817
commit 182324e146
8 changed files with 1169 additions and 1 deletions

View file

@ -1 +0,0 @@
external/happycube/projects/acatcher

25
acatcher/README Normal file
View file

@ -0,0 +1,25 @@
host side:
apt-get install libopencv-dev
clang++ -O2 -lopencv_core -lopencv_highgui -lopencv_imgproc -o acatcher acatcher.cxx
to actually run it:
./acatcher | (aplay -t raw -c 1 -f S16_LE -r 22050 > /dev/null 2> /dev/null)
---
webcam side:
copy v4lcap.c and build it on the rpi
xx = session #
make the following into a script
---
# different res is ok, MUST be mjpg
v4l2-ctl --set-fmt-video=width=640,height=480,pixelformat=MJPG
(while true; do arecord -t raw -c 1 -f S16_LE -r 22050 -D hw:1 | nc 192.168.1.122 40xx; sleep 1; done) &
while true; do v4lcap -o | nc 192.168.1.122 41xx; sleep 1; done

BIN
acatcher/acatcher Executable file

Binary file not shown.

448
acatcher/acatcher.cxx Normal file
View file

@ -0,0 +1,448 @@
/*
awatcher.cxx
Copyright (c) 2015 Chad Page
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
const char *names[] = {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"A",
"B",
"C",
"D",
"E",
"F",
};
void cls()
{
cerr << "\x1B[2J\x1B[H";
}
int baseport = 4000;
const int MAX = 16;
const int BUFLEN = 1024*1024; // in bytes
//const int ABUFLEN = 1024; // in words
// Yes this is ugly. :P
bool failure = false;
struct sockin {
int listener_fd, data_fd;
struct sockaddr_in serv_addr, cli_addr;
int id;
sockin(int _id = 0, int baseport = 5000) {
int option = 1;
id = _id;
listener_fd = data_fd = -1;
bzero(&serv_addr, sizeof(serv_addr));
bzero(&cli_addr, sizeof(cli_addr));
listener_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listener_fd < 0) {
cerr << "Couldn't open a listener socket. Weird.\n";
failure = true;
return;
}
setsockopt(listener_fd,SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(baseport + id);
if (bind(listener_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
cerr << "ERROR: Couldn't bind to port #" << baseport + id << endl;
failure = true;
return;
}
listen(listener_fd, 1);
}
virtual void handle(unsigned char *data, int len, int listener) {
}
virtual void newconn() {
}
};
struct audio_sockin : public sockin {
int leftover;
// uint16_t buf[ABUFLEN];
// int bufsize;
audio_sockin(int _id) : sockin(_id, 4000) {
leftover = -1;
//bufsize = 0;
}
virtual void handle(unsigned char *data, int len, int listener) {
int new_leftover = -1;
if (len % 2) {
new_leftover = data[len - 1];
len--;
}
if (listener == id) {
if (leftover >= 0) {
write(1, &leftover, 1);
leftover = -1;
}
write(1, data, len);
}
leftover = new_leftover;
}
// new connection: better not have an odd # output for audio!
virtual void newconn() {
leftover = -1;
}
};
const int IBUFLEN = 2048*1024; // in bytes
struct image_sockin : public sockin {
uint8_t buf[IBUFLEN];
int bufsize;
image_sockin(int _id) : sockin(_id, 4100) {
bufsize = 0;
}
void showImage(int begin, int end, int listener) {
if (listener == id) {
Mat imgbuf = cv::Mat(480, 640, CV_8U, &buf[begin]);
Mat imgMat = cv::imdecode(imgbuf, CV_LOAD_IMAGE_COLOR);
if (!imgMat.data) cerr << "reading failed\r\n";
// cerr << "x " << imgMat.rows << ' ' << imgMat.cols << "\r\n";
imshow("Display Window", imgMat);
// cerr << "updated\r\n";
waitKey(1);
}
}
virtual void handle(unsigned char *data, int len, int listener) {
int begin = -1, end = -1;
if ((len + bufsize) > IBUFLEN) {
bufsize = 0;
}
memcpy(&buf[bufsize], data, len);
bufsize += len;
for (int i = 0; ((begin == -1) || (end == -1)) && (i < bufsize - 1); i++) {
// if (buf[i] == 0xff) cerr << i << ' ' << (int)buf[i + 1] << "\r\n";
if ((buf[i] == 0xff) && (buf[i + 1] == 0xd8)) begin = i;
if ((buf[i] == 0xff) && (buf[i + 1] == 0xd9)) end = i;
}
// cerr << "A " << bufsize << ' ' << begin << ' ' << end << "\r\n";
if ((begin >= 0) && (end >= 0)) {
if (begin > end) {
memmove(buf, &buf[begin], bufsize - begin);
bufsize -= begin;
} else {
// cerr << "doshow\r\n";
showImage(begin, end, listener);
bufsize = 0;
}
}
// if (listener == id) {
// if (have_fd3) write(3, data, len);
// }
}
virtual void newconn() {
bufsize = 0;
}
};
sockin *s[MAX];
unsigned char buf[BUFLEN];
struct termios oldtermios;
void sigcatch(int sig)
{
tcsetattr(0, TCSAFLUSH, &oldtermios);
_exit(0);
}
bool setrawkbd()
{
// set keyboard to raw mode + unblocking
struct termios newtermios;
if(tcgetattr(0, &oldtermios) < 0)
_exit(1);
newtermios = oldtermios;
newtermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
newtermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
newtermios.c_cflag &= ~(CSIZE | PARENB);
newtermios.c_cflag |= CS8;
newtermios.c_oflag &= ~(OPOST);
newtermios.c_cc[VMIN] = 1;
newtermios.c_cc[VTIME] = 0;
if(tcsetattr(0, TCSAFLUSH, &newtermios) < 0)
_exit(1);
int opts = fcntl(0, F_GETFL);
if (opts < 0) {
cerr << "HUH? fcntl failed\n";
return false;
}
if (fcntl(0, F_SETFL, opts | O_NONBLOCK) < 0) {
cerr << "HUH? fcntl(F_SETFL) failed\n";
return false;
}
return true;
}
int main(void)
{
int main_rv = 0;
int cur_listener = 0;
int num_sockets = (MAX * 2);
sockin *s[num_sockets];
#if 0
// check to see if we have a video socket
have_fd3 = (fcntl(3, F_GETFD) >= 0);
if (have_fd3) cerr << "Have video output socket\n";
#endif
// namedWindow("Display Window", WINDOW_AUTOSIZE );
namedWindow("Display Window", WINDOW_AUTOSIZE );
// catch signals
if (((void *)signal(SIGINT,sigcatch) < 0) ||
((void *)signal(SIGQUIT,sigcatch) < 0) ||
((void *)signal(SIGTERM,sigcatch) < 0)) {
cerr << "Couldn't set up signal catching. huh?\n";
return(1);
}
if (!setrawkbd()) {
goto err_exit;
}
for (int i = 0; i < num_sockets; i+=2) {
s[i] = NULL;
}
// init listening sockets
for (int i = 0; i < num_sockets; i+=2) {
s[i] = new audio_sockin(i / 2);
s[i+1] = new image_sockin(i / 2);
}
if (failure)
goto err_exit;
cls();
// now listen for connections and data
while (1) {
int topfd = -1;
int rv;
fd_set readfds, writefds, exceptfds;
struct timespec t;
t.tv_sec = 1;
t.tv_nsec = 0;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
FD_SET(0, &readfds);
for (int i = 0; i < num_sockets; i++) {
if (s[i]->listener_fd >= 0) {
FD_SET(s[i]->listener_fd, &readfds);
if (s[i]->listener_fd >= topfd) topfd = s[i]->listener_fd + 1;
}
if (s[i]->data_fd >= 0) {
FD_SET(s[i]->data_fd, &readfds);
if (s[i]->data_fd >= topfd) topfd = s[i]->data_fd + 1;
}
}
waitKey(1);
rv = pselect(topfd, &readfds, &writefds, &exceptfds, &t, NULL);
if (rv == -1 && errno != EINTR) {
cerr << "ERROR: select failed\n";
goto err_exit;
}
if ((rv > 0) && FD_ISSET(0, &readfds)) {
unsigned char c;
int rv = read(0, &c, 1);
if (rv == 0) goto good_exit;
if (rv == 1) {
int old_listener = cur_listener;
// cerr << "Got char " << (int)c << "\r\n";
if (c == 'q') goto good_exit;
if ((c >= '0') && (c <= '9')) {
cur_listener = c - '0';
}
if ((c >= 'a') && (c <= 'f')) {
cur_listener = c + 10 - 'a';
}
if ((c >= 'A') && (c <= 'F')) {
cur_listener = c + 10 - 'A';
}
if (old_listener != cur_listener) {
cls();
cerr << "Now listening to " << names[cur_listener] << "\r\n";
}
}
}
for (int i = 0; (rv > 0) && (i < num_sockets); i++) {
if (!s[i]) break;
// check for new connections
if (FD_ISSET(s[i]->listener_fd, &readfds)) {
socklen_t len = sizeof(struct sockaddr);
int newfd = accept(s[i]->listener_fd, (struct sockaddr *)&s[i]->cli_addr, &len);
if (newfd >= 0) {
if (s[i]->data_fd < 0) {
int opts = fcntl(newfd, F_GETFL);
if (opts < 0) {
cerr << "HUH? fcntl failed\n";
goto err_exit;
}
if (fcntl(newfd, F_SETFL, opts | O_NONBLOCK) < 0) {
cerr << "HUH? fcntl(F_SETFL) failed\n";
goto err_exit;
}
s[i]->data_fd = newfd;
cerr << "New connection on " << i/2 << " " << ((i % 2) ? "video" : "audio") << "\r\n";
s[i]->newconn();
} else {
cerr << "HUH? new connection on socket # " << i << endl;
}
}
}
// check for new data
if (FD_ISSET(s[i]->data_fd, &readfds)) {
int rv = read(s[i]->data_fd, buf, BUFLEN);
if (rv == 0) {
close(s[i]->data_fd);
s[i]->data_fd = -1;
listen(s[i]->listener_fd, 1);
} else {
s[i]->handle(buf, rv, cur_listener);
}
}
}
};
err_exit:
main_rv = 1;
good_exit:
tcsetattr(0, TCSAFLUSH, &oldtermios);
int sdcount = 0;
for (int i = 0; i < num_sockets; i++) {
if (s[i] && s[i]->listener_fd >= 0) {
shutdown(s[i]->listener_fd, SHUT_RDWR);
sdcount++;
}
if (s[i] && s[i]->data_fd >= 0) {
shutdown(s[i]->data_fd, SHUT_RDWR);
sdcount++;
}
}
if (sdcount) {
cerr << "sent " << sdcount << " shutdowns\n";
}
for (int i = 0; i < num_sockets; i++) {
if (s[i] && s[i]->listener_fd >= 0) {
close(s[i]->listener_fd);
}
if (s[i] && s[i]->data_fd >= 0) {
close(s[i]->data_fd);
}
}
return main_rv;
}

24
acatcher/rpi_stream.py Normal file
View file

@ -0,0 +1,24 @@
# adapted from http://petrkout.com/electronics/low-latency-0-4-s-video-streaming-from-raspberry-pi-mjpeg-streamer-opencv/
import cv2
import numpy as np
import sys
#stream=urllib.urlopen('http://192.168.0.193:8080/?action=stream')
stream=sys.stdin
bytes=''
while True:
r = stream.read(1024)
if (len(r) == 0):
exit(0)
bytes+=r
a = bytes.find('\xff\xd8')
b = bytes.find('\xff\xd9')
if a!=-1 and b!=-1:
jpg = bytes[a:b+2]
bytes= bytes[b+2:]
i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),cv2.CV_LOAD_IMAGE_COLOR)
cv2.imshow('i',i)
if cv2.waitKey(1) == 27:
exit(0)

11
acatcher/stream.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
SERVERIP=192.168.24.20
SESSION_ID="01"
# different res is ok, MUST be mjpg
v4l2-ctl --set-fmt-video=width=640,height=480,pixelformat=MJPG
amixer -c 1 sset Mic,0 80%,80% unmute cap
(while true; do arecord -t raw -c 1 -f S16_LE -r 22050 -D hw:1 | nc $SERVERIP 40$SESSION_ID; sleep 1; done) &
while true; do ./v4lcap -o | nc $SERVERIP 41$SESSION_ID; sleep 1; done

1
acatcher/upstream Symbolic link
View file

@ -0,0 +1 @@
../external/happycube/projects/acatcher

660
acatcher/v4lcap.c Normal file
View file

@ -0,0 +1,660 @@
/*
* V4L2 video capture example
*
* Modified by Chad Page for acatcher
*
* This program can (still) be used and distributed without restrictions.
*
* This program is provided with the V4L2 API
* see http://linuxtv.org/docs.php for more information
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h> /* getopt_long() */
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define CLEAR(x) memset(&(x), 0, sizeof(x))
enum io_method {
IO_METHOD_READ,
IO_METHOD_MMAP,
IO_METHOD_USERPTR,
};
struct buffer {
void *start;
size_t length;
};
static char *dev_name;
static enum io_method io = IO_METHOD_MMAP;
static int fd = -1;
struct buffer *buffers;
static unsigned int n_buffers;
static int out_buf;
static int force_format;
static int frame_count = 70;
static void errno_exit(const char *s)
{
fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
exit(EXIT_FAILURE);
}
static int xioctl(int fh, int request, void *arg)
{
int r;
do {
r = ioctl(fh, request, arg);
} while (-1 == r && EINTR == errno);
return r;
}
static void process_image(const void *p, int size)
{
if (out_buf)
fwrite(p, size, 1, stdout);
fflush(stderr);
fprintf(stderr, ".");
fflush(stdout);
}
static int read_frame(void)
{
struct v4l2_buffer buf;
unsigned int i;
switch (io) {
case IO_METHOD_READ:
if (-1 == read(fd, buffers[0].start, buffers[0].length)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
/* Could ignore EIO, see spec. */
/* fall through */
default:
errno_exit("read");
}
}
process_image(buffers[0].start, buffers[0].length);
break;
case IO_METHOD_MMAP:
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
/* Could ignore EIO, see spec. */
/* fall through */
default:
errno_exit("VIDIOC_DQBUF");
}
}
assert(buf.index < n_buffers);
process_image(buffers[buf.index].start, buf.bytesused);
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
break;
case IO_METHOD_USERPTR:
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
/* Could ignore EIO, see spec. */
/* fall through */
default:
errno_exit("VIDIOC_DQBUF");
}
}
for (i = 0; i < n_buffers; ++i)
if (buf.m.userptr == (unsigned long)buffers[i].start
&& buf.length == buffers[i].length)
break;
assert(i < n_buffers);
process_image((void *)buf.m.userptr, buf.bytesused);
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
break;
}
return 1;
}
static void mainloop(void)
{
unsigned int count;
count = frame_count;
while (1 /* count-- > 0 */) {
for (;;) {
fd_set fds;
struct timeval tv;
int r;
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* Timeout. */
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1, &fds, NULL, NULL, &tv);
if (-1 == r) {
if (EINTR == errno)
continue;
errno_exit("select");
}
if (0 == r) {
fprintf(stderr, "select timeout\n");
exit(EXIT_FAILURE);
}
if (read_frame())
break;
/* EAGAIN - continue select loop. */
}
}
}
static void stop_capturing(void)
{
enum v4l2_buf_type type;
switch (io) {
case IO_METHOD_READ:
/* Nothing to do. */
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
errno_exit("VIDIOC_STREAMOFF");
break;
}
}
static void start_capturing(void)
{
unsigned int i;
enum v4l2_buf_type type;
switch (io) {
case IO_METHOD_READ:
/* Nothing to do. */
break;
case IO_METHOD_MMAP:
for (i = 0; i < n_buffers; ++i) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
errno_exit("VIDIOC_STREAMON");
break;
case IO_METHOD_USERPTR:
for (i = 0; i < n_buffers; ++i) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
buf.index = i;
buf.m.userptr = (unsigned long)buffers[i].start;
buf.length = buffers[i].length;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
errno_exit("VIDIOC_STREAMON");
break;
}
}
static void uninit_device(void)
{
unsigned int i;
switch (io) {
case IO_METHOD_READ:
free(buffers[0].start);
break;
case IO_METHOD_MMAP:
for (i = 0; i < n_buffers; ++i)
if (-1 == munmap(buffers[i].start, buffers[i].length))
errno_exit("munmap");
break;
case IO_METHOD_USERPTR:
for (i = 0; i < n_buffers; ++i)
free(buffers[i].start);
break;
}
free(buffers);
}
static void init_read(unsigned int buffer_size)
{
buffers = calloc(1, sizeof(*buffers));
if (!buffers) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
buffers[0].length = buffer_size;
buffers[0].start = malloc(buffer_size);
if (!buffers[0].start) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
}
static void init_mmap(void)
{
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
fprintf(stderr, "%s does not support "
"memory mapping\n", dev_name);
exit(EXIT_FAILURE);
} else {
errno_exit("VIDIOC_REQBUFS");
}
}
if (req.count < 2) {
fprintf(stderr, "Insufficient buffer memory on %s\n",
dev_name);
exit(EXIT_FAILURE);
}
buffers = calloc(req.count, sizeof(*buffers));
if (!buffers) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
errno_exit("VIDIOC_QUERYBUF");
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start =
mmap(NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
fd, buf.m.offset);
if (MAP_FAILED == buffers[n_buffers].start)
errno_exit("mmap");
}
}
static void init_userp(unsigned int buffer_size)
{
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_USERPTR;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
fprintf(stderr, "%s does not support "
"user pointer i/o\n", dev_name);
exit(EXIT_FAILURE);
} else {
errno_exit("VIDIOC_REQBUFS");
}
}
buffers = calloc(4, sizeof(*buffers));
if (!buffers) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
buffers[n_buffers].length = buffer_size;
buffers[n_buffers].start = malloc(buffer_size);
if (!buffers[n_buffers].start) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
}
}
static void init_device(void)
{
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
unsigned int min;
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
if (EINVAL == errno) {
fprintf(stderr, "%s is no V4L2 device\n",
dev_name);
exit(EXIT_FAILURE);
} else {
errno_exit("VIDIOC_QUERYCAP");
}
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "%s is no video capture device\n",
dev_name);
exit(EXIT_FAILURE);
}
switch (io) {
case IO_METHOD_READ:
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
fprintf(stderr, "%s does not support read i/o\n",
dev_name);
exit(EXIT_FAILURE);
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr, "%s does not support streaming i/o\n",
dev_name);
exit(EXIT_FAILURE);
}
break;
}
/* Select video input, video standard and tune here. */
CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
switch (errno) {
case EINVAL:
/* Cropping not supported. */
break;
default:
/* Errors ignored. */
break;
}
}
} else {
/* Errors ignored. */
}
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (force_format) {
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
errno_exit("VIDIOC_S_FMT");
/* Note VIDIOC_S_FMT may change width and height. */
} else {
/* Preserve original settings as set by v4l2-ctl for example */
if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
errno_exit("VIDIOC_G_FMT");
}
/* Buggy driver paranoia. */
min = fmt.fmt.pix.width * 2;
if (fmt.fmt.pix.bytesperline < min)
fmt.fmt.pix.bytesperline = min;
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if (fmt.fmt.pix.sizeimage < min)
fmt.fmt.pix.sizeimage = min;
switch (io) {
case IO_METHOD_READ:
init_read(fmt.fmt.pix.sizeimage);
break;
case IO_METHOD_MMAP:
init_mmap();
break;
case IO_METHOD_USERPTR:
init_userp(fmt.fmt.pix.sizeimage);
break;
}
}
static void close_device(void)
{
if (-1 == close(fd))
errno_exit("close");
fd = -1;
}
static void open_device(void)
{
struct stat st;
if (-1 == stat(dev_name, &st)) {
fprintf(stderr, "Cannot identify '%s': %d, %s\n",
dev_name, errno, strerror(errno));
exit(EXIT_FAILURE);
}
if (!S_ISCHR(st.st_mode)) {
fprintf(stderr, "%s is no device\n", dev_name);
exit(EXIT_FAILURE);
}
fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
if (-1 == fd) {
fprintf(stderr, "Cannot open '%s': %d, %s\n",
dev_name, errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
static void usage(FILE *fp, int argc, char **argv)
{
fprintf(fp,
"Usage: %s [options]\n\n"
"Version 1.3\n"
"Options:\n"
"-d | --device name Video device name [%s]\n"
"-h | --help Print this message\n"
"-m | --mmap Use memory mapped buffers [default]\n"
"-r | --read Use read() calls\n"
"-u | --userp Use application allocated buffers\n"
"-o | --output Outputs stream to stdout\n"
"-f | --format Force format to 640x480 YUYV\n"
"-c | --count Number of frames to grab [%i]\n"
"",
argv[0], dev_name, frame_count);
}
static const char short_options[] = "d:hmruofc:";
static const struct option
long_options[] = {
{ "device", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "mmap", no_argument, NULL, 'm' },
{ "read", no_argument, NULL, 'r' },
{ "userp", no_argument, NULL, 'u' },
{ "output", no_argument, NULL, 'o' },
{ "format", no_argument, NULL, 'f' },
{ "count", required_argument, NULL, 'c' },
{ 0, 0, 0, 0 }
};
int main(int argc, char **argv)
{
dev_name = "/dev/video0";
for (;;) {
int idx;
int c;
c = getopt_long(argc, argv,
short_options, long_options, &idx);
if (-1 == c)
break;
switch (c) {
case 0: /* getopt_long() flag */
break;
case 'd':
dev_name = optarg;
break;
case 'h':
usage(stdout, argc, argv);
exit(EXIT_SUCCESS);
case 'm':
io = IO_METHOD_MMAP;
break;
case 'r':
io = IO_METHOD_READ;
break;
case 'u':
io = IO_METHOD_USERPTR;
break;
case 'o':
out_buf++;
break;
case 'f':
force_format++;
break;
case 'c':
errno = 0;
frame_count = strtol(optarg, NULL, 0);
if (errno)
errno_exit(optarg);
break;
default:
usage(stderr, argc, argv);
exit(EXIT_FAILURE);
}
}
open_device();
init_device();
start_capturing();
mainloop();
stop_capturing();
uninit_device();
close_device();
fprintf(stderr, "\n");
return 0;
}