mirror of
https://github.com/gozfree/gear-lib.git
synced 2026-02-06 11:13:01 +08:00
[libuvc] add dummy support for fake file
This commit is contained in:
@@ -30,7 +30,7 @@ TGT_LIB_SO = $(LIBNAME).so
|
||||
TGT_LIB_SO_VER = $(TGT_LIB_SO).${VER}
|
||||
TGT_UNIT_TEST = test_$(LIBNAME)
|
||||
|
||||
OBJS_LIB = $(LIBNAME).o v4l2.o
|
||||
OBJS_LIB = $(LIBNAME).o v4l2.o dummy.o
|
||||
OBJS_UNIT_TEST = test_$(LIBNAME).o
|
||||
|
||||
###############################################################################
|
||||
|
||||
308
gear-lib/libuvc/dummy.c
Normal file
308
gear-lib/libuvc/dummy.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014-2020 Zhifeng Gong <gozfree@163.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
******************************************************************************/
|
||||
#include "libuvc.h"
|
||||
#include <libthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
struct dummy_ctx {
|
||||
int fd;
|
||||
int rd_fd;
|
||||
int wr_fd;
|
||||
uint64_t seek_offset;
|
||||
uint64_t first_ts;
|
||||
uint64_t frame_id;
|
||||
struct uvc_ctx *parent;
|
||||
struct thread *thread;
|
||||
bool is_streaming;
|
||||
int epfd;
|
||||
struct epoll_event events;
|
||||
};
|
||||
|
||||
#define timespec2ns(ts) \
|
||||
(((uint64_t)ts.tv_sec * 1000000000) + ((uint64_t)ts.tv_nsec))
|
||||
|
||||
static void *uvc_dummy_open(struct uvc_ctx *uvc, const char *dev, struct uvc_config *conf)
|
||||
{
|
||||
int fds[2];
|
||||
struct dummy_ctx *c = calloc(1, sizeof(struct dummy_ctx));
|
||||
if (!c) {
|
||||
printf("malloc dummy_ctx failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c->fd = open(dev, O_RDWR);
|
||||
if (c->fd == -1) {
|
||||
printf("open %s failed: %d\n", dev, errno);
|
||||
goto failed;
|
||||
}
|
||||
c->frame_id = 0;
|
||||
c->seek_offset = 0;
|
||||
|
||||
if (pipe(fds)) {
|
||||
printf("create pipe failed(%d): %s\n", errno, strerror(errno));
|
||||
goto failed;
|
||||
}
|
||||
c->rd_fd = fds[0];
|
||||
c->wr_fd = fds[1];
|
||||
|
||||
memcpy(&uvc->conf, conf, sizeof(struct uvc_config));
|
||||
c->parent = uvc;
|
||||
|
||||
return c;
|
||||
|
||||
failed:
|
||||
if (c->fd != -1) {
|
||||
close(c->fd);
|
||||
}
|
||||
if (c) {
|
||||
free(c);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int msleep(uint64_t ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = ms*1000;
|
||||
return select(0, NULL, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
static int uvc_dummy_enqueue(struct uvc_ctx *uvc, void *buf, size_t len)
|
||||
{
|
||||
char notify = '1';
|
||||
uint64_t delay_ms = 0;
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
|
||||
delay_ms = (1000)/(uvc->conf.fps.num/(uvc->conf.fps.den*1.0));
|
||||
msleep(delay_ms);
|
||||
if (write(c->wr_fd, ¬ify, 1) != 1) {
|
||||
printf("Failed writing to notify pipe\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvc_dummy_dequeue(struct uvc_ctx *uvc, struct video_frame *frame)
|
||||
{
|
||||
int i;
|
||||
ssize_t ret;
|
||||
size_t len;
|
||||
char notify;
|
||||
struct timespec ts;
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
|
||||
if (read(c->rd_fd, ¬ify, sizeof(notify)) != 1) {
|
||||
printf("Failed read from notify pipe\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < frame->planes; ++i) {
|
||||
len = frame->linesize[i]*frame->height;
|
||||
ret = read(c->fd, frame->data[i], len);
|
||||
if (ret != len) {
|
||||
printf("read failed: ret=%lu, len=%zu, errno=%d, ptr=%p\n", ret, len, errno, frame->data[i]);
|
||||
return -1;
|
||||
}
|
||||
c->seek_offset += len;
|
||||
lseek(c->fd, c->seek_offset, SEEK_SET);
|
||||
}
|
||||
if (-1 == clock_gettime(CLOCK_REALTIME, &ts)) {
|
||||
printf("clock_gettime failed %d:%s\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
frame->timestamp = timespec2ns(ts);
|
||||
if (c->frame_id == 0) {
|
||||
c->first_ts = frame->timestamp;
|
||||
}
|
||||
frame->timestamp -= c->first_ts;
|
||||
frame->frame_id = c->frame_id;
|
||||
c->frame_id++;
|
||||
|
||||
return frame->total_size;
|
||||
}
|
||||
|
||||
static int uvc_dummy_poll_init(struct dummy_ctx *c)
|
||||
{
|
||||
struct epoll_event epev;
|
||||
memset(&epev, 0, sizeof(epev));
|
||||
printf("dummy_ctx=%p\n", c);
|
||||
c->epfd = epoll_create(1);
|
||||
if (c->epfd == -1) {
|
||||
printf("epoll_create failed %d!\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
epev.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET | EPOLLERR;
|
||||
epev.data.ptr = (void *)c;
|
||||
if (-1 == epoll_ctl(c->epfd, EPOLL_CTL_ADD, c->rd_fd, &epev)) {
|
||||
printf("epoll_ctl EPOLL_CTL_ADD failed %d!\n", errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvc_dummy_poll(struct uvc_ctx *uvc, int timeout)
|
||||
{
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
int ret = 0;
|
||||
|
||||
int n = epoll_wait(c->epfd, &c->events, 1, timeout);
|
||||
switch (n) {
|
||||
case 0:
|
||||
printf("poll timeout\n");
|
||||
ret = -1;
|
||||
break;
|
||||
case -1:
|
||||
printf("poll error %d\n", errno);
|
||||
ret = -1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void uvc_dummy_poll_deinit(struct dummy_ctx *c)
|
||||
{
|
||||
if (-1 == epoll_ctl(c->epfd, EPOLL_CTL_DEL, c->rd_fd, NULL)) {
|
||||
printf("epoll_ctl EPOLL_CTL_DEL failed %d!\n", errno);
|
||||
}
|
||||
close(c->epfd);
|
||||
}
|
||||
|
||||
static void *dummy_thread(struct thread *t, void *arg)
|
||||
{
|
||||
struct uvc_ctx *uvc = arg;
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
struct video_frame frame;
|
||||
|
||||
if (uvc_dummy_poll_init(c) == -1) {
|
||||
printf("uvc_dummy_poll_init failed!\n");
|
||||
}
|
||||
video_frame_init(&frame, uvc->conf.format, uvc->conf.width, uvc->conf.height, VFC_ALLOC);
|
||||
c->is_streaming = true;
|
||||
while (c->is_streaming) {
|
||||
if (uvc_dummy_enqueue(uvc, NULL, 0) != 0) {
|
||||
printf("uvc_dummy_enqueue failed\n");
|
||||
continue;
|
||||
}
|
||||
if (uvc_dummy_poll(uvc, -1) != 0) {
|
||||
printf("uvc_dummy_poll failed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uvc_dummy_dequeue(uvc, &frame) == -1) {
|
||||
printf("uvc_dummy_dequeue failed\n");
|
||||
}
|
||||
uvc->on_video_frame(uvc, &frame);
|
||||
}
|
||||
uvc_dummy_poll_deinit(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int uvc_dummy_start_stream(struct uvc_ctx *uvc)
|
||||
{
|
||||
char notify = '1';
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
|
||||
if (c->is_streaming) {
|
||||
printf("dummy is streaming already!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(c->wr_fd, ¬ify, 1) != 1) {
|
||||
printf("Failed writing to notify pipe\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uvc->on_video_frame) {
|
||||
c->thread = thread_create(dummy_thread, uvc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvc_dummy_stop_stream(struct uvc_ctx *uvc)
|
||||
{
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
char notify = '1';
|
||||
|
||||
if (!c->is_streaming) {
|
||||
printf("dummy stream stopped already!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(c->wr_fd, ¬ify, 1) != 1) {
|
||||
printf("Failed writing to notify pipe\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uvc->on_video_frame) {
|
||||
c->is_streaming = false;
|
||||
thread_destroy(c->thread);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uvc_dummy_query_frame(struct uvc_ctx *uvc, struct video_frame *frame)
|
||||
{
|
||||
if (uvc_dummy_enqueue(uvc, NULL, 0) != 0) {
|
||||
printf("uvc_dummy_enqueue failed\n");
|
||||
return -1;
|
||||
}
|
||||
if (uvc_dummy_dequeue(uvc, frame) == -1) {
|
||||
printf("uvc_dummy_dequeue failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uvc_dummy_close(struct uvc_ctx *uvc)
|
||||
{
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
uvc_dummy_stop_stream(uvc);
|
||||
close(c->fd);
|
||||
close(c->epfd);
|
||||
close(c->rd_fd);
|
||||
close(c->wr_fd);
|
||||
free(c);
|
||||
}
|
||||
|
||||
struct uvc_ops dummy_ops = {
|
||||
.open = uvc_dummy_open,
|
||||
.close = uvc_dummy_close,
|
||||
.ioctl = NULL,
|
||||
.start_stream = uvc_dummy_start_stream,
|
||||
.stop_stream = uvc_dummy_stop_stream,
|
||||
.query_frame = uvc_dummy_query_frame,
|
||||
};
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
extern struct uvc_ops dummy_ops;
|
||||
#if defined (OS_LINUX)
|
||||
extern struct uvc_ops v4l2_ops;
|
||||
#elif defined (OS_WINDOWS)
|
||||
@@ -35,15 +36,16 @@ extern struct uvc_ops dshow_ops;
|
||||
#endif
|
||||
|
||||
static struct uvc_ops *uvc_ops[] = {
|
||||
[UVC_TYPE_DUMMY] = &dummy_ops,
|
||||
#if defined (OS_LINUX)
|
||||
&v4l2_ops,
|
||||
[UVC_TYPE_V4L2] = &v4l2_ops,
|
||||
#elif defined (OS_WINDOWS)
|
||||
&dshow_ops,
|
||||
[UVC_TYPE_DSHOW] = &dshow_ops,
|
||||
#endif
|
||||
NULL
|
||||
[UVC_TYPE_MAX] = NULL,
|
||||
};
|
||||
|
||||
struct uvc_ctx *uvc_open(const char *dev, struct uvc_config *conf)
|
||||
struct uvc_ctx *uvc_open(enum uvc_type type, const char *dev, struct uvc_config *conf)
|
||||
{
|
||||
struct uvc_ctx *uvc;
|
||||
if (!dev || !conf) {
|
||||
@@ -55,7 +57,11 @@ struct uvc_ctx *uvc_open(const char *dev, struct uvc_config *conf)
|
||||
printf("malloc failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
uvc->ops = uvc_ops[0];
|
||||
uvc->ops = uvc_ops[type];
|
||||
if (!uvc->ops) {
|
||||
printf("uvc->ops %d is NULL!\n", type);
|
||||
return NULL;
|
||||
}
|
||||
uvc->opaque = uvc->ops->open(uvc, dev, conf);
|
||||
if (!uvc->opaque) {
|
||||
printf("open %s failed!\n", dev);
|
||||
|
||||
@@ -31,12 +31,19 @@
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#define LIBUVC_VERSION "0.1.0"
|
||||
#define LIBUVC_VERSION "0.2.0"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum uvc_type {
|
||||
UVC_TYPE_DUMMY = 0,
|
||||
UVC_TYPE_V4L2,
|
||||
UVC_TYPE_DSHOW,
|
||||
UVC_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct uvc_ctx;
|
||||
struct uvc_ops;
|
||||
typedef int (video_frame_cb)(struct uvc_ctx *c, struct video_frame *frame);
|
||||
@@ -78,7 +85,7 @@ struct uvc_ops {
|
||||
int (*query_frame)(struct uvc_ctx *c, struct video_frame *frame);
|
||||
};
|
||||
|
||||
struct uvc_ctx *uvc_open(const char *dev, struct uvc_config *conf);
|
||||
struct uvc_ctx *uvc_open(enum uvc_type type, const char *dev, struct uvc_config *conf);
|
||||
int uvc_ioctl(struct uvc_ctx *c, unsigned long int cmd, ...);
|
||||
void uvc_close(struct uvc_ctx *c);
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
#define VIDEO_DEV "/dev/video0"
|
||||
#define VIDEO_WIDTH 640
|
||||
#define VIDEO_HEIGHT 480
|
||||
#define OUTPUT_FILE "uvc.yuv"
|
||||
#define OUTPUT_V4L2 "v4l2.yuv"
|
||||
#define OUTPUT_DUMMY "dummy.yuv"
|
||||
|
||||
static struct file *fp;
|
||||
|
||||
@@ -42,7 +43,7 @@ static int on_frame(struct uvc_ctx *c, struct video_frame *frm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int v4l2_test()
|
||||
{
|
||||
struct video_frame *frm;
|
||||
struct uvc_config conf = {
|
||||
@@ -50,7 +51,7 @@ int main(int argc, char **argv)
|
||||
.height = VIDEO_HEIGHT,
|
||||
.fps = {30, 1},
|
||||
};
|
||||
struct uvc_ctx *uvc = uvc_open(VIDEO_DEV, &conf);
|
||||
struct uvc_ctx *uvc = uvc_open(UVC_TYPE_V4L2, VIDEO_DEV, &conf);
|
||||
if (!uvc) {
|
||||
printf("uvc_open failed!\n");
|
||||
return -1;
|
||||
@@ -64,13 +65,54 @@ int main(int argc, char **argv)
|
||||
printf("%s %dx%d@%d/%d fps format:%s\n", VIDEO_DEV, uvc->conf.width, uvc->conf.height,
|
||||
uvc->conf.fps.num, uvc->conf.fps.den, pixel_format_to_string(uvc->conf.format));
|
||||
//uvc_ioctl(uvc, UVC_GET_CAP, NULL, 0);
|
||||
fp = file_open(OUTPUT_FILE, F_CREATE);
|
||||
fp = file_open(OUTPUT_V4L2, F_CREATE);
|
||||
uvc_start_stream(uvc, on_frame);
|
||||
sleep(5);
|
||||
uvc_stop_stream(uvc);
|
||||
file_close(fp);
|
||||
video_frame_destroy(frm);
|
||||
uvc_close(uvc);
|
||||
printf("write %s fininshed!\n", OUTPUT_FILE);
|
||||
printf("write %s fininshed!\n", OUTPUT_V4L2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dummy_test()
|
||||
{
|
||||
struct video_frame *frm;
|
||||
struct uvc_config conf = {
|
||||
.width = 320,
|
||||
.height = 240,
|
||||
.fps = {5, 1},
|
||||
.format = PIXEL_FORMAT_YUY2,
|
||||
};
|
||||
struct uvc_ctx *uvc = uvc_open(UVC_TYPE_DUMMY, "sample_yuv422p.yuv", &conf);
|
||||
if (!uvc) {
|
||||
printf("uvc_open failed!\n");
|
||||
return -1;
|
||||
}
|
||||
frm = video_frame_create(uvc->conf.format, uvc->conf.width, uvc->conf.height, VFC_NONE);
|
||||
if (!frm) {
|
||||
printf("video_frame_create failed!\n");
|
||||
uvc_close(uvc);
|
||||
return -1;
|
||||
}
|
||||
printf("%s %dx%d@%d/%d fps format:%s\n", VIDEO_DEV, uvc->conf.width, uvc->conf.height,
|
||||
uvc->conf.fps.num, uvc->conf.fps.den, pixel_format_to_string(uvc->conf.format));
|
||||
fp = file_open(OUTPUT_DUMMY, F_CREATE);
|
||||
uvc_start_stream(uvc, on_frame);
|
||||
sleep(5);
|
||||
uvc_stop_stream(uvc);
|
||||
file_sync(fp);
|
||||
file_close(fp);
|
||||
video_frame_destroy(frm);
|
||||
uvc_close(uvc);
|
||||
printf("write %s fininshed!\n", OUTPUT_DUMMY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
v4l2_test();
|
||||
dummy_test();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ struct v4l2_ctx {
|
||||
int req_count;
|
||||
bool qbuf_done;
|
||||
uint64_t first_ts;
|
||||
uint64_t prev_ts;
|
||||
uint64_t frame_id;
|
||||
struct v4l2_capability cap;
|
||||
uint32_t ctrl_flags;
|
||||
@@ -463,7 +462,6 @@ retry:
|
||||
frame->frame_id = c->frame_id;
|
||||
c->frame_id++;
|
||||
|
||||
c->prev_ts = frame->timestamp;
|
||||
start = (uint8_t *)c->buf[qbuf.index].iov_base;
|
||||
|
||||
if (frame->flag == VFC_NONE) {//frame data ptr
|
||||
@@ -492,7 +490,6 @@ static int uvc_v4l2_poll_init(struct v4l2_ctx *c)
|
||||
}
|
||||
|
||||
epev.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET | EPOLLERR;
|
||||
epev.data.fd = c->fd;
|
||||
epev.data.ptr = (void *)c;
|
||||
if (-1 == epoll_ctl(c->epfd, EPOLL_CTL_ADD, c->fd, &epev)) {
|
||||
printf("epoll_ctl EPOLL_CTL_ADD failed %d!\n", errno);
|
||||
|
||||
Reference in New Issue
Block a user