Add my own fork of acatcher
This commit is contained in:
parent
62f85ee817
commit
182324e146
8 changed files with 1169 additions and 1 deletions
1
acatcher
1
acatcher
|
@ -1 +0,0 @@
|
||||||
external/happycube/projects/acatcher
|
|
25
acatcher/README
Normal file
25
acatcher/README
Normal 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
BIN
acatcher/acatcher
Executable file
Binary file not shown.
448
acatcher/acatcher.cxx
Normal file
448
acatcher/acatcher.cxx
Normal 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
24
acatcher/rpi_stream.py
Normal 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
11
acatcher/stream.sh
Executable 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
1
acatcher/upstream
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../external/happycube/projects/acatcher
|
660
acatcher/v4lcap.c
Normal file
660
acatcher/v4lcap.c
Normal 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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue