//Merci à https://stackoverflow.com/questions/278112/webcam-library-for-c-on-linux #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)) #define WRITE_PPM_ERR 12 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; /*============================================================================*/ typedef struct { s16 width; // largeur de l'image en pixels s16 height; u8 nchannels; // nombre de channels u8* pixels; // tableau de pixels u32 len; // nombre d'octets en tout } PIX; PIX lastframe; /*============================================================================*/ CommonV4l2 common_v4l2; struct buffer *buffers; /*============================================================================*/ void print_error(const int exit_status, const int err) { char* msg = strerror(err); printf("%s\n", msg); _exit(exit_status); } /*============================================================================*/ 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); tv.tv_sec = 2; // Timeout 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){ //printf("buffer %d\n",i); v4l2_munmap(this->buffers[i].start, this->buffers[i].length); } v4l2_close(this->fd); free(this->buffers); } /*image couleur 3 channels ===================================================*/ static void save_ppm(u32 i, u32 x_res, u32 y_res, char *data, size_t data_length) { FILE *fout; char out_name[9]; sprintf(out_name, "%03d.ppm", i); fout = fopen(out_name, "w"); if (!fout) print_error(WRITE_PPM_ERR, errno); fprintf(fout, "P6\n%d %d 255\n", x_res, y_res); fwrite(data, data_length, 1, fout); fclose(fout); } /*image gris 1 channel P5:binaire P2:ASCII ===================================*/ // 228Ko ppm -> 76Ko pgm intéressant pour le réseau, ça permettrait d'avoir une résolution plus grande ou plus de FPS static void save_PGM(u32 i, u32 x_res, u32 y_res, u8 *data, size_t data_length) { FILE *fout; char out_name[9]; sprintf(out_name, "%03d.pgm", i); fout = fopen(out_name, "w"); if (!fout) print_error(WRITE_PPM_ERR, errno); fprintf(fout, "P5\n%d %d 255\n", x_res, y_res); fwrite(data, data_length, 1, fout); fclose(fout); } /*image monochrome 1 channel 0:noir 1:blanc P4 ==================================*/ //static void save_PBM(u32 i, u32 x_res, u32 y_res, char *data, size_t data_length) {} /*============================================================================*/ // https://www.linuxtv.org/downloads/v4l-dvb-apis-old/v4l2grab-example.html void grabFrameFromCamera(){ CommonV4l2_update_image(&common_v4l2); // je voudrais bien que le serveur n'envoie qu'un seul channel, en noir et blanc. 1 pixel = 1 octet de 0 à 255. // commence par essayer de générer cette image et l'écrire en local, après on verra comment l'envoyer. // ne garde que le canal rouge pour 3X moins de bande passante 228Ko ppm -> 76Ko pgm lastframe.len = CommonV4l2_get_image_size(&common_v4l2); lastframe.pixels = CommonV4l2_get_image(&common_v4l2); int nbredpixels = lastframe.len / 3; u8* redpixels = (char*)malloc(nbredpixels+1); int n = 0; while (n < nbredpixels){ redpixels[n] = lastframe.pixels[3*n+1];//(u8)((lastframe.pixels[3*n]+lastframe.pixels[3*n+1]+lastframe.pixels[3*n+2]) / 3);//lastframe.pixels[3*n]; n++; } redpixels[n]=0; static int i=0; save_PGM (i++, x_res, y_res, redpixels, nbredpixels); free(redpixels); //save_ppm(i++, x_res, y_res, CommonV4l2_get_image(&common_v4l2), CommonV4l2_get_image_size(&common_v4l2)); //save_ppm(i++, x_res, y_res, lastframe.pixels, lastframe.len); }
camera.h 208 lignes, 7586 octets