mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
v4l2: continue work on the capture device
Remove activate and deactivate commands. Add STEP property range type for v4l2 frame sizes later v4l2: implement negotiation and data capture
This commit is contained in:
parent
beedb65f00
commit
a1a27328e2
17 changed files with 976 additions and 308 deletions
|
|
@ -22,8 +22,8 @@
|
|||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <libv4l2.h>
|
||||
#include <pthread.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <spa/node.h>
|
||||
#include <spa/video/format.h>
|
||||
|
|
@ -45,11 +45,7 @@ reset_v4l2_source_props (SpaV4l2SourceProps *props)
|
|||
strncpy (props->device, default_device, 64);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
pthread_t thread;
|
||||
bool running;
|
||||
} SpaV4l2State;
|
||||
#define MAX_BUFFERS 256
|
||||
|
||||
typedef struct _V4l2Buffer V4l2Buffer;
|
||||
|
||||
|
|
@ -59,29 +55,42 @@ struct _V4l2Buffer {
|
|||
SpaMetaHeader header;
|
||||
SpaData data[1];
|
||||
V4l2Buffer *next;
|
||||
uint32_t index;
|
||||
SpaV4l2Source *source;
|
||||
bool outstanding;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool opened;
|
||||
int fd;
|
||||
struct v4l2_capability cap;
|
||||
struct v4l2_format fmt;
|
||||
enum v4l2_buf_type type;
|
||||
struct v4l2_requestbuffers reqbuf;
|
||||
pthread_t thread;
|
||||
bool running;
|
||||
V4l2Buffer buffers[MAX_BUFFERS];
|
||||
V4l2Buffer *ready;
|
||||
uint32_t ready_count;
|
||||
} SpaV4l2State;
|
||||
|
||||
struct _SpaV4l2Source {
|
||||
SpaHandle handle;
|
||||
|
||||
SpaV4l2SourceProps tmp_props;
|
||||
SpaV4l2SourceProps props;
|
||||
|
||||
bool activated;
|
||||
|
||||
SpaEventCallback event_cb;
|
||||
void *user_data;
|
||||
|
||||
bool have_format;
|
||||
SpaVideoRawFormat query_format;
|
||||
SpaVideoRawFormat current_format;
|
||||
SpaVideoRawFormat raw_format[2];
|
||||
SpaFormat *current_format;
|
||||
|
||||
SpaV4l2State state;
|
||||
|
||||
SpaPortInfo info;
|
||||
SpaPortStatus status;
|
||||
|
||||
V4l2Buffer buffer;
|
||||
};
|
||||
|
||||
#include "v4l2-utils.c"
|
||||
|
|
@ -181,48 +190,39 @@ spa_v4l2_source_node_send_command (SpaHandle *handle,
|
|||
case SPA_COMMAND_INVALID:
|
||||
return SPA_RESULT_INVALID_COMMAND;
|
||||
|
||||
case SPA_COMMAND_ACTIVATE:
|
||||
if (!this->activated) {
|
||||
spa_v4l2_open (this);
|
||||
this->activated = true;
|
||||
}
|
||||
if (this->event_cb) {
|
||||
SpaEvent event;
|
||||
|
||||
event.refcount = 1;
|
||||
event.notify = NULL;
|
||||
event.type = SPA_EVENT_TYPE_ACTIVATED;
|
||||
event.port_id = -1;
|
||||
event.data = NULL;
|
||||
event.size = 0;
|
||||
|
||||
this->event_cb (handle, &event, this->user_data);
|
||||
}
|
||||
break;
|
||||
case SPA_COMMAND_DEACTIVATE:
|
||||
if (this->activated) {
|
||||
spa_v4l2_close (this);
|
||||
this->activated = false;
|
||||
}
|
||||
if (this->event_cb) {
|
||||
SpaEvent event;
|
||||
|
||||
event.refcount = 1;
|
||||
event.notify = NULL;
|
||||
event.type = SPA_EVENT_TYPE_DEACTIVATED;
|
||||
event.port_id = -1;
|
||||
event.data = NULL;
|
||||
event.size = 0;
|
||||
|
||||
this->event_cb (handle, &event, this->user_data);
|
||||
}
|
||||
break;
|
||||
case SPA_COMMAND_START:
|
||||
spa_v4l2_start (this);
|
||||
|
||||
if (this->event_cb) {
|
||||
SpaEvent event;
|
||||
|
||||
event.refcount = 1;
|
||||
event.notify = NULL;
|
||||
event.type = SPA_EVENT_TYPE_STARTED;
|
||||
event.port_id = -1;
|
||||
event.data = NULL;
|
||||
event.size = 0;
|
||||
|
||||
this->event_cb (handle, &event, this->user_data);
|
||||
}
|
||||
break;
|
||||
case SPA_COMMAND_STOP:
|
||||
spa_v4l2_stop (this);
|
||||
|
||||
if (this->event_cb) {
|
||||
SpaEvent event;
|
||||
|
||||
event.refcount = 1;
|
||||
event.notify = NULL;
|
||||
event.type = SPA_EVENT_TYPE_STOPPED;
|
||||
event.port_id = -1;
|
||||
event.data = NULL;
|
||||
event.size = 0;
|
||||
|
||||
this->event_cb (handle, &event, this->user_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case SPA_COMMAND_FLUSH:
|
||||
case SPA_COMMAND_DRAIN:
|
||||
case SPA_COMMAND_MARKER:
|
||||
|
|
@ -317,12 +317,12 @@ spa_v4l2_source_node_enum_port_formats (SpaHandle *handle,
|
|||
|
||||
switch (index) {
|
||||
case 0:
|
||||
spa_video_raw_format_init (&this->query_format);
|
||||
spa_video_raw_format_init (&this->raw_format[0]);
|
||||
break;
|
||||
default:
|
||||
return SPA_RESULT_ENUM_END;
|
||||
}
|
||||
*format = &this->query_format.format;
|
||||
*format = &this->raw_format[0].format;
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
|
@ -330,11 +330,13 @@ spa_v4l2_source_node_enum_port_formats (SpaHandle *handle,
|
|||
static SpaResult
|
||||
spa_v4l2_source_node_set_port_format (SpaHandle *handle,
|
||||
uint32_t port_id,
|
||||
int test_only,
|
||||
bool test_only,
|
||||
const SpaFormat *format)
|
||||
{
|
||||
SpaV4l2Source *this = (SpaV4l2Source *) handle;
|
||||
SpaResult res;
|
||||
SpaFormat *f, *tf;
|
||||
size_t fs;
|
||||
|
||||
if (handle == NULL || format == NULL)
|
||||
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||
|
|
@ -343,14 +345,30 @@ spa_v4l2_source_node_set_port_format (SpaHandle *handle,
|
|||
return SPA_RESULT_INVALID_PORT;
|
||||
|
||||
if (format == NULL) {
|
||||
this->have_format = false;
|
||||
this->current_format = NULL;
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
if ((res = spa_video_raw_format_parse (format, &this->current_format)) < 0)
|
||||
return res;
|
||||
if (format->media_type == SPA_MEDIA_TYPE_VIDEO) {
|
||||
if (format->media_subtype == SPA_MEDIA_SUBTYPE_RAW) {
|
||||
if ((res = spa_video_raw_format_parse (format, &this->raw_format[0]) < 0))
|
||||
return res;
|
||||
|
||||
this->have_format = true;
|
||||
f = &this->raw_format[0].format;
|
||||
tf = &this->raw_format[1].format;
|
||||
fs = sizeof (SpaVideoRawFormat);
|
||||
} else
|
||||
return SPA_RESULT_INVALID_MEDIA_TYPE;
|
||||
} else
|
||||
return SPA_RESULT_INVALID_MEDIA_TYPE;
|
||||
|
||||
if (spa_v4l2_set_format (this, f, test_only) < 0)
|
||||
return SPA_RESULT_INVALID_MEDIA_TYPE;
|
||||
|
||||
if (!test_only) {
|
||||
memcpy (tf, f, fs);
|
||||
this->current_format = tf;
|
||||
}
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
|
@ -368,10 +386,10 @@ spa_v4l2_source_node_get_port_format (SpaHandle *handle,
|
|||
if (port_id != 0)
|
||||
return SPA_RESULT_INVALID_PORT;
|
||||
|
||||
if (!this->have_format)
|
||||
if (this->current_format == NULL)
|
||||
return SPA_RESULT_NO_FORMAT;
|
||||
|
||||
*format = &this->current_format.format;
|
||||
*format = this->current_format;
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
|
@ -442,23 +460,41 @@ spa_v4l2_source_node_pull_port_output (SpaHandle *handle,
|
|||
SpaOutputInfo *info)
|
||||
{
|
||||
SpaV4l2Source *this = (SpaV4l2Source *) handle;
|
||||
SpaV4l2State *state;
|
||||
unsigned int i;
|
||||
bool have_error = false;
|
||||
|
||||
if (handle == NULL || n_info == 0 || info == NULL)
|
||||
return SPA_RESULT_INVALID_ARGUMENTS;
|
||||
|
||||
state = &this->state;
|
||||
|
||||
for (i = 0; i < n_info; i++) {
|
||||
V4l2Buffer *b;
|
||||
|
||||
if (info[i].port_id != 0) {
|
||||
info[i].status = SPA_RESULT_INVALID_PORT;
|
||||
have_error = true;
|
||||
continue;
|
||||
}
|
||||
if (!this->have_format) {
|
||||
if (this->current_format == NULL) {
|
||||
info[i].status = SPA_RESULT_NO_FORMAT;
|
||||
have_error = true;
|
||||
continue;
|
||||
}
|
||||
if (state->ready_count == 0) {
|
||||
info[i].status = SPA_RESULT_UNEXPECTED;
|
||||
have_error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
b = state->ready;
|
||||
state->ready = b->next;
|
||||
state->ready_count--;
|
||||
|
||||
b->outstanding = true;
|
||||
|
||||
info[i].buffer = &b->buffer;
|
||||
info[i].status = SPA_RESULT_OK;
|
||||
}
|
||||
if (have_error)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,35 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; }
|
||||
#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;
|
||||
struct stat st;
|
||||
|
||||
if (state->opened)
|
||||
return 0;
|
||||
|
||||
fprintf (stderr, "Playback device is '%s'\n", this->props.device);
|
||||
|
||||
if (stat (this->props.device, &st) < 0) {
|
||||
|
|
@ -28,42 +43,511 @@ spa_v4l2_open (SpaV4l2Source *this)
|
|||
return -1;
|
||||
}
|
||||
|
||||
this->state.fd = open (this->props.device, O_RDWR | O_NONBLOCK, 0);
|
||||
state->fd = open (this->props.device, O_RDWR | O_NONBLOCK, 0);
|
||||
|
||||
if (this->state.fd == -1) {
|
||||
fprintf(stderr, "Cannot open '%s': %d, %s\n",
|
||||
if (state->fd == -1) {
|
||||
fprintf (stderr, "Cannot open '%s': %d, %s\n",
|
||||
this->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", this->props.device);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
spa_v4l2_set_format (SpaV4l2Source *this, SpaFormat *format, bool try_only)
|
||||
{
|
||||
SpaV4l2State *state = &this->state;
|
||||
int cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
|
||||
struct v4l2_format reqfmt, fmt;
|
||||
|
||||
CLEAR (fmt);
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
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;
|
||||
fprintf (stderr, "set %08x %dx%d\n", fmt.fmt.pix.pixelformat,
|
||||
fmt.fmt.pix.width, fmt.fmt.pix.height);
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_v4l2_close (SpaV4l2Source *this)
|
||||
{
|
||||
if (close(this->state.fd))
|
||||
fprintf(stderr, "Cannot close %d, %s\n",
|
||||
errno, strerror (errno));
|
||||
SpaV4l2State *state = &this->state;
|
||||
|
||||
if (!state->opened)
|
||||
return 0;
|
||||
|
||||
if (close(state->fd))
|
||||
perror ("close");
|
||||
|
||||
state->fd = -1;
|
||||
state->opened = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mmap_read (SpaV4l2Source *this)
|
||||
{
|
||||
SpaV4l2State *state = &this->state;
|
||||
struct v4l2_buffer buf;
|
||||
V4l2Buffer *b;
|
||||
|
||||
CLEAR(buf);
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (xioctl (state->fd, VIDIOC_DQBUF, &buf) < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
return 0;
|
||||
case EIO:
|
||||
default:
|
||||
perror ("VIDIOC_DQBUF");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "captured buffer %d\n", buf.index);
|
||||
|
||||
b = &state->buffers[buf.index];
|
||||
b->next = state->ready;
|
||||
state->ready = b;
|
||||
state->ready_count++;
|
||||
|
||||
this->state.fd = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
v4l2_loop (void *user_data)
|
||||
{
|
||||
SpaV4l2Source *this = user_data;
|
||||
SpaV4l2State *state = &this->state;
|
||||
struct pollfd fds[1];
|
||||
|
||||
fds[0].fd = state->fd;
|
||||
fds[0].events = POLLIN | POLLPRI | POLLERR;
|
||||
fds[0].revents = 0;
|
||||
|
||||
while (state->running) {
|
||||
int r;
|
||||
|
||||
r = poll (fds, 1, 2000);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (r == 0) {
|
||||
fprintf (stderr, "select timeout\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmap_read (this) < 0)
|
||||
break;
|
||||
|
||||
{
|
||||
SpaEvent event;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static void
|
||||
v4l2_buffer_free (void *data)
|
||||
{
|
||||
V4l2Buffer *b = (V4l2Buffer *) data;
|
||||
SpaV4l2Source *this = b->source;
|
||||
SpaV4l2State *state = &this->state;
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR (buf);
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = b->index;
|
||||
|
||||
b->buffer.refcount = 1;
|
||||
b->outstanding = false;
|
||||
|
||||
fprintf (stderr, "queue buffer %d\n", buf.index);
|
||||
|
||||
if (xioctl (state->fd, VIDIOC_QBUF, &buf) < 0) {
|
||||
perror ("VIDIOC_QBUF");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mmap_init (SpaV4l2Source *this)
|
||||
{
|
||||
SpaV4l2State *state = &this->state;
|
||||
struct v4l2_requestbuffers reqbuf;
|
||||
int i;
|
||||
|
||||
CLEAR(reqbuf);
|
||||
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
reqbuf.memory = V4L2_MEMORY_MMAP;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = i;
|
||||
|
||||
if (xioctl (state->fd, VIDIOC_QUERYBUF, &buf) < 0) {
|
||||
perror ("VIDIOC_QUERYBUF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
b = &state->buffers[i];
|
||||
b->index = i;
|
||||
b->source = this;
|
||||
b->buffer.refcount = 1;
|
||||
b->buffer.notify = v4l2_buffer_free;
|
||||
b->buffer.size = buf.length;
|
||||
b->buffer.n_metas = 1;
|
||||
b->buffer.metas = b->meta;
|
||||
b->buffer.n_datas = 1;
|
||||
b->buffer.datas = b->data;
|
||||
|
||||
b->header.flags = 0;
|
||||
b->header.seq = 0;
|
||||
b->header.pts = 0;
|
||||
b->header.dts_offset = 0;
|
||||
|
||||
b->meta[0].type = SPA_META_TYPE_HEADER;
|
||||
b->meta[0].data = &b->header;
|
||||
b->meta[0].size = sizeof (b->header);
|
||||
|
||||
b->data[0].type = SPA_DATA_TYPE_MEMPTR;
|
||||
b->data[0].size = buf.length;
|
||||
b->data[0].data = mmap (NULL,
|
||||
buf.length,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
state->fd,
|
||||
buf.m.offset);
|
||||
|
||||
if (b->data[0].data == MAP_FAILED) {
|
||||
perror ("mmap");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < state->reqbuf.count; ++i) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR (buf);
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = i;
|
||||
|
||||
if (xioctl (state->fd, VIDIOC_QBUF, &buf) < 0) {
|
||||
perror ("VIDIOC_QBUF");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
userptr_init (SpaV4l2Source *this)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
read_init (SpaV4l2Source *this)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
spa_v4l2_start (SpaV4l2Source *this)
|
||||
{
|
||||
SpaV4l2State *state = &this->state;
|
||||
int err;
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
if (spa_v4l2_open (this) < 0)
|
||||
return -1;
|
||||
|
||||
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)
|
||||
return -1;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (xioctl (state->fd, VIDIOC_STREAMON, &type) < 0) {
|
||||
perror ("VIDIOC_STREAMON");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->running = true;
|
||||
if ((err = pthread_create (&state->thread, NULL, v4l2_loop, this)) != 0) {
|
||||
printf ("can't create thread: %d", err);
|
||||
printf ("can't create thread: %d %s", err, strerror (err));
|
||||
state->running = false;
|
||||
}
|
||||
return err;
|
||||
|
|
@ -73,10 +557,19 @@ static int
|
|||
spa_v4l2_stop (SpaV4l2Source *this)
|
||||
{
|
||||
SpaV4l2State *state = &this->state;
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
if (state->running) {
|
||||
state->running = false;
|
||||
pthread_join (state->thread, NULL);
|
||||
}
|
||||
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (xioctl (state->fd, VIDIOC_STREAMOFF, &type) < 0) {
|
||||
perror ("VIDIOC_STREAMOFF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
spa_v4l2_close (this);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue