mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
Remove _remove from properties, we can do the same with set of a NULL value. Add signals to the stream API to manage the buffers. Wrap those buffers in a GstBuffer in the pinossrc and pinossink elements and pool them in a bufferpool. Remove SPA_EVENT_TYPE_PULL_INPUT, we can do the same with NEED_INPUT and by using a ringbuffer. Do more complete allocation of buffers in the link. Use the buffer allocator if none of the nodes can allocate. Follow the node state to trigger negotiation and allocation. Remove offset and size when refering to buffers, we want to always deal with the complete buffer and use a ringbuffer for ranges or change the offset/size in the buffer data when needed. Serialize port_info structures as part of the port_update Print both the enum number and the name when debuging properties or formats.
854 lines
28 KiB
C
854 lines
28 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <poll.h>
|
|
|
|
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
|
|
|
static int
|
|
xioctl (int fd, int request, void *arg)
|
|
{
|
|
int err;
|
|
|
|
do {
|
|
err = ioctl (fd, request, arg);
|
|
} while (err == -1 && errno == EINTR);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
spa_v4l2_open (SpaV4l2Source *this)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
struct stat st;
|
|
SpaV4l2SourceProps *props = &this->props[1];
|
|
|
|
if (state->opened)
|
|
return 0;
|
|
|
|
fprintf (stderr, "Playback device is '%s'\n", props->device);
|
|
|
|
if (stat (props->device, &st) < 0) {
|
|
fprintf(stderr, "Cannot identify '%s': %d, %s\n",
|
|
props->device, errno, strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
if (!S_ISCHR (st.st_mode)) {
|
|
fprintf(stderr, "%s is no device\n", props->device);
|
|
return -1;
|
|
}
|
|
|
|
state->fd = open (props->device, O_RDWR | O_NONBLOCK, 0);
|
|
|
|
if (state->fd == -1) {
|
|
fprintf (stderr, "Cannot open '%s': %d, %s\n",
|
|
props->device, errno, strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
if (xioctl (state->fd, VIDIOC_QUERYCAP, &state->cap) < 0) {
|
|
perror ("QUERYCAP");
|
|
return -1;
|
|
}
|
|
|
|
if ((state->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
|
|
fprintf (stderr, "%s is no video capture device\n", props->device);
|
|
return -1;
|
|
}
|
|
state->opened = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
spa_v4l2_close (SpaV4l2Source *this)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
|
|
if (!state->opened)
|
|
return 0;
|
|
|
|
fprintf (stderr, "close\n");
|
|
if (close(state->fd))
|
|
perror ("close");
|
|
|
|
state->fd = -1;
|
|
state->opened = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef struct {
|
|
uint32_t fourcc;
|
|
SpaVideoFormat format;
|
|
SpaMediaType media_type;
|
|
SpaMediaSubType media_subtype;
|
|
} FormatInfo;
|
|
|
|
static const FormatInfo format_info[] =
|
|
{
|
|
/* RGB formats */
|
|
{ V4L2_PIX_FMT_RGB332, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_ARGB555, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_XRGB555, SPA_VIDEO_FORMAT_RGB15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_ARGB555X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_XRGB555X, SPA_VIDEO_FORMAT_BGR15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_RGB565, SPA_VIDEO_FORMAT_RGB16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_RGB565X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_BGR666, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_BGR24, SPA_VIDEO_FORMAT_BGR, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_RGB24, SPA_VIDEO_FORMAT_RGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_ABGR32, SPA_VIDEO_FORMAT_BGRA, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_XBGR32, SPA_VIDEO_FORMAT_BGRx, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_ARGB32, SPA_VIDEO_FORMAT_ARGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_XRGB32, SPA_VIDEO_FORMAT_xRGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* Deprecated Packed RGB Image Formats (alpha ambiguity) */
|
|
{ V4L2_PIX_FMT_RGB444, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_RGB555, SPA_VIDEO_FORMAT_RGB15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_RGB555X, SPA_VIDEO_FORMAT_BGR15, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_BGR32, SPA_VIDEO_FORMAT_BGRx, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_RGB32, SPA_VIDEO_FORMAT_xRGB, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* Grey formats */
|
|
{ V4L2_PIX_FMT_GREY, SPA_VIDEO_FORMAT_GRAY8, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y4, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y6, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y10, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y12, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y16, SPA_VIDEO_FORMAT_GRAY16_LE, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y16_BE, SPA_VIDEO_FORMAT_GRAY16_BE, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y10BPACK, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* Palette formats */
|
|
{ V4L2_PIX_FMT_PAL8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* Chrominance formats */
|
|
{ V4L2_PIX_FMT_UV8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* Luminance+Chrominance formats */
|
|
{ V4L2_PIX_FMT_YVU410, SPA_VIDEO_FORMAT_YVU9, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YVU420, SPA_VIDEO_FORMAT_YV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YVU420M, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUYV, SPA_VIDEO_FORMAT_YUY2, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YYUV, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YVYU, SPA_VIDEO_FORMAT_YVYU, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_UYVY, SPA_VIDEO_FORMAT_UYVY, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_VYUY, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV422P, SPA_VIDEO_FORMAT_Y42B, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV411P, SPA_VIDEO_FORMAT_Y41B, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_Y41P, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV444, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV555, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV565, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV32, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV410, SPA_VIDEO_FORMAT_YUV9, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV420, SPA_VIDEO_FORMAT_I420, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_YUV420M, SPA_VIDEO_FORMAT_I420, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_HI240, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_HM12, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_M420, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* two planes -- one Y, one Cr + Cb interleaved */
|
|
{ V4L2_PIX_FMT_NV12, SPA_VIDEO_FORMAT_NV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV12M, SPA_VIDEO_FORMAT_NV12, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV12MT, SPA_VIDEO_FORMAT_NV12_64Z32, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV12MT_16X16, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV21, SPA_VIDEO_FORMAT_NV21, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV21M, SPA_VIDEO_FORMAT_NV21, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV16, SPA_VIDEO_FORMAT_NV16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV16M, SPA_VIDEO_FORMAT_NV16, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV61, SPA_VIDEO_FORMAT_NV61, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV61M, SPA_VIDEO_FORMAT_NV61, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV24, SPA_VIDEO_FORMAT_NV24, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{ V4L2_PIX_FMT_NV42, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
|
|
/* Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm */
|
|
{ V4L2_PIX_FMT_SBGGR8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER },
|
|
{ V4L2_PIX_FMT_SGBRG8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER },
|
|
{ V4L2_PIX_FMT_SGRBG8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER },
|
|
{ V4L2_PIX_FMT_SRGGB8, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_BAYER },
|
|
|
|
/* compressed formats */
|
|
{V4L2_PIX_FMT_MJPEG, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MJPG },
|
|
{V4L2_PIX_FMT_JPEG, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_IMAGE, SPA_MEDIA_SUBTYPE_JPEG },
|
|
{V4L2_PIX_FMT_PJPG, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{V4L2_PIX_FMT_DV, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_DV },
|
|
{V4L2_PIX_FMT_MPEG, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEGTS },
|
|
{V4L2_PIX_FMT_H264, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H264 },
|
|
{V4L2_PIX_FMT_H264_NO_SC, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H264 },
|
|
{V4L2_PIX_FMT_H264_MVC, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H264 },
|
|
{V4L2_PIX_FMT_H263, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_H263 },
|
|
{V4L2_PIX_FMT_MPEG1, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEG1 },
|
|
{V4L2_PIX_FMT_MPEG2, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEG2 },
|
|
{V4L2_PIX_FMT_MPEG4, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_MPEG4 },
|
|
{V4L2_PIX_FMT_XVID, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_XVID },
|
|
{V4L2_PIX_FMT_VC1_ANNEX_G, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_VC1 },
|
|
{V4L2_PIX_FMT_VC1_ANNEX_L, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_VC1 },
|
|
{V4L2_PIX_FMT_VP8, SPA_VIDEO_FORMAT_ENCODED, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_VP8 },
|
|
|
|
/* Vendor-specific formats */
|
|
{V4L2_PIX_FMT_WNVA, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{V4L2_PIX_FMT_SN9C10X, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{V4L2_PIX_FMT_PWC1, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
{V4L2_PIX_FMT_PWC2, SPA_VIDEO_FORMAT_UNKNOWN, SPA_MEDIA_TYPE_VIDEO, SPA_MEDIA_SUBTYPE_RAW },
|
|
};
|
|
|
|
static const FormatInfo *
|
|
fourcc_to_format_info (uint32_t fourcc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) {
|
|
if (format_info[i].fourcc == fourcc)
|
|
return &format_info[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#if 0
|
|
static const FormatInfo *
|
|
video_format_to_format_info (SpaVideoFormat format)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) {
|
|
if (format_info[i].format == format)
|
|
return &format_info[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#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, i, pi;
|
|
V4l2Format *fmt;
|
|
const FormatInfo *info;
|
|
|
|
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);
|
|
*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 (!(info = fourcc_to_format_info (state->fmtdesc.pixelformat))) {
|
|
state->fmtdesc.index++;
|
|
state->next_fmtdesc = true;
|
|
goto again;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
fmt = &state->format[0];
|
|
fmt->fmt.media_type = info->media_type;
|
|
fmt->fmt.media_subtype = info->media_subtype;
|
|
fmt->fmt.props.prop_info = fmt->infos;
|
|
fmt->fmt.props.n_prop_info = pi = 0;
|
|
fmt->fmt.props.unset_mask = 0;
|
|
|
|
if (info->media_subtype == SPA_MEDIA_SUBTYPE_RAW) {
|
|
spa_video_raw_fill_prop_info (&fmt->infos[pi],
|
|
SPA_PROP_ID_VIDEO_FORMAT,
|
|
offsetof (V4l2Format, format));
|
|
fmt->format = info->format;
|
|
pi = ++fmt->fmt.props.n_prop_info;
|
|
}
|
|
|
|
spa_video_raw_fill_prop_info (&fmt->infos[pi],
|
|
SPA_PROP_ID_VIDEO_SIZE,
|
|
offsetof (V4l2Format, size));
|
|
fmt->size.width = state->frmsize.discrete.width;
|
|
fmt->size.height = state->frmsize.discrete.height;
|
|
pi = ++fmt->fmt.props.n_prop_info;
|
|
|
|
spa_video_raw_fill_prop_info (&fmt->infos[pi],
|
|
SPA_PROP_ID_VIDEO_FRAMERATE,
|
|
offsetof (V4l2Format, framerate));
|
|
fmt->infos[pi].range_type = SPA_PROP_RANGE_TYPE_ENUM;
|
|
fmt->infos[pi].range_values = fmt->ranges;
|
|
fmt->infos[pi].n_range_values = 0;
|
|
i = state->frmival.index = 0;
|
|
|
|
while (true) {
|
|
if ((res = xioctl (state->fd, VIDIOC_ENUM_FRAMEINTERVALS, &state->frmival)) < 0) {
|
|
if (errno == EINVAL) {
|
|
state->frmsize.index++;
|
|
state->next_frmsize = true;
|
|
break;
|
|
}
|
|
perror ("VIDIOC_ENUM_FRAMEINTERVALS");
|
|
return SPA_RESULT_ENUM_END;
|
|
}
|
|
|
|
fmt->ranges[i].name = NULL;
|
|
fmt->ranges[i].description = NULL;
|
|
fmt->ranges[i].size = sizeof (SpaFraction);
|
|
fmt->framerates[i].num = state->frmival.discrete.numerator;
|
|
fmt->framerates[i].denom = state->frmival.discrete.denominator;
|
|
fmt->ranges[i].value = &fmt->framerates[i];
|
|
|
|
i = ++state->frmival.index;
|
|
}
|
|
fmt->infos[pi].n_range_values = i;
|
|
if (i == 1) {
|
|
fmt->framerate = fmt->framerates[0];
|
|
} else {
|
|
fmt->fmt.props.unset_mask |= 1 << pi;
|
|
}
|
|
pi = ++fmt->fmt.props.n_prop_info;
|
|
|
|
*format = &state->format[0].fmt;
|
|
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
static int
|
|
spa_v4l2_set_format (SpaV4l2Source *this, V4l2Format *f, bool try_only)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
|
|
struct v4l2_format reqfmt, fmt;
|
|
struct v4l2_streamparm streamparm;
|
|
const FormatInfo *info = NULL;
|
|
int i;
|
|
|
|
CLEAR (fmt);
|
|
CLEAR (streamparm);
|
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
for (i = 0; i < SPA_N_ELEMENTS (format_info); i++) {
|
|
if (format_info[i].media_type == f->fmt.media_type &&
|
|
format_info[i].media_subtype == f->fmt.media_subtype &&
|
|
format_info[i].format == f->format) {
|
|
info = &format_info[i];
|
|
break;
|
|
}
|
|
}
|
|
if (info == NULL)
|
|
return -1;
|
|
|
|
fmt.fmt.pix.pixelformat = info->fourcc;
|
|
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
|
fmt.fmt.pix.width = f->size.width;
|
|
fmt.fmt.pix.height = f->size.height;
|
|
streamparm.parm.capture.timeperframe.numerator = f->framerate.denom;
|
|
streamparm.parm.capture.timeperframe.denominator = f->framerate.num;
|
|
|
|
fprintf (stderr, "set %08x %dx%d %d/%d\n", fmt.fmt.pix.pixelformat,
|
|
fmt.fmt.pix.width, fmt.fmt.pix.height,
|
|
streamparm.parm.capture.timeperframe.denominator,
|
|
streamparm.parm.capture.timeperframe.numerator);
|
|
|
|
reqfmt = fmt;
|
|
|
|
if (spa_v4l2_open (this) < 0)
|
|
return -1;
|
|
|
|
if (xioctl (state->fd, cmd, &fmt) < 0) {
|
|
perror ("VIDIOC_S_FMT");
|
|
return -1;
|
|
}
|
|
|
|
/* some cheap USB cam's won't accept any change */
|
|
if (xioctl (state->fd, VIDIOC_S_PARM, &streamparm) < 0)
|
|
perror ("VIDIOC_S_PARM");
|
|
|
|
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;
|
|
state->info.flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS |
|
|
SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS;
|
|
state->info.maxbuffering = -1;
|
|
state->info.latency = -1;
|
|
|
|
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->param_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;
|
|
state->info.features = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SpaResult
|
|
mmap_read (SpaV4l2Source *this)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
struct v4l2_buffer buf;
|
|
V4l2Buffer *b;
|
|
|
|
CLEAR(buf);
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = state->memtype;
|
|
|
|
if (xioctl (state->fd, VIDIOC_DQBUF, &buf) < 0) {
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
return 0;
|
|
case EIO:
|
|
default:
|
|
perror ("VIDIOC_DQBUF");
|
|
return SPA_RESULT_ERROR;
|
|
}
|
|
}
|
|
|
|
b = &state->alloc_buffers[buf.index];
|
|
b->header.seq = buf.sequence;
|
|
b->header.pts = (uint64_t)buf.timestamp.tv_sec * 1000000000lu + (uint64_t)buf.timestamp.tv_usec * 1000lu;
|
|
b->next = state->ready;
|
|
state->ready = b;
|
|
state->ready_count++;
|
|
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
static int
|
|
v4l2_on_fd_events (SpaPollNotifyData *data)
|
|
{
|
|
SpaV4l2Source *this = data->user_data;
|
|
SpaEvent event;
|
|
|
|
if (mmap_read (this) < 0)
|
|
return 0;
|
|
|
|
event.type = SPA_EVENT_TYPE_HAVE_OUTPUT;
|
|
event.port_id = 0;
|
|
event.size = 0;
|
|
event.data = NULL;
|
|
this->event_cb (&this->node, &event, this->user_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
spa_v4l2_buffer_recycle (SpaV4l2Source *this, uint32_t buffer_id)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
V4l2Buffer *b = &state->alloc_buffers[buffer_id];
|
|
|
|
if (!b->outstanding)
|
|
return;
|
|
|
|
b->outstanding = false;
|
|
|
|
if (xioctl (state->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
|
|
perror ("VIDIOC_QBUF");
|
|
}
|
|
}
|
|
|
|
static void
|
|
clear_buffers (SpaV4l2Source *this)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
int i;
|
|
|
|
if (!state->have_buffers)
|
|
return;
|
|
|
|
for (i = 0; i < state->reqbuf.count; i++) {
|
|
V4l2Buffer *b;
|
|
SpaMemory *mem;
|
|
|
|
b = &state->alloc_buffers[i];
|
|
if (b->outstanding) {
|
|
fprintf (stderr, "queueing outstanding buffer %p\n", b);
|
|
spa_v4l2_buffer_recycle (this, i);
|
|
}
|
|
mem = spa_memory_find (&b->datas[0].mem.mem);
|
|
if (state->export_buf) {
|
|
close (mem->fd);
|
|
} else {
|
|
munmap (mem->ptr, mem->size);
|
|
}
|
|
spa_memory_unref (&mem->mem);
|
|
}
|
|
if (state->alloc_mem)
|
|
spa_memory_unref (&state->alloc_mem->mem);
|
|
|
|
state->have_buffers = false;
|
|
}
|
|
|
|
static SpaResult
|
|
spa_v4l2_import_buffers (SpaV4l2Source *this, SpaBuffer **buffers, uint32_t n_buffers)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
struct v4l2_requestbuffers reqbuf;
|
|
int i;
|
|
|
|
if (buffers == NULL) {
|
|
clear_buffers (this);
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
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 SPA_RESULT_ERROR;
|
|
}
|
|
fprintf (stderr, "got %d buffers\n", reqbuf.count);
|
|
if (reqbuf.count < 2) {
|
|
fprintf (stderr, "can't allocate enough buffers\n");
|
|
return SPA_RESULT_ERROR;
|
|
}
|
|
state->reqbuf = reqbuf;
|
|
|
|
if (state->alloc_mem)
|
|
spa_memory_unref (&state->alloc_mem->mem);
|
|
state->alloc_mem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED,
|
|
NULL,
|
|
sizeof (V4l2Buffer) * reqbuf.count);
|
|
state->alloc_buffers = spa_memory_ensure_ptr (state->alloc_mem);
|
|
|
|
for (i = 0; i < reqbuf.count; i++) {
|
|
V4l2Buffer *b;
|
|
SpaMemoryRef *mem_ref;
|
|
SpaMemory *mem;
|
|
SpaData *d = SPA_BUFFER_DATAS (buffers[i]);
|
|
|
|
b = &state->alloc_buffers[i];
|
|
b->buffer.mem.mem = state->alloc_mem->mem;
|
|
b->buffer.mem.offset = sizeof (V4l2Buffer) * i;
|
|
b->buffer.mem.size = sizeof (V4l2Buffer);
|
|
b->buffer.id = SPA_ID_INVALID;
|
|
b->outbuf = buffers[i];
|
|
b->outstanding = true;
|
|
|
|
fprintf (stderr, "import buffer %p\n", buffers[i]);
|
|
|
|
mem_ref = &SPA_BUFFER_DATAS (buffers[i])[0].mem.mem;
|
|
if (!(mem = spa_memory_find (mem_ref))) {
|
|
fprintf (stderr, "invalid memory on buffer %p\n", buffers[i]);
|
|
continue;
|
|
}
|
|
|
|
CLEAR (b->v4l2_buffer);
|
|
b->v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
b->v4l2_buffer.memory = state->memtype;
|
|
b->v4l2_buffer.index = i;
|
|
b->v4l2_buffer.m.userptr = (unsigned long) ((uint8_t*)mem->ptr + d[0].mem.offset);
|
|
b->v4l2_buffer.length = d[0].mem.size;
|
|
|
|
spa_v4l2_buffer_recycle (this, buffers[i]->id);
|
|
}
|
|
state->have_buffers = true;
|
|
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
static SpaResult
|
|
mmap_init (SpaV4l2Source *this,
|
|
SpaAllocParam **params,
|
|
unsigned int n_params,
|
|
SpaBuffer **buffers,
|
|
unsigned int *n_buffers)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
struct v4l2_requestbuffers reqbuf;
|
|
int i;
|
|
|
|
state->memtype = V4L2_MEMORY_MMAP;
|
|
|
|
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 SPA_RESULT_ERROR;
|
|
}
|
|
|
|
fprintf (stderr, "got %d buffers\n", reqbuf.count);
|
|
*n_buffers = reqbuf.count;
|
|
|
|
if (reqbuf.count < 2) {
|
|
fprintf (stderr, "can't allocate enough buffers\n");
|
|
return SPA_RESULT_ERROR;
|
|
}
|
|
if (state->export_buf)
|
|
fprintf (stderr, "using EXPBUF\n");
|
|
|
|
state->reqbuf = reqbuf;
|
|
|
|
if (state->alloc_mem)
|
|
spa_memory_unref (&state->alloc_mem->mem);
|
|
state->alloc_mem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED,
|
|
NULL,
|
|
sizeof (V4l2Buffer) * reqbuf.count);
|
|
state->alloc_buffers = spa_memory_ensure_ptr (state->alloc_mem);
|
|
|
|
for (i = 0; i < reqbuf.count; i++) {
|
|
struct v4l2_buffer buf;
|
|
V4l2Buffer *b;
|
|
SpaMemory *mem;
|
|
|
|
CLEAR (buf);
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = state->memtype;
|
|
buf.index = i;
|
|
|
|
if (xioctl (state->fd, VIDIOC_QUERYBUF, &buf) < 0) {
|
|
perror ("VIDIOC_QUERYBUF");
|
|
return SPA_RESULT_ERROR;
|
|
}
|
|
|
|
b = &state->alloc_buffers[i];
|
|
b->buffer.id = i;
|
|
b->buffer.mem.mem = state->alloc_mem->mem;
|
|
b->buffer.mem.offset = sizeof (V4l2Buffer) * i;
|
|
b->buffer.mem.size = sizeof (V4l2Buffer);
|
|
|
|
buffers[i] = &b->buffer;
|
|
|
|
b->buffer.n_metas = 1;
|
|
b->buffer.metas = offsetof (V4l2Buffer, metas);
|
|
b->buffer.n_datas = 1;
|
|
b->buffer.datas = offsetof (V4l2Buffer, datas);
|
|
|
|
b->header.flags = 0;
|
|
b->header.seq = 0;
|
|
b->header.pts = 0;
|
|
b->header.dts_offset = 0;
|
|
|
|
b->metas[0].type = SPA_META_TYPE_HEADER;
|
|
b->metas[0].offset = offsetof (V4l2Buffer, header);
|
|
b->metas[0].size = sizeof (b->header);
|
|
|
|
mem = spa_memory_alloc (SPA_MEMORY_POOL_SHARED);
|
|
mem->flags = SPA_MEMORY_FLAG_READABLE;
|
|
mem->size = buf.length;
|
|
b->datas[0].mem.mem = mem->mem;
|
|
b->datas[0].mem.offset = 0;
|
|
b->datas[0].mem.size = buf.length;
|
|
b->datas[0].stride = state->fmt.fmt.pix.bytesperline;
|
|
|
|
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;
|
|
}
|
|
|
|
mem->fd = expbuf.fd;
|
|
mem->type = "dmabuf";
|
|
mem->ptr = NULL;
|
|
b->dmafd = expbuf.fd;
|
|
} else {
|
|
mem->fd = -1;
|
|
mem->type = "sysmem";
|
|
mem->ptr = mmap (NULL,
|
|
buf.length,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED,
|
|
state->fd,
|
|
buf.m.offset);
|
|
if (mem->ptr == MAP_FAILED) {
|
|
perror ("mmap");
|
|
continue;
|
|
}
|
|
}
|
|
b->outbuf = &b->buffer;
|
|
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;
|
|
|
|
spa_v4l2_buffer_recycle (this, i);
|
|
}
|
|
state->have_buffers = true;
|
|
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
static SpaResult
|
|
userptr_init (SpaV4l2Source *this)
|
|
{
|
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static SpaResult
|
|
read_init (SpaV4l2Source *this)
|
|
{
|
|
return SPA_RESULT_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
static SpaResult
|
|
spa_v4l2_alloc_buffers (SpaV4l2Source *this,
|
|
SpaAllocParam **params,
|
|
unsigned int n_params,
|
|
SpaBuffer **buffers,
|
|
unsigned int *n_buffers)
|
|
{
|
|
SpaResult res;
|
|
SpaV4l2State *state = &this->state[0];
|
|
unsigned int i;
|
|
|
|
if (state->have_buffers) {
|
|
if (*n_buffers < state->reqbuf.count)
|
|
return SPA_RESULT_NO_BUFFERS;
|
|
|
|
*n_buffers = state->reqbuf.count;
|
|
for (i = 0; i < state->reqbuf.count; i++) {
|
|
buffers[i] = &state->alloc_buffers[i].buffer;
|
|
}
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
if (state->cap.capabilities & V4L2_CAP_STREAMING) {
|
|
if ((res = mmap_init (this, params, n_params, buffers, n_buffers)) < 0)
|
|
if ((res = userptr_init (this)) < 0)
|
|
return res;
|
|
} else if (state->cap.capabilities & V4L2_CAP_READWRITE) {
|
|
if ((res = read_init (this)) < 0)
|
|
return res;
|
|
} else
|
|
return SPA_RESULT_ERROR;
|
|
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
static SpaResult
|
|
spa_v4l2_start (SpaV4l2Source *this)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
enum v4l2_buf_type type;
|
|
SpaEvent event;
|
|
|
|
if (spa_v4l2_open (this) < 0)
|
|
return SPA_RESULT_ERROR;
|
|
|
|
if (!state->have_buffers)
|
|
return SPA_RESULT_NO_BUFFERS;
|
|
|
|
event.type = SPA_EVENT_TYPE_ADD_POLL;
|
|
event.port_id = 0;
|
|
event.data = &state->poll;
|
|
event.size = sizeof (state->poll);
|
|
|
|
state->fds[0].fd = state->fd;
|
|
state->fds[0].events = POLLIN | POLLPRI | POLLERR;
|
|
state->fds[0].revents = 0;
|
|
|
|
state->poll.id = 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;
|
|
state->poll.user_data = this;
|
|
this->event_cb (&this->node, &event, this->user_data);
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
if (xioctl (state->fd, VIDIOC_STREAMON, &type) < 0) {
|
|
perror ("VIDIOC_STREAMON");
|
|
return SPA_RESULT_ERROR;
|
|
}
|
|
return SPA_RESULT_OK;
|
|
}
|
|
|
|
static SpaResult
|
|
spa_v4l2_stop (SpaV4l2Source *this)
|
|
{
|
|
SpaV4l2State *state = &this->state[0];
|
|
enum v4l2_buf_type type;
|
|
SpaEvent event;
|
|
|
|
if (!state->opened)
|
|
return SPA_RESULT_OK;
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
if (xioctl (state->fd, VIDIOC_STREAMOFF, &type) < 0) {
|
|
perror ("VIDIOC_STREAMOFF");
|
|
return SPA_RESULT_ERROR;
|
|
}
|
|
|
|
clear_buffers (this);
|
|
|
|
event.type = SPA_EVENT_TYPE_REMOVE_POLL;
|
|
event.port_id = 0;
|
|
event.data = &state->poll;
|
|
event.size = sizeof (state->poll);
|
|
this->event_cb (&this->node, &event, this->user_data);
|
|
|
|
spa_v4l2_close (this);
|
|
|
|
return SPA_RESULT_OK;
|
|
}
|