camera_kernel之---videobuf2简单介绍(2)

videobuf2是嵌入到v4l2子系统,以供驱动与用户空间提供数据申请与交互的接口集合,它实现了包括buffer分配,根据状态可以入队出队的控制流。
文件目录
kernel/drivers/media/v4l2-core 相关videobuf2相关文件如下,videobuf相关的文件可以不关心,只讨论videobuf2
├── videobuf2-core.c
├── videobuf2-dma-contig.c
├── videobuf2-dma-sg.c
├── videobuf2-dvb.c
├── videobuf2-internal.h
├── videobuf2-memops.c
├── videobuf2-v4l2.c
├── videobuf2-vmalloc.c
├── videobuf-core.c
├── videobuf-dma-contig.c
├── videobuf-dma-sg.c
├── videobuf-dvb.c
└── videobuf-vmalloc.
既然是buffer相关。一般分配有几种可能。

  1. buffer物理地址不连续,硬件上支持scatter/gather DMA,rkisp内部有iommu,可以实现分段dma。
  2. buffer 虚拟地址连续,物理不连续,也就是vmalloc操作,也很难使用dma
  3. 物理地址连续,一般可以从cma内存申请(预留)。
    分别对应文件videobuf2-dma-sg.c,videobuf2-vmalloc.c,videobuf2-dma-contig.c
    核心文件是videobuf2-core.c,videobuf2-v4l2.c

videobuf2-core.h里面vb2_queue是总的videobuf2结构体,每个video节点需要实现。
Rk定义结构体

struct rkisp1_vdev_node {
	struct vb2_queue buf_queue;//这个就是要实现的总结构体
	struct video_device vdev;//包含video文件操作函数,vb2重要结构
	struct media_pad pad;
};

Video节点操作

static int rkisp1_register_stream_vdev(struct rkisp1_stream *stream)
{
	struct rkisp1_device *dev = stream->ispdev;
	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
	struct video_device *vdev = &stream->vnode.vdev;
	struct rkisp1_vdev_node *node;
……
	vdev->ioctl_ops = &rkisp1_v4l2_ioctl_ops;
	vdev->fops = &rkisp1_fops;
……
	rkisp_init_vb2_queue(&node->buf_queue, stream,
			     V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);//mark,初始化vb2_queue
	vdev->queue = &node->buf_queue;//这里就是video节点能访问queue
	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
……
ret = media_entity_init(&vdev->entity, 1, &node->pad, 0);
}
static int rkisp_init_vb2_queue(struct vb2_queue *q,
				struct rkisp1_stream *stream,
				enum v4l2_buf_type buf_type)
{
	q->type = buf_type;
	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
	q->drv_priv = stream;
	q->ops = &rkisp1_vb2_ops;//各家厂商实现
	q->mem_ops = &vb2_dma_contig_memops;
	q->buf_struct_size = sizeof(struct rkisp1_buffer);
	q->min_buffers_needed = CIF_ISP_REQ_BUFS_MIN;
	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
	q->lock = &stream->ispdev->apilock;
后面初始化还会调用q->buf_ops = &v4l2_buf_ops;

以上是初始化过程,下面看下结构体一些变量

struct vb2_queue {
unsigned int			type;//buf类型,V4L2_BUF_TYPE_VIDEO_CAPTURE
//rk用的V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,支持多plane
unsigned int			io_modes;//看上面定义支持3种stream类型
unsigned			fileio_read_once:1;
unsigned			fileio_write_immediately:1;
unsigned			allow_zero_bytesused:1;
unsigned			use_dma_bidirectional:1;

struct mutex			*lock;//锁
void				*owner;

const struct vb2_ops		*ops;//比较重要,rk实现,buf管理函数
const struct vb2_mem_ops	*mem_ops;//内存相关操作
const struct vb2_buf_ops	*buf_ops;//相关buf的填充以及赋值用户空间地址

void				*drv_priv;//厂商私有数据区
unsigned int			buf_struct_size;//自定义buffer结构大小,//vb2_v4l2_buffer必须放在开头,可以用container_of访问总结构
u32				timestamp_flags;//时间戳
gfp_t				gfp_flags;
u32				min_buffers_needed;

/* private: internal use only */
struct mutex			mmap_lock;
unsigned int			memory;
struct vb2_buffer		*bufs[VB2_MAX_FRAME];// 一个视频缓冲区
unsigned int			num_buffers;//buffer个数

struct list_head		queued_list;//存放用户enqueue的buffer
unsigned int			queued_count;

atomic_t			owned_by_drv_count;
struct list_head		done_list;// 存放处理好等待dequeue的buffer
spinlock_t			done_lock;
wait_queue_head_t		done_wq;

void				*alloc_ctx[VB2_MAX_PLANES];
unsigned int			plane_sizes[VB2_MAX_PLANES];

unsigned int			streaming:1;
unsigned int			start_streaming_called:1;
unsigned int			error:1;
unsigned int			waiting_for_buffers:1;
unsigned int			is_multiplanar:1;
unsigned int			is_output:1;
unsigned int			last_buffer_dequeued:1;

struct vb2_fileio_data		*fileio;
struct vb2_threadio_data	*threadio;
enum dma_data_direction		dma_dir;

#ifdef CONFIG_VIDEO_ADV_DEBUG
	/*
	 * Counters for how often these queue-related ops are
	 * called. Used to check for unbalanced ops.
	 */
	u32				cnt_queue_setup;
	u32				cnt_wait_prepare;
	u32				cnt_wait_finish;
	u32				cnt_start_streaming;
	u32				cnt_stop_streaming;
#endif
};

接下来看vb2_buffer的定义

struct vb2_buffer {//这个是设备内部帧缓存描述,v4l2_buffer是用户态
	struct vb2_queue	*vb2_queue;		//包含外部指针
	unsigned int		index;			//buf的编号
	unsigned int		type;
	unsigned int		memory;
	unsigned int		num_planes;		//plane的个数
	struct vb2_plane	planes[VB2_MAX_PLANES];	//看下面

	/* private: internal use only
	 *
	 * state:		current buffer state; do not change
	 * queued_entry:	entry on the queued buffers list, which holds
	 *			all buffers queued from userspace
	 * done_entry:		entry on the list that stores all buffers ready
	 *			to be dequeued to userspace
	 */
	enum vb2_buffer_state	state;//buffer的状态,buffer轮转的时候要设置

	struct list_head	queued_entry;
	struct list_head	done_entry;..

再来看,以前不明白plane的意思,后来发现yuv数据可以不连续,比如Y数据完成后,后面不是紧接着uv,可以是其他地址,这个数据就是多plane。
最后带M的格式是多plane,比如V4L2_PIX_FMT_NV21M

struct vb2_plane {
	void			*mem_priv;	//当前plane的私有数据
	struct dma_buf		*dbuf;//共享buf目标
	unsigned int		dbuf_mapped;//标记是否映射
	unsigned int		bytesused;//占用大小
	unsigned int		length;//plane大小
	union {
		unsigned int	offset;		//mmap记录偏移
		unsigned long	userptr;	//针对userptr模式
		int		fd;		//dma 记录fd
	} m;
	unsigned int		data_offset;// 这个plane中数据开始的偏移值
};

这里再贴下vb2_ops定义,在videobuf2-core.h文件定义前面有各个函数的注释以及说明时候会被调用。
struct vb2_ops {
int (*queue_setup)(struct vb2_queue *q, const void *parg,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[]);
// alloc_ctxs指针数组包含了每个视频层的“分配上下文”,可以指定申请buffer的属性,比如writecombine模式

void *vb2_dma_contig_init_ctx(struct device *dev)
voidvb2_dma_contig_cleanup_ctx(void *alloc_ctx)

void (*wait_prepare)(struct vb2_queue *q);
void (*wait_finish)(struct vb2_queue *q);

int (*buf_init)(struct vb2_buffer *vb);
int (*buf_prepare)(struct vb2_buffer *vb);
void (*buf_finish)(struct vb2_buffer *vb);
void (*buf_cleanup)(struct vb2_buffer *vb);

int (*start_streaming)(struct vb2_queue *q, unsigned int count);
void (*stop_streaming)(struct vb2_queue *q);

void (*buf_queue)(struct vb2_buffer *vb);

};

其实我真正关心的是buffer的申请与返回。数据的流转,dma怎么设置。下一节先看request_buffer。
摘抄网上的一段,理解下流程,后面会详细介绍这些过程
1,调用vb2_queue_init 初始化队列 q 。
2,调用reqbuf 时候会根据请求(v4l2_requestbuffers)分配vb2结构,并且加入到q->buf中
3,调用querybuf时候,根据信息(v4l2_buffer)返回q->buf中对应的vb2_buffer的信息(v4l2_buffer)
4,mmap上面信息对应的 vb空间到用户空间
5,调用qbuf 时,将对应的vb2_buffer ( vivi_bufer->list )添加到 q->queued_list 队列中
6,使用select 调用poll 休眠等待 q->done_list 有数据
7, 调用qbuf 和 vidioc_streamon 时候都会查询,如果这两个条件都成立,则调用q->ops->buf_queue 将 核心中的vb2_buffer调入我们写的驱动中,放入一个列表,然后等待(上面的poll过程休眠)我们的驱动程序将数据放入该vb2_buffer
8, 数据存放完成后 调用vb2_buffer_done函数,即将上面有数据的vb2_buffer放入q->done_list中,然后唤醒上面poll休眠的进程
9, poll唤醒后会调用dqbuf将q->done_list 中的vb2_buffer提出来后,将此vb2的信息(v4l2_buffer)返回
10, 应用程序得到buffer信息后,就去对应的mmap后的用户空间中读数据。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在ROS2中,需要对代码进行一些修改和调整来适应新的API和数据类型。以下是将代码移植到ROS2的示例: ```cpp #include "rclcpp/rclcpp.hpp" #include "sensor_msgs/msg/camera_info.hpp" class CheckpointDetector : public rclcpp::Node { public: CheckpointDetector() : Node("checkpoint_detector") { camera_info_subscription_ = create_subscription<sensor_msgs::msg::CameraInfo>( "camera_info", 10, std::bind(&CheckpointDetector::cameraInfoCallback, this, std::placeholders::_1)); } private: void cameraInfoCallback(const sensor_msgs::msg::CameraInfo::SharedPtr camera_info) { sensor_msgs::msg::CameraInfo my_camera_info; my_camera_info.header = camera_info->header; my_camera_info.distortion_model = camera_info->distortion_model; my_camera_info.binning_x = camera_info->binning_x; my_camera_info.binning_y = camera_info->binning_y; my_camera_info.width = camera_info->width; my_camera_info.height = camera_info->height; my_camera_info.d = camera_info->d; my_camera_info.k = camera_info->k; my_camera_info.p = camera_info->p; my_camera_info.r = camera_info->r; } rclcpp::Subscription<sensor_msgs::msg::CameraInfo>::SharedPtr camera_info_subscription_; }; int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<CheckpointDetector>()); rclcpp::shutdown(); return 0; } ``` 请注意,ROS2中使用`rclcpp`库代替了ROS中的`ros::NodeHandle`和`ros::Subscriber`等。另外,`sensor_msgs::CameraInfo`的成员变量名称也有所不同。在ROS2中,它们被改为小写字母。 你可以将这个代码段放入你的ROS2工程中,并进行构建和运行。这样,当订阅到`camera_info`主题时,`cameraInfoCallback`函数将被调用,并将`camera_info`消息的内容赋值给`my_camera_info`变量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值