海思uvc_app源码学习笔记
前言
海思mpp例子里面的uvc_app源码学习笔记。
看了半天,以为是3516读取usb摄像头数据的,结果是实现一个usb摄像头的。可以搞个公对公的usb线测试下。
大概流程是从摄像头获取图像,进行处理或者编码,按照v4l2的标准操作字符设备实现图像的输出。基本思想是一个生产者和消费者的模型,用两个队列做图像缓冲区。
流程
读取配置文件,使用了一个开源的库iniparser库,配置文件格式是ini文件。
if (create_config_svc("./uvc_app.conf") != 0)
{
goto ERR;
}
UVC全称为USB Video Class,即:USB视频类,UAC全称为USB Audio Class,即:USB音频类。
创建分别用于存放图像或者声音的缓存,实质上是一个链表实现的队列。
if (create_uvc_cache() != 0)
{
goto ERR;
}
if (g_uac)
{
if (create_uac_cache() != 0)
{
goto ERR;
}
}
初始化以及运行业务程序。
if (get_hicamera()->init() != 0 ||
get_hicamera()->open() != 0 ||
get_hicamera()->run() != 0)
{
goto ERR;
}
退出时释放资源等等。
hicamera
hicamera结构体可以看作相机或者最顶层方法的封装,包括初始化、打开、关闭、运行等。
static hicamera __hi_camera =
{
.init = __init,
.open = __open,
.close = __close,
.run = __run,
};
init初始化方法:初始化hiuvc、histream、hiuac、hiaudio。
open打开方法:打开hiuvc、hiaudio。
close关闭方法:关闭hiuvc、histream、hiuac、hiaudio。
run运行方法:启动两个线程运行hiuvc、hiuac。
histream
typedef struct histream
{
struct stream_control_ops *mpi_sc_ops;
struct processing_unit_ops *mpi_pu_ops;
struct input_terminal_ops *mpi_it_ops;
struct extension_unit_ops *mpi_eu_ops;
int streaming;
int exposure_auto_stall;
int brightness_stall;
} histream;
static struct histream __hi_stream = {
.mpi_sc_ops = NULL,
.mpi_pu_ops = NULL,
.mpi_it_ops = NULL,
.mpi_eu_ops = NULL,
.streaming = 0,
.exposure_auto_stall = 0,
.brightness_stall = 0,
};
结构体有些复杂,这里是和mpp驱动相关的部分,在sample_venc_config函数中进行注册。
HI_VOID sample_venc_config(HI_VOID)
{
printf("\n@@@@@ HiUVC App Sample @@@@@\n\n");
histream_register_mpi_ops(&venc_sc_ops, &venc_pu_ops, &venc_it_ops, HI_NULL);
}
static struct stream_control_ops venc_sc_ops = {
.init = sample_venc_init,
.startup = sample_venc_startup,
.shutdown = sample_venc_shutdown,
.set_idr = sample_venc_set_idr,
.set_property = sample_venc_set_property,
};
这里是mpp初始化等等操作,是常规的处理流程。
sample_venc_init mpp初始化:初始化SYS和VB。
sample_venc_startup mpp流媒体开始:VPSS、VENC初始化,使用系统绑定处理图像,可以将原始数据或者编码后的数据放到UVC的队列里面。
sample_venc_shutdown mpp结束:关闭或者停止。
UVC图像节点设置的函数为sample_yuv_dump和SAMPLE_COMM_VENC_SaveData,前者将YUV数据放入节点中,后者将编码后的数据放入节点中,具体执行哪个函数由一个全局变量encoder_property控制,可以上位机进行。两个函数都是从free_queue节点中获取空闲图像缓存节点,存放数据放入到ok_queue队列中,是生产者。
static HI_VOID sample_yuv_dump(VIDEO_FRAME_S *pVBuf, FILE *g_pfd)
{
PIXEL_FORMAT_E enPixelFormat = pVBuf->enPixelFormat;
uvc_cache_t *uvc_cache = HI_NULL;
frame_node_t *fnode = HI_NULL;
sample_yuv_get_buf_size(pVBuf, &g_u32Size);
g_pUserPageAddr = (HI_CHAR*)HI_MPI_SYS_Mmap(pVBuf->u64PhyAddr[0], g_u32Size);
if (HI_NULL == g_pUserPageAddr)
{
goto ERR;
}
//get free cache node
uvc_cache = uvc_cache_get();
if (uvc_cache)
{
get_node_from_queue(uvc_cache->free_queue, &fnode);
}
if (!fnode)
{
goto ERR;
}
if (PIXEL_FORMAT_YVU_SEMIPLANAR_422 == enPixelFormat)
{
if (sample_yuv_sp422_to_p422(pVBuf, fnode) != HI_SUCCESS)
{
goto ERR;
}
}
else if (PIXEL_FORMAT_YVU_SEMIPLANAR_420 == enPixelFormat)
{
sample_yuv_sp420_to_p420(pVBuf, fnode);
}
else
{
}
ERR:
if (fnode)
{
put_node_to_queue(uvc_cache->ok_queue, fnode);
}
if (g_pUserPageAddr)
{
HI_MPI_SYS_Munmap(g_pUserPageAddr, g_u32Size);
g_pUserPageAddr = HI_NULL;
}
return;
}
其他操作是一些设置相关的,暂不分析。
hiuvc
hiuvc是对UVC类操作的封装,这里涉及到V4L2的一些知识。
static hiuvc __hi_uvc =
{
.init = __init,
.open = __open,
.close = __close,
.run = __run,
};
打开方法:打开设备及一些初始化操作。
static int __open()
{
const char *devpath = "/dev/video0";
return open_uvc_device(devpath);
}
run运行方法:两个线程以死循环的形式分别跑了run_uvc_data,run_uvc_device函数。
这里需要注意的点是run_uvc_data和run_uvc_device都是使用select在等待前面open的同一个文件描述符,不过一个监听的是可以向其中写入数据,一个监听的是异常情况。
uvc_video_process_userptr是设备文件描述符可写时的处理函数,表示可以将图像视频数据输出,这里使用V4L2_MEMORY_USERPTR用户指针模式,内存由用户进行分配,V4L2_BUF_TYPE_VIDEO_OUTPUT表示视频图像输出。
static int uvc_video_process_userptr(struct uvc_device* dev)
{
struct v4l2_buffer buf;
int ret;
// INFO("#############uvc_video_process_userptr\n");
memset(&buf, 0, sizeof buf);
buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
buf.memory = V4L2_MEMORY_USERPTR;
if ((ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf)) < 0)
{
return ret;
}
uvc_video_fill_buffer_userptr(dev, &buf);
if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0)
{
LOG("Unable to requeue buffer: %s (%d).\n", strerror(errno), errno);
return ret;
}
return 0;
}
uvc_video_fill_buffer_userptr先获取用于输出图像的缓存,然后从ok_queue队列中取出已经存放好的图像,__waited_node数组指针用于存放上次的图像节点,然后放到free_queue队列中,表示该节点空闲可以再次使用,是消费者。
run_uvc_device运行uvc_events_process函数,主要是对UVC事件的处理函数。
模块分析
把代码大概划分一下模块
- 分享
- 举报
-
2023-08-22 16:45:59
-
浏览量:3750次2020-08-06 20:14:59
-
浏览量:3279次2020-08-17 19:59:15
-
浏览量:2846次2020-08-10 09:27:13
-
浏览量:942次2024-01-24 17:37:01
-
浏览量:3426次2020-07-27 16:05:14
-
浏览量:3713次2021-12-10 16:59:31
-
浏览量:3121次2020-03-11 13:56:02
-
浏览量:3185次2020-07-29 11:49:25
-
浏览量:2703次2020-07-29 15:54:29
-
浏览量:4448次2020-07-31 13:45:09
-
浏览量:2903次2020-07-29 15:38:57
-
浏览量:608次2023-06-02 17:41:00
-
浏览量:1926次2020-08-28 16:40:19
-
浏览量:1359次2024-02-19 15:26:47
-
2024-01-11 15:44:19
-
浏览量:1247次2023-03-10 16:37:14
-
浏览量:532次2023-07-05 10:16:37
-
浏览量:559次2023-06-03 15:58:59
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
fly
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明