是否有任何c库可以从Linux上的网络摄像头获取视频?
我们很多人都使用OpenCV(跨平台计算机视觉库,目前在v2.1上)
以下代码段从相机抓取帧,将它们转换为灰度并在屏幕上显示:
#include#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
官方的例子
你得到什么:
./v4l2grab
:捕获一些快照到文件 outNNN.ppm
./v4l2gl
:使用OpenGL纹理(立即渲染,嘿!)和原始X11窗口(加上GLUT gluLookAt
用于良好测量)在窗口上直播显示视频.
如何在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
在Git树之外使用这些示例也很容易,只需将它们复制出来,使相对包含""
绝对值<>
,然后删除即可config.h
.我已经为你完成了这个:https://github.com/cirosantilli/cpp-cheat/tree/09fe73d248f7da2e9c9f3eff2520a143c259f4a6/v4l2
来自docs的最小例子
该文档4.9.0包含的内容似乎是最低版本./v4l2grab
的https://linuxtv.org/downloads/v4l-dvb-apis-new/uapi/v4l/v4l2grab-example.html.我需要对其进行最低限度的修补,然后我将补丁发送到http://www.spinics.net/lists/linux-media/(他们的文档在Linux内核树中作为第一个,整洁的)存在,在那里它被彻底忽略了.
用法:
gcc v4l2grab.c -lv4l2 ./a.out
修补代码:
/* V4L2 video picture grabber Copyright (C) 2009 Mauro Carvalho ChehabThis 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 #include #include #include #include #include #include #include #include #include #include #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; }
标头仅面向对象的版本以供重用
从文档中的示例中提取,但其形式使其易于重用.
common_v4l2.h
:
#ifndef COMMON_V4L2_H #define COMMON_V4L2_H #include#include #include #include #include #include #include #include #include #include #include #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#include #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; }
上游:https://github.com/cirosantilli/cpp-cheat/blob/be5d6444bddab93e95949b3388d92007b5ca916f/v4l2/common_v4l2.h
SDL
视频捕获在他们的路线图中:https://wiki.libsdl.org/Roadmap,我打赌它将在Linux上包装v4l.
当我们获得可移植性层时,它会比OpenCV更少膨胀.
你最好的选择可能是:video4linux(V4L)
它易于使用,功能强大.