mirror of
https://github.com/gozfree/gear-lib.git
synced 2026-02-05 18:51:20 +08:00
1.remove libuvc and libuac, replace with libavcap
2.update libbitmap from kernel 3.update libstrex
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
## 多媒体
|
||||
| | |
|
||||
|--|--|
|
||||
| libuvc: USB摄像头库 | libmp4: MP4录制解析库 |
|
||||
| libavcap: 音频视频捕获库 | libmp4: MP4录制解析库 |
|
||||
| libjpeg-ex: | libmedia-io: 音频视频格式定义 |
|
||||
|
||||
## 系统抽象层
|
||||
|
||||
@@ -44,7 +44,7 @@ This is a collection of basic libraries.
|
||||
## Multi-Media
|
||||
| | |
|
||||
|--|--|
|
||||
| libuvc: USB video class V4L2/dshow | libmp4: MP4 muxer and parser |
|
||||
| libavcap: audio/video capture api (v4l2/uvc/esp32/dshow) | libmp4: MP4 muxer and parser |
|
||||
| libjpeg-ex: | libmedia-io: audio/video frame/packet define |
|
||||
|
||||
## OS Abstraction Layer
|
||||
|
||||
2
build.sh
2
build.sh
@@ -26,7 +26,7 @@ PLATFORM="[linux|pi|android|ios]"
|
||||
|
||||
#basic libraries
|
||||
BASIC_LIBS="libposix libtime liblog libdarray libthread libgevent libworkq libdict libhash libsort \
|
||||
librbtree libringbuffer libvector libbase64 libmedia-io \
|
||||
librbtree libringbuffer libvector libstrex libmedia-io \
|
||||
libdebug libfile libqueue libplugin libhal libsubmask"
|
||||
MEDIA_LIBS="libavcap"
|
||||
FRAMEWORK_LIBS="libipc"
|
||||
|
||||
@@ -22,7 +22,7 @@ SET(QUEUE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libqueue/)
|
||||
SET(LOG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/liblog/)
|
||||
SET(SOCK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libsock/)
|
||||
SET(FILE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libfile/)
|
||||
SET(UVC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libuvc/)
|
||||
SET(AVCAP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libavcap/)
|
||||
SET(TIME_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libtime/)
|
||||
|
||||
ADD_SUBDIRECTORY(libposix)
|
||||
@@ -39,6 +39,5 @@ ADD_SUBDIRECTORY(libsock)
|
||||
ADD_SUBDIRECTORY(liblog)
|
||||
ADD_SUBDIRECTORY(libmedia-io)
|
||||
ADD_SUBDIRECTORY(libavcap)
|
||||
ADD_SUBDIRECTORY(libuvc)
|
||||
ADD_SUBDIRECTORY(librtmpc)
|
||||
ADD_SUBDIRECTORY(librtsp)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS = . ./libposix ./libthread ./libdarray ./libmedia-io ./libqueue ./liblog ./libavcap ./libfile ./libhal
|
||||
COMPONENT_SRCDIRS = libposix liblog libdarray libqueue libthread libmedia-io librtmpc libgevent libavcap libfile libhal
|
||||
COMPONENT_ADD_INCLUDEDIRS = . ./libposix ./libthread ./libdarray ./libmedia-io ./libqueue ./liblog ./libavcap ./libfile ./libhal ./librbtree
|
||||
COMPONENT_SRCDIRS = libposix liblog libdarray libqueue libthread libmedia-io librtmpc libgevent libavcap libfile libhal librbtree
|
||||
COMPONENT_OBJEXCLUDE = libposix/libposix4win.o libposix/libposix4nix.o libposix/libposix4rtthread.o libposix/test_libposix.o \
|
||||
libavcap/dshow.o libavcap/xcbgrab.o libavcap/v4l2.o libavcap/test_libavcap.o libavcap/pulseaudio.o libavcap/uvc.o \
|
||||
libfile/test_libfile.o libfile/filewatcher.o \
|
||||
libdarray/test_libdarray.o libqueue/test_libqueue.o libthread/test_libthread.o libmedia-io/test_libmedia-io.o \
|
||||
librtmpc/test_librtmpc.o liblog/test_liblog.o libgevent/iocp.o libgevent/epoll.o libgevent/wepoll.o \
|
||||
libhal/hal_nix.o libhal/hal_win.o libhal/test_hal.o
|
||||
libhal/hal_nix.o libhal/hal_win.o libhal/test_hal.o \
|
||||
librbtree/test_librbtree.o
|
||||
|
||||
COMPONENT_DEPENDS := newlib
|
||||
CFLAGS += -DFREERTOS -DESP32 #for libposix
|
||||
CFLAGS += -DNO_CRYPTO #for librtmpc
|
||||
|
||||
@@ -13,22 +13,21 @@ TGT_LIB_A = $(LIBNAME).lib
|
||||
TGT_LIB_SO = $(LIBNAME).dll
|
||||
TGT_UNIT_TEST = test_$(LIBNAME).exe
|
||||
|
||||
OBJS_LIB = $(LIBNAME).obj
|
||||
OBJS_LIB = $(LIBNAME).obj dshow.obj
|
||||
OBJS_UNIT_TEST = test_$(LIBNAME).obj
|
||||
|
||||
###############################################################################
|
||||
# cflags and ldflags
|
||||
###############################################################################
|
||||
CFLAGS = /Iinclude /I.
|
||||
CFLAGS = /I../libposix/ /I../libmedia-io/ /I../libfile/ /I.
|
||||
!IF "$(MODE)"=="release"
|
||||
CFLAGS = $(CFLAGS) /O2 /GF
|
||||
!ELSE
|
||||
CFLAGS = $(CFLAGS) /Od /W3 /Zi
|
||||
!ENDIF
|
||||
|
||||
LDFLAGS = /NOLOGO
|
||||
|
||||
LIBS = ws2_32.lib
|
||||
LIBS = ole32.lib strmiids.lib uuid.lib oleaut32.lib shlwapi.lib \
|
||||
../libmedia-io/libmedia-io.lib ../libfile/libfile.lib ../libposix/libposix.lib
|
||||
|
||||
###############################################################################
|
||||
# target
|
||||
@@ -40,13 +39,13 @@ OBJS = $(OBJS_LIB) $(OBJS_UNIT_TEST)
|
||||
all: $(TGT)
|
||||
|
||||
$(TGT_LIB_A): $(OBJS_LIB)
|
||||
$(AR) $(OBJS_LIB) $(LIBS) /o:$(TGT_LIB_A)
|
||||
$(AR) $(OBJS_LIB) $(LIBS) /out:$(TGT_LIB_A)
|
||||
|
||||
$(TGT_LIB_SO): $(OBJS_LIB)
|
||||
$(LD) $(LDFLAGS) /Dll $(OBJS_LIB) $(LIBS) /o:$(TGT_LIB_SO)
|
||||
$(LD) /Dll $(OBJS_LIB) $(LIBS)
|
||||
|
||||
$(TGT_UNIT_TEST): $(OBJS_UNIT_TEST)
|
||||
$(CC) $(TGT_LIB_A) $(OBJS_UNIT_TEST) /o $(TGT_UNIT_TEST)
|
||||
$(CC) $(TGT_LIB_A) $(OBJS_UNIT_TEST) /link $(LIBS)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS)
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
This is a simple audio video capture library.
|
||||
It's aim is to provide a unified API for Linux, Windows and Mac OS X to capture audio and video from appropriate hardware.
|
||||
The supported hardware contain Webcam, RaspberryPi, Laptop Camera which based onUVC.
|
||||
refer to https://sourceforge.net/projects/libavcap/
|
||||
|
||||
### Video
|
||||
* uvc: USB Video Class
|
||||
* uvc: USB Video Class (depend on libuvc https://github.com/ktossell/libuvc)
|
||||
* v4l2: video for linux
|
||||
* dshow: windows video api
|
||||
* dummy: file of frame sequence, as virtual device
|
||||
* esp32-camera: ESP32 camera (https://github.com/espressif/esp32-camera.git)
|
||||
* xcb: linux desktop screen capture, XCB(X protocol C-language Binding)
|
||||
|
||||
### Audio
|
||||
@@ -15,6 +17,99 @@ The supported hardware contain Webcam, RaspberryPi, Laptop Camera which based on
|
||||
|
||||
#### Test
|
||||
* ffplay -f rawvideo -pixel_format yuyv422 -video_size 640x480 v4l2.yuv
|
||||
* ffplay -f rawvideo -pixel_format yuv420p -video_size 640x480 v4l2.yuv (raspberry pi)
|
||||
* ffplay -ar 48000 -channels 2 -f s16le -i pulseaudio.pcm
|
||||
* ffplay -f x11grab -i :0.0
|
||||
* ffplay -pix_fmts
|
||||
* ffmpeg -f x11grab -framerate 25 -video_size 1366*768 -i :0.0 out.mp4
|
||||
|
||||
# raspberry pi
|
||||
`$ sudo modprobe bcm2835_v4l2 `
|
||||
|
||||
```
|
||||
x86 $ ./test_libavcap
|
||||
[V4L2 Input information]:
|
||||
input.name: "Camera 1"
|
||||
[V4L2 Capability Information]:
|
||||
cap.driver: "uvcvideo"
|
||||
cap.card: "Lenovo EasyCamera: Lenovo EasyC"
|
||||
cap.bus_info: "usb-0000:00:1d.0-1.6"
|
||||
cap.version: "266002"
|
||||
cap.capabilities:
|
||||
VIDEO_CAPTURE
|
||||
STREAMING
|
||||
[V4L2 Support Format]:
|
||||
0. [YUYV] "YUYV 4:2:2"
|
||||
1. [MJPG] "Motion-JPEG"
|
||||
[V4L2 Supported Control]:
|
||||
Brightness, range: [-64, 64], default: 0, current: 0
|
||||
Contrast, range: [0, 64], default: 32, current: 32
|
||||
Saturation, range: [0, 128], default: 64, current: 64
|
||||
Hue, range: [-180, 180], default: 0, current: 0
|
||||
White Balance Temperature, Auto, range: [0, 1], default: 1, current: 1
|
||||
Gamma, range: [100, 500], default: 300, current: 300
|
||||
Power Line Frequency, range: [0, 2], default: 1, current: 1
|
||||
White Balance Temperature, range: [2800, 6500], default: 4650, current: 4650
|
||||
Sharpness, range: [0, 8], default: 6, current: 6
|
||||
Backlight Compensation, range: [0, 2], default: 0, current: 0
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
raspberrypi0w $ ./test_libavcap
|
||||
[V4L2 Input information]:
|
||||
input.name: "Camera 0"
|
||||
[V4L2 Capability Information]:
|
||||
cap.driver: "bm2835 mmal"
|
||||
cap.card: "mmal service 16.1"
|
||||
cap.bus_info: "platform:bcm2835-v4l2"
|
||||
cap.version: "265799"
|
||||
cap.capabilities:
|
||||
VIDEO_CAPTURE
|
||||
VIDEO_OVERLAY
|
||||
READWRITE
|
||||
STREAMING
|
||||
[V4L2 Support Format]:
|
||||
0. [YU12] "Planar YUV 4:2:0"
|
||||
1. [YUYV] "YUYV 4:2:2"
|
||||
2. [RGB3] "24-bit RGB 8-8-8"
|
||||
3. [JPEG] "JFIF JPEG"
|
||||
4. [H264] "H.264"
|
||||
5. [MJPG] "Motion-JPEG"
|
||||
6. [YVYU] "YVYU 4:2:2"
|
||||
7. [VYUY] "VYUY 4:2:2"
|
||||
8. [UYVY] "UYVY 4:2:2"
|
||||
9. [NV12] "Y/CbCr 4:2:0"
|
||||
10. [BGR3] "24-bit BGR 8-8-8"
|
||||
11. [YV12] "Planar YVU 4:2:0"
|
||||
12. [NV21] "Y/CrCb 4:2:0"
|
||||
13. [BGR4] "32-bit BGRA/X 8-8-8-8"
|
||||
[V4L2 Supported Control]:
|
||||
Brightness, range: [0, 100], default: 50, current: 50
|
||||
Contrast, range: [-100, 100], default: 0, current: 0
|
||||
Saturation, range: [-100, 100], default: 0, current: 0
|
||||
Power Line Frequency, range: [0, 3], default: 1, current: 1
|
||||
Sharpness, range: [-100, 100], default: 0, current: 0
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
raspberrypi2b $ ./test_libavcap
|
||||
[V4L2 Input information]:
|
||||
input.name: "Camera 0"
|
||||
[V4L2 Capability Information]:
|
||||
cap.driver: "unicam"
|
||||
cap.card: "unicam"
|
||||
cap.bus_info: "platform:3f801000.csi"
|
||||
cap.version: "330303"
|
||||
cap.capabilities:
|
||||
VIDEO_CAPTURE
|
||||
READWRITE
|
||||
STREAMING
|
||||
[V4L2 Support Format]:
|
||||
0. [BA81] "8-bit Bayer BGBG/GRGR"
|
||||
1. [pBAA] "10-bit Bayer BGBG/GRGR Packed"
|
||||
2. [BG10] "10-bit Bayer BGBG/GRGR"
|
||||
[V4L2 Supported Control]:
|
||||
White Balance, Automatic, range: [0, 1], default: 0, current: 0
|
||||
```
|
||||
|
||||
171
gear-lib/libavcap/esp32-camera.c
Normal file
171
gear-lib/libavcap/esp32-camera.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "libavcap.h"
|
||||
#include <libthread.h>
|
||||
#include <liblog.h>
|
||||
#include <esp_camera.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#define CONFIG_CAMERA_MODEL_ESP_EYE 1
|
||||
|
||||
#if CONFIG_CAMERA_MODEL_ESP_EYE
|
||||
#define CAMERA_PIN_PWDN -1
|
||||
#define CAMERA_PIN_RESET -1
|
||||
#define CAMERA_PIN_SIOD 18
|
||||
#define CAMERA_PIN_SIOC 23
|
||||
#define CAMERA_PIN_XCLK 4
|
||||
|
||||
#define CAMERA_PIN_D0 34
|
||||
#define CAMERA_PIN_D1 13
|
||||
#define CAMERA_PIN_D2 14
|
||||
#define CAMERA_PIN_D3 35
|
||||
#define CAMERA_PIN_D4 39
|
||||
#define CAMERA_PIN_D5 38
|
||||
#define CAMERA_PIN_D6 37
|
||||
#define CAMERA_PIN_D7 36
|
||||
#define CAMERA_PIN_VSYNC 5
|
||||
#define CAMERA_PIN_HREF 27
|
||||
#define CAMERA_PIN_PCLK 25
|
||||
#endif
|
||||
|
||||
#define CAMERA_FRAME_SIZE FRAMESIZE_VGA
|
||||
#define CAMERA_WIDTH 640
|
||||
#define CAMERA_HEIGHT 480
|
||||
|
||||
struct esp32_cam_ctx {
|
||||
camera_config_t conf;
|
||||
camera_fb_t *fb;
|
||||
struct thread *thread;
|
||||
bool is_streaming;
|
||||
};
|
||||
|
||||
static camera_config_t default_camera_config = {
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.pin_d0 = CAMERA_PIN_D0,
|
||||
.pin_d1 = CAMERA_PIN_D1,
|
||||
.pin_d2 = CAMERA_PIN_D2,
|
||||
.pin_d3 = CAMERA_PIN_D3,
|
||||
.pin_d4 = CAMERA_PIN_D4,
|
||||
.pin_d5 = CAMERA_PIN_D5,
|
||||
.pin_d6 = CAMERA_PIN_D6,
|
||||
.pin_d7 = CAMERA_PIN_D7,
|
||||
.pin_xclk = CAMERA_PIN_XCLK,
|
||||
.pin_pclk = CAMERA_PIN_PCLK,
|
||||
.pin_vsync = CAMERA_PIN_VSYNC,
|
||||
.pin_href = CAMERA_PIN_HREF,
|
||||
.pin_sscb_sda = CAMERA_PIN_SIOD,
|
||||
.pin_sscb_scl = CAMERA_PIN_SIOC,
|
||||
.pin_pwdn = CAMERA_PIN_PWDN,
|
||||
.pin_reset = CAMERA_PIN_RESET,
|
||||
.xclk_freq_hz = CONFIG_XCLK_FREQ,
|
||||
|
||||
.frame_size = CAMERA_FRAME_SIZE,
|
||||
.pixel_format = PIXFORMAT_JPEG,
|
||||
.jpeg_quality = 15,
|
||||
|
||||
.fb_count = 2,
|
||||
};
|
||||
|
||||
static void *cam_open(struct avcap_ctx *avcap, const char *dev, struct avcap_config *avconf)
|
||||
{
|
||||
struct esp32_cam_ctx *c = calloc(1, sizeof(struct esp32_cam_ctx));
|
||||
if (!c) {
|
||||
printf("malloc esp32_cam_ctx failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&c->conf, &default_camera_config, sizeof(camera_config_t));
|
||||
|
||||
/* IO13, IO14 is designed for JTAG by default,
|
||||
* to use it as generalized input,
|
||||
* firstly declair it as pullup input */
|
||||
gpio_config_t conf;
|
||||
conf.mode = GPIO_MODE_INPUT;
|
||||
conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
conf.intr_type = GPIO_INTR_DISABLE;
|
||||
conf.pin_bit_mask = 1LL << 13;
|
||||
gpio_config(&conf);
|
||||
conf.pin_bit_mask = 1LL << 14;
|
||||
gpio_config(&conf);
|
||||
|
||||
esp_err_t err = esp_camera_init(&c->conf);
|
||||
if (err != ESP_OK) {
|
||||
printf("Camera init failed with error 0x%x", err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(&avcap->conf, avconf, sizeof(struct avcap_config));
|
||||
return c;
|
||||
}
|
||||
|
||||
static void cam_close(struct avcap_ctx *avcap)
|
||||
{
|
||||
struct esp32_cam_ctx *c = (struct esp32_cam_ctx *)avcap->opaque;
|
||||
if (esp_camera_deinit() != ESP_OK) {
|
||||
printf("esp_camera_deinit failed!\n");
|
||||
}
|
||||
free(c);
|
||||
}
|
||||
|
||||
static void *cam_thread(struct thread *t, void *arg)
|
||||
{
|
||||
struct avcap_ctx *avcap = arg;
|
||||
struct esp32_cam_ctx *c = (struct esp32_cam_ctx *)avcap->opaque;
|
||||
struct media_frame media;
|
||||
struct video_frame *frame = &media.video;
|
||||
media.type = MEDIA_TYPE_VIDEO;
|
||||
|
||||
video_frame_init(frame, avcap->conf.video.format, avcap->conf.video.width,
|
||||
avcap->conf.video.height, MEDIA_MEM_DEEP);
|
||||
c->is_streaming = true;
|
||||
while (c->is_streaming) {
|
||||
c->fb = esp_camera_fb_get();
|
||||
if (!c->fb) {
|
||||
printf("Camera capture failed");
|
||||
return NULL;
|
||||
}
|
||||
printf("Camera capture width=%d, height=%d size=%d\n",
|
||||
c->fb->width, c->fb->height, c->fb->len);
|
||||
avcap->on_media_frame(avcap, &media);
|
||||
esp_camera_fb_return(c->fb);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int cam_start_stream(struct avcap_ctx *avcap)
|
||||
{
|
||||
struct esp32_cam_ctx *c = (struct esp32_cam_ctx *)avcap->opaque;
|
||||
|
||||
if (avcap->on_media_frame) {
|
||||
c->thread = thread_create(cam_thread, avcap);
|
||||
if (!c->thread) {
|
||||
printf("thread_create failed!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cam_stop_stream(struct avcap_ctx *avcap)
|
||||
{
|
||||
struct esp32_cam_ctx *c = (struct esp32_cam_ctx *)avcap->opaque;
|
||||
if (!c->is_streaming) {
|
||||
printf("esp32 stream stopped already!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (avcap->on_media_frame) {
|
||||
c->is_streaming = false;
|
||||
thread_join(c->thread);
|
||||
thread_destroy(c->thread);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct avcap_ops esp32cam_ops = {
|
||||
._open = cam_open,
|
||||
._close = cam_close,
|
||||
.ioctl = NULL,
|
||||
.start_stream = cam_start_stream,
|
||||
.stop_stream = cam_stop_stream,
|
||||
};
|
||||
@@ -5,6 +5,23 @@
|
||||
#include <asm/types.h>
|
||||
#include "kernel.h"
|
||||
//#include <linux/compiler.h>
|
||||
typedef __signed__ char __s8;
|
||||
typedef unsigned char __u8;
|
||||
|
||||
typedef __signed__ short __s16;
|
||||
typedef unsigned short __u16;
|
||||
|
||||
typedef __signed__ int __s32;
|
||||
typedef unsigned int __u32;
|
||||
|
||||
#ifdef __GNUC__
|
||||
__extension__ typedef __signed__ long long __s64;
|
||||
__extension__ typedef unsigned long long __u64;
|
||||
#else
|
||||
typedef __signed__ long long __s64;
|
||||
typedef unsigned long long __u64;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __WORDSIZE
|
||||
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __TOOLS_LINUX_KERNEL_H
|
||||
#define __TOOLS_LINUX_KERNEL_H
|
||||
#ifndef _LINUX_KERNEL_H
|
||||
#define _LINUX_KERNEL_H
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
@@ -9,9 +10,24 @@
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
|
||||
#define USHRT_MAX ((u16)(~0U))
|
||||
#define SHRT_MAX ((s16)(USHRT_MAX>>1))
|
||||
#define SHRT_MIN ((s16)(-SHRT_MAX - 1))
|
||||
#define INT_MAX ((int)(~0U>>1))
|
||||
#define INT_MIN (-INT_MAX - 1)
|
||||
#ifndef UINT_MAX
|
||||
#define UINT_MAX (~0U)
|
||||
#endif
|
||||
#define LONG_MAX ((long)(~0UL>>1))
|
||||
#define LONG_MIN (-LONG_MAX - 1)
|
||||
#define ULONG_MAX (~0UL)
|
||||
#define LLONG_MAX ((long long)(~0ULL>>1))
|
||||
#define LLONG_MIN (-LLONG_MAX - 1)
|
||||
#define ULLONG_MAX (~0ULL)
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX (~(size_t)0)
|
||||
#endif
|
||||
|
||||
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
|
||||
|
||||
@@ -35,7 +51,9 @@
|
||||
(type *)((char *)__mptr - offsetof(type, member)); })
|
||||
#endif
|
||||
|
||||
#ifndef BUILD_BUG_ON
|
||||
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
|
||||
#endif
|
||||
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
|
||||
|
||||
#ifndef max
|
||||
@@ -102,7 +120,21 @@
|
||||
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
||||
int scnprintf(char * buf, size_t size, const char * fmt, ...);
|
||||
|
||||
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
|
||||
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
||||
|
||||
#define PAGE_SHIFT 13
|
||||
#define PAGE_SIZE (1 << PAGE_SHIFT)
|
||||
|
||||
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
|
||||
#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
|
||||
#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))
|
||||
#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))
|
||||
#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This looks more complex than it should be. But we need to
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
|
||||
PROJECT(gear-lib)
|
||||
|
||||
INCLUDE_DIRECTORIES(. ${POSIX_INCLUDE_DIR} ${LOG_INCLUDE_DIR} ${DICT_INCLUDE_DIR} ${THREAD_INCLUDE_DIR} ${GEVENT_INCLUDE_DIR} ${DARRAY_INCLUDE_DIR} ${SOCK_INCLUDE_DIR} ${MEDIA_IO_INCLUDE_DIR} ${FILE_INCLUDE_DIR} ${QUEUE_INCLUDE_DIR} ${UVC_INCLUDE_DIR} ${TIME_INCLUDE_DIR})
|
||||
INCLUDE_DIRECTORIES(. ${POSIX_INCLUDE_DIR} ${LOG_INCLUDE_DIR} ${DICT_INCLUDE_DIR} ${THREAD_INCLUDE_DIR} ${GEVENT_INCLUDE_DIR} ${DARRAY_INCLUDE_DIR} ${SOCK_INCLUDE_DIR} ${MEDIA_IO_INCLUDE_DIR} ${FILE_INCLUDE_DIR} ${QUEUE_INCLUDE_DIR} ${AVCAP_INCLUDE_DIR} ${TIME_INCLUDE_DIR})
|
||||
AUX_SOURCE_DIRECTORY(. SOURCE_FILES)
|
||||
|
||||
ADD_LIBRARY(rtsp ${SOURCE_FILES})
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
******************************************************************************/
|
||||
#include <libuvc.h>
|
||||
#include <libavcap.h>
|
||||
#include <liblog.h>
|
||||
#include <libdarray.h>
|
||||
#include <libtime.h>
|
||||
@@ -58,12 +58,12 @@ struct x264_ctx {
|
||||
|
||||
struct live_source_ctx {
|
||||
const char name[32];
|
||||
struct uvc_config conf;
|
||||
struct uvc_ctx *uvc;
|
||||
struct avcap_config conf;
|
||||
struct avcap_ctx *uvc;
|
||||
bool uvc_opened;
|
||||
struct x264_ctx *x264;
|
||||
struct iovec extradata;
|
||||
struct video_frame *frm;
|
||||
struct media_frame frm;
|
||||
struct media_packet *pkt;
|
||||
void *priv;
|
||||
};
|
||||
@@ -140,7 +140,7 @@ static struct x264_ctx *x264_open(struct live_source_ctx *cc)
|
||||
return NULL;
|
||||
}
|
||||
x264_param_default_preset(&c->param, "ultrafast" , "zerolatency");
|
||||
c->input_format = cc->uvc->conf.format;
|
||||
c->input_format = cc->uvc->conf.video.format;
|
||||
|
||||
c->param.rc.i_vbv_max_bitrate = 2500;
|
||||
c->param.rc.i_vbv_buffer_size = 2500;
|
||||
@@ -153,10 +153,10 @@ static struct x264_ctx *x264_open(struct live_source_ctx *cc)
|
||||
c->param.i_log_level = X264_LOG_INFO;
|
||||
c->param.i_csp = pixel_format_to_x264_csp(c->input_format);
|
||||
|
||||
c->param.i_width = cc->uvc->conf.width;
|
||||
c->param.i_height = cc->uvc->conf.height;
|
||||
c->param.i_fps_num = cc->uvc->conf.fps.num;
|
||||
c->param.i_fps_den = cc->uvc->conf.fps.den;
|
||||
c->param.i_width = cc->uvc->conf.video.width;
|
||||
c->param.i_height = cc->uvc->conf.video.height;
|
||||
c->param.i_fps_num = cc->uvc->conf.video.fps.num;
|
||||
c->param.i_fps_den = cc->uvc->conf.video.fps.den;
|
||||
|
||||
x264_param_apply_profile(&c->param, NULL);
|
||||
|
||||
@@ -178,11 +178,11 @@ static struct x264_ctx *x264_open(struct live_source_ctx *cc)
|
||||
}
|
||||
|
||||
c->encoder.format = VIDEO_CODEC_H264;
|
||||
c->encoder.width = cc->uvc->conf.width;
|
||||
c->encoder.height = cc->uvc->conf.height;
|
||||
c->encoder.width = cc->uvc->conf.video.width;
|
||||
c->encoder.height = cc->uvc->conf.video.height;
|
||||
c->encoder.bitrate = 2500;
|
||||
c->encoder.framerate.num = cc->uvc->conf.fps.num;
|
||||
c->encoder.framerate.den = cc->uvc->conf.fps.den;
|
||||
c->encoder.framerate.num = cc->uvc->conf.video.fps.num;
|
||||
c->encoder.framerate.den = cc->uvc->conf.video.fps.den;
|
||||
c->encoder.timebase.num = c->param.i_fps_den;
|
||||
c->encoder.timebase.den = c->param.i_fps_num;
|
||||
logi("width=%d, height=%d, timebase=%d/%d\n", c->encoder.width, c->encoder.height, c->encoder.timebase.num, c->encoder.timebase.den);
|
||||
@@ -348,23 +348,19 @@ static int live_open(struct media_source *ms, const char *name)
|
||||
logi("uvc already opened!\n");
|
||||
return 0;
|
||||
}
|
||||
c->conf.width = 640;
|
||||
c->conf.height = 480;
|
||||
c->conf.fps.num = 30;
|
||||
c->conf.fps.den = 1;
|
||||
c->conf.format = PIXEL_FORMAT_YUY2,
|
||||
c->uvc = uvc_open("/dev/video0", &c->conf);
|
||||
c->conf.video.width = 640;
|
||||
c->conf.video.height = 480;
|
||||
c->conf.video.fps.num = 30;
|
||||
c->conf.video.fps.den = 1;
|
||||
c->conf.video.format = PIXEL_FORMAT_YUY2,
|
||||
c->uvc = avcap_open("/dev/video0", &c->conf);
|
||||
if (!c->uvc) {
|
||||
loge("uvc open failed!\n");
|
||||
return -1;
|
||||
}
|
||||
c->frm = video_frame_create(c->uvc->conf.format, c->uvc->conf.width, c->uvc->conf.height, MEDIA_MEM_SHALLOW);
|
||||
if (!c->frm) {
|
||||
loge("video_frame_create failed!\n");
|
||||
return -1;
|
||||
}
|
||||
video_frame_init(&c->frm.video, c->uvc->conf.video.format, c->uvc->conf.video.width, c->uvc->conf.video.height, MEDIA_MEM_SHALLOW);
|
||||
c->pkt = media_packet_create(MEDIA_TYPE_VIDEO, MEDIA_MEM_SHALLOW, NULL, 0);
|
||||
if (uvc_start_stream(c->uvc, NULL)) {
|
||||
if (avcap_start_stream(c->uvc, NULL)) {
|
||||
loge("uvc start stream failed!\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -381,9 +377,9 @@ static int live_open(struct media_source *ms, const char *name)
|
||||
static void live_close(struct media_source *ms)
|
||||
{
|
||||
struct live_source_ctx *c = (struct live_source_ctx *)ms->opaque;
|
||||
uvc_stop_stream(c->uvc);
|
||||
avcap_stop_stream(c->uvc);
|
||||
x264_close(c->x264);
|
||||
uvc_close(c->uvc);
|
||||
avcap_close(c->uvc);
|
||||
c->uvc_opened = false;
|
||||
}
|
||||
|
||||
@@ -423,13 +419,13 @@ static int live_read(struct media_source *ms, void **data, size_t *len)
|
||||
struct live_source_ctx *c = (struct live_source_ctx *)ms->opaque;
|
||||
int size;
|
||||
ms_pre = time_now_msec();
|
||||
size = uvc_query_frame(c->uvc, c->frm);
|
||||
size = avcap_query_frame(c->uvc, &c->frm);
|
||||
if (size < 0) {
|
||||
loge("uvc_query_frame failed!\n");
|
||||
loge("avcap_query_frame failed!\n");
|
||||
}
|
||||
ms_post = time_now_msec();
|
||||
logd("uvc_query_frame cost %" PRIu64 "ms\n", ms_post - ms_pre);
|
||||
in.iov_base = c->frm;
|
||||
logd("avcap_query_frame cost %" PRIu64 "ms\n", ms_post - ms_pre);
|
||||
in.iov_base = &c->frm.video;
|
||||
in.iov_len = size;
|
||||
out.iov_base = c->pkt;
|
||||
ms_pre = time_now_msec();
|
||||
|
||||
@@ -109,6 +109,22 @@ char *strtoupper(char *dst, char *src, size_t n)
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* hex_to_bin - convert a hex digit to its real value
|
||||
* @ch: ascii character represents hex digit
|
||||
*
|
||||
* hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
|
||||
* input.
|
||||
*/
|
||||
int strhex2bin(char ch)
|
||||
{
|
||||
if ((ch >= '0') && (ch <= '9'))
|
||||
return ch - '0';
|
||||
ch = tolower(ch);
|
||||
if ((ch >= 'a') && (ch <= 'f'))
|
||||
return ch - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char s_base64_enc[64] = {
|
||||
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
||||
|
||||
@@ -60,6 +60,7 @@ size_t strlncpy(char *dst, size_t len, const char *src, size_t n);
|
||||
char *strtoupper(char *dst, char *src, size_t n);
|
||||
char *strtolower(char *dst, char *src, size_t n);
|
||||
|
||||
int strhex2bin(char ch);
|
||||
|
||||
GEAR_API size_t base64_encode(char* target, const void *source, size_t bytes);
|
||||
GEAR_API size_t base64_encode_url(char* target, const void *source, size_t bytes);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libuac
|
||||
|
||||
ifeq ($(MODE), release)
|
||||
LOCAL_CFLAGS += -O2
|
||||
endif
|
||||
|
||||
LIBRARIES_DIR := $(LOCAL_PATH)/../
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
|
||||
# Add your application source files here...
|
||||
LOCAL_SRC_FILES := libuac.c
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -1,106 +0,0 @@
|
||||
###############################################################################
|
||||
# common
|
||||
###############################################################################
|
||||
#ARCH: linux/arm/android/ios/win
|
||||
ARCH ?= linux
|
||||
OUTPUT ?= /usr/local
|
||||
BUILD_DIR := $(shell pwd)/../../build/
|
||||
ARCH_INC := $(BUILD_DIR)/$(ARCH).inc
|
||||
COLOR_INC := $(BUILD_DIR)/color.inc
|
||||
|
||||
include $(ARCH_INC)
|
||||
include $(COLOR_INC)
|
||||
|
||||
CC_V ?= $(CC)
|
||||
CXX_V ?= $(CXX)
|
||||
LD_V ?= $(LD)
|
||||
AR_V ?= $(AR)
|
||||
CP_V ?= $(CP)
|
||||
RM_V ?= $(RM)
|
||||
|
||||
###############################################################################
|
||||
# target and object
|
||||
###############################################################################
|
||||
LIBNAME = libuac
|
||||
VER_TAG = $(shell echo ${LIBNAME} | tr 'a-z' 'A-Z')
|
||||
VER = $(shell awk '/'"${VER_TAG}_VERSION"'/{print $$3}' ${LIBNAME}.h)
|
||||
TGT_LIB_H = $(LIBNAME).h
|
||||
TGT_LIB_A = $(LIBNAME).a
|
||||
TGT_LIB_SO = $(LIBNAME).so
|
||||
TGT_LIB_SO_VER = $(TGT_LIB_SO).${VER}
|
||||
TGT_UNIT_TEST = test_$(LIBNAME)
|
||||
|
||||
OBJS_LIB = $(LIBNAME).o pulseaudio.o
|
||||
OBJS_UNIT_TEST = test_$(LIBNAME).o
|
||||
|
||||
###############################################################################
|
||||
# cflags and ldflags
|
||||
###############################################################################
|
||||
ifeq ($(MODE), release)
|
||||
CFLAGS := -O2 -Wall -Werror -fPIC
|
||||
LTYPE := release
|
||||
else
|
||||
CFLAGS := -g -Wall -Werror -fPIC
|
||||
LTYPE := debug
|
||||
endif
|
||||
ifeq ($(OUTPUT),/usr/local)
|
||||
OUTLIBPATH :=/usr/local
|
||||
else
|
||||
OUTLIBPATH :=$(OUTPUT)/$(LTYPE)
|
||||
endif
|
||||
|
||||
CFLAGS += $($(ARCH)_CFLAGS)
|
||||
CFLAGS += -I$(OUTPUT)/include/gear-lib
|
||||
|
||||
SHARED := -shared
|
||||
|
||||
LDFLAGS := $($(ARCH)_LDFLAGS)
|
||||
LDFLAGS += -pthread -lpulse -lmedia-io
|
||||
|
||||
###############################################################################
|
||||
# target
|
||||
###############################################################################
|
||||
.PHONY : all clean
|
||||
|
||||
TGT := $(TGT_LIB_A)
|
||||
TGT += $(TGT_LIB_SO)
|
||||
TGT += $(TGT_UNIT_TEST)
|
||||
|
||||
OBJS := $(OBJS_LIB) $(OBJS_UNIT_TEST)
|
||||
|
||||
all: $(TGT)
|
||||
|
||||
%.o:%.c
|
||||
$(CC_V) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(TGT_LIB_A): $(OBJS_LIB)
|
||||
$(AR_V) rcs $@ $^
|
||||
|
||||
$(TGT_LIB_SO): $(OBJS_LIB)
|
||||
$(CC_V) -o $@ $^ $(SHARED)
|
||||
@mv $(TGT_LIB_SO) $(TGT_LIB_SO_VER)
|
||||
@ln -sf $(TGT_LIB_SO_VER) $(TGT_LIB_SO)
|
||||
|
||||
$(TGT_UNIT_TEST): $(OBJS_UNIT_TEST) $(ANDROID_MAIN_OBJ)
|
||||
$(CC_V) -o $@ $^ $(TGT_LIB_A) $(LDFLAGS) -L$(OUTLIBPATH)/lib/gear-lib -lfile -lposix -llog
|
||||
|
||||
clean:
|
||||
$(RM_V) -f $(OBJS)
|
||||
$(RM_V) -f $(TGT)
|
||||
$(RM_V) -f version.h
|
||||
$(RM_V) -f $(TGT_LIB_SO)*
|
||||
$(RM_V) -f $(TGT_LIB_SO_VER)
|
||||
|
||||
install:
|
||||
$(MAKEDIR_OUTPUT)
|
||||
@if [ "$(MODE)" = "release" ];then $(STRIP) $(TGT); fi
|
||||
$(CP_V) -r $(TGT_LIB_H) $(OUTPUT)/include/gear-lib
|
||||
$(CP_V) -r $(TGT_LIB_A) $(OUTLIBPATH)/lib/gear-lib
|
||||
$(CP_V) -r $(TGT_LIB_SO) $(OUTLIBPATH)/lib/gear-lib
|
||||
$(CP_V) -r $(TGT_LIB_SO_VER) $(OUTLIBPATH)/lib/gear-lib
|
||||
|
||||
uninstall:
|
||||
cd $(OUTPUT)/include/gear-lib/ && rm -f $(TGT_LIB_H)
|
||||
$(RM_V) -f $(OUTLIBPATH)/lib/gear-lib/$(TGT_LIB_A)
|
||||
$(RM_V) -f $(OUTLIBPATH)/lib/gear-lib/$(TGT_LIB_SO)
|
||||
$(RM_V) -f $(OUTLIBPATH)/lib/gear-lib/$(TGT_LIB_SO_VER)
|
||||
@@ -1,54 +0,0 @@
|
||||
###############################################################################
|
||||
# common
|
||||
###############################################################################
|
||||
#ARCH: linux/pi/android/ios/win
|
||||
LD = link
|
||||
AR = lib
|
||||
RM = del
|
||||
###############################################################################
|
||||
# target and object
|
||||
###############################################################################
|
||||
LIBNAME = libuac
|
||||
TGT_LIB_A = $(LIBNAME).lib
|
||||
TGT_LIB_SO = $(LIBNAME).dll
|
||||
TGT_UNIT_TEST = test_$(LIBNAME).exe
|
||||
|
||||
OBJS_LIB = $(LIBNAME).obj
|
||||
OBJS_UNIT_TEST = test_$(LIBNAME).obj
|
||||
|
||||
###############################################################################
|
||||
# cflags and ldflags
|
||||
###############################################################################
|
||||
CFLAGS = /Iinclude /I.
|
||||
!IF "$(MODE)"=="release"
|
||||
CFLAGS = $(CFLAGS) /O2 /GF
|
||||
!ELSE
|
||||
CFLAGS = $(CFLAGS) /Od /W3 /Zi
|
||||
!ENDIF
|
||||
|
||||
LDFLAGS = /NOLOGO
|
||||
|
||||
LIBS = ws2_32.lib
|
||||
|
||||
###############################################################################
|
||||
# target
|
||||
###############################################################################
|
||||
TGT = $(TGT_LIB_A) $(TGT_LIB_SO) $(TGT_UNIT_TEST)
|
||||
|
||||
OBJS = $(OBJS_LIB) $(OBJS_UNIT_TEST)
|
||||
|
||||
all: $(TGT)
|
||||
|
||||
$(TGT_LIB_A): $(OBJS_LIB)
|
||||
$(AR) $(OBJS_LIB) $(LIBS) /o:$(TGT_LIB_A)
|
||||
|
||||
$(TGT_LIB_SO): $(OBJS_LIB)
|
||||
$(LD) $(LDFLAGS) /Dll $(OBJS_LIB) $(LIBS) /o:$(TGT_LIB_SO)
|
||||
|
||||
$(TGT_UNIT_TEST): $(OBJS_UNIT_TEST)
|
||||
$(CC) $(TGT_LIB_A) $(OBJS_UNIT_TEST) /o $(TGT_UNIT_TEST)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS)
|
||||
$(RM) $(TGT)
|
||||
$(RM) $(TGT_LIB_SO)*
|
||||
@@ -1,5 +0,0 @@
|
||||
## libuac
|
||||
This is a simple libuac library.
|
||||
|
||||
ffplay -ar 48000 -channels 2 -f s16le -i uac.pcm
|
||||
ffplay -formats | grep PCM
|
||||
@@ -1,102 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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 "libuac.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined (OS_LINUX)
|
||||
extern struct uac_ops pa_ops;
|
||||
#elif defined (OS_WINDOWS)
|
||||
#endif
|
||||
|
||||
static struct uac_ops *uac_ops[] = {
|
||||
#if defined (OS_LINUX)
|
||||
&pa_ops,
|
||||
#elif defined (OS_WINDOWS)
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
struct uac_ctx *uac_open(const char *dev, struct uac_config *conf)
|
||||
{
|
||||
struct uac_ctx *uac;
|
||||
if (!dev || !conf) {
|
||||
printf("invalid paraments!\n");
|
||||
return NULL;
|
||||
}
|
||||
uac = (struct uac_ctx *)calloc(1, sizeof(struct uac_ctx));
|
||||
if (!uac) {
|
||||
printf("malloc failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
uac->ops = uac_ops[0];
|
||||
uac->opaque = uac->ops->open(uac, dev, conf);
|
||||
if (!uac->opaque) {
|
||||
printf("open %s failed!\n", dev);
|
||||
goto failed;
|
||||
}
|
||||
return uac;
|
||||
failed:
|
||||
free(uac);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void uac_close(struct uac_ctx *uac)
|
||||
{
|
||||
if (!uac) {
|
||||
printf("invalid paraments!\n");
|
||||
return;
|
||||
}
|
||||
uac->ops->close(uac);
|
||||
free(uac);
|
||||
}
|
||||
|
||||
int uac_query_frame(struct uac_ctx *uac, struct audio_frame *frame)
|
||||
{
|
||||
if (!uac || !frame) {
|
||||
printf("invalid paraments!\n");
|
||||
return -1;
|
||||
}
|
||||
return uac->ops->query_frame(uac, frame);
|
||||
}
|
||||
|
||||
int uac_start_stream(struct uac_ctx *uac, audio_frame_cb *cb)
|
||||
{
|
||||
if (!uac) {
|
||||
printf("invalid paraments!\n");
|
||||
return -1;
|
||||
}
|
||||
uac->on_audio_frame = cb;
|
||||
return uac->ops->start_stream(uac);
|
||||
}
|
||||
|
||||
int uac_stop_stream(struct uac_ctx *uac)
|
||||
{
|
||||
if (!uac) {
|
||||
printf("invalid paraments!\n");
|
||||
return -1;
|
||||
}
|
||||
return uac->ops->stop_stream(uac);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
#ifndef LIBUAC_H
|
||||
#define LIBUAC_H
|
||||
|
||||
#include <libposix.h>
|
||||
#include <libmedia-io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define LIBUAC_VERSION "0.1.0"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct uac_ctx;
|
||||
struct uac_ops;
|
||||
typedef int (audio_frame_cb)(struct uac_ctx *c, struct audio_frame *frame);
|
||||
|
||||
struct uac_config {
|
||||
enum sample_format format;
|
||||
uint32_t sample_rate;
|
||||
uint8_t channels;
|
||||
const char *device;
|
||||
};
|
||||
|
||||
struct uac_ctx {
|
||||
int fd;
|
||||
struct uac_config conf;
|
||||
struct uac_ops *ops;
|
||||
audio_frame_cb *on_audio_frame;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
struct uac_ops {
|
||||
void *(*open)(struct uac_ctx *uac, const char *dev, struct uac_config *conf);
|
||||
void (*close)(struct uac_ctx *c);
|
||||
int (*ioctl)(struct uac_ctx *c, unsigned long int cmd, ...);
|
||||
int (*start_stream)(struct uac_ctx *c);
|
||||
int (*stop_stream)(struct uac_ctx *c);
|
||||
int (*query_frame)(struct uac_ctx *c, struct audio_frame *frame);
|
||||
};
|
||||
|
||||
GEAR_API struct uac_ctx *uac_open(const char *dev, struct uac_config *conf);
|
||||
GEAR_API int uac_ioctl(struct uac_ctx *c, unsigned long int cmd, ...);
|
||||
GEAR_API void uac_close(struct uac_ctx *c);
|
||||
|
||||
/*
|
||||
* active query frame one by one
|
||||
*/
|
||||
GEAR_API int uac_query_frame(struct uac_ctx *c, struct audio_frame *frame);
|
||||
|
||||
/*
|
||||
* passive get frame when cb is set, otherwise need query frame one by one
|
||||
*/
|
||||
GEAR_API int uac_start_stream(struct uac_ctx *uac, audio_frame_cb *cb);
|
||||
GEAR_API int uac_stop_stream(struct uac_ctx *uac);
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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 "libuac.h"
|
||||
#include <libfile.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#define OUTPUT_FILE "uac.pcm"
|
||||
|
||||
static struct file *fp;
|
||||
|
||||
static int on_frame(struct uac_ctx *c, struct audio_frame *frm)
|
||||
{
|
||||
printf("frm[%" PRIu64 "] cnt=%d size=%" PRIu64 ", ts=%" PRIu64 " ms\n",
|
||||
frm->frame_id, frm->frames, frm->total_size, frm->timestamp/1000000);
|
||||
file_write(fp, frm->data[0], frm->total_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int foo()
|
||||
{
|
||||
struct uac_config conf = {
|
||||
.format = SAMPLE_FORMAT_PCM_S16LE,
|
||||
.sample_rate = 44100,
|
||||
.channels = 2,
|
||||
};
|
||||
struct uac_ctx *uac = uac_open("xxx", &conf);
|
||||
if (!uac) {
|
||||
printf("uac_open failed!\n");
|
||||
return -1;
|
||||
}
|
||||
printf("sample_rate: %d\n", uac->conf.sample_rate);
|
||||
printf(" channel: %d\n", uac->conf.channels);
|
||||
printf(" format: %s\n", sample_format_to_string(uac->conf.format));
|
||||
printf(" device: %s\n", uac->conf.device);
|
||||
fp = file_open(OUTPUT_FILE, F_CREATE);
|
||||
uac_start_stream(uac, on_frame);
|
||||
sleep(5);
|
||||
uac_stop_stream(uac);
|
||||
file_close(fp);
|
||||
uac_close(uac);
|
||||
printf("write %s fininshed!\n", OUTPUT_FILE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
foo();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libuvc
|
||||
|
||||
ifeq ($(MODE), release)
|
||||
LOCAL_CFLAGS += -O2
|
||||
endif
|
||||
|
||||
LIBRARIES_DIR := $(LOCAL_PATH)/../
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
|
||||
# Add your application source files here...
|
||||
LOCAL_SRC_FILES := libuvc.c
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -1,14 +0,0 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
|
||||
PROJECT(gear-lib)
|
||||
|
||||
INCLUDE_DIRECTORIES(. ${POSIX_INCLUDE_DIR} ${MEDIA_IO_INCLUDE_DIR} ${THREAD_INCLUDE_DIR})
|
||||
|
||||
LIST(APPEND SOURCE_FILES libuvc.c)
|
||||
|
||||
IF (DEFINED OS_LINUX)
|
||||
LIST(APPEND SOURCE_FILES v4l2.c)
|
||||
ELSEIF (DEFINED OS_WINDOWS)
|
||||
LIST(APPEND SOURCE_FILES dshow.c)
|
||||
ENDIF ()
|
||||
|
||||
ADD_LIBRARY(uvc ${SOURCE_FILES})
|
||||
@@ -1,107 +0,0 @@
|
||||
###############################################################################
|
||||
# common
|
||||
###############################################################################
|
||||
#ARCH: linux/arm/android/ios/win
|
||||
ARCH ?= linux
|
||||
OUTPUT ?= /usr/local
|
||||
BUILD_DIR := $(shell pwd)/../../build/
|
||||
ARCH_INC := $(BUILD_DIR)/$(ARCH).inc
|
||||
COLOR_INC := $(BUILD_DIR)/color.inc
|
||||
|
||||
include $(ARCH_INC)
|
||||
include $(COLOR_INC)
|
||||
|
||||
CC_V ?= $(CC)
|
||||
CXX_V ?= $(CXX)
|
||||
LD_V ?= $(LD)
|
||||
AR_V ?= $(AR)
|
||||
CP_V ?= $(CP)
|
||||
RM_V ?= $(RM)
|
||||
|
||||
###############################################################################
|
||||
# target and object
|
||||
###############################################################################
|
||||
LIBNAME = libuvc
|
||||
VER_TAG = $(shell echo ${LIBNAME} | tr 'a-z' 'A-Z')
|
||||
VER = $(shell awk '/'"${VER_TAG}_VERSION"'/{print $$3}' ${LIBNAME}.h)
|
||||
TGT_LIB_H = $(LIBNAME).h
|
||||
TGT_LIB_A = $(LIBNAME).a
|
||||
TGT_LIB_SO = $(LIBNAME).so
|
||||
TGT_LIB_SO_VER = $(TGT_LIB_SO).${VER}
|
||||
TGT_UNIT_TEST = test_$(LIBNAME)
|
||||
|
||||
OBJS_LIB = $(LIBNAME).o v4l2.o dummy.o usbcam.o
|
||||
OBJS_UNIT_TEST = test_$(LIBNAME).o
|
||||
|
||||
###############################################################################
|
||||
# cflags and ldflags
|
||||
###############################################################################
|
||||
ifeq ($(MODE), release)
|
||||
CFLAGS := -O2 -Wall -Werror -fPIC
|
||||
LTYPE := release
|
||||
else
|
||||
CFLAGS := -g -Wall -Werror -fPIC
|
||||
# -DHAVE_LIBV4L2
|
||||
LTYPE := debug
|
||||
endif
|
||||
ifeq ($(OUTPUT),/usr/local)
|
||||
OUTLIBPATH :=/usr/local
|
||||
else
|
||||
OUTLIBPATH :=$(OUTPUT)/$(LTYPE)
|
||||
endif
|
||||
CFLAGS += $($(ARCH)_CFLAGS)
|
||||
CFLAGS += -I$(OUTPUT)/include/gear-lib
|
||||
|
||||
SHARED := -shared
|
||||
|
||||
LDFLAGS := $($(ARCH)_LDFLAGS)
|
||||
LDFLAGS += -pthread -lmedia-io -lthread -lusb-1.0
|
||||
# -lv4l2
|
||||
|
||||
###############################################################################
|
||||
# target
|
||||
###############################################################################
|
||||
.PHONY : all clean
|
||||
|
||||
TGT := $(TGT_LIB_A)
|
||||
TGT += $(TGT_LIB_SO)
|
||||
TGT += $(TGT_UNIT_TEST)
|
||||
|
||||
OBJS := $(OBJS_LIB) $(OBJS_UNIT_TEST)
|
||||
|
||||
all: $(TGT)
|
||||
|
||||
%.o:%.c
|
||||
$(CC_V) -c $(CFLAGS) $< -o $@
|
||||
|
||||
$(TGT_LIB_A): $(OBJS_LIB)
|
||||
$(AR_V) rcs $@ $^
|
||||
|
||||
$(TGT_LIB_SO): $(OBJS_LIB)
|
||||
$(CC_V) -o $@ $^ $(SHARED)
|
||||
@mv $(TGT_LIB_SO) $(TGT_LIB_SO_VER)
|
||||
@ln -sf $(TGT_LIB_SO_VER) $(TGT_LIB_SO)
|
||||
|
||||
$(TGT_UNIT_TEST): $(OBJS_UNIT_TEST) $(ANDROID_MAIN_OBJ)
|
||||
$(CC_V) -o $@ $^ $(TGT_LIB_A) $(LDFLAGS) -L$(OUTLIBPATH)/lib/gear-lib -lfile -lposix -llog
|
||||
|
||||
clean:
|
||||
$(RM_V) -f $(OBJS)
|
||||
$(RM_V) -f $(TGT)
|
||||
$(RM_V) -f version.h
|
||||
$(RM_V) -f $(TGT_LIB_SO)*
|
||||
$(RM_V) -f $(TGT_LIB_SO_VER)
|
||||
|
||||
install:
|
||||
$(MAKEDIR_OUTPUT)
|
||||
@if [ "$(MODE)" = "release" ];then $(STRIP) $(TGT); fi
|
||||
$(CP_V) -r $(TGT_LIB_H) $(OUTPUT)/include/gear-lib
|
||||
$(CP_V) -r $(TGT_LIB_A) $(OUTLIBPATH)/lib/gear-lib
|
||||
$(CP_V) -r $(TGT_LIB_SO) $(OUTLIBPATH)/lib/gear-lib
|
||||
$(CP_V) -r $(TGT_LIB_SO_VER) $(OUTLIBPATH)/lib/gear-lib
|
||||
|
||||
uninstall:
|
||||
cd $(OUTPUT)/include/gear-lib/ && rm -f $(TGT_LIB_H)
|
||||
$(RM_V) -f $(OUTLIBPATH)/lib/gear-lib/$(TGT_LIB_A)
|
||||
$(RM_V) -f $(OUTLIBPATH)/lib/gear-lib/$(TGT_LIB_SO)
|
||||
$(RM_V) -f $(OUTLIBPATH)/lib/gear-lib/$(TGT_LIB_SO_VER)
|
||||
@@ -1,54 +0,0 @@
|
||||
###############################################################################
|
||||
# common
|
||||
###############################################################################
|
||||
#ARCH: linux/pi/android/ios/win
|
||||
LD = link
|
||||
AR = lib
|
||||
RM = del
|
||||
|
||||
###############################################################################
|
||||
# target and object
|
||||
###############################################################################
|
||||
LIBNAME = libuvc
|
||||
TGT_LIB_A = $(LIBNAME).lib
|
||||
TGT_LIB_SO = $(LIBNAME).dll
|
||||
TGT_UNIT_TEST = test_$(LIBNAME).exe
|
||||
|
||||
OBJS_LIB = $(LIBNAME).obj dshow.obj
|
||||
OBJS_UNIT_TEST = test_$(LIBNAME).obj
|
||||
|
||||
###############################################################################
|
||||
# cflags and ldflags
|
||||
###############################################################################
|
||||
CFLAGS = /I../libposix/ /I../libmedia-io/ /I../libfile/ /I.
|
||||
!IF "$(MODE)"=="release"
|
||||
CFLAGS = $(CFLAGS) /O2 /GF
|
||||
!ELSE
|
||||
CFLAGS = $(CFLAGS) /Od /W3 /Zi
|
||||
!ENDIF
|
||||
|
||||
LIBS = ole32.lib strmiids.lib uuid.lib oleaut32.lib shlwapi.lib \
|
||||
../libmedia-io/libmedia-io.lib ../libfile/libfile.lib ../libposix/libposix.lib
|
||||
|
||||
###############################################################################
|
||||
# target
|
||||
###############################################################################
|
||||
TGT = $(TGT_LIB_A) $(TGT_LIB_SO) $(TGT_UNIT_TEST)
|
||||
|
||||
OBJS = $(OBJS_LIB) $(OBJS_UNIT_TEST)
|
||||
|
||||
all: $(TGT)
|
||||
|
||||
$(TGT_LIB_A): $(OBJS_LIB)
|
||||
$(AR) $(OBJS_LIB) $(LIBS) /out:$(TGT_LIB_A)
|
||||
|
||||
$(TGT_LIB_SO): $(OBJS_LIB)
|
||||
$(LD) /Dll $(OBJS_LIB) $(LIBS)
|
||||
|
||||
$(TGT_UNIT_TEST): $(OBJS_UNIT_TEST)
|
||||
$(CC) $(TGT_LIB_A) $(OBJS_UNIT_TEST) /link $(LIBS)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS)
|
||||
$(RM) $(TGT)
|
||||
$(RM) $(TGT_LIB_SO)*
|
||||
@@ -1,99 +0,0 @@
|
||||
## libuvc
|
||||
This is a simple libuvc library.
|
||||
if you want to enable HAVE_LIBV4L2, please install libv4l2-dev
|
||||
|
||||
* v4l2: video for linux
|
||||
* dummy: file of frame sequence, as virtual device
|
||||
* dshow: windows video api
|
||||
|
||||
# pi
|
||||
sudo modprobe bcm2835_v4l2
|
||||
|
||||
ffplay -pix_fmts
|
||||
x86_usbcam:
|
||||
ffplay -f rawvideo -pixel_format yuyv422 -video_size 640x480 uvc.yuv
|
||||
raspberrypi:
|
||||
ffplay -f rawvideo -pixel_format yuv420p -video_size 640x480 uvc.yuv
|
||||
|
||||
x86 $ ./test_libuvc
|
||||
[V4L2 Input information]:
|
||||
input.name: "Camera 1"
|
||||
[V4L2 Capability Information]:
|
||||
cap.driver: "uvcvideo"
|
||||
cap.card: "Lenovo EasyCamera: Lenovo EasyC"
|
||||
cap.bus_info: "usb-0000:00:1d.0-1.6"
|
||||
cap.version: "266002"
|
||||
cap.capabilities:
|
||||
VIDEO_CAPTURE
|
||||
STREAMING
|
||||
[V4L2 Support Format]:
|
||||
0. [YUYV] "YUYV 4:2:2"
|
||||
1. [MJPG] "Motion-JPEG"
|
||||
[V4L2 Supported Control]:
|
||||
Brightness, range: [-64, 64], default: 0, current: 0
|
||||
Contrast, range: [0, 64], default: 32, current: 32
|
||||
Saturation, range: [0, 128], default: 64, current: 64
|
||||
Hue, range: [-180, 180], default: 0, current: 0
|
||||
White Balance Temperature, Auto, range: [0, 1], default: 1, current: 1
|
||||
Gamma, range: [100, 500], default: 300, current: 300
|
||||
Power Line Frequency, range: [0, 2], default: 1, current: 1
|
||||
White Balance Temperature, range: [2800, 6500], default: 4650, current: 4650
|
||||
Sharpness, range: [0, 8], default: 6, current: 6
|
||||
Backlight Compensation, range: [0, 2], default: 0, current: 0
|
||||
|
||||
|
||||
raspberrypi0w $ ./test_libuvc
|
||||
[V4L2 Input information]:
|
||||
input.name: "Camera 0"
|
||||
[V4L2 Capability Information]:
|
||||
cap.driver: "bm2835 mmal"
|
||||
cap.card: "mmal service 16.1"
|
||||
cap.bus_info: "platform:bcm2835-v4l2"
|
||||
cap.version: "265799"
|
||||
cap.capabilities:
|
||||
VIDEO_CAPTURE
|
||||
VIDEO_OVERLAY
|
||||
READWRITE
|
||||
STREAMING
|
||||
[V4L2 Support Format]:
|
||||
0. [YU12] "Planar YUV 4:2:0"
|
||||
1. [YUYV] "YUYV 4:2:2"
|
||||
2. [RGB3] "24-bit RGB 8-8-8"
|
||||
3. [JPEG] "JFIF JPEG"
|
||||
4. [H264] "H.264"
|
||||
5. [MJPG] "Motion-JPEG"
|
||||
6. [YVYU] "YVYU 4:2:2"
|
||||
7. [VYUY] "VYUY 4:2:2"
|
||||
8. [UYVY] "UYVY 4:2:2"
|
||||
9. [NV12] "Y/CbCr 4:2:0"
|
||||
10. [BGR3] "24-bit BGR 8-8-8"
|
||||
11. [YV12] "Planar YVU 4:2:0"
|
||||
12. [NV21] "Y/CrCb 4:2:0"
|
||||
13. [BGR4] "32-bit BGRA/X 8-8-8-8"
|
||||
[V4L2 Supported Control]:
|
||||
Brightness, range: [0, 100], default: 50, current: 50
|
||||
Contrast, range: [-100, 100], default: 0, current: 0
|
||||
Saturation, range: [-100, 100], default: 0, current: 0
|
||||
Power Line Frequency, range: [0, 3], default: 1, current: 1
|
||||
Sharpness, range: [-100, 100], default: 0, current: 0
|
||||
|
||||
|
||||
raspberrypi2b $ ./test_libuvc
|
||||
[V4L2 Input information]:
|
||||
input.name: "Camera 0"
|
||||
[V4L2 Capability Information]:
|
||||
cap.driver: "unicam"
|
||||
cap.card: "unicam"
|
||||
cap.bus_info: "platform:3f801000.csi"
|
||||
cap.version: "330303"
|
||||
cap.capabilities:
|
||||
VIDEO_CAPTURE
|
||||
READWRITE
|
||||
STREAMING
|
||||
[V4L2 Support Format]:
|
||||
0. [BA81] "8-bit Bayer BGBG/GRGR"
|
||||
1. [pBAA] "10-bit Bayer BGBG/GRGR Packed"
|
||||
2. [BG10] "10-bit Bayer BGBG/GRGR"
|
||||
[V4L2 Supported Control]:
|
||||
White Balance, Automatic, range: [0, 1], default: 0, current: 0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,245 +0,0 @@
|
||||
#ifndef DSHOW_H
|
||||
#define DSHOW_H
|
||||
|
||||
#define COBJMACROS
|
||||
#define CINTERFACE
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <dshow.h>
|
||||
#include <strmif.h>
|
||||
#include <dvdmedia.h>
|
||||
#include <uuids.h>
|
||||
#include <control.h>
|
||||
#include <objidl.h>
|
||||
#include <shlwapi.h>
|
||||
#include <vfwmsgs.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
enum dshowDeviceType {
|
||||
VideoDevice = 0,
|
||||
AudioDevice = 1,
|
||||
};
|
||||
|
||||
|
||||
//#define dshowdebug(...) printf(__VA_ARGS__)
|
||||
#define dshowdebug(...) do{} while(0)
|
||||
|
||||
struct GUIDoffset {
|
||||
const GUID *iid;
|
||||
int offset;
|
||||
};
|
||||
|
||||
#define DECLARE_QUERYINTERFACE(class, ...) \
|
||||
long WINAPI class##_QueryInterface(class *this, const GUID *riid, void **ppobj) \
|
||||
{ \
|
||||
struct GUIDoffset ifaces[] = __VA_ARGS__; \
|
||||
int i; \
|
||||
dshowdebug("%s(%p, %p, %p)\n", __func__, this, riid, ppobj); \
|
||||
if (!ppobj) \
|
||||
return E_POINTER; \
|
||||
for (i = 0; i < sizeof(ifaces)/sizeof(ifaces[0]); i++) { \
|
||||
if (IsEqualGUID(riid, ifaces[i].iid)) { \
|
||||
void *obj = (void *) ((uint8_t *) this + ifaces[i].offset); \
|
||||
class##_AddRef(this); \
|
||||
*ppobj = (void *) obj; \
|
||||
return S_OK; \
|
||||
} \
|
||||
} \
|
||||
dshowdebug("%s(%p) E_NOINTERFACE\n", __func__, this, riid, ppobj); \
|
||||
*ppobj = NULL; \
|
||||
return E_NOINTERFACE; \
|
||||
}
|
||||
|
||||
#define DECLARE_ADDREF(class) \
|
||||
unsigned long WINAPI \
|
||||
class##_AddRef(class *this) \
|
||||
{ \
|
||||
dshowdebug("%s(%p)\t%ld\n", __func__, this, this->ref+1); \
|
||||
return InterlockedIncrement(&this->ref); \
|
||||
}
|
||||
#define DECLARE_RELEASE(class) \
|
||||
unsigned long WINAPI \
|
||||
class##_Release(class *this) \
|
||||
{ \
|
||||
long ref = InterlockedDecrement(&this->ref); \
|
||||
dshowdebug("%s(%p)\t%ld\n", __func__, this, ref); \
|
||||
if (!ref) \
|
||||
class##_Destroy(this); \
|
||||
return ref; \
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_DESTROY(class, func) \
|
||||
void class##_Destroy(class *this) \
|
||||
{ \
|
||||
dshowdebug("%s(%p)\n", __func__, this); \
|
||||
func(this); \
|
||||
if (this) { \
|
||||
if (this->vtbl) \
|
||||
CoTaskMemFree(this->vtbl); \
|
||||
CoTaskMemFree(this); \
|
||||
} \
|
||||
}
|
||||
#define DECLARE_CREATE(class, setup, ...) \
|
||||
class *class##_Create(__VA_ARGS__) \
|
||||
{ \
|
||||
class *this = CoTaskMemAlloc(sizeof(class)); \
|
||||
void *vtbl = CoTaskMemAlloc(sizeof(*this->vtbl)); \
|
||||
if (!this || !vtbl) \
|
||||
goto fail; \
|
||||
ZeroMemory(this, sizeof(class)); \
|
||||
ZeroMemory(vtbl, sizeof(*this->vtbl)); \
|
||||
this->ref = 1; \
|
||||
this->vtbl = vtbl; \
|
||||
if (!setup) \
|
||||
goto fail; \
|
||||
dshowdebug("%s(%p)\n", __func__, this); \
|
||||
return this; \
|
||||
fail: \
|
||||
class##_Destroy(this); \
|
||||
dshowdebug("could not create \n"); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define SETVTBL(vtbl, class, fn) \
|
||||
do { (vtbl)->fn = (void *) class##_##fn; } while(0)
|
||||
|
||||
#define imemoffset offsetof(dshow_pin, imemvtbl)
|
||||
|
||||
/*****************************************************************************
|
||||
* dshow_pin
|
||||
****************************************************************************/
|
||||
typedef struct dshow_pin dshow_pin;
|
||||
typedef struct dshow_filter dshow_filter;
|
||||
typedef struct dshow_enum_pins dshow_enum_pins;
|
||||
typedef struct dshow_enum_media_types dshow_enum_media_types;
|
||||
typedef struct dshow_input_pin dshow_input_pin;
|
||||
|
||||
struct dshow_pin {
|
||||
IPinVtbl *vtbl;
|
||||
IMemInputPinVtbl *imemvtbl;
|
||||
IPin *connectedto;
|
||||
struct dshow_filter *filter;
|
||||
AM_MEDIA_TYPE type;
|
||||
long ref;
|
||||
};
|
||||
|
||||
struct dshow_filter {
|
||||
IBaseFilterVtbl *vtbl;
|
||||
long ref;
|
||||
const wchar_t *name;
|
||||
struct dshow_pin *pin;
|
||||
FILTER_INFO info;
|
||||
FILTER_STATE state;
|
||||
IReferenceClock *clock;
|
||||
void *priv_data;
|
||||
int stream_index;
|
||||
int64_t start_time;
|
||||
void (*callback)(void *priv, int index, uint8_t *buf, int size, int64_t time, int devtype);
|
||||
};
|
||||
|
||||
|
||||
long WINAPI dshow_pin_QueryInterface (dshow_pin *, const GUID *, void **);
|
||||
unsigned long WINAPI dshow_pin_AddRef (dshow_pin *);
|
||||
unsigned long WINAPI dshow_pin_Release (dshow_pin *);
|
||||
long WINAPI dshow_pin_Connect (dshow_pin *, IPin *, const AM_MEDIA_TYPE *);
|
||||
long WINAPI dshow_pin_ReceiveConnection (dshow_pin *, IPin *, const AM_MEDIA_TYPE *);
|
||||
long WINAPI dshow_pin_Disconnect (dshow_pin *);
|
||||
long WINAPI dshow_pin_ConnectedTo (dshow_pin *, IPin **);
|
||||
long WINAPI dshow_pin_ConnectionMediaType (dshow_pin *, AM_MEDIA_TYPE *);
|
||||
long WINAPI dshow_pin_QueryPinInfo (dshow_pin *, PIN_INFO *);
|
||||
long WINAPI dshow_pin_QueryDirection (dshow_pin *, PIN_DIRECTION *);
|
||||
long WINAPI dshow_pin_QueryId (dshow_pin *, wchar_t **);
|
||||
long WINAPI dshow_pin_QueryAccept (dshow_pin *, const AM_MEDIA_TYPE *);
|
||||
long WINAPI dshow_pin_EnumMediaTypes (dshow_pin *, IEnumMediaTypes **);
|
||||
long WINAPI dshow_pin_QueryInternalConnections(dshow_pin *, IPin **, unsigned long *);
|
||||
long WINAPI dshow_pin_EndOfStream (dshow_pin *);
|
||||
long WINAPI dshow_pin_BeginFlush (dshow_pin *);
|
||||
long WINAPI dshow_pin_EndFlush (dshow_pin *);
|
||||
long WINAPI dshow_pin_NewSegment (dshow_pin *, REFERENCE_TIME, REFERENCE_TIME, double);
|
||||
dshow_pin *dshow_pin_Create (dshow_filter *filter);
|
||||
void dshow_pin_Destroy(dshow_pin *);
|
||||
|
||||
/*****************************************************************************
|
||||
* dshow_input_pin
|
||||
****************************************************************************/
|
||||
long WINAPI dshow_input_pin_QueryInterface (dshow_input_pin *, const GUID *, void **);
|
||||
unsigned long WINAPI dshow_input_pin_AddRef (dshow_input_pin *);
|
||||
unsigned long WINAPI dshow_input_pin_Release (dshow_input_pin *);
|
||||
long WINAPI dshow_input_pin_GetAllocator (dshow_input_pin *, IMemAllocator **);
|
||||
long WINAPI dshow_input_pin_NotifyAllocator (dshow_input_pin *, IMemAllocator *, BOOL);
|
||||
long WINAPI dshow_input_pin_GetAllocatorRequirements(dshow_input_pin *, ALLOCATOR_PROPERTIES *);
|
||||
long WINAPI dshow_input_pin_Receive (dshow_input_pin *, IMediaSample *);
|
||||
long WINAPI dshow_input_pin_ReceiveMultiple (dshow_input_pin *, IMediaSample **, long, long *);
|
||||
long WINAPI dshow_input_pin_ReceiveCanBlock (dshow_input_pin *);
|
||||
void dshow_input_pin_Destroy(dshow_input_pin *);
|
||||
|
||||
/*****************************************************************************
|
||||
* dshow_enum_media_types
|
||||
****************************************************************************/
|
||||
struct dshow_enum_media_types {
|
||||
IEnumMediaTypesVtbl *vtbl;
|
||||
long ref;
|
||||
int pos;
|
||||
AM_MEDIA_TYPE type;
|
||||
};
|
||||
|
||||
long WINAPI dshow_enum_media_types_QueryInterface(dshow_enum_media_types *, const GUID *, void **);
|
||||
unsigned long WINAPI dshow_enum_media_types_AddRef (dshow_enum_media_types *);
|
||||
unsigned long WINAPI dshow_enum_media_types_Release (dshow_enum_media_types *);
|
||||
long WINAPI dshow_enum_media_types_Next (dshow_enum_media_types *, unsigned long, AM_MEDIA_TYPE **, unsigned long *);
|
||||
long WINAPI dshow_enum_media_types_Skip (dshow_enum_media_types *, unsigned long);
|
||||
long WINAPI dshow_enum_media_types_Reset (dshow_enum_media_types *);
|
||||
long WINAPI dshow_enum_media_types_Clone (dshow_enum_media_types *, dshow_enum_media_types **);
|
||||
|
||||
void dshow_enum_media_types_Destroy(dshow_enum_media_types *);
|
||||
dshow_enum_media_types *dshow_enum_media_types_Create(const AM_MEDIA_TYPE *type);
|
||||
|
||||
/*****************************************************************************
|
||||
* dshow_filter
|
||||
****************************************************************************/
|
||||
long WINAPI dshow_filter_QueryInterface (dshow_filter *, const GUID *, void **);
|
||||
unsigned long WINAPI dshow_filter_AddRef (dshow_filter *);
|
||||
unsigned long WINAPI dshow_filter_Release (dshow_filter *);
|
||||
long WINAPI dshow_filter_GetClassID (dshow_filter *, CLSID *);
|
||||
long WINAPI dshow_filter_Stop (dshow_filter *);
|
||||
long WINAPI dshow_filter_Pause (dshow_filter *);
|
||||
long WINAPI dshow_filter_Run (dshow_filter *, REFERENCE_TIME);
|
||||
long WINAPI dshow_filter_GetState (dshow_filter *, DWORD, FILTER_STATE *);
|
||||
long WINAPI dshow_filter_SetSyncSource (dshow_filter *, IReferenceClock *);
|
||||
long WINAPI dshow_filter_GetSyncSource (dshow_filter *, IReferenceClock **);
|
||||
long WINAPI dshow_filter_EnumPins (dshow_filter *, IEnumPins **);
|
||||
long WINAPI dshow_filter_FindPin (dshow_filter *, const wchar_t *, IPin **);
|
||||
long WINAPI dshow_filter_QueryFilterInfo(dshow_filter *, FILTER_INFO *);
|
||||
long WINAPI dshow_filter_JoinFilterGraph(dshow_filter *, IFilterGraph *, const wchar_t *);
|
||||
long WINAPI dshow_filter_QueryVendorInfo(dshow_filter *, wchar_t **);
|
||||
|
||||
void dshow_filter_Destroy(dshow_filter *);
|
||||
dshow_filter *dshow_filter_Create (void *, void *, enum dshowDeviceType);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* dshow_enum_pins
|
||||
****************************************************************************/
|
||||
struct dshow_enum_pins {
|
||||
IEnumPinsVtbl *vtbl;
|
||||
long ref;
|
||||
int pos;
|
||||
dshow_pin *pin;
|
||||
dshow_filter *filter;
|
||||
};
|
||||
|
||||
long WINAPI dshow_enum_pins_QueryInterface(dshow_enum_pins *, const GUID *, void **);
|
||||
unsigned long WINAPI dshow_enum_pins_AddRef (dshow_enum_pins *);
|
||||
unsigned long WINAPI dshow_enum_pins_Release (dshow_enum_pins *);
|
||||
long WINAPI dshow_enum_pins_Next (dshow_enum_pins *, unsigned long, IPin **, unsigned long *);
|
||||
long WINAPI dshow_enum_pins_Skip (dshow_enum_pins *, unsigned long);
|
||||
long WINAPI dshow_enum_pins_Reset (dshow_enum_pins *);
|
||||
long WINAPI dshow_enum_pins_Clone (dshow_enum_pins *, dshow_enum_pins **);
|
||||
|
||||
void dshow_enum_pins_Destroy(dshow_enum_pins *);
|
||||
dshow_enum_pins *dshow_enum_pins_Create (dshow_pin *pin, dshow_filter *filter);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,339 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
struct dummy_ctx {
|
||||
int fd;
|
||||
int ev_fd;
|
||||
uint64_t seek_offset;
|
||||
uint64_t file_size_total;
|
||||
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 ssize_t get_file_size(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
off_t size = 0;
|
||||
if (!path) {
|
||||
return -1;
|
||||
}
|
||||
if (stat(path, &st) < 0) {
|
||||
printf("%s stat error: %s\n", path, strerror(errno));
|
||||
} else {
|
||||
size = st.st_size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static void *uvc_dummy_open(struct uvc_ctx *uvc, const char *dev, struct uvc_config *conf)
|
||||
{
|
||||
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;
|
||||
c->ev_fd = eventfd(0, 0);
|
||||
if (c->ev_fd == -1) {
|
||||
printf("eventfd failed %d\n", errno);
|
||||
goto failed;
|
||||
}
|
||||
c->file_size_total = get_file_size(dev);
|
||||
|
||||
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)
|
||||
{
|
||||
uint64_t 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->ev_fd, ¬ify, sizeof(uint64_t)) != sizeof(uint64_t)) {
|
||||
printf("%s failed to notify ev_fd=%d, %d\n", __func__, c->ev_fd, errno);
|
||||
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;
|
||||
uint64_t notify;
|
||||
struct timespec ts;
|
||||
struct dummy_ctx *c = (struct dummy_ctx *)uvc->opaque;
|
||||
|
||||
if (read(c->ev_fd, ¬ify, sizeof(notify)) != sizeof(uint64_t)) {
|
||||
printf("failed to read from notify %d\n", errno);
|
||||
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=%zd, len=%zu, errno=%d, ptr=%p\n", ret, len, errno, frame->data[i]);
|
||||
return -1;
|
||||
}
|
||||
c->seek_offset += len;
|
||||
if (c->seek_offset > c->file_size_total) {
|
||||
printf("read file exceed total file size\n");
|
||||
return -1;
|
||||
}
|
||||
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->ev_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->ev_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");
|
||||
}
|
||||
frame = video_frame_create(uvc->conf.format, uvc->conf.width, uvc->conf.height, MEDIA_MEM_DEEP);
|
||||
if (!frame) {
|
||||
printf("video_frame_create failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
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");
|
||||
break;
|
||||
}
|
||||
uvc->on_video_frame(uvc, frame);
|
||||
}
|
||||
video_frame_destroy(frame);
|
||||
uvc_dummy_poll_deinit(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int uvc_dummy_start_stream(struct uvc_ctx *uvc)
|
||||
{
|
||||
uint64_t 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->ev_fd, ¬ify, sizeof(uint64_t)) != sizeof(uint64_t)) {
|
||||
printf("%s failed to notify ev_fd=%d, %d\n", __func__, c->ev_fd, errno);
|
||||
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;
|
||||
uint64_t notify = '1';
|
||||
|
||||
if (!c->is_streaming) {
|
||||
printf("dummy stream stopped already!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(c->ev_fd, ¬ify, sizeof(uint64_t)) != sizeof(uint64_t)) {
|
||||
printf("%s failed to notify ev_fd=%d, %d\n", __func__, c->ev_fd, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uvc->on_video_frame) {
|
||||
c->is_streaming = false;
|
||||
thread_join(c->thread);
|
||||
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 int uvc_dummy_ioctl(struct uvc_ctx *uvc, unsigned long int cmd, ...)
|
||||
{
|
||||
printf("uvc_dummy_ioctl unsupport cmd!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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->ev_fd);
|
||||
free(c);
|
||||
}
|
||||
|
||||
struct uvc_ops dummy_ops = {
|
||||
._open = uvc_dummy_open,
|
||||
._close = uvc_dummy_close,
|
||||
.ioctl = uvc_dummy_ioctl,
|
||||
.start_stream = uvc_dummy_start_stream,
|
||||
.stop_stream = uvc_dummy_stop_stream,
|
||||
.query_frame = uvc_dummy_query_frame,
|
||||
};
|
||||
@@ -1,165 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined (OS_LINUX)
|
||||
extern struct uvc_ops dummy_ops;
|
||||
extern struct uvc_ops v4l2_ops;
|
||||
extern struct uvc_ops ucam_ops;
|
||||
#elif defined (OS_WINDOWS)
|
||||
extern struct uvc_ops dshow_ops;
|
||||
#endif
|
||||
|
||||
enum uvc_type {
|
||||
#if defined (OS_LINUX)
|
||||
UVC_TYPE_DUMMY,
|
||||
UVC_TYPE_V4L2,
|
||||
UVC_TYPE_UCAM,
|
||||
#elif defined (OS_WINDOWS)
|
||||
UVC_TYPE_DSHOW,
|
||||
#endif
|
||||
UVC_TYPE_MAX,
|
||||
};
|
||||
|
||||
static struct uvc_ops *uvc_ops[] = {
|
||||
#if defined (OS_LINUX)
|
||||
&dummy_ops,
|
||||
&v4l2_ops,
|
||||
&ucam_ops,
|
||||
#elif defined (OS_WINDOWS)
|
||||
&dshow_ops,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct uvc_ctx *uvc_open(const char *dev, struct uvc_config *conf)
|
||||
{
|
||||
enum uvc_type type;
|
||||
struct uvc_ctx *uvc;
|
||||
#if defined (OS_LINUX)
|
||||
type = UVC_TYPE_V4L2;
|
||||
//type = UVC_TYPE_UCAM;
|
||||
#elif defined (OS_WINDOWS)
|
||||
type = UVC_TYPE_DSHOW;
|
||||
#endif
|
||||
if (!dev) {
|
||||
type = UVC_TYPE_DUMMY;
|
||||
dev = conf->dev_name;
|
||||
printf("%s:%d open dummy device\n", __func__, __LINE__);
|
||||
}
|
||||
if (!conf) {
|
||||
printf("%s:%d invalid paraments!\n", __func__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
uvc = (struct uvc_ctx *)calloc(1, sizeof(struct uvc_ctx));
|
||||
if (!uvc) {
|
||||
printf("malloc failed!\n");
|
||||
return NULL;
|
||||
}
|
||||
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);
|
||||
goto failed;
|
||||
}
|
||||
return uvc;
|
||||
failed:
|
||||
free(uvc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int uvc_query_frame(struct uvc_ctx *uvc, struct video_frame *frame)
|
||||
{
|
||||
if (!uvc || !frame) {
|
||||
printf("%s:%d invalid paraments!\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
return uvc->ops->query_frame(uvc, frame);
|
||||
}
|
||||
|
||||
int uvc_start_stream(struct uvc_ctx *uvc, video_frame_cb *cb)
|
||||
{
|
||||
if (!uvc) {
|
||||
printf("%s:%d invalid paraments!\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
uvc->on_video_frame = cb;
|
||||
return uvc->ops->start_stream(uvc);
|
||||
}
|
||||
|
||||
int uvc_stop_stream(struct uvc_ctx *uvc)
|
||||
{
|
||||
if (!uvc) {
|
||||
printf("%s:%d invalid paraments!\n", __func__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
return uvc->ops->stop_stream(uvc);
|
||||
}
|
||||
|
||||
int uvc_ioctl(struct uvc_ctx *uvc, unsigned long int cmd, ...)
|
||||
{
|
||||
int ret;
|
||||
void *arg;
|
||||
va_list ap;
|
||||
va_start(ap, cmd);
|
||||
arg = va_arg(ap, void *);
|
||||
va_end(ap);
|
||||
|
||||
switch (cmd) {
|
||||
case UVC_GET_CAP:
|
||||
ret = uvc->ops->ioctl(uvc, cmd, NULL);
|
||||
break;
|
||||
case UVC_SET_CTRL: {
|
||||
struct video_ctrl *vctrl;
|
||||
vctrl = (struct video_ctrl *)arg;
|
||||
ret = uvc->ops->ioctl(uvc, vctrl->cmd, vctrl->val);
|
||||
} break;
|
||||
case UVC_SET_CONF: {
|
||||
ret = uvc->ops->ioctl(uvc, cmd, (struct uvc_config *)arg);
|
||||
} break;
|
||||
default:
|
||||
ret = uvc->ops->ioctl(uvc, cmd, arg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void uvc_close(struct uvc_ctx *uvc)
|
||||
{
|
||||
if (!uvc) {
|
||||
printf("%s:%d invalid paraments!\n", __func__, __LINE__);
|
||||
return;
|
||||
}
|
||||
uvc->ops->_close(uvc);
|
||||
free(uvc);
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
#ifndef LIBUVC_H
|
||||
#define LIBUVC_H
|
||||
|
||||
#include <libposix.h>
|
||||
#include <libmedia-io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#if defined (OS_LINUX)
|
||||
#define __USE_LINUX_IOCTL_DEFS
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#define LIBUVC_VERSION "0.2.1"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct uvc_ctx;
|
||||
struct uvc_ops;
|
||||
typedef int (video_frame_cb)(struct uvc_ctx *c, struct video_frame *frame);
|
||||
|
||||
struct uvc_config {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
rational_t fps;
|
||||
enum pixel_format format;
|
||||
char *dev_name;
|
||||
};
|
||||
|
||||
struct uvc_image_quality {
|
||||
int brightness;
|
||||
int contrast;
|
||||
int saturation;
|
||||
int hue;
|
||||
int awb;
|
||||
int gamma;
|
||||
int sharpness;
|
||||
};
|
||||
|
||||
struct uvc_ctx {
|
||||
int fd;
|
||||
struct uvc_config conf;
|
||||
struct uvc_ops *ops;
|
||||
video_frame_cb *on_video_frame;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
struct video_cap {
|
||||
uint8_t desc[32];
|
||||
uint32_t version;
|
||||
};
|
||||
|
||||
struct video_ctrl {
|
||||
uint32_t cmd;
|
||||
uint32_t val;
|
||||
};
|
||||
|
||||
#define UVC_GET_CAP _IOWR('V', 0, struct video_cap)
|
||||
#define UVC_SET_CTRL _IOWR('V', 1, struct video_ctrl)
|
||||
#define UVC_SET_CONF _IOWR('V', 2, struct uvc_config)
|
||||
#define UVC_GET_LUMA _IOWR('V', 3, int *)
|
||||
#define UVC_SET_LUMA _IOWR('V', 4, int)
|
||||
#define UVC_GET_CTRST _IOWR('V', 5, int *)
|
||||
#define UVC_SET_CTRST _IOWR('V', 6, int)
|
||||
#define UVC_GET_SAT _IOWR('V', 7, int *)
|
||||
#define UVC_SET_SAT _IOWR('V', 8, int)
|
||||
#define UVC_GET_HUE _IOWR('V', 9, int *)
|
||||
#define UVC_SET_HUE _IOWR('V', 10, int)
|
||||
#define UVC_GET_AWB _IOWR('V', 11, int *)
|
||||
#define UVC_SET_AWB _IOWR('V', 12, int)
|
||||
#define UVC_GET_GAMMA _IOWR('V', 13, int *)
|
||||
#define UVC_SET_GAMMA _IOWR('V', 14, int)
|
||||
#define UVC_GET_SHARP _IOWR('V', 15, int *)
|
||||
#define UVC_SET_SHARP _IOWR('V', 16, int)
|
||||
|
||||
|
||||
|
||||
struct uvc_ops {
|
||||
void *(*_open)(struct uvc_ctx *uvc, const char *dev, struct uvc_config *conf);
|
||||
void (*_close)(struct uvc_ctx *c);
|
||||
int (*ioctl)(struct uvc_ctx *c, unsigned long int cmd, ...);
|
||||
int (*start_stream)(struct uvc_ctx *c);
|
||||
int (*stop_stream)(struct uvc_ctx *c);
|
||||
int (*query_frame)(struct uvc_ctx *c, struct video_frame *frame);
|
||||
};
|
||||
|
||||
GEAR_API struct uvc_ctx *uvc_open(const char *dev, struct uvc_config *conf);
|
||||
GEAR_API int uvc_ioctl(struct uvc_ctx *c, unsigned long int cmd, ...);
|
||||
GEAR_API void uvc_close(struct uvc_ctx *c);
|
||||
|
||||
/*
|
||||
* active query frame one by one
|
||||
*/
|
||||
GEAR_API int uvc_query_frame(struct uvc_ctx *c, struct video_frame *frame);
|
||||
|
||||
/*
|
||||
* passive get frame when cb is set, otherwise need query frame one by one
|
||||
*/
|
||||
GEAR_API int uvc_start_stream(struct uvc_ctx *uvc, video_frame_cb *cb);
|
||||
GEAR_API int uvc_stop_stream(struct uvc_ctx *uvc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,134 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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 <libfile.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
#define VIDEO_DEV "/dev/video0"
|
||||
#define VIDEO_WIDTH 640
|
||||
#define VIDEO_HEIGHT 480
|
||||
#define OUTPUT_V4L2 "v4l2.yuv"
|
||||
#define OUTPUT_DUMMY "dummy.yuv"
|
||||
|
||||
static struct file *fp;
|
||||
|
||||
static int on_frame(struct uvc_ctx *c, struct video_frame *frm)
|
||||
{
|
||||
static uint64_t last_ms = 0;
|
||||
static int luma = 0;
|
||||
static int i = 0;
|
||||
|
||||
printf("frm[%" PRIu64 "] size=%" PRIu64 ", ts=%" PRIu64 " ms, gap=%" PRIu64 " ms\n",
|
||||
frm->frame_id, frm->total_size, frm->timestamp/1000000, frm->timestamp/1000000 - last_ms);
|
||||
last_ms = frm->timestamp/1000000;
|
||||
luma = 2 * i++;
|
||||
luma *= i%2 ? 1: -1;
|
||||
uvc_ioctl(c, UVC_SET_LUMA, luma);
|
||||
file_write(fp, frm->data[0], frm->total_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int v4l2_test()
|
||||
{
|
||||
struct uvc_ctx *uvc;
|
||||
struct video_frame *frm;
|
||||
struct uvc_config conf = {
|
||||
VIDEO_WIDTH,
|
||||
VIDEO_HEIGHT,
|
||||
{30, 1},
|
||||
PIXEL_FORMAT_YUY2,
|
||||
};
|
||||
uvc = uvc_open(VIDEO_DEV, &conf);
|
||||
if (!uvc) {
|
||||
printf("uvc_open failed!\n");
|
||||
return -1;
|
||||
}
|
||||
//uvc_ioctl(uvc, UVC_SET_CONF, &conf);
|
||||
uvc_ioctl(uvc, UVC_GET_CAP, NULL);
|
||||
frm = video_frame_create(uvc->conf.format, uvc->conf.width, uvc->conf.height, MEDIA_MEM_SHALLOW);
|
||||
if (!frm) {
|
||||
printf("video_frame_create failed!\n");
|
||||
uvc_close(uvc);
|
||||
return -1;
|
||||
}
|
||||
printf("uvc info: %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_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_V4L2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dummy_test()
|
||||
{
|
||||
#if defined (OS_LINUX)
|
||||
struct video_frame *frm;
|
||||
struct uvc_config conf = {
|
||||
320,
|
||||
240,
|
||||
{30, 1},
|
||||
PIXEL_FORMAT_YUY2,
|
||||
"sample_320x240_yuv422p.yuv",
|
||||
};
|
||||
struct uvc_ctx *uvc = uvc_open(NULL, &conf);
|
||||
if (!uvc) {
|
||||
printf("uvc_open failed!\n");
|
||||
return -1;
|
||||
}
|
||||
frm = video_frame_create(uvc->conf.format, uvc->conf.width, uvc->conf.height, MEDIA_MEM_SHALLOW);
|
||||
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);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
v4l2_test();
|
||||
dummy_test();
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,458 +0,0 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
#ifndef USBCAM_H
|
||||
#define USBCAM_H
|
||||
/*
|
||||
* refer to https://github.com/libuvc/libuvc
|
||||
*/
|
||||
|
||||
/** Converts an unaligned four-byte little-endian integer into an int32 */
|
||||
#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
|
||||
/** Converts an unaligned two-byte little-endian integer into an int16 */
|
||||
#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8))
|
||||
/** Converts an int16 into an unaligned two-byte little-endian integer */
|
||||
#define SHORT_TO_SW(s, p) \
|
||||
(p)[0] = (s); \
|
||||
(p)[1] = (s) >> 8;
|
||||
/** Converts an int32 into an unaligned four-byte little-endian integer */
|
||||
#define INT_TO_DW(i, p) \
|
||||
(p)[0] = (i); \
|
||||
(p)[1] = (i) >> 8; \
|
||||
(p)[2] = (i) >> 16; \
|
||||
(p)[3] = (i) >> 24;
|
||||
|
||||
/** VideoControl interface descriptor subtype (A.5) */
|
||||
enum uvc_vc_desc_subtype {
|
||||
UVC_VC_DESCRIPTOR_UNDEFINED = 0x00,
|
||||
UVC_VC_HEADER = 0x01,
|
||||
UVC_VC_INPUT_TERMINAL = 0x02,
|
||||
UVC_VC_OUTPUT_TERMINAL = 0x03,
|
||||
UVC_VC_SELECTOR_UNIT = 0x04,
|
||||
UVC_VC_PROCESSING_UNIT = 0x05,
|
||||
UVC_VC_EXTENSION_UNIT = 0x06
|
||||
};
|
||||
|
||||
/** VideoStreaming interface descriptor subtype (A.6) */
|
||||
enum uvc_vs_desc_subtype {
|
||||
UVC_VS_UNDEFINED = 0x00,
|
||||
UVC_VS_INPUT_HEADER = 0x01,
|
||||
UVC_VS_OUTPUT_HEADER = 0x02,
|
||||
UVC_VS_STILL_IMAGE_FRAME = 0x03,
|
||||
UVC_VS_FORMAT_UNCOMPRESSED = 0x04,
|
||||
UVC_VS_FRAME_UNCOMPRESSED = 0x05,
|
||||
UVC_VS_FORMAT_MJPEG = 0x06,
|
||||
UVC_VS_FRAME_MJPEG = 0x07,
|
||||
UVC_VS_FORMAT_MPEG2TS = 0x0a,
|
||||
UVC_VS_FORMAT_DV = 0x0c,
|
||||
UVC_VS_COLORFORMAT = 0x0d,
|
||||
UVC_VS_FORMAT_FRAME_BASED = 0x10,
|
||||
UVC_VS_FRAME_FRAME_BASED = 0x11,
|
||||
UVC_VS_FORMAT_STREAM_BASED = 0x12
|
||||
};
|
||||
|
||||
/** UVC endpoint descriptor subtype (A.7) */
|
||||
enum uvc_ep_desc_subtype {
|
||||
UVC_EP_UNDEFINED = 0x00,
|
||||
UVC_EP_GENERAL = 0x01,
|
||||
UVC_EP_ENDPOINT = 0x02,
|
||||
UVC_EP_INTERRUPT = 0x03
|
||||
};
|
||||
|
||||
/** VideoControl interface control selector (A.9.1) */
|
||||
enum uvc_vc_ctrl_selector {
|
||||
UVC_VC_CONTROL_UNDEFINED = 0x00,
|
||||
UVC_VC_VIDEO_POWER_MODE_CONTROL = 0x01,
|
||||
UVC_VC_REQUEST_ERROR_CODE_CONTROL = 0x02
|
||||
};
|
||||
|
||||
/** Terminal control selector (A.9.2) */
|
||||
enum uvc_term_ctrl_selector {
|
||||
UVC_TE_CONTROL_UNDEFINED = 0x00
|
||||
};
|
||||
|
||||
/** Selector unit control selector (A.9.3) */
|
||||
enum uvc_su_ctrl_selector {
|
||||
UVC_SU_CONTROL_UNDEFINED = 0x00,
|
||||
UVC_SU_INPUT_SELECT_CONTROL = 0x01
|
||||
};
|
||||
|
||||
/** Extension unit control selector (A.9.6) */
|
||||
enum uvc_xu_ctrl_selector {
|
||||
UVC_XU_CONTROL_UNDEFINED = 0x00
|
||||
};
|
||||
|
||||
/** VideoStreaming interface control selector (A.9.7) */
|
||||
enum uvc_vs_ctrl_selector {
|
||||
UVC_VS_CONTROL_UNDEFINED = 0x00,
|
||||
UVC_VS_PROBE_CONTROL = 0x01,
|
||||
UVC_VS_COMMIT_CONTROL = 0x02,
|
||||
UVC_VS_STILL_PROBE_CONTROL = 0x03,
|
||||
UVC_VS_STILL_COMMIT_CONTROL = 0x04,
|
||||
UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05,
|
||||
UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06,
|
||||
UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07,
|
||||
UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08,
|
||||
UVC_VS_SYNC_DELAY_CONTROL = 0x09
|
||||
};
|
||||
|
||||
/** USB terminal type (B.1) */
|
||||
enum uvc_term_type {
|
||||
UVC_TT_VENDOR_SPECIFIC = 0x0100,
|
||||
UVC_TT_STREAMING = 0x0101
|
||||
};
|
||||
|
||||
/** Input terminal type (B.2) */
|
||||
enum uvc_it_type {
|
||||
UVC_ITT_VENDOR_SPECIFIC = 0x0200,
|
||||
UVC_ITT_CAMERA = 0x0201,
|
||||
UVC_ITT_MEDIA_TRANSPORT_INPUT = 0x0202
|
||||
};
|
||||
|
||||
/** Output terminal type (B.3) */
|
||||
enum uvc_ot_type {
|
||||
UVC_OTT_VENDOR_SPECIFIC = 0x0300,
|
||||
UVC_OTT_DISPLAY = 0x0301,
|
||||
UVC_OTT_MEDIA_TRANSPORT_OUTPUT = 0x0302
|
||||
};
|
||||
|
||||
/** External terminal type (B.4) */
|
||||
enum uvc_et_type {
|
||||
UVC_EXTERNAL_VENDOR_SPECIFIC = 0x0400,
|
||||
UVC_COMPOSITE_CONNECTOR = 0x0401,
|
||||
UVC_SVIDEO_CONNECTOR = 0x0402,
|
||||
UVC_COMPONENT_CONNECTOR = 0x0403
|
||||
};
|
||||
|
||||
/** Status packet type (2.4.2.2) */
|
||||
enum uvc_status_type {
|
||||
UVC_STATUS_TYPE_CONTROL = 1,
|
||||
UVC_STATUS_TYPE_STREAMING = 2
|
||||
};
|
||||
|
||||
enum uvc_status_class {
|
||||
UVC_STATUS_CLASS_CONTROL = 0x10,
|
||||
UVC_STATUS_CLASS_CONTROL_CAMERA = 0x11,
|
||||
UVC_STATUS_CLASS_CONTROL_PROCESSING = 0x12,
|
||||
};
|
||||
|
||||
enum uvc_status_attribute {
|
||||
UVC_STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00,
|
||||
UVC_STATUS_ATTRIBUTE_INFO_CHANGE = 0x01,
|
||||
UVC_STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02,
|
||||
UVC_STATUS_ATTRIBUTE_UNKNOWN = 0xff
|
||||
};
|
||||
|
||||
enum uvc_frame_format {
|
||||
UVC_FRAME_FORMAT_UNKNOWN = 0,
|
||||
/** Any supported format */
|
||||
UVC_FRAME_FORMAT_ANY = 0,
|
||||
UVC_FRAME_FORMAT_UNCOMPRESSED,
|
||||
UVC_FRAME_FORMAT_COMPRESSED,
|
||||
/** YUYV/YUV2/YUV422: YUV encoding with one luminance value per pixel and
|
||||
* one UV (chrominance) pair for every two pixels.
|
||||
*/
|
||||
UVC_FRAME_FORMAT_YUYV,
|
||||
UVC_FRAME_FORMAT_UYVY,
|
||||
/** 24-bit RGB */
|
||||
UVC_FRAME_FORMAT_RGB,
|
||||
UVC_FRAME_FORMAT_BGR,
|
||||
/** Motion-JPEG (or JPEG) encoded images */
|
||||
UVC_FRAME_FORMAT_MJPEG,
|
||||
UVC_FRAME_FORMAT_GRAY8,
|
||||
UVC_FRAME_FORMAT_BY8,
|
||||
/** Number of formats understood */
|
||||
UVC_FRAME_FORMAT_COUNT,
|
||||
};
|
||||
|
||||
/** Payload header flags (2.4.3.3) */
|
||||
#define UVC_STREAM_EOH (1 << 7)
|
||||
#define UVC_STREAM_ERR (1 << 6)
|
||||
#define UVC_STREAM_STI (1 << 5)
|
||||
#define UVC_STREAM_RES (1 << 4)
|
||||
#define UVC_STREAM_SCR (1 << 3)
|
||||
#define UVC_STREAM_PTS (1 << 2)
|
||||
#define UVC_STREAM_EOF (1 << 1)
|
||||
#define UVC_STREAM_FID (1 << 0)
|
||||
|
||||
/** Control capabilities (4.1.2) */
|
||||
#define UVC_CONTROL_CAP_GET (1 << 0)
|
||||
#define UVC_CONTROL_CAP_SET (1 << 1)
|
||||
#define UVC_CONTROL_CAP_DISABLED (1 << 2)
|
||||
#define UVC_CONTROL_CAP_AUTOUPDATE (1 << 3)
|
||||
#define UVC_CONTROL_CAP_ASYNCHRONOUS (1 << 4)
|
||||
|
||||
struct ucam_frame_desc {
|
||||
/** Type of frame, such as JPEG frame or uncompressed frme */
|
||||
enum uvc_vs_desc_subtype bDescriptorSubtype;
|
||||
/** Index of the frame within the list of specs available for this format */
|
||||
uint8_t bFrameIndex;
|
||||
uint8_t bmCapabilities;
|
||||
/** Image width */
|
||||
uint16_t wWidth;
|
||||
/** Image height */
|
||||
uint16_t wHeight;
|
||||
/** Bitrate of corresponding stream at minimal frame rate */
|
||||
uint32_t dwMinBitRate;
|
||||
/** Bitrate of corresponding stream at maximal frame rate */
|
||||
uint32_t dwMaxBitRate;
|
||||
/** Maximum number of bytes for a video frame */
|
||||
uint32_t dwMaxVideoFrameBufferSize;
|
||||
/** Default frame interval (in 100ns units) */
|
||||
uint32_t dwDefaultFrameInterval;
|
||||
/** Minimum frame interval for continuous mode (100ns units) */
|
||||
uint32_t dwMinFrameInterval;
|
||||
/** Maximum frame interval for continuous mode (100ns units) */
|
||||
uint32_t dwMaxFrameInterval;
|
||||
/** Granularity of frame interval range for continuous mode (100ns) */
|
||||
uint32_t dwFrameIntervalStep;
|
||||
/** Frame intervals */
|
||||
uint8_t bFrameIntervalType;
|
||||
/** number of bytes per line */
|
||||
uint32_t dwBytesPerLine;
|
||||
/** Available frame rates, zero-terminated (in 100ns units) */
|
||||
uint32_t *intervals;
|
||||
};
|
||||
|
||||
struct ucam_format_desc {
|
||||
/** Type of image stream, such as JPEG or uncompressed. */
|
||||
enum uvc_vs_desc_subtype bDescriptorSubtype;
|
||||
/** Identifier of this format within the VS interface's format list */
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bNumFrameDescriptors;
|
||||
/** Format specifier */
|
||||
union {
|
||||
uint8_t guidFormat[16];
|
||||
uint8_t fourccFormat[4];
|
||||
};
|
||||
/** Format-specific data */
|
||||
union {
|
||||
/** BPP for uncompressed stream */
|
||||
uint8_t bBitsPerPixel;
|
||||
/** Flags for JPEG stream */
|
||||
uint8_t bmFlags;
|
||||
};
|
||||
/** Default {uvc_frame_desc} to choose given this format */
|
||||
uint8_t bDefaultFrameIndex;
|
||||
uint8_t bAspectRatioX;
|
||||
uint8_t bAspectRatioY;
|
||||
uint8_t bmInterlaceFlags;
|
||||
uint8_t bCopyProtect;
|
||||
uint8_t bVariableSize;
|
||||
/** Available frame specifications for this format */
|
||||
struct ucam_frame_desc *frame_descs;
|
||||
};
|
||||
|
||||
struct ucam_input_terminal {
|
||||
/** Index of the terminal within the device */
|
||||
uint8_t bTerminalID;
|
||||
/** Type of terminal (e.g., camera) */
|
||||
enum uvc_it_type wTerminalType;
|
||||
uint16_t wObjectiveFocalLengthMin;
|
||||
uint16_t wObjectiveFocalLengthMax;
|
||||
uint16_t wOcularFocalLength;
|
||||
/** Camera controls (meaning of bits given in {uvc_ct_ctrl_selector}) */
|
||||
uint64_t bmControls;
|
||||
};
|
||||
|
||||
struct ucam_stream_ctrl {
|
||||
uint16_t bmHint;
|
||||
uint8_t bFormatIndex;
|
||||
uint8_t bFrameIndex;
|
||||
uint32_t dwFrameInterval;
|
||||
uint16_t wKeyFrameRate;
|
||||
uint16_t wPFrameRate;
|
||||
uint16_t wCompQuality;
|
||||
uint16_t wCompWindowSize;
|
||||
uint16_t wDelay;
|
||||
uint32_t dwMaxVideoFrameSize;
|
||||
uint32_t dwMaxPayloadTransferSize;
|
||||
uint32_t dwClockFrequency;
|
||||
uint8_t bmFramingInfo;
|
||||
uint8_t bPreferredVersion;
|
||||
uint8_t bMinVersion;
|
||||
uint8_t bMaxVersion;
|
||||
uint8_t bInterfaceNumber;
|
||||
};
|
||||
|
||||
struct ucam_frame {
|
||||
/** Image data for this frame */
|
||||
void *data;
|
||||
/** Size of image data buffer */
|
||||
size_t data_bytes;
|
||||
/** Width of image in pixels */
|
||||
uint32_t width;
|
||||
/** Height of image in pixels */
|
||||
uint32_t height;
|
||||
/** Pixel data format */
|
||||
enum uvc_frame_format frame_format;
|
||||
/** Number of bytes per horizontal line (undefined for compressed format) */
|
||||
size_t step;
|
||||
/** Frame number (may skip, but is strictly monotonically increasing) */
|
||||
uint32_t sequence;
|
||||
/** Estimate of system time when the device started capturing the image */
|
||||
struct timeval capture_time;
|
||||
/** Handle on the device that produced the image.
|
||||
* @warning You must not call any uvc_* functions during a callback. */
|
||||
//uvc_device_handle_t *source;
|
||||
/** Is the data buffer owned by the library?
|
||||
* If 1, the data buffer can be arbitrarily reallocated by frame conversion
|
||||
* functions.
|
||||
* If 0, the data buffer will not be reallocated or freed by the library.
|
||||
* Set this field to zero if you are supplying the buffer.
|
||||
*/
|
||||
uint8_t library_owns_data;
|
||||
} uvc_frame_t;
|
||||
|
||||
struct ucam_ctrl {
|
||||
#if 0
|
||||
struct uvc_device_info *parent;
|
||||
struct uvc_input_terminal *input_term_descs;
|
||||
// struct uvc_output_terminal *output_term_descs;
|
||||
struct uvc_selector_unit *selector_unit_descs;
|
||||
struct uvc_processing_unit *processing_unit_descs;
|
||||
struct uvc_extension_unit *extension_unit_descs;
|
||||
#endif
|
||||
struct ucam_input_terminal term;
|
||||
struct ucam_selector_unit {
|
||||
/** Index of the selector unit within the device */
|
||||
uint8_t bUnitID;
|
||||
} selector;
|
||||
|
||||
struct ucam_processing_unit {
|
||||
struct uvc_processing_unit *prev, *next;
|
||||
/** Index of the processing unit within the device */
|
||||
uint8_t bUnitID;
|
||||
/** Index of the terminal from which the device accepts images */
|
||||
uint8_t bSourceID;
|
||||
/** Processing controls (meaning of bits given in {uvc_pu_ctrl_selector}) */
|
||||
uint64_t bmControls;
|
||||
} processing;
|
||||
|
||||
struct ucam_extension_unit {
|
||||
/** Index of the extension unit within the device */
|
||||
uint8_t bUnitID;
|
||||
/** GUID identifying the extension unit */
|
||||
uint8_t guidExtensionCode[16];
|
||||
/** Bitmap of available controls (manufacturer-dependent) */
|
||||
uint64_t bmControls;
|
||||
} extension;
|
||||
|
||||
uint16_t bcdUVC;
|
||||
uint32_t dwClockFrequency;
|
||||
uint8_t bEndpointAddress;
|
||||
/** Interface number */
|
||||
uint8_t bInterfaceNumber;
|
||||
};
|
||||
|
||||
struct ucam_stream {
|
||||
/** Interface number */
|
||||
uint8_t bInterfaceNumber;
|
||||
/** Video formats that this interface provides */
|
||||
struct uvc_format_desc *format_descs;
|
||||
/** USB endpoint to use when communicating with this interface */
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bTerminalLink;
|
||||
};
|
||||
|
||||
struct ucam_descriptor {
|
||||
/** Vendor ID */
|
||||
uint16_t idVendor;
|
||||
/** Product ID */
|
||||
uint16_t idProduct;
|
||||
/** UVC compliance level, e.g. 0x0100 (1.0), 0x0110 */
|
||||
uint16_t bcdUVC;
|
||||
/** Serial number (null if unavailable) */
|
||||
const char *serialNumber;
|
||||
/** Device-reported manufacturer name (or null) */
|
||||
const char *manufacturer;
|
||||
/** Device-reporter product name (or null) */
|
||||
const char *product;
|
||||
};
|
||||
|
||||
struct ucam_dev_info {
|
||||
struct libusb_config_descriptor *usb_conf;
|
||||
struct ucam_ctrl control;
|
||||
/** VideoStream interface */
|
||||
struct ucam_stream stream;
|
||||
struct ucam_stream_ctrl stream_ctrl;
|
||||
struct ucam_format_desc format;
|
||||
struct ucam_frame_desc frame;
|
||||
struct ucam_descriptor descriptor;
|
||||
};
|
||||
|
||||
typedef void(ucam_frame_callback_t)(struct ucam_frame *frame, void *user_ptr);
|
||||
typedef void(uvc_status_callback_t)(enum uvc_status_class status_class,
|
||||
int event,
|
||||
int selector,
|
||||
enum uvc_status_attribute status_attribute,
|
||||
void *data, size_t data_len,
|
||||
void *user_ptr);
|
||||
|
||||
typedef void(uvc_button_callback_t)(int button,
|
||||
int state,
|
||||
void *user_ptr);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
set a high number of transfer buffers. This uses a lot of ram, but
|
||||
avoids problems with scheduling delays on slow boards causing missed
|
||||
transfers. A better approach may be to make the transfer thread FIFO
|
||||
scheduled (if we have root).
|
||||
We could/should change this to allow reduce it to, say, 5 by default
|
||||
and then allow the user to change the number of buffers as required.
|
||||
*/
|
||||
#define LIBUVC_NUM_TRANSFER_BUFS 100
|
||||
|
||||
#define LIBUVC_XFER_BUF_SIZE ( 16 * 1024 * 1024 )
|
||||
|
||||
struct ucam_stream_handle {
|
||||
struct uvc_device_handle *devh;
|
||||
struct ucam_stream_handle *prev, *next;
|
||||
struct uvc_streaming_interface *stream_if;
|
||||
|
||||
/** if true, stream is running (streaming video to host) */
|
||||
uint8_t running;
|
||||
/** Current control block */
|
||||
struct ucam_stream_ctrl cur_ctrl;
|
||||
|
||||
/* listeners may only access hold*, and only when holding a
|
||||
* lock on cb_mutex (probably signaled with cb_cond) */
|
||||
uint8_t fid;
|
||||
uint32_t seq, hold_seq;
|
||||
uint32_t pts, hold_pts;
|
||||
uint32_t last_scr, hold_last_scr;
|
||||
size_t got_bytes, hold_bytes;
|
||||
uint8_t *outbuf, *holdbuf;
|
||||
pthread_mutex_t cb_mutex;
|
||||
pthread_cond_t cb_cond;
|
||||
pthread_t cb_thread;
|
||||
uint32_t last_polled_seq;
|
||||
ucam_frame_callback_t *user_cb;
|
||||
void *user_ptr;
|
||||
struct libusb_transfer *transfers[LIBUVC_NUM_TRANSFER_BUFS];
|
||||
uint8_t *transfer_bufs[LIBUVC_NUM_TRANSFER_BUFS];
|
||||
struct ucam_frame frame;
|
||||
enum uvc_frame_format frame_format;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user