Библиотека веб-камеры для C в Linux?

Есть ли библиотека c для получения видео с веб-камеры в Linux?

Подскажите, пожалуйста, какую библиотеку вы в итоге использовали?

russoue 07.01.2013 07:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
1
22 611
3

Ответы 3

Вероятно, ваш лучший выбор: video4linux (V4L)

Он прост в использовании и мощный.

Официальная ссылка linuxtv.org

Matthieu 04.05.2018 00:33

Многие из нас используют OpenCV (кроссплатформенная библиотека компьютерного зрения, в настоящее время находится на v2.1)

Следующий фрагмент кода захватывает кадры с камеры, преобразует их в оттенки серого и отображает их на экране:

#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;
}

Новая ссылка opencv.org

GramThanos 03.11.2014 02:02

Подсказка о том, как скомпилировать приведенный выше фрагмент кода на OpenCV 3.4.1 с помощью gcc: gcc вышеcode.c -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio

Bernhard Bodenstorfer 26.01.2019 20:13

Официальные примеры v4l2

То, что вы получаете:

  • ./v4l2grab: захватить несколько снимков в файлы outNNN.ppm
  • ./v4l2gl: показывать видео в реальном времени в окне с использованием текстуры OpenGL (немедленный рендеринг, привет!) И необработанных окон X11 (плюс gluLookAt GLUT для хорошей оценки).

Как получить на 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, просто скопируйте их, сделайте относительный включает "" absolute <> и удалите config.h. Я сделал это для вас по адресу: https://github.com/cirosantilli/cpp-cheat/tree/09fe73d248f7da2e9c9f3eff2520a143c259f4a6/v4l2

Минимальный пример из документов

Документы 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 Chehab <[email protected]>

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;
}

Объектно-ориентированная версия только для заголовков для повторного использования

Извлечено из примера в документации, но в форме, упрощающей повторное использование.

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;
}

Вверх по течению: https://github.com/cirosantilli/cpp-cheat/blob/be5d6444bddab93e95949b3388d92007b5ca916f/v4l2/common_v4l2.h

SDL

Захват видео есть в их дорожной карте: https://wiki.libsdl.org/Roadmap, и я уверен, что он обернет v4l в Linux.

Будет приятно, когда мы получим этот уровень переносимости, с меньшим раздуванием, чем OpenCV.

Потрясающий! Могу ли я принять это от имени @joxer? ;) (PS: на моем 16.04 все скомпилировалось нормально, без всяких патчей, в 2018 году)

Matthieu 04.05.2018 00:29

@Matthieu да, я принимаю ваше согласие :-)

Ciro Santilli新疆棉花TRUMP BAN BAD 04.05.2018 00:51

Другие вопросы по теме