Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
Is there any c library to get a video from the webcam on linux?
A lot of us use OpenCV (cross-platform Computer Vision library, currently on v2.1)
The following snippet grabs frames from camera, converts them to grayscale and displays them on the screen:
#include <stdio.h>
#include "cv.h"
#include "highgui.h"
typedef IplImage* (*callback_prototype)(IplImage*);
/*
* make_it_gray: custom callback to convert a colored frame to its grayscale version.
* Remember that you must deallocate the returned IplImage* yourself after calling this function.
*/
IplImage* make_it_gray(IplImage* frame)
{
// Allocate space for a new image
IplImage* gray_frame = 0;
gray_frame = cvCreateImage(cvSize(frame->width, frame->height), frame->depth, 1);
if (!gray_frame)
{
fprintf(stderr, "!!! cvCreateImage failed!\n" );
return NULL;
}
cvCvtColor(frame, gray_frame, CV_RGB2GRAY);
return gray_frame;
}
/*
* process_video: retrieves frames from camera and executes a callback to do individual frame processing.
* Keep in mind that if your callback takes too much time to execute, you might loose a few frames from
* the camera.
*/
void process_video(callback_prototype custom_cb)
{
// Initialize camera
CvCapture *capture = 0;
capture = cvCaptureFromCAM(-1);
if (!capture)
{
fprintf(stderr, "!!! Cannot open initialize webcam!\n" );
return;
}
// Create a window for the video
cvNamedWindow("result", CV_WINDOW_AUTOSIZE);
IplImage* frame = 0;
char key = 0;
while (key != 27) // ESC
{
frame = cvQueryFrame(capture);
if(!frame)
{
fprintf( stderr, "!!! cvQueryFrame failed!\n" );
break;
}
// Execute callback on each frame
IplImage* processed_frame = (*custom_cb)(frame);
// Display processed frame
cvShowImage("result", processed_frame);
// Release resources
cvReleaseImage(&processed_frame);
// Exit when user press ESC
key = cvWaitKey(10);
}
// Free memory
cvDestroyWindow("result");
cvReleaseCapture(&capture);
}
int main( int argc, char **argv )
{
process_video(make_it_gray);
return 0;
}
v4l2 official examples
What you get:
./v4l2grab: capture a few snapshots to files outNNN.ppm
./v4l2gl: show video live on a window using an OpenGL texture (immediate rendering, hey!) and raw X11 windowing (plus GLUT's gluLookAt for good measure).
How to get it on Ubuntu 16.04:
sudo apt-get install libv4l-dev
sudo apt-get build-dep libv4l-dev
git clone git://linuxtv.org/v4l-utils.git
cd v4l-utils
# Matching the installed version of dpkg -s libv4l-dev
git checkout v4l-utils-1.10.0
./bootstrap.sh
./configure
make
# TODO: fails halfway, but it does not matter for us now.
cd contrib/tests
make
It is also easy to use those examples outside of the Git tree, just copy them out, make relative includes "" absolute <>, and remove config.h. I've done that for you at: https://github.com/cirosantilli/cpp-cheat/tree/09fe73d248f7da2e9c9f3eff2520a143c259f4a6/v4l2
Minimal example from docs
The docs 4.9.0 contain what appears to be a minimal version of ./v4l2grab at https://linuxtv.org/downloads/v4l-dvb-apis-new/uapi/v4l/v4l2grab-example.html. I needed to patch it minimally and I've sent the patch to http://www.spinics.net/lists/linux-media/ (their docs live in the Linux kernel tree as rst, neat), where it was dully ignored.
Usage:
gcc v4l2grab.c -lv4l2
./a.out
Patched code:
/* V4L2 video picture grabber
Copyright (C) 2009 Mauro Carvalho Chehab <mchehab#infradead.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libv4l2.h>
#define CLEAR(x) memset(&(x), 0, sizeof(x))
struct buffer {
void *start;
size_t length;
};
static void xioctl(int fh, int request, void *arg)
{
int r;
do {
r = v4l2_ioctl(fh, request, arg);
} while (r == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if (r == -1) {
fprintf(stderr, "error %d, %s\\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv)
{
struct v4l2_format fmt;
struct v4l2_buffer buf;
struct v4l2_requestbuffers req;
enum v4l2_buf_type type;
fd_set fds;
struct timeval tv;
int r, fd = -1;
unsigned int i, n_buffers;
char *dev_name = "/dev/video0";
char out_name[256];
FILE *fout;
struct buffer *buffers;
fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
if (fd < 0) {
perror("Cannot open device");
exit(EXIT_FAILURE);
}
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
xioctl(fd, VIDIOC_S_FMT, &fmt);
if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24) {
printf("Libv4l didn't accept RGB24 format. Can't proceed.\\n");
exit(EXIT_FAILURE);
}
if ((fmt.fmt.pix.width != 640) || (fmt.fmt.pix.height != 480))
printf("Warning: driver is sending image at %dx%d\\n",
fmt.fmt.pix.width, fmt.fmt.pix.height);
CLEAR(req);
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
xioctl(fd, VIDIOC_REQBUFS, &req);
buffers = calloc(req.count, sizeof(*buffers));
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
xioctl(fd, VIDIOC_QUERYBUF, &buf);
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start = v4l2_mmap(NULL, buf.length,
PROT_READ | PROT_WRITE, MAP_SHARED,
fd, buf.m.offset);
if (MAP_FAILED == buffers[n_buffers].start) {
perror("mmap");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < n_buffers; ++i) {
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
xioctl(fd, VIDIOC_QBUF, &buf);
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_STREAMON, &type);
for (i = 0; i < 20; i++) {
do {
FD_ZERO(&fds);
FD_SET(fd, &fds);
/* Timeout. */
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1, &fds, NULL, NULL, &tv);
} while ((r == -1 && (errno = EINTR)));
if (r == -1) {
perror("select");
return errno;
}
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
xioctl(fd, VIDIOC_DQBUF, &buf);
sprintf(out_name, "out%03d.ppm", i);
fout = fopen(out_name, "w");
if (!fout) {
perror("Cannot open image");
exit(EXIT_FAILURE);
}
fprintf(fout, "P6\n%d %d 255\n",
fmt.fmt.pix.width, fmt.fmt.pix.height);
fwrite(buffers[buf.index].start, buf.bytesused, 1, fout);
fclose(fout);
xioctl(fd, VIDIOC_QBUF, &buf);
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_STREAMOFF, &type);
for (i = 0; i < n_buffers; ++i)
v4l2_munmap(buffers[i].start, buffers[i].length);
v4l2_close(fd);
return 0;
}
Header only object oriented version for reuse
Extracted from the example in the docs, but in a form that makes it super easy to reuse.
common_v4l2.h
#ifndef COMMON_V4L2_H
#define COMMON_V4L2_H
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <libv4l2.h>
#include <linux/videodev2.h>
#define COMMON_V4L2_CLEAR(x) memset(&(x), 0, sizeof(x))
typedef struct {
void *start;
size_t length;
} CommonV4l2_Buffer;
typedef struct {
int fd;
CommonV4l2_Buffer *buffers;
struct v4l2_buffer buf;
unsigned int n_buffers;
} CommonV4l2;
void CommonV4l2_xioctl(int fh, unsigned long int request, void *arg)
{
int r;
do {
r = v4l2_ioctl(fh, request, arg);
} while (r == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if (r == -1) {
fprintf(stderr, "error %d, %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
void CommonV4l2_init(CommonV4l2 *this, char *dev_name, unsigned int x_res, unsigned int y_res) {
enum v4l2_buf_type type;
struct v4l2_format fmt;
struct v4l2_requestbuffers req;
unsigned int i;
this->fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
if (this->fd < 0) {
perror("Cannot open device");
exit(EXIT_FAILURE);
}
COMMON_V4L2_CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = x_res;
fmt.fmt.pix.height = y_res;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
CommonV4l2_xioctl(this->fd, VIDIOC_S_FMT, &fmt);
if ((fmt.fmt.pix.width != x_res) || (fmt.fmt.pix.height != y_res))
printf("Warning: driver is sending image at %dx%d\n",
fmt.fmt.pix.width, fmt.fmt.pix.height);
COMMON_V4L2_CLEAR(req);
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
CommonV4l2_xioctl(this->fd, VIDIOC_REQBUFS, &req);
this->buffers = calloc(req.count, sizeof(*this->buffers));
for (this->n_buffers = 0; this->n_buffers < req.count; ++this->n_buffers) {
COMMON_V4L2_CLEAR(this->buf);
this->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
this->buf.memory = V4L2_MEMORY_MMAP;
this->buf.index = this->n_buffers;
CommonV4l2_xioctl(this->fd, VIDIOC_QUERYBUF, &this->buf);
this->buffers[this->n_buffers].length = this->buf.length;
this->buffers[this->n_buffers].start = v4l2_mmap(NULL, this->buf.length,
PROT_READ | PROT_WRITE, MAP_SHARED, this->fd, this->buf.m.offset);
if (MAP_FAILED == this->buffers[this->n_buffers].start) {
perror("mmap");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < this->n_buffers; ++i) {
COMMON_V4L2_CLEAR(this->buf);
this->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
this->buf.memory = V4L2_MEMORY_MMAP;
this->buf.index = i;
CommonV4l2_xioctl(this->fd, VIDIOC_QBUF, &this->buf);
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
CommonV4l2_xioctl(this->fd, VIDIOC_STREAMON, &type);
}
void CommonV4l2_update_image(CommonV4l2 *this) {
fd_set fds;
int r;
struct timeval tv;
do {
FD_ZERO(&fds);
FD_SET(this->fd, &fds);
/* Timeout. */
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(this->fd + 1, &fds, NULL, NULL, &tv);
} while ((r == -1 && (errno == EINTR)));
if (r == -1) {
perror("select");
exit(EXIT_FAILURE);
}
COMMON_V4L2_CLEAR(this->buf);
this->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
this->buf.memory = V4L2_MEMORY_MMAP;
CommonV4l2_xioctl(this->fd, VIDIOC_DQBUF, &this->buf);
CommonV4l2_xioctl(this->fd, VIDIOC_QBUF, &this->buf);
}
char * CommonV4l2_get_image(CommonV4l2 *this) {
return ((char *)this->buffers[this->buf.index].start);
}
size_t CommonV4l2_get_image_size(CommonV4l2 *this) {
return this->buffers[this->buf.index].length;
}
void CommonV4l2_deinit(CommonV4l2 *this) {
unsigned int i;
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
CommonV4l2_xioctl(this->fd, VIDIOC_STREAMOFF, &type);
for (i = 0; i < this->n_buffers; ++i)
v4l2_munmap(this->buffers[i].start, this->buffers[i].length);
v4l2_close(this->fd);
free(this->buffers);
}
#endif
main.c
#include <stdio.h>
#include <stdlib.h>
#include "common_v4l2.h"
static void save_ppm(
unsigned int i,
unsigned int x_res,
unsigned int y_res,
size_t data_lenght,
char *data
) {
FILE *fout;
char out_name[256];
sprintf(out_name, "out%03d.ppm", i);
fout = fopen(out_name, "w");
if (!fout) {
perror("error: fopen");
exit(EXIT_FAILURE);
}
fprintf(fout, "P6\n%d %d 255\n", x_res, y_res);
fwrite(data, data_lenght, 1, fout);
fclose(fout);
}
int main(void) {
CommonV4l2 common_v4l2;
char *dev_name = "/dev/video0";
struct buffer *buffers;
unsigned int
i,
x_res = 640,
y_res = 480
;
CommonV4l2_init(&common_v4l2, dev_name, x_res, y_res);
for (i = 0; i < 20; i++) {
CommonV4l2_update_image(&common_v4l2);
save_ppm(
i,
x_res,
y_res,
CommonV4l2_get_image_size(&common_v4l2),
CommonV4l2_get_image(&common_v4l2)
);
}
CommonV4l2_deinit(&common_v4l2);
return EXIT_SUCCESS;
}
Upstream: https://github.com/cirosantilli/cpp-cheat/blob/be5d6444bddab93e95949b3388d92007b5ca916f/v4l2/common_v4l2.h
SDL
Video capture is in their roadmap: https://wiki.libsdl.org/Roadmap and I bet it will wrap v4l on Linux.
It will be sweet when we get that portability layer, with less bloat than OpenCV.
Your best bet is probably: video4linux (V4L)
It's easy to use, and powerful.
Related
I have a working code with performs asynchronous DNS resolution with c-ares library calls. The program uses select to monitor file descriptors up to a maximum of FD_SETSIZE which is 1024 on my system. I want to use many more file descriptors so want to rewrite the code to use epoll instead of select.
Here is the select based function of my current program:
static void
wait_ares(ares_channel channel)
{
struct timeval *tvp, tv;
fd_set read_fds, write_fds;
int nfds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = ares_fds(channel, &read_fds, &write_fds);
if (nfds > 0) {
tvp = ares_timeout(channel, NULL, &tv);
select(nfds, &read_fds, &write_fds, NULL, tvp);
ares_process(channel, &read_fds, &write_fds);
}
}
I've done some googling before posting my question and I've found out that to implement this with epoll I can no longer use ares_fds, ares_timeout and ares_process but must use ares_getsock() and ares_process_fd() instead. But further than that I have no idea how to do this and can't find any example codes using epoll with c-ares. Can anyone modify the code provided below to use epoll instead of select? Or at least give me some pointers to get me started?
#include <ares.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#define MAXWAITING 1000 /* Max. number of parallel DNS queries */
#define MAXTRIES 3 /* Max. number of tries per domain */
#define TIMEOUT 3000 /* Max. number of ms for first try */
#define SERVERS "1.0.0.1,8.8.8.8" /* DNS server to use (Cloudflare & Google) */
static int nwaiting;
static void
state_cb(void *data, int s, int read, int write)
{
//printf("Change state fd %d read:%d write:%d\n", s, read, write);
}
static void
callback(void *arg, int status, int timeouts, struct hostent *host)
{
nwaiting--;
if(!host || status != ARES_SUCCESS){
fprintf(stderr, "Failed to lookup %s\n", ares_strerror(status));
return;
}
char ip[INET6_ADDRSTRLEN];
if (host->h_addr_list[0] != NULL){
inet_ntop(host->h_addrtype, host->h_addr_list[0], ip, sizeof(ip));
printf("%s\n%s\n", host->h_name, ip);
}
}
static void
wait_ares(ares_channel channel)
{
struct timeval *tvp, tv;
fd_set read_fds, write_fds;
int nfds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = ares_fds(channel, &read_fds, &write_fds);
if (nfds > 0) {
tvp = ares_timeout(channel, NULL, &tv);
select(nfds, &read_fds, &write_fds, NULL, tvp);
ares_process(channel, &read_fds, &write_fds);
}
}
int
main(int argc, char *argv[])
{
FILE * fp;
char domain[128];
size_t len = 0;
ssize_t read;
ares_channel channel;
int status, done = 0;
int optmask;
status = ares_library_init(ARES_LIB_INIT_ALL);
if (status != ARES_SUCCESS) {
printf("ares_library_init: %s\n", ares_strerror(status));
return 1;
}
struct ares_options options = {
.timeout = TIMEOUT, /* set first query timeout */
.tries = MAXTRIES /* set max. number of tries */
};
optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
status = ares_init_options(&channel, &options, optmask);
if (status != ARES_SUCCESS) {
printf("ares_init_options: %s\n", ares_strerror(status));
return 1;
}
status = ares_set_servers_csv(channel, SERVERS);
if (status != ARES_SUCCESS) {
printf("ares_set_servers_csv: %s\n", ares_strerror(status));
return 1;
}
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
do {
if (nwaiting >= MAXWAITING || done) {
do {
wait_ares(channel);
} while (nwaiting > MAXWAITING);
}
if (!done) {
if (fscanf(fp, "%127s", domain) == 1) {
ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
nwaiting++;
} else {
fprintf(stderr, "done sending\n");
done = 1;
}
}
} while (nwaiting > 0);
ares_destroy(channel);
ares_library_cleanup();
fclose(fp);
return 0;
}
The program requires a file with a domain name on each line to work.
This is what I ended up coming up with.
#include <ares.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <errno.h>
#define MAXWAITING 1000 /* Max. number of parallel DNS queries */
#define MAXTRIES 3 /* Max. number of tries per domain */
#define TIMEOUT 3000 /* Max. number of ms for first try */
#define DNS_MAX_EVENTS 10000
#define DNS_MAX_SERVERS 2
#define SERVERS "1.0.0.1,8.8.8.8" /* DNS server to use (Cloudflare & Google) */
static int nwaiting;
ares_socket_t dns_client_fds[ARES_GETSOCK_MAXNUM] = {0};
struct epoll_event ev, events[DNS_MAX_EVENTS];
int i,bitmask,nfds, epollfd, timeout, fd_count, ret;
static void
state_cb(void *data, int s, int read, int write)
{
//printf("Change state fd %d read:%d write:%d\n", s, read, write);
}
static void
callback(void *arg, int status, int timeouts, struct hostent *host)
{
nwaiting--;
if(!host || status != ARES_SUCCESS){
fprintf(stderr, "Failed to lookup %s\n", ares_strerror(status));
return;
}
char ip[INET6_ADDRSTRLEN];
if (host->h_addr_list[0] != NULL){
inet_ntop(host->h_addrtype, host->h_addr_list[0], ip, sizeof(ip));
printf("%s\n%s\n", host->h_name, ip);
}
}
static void
wait_ares(ares_channel channel)
{
nfds=0;
bitmask=0;
for (i =0; i < DNS_MAX_SERVERS ; i++) {
if (dns_client_fds[i] > 0) {
if (epoll_ctl(epollfd, EPOLL_CTL_DEL, dns_client_fds[i], NULL) < 0) {
continue;
}
}
}
memset(dns_client_fds, 0, sizeof(dns_client_fds));
bitmask = ares_getsock(channel, dns_client_fds, DNS_MAX_SERVERS);
for (i =0; i < DNS_MAX_SERVERS ; i++) {
if (dns_client_fds[i] > 0) {
ev.events = 0;
if (ARES_GETSOCK_READABLE(bitmask, i)) {
ev.events |= EPOLLIN;
}
if (ARES_GETSOCK_WRITABLE(bitmask, i)) {
ev.events |= EPOLLOUT;
}
ev.data.fd = dns_client_fds[i];
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, dns_client_fds[i], &ev) < 0) {
if(errno == EEXIST) {
nfds++;
continue;
}
continue;
}
nfds++;
}
}
if(nfds==0)
{
return;
}
timeout = 1000;//millisecs
fd_count = epoll_wait(epollfd, events, DNS_MAX_EVENTS, timeout);
if (fd_count < 0) {
return;
}
if (fd_count > 0) {
for (i = 0; i < fd_count; ++i) {
ares_process_fd(channel, ((events[i].events) & (EPOLLIN) ? events[i].data.fd:ARES_SOCKET_BAD), ((events[i].events) & (EPOLLOUT)? events[i].data.fd:ARES_SOCKET_BAD));
}
} else {
ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
}
}
int
main(int argc, char *argv[])
{
FILE * fp;
char domain[128];
size_t len = 0;
ssize_t read;
ares_channel channel;
int status, done = 0;
int optmask;
status = ares_library_init(ARES_LIB_INIT_ALL);
if (status != ARES_SUCCESS) {
printf("ares_library_init: %s\n", ares_strerror(status));
return 1;
}
struct ares_options options = {
.timeout = TIMEOUT, /* set first query timeout */
.tries = MAXTRIES /* set max. number of tries */
};
optmask = ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
status = ares_init_options(&channel, &options, optmask);
if (status != ARES_SUCCESS) {
printf("ares_init_options: %s\n", ares_strerror(status));
return 1;
}
status = ares_set_servers_csv(channel, SERVERS);
if (status != ARES_SUCCESS) {
printf("ares_set_servers_csv: %s\n", ares_strerror(status));
return 1;
}
fp = fopen(argv[1], "r");
if (!fp)
exit(EXIT_FAILURE);
memset(dns_client_fds, 0, sizeof(dns_client_fds));
memset((char *)&ev, 0, sizeof(struct epoll_event));
memset((char *)&events[0], 0, sizeof(events));
epollfd = epoll_create(DNS_MAX_SERVERS);
if (epollfd < 0) {
perror("epoll_create: ");
}
do {
if (nwaiting >= MAXWAITING || done) {
do {
wait_ares(channel);
} while (nwaiting > MAXWAITING);
}
if (!done) {
if (fscanf(fp, "%127s", domain) == 1) {
ares_gethostbyname(channel, domain, AF_INET, callback, NULL);
nwaiting++;
} else {
fprintf(stderr, "done sending\n");
done = 1;
}
}
} while (nwaiting > 0);
ares_destroy(channel);
ares_library_cleanup();
fclose(fp);
return 0;
}
I am writing a MIPI driver without using the I2C functionality, since it is not possible for me to use it. I am writing this for the Google Coral.
To write this new driver I looked at this example and adjusted it to only use the MMAP functionality.
My new code is this:
/*
* V4L2 video capture example
*
* This program can 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))
#ifndef V4L2_PIX_FMT_H264
#define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
#endif
struct buffer {
void *start;
size_t length;
};
static char *dev_name;
static int fd = -1;
struct buffer *buffers;
static unsigned int n_buffers;
static int out_buf;
static int force_format;
static int frame_count = 200;
static int frame_number = 0;
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)
{
printf("processing image\n");
frame_number++;
char filename[15];
sprintf(filename, "frame-%d.raw", frame_number); // filename becomes frame-x.raw
FILE *fp=fopen(filename,"wb");
if (out_buf)
fwrite(p, size, 1, fp); // write data to file fp
fflush(fp);
fclose(fp);
}
static int read_frame(void)
{
printf("reading frame\n");
struct v4l2_buffer buf;
unsigned int i;
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");
return 1;
}
static void mainloop(void)
{
printf("mainloop\n");
unsigned int count;
count = frame_count;
while (count-- > 0) {
printf("count number = %d\n", count );
for (;;) {
fd_set fds;
struct timeval tv;
int r;
FD_ZERO(&fds); // clear file descriptor
FD_SET(fd, &fds); // set file descriptors to the descriptor fd
/* Timeout. */
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1, &fds, NULL, NULL, &tv); // select uses a timeout, allows program to monitor file descriptors waiting untill files becomes "ready"
// returns the number of file descriptors changed. This maybe zero if timeout expires.
// probably watching reafds descriptor to change?
if (-1 == r) {
if (EINTR == errno)
continue;
errno_exit("select");
}
if (0 == r) {
fprintf(stderr, "select timeout\n");
exit(EXIT_FAILURE);
}
if (read_frame()) // if one of the descriptors is set, a frame can be read.
break;
/* EAGAIN - continue select loop. */
}
}
printf("mainloop ended\n");
}
static void stop_capturing(void)
{
printf("stop capturing\n");
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
errno_exit("VIDIOC_STREAMOFF");
printf("capturing stopped\n");
}
static void start_capturing(void)
{
printf("initiating capturing\n");
unsigned int i;
enum v4l2_buf_type type;
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");
}
printf("capturing initiated\n");
}
static void uninit_device(void)
{
unsigned int i;
for (i = 0; i < n_buffers; ++i){
if (-1 == munmap(buffers[i].start, buffers[i].length)){
errno_exit("munmap");
}
}
free(buffers);
}
static void init_mmap(void)
{
printf("initiating mmap buffer\n");
struct v4l2_requestbuffers req; //struct with details of the buffer to compose
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { // initiate buffer
if (EINVAL == errno) {
fprintf(stderr, "%s does not support "
"memory mapping\n", dev_name);
exit(EXIT_FAILURE);
} else {
errno_exit("VIDIOC_REQBUFS");
}
}
printf("memory allocated\n");
if (req.count < 2) {
fprintf(stderr, "Insufficient buffer memory on %s\n",
dev_name);
exit(EXIT_FAILURE);
}
buffers = calloc(req.count, sizeof(*buffers)); // make the amount of buffers available
if (!buffers) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { // go through buffers and adjust struct in it
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");
}
printf("mmap buffer initiated\n");
}
static void init_device(void)
{
printf("initiating device\n");
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)) { // gets information about driver and harware capabilities
if (EINVAL == errno) { // driver is not compatible with specifications
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);
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr, "%s does not support streaming i/o\n",
dev_name);
exit(EXIT_FAILURE);
}
/* Select video input, video standard and tune here. */
CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { // used to get cropping limits, pixel aspects, ... fill in type field and get all this information back
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { // get cropping rectangle
switch (errno) {
case EINVAL:
printf("EINVAL in VIDIOC_S_CROP\n");
/* Cropping not supported. */
break;
default:
printf("other error in VIDIOC_S_CROP\n");
/* Errors ignored. */
break;
}
}
} else {
/* Errors ignored. */
}
CLEAR(fmt); // set the format of the v4l2 video
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (force_format) {
fprintf(stderr, "Set H264\r\n");
fmt.fmt.pix.width = 640; //replace
fmt.fmt.pix.height = 480; //replace
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264; //replace
fmt.fmt.pix.field = V4L2_FIELD_ANY;
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;
init_mmap();
printf("device inititiated\n");
}
static void close_device(void)
{
printf("closing device\n");
if (-1 == close(fd))
errno_exit("close");
fd = -1;
printf("device closed\n");
}
/*
struct stat {
dev_t st_dev; ID of device containing file
ino_t st_ino; Inode number
mode_t st_mode; File type and mode
nlink_t st_nlink; Number of hard links
uid_t st_uid; User ID of owner
gid_t st_gid; Group ID of owner
dev_t st_rdev; Device ID (if special file)
off_t st_size; Total size, in bytes
blksize_t st_blksize; Block size for filesystem I/O
blkcnt_t st_blocks; Number of 512B blocks allocated
Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES.
struct timespec st_atim; Time of last access
struct timespec st_mtim; Time of last modification
struct timespec st_ctim; Time of last status change
#define st_atime st_atim.tv_sec Backward compatibility
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
*/
static void open_device(void)
{
printf("openening device\n");
struct stat st;
if (-1 == stat(dev_name, &st)) { // stat() returns info about file into struct
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); // open the file dev/video0, returns a file descriptor
// if fd == -1, the file could not be opened.
if (-1 == fd) {
fprintf(stderr, "Cannot open '%s': %d, %s\n",
dev_name, errno, strerror(errno));
exit(EXIT_FAILURE);
}
printf("device opened\n");
}
int main(int argc, char **argv)
{
printf("main begins\n");
dev_name = "/dev/video0";
for (;;) {
printf("back here\n");
break;
}
open_device();
init_device();
start_capturing();
mainloop();
stop_capturing();
uninit_device();
close_device();
fprintf(stderr, "\n");
return 0;
}
However I get the following error:
VIDIOC_REQBUFS error 12, Cannot allocate memory
The entire output is:
main begins
back here
openening device
device opened
initiating device
initiating mmap buffer
VIDIOC_REQBUFS error 12, Cannot allocate memory
make: *** [makefile:3: all] Error 1
In the above code this is caused by:
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { // initiate buffer
if (EINVAL == errno) {
fprintf(stderr, "%s does not support "
"memory mapping\n", dev_name);
exit(EXIT_FAILURE);
} else {
errno_exit("VIDIOC_REQBUFS");
}
}
Thus the ioctl(fd, VIDIOC_REQBUFS, &req) causes this error.
I have already looked on StackOverflow and found 1 other person with the same mistake.
He suggested to change CONFIG_CMA_SIZE_MBYTES to 32 from 16. I tried this by looking where I could find this setting. I found it in: boot/config-4.14.98-imx . However, it was already 320. (yes tenfold). I am now rather stuck on this. Is there a problem in my code, or do I need to change the setting from 320 to 32 (which seems counterintuitive).
With kind regards.
Context
Hello! I've been working on software for a robotics project that will be using vision as one of the primary senses. I've been building the system around the Raspberry Pi 3 running Raspbian Jessie Lite with kernel 4.9.35-v7+. I'm planning to train some ML models on recorded driving data from the camera in addition to other onboard sensors. To retrieve video frames I'm using the v4l2 api with a Minoru 3D USB webcam. The source for my project can be found at https://github.com/mrpossoms/AVC2017.
The Problem
This is a really bizarre issue. That which I described above works fine, with the exception that the frame rate drops significantly from the target 15fps, but only when it is looking at an object over ~1 meter away! I recorded a video here showing the issue. First I place my hand very near the camera, you can see in the terminal that the frame rate starts at the ideal 15fps. However, as I slowly move my hand away the frame rate drops to around 7fps!
Here is my camera code:
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include "structs.h"
#include "cam.h"
#define CAM_FPS 5
cam_t cam_open(const char* path, cam_settings_t* cfg)
{
int fd = open(path, O_RDWR);
int res;
if(fd < 0)
{
fprintf(stderr, "Error opening video device '%s'\n", path);
//exit(-1);
cam_t empty = {};
return empty;
}
struct v4l2_capability cap;
res = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if(res < 0)
{
fprintf(stderr, "Error: %d querying '%s' for capabilities (%d)\n", res, path, errno);
exit(-2);
}
if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
fprintf(stderr, "Error: '%s' lacks V4L2_CAP_VIDEO_CAPTURE capability\n", path);
}
res = cam_config(fd, cfg);
if(res < 0)
{
fprintf(stderr, "Error: %d configuring '%s' (%d)\n", res, path, errno);
exit(-3);
}
// Inform v4l about the buffers we want to receive data through
struct v4l2_requestbuffers bufrequest = {};
bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufrequest.memory = V4L2_MEMORY_MMAP;
bufrequest.count = 2;
if(ioctl(fd, VIDIOC_REQBUFS, &bufrequest) < 0)
{
fprintf(stderr, "VIDIOC_REQBUFS\n");
exit(-4);
}
if(bufrequest.count < 2)
{
fprintf(stderr, "Not enough memory\n");
exit(-5);
}
struct v4l2_buffer bufferinfo = {};
void** fbs = calloc(sizeof(void*), bufrequest.count);
for(int i = bufrequest.count; i--;)
{
bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufferinfo.memory = V4L2_MEMORY_MMAP;
bufferinfo.index = i;
if(ioctl(fd, VIDIOC_QUERYBUF, &bufferinfo) < 0)
{
fprintf(stderr, "VIDIOC_QUERYBUF\n");
exit(-5);
}
fbs[i] = mmap(
NULL,
bufferinfo.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
bufferinfo.m.offset
);
if(fbs[i] == MAP_FAILED)
{
fprintf(stderr, "mmap failed\n");
exit(-6);
}
bzero(fbs[i], bufferinfo.length);
ioctl(fd, VIDIOC_QBUF, &bufferinfo);
}
cam_t cam = {
.fd = fd,
.frame_buffers = fbs,
.buffer_info = bufferinfo,
};
cam_wait_frame(&cam);
int type = bufferinfo.type;
if(ioctl(fd, VIDIOC_STREAMON, &type) < 0)
{
fprintf(stderr, "Error starting streaming\n");
exit(-7);
}
return cam;
}
int cam_request_frame(cam_t* cam)
{
cam->buffer_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
cam->buffer_info.memory = V4L2_MEMORY_MMAP;
return ioctl(cam->fd, VIDIOC_QBUF, &cam->buffer_info);
}
int cam_wait_frame(cam_t* cam)
{
ioctl(cam->fd, VIDIOC_DQBUF, &cam->buffer_info);
}
int cam_config(int fd, cam_settings_t* cfg)
{
int res = 0;
struct v4l2_format format;
if(!cfg)
{
fprintf(stderr, "Error: null configuration provided\n");
return -1;
}
/*
res = ioctl(fd, VIDIOC_G_FMT, &format);
if(res < 0)
{
fprintf(stderr, "Error: failed retrieving camera settings (%d)\n", errno);
return -2;
}
*/
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format.fmt.pix.width = cfg->width;
format.fmt.pix.height = cfg->height;
if(ioctl(fd, VIDIOC_S_FMT, &format) < 0)
{
fprintf(stderr, "Error: failed applying camera settings\n");
return -3;
}
struct v4l2_streamparm parm = {};
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1;
parm.parm.capture.timeperframe.denominator = 15;
res = ioctl(fd, VIDIOC_S_PARM, &parm);
return 0;
}
I can reproduce the problem 100% of the time, and I'm really at a loss as to what could be causing this behavior. Any suggestions, or ideas are greatly appreciated.
As Mark suggested, the problem was the exposure. The driver must have decided that it needed more exposure time per frame in darker situations. Just by bringing the camera outside immediately boosted the frame rate to the target 15fps.
Trying to create a raw socket based program using mmap_packet to send packets at fast rate.
The following code is adopted from the example at this gist. It does send packets but it doesn't send it fast. On my 1Gbps nic (r8169 driver), it only sends at a rate of about 95,000 packets/second on my corei7 processor (3.1GHz). I believe it could have sent at much higher rate.
Not sure what is the bottleneck. Any ideas? Thanks!
Here is the code snippet:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include <sys/time.h>
#define PACKET_QDISC_BYPASS 20
/// The number of frames in the ring
// This number is not set in stone. Nor are block_size, block_nr or frame_size
#define CONF_RING_FRAMES 1024
#define CONF_DEVICE "eth0"
/// Offset of data from start of frame
#define PKT_OFFSET (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + \
TPACKET_ALIGN(sizeof(struct sockaddr_ll)))
/// (unimportant) macro for loud failure
#define RETURN_ERROR(lvl, msg) \
do { \
fprintf(stderr, msg); \
return lvl; \
} while(0);
static struct sockaddr_ll txring_daddr;
double getTS() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec/1000000.0;
}
/// create a linklayer destination address
// #param ringdev is a link layer device name, such as "eth0"
static int
init_ring_daddr(int fd, const char *ringdev)
{
struct ifreq ifreq;
// get device index
strcpy(ifreq.ifr_name, ringdev);
if (ioctl(fd, SIOCGIFINDEX, &ifreq)) {
perror("ioctl");
return -1;
}
txring_daddr.sll_family = AF_PACKET;
txring_daddr.sll_protocol = htons(ETH_P_IP);
txring_daddr.sll_ifindex = ifreq.ifr_ifindex;
// set the linklayer destination address
// NOTE: this should be a real address, not ff.ff....
txring_daddr.sll_halen = ETH_ALEN;
memset(&txring_daddr.sll_addr, 0xff, ETH_ALEN);
return 0;
}
/// Initialize a packet socket ring buffer
// #param ringtype is one of PACKET_RX_RING or PACKET_TX_RING
static char *
init_packetsock_ring(int fd, int ringtype)
{
struct tpacket_req tp;
char *ring;
// tell kernel to export data through mmap()ped ring
tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
tp.tp_block_nr = 1;
tp.tp_frame_size = getpagesize();
tp.tp_frame_nr = CONF_RING_FRAMES;
if (setsockopt(fd, SOL_PACKET, ringtype, (void*) &tp, sizeof(tp))) {
perror("setting up ring");
RETURN_ERROR(NULL, "setsockopt() ring\n");
}
#ifdef TPACKET_V2
printf("it's TPACKET_V2\n");
val = TPACKET_V1;
setsockopt(fd, SOL_PACKET, PACKET_HDRLEN, &val, sizeof(val));
#endif
// open ring
ring = mmap(0, tp.tp_block_size * tp.tp_block_nr,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (!ring)
RETURN_ERROR(NULL, "mmap()\n");
if (init_ring_daddr(fd, CONF_DEVICE))
return NULL;
return ring;
}
/// Create a packet socket. If param ring is not NULL, the buffer is mapped
// #param ring will, if set, point to the mapped ring on return
// #return the socket fd
static int
init_packetsock(char **ring, int ringtype)
{
int fd;
// open packet socket
//fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
//fd = socket(AF_INET,SOCK_RAW,htons(ETH_P_ALL)); //ETH_P_ALL = 3
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd < 0) {
perror("open socket");
RETURN_ERROR(-1, "Root priliveges are required\nsocket() rx. \n");
}
if (ring) {
*ring = init_packetsock_ring(fd, ringtype);
if (!*ring) {
close(fd);
return -1;
}
}
return fd;
}
static int
exit_packetsock(int fd, char *ring)
{
if (munmap(ring, CONF_RING_FRAMES * getpagesize())) {
perror("munmap");
return 1;
}
if (close(fd)) {
perror("close");
return 1;
}
return 0;
}
/// transmit a packet using packet ring
// NOTE: for high rate processing try to batch system calls,
// by writing multiple packets to the ring before calling send()
//
// #param pkt is a packet from the network layer up (e.g., IP)
// #return 0 on success, -1 on failure
static int process_tx(int fd, char *ring, const char *pkt, int pktlen)
{
static int ring_offset = 0;
struct tpacket_hdr *header;
struct pollfd pollset;
char *off;
int ret;
// fetch a frame
// like in the PACKET_RX_RING case, we define frames to be a page long,
// including their header. This explains the use of getpagesize().
header = (void *) ring + (ring_offset * getpagesize());
assert((((unsigned long) header) & (getpagesize() - 1)) == 0);
while (header->tp_status != TP_STATUS_AVAILABLE) {
// if none available: wait on more data
pollset.fd = fd;
pollset.events = POLLOUT;
pollset.revents = 0;
ret = poll(&pollset, 1, 1000 /* don't hang */);
if (ret < 0) {
if (errno != EINTR) {
perror("poll");
return -1;
}
//return 0;
}
}
// fill data
off = ((void *) header) + (TPACKET_HDRLEN - sizeof(struct sockaddr_ll));
memcpy(off, pkt, pktlen);
// fill header
header->tp_len = pktlen;
header->tp_status = TP_STATUS_SEND_REQUEST;
// increase consumer ring pointer
ring_offset = (ring_offset + 1) & (CONF_RING_FRAMES - 1);
// notify kernel
return 0;
}
/// Example application that opens a packet socket with rx_ring
int main(int argc, char **argv)
{
char *ring;
char pkt[125] = {0x00,0x0c,0x29,0xa4,0xff,0xbc,0x40,0x25,0xc2,0xd9,0xfb,0x8c,0x08,0x00,0x45,0x00,0x00,0x6f,0x24,0x1b,0x40,0x00,0x40,0x06,0x02,0x4b,0x0a,0x00,0x00,0x07,0x0a,0x00,0x00,0x1d,0xb8,0x64,0x01,0xbb,0x80,0x9e,0xaa,0x77,0x17,0x6d,0xa2,0x04,0x80,0x18,0x00,0x73,0x03,0xa0,0x00,0x00,0x01,0x01,0x08,0x0a,0x01,0x27,0x8e,0xaf,0x00,0x01,0xe8,0x71,0x16,0x03,0x01,0x00,0x36,0x01,0x00,0x00,0x32,0x03,0x02,0x55,0xf5,0x01,0xa9,0xc0,0xca,0xae,0xd6,0xd2,0x9b,0x6a,0x79,0x6d,0x9a,0xe8,0x9d,0x78,0xe2,0x64,0x98,0xf0,0xac,0xcb,0x2c,0x0d,0x51,0xa5,0xf8,0xc4,0x0f,0x93,0x87,0x00,0x00,0x04,0x00,0x35,0x00,0xff,0x01,0x00,0x00,0x05,0x00,0x0f,0x00,0x01,0x01};
int fd;
printf("page size %x\n", getpagesize());
fd = init_packetsock(&ring, PACKET_TX_RING);
if (fd < 0)
return 1;
// TODO: make correct IP packet out of pkt
int i;
double startTs = getTS();
double currentTs;
int pktCnt = 0;
int sendCnt = 0;
while (1) {
for (i=0; i<1000; i++) {
pkt[1] ++; pktCnt++;
process_tx(fd, ring, pkt, 125);
}
if (sendto(fd, NULL, 0, 0, (void *) &txring_daddr, sizeof(txring_daddr)) < 0) {
perror("sendto");
return -1;
}
sendCnt++;
usleep(300);
currentTs = getTS();
if ((currentTs - startTs) >= 1.0) {
startTs += 1.0;
printf("%7d %6d\n", pktCnt, sendCnt);
pktCnt = 0; sendCnt = 0;
}
}
if (exit_packetsock(fd, ring))
return 1;
printf("OK\n");
return 0;
}
UPDATE1
The current NIC is RealTek RTL8111/8168/8411 NIC. After upgrading the driver to the version as of 8.044, the rate goes up to 135K/second.
Ran the same program on Intel 82577LM Gigabit NIC, got about 430K/seconds rate.
I'v written a simple C shared object library which calls v4l2(Video for Linux two) API e.g. v4l2_open(). Then I'm trying to poll() on the returned device handle but it always return POLLERR in the revents. I tried different parameters with timeout but it does not help. Here is the complete code.
/*
* libwebcam.h
*
* Created on: 13.04.2016
* Author: max
*/
#ifndef LIBWEBCAM_H_
#define LIBWEBCAM_H_
#include <stdint.h>
struct webcam
{
int fd;
uint32_t width;
uint32_t height;
uint32_t sizeimage;
uint32_t bytesperline;
uint8_t *image_buffer;
void *priv_data;
};
int webcam_open(struct webcam *w);
int webcam_close(struct webcam *w);
int webcam_take_image(struct webcam *w);
int webcam_poll(struct webcam *w);
#endif /* LIBWEBCAM_H_ */
/*
* libwebcam.c
*
* Created on: 13.04.2016
* Author: max
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libv4l2.h>
#include <poll.h>
#include "libwebcam.h"
int webcam_open(struct webcam *w)
{
struct v4l2_capability caps;
struct v4l2_format fmt;
int dev_index;
int dev;
char buffer[255];
for (dev_index = 0; dev_index < 64; dev_index++) {
memset(&buffer, 0, sizeof(buffer));
sprintf(buffer, "/dev/video%d", dev_index);
#ifdef DEBUG
printf("libwebcam: Probing %s\n", buffer);
#endif
dev = v4l2_open(buffer, O_RDWR | O_NONBLOCK, 0);
if (dev != -1) {
memset(&caps, 0, sizeof(caps));
if (v4l2_ioctl(dev, VIDIOC_QUERYCAP, &caps) == -1) {
return -1;
}
if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
#ifdef DEBUG
printf("libwebcam: %s is video capture device\n", buffer);
#endif
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) == -1) {
return -1;
}
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
if(v4l2_ioctl(dev, VIDIOC_S_FMT, &fmt) == -1) {
return -1;
}
if(v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) == -1) {
return -1;
}
if(w)
{
w->fd = dev;
w->bytesperline = fmt.fmt.pix.bytesperline;
w->height = fmt.fmt.pix.height;
w->sizeimage = fmt.fmt.pix.sizeimage;
w->width = fmt.fmt.pix.width;
w->image_buffer = calloc(fmt.fmt.pix.sizeimage, sizeof(uint8_t));
if(w->image_buffer == NULL)
return -1;
w->priv_data = calloc(1, sizeof(struct pollfd));
if(w->priv_data == NULL)
return -1;
return 0;
}
else
{
errno = EINVAL;
return -1;
}
}
}
}
errno = ENODEV;
return -1;
}
int webcam_close(struct webcam *w)
{
if(w)
{
if(w->image_buffer != NULL){
free(w->image_buffer);
w->image_buffer = NULL;
}
if(w->priv_data != NULL) {
free(w->priv_data);
w->priv_data = NULL;
}
if(w->fd != -1)
if(v4l2_close(w->fd) == -1)
return -1;
return 0;
}
errno = EINVAL;
return -1;
}
int webcam_take_image(struct webcam *w)
{
if(w)
{
return v4l2_read(w->fd, w->image_buffer, w->sizeimage * sizeof(uint8_t));
}
errno = EINVAL;
return -1;
}
int webcam_poll(struct webcam *w)
{
if(w)
{
((struct pollfd *)w->priv_data)->events = POLLIN;
((struct pollfd *)w->priv_data)->revents = 0;
((struct pollfd *)w->priv_data)->fd = w->fd;
if(poll(((struct pollfd*)w->priv_data), 1, -1) == -1)
{
return -1;
}
if(((struct pollfd*)w->priv_data)->revents & POLLIN) {
#ifdef DEBUG
printf("libwebcam: Data is available...\n");
#endif
return 1;
}
if(((struct pollfd*)w->priv_data)->revents & POLLERR) {
#ifdef DEBUG
printf("libwebcam: Error in poll...\n");
#endif
return -1;
}
#ifdef DEBUG
printf("libwebcam: Timeout...\n");
#endif
return 0;
}
#ifdef DEBUG
printf("libwebcam: Struct not valid...\n");
#endif
errno = EINVAL;
return -1;
}
/*
* test.c
*
* Created on: 14.04.2016
* Author: max
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libwebcam.h>
int main(int argc, char **argv)
{
struct webcam w;
int polled;
memset(&w, 0, sizeof(w));
if(webcam_open(&w) == -1)
{
perror("Unable to find webcam");
return EXIT_FAILURE;
}
while(1)
{
polled = webcam_poll(&w);
if(polled == -1)
{
perror("Error in poll");
return EXIT_FAILURE;
}
if(polled == 1)
{
webcam_take_image(&w);
}
}
if(webcam_close(&w) == -1)
{
perror("Unable to close webcam");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Can anyone tell me what is going on in the code?
I tested your code, and on my system the poll call is returning 0. From the man page, "A value of 0 indicates that the call timed out and no file descriptors were ready." So you should test for 0 in addition to -1 and 1.
I don't know why it fills in revents with POLLERR, but I would only check the resulting revents field if poll returned 1.
Also, not all V4L2 devices support read(), so you should check that your device supports it by testingcaps.capabilities & V4L2_CAP_READWRITE. My laptop webcam where I tested this does not support read/write.
See this example program for reference.