2016-07-05 17:58:34 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
2016-07-06 19:43:37 +02:00
|
|
|
#include <unistd.h>
|
2016-07-05 17:58:34 +02:00
|
|
|
#include <sched.h>
|
|
|
|
|
#include <errno.h>
|
2016-07-06 19:43:37 +02:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
#include <poll.h>
|
2016-07-05 17:58:34 +02:00
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
2016-07-05 17:58:34 +02:00
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
static int
|
|
|
|
|
xioctl (int fd, int request, void *arg)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
err = ioctl (fd, request, arg);
|
|
|
|
|
} while (err == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2016-07-05 17:58:34 +02:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spa_v4l2_open (SpaV4l2Source *this)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-05 17:58:34 +02:00
|
|
|
struct stat st;
|
2016-07-07 09:30:18 +02:00
|
|
|
SpaV4l2SourceProps *props = &this->props[1];
|
2016-07-05 17:58:34 +02:00
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
if (state->opened)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2016-07-07 09:30:18 +02:00
|
|
|
fprintf (stderr, "Playback device is '%s'\n", props->device);
|
2016-07-05 17:58:34 +02:00
|
|
|
|
2016-07-07 09:30:18 +02:00
|
|
|
if (stat (props->device, &st) < 0) {
|
2016-07-05 17:58:34 +02:00
|
|
|
fprintf(stderr, "Cannot identify '%s': %d, %s\n",
|
2016-07-07 09:30:18 +02:00
|
|
|
props->device, errno, strerror (errno));
|
2016-07-05 17:58:34 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!S_ISCHR (st.st_mode)) {
|
2016-07-07 09:30:18 +02:00
|
|
|
fprintf(stderr, "%s is no device\n", props->device);
|
2016-07-05 17:58:34 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-07 09:30:18 +02:00
|
|
|
state->fd = open (props->device, O_RDWR | O_NONBLOCK, 0);
|
2016-07-05 17:58:34 +02:00
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
if (state->fd == -1) {
|
|
|
|
|
fprintf (stderr, "Cannot open '%s': %d, %s\n",
|
2016-07-07 09:30:18 +02:00
|
|
|
props->device, errno, strerror (errno));
|
2016-07-05 17:58:34 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_QUERYCAP, &state->cap) < 0) {
|
|
|
|
|
perror ("QUERYCAP");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((state->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
|
2016-07-07 09:30:18 +02:00
|
|
|
fprintf (stderr, "%s is no video capture device\n", props->device);
|
2016-07-06 19:43:37 +02:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
state->opened = true;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static SpaVideoFormat
|
|
|
|
|
fourcc_to_video_format (uint32_t fourcc)
|
|
|
|
|
{
|
|
|
|
|
SpaVideoFormat format;
|
|
|
|
|
|
|
|
|
|
switch (fourcc) {
|
|
|
|
|
case V4L2_PIX_FMT_GREY: /* 8 Greyscale */
|
|
|
|
|
format = SPA_VIDEO_FORMAT_GRAY8;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_Y16:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_GRAY16_LE;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_Y16_BE:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_GRAY16_BE;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_XRGB555:
|
|
|
|
|
case V4L2_PIX_FMT_RGB555:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_RGB15;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_XRGB555X:
|
|
|
|
|
case V4L2_PIX_FMT_RGB555X:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_BGR15;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_RGB565:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_RGB16;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_RGB24:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_RGB;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_BGR24:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_BGR;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_XRGB32:
|
|
|
|
|
case V4L2_PIX_FMT_RGB32:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_xRGB;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_XBGR32:
|
|
|
|
|
case V4L2_PIX_FMT_BGR32:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_BGRx;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_ABGR32:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_BGRA;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_ARGB32:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_ARGB;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_NV12:
|
|
|
|
|
case V4L2_PIX_FMT_NV12M:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_NV12;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_NV12MT:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_NV12_64Z32;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_NV21:
|
|
|
|
|
case V4L2_PIX_FMT_NV21M:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_NV21;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YVU410:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_YVU9;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YUV410:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_YUV9;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YUV420:
|
|
|
|
|
case V4L2_PIX_FMT_YUV420M:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_I420;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YUYV:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_YUY2;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YVU420:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_YV12;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_UYVY:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_UYVY;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YUV411P:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_Y41B;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YUV422P:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_Y42B;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_YVYU:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_YVYU;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_NV16:
|
|
|
|
|
case V4L2_PIX_FMT_NV16M:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_NV16;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_NV61:
|
|
|
|
|
case V4L2_PIX_FMT_NV61M:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_NV61;
|
|
|
|
|
break;
|
|
|
|
|
case V4L2_PIX_FMT_NV24:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_NV24;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
format = SPA_VIDEO_FORMAT_UNKNOWN;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return format;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
video_format_to_fourcc (SpaVideoFormat format)
|
|
|
|
|
{
|
|
|
|
|
uint32_t fourcc;
|
|
|
|
|
|
|
|
|
|
switch (format) {
|
|
|
|
|
case SPA_VIDEO_FORMAT_I420:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_YUV420;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_YUY2:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_YUYV;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_UYVY:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_UYVY;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_YV12:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_YVU420;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_Y41B:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_YUV411P;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_Y42B:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_YUV422P;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_NV12:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_NV12;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_NV12_64Z32:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_NV12MT;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_NV21:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_NV21;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_NV16:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_NV16;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_NV61:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_NV61;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_NV24:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_NV24;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_YVYU:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_YVYU;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_RGB15:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_RGB555;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_RGB16:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_RGB565;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_RGB:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_RGB24;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_BGR:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_BGR24;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_xRGB:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_RGB32;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_ARGB:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_RGB32;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_BGRx:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_BGR32;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_BGRA:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_BGR32;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_GRAY8:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_GREY;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_GRAY16_LE:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_Y16;
|
|
|
|
|
break;
|
|
|
|
|
case SPA_VIDEO_FORMAT_GRAY16_BE:
|
|
|
|
|
fourcc = V4L2_PIX_FMT_Y16_BE;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fourcc = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return fourcc;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-15 13:20:20 +02:00
|
|
|
#define FOURCC_ARGS(f) (f)&0x7f,((f)>>8)&0x7f,((f)>>16)&0x7f,((f)>>24)&0x7f
|
|
|
|
|
|
|
|
|
|
static SpaResult
|
|
|
|
|
spa_v4l2_enum_format (SpaV4l2Source *this, SpaFormat **format, void **cookie)
|
|
|
|
|
{
|
|
|
|
|
SpaV4l2State *state = &this->state[0];
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
if (spa_v4l2_open (this) < 0)
|
|
|
|
|
return SPA_RESULT_ERROR;
|
|
|
|
|
|
|
|
|
|
*format = NULL;
|
|
|
|
|
|
|
|
|
|
if (*cookie == NULL) {
|
|
|
|
|
CLEAR (state->fmtdesc);
|
|
|
|
|
state->fmtdesc.index = 0;
|
|
|
|
|
state->fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
state->next_fmtdesc = true;
|
|
|
|
|
|
|
|
|
|
CLEAR (state->frmsize);
|
|
|
|
|
state->next_frmsize = true;
|
|
|
|
|
|
|
|
|
|
CLEAR (state->frmival);
|
|
|
|
|
state->next_frmival = true;
|
|
|
|
|
|
|
|
|
|
*cookie = state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
if (state->next_fmtdesc) {
|
|
|
|
|
if ((res = xioctl (state->fd, VIDIOC_ENUM_FMT, &state->fmtdesc)) < 0) {
|
|
|
|
|
if (errno != EINVAL)
|
|
|
|
|
perror ("VIDIOC_ENUM_FMT");
|
|
|
|
|
return SPA_RESULT_ENUM_END;
|
|
|
|
|
}
|
|
|
|
|
state->next_fmtdesc = false;
|
|
|
|
|
|
|
|
|
|
state->frmsize.index = 0;
|
|
|
|
|
state->frmsize.pixel_format = state->fmtdesc.pixelformat;
|
|
|
|
|
state->next_frmsize = true;
|
|
|
|
|
}
|
|
|
|
|
if (state->next_frmsize) {
|
|
|
|
|
if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMESIZES, &state->frmsize)) < 0) {
|
|
|
|
|
if (errno == EINVAL) {
|
|
|
|
|
state->fmtdesc.index++;
|
|
|
|
|
state->next_fmtdesc = true;
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
perror ("VIDIOC_ENUM_FRAMESIZES");
|
|
|
|
|
return SPA_RESULT_ENUM_END;
|
|
|
|
|
}
|
|
|
|
|
state->next_frmsize = false;
|
|
|
|
|
|
|
|
|
|
if (state->frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
|
|
|
|
|
state->frmival.index = 0;
|
|
|
|
|
state->frmival.pixel_format = state->frmsize.pixel_format;
|
|
|
|
|
state->frmival.width = state->frmsize.discrete.width;
|
|
|
|
|
state->frmival.height = state->frmsize.discrete.height;
|
|
|
|
|
state->next_frmival = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (state->next_frmival) {
|
|
|
|
|
if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMEINTERVALS, &state->frmival)) < 0) {
|
|
|
|
|
if (errno == EINVAL) {
|
|
|
|
|
state->frmsize.index++;
|
|
|
|
|
state->next_frmsize = true;
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
perror ("VIDIOC_ENUM_FRAMEINTERVALS");
|
|
|
|
|
return SPA_RESULT_ENUM_END;
|
|
|
|
|
}
|
|
|
|
|
state->frmival.index++;
|
|
|
|
|
}
|
|
|
|
|
fprintf (stderr, "format %c%c%c%c\n", FOURCC_ARGS (state->fmtdesc.pixelformat));
|
|
|
|
|
if (state->frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
|
|
|
|
|
fprintf (stderr, "size %dx%d\n", state->frmsize.discrete.width, state->frmsize.discrete.height);
|
|
|
|
|
} else if (state->frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
|
|
|
|
|
fprintf (stderr, "size %dx%d - %dx%d with step %d/%d\n",
|
|
|
|
|
state->frmsize.stepwise.min_width,
|
|
|
|
|
state->frmsize.stepwise.min_height,
|
|
|
|
|
state->frmsize.stepwise.max_width,
|
|
|
|
|
state->frmsize.stepwise.max_height,
|
|
|
|
|
state->frmsize.stepwise.step_width,
|
|
|
|
|
state->frmsize.stepwise.step_height);
|
|
|
|
|
}
|
|
|
|
|
if (state->frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
|
|
|
|
|
fprintf (stderr, "framerate %u/%u\n",
|
|
|
|
|
state->frmival.discrete.numerator,
|
|
|
|
|
state->frmival.discrete.denominator);
|
|
|
|
|
} else if (state->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
|
|
|
|
|
fprintf (stderr, "framerate %u/%u - %u/%u\n",
|
|
|
|
|
state->frmival.stepwise.min.numerator,
|
|
|
|
|
state->frmival.stepwise.min.denominator,
|
|
|
|
|
state->frmival.stepwise.max.numerator,
|
|
|
|
|
state->frmival.stepwise.max.denominator);
|
|
|
|
|
} else if (state->frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
|
|
|
|
|
fprintf (stderr, "framerate %u/%u - %u/%u step %u/%u\n",
|
|
|
|
|
state->frmival.stepwise.min.numerator,
|
|
|
|
|
state->frmival.stepwise.min.denominator,
|
|
|
|
|
state->frmival.stepwise.max.numerator,
|
|
|
|
|
state->frmival.stepwise.max.denominator,
|
|
|
|
|
state->frmival.stepwise.step.numerator,
|
|
|
|
|
state->frmival.stepwise.step.denominator);
|
|
|
|
|
}
|
|
|
|
|
return SPA_RESULT_OK;
|
|
|
|
|
}
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spa_v4l2_set_format (SpaV4l2Source *this, SpaFormat *format, bool try_only)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
|
|
|
|
|
struct v4l2_format reqfmt, fmt;
|
2016-07-08 20:12:56 +02:00
|
|
|
struct v4l2_streamparm streamparm;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
CLEAR (fmt);
|
2016-07-08 20:12:56 +02:00
|
|
|
CLEAR (streamparm);
|
2016-07-06 19:43:37 +02:00
|
|
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
2016-07-08 20:12:56 +02:00
|
|
|
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
if (format->media_type == SPA_MEDIA_TYPE_VIDEO) {
|
|
|
|
|
if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) {
|
|
|
|
|
SpaVideoRawFormat *f = (SpaVideoRawFormat *) format;
|
|
|
|
|
|
|
|
|
|
fmt.fmt.pix.pixelformat = video_format_to_fourcc (f->info.format);
|
|
|
|
|
fmt.fmt.pix.width = f->info.width;
|
|
|
|
|
fmt.fmt.pix.height = f->info.height;
|
|
|
|
|
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
2016-07-08 20:12:56 +02:00
|
|
|
streamparm.parm.capture.timeperframe.numerator = f->info.framerate.denom;
|
|
|
|
|
streamparm.parm.capture.timeperframe.denominator = f->info.framerate.num;
|
|
|
|
|
|
|
|
|
|
fprintf (stderr, "set %08x %dx%d %d/%d\n", fmt.fmt.pix.pixelformat,
|
|
|
|
|
fmt.fmt.pix.width, fmt.fmt.pix.height, f->info.framerate.denom,
|
|
|
|
|
f->info.framerate.num);
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
} else
|
|
|
|
|
return -1;
|
|
|
|
|
} else
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
reqfmt = fmt;
|
|
|
|
|
|
|
|
|
|
if (spa_v4l2_open (this) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (xioctl (state->fd, cmd, &fmt) < 0) {
|
|
|
|
|
perror ("VIDIOC_S_FMT");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
/* some cheap USB cam's won't accept any change */
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_S_PARM, &streamparm) < 0)
|
|
|
|
|
perror ("VIDIOC_S_PARM");
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
if (reqfmt.fmt.pix.pixelformat != fmt.fmt.pix.pixelformat ||
|
|
|
|
|
reqfmt.fmt.pix.width != fmt.fmt.pix.width ||
|
|
|
|
|
reqfmt.fmt.pix.height != fmt.fmt.pix.height)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (try_only)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
state->fmt = fmt;
|
2016-07-08 20:12:56 +02:00
|
|
|
state->info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER;
|
|
|
|
|
state->info.maxbuffering = -1;
|
|
|
|
|
state->info.latency = -1;
|
2016-07-13 18:29:55 +02:00
|
|
|
|
|
|
|
|
state->info.n_params = 1;
|
|
|
|
|
state->info.params = state->params;
|
|
|
|
|
state->params[0] = &state->param_buffers.param;
|
|
|
|
|
state->param_buffers.param.type = SPA_ALLOC_PARAM_TYPE_BUFFERS;
|
|
|
|
|
state->param_buffers.param.size = sizeof (&state->buffers);
|
|
|
|
|
state->param_buffers.minsize = fmt.fmt.pix.sizeimage;
|
|
|
|
|
state->param_buffers.stride = fmt.fmt.pix.bytesperline;
|
|
|
|
|
state->param_buffers.min_buffers = 2;
|
|
|
|
|
state->param_buffers.max_buffers = MAX_BUFFERS;
|
|
|
|
|
state->param_buffers.align = 16;
|
2016-07-08 20:12:56 +02:00
|
|
|
state->info.features = NULL;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
2016-07-05 17:58:34 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spa_v4l2_close (SpaV4l2Source *this)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
if (!state->opened)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2016-07-13 18:29:55 +02:00
|
|
|
fprintf (stderr, "close\n");
|
2016-07-06 19:43:37 +02:00
|
|
|
if (close(state->fd))
|
|
|
|
|
perror ("close");
|
|
|
|
|
|
|
|
|
|
state->fd = -1;
|
|
|
|
|
state->opened = false;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
mmap_read (SpaV4l2Source *this)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
struct v4l2_buffer buf;
|
|
|
|
|
V4l2Buffer *b;
|
|
|
|
|
|
|
|
|
|
CLEAR(buf);
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
2016-07-08 20:12:56 +02:00
|
|
|
buf.memory = state->memtype;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_DQBUF, &buf) < 0) {
|
|
|
|
|
switch (errno) {
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
return 0;
|
|
|
|
|
case EIO:
|
|
|
|
|
default:
|
|
|
|
|
perror ("VIDIOC_DQBUF");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b = &state->buffers[buf.index];
|
|
|
|
|
b->next = state->ready;
|
|
|
|
|
state->ready = b;
|
|
|
|
|
state->ready_count++;
|
2016-07-05 17:58:34 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-08 12:18:01 +02:00
|
|
|
static int
|
|
|
|
|
v4l2_on_fd_events (SpaPollNotifyData *data)
|
2016-07-07 09:30:18 +02:00
|
|
|
{
|
2016-07-08 12:18:01 +02:00
|
|
|
SpaV4l2Source *this = data->user_data;
|
2016-07-07 09:30:18 +02:00
|
|
|
SpaEvent event;
|
|
|
|
|
|
|
|
|
|
mmap_read (this);
|
|
|
|
|
|
|
|
|
|
event.refcount = 1;
|
|
|
|
|
event.notify = NULL;
|
|
|
|
|
event.type = SPA_EVENT_TYPE_CAN_PULL_OUTPUT;
|
|
|
|
|
event.port_id = 0;
|
|
|
|
|
event.size = 0;
|
|
|
|
|
event.data = NULL;
|
|
|
|
|
this->event_cb (&this->handle, &event, this->user_data);
|
2016-07-08 12:18:01 +02:00
|
|
|
|
|
|
|
|
return 0;
|
2016-07-07 09:30:18 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
static void
|
|
|
|
|
v4l2_buffer_free (void *data)
|
|
|
|
|
{
|
|
|
|
|
V4l2Buffer *b = (V4l2Buffer *) data;
|
|
|
|
|
SpaV4l2Source *this = b->source;
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
b->buffer.refcount = 1;
|
|
|
|
|
b->outstanding = false;
|
|
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
if (xioctl (state->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
|
2016-07-06 19:43:37 +02:00
|
|
|
perror ("VIDIOC_QBUF");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
static int
|
2016-07-11 17:45:54 +02:00
|
|
|
spa_v4l2_import_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffers)
|
2016-07-08 20:12:56 +02:00
|
|
|
{
|
|
|
|
|
SpaV4l2State *state = &this->state[0];
|
|
|
|
|
struct v4l2_requestbuffers reqbuf;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
state->memtype = V4L2_MEMORY_USERPTR;
|
|
|
|
|
|
|
|
|
|
CLEAR(reqbuf);
|
|
|
|
|
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
reqbuf.memory = state->memtype;
|
|
|
|
|
reqbuf.count = n_buffers;
|
|
|
|
|
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
|
|
|
|
|
perror ("VIDIOC_REQBUFS");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
fprintf (stderr, "got %d buffers\n", reqbuf.count);
|
|
|
|
|
if (reqbuf.count < 2) {
|
|
|
|
|
fprintf (stderr, "can't allocate enough buffers\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
state->reqbuf = reqbuf;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < reqbuf.count; i++) {
|
|
|
|
|
V4l2Buffer *b;
|
|
|
|
|
|
|
|
|
|
b = &state->buffers[i];
|
2016-07-11 17:45:54 +02:00
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
b->source = this;
|
|
|
|
|
b->buffer.refcount = 0;
|
|
|
|
|
b->buffer.notify = v4l2_buffer_free;
|
2016-07-11 17:45:54 +02:00
|
|
|
b->buffer.size = buffers[i]->size;
|
|
|
|
|
b->buffer.n_metas = buffers[i]->n_metas;
|
|
|
|
|
b->buffer.metas = buffers[i]->metas;
|
|
|
|
|
b->buffer.n_datas = buffers[i]->n_datas;
|
|
|
|
|
b->buffer.datas = buffers[i]->datas;
|
|
|
|
|
b->imported = buffers[i];
|
2016-07-08 20:12:56 +02:00
|
|
|
b->outstanding = true;
|
|
|
|
|
|
|
|
|
|
CLEAR (b->v4l2_buffer);
|
|
|
|
|
b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
b->v4l2_buffer.memory = state->memtype;
|
|
|
|
|
b->v4l2_buffer.index = i;
|
2016-07-13 20:23:03 +02:00
|
|
|
b->v4l2_buffer.m.userptr = (unsigned long) b->buffer.datas[0].ptr;
|
2016-07-11 17:45:54 +02:00
|
|
|
b->v4l2_buffer.length = b->buffer.datas[0].size;
|
2016-07-08 20:12:56 +02:00
|
|
|
|
|
|
|
|
v4l2_buffer_free (b);
|
|
|
|
|
}
|
|
|
|
|
state->have_buffers = true;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
static int
|
|
|
|
|
mmap_init (SpaV4l2Source *this)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
struct v4l2_requestbuffers reqbuf;
|
|
|
|
|
int i;
|
|
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
state->memtype = V4L2_MEMORY_MMAP;
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
CLEAR(reqbuf);
|
|
|
|
|
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
2016-07-08 20:12:56 +02:00
|
|
|
reqbuf.memory = state->memtype;
|
2016-07-06 19:43:37 +02:00
|
|
|
reqbuf.count = MAX_BUFFERS;
|
|
|
|
|
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
|
|
|
|
|
perror ("VIDIOC_REQBUFS");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf (stderr, "got %d buffers\n", reqbuf.count);
|
|
|
|
|
if (reqbuf.count < 2) {
|
|
|
|
|
fprintf (stderr, "can't allocate enough buffers\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2016-07-15 13:20:20 +02:00
|
|
|
if (state->export_buf)
|
|
|
|
|
fprintf (stderr, "using EXPBUF\n");
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
state->reqbuf = reqbuf;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < reqbuf.count; i++) {
|
|
|
|
|
struct v4l2_buffer buf;
|
|
|
|
|
V4l2Buffer *b;
|
|
|
|
|
|
|
|
|
|
CLEAR (buf);
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
2016-07-08 20:12:56 +02:00
|
|
|
buf.memory = state->memtype;
|
2016-07-06 19:43:37 +02:00
|
|
|
buf.index = i;
|
|
|
|
|
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_QUERYBUF, &buf) < 0) {
|
|
|
|
|
perror ("VIDIOC_QUERYBUF");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b = &state->buffers[i];
|
|
|
|
|
b->source = this;
|
2016-07-08 20:12:56 +02:00
|
|
|
b->buffer.refcount = 0;
|
2016-07-06 19:43:37 +02:00
|
|
|
b->buffer.notify = v4l2_buffer_free;
|
|
|
|
|
b->buffer.size = buf.length;
|
|
|
|
|
b->buffer.n_metas = 1;
|
2016-07-08 20:12:56 +02:00
|
|
|
b->buffer.metas = b->metas;
|
2016-07-06 19:43:37 +02:00
|
|
|
b->buffer.n_datas = 1;
|
2016-07-08 20:12:56 +02:00
|
|
|
b->buffer.datas = b->datas;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
b->header.flags = 0;
|
|
|
|
|
b->header.seq = 0;
|
|
|
|
|
b->header.pts = 0;
|
|
|
|
|
b->header.dts_offset = 0;
|
|
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
b->metas[0].type = SPA_META_TYPE_HEADER;
|
|
|
|
|
b->metas[0].data = &b->header;
|
|
|
|
|
b->metas[0].size = sizeof (b->header);
|
|
|
|
|
|
2016-07-13 19:56:39 +02:00
|
|
|
if (state->export_buf) {
|
|
|
|
|
struct v4l2_exportbuffer expbuf;
|
|
|
|
|
|
|
|
|
|
CLEAR (expbuf);
|
|
|
|
|
expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
expbuf.index = i;
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
|
|
|
|
|
perror("VIDIOC_EXPBUF");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b->dmafd = expbuf.fd;
|
|
|
|
|
b->datas[0].type = SPA_DATA_TYPE_FD;
|
2016-07-13 20:23:03 +02:00
|
|
|
b->datas[0].ptr = &b->dmafd;
|
|
|
|
|
b->datas[0].ptr_type = "dmabuf";
|
2016-07-13 19:56:39 +02:00
|
|
|
b->datas[0].offset = 0;
|
|
|
|
|
b->datas[0].size = buf.length;
|
|
|
|
|
b->datas[0].stride = state->fmt.fmt.pix.bytesperline;
|
|
|
|
|
} else {
|
|
|
|
|
b->datas[0].type = SPA_DATA_TYPE_MEMPTR;
|
2016-07-13 20:23:03 +02:00
|
|
|
b->datas[0].ptr_type = "sysmem";
|
|
|
|
|
b->datas[0].ptr = mmap (NULL,
|
|
|
|
|
buf.length,
|
|
|
|
|
PROT_READ | PROT_WRITE,
|
|
|
|
|
MAP_SHARED,
|
|
|
|
|
state->fd,
|
|
|
|
|
buf.m.offset);
|
2016-07-13 19:56:39 +02:00
|
|
|
b->datas[0].offset = 0;
|
|
|
|
|
b->datas[0].size = buf.length;
|
|
|
|
|
b->datas[0].stride = state->fmt.fmt.pix.bytesperline;
|
2016-07-13 20:23:03 +02:00
|
|
|
if (b->datas[0].ptr == MAP_FAILED) {
|
2016-07-13 19:56:39 +02:00
|
|
|
perror ("mmap");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2016-07-06 19:43:37 +02:00
|
|
|
}
|
2016-07-13 19:56:39 +02:00
|
|
|
b->outstanding = true;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
CLEAR (b->v4l2_buffer);
|
|
|
|
|
b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
b->v4l2_buffer.memory = state->memtype;
|
|
|
|
|
b->v4l2_buffer.index = i;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
v4l2_buffer_free (b);
|
2016-07-06 19:43:37 +02:00
|
|
|
}
|
2016-07-08 20:12:56 +02:00
|
|
|
state->have_buffers = true;
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
userptr_init (SpaV4l2Source *this)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
read_init (SpaV4l2Source *this)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2016-07-05 17:58:34 +02:00
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spa_v4l2_start (SpaV4l2Source *this)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
enum v4l2_buf_type type;
|
2016-07-07 09:30:18 +02:00
|
|
|
SpaEvent event;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
if (spa_v4l2_open (this) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2016-07-08 20:12:56 +02:00
|
|
|
if (!state->have_buffers) {
|
|
|
|
|
if (state->cap.capabilities & V4L2_CAP_STREAMING) {
|
|
|
|
|
if (mmap_init (this) < 0)
|
|
|
|
|
if (userptr_init (this) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (state->cap.capabilities & V4L2_CAP_READWRITE) {
|
|
|
|
|
if (read_init (this) < 0)
|
2016-07-06 19:43:37 +02:00
|
|
|
return -1;
|
2016-07-08 20:12:56 +02:00
|
|
|
} else
|
2016-07-06 19:43:37 +02:00
|
|
|
return -1;
|
2016-07-08 20:12:56 +02:00
|
|
|
}
|
2016-07-06 19:43:37 +02:00
|
|
|
|
2016-07-07 09:30:18 +02:00
|
|
|
event.refcount = 1;
|
|
|
|
|
event.notify = NULL;
|
|
|
|
|
event.type = SPA_EVENT_TYPE_ADD_POLL;
|
|
|
|
|
event.port_id = 0;
|
2016-07-07 16:59:23 +02:00
|
|
|
event.data = &state->poll;
|
|
|
|
|
event.size = sizeof (state->poll);
|
|
|
|
|
|
2016-07-08 12:18:01 +02:00
|
|
|
state->fds[0].fd = state->fd;
|
|
|
|
|
state->fds[0].events = POLLIN | POLLPRI | POLLERR;
|
|
|
|
|
state->fds[0].revents = 0;
|
|
|
|
|
|
|
|
|
|
state->poll.fds = state->fds;
|
|
|
|
|
state->poll.n_fds = 1;
|
|
|
|
|
state->poll.idle_cb = NULL;
|
|
|
|
|
state->poll.before_cb = NULL;
|
|
|
|
|
state->poll.after_cb = v4l2_on_fd_events;
|
2016-07-07 16:59:23 +02:00
|
|
|
state->poll.user_data = this;
|
2016-07-07 09:30:18 +02:00
|
|
|
this->event_cb (&this->handle, &event, this->user_data);
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_STREAMON, &type) < 0) {
|
|
|
|
|
perror ("VIDIOC_STREAMON");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2016-07-07 16:59:23 +02:00
|
|
|
return 0;
|
2016-07-05 17:58:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
spa_v4l2_stop (SpaV4l2Source *this)
|
|
|
|
|
{
|
2016-07-08 20:12:56 +02:00
|
|
|
SpaV4l2State *state = &this->state[0];
|
2016-07-06 19:43:37 +02:00
|
|
|
enum v4l2_buf_type type;
|
2016-07-07 16:59:23 +02:00
|
|
|
SpaEvent event;
|
2016-07-13 19:56:39 +02:00
|
|
|
int i;
|
2016-07-06 19:43:37 +02:00
|
|
|
|
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
|
if (xioctl (state->fd, VIDIOC_STREAMOFF, &type) < 0) {
|
|
|
|
|
perror ("VIDIOC_STREAMOFF");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 19:56:39 +02:00
|
|
|
for (i = 0; i < state->reqbuf.count; i++) {
|
|
|
|
|
V4l2Buffer *b;
|
|
|
|
|
|
|
|
|
|
b = &state->buffers[i];
|
|
|
|
|
if (b->outstanding) {
|
|
|
|
|
fprintf (stderr, "queueing outstanding buffer %p\n", b);
|
|
|
|
|
v4l2_buffer_free (b);
|
|
|
|
|
}
|
|
|
|
|
if (state->export_buf) {
|
|
|
|
|
close (b->dmafd);
|
|
|
|
|
} else {
|
2016-07-13 20:23:03 +02:00
|
|
|
munmap (b->datas[0].ptr, b->datas[0].size);
|
2016-07-13 19:56:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
state->have_buffers = false;
|
|
|
|
|
|
2016-07-07 16:59:23 +02:00
|
|
|
event.refcount = 1;
|
|
|
|
|
event.notify = NULL;
|
|
|
|
|
event.type = SPA_EVENT_TYPE_REMOVE_POLL;
|
|
|
|
|
event.port_id = 0;
|
|
|
|
|
event.data = &state->poll;
|
|
|
|
|
event.size = sizeof (state->poll);
|
|
|
|
|
this->event_cb (&this->handle, &event, this->user_data);
|
|
|
|
|
|
2016-07-06 19:43:37 +02:00
|
|
|
spa_v4l2_close (this);
|
2016-07-07 16:59:23 +02:00
|
|
|
|
2016-07-05 17:58:34 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|