From 182324e14661d53b98010ecd4ddd790f91eb82ee Mon Sep 17 00:00:00 2001 From: Donald Burr Date: Tue, 17 Feb 2015 20:50:46 -0800 Subject: [PATCH] Add my own fork of acatcher --- acatcher | 1 - acatcher/README | 25 ++ acatcher/acatcher | Bin 0 -> 25321 bytes acatcher/acatcher.cxx | 448 ++++++++++++++++++++++++++++ acatcher/rpi_stream.py | 24 ++ acatcher/stream.sh | 11 + acatcher/upstream | 1 + acatcher/v4lcap.c | 660 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1169 insertions(+), 1 deletion(-) delete mode 120000 acatcher create mode 100644 acatcher/README create mode 100755 acatcher/acatcher create mode 100644 acatcher/acatcher.cxx create mode 100644 acatcher/rpi_stream.py create mode 100755 acatcher/stream.sh create mode 120000 acatcher/upstream create mode 100644 acatcher/v4lcap.c diff --git a/acatcher b/acatcher deleted file mode 120000 index 3b8f2ab..0000000 --- a/acatcher +++ /dev/null @@ -1 +0,0 @@ -external/happycube/projects/acatcher \ No newline at end of file diff --git a/acatcher/README b/acatcher/README new file mode 100644 index 0000000..0f5d57d --- /dev/null +++ b/acatcher/README @@ -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 + diff --git a/acatcher/acatcher b/acatcher/acatcher new file mode 100755 index 0000000000000000000000000000000000000000..aa0ef2fb19380a775f1f4220e17d8100d9d1f60d GIT binary patch literal 25321 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 MMs>pu6W8AV0K=c9(f|Me literal 0 HcmV?d00001 diff --git a/acatcher/acatcher.cxx b/acatcher/acatcher.cxx new file mode 100644 index 0000000..e644607 --- /dev/null +++ b/acatcher/acatcher.cxx @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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; +} + + diff --git a/acatcher/rpi_stream.py b/acatcher/rpi_stream.py new file mode 100644 index 0000000..acfb488 --- /dev/null +++ b/acatcher/rpi_stream.py @@ -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) + diff --git a/acatcher/stream.sh b/acatcher/stream.sh new file mode 100755 index 0000000..20b42d0 --- /dev/null +++ b/acatcher/stream.sh @@ -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 diff --git a/acatcher/upstream b/acatcher/upstream new file mode 120000 index 0000000..7452887 --- /dev/null +++ b/acatcher/upstream @@ -0,0 +1 @@ +../external/happycube/projects/acatcher \ No newline at end of file diff --git a/acatcher/v4lcap.c b/acatcher/v4lcap.c new file mode 100644 index 0000000..8be3388 --- /dev/null +++ b/acatcher/v4lcap.c @@ -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 +#include +#include +#include + +#include /* getopt_long() */ + +#include /* low-level i/o */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +}