浅析 Hi MPP 中的 uvc_app

摘要:
数据功能块负责一件事。流启动后,它将侦听视频设备描述符。当它准备好时,它会传递UVC_video_process_userptr将一帧数据推送到UVC视频缓冲区,而特定的函数实现会先跳过它。当不需要数据流时触发UVC _ EVENT_禁用STREAMOFF事件_ UVC_Video()停止流。到目前为止,整个框架已经基本完成。首先,open-it_uvc_Device打开视频设备驱动程序,然后run_uvc_Device控制应用层视频流的打开和关闭,最后runs_uvc_数据被推入流中以驱动输出。数据缓存uvc_cache由6个帧节点和2个帧队列组成。随后的消耗工作也返回到线程run_uvc_Data。

以往我们说UVC一般搜索到的内容是板端作为主机,外接USB视频设备并使用UVC去控制,那么板端也就是从机中的UVC是如何实现的。下面就记录一个海思SDK中的例子,源码路径HISDK/mpp/sample/uvc_app

文件描述
文件说明
application.c主函数起始
hiuac.c提供hiuac对象,负责音频控制
hiuvc.c提供hiuvc对象,负责视频控制
camera.c提供hicamera摄像头对象,负责hiuvc,hiuac对象控制
uvc_gadget.c实现uvc设备操作功能
frame_cache.c实现uvc缓存操作功能
histream.c实现视频流操作功能
sample*.c实现对接mpp媒体开发框架操作功能
对象操作

以文件划分功能,文件的操作函数都为静态文件作用域(对外部不可见),这些函数最终被赋值到变量中,而这个变量也是静态的,只能被唯一一个的全局作用域函数get_xx()获取。

static int __init(void){};
static int __run(void){};

static hicamera __hi_camera =
{
    .init = __init,
    .run = __run,
};

hicamera *get_hicamera(void)
{
    return &__hi_camera;
}
对象分析

直接进入正题以hiuvc对象为切入点,它由初始化、打开、关闭和运行四个部分组成。对象主要负责流程控制,不包含具体实现,其中__init并没有做任何事,__open__close为直接调用,__run创建了线程uvc_send_data_thread去循环run_uvc_data,然后主线程就进入循环run_uvc_device状态。通过查找可以发现这些对象支持函数都指向文件uvc_gadget.c

浅析 Hi MPP 中的 uvc_app第1张

__open

深入open_uvc_device函数,最后可以看出它的最终执行的是v4l2常规流程,首先open设备视频设备节点获得fd,其次ioctl VIDIOC_UERYCAP去查询v4l2能力,最后再ioctl VIDIOC_SUBSCRBE_EVENT去设定订阅事件,如:VC处理(UVC_EVENT_SETUP),VS处理(UVC_EVENT_DATA),开启流(UVC_EVENT_STREAMON), 停止流(UVC_EVENT_STREAMOFF)。

浅析 Hi MPP 中的 uvc_app第2张

__close

关闭是打开的相反操作,主要是去close掉打开的描述符,在这之前需要关闭视频能力。

浅析 Hi MPP 中的 uvc_app第3张

__run

这个函数会创建线程循环run_uvc_data,自身进入run_uvc_device循环。

浅析 Hi MPP 中的 uvc_app第4张

run_uvc_data

这个功能块就负责一件事,在流启动后去监听视频设备描述符,就绪时就通过uvc_video_process_userptr把一帧数据推入UVC视频缓冲区,具体功能实现先跳过。

浅析 Hi MPP 中的 uvc_app第5张

run_uvc_device

这个功能块负责执行UVC事件处理,函数通过select监听描述符,当描述符就绪时就从视频设备的事件队列中出队一个事件并做处理。当初始化事件完成后会触发UVC_EVENT_STREAMON事件,对应的执行enable_uvc_video()去启动流。当不需要据流时触发UVC_EVENT_STREAMOFF事件去执行disable_uvc_video()停止流。

浅析 Hi MPP 中的 uvc_app第6张

至此整个框架基本完成,首先open_uvc_device打开视频设备驱动,其次run_uvc_device去控制应用层的视频流开启关闭,最后通过run_uvc_data推入流到驱动向外输出。接下来就看看数据是如何被启动关闭的又是如何流转的,这里就需要提到uvc_cache视频设备缓存管理。

数据缓存

uvc_cache它由有6个帧节点和2个帧队列组成。其中帧队列free_queue表示空闲节点队列,初始化时得到了所有节点的。帧队列ok_queue表示完成节点队列,当节点填充完数据后才会被put到这个队列当中。

create_uvc_cache
    create_cache_node_list
        node = malloc //创建6块
        put_node_to_queue(uvc_cache->free_queue, node)
数据制造

前面提到,当触发UVC_EVENT_STREAMON事件是会执行enable_uvc_video去启动流,可用看到启动通常是先经过清理关机再开机的方式。直接进入histream_startup()这个函数动作,以看到最终创建了一条线程不断去监听Venc描述符,当图像就绪时去获取保存。首先会从空闲队列free_queue中取出节点,然后填充帧数据,最后put到完成队列ok_queue中,这是节点在队列中的第一次位置交换。函数最后可用看到dev-streaming被置1这就标志着流被开启,上面说到的run_uvc_data根据这个状态就可以开始推数据了。

浅析 Hi MPP 中的 uvc_app第7张

数据首次消费

到这里数据流是开启了,但是初始化并没有完成,对于视频设备/dev/video目前也就仅经历了open和订阅UVC_EVENT_操作。接下来enable_uvc_videoioctl VIDIOC_REQBUFS去命令驱动申请缓存空间。接着从完成队列ok_queue中取出节点,并将节点成员node->mem赋值到v4l2_bufioctrl VIDIOC_QBUF入队到内核缓存空间中,这个node也还被记录在等待队列__waited_node[]上表示这个节点真正被处理。到这里就完成了视频帧的第一次消费。

浅析 Hi MPP 中的 uvc_app第8张

数据后续消费

初始化后,数据制造者_SAMPLE_COMM_VENC_SaveData()会不断的从free_queue取出节点填充并挂到ok_queue上。而后续的消费工作也交回到线程的run_uvc_data去处理。可以看到首先会ioctl VIDIOC_DQBUF从内核出队一个帧,并从完成队列取出一个节点,被出队的帧号也对应等待队列的标号,帧可以出队就表示它被处理完了,这时对应等待队列中的节点__waited_node[buf->index] 就可以把他放回空闲队列中去,并记录下本次取出节点。同样节点成员node->mem也将通过v4l2_buf被ioctrl到内核中。这各过程被重复执行就实现了内核与用户数据源源不断的轮换。

浅析 Hi MPP 中的 uvc_app第9张

结束工作

UVC_EVENT_STREAMOFF事件到来时,表示结束当前工作,首先ioctl VIDIOC_STREAMOFF停止内核流传输,接着关闭应用成流生产, 最后清空完成队列ok_queue

浅析 Hi MPP 中的 uvc_app第10张

免责声明:文章转载自《浅析 Hi MPP 中的 uvc_app》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇scrapy爬虫系列之四--爬取列表和详情SAPBW数据仓库增量更新下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【OC学习-12】什么是类的继承?为什么要继承?以及继承里面的注意事项

(1)怎样实现类的继承? //之前的继承根类,如类ASStudent继承自根类 @interface ASStudent:NSObject{ } @end //如今继承现有自己定义的类,如ASStudent继承自Student @interface ASStudent:Student{ } @end (2)为什么要学习继承?理由就是少些代码。某些东西能...

C99规范

1. 增加restrict指针    C99中增加了公适用于指针的restrict类型修饰符,它是初始访问指针所指对象的惟一途径,因此只有借助restrict指针表达式才能访问对象。restrict指针指针主要用做函数变元,或者指向由malloc()函数所分配的内存变量。restrict数据类型不改变程序的语义。    如果某个函数定义了两个restr...

swiper 绑定点击事件 点击失效处理

1、 问题:react使用swiper3插件实现banner轮播,其中有个banner图有个click点击事件,而其他的是页面跳转。出现了一个问题: 就是向右滑动到该帧时的swiper,点击时未触发react的onClick方法。 参考文章地址:https://www.cnblogs.com/wuhairui/p/9343846.html 错误示范 组件初...

Windows启动过程详解

Windows启动过程详解   我们每天都在和Windows打交道,很多人可能每天都要面对多次Windows的启动过程,可是您知道在Windows的启动过程背后,隐藏着什么秘密吗?在这一系列过程中都用到了哪些重要的系统文件?系统的启动分为几个步骤?在这些步骤中计算机中发生了什么事情?这些就是本文试图告诉您的。 本文的适用范围 随着技术的发展,我们能够见到的...

Swift 特殊关键字 与符号

#available() 函数来检查API函数的可用性 // 判断当前版本是否 iOS8.0+,OSX10.10+以及以其他平台 if #available(iOS 8.0, OSX 10.10, *) { // 当版本匹配时 } else { //当版本不匹配时 } 也可以放在方法前面, 使其方法在某个版本可用 // iOS...

AI佳作解读系列(一)——深度学习模型训练痛点及解决方法

1 模型训练基本步骤 进入了AI领域,学习了手写字识别等几个demo后,就会发现深度学习模型训练是十分关键和有挑战性的。选定了网络结构后,深度学习训练过程基本大同小异,一般分为如下几个步骤 定义算法公式,也就是神经网络的前向算法。我们一般使用现成的网络,如inceptionV4,mobilenet等。 定义loss,选择优化器,来让loss最小 对数据进...