Notes
main
main
  • Introduction
  • linuxKernel
    • tips
    • make_help
    • old linux
      • compile_linux0.11
      • TestEnvironment
      • load_setup
      • get_hard_data
    • list
    • plist
    • fifo
    • idr
    • xarray
    • rbtree
    • maple_tree
    • syscall
    • bitmap
    • page
    • page_flags
    • page_size
    • page mapcount
    • page refcount
    • folio
    • slub
      • proc_slabinfo
      • slub_theory
      • kmalloc_kfree
      • kmem_cache
      • slab_alloc
      • slab_free
      • proc_meminfo_SReclaimable_SReclaimable
    • vmalloc
    • brk
    • mmap
    • mremap
    • mprotect
    • madvise
    • read
    • write
    • shmem
    • huge_page
    • page_fault
    • rmap
    • lru
    • multi-gen-LRU
    • page_reclaim
    • page_cache
    • page_table
    • rcu
    • kvm
    • aarch64_boot
    • tracing_system
    • cache_coherence_and_memory_consistency
    • cpu_speculates
    • mmap_lock
    • per-vma_lock
    • cgroup
    • symbol
    • campact
    • page_ext
    • mempool
    • kernelstack
    • filesystem
    • io_stack
    • workingset
    • ioremap
    • sched_period
  • linuxDebug
    • openocd_openjtag
    • i2c_tools
    • objdump
    • addr2line
    • gdb_useage
    • debug_linux_kernel_via_gdb
    • debug_linux_module_via_gdb
    • early_boot
    • sequentially_execute
    • dynamic_debug
    • research_linuxKernel_by_patch
    • tracefs
    • ebpf
    • bpftrace
    • perf
    • flame_graph
    • crash
    • ASAN_HWASAN_MTE_check_mem_bug
    • page_owner
    • vmtouch
    • fio
    • benchmark
  • linuxSystem
    • common
      • system_version
      • procfs
      • proc_sys_vm
      • cmd_ps
      • makefile
      • file_descriptor
      • psi
      • ulimit
      • top
      • delay_accounting
    • ubuntu
      • custom_kernel
      • get_cmd_src
      • record_ssh_info
      • log
      • run_custom_script
      • repo
      • cockpit
      • nfs
      • tftp
      • misc
    • fedora
      • system_upgrade
      • custom_kernel
      • lvextend
      • yt-dlp
      • jellyfin
  • linuxDriver
    • i2c_peripherals_driver
    • spi_peripherals_driver
    • gpio_subsystem
    • IRQ_driver
    • blockIO_unblockIO_async
    • linux_own_driver
    • misc_device
    • input_device
    • timer
    • atomic_spinlock_semaphore_mutex
    • lcd
    • touch_screen
    • debugfs
    • v4l2
    • mmap
  • hardware
    • paging_mmu_pt
    • iommu
  • process_thread_scheduler
    • scheduler01
    • scheduler02
    • scheduler03
    • scheduler04
    • scheduler05
    • scheduler06
  • memory_management
    • mm1
    • mm2
    • mm3
    • mm4
    • mm5
  • input_output_filesystem
    • io_fs_01
    • io_fs_02
    • io_fs_03
    • io_fs_04
  • lock_and_lockup_detector
    • general_lock
    • hung_task
    • softLockup_hardLockup
    • crash_experiment
  • MIT_6.S081
    • 6.S081_Operating_System_Engineering
    • Schedule.md
    • Class
      • Overview
      • Administrivia
    • Labs
      • Tools
      • Guidance
      • startup
      • syscall
      • page_table
      • Calling_Convention
      • traps
    • xv6
      • xv6
    • References.md
  • qemu
    • qemu_buildroot
    • qemu_busybox.md
    • Serial.md
    • demo_mini2440
      • 0_compilation_error_summary
      • 1_compilation_steps
      • 2_operation_mode
      • 3_transplant_tools_libraries
      • 4_tools_use
      • reference_website
  • tools
    • getKernelSourceCodeList
    • nat
    • shell
    • translating
    • YouCompleteMe
    • cscope
    • global
    • vscode
    • vim
    • binary
    • markdown
    • draw
    • git
    • tig
    • tmux
    • mail_client
    • download_patchset_from_LKML
    • minicom
    • clash
  • other
    • interview
    • interview_c_base
    • know_dontknow
    • Stop-Ask-Questions-The-Stupid-Ways
    • How-To-Ask-Questions-The-Smart-Way
    • docker
    • buildroot
    • rv32_to_rv64
Powered by GitBook
On this page
  • 简述
  • linux kernel v4l2驱动的编写
  • 1. 注册struct v4l2_device, struct vb2_queue, struct video_device
  • 2. 填充struct vb2_ops, struct v4l2_file_operations, v4l2_ioctl_ops
  • 3. 注销
  • linux应用层
  • 1. 通过v4l2-ctl捕捉RAW图
  • 2. 通过gst-launch-1.0转换成.jpeg格式
  • 参考网址

Was this helpful?

  1. linuxDriver

v4l2

简述

linux kernel v4l2驱动的编写,可以分为四步:

  1. 注册struct v4l2_device, struct vb2_queue, struct video_device

  2. 填充struct vb2_ops, struct v4l2_file_operations, v4l2_ioctl_ops

  3. 注销

linux kernel v4l2驱动的测试,可以分为二步:

  1. 通过v4l2-ctl捕捉RAW图

  2. 通过gst-launch-1.0转换成.jpeg格式

linux kernel v4l2驱动的编写

1. 注册struct v4l2_device, struct vb2_queue, struct video_device

struct device *dev;
struct v4l2_device v4l2_dev;
struct vb2_queue *queue;
struct video_device *vdev;
struct list_head list;
struct mutex mutex;

// 注册struct v4l2_device
v4l2_device_register(dev, &v4l2_dev);

// 初始化链表
INIT_LIST_HEAD(&list);
// 初始化互斥表
mutex_init(&mutex);

queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// 应用层采用mmap方法进行读取数据
queue->io_modes = VB2_MMAP;
// buffers队列管理
queue->ops = &xxx_queue_ops;
// buffers内存管理
queue->mem_ops = &vb2_vmalloc_memops;
queue->buf_struct_size = sizeof(struct xxx_buffer);
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
queue->lock = &mutex;
queue->drv_priv = xxx;
// 初始化struct vb2_queue
vb2_queue_init(queue);

vdev->fops = &xxx_fops;
vdev->ioctl_ops = &xxx_ioctl_ops;
// V4L2_CAP_VIDEO_CAPTURE, V4L2_CAP_STREAMING代表一个摄像头
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
vdev->release = xxx_release;
vdev->v4l2_dev = &v4l2_dev;
vdev->queue = queue;
video_set_drvdata(vdev, xxx);
// 注册struct video_device
video_register_device(vdev, VFL_TYPE_GRABBER, -1);

2. 填充struct vb2_ops, struct v4l2_file_operations, v4l2_ioctl_ops

struct task_struct *kthread;

struct xxx_buffer {
    /* common v4l buffer stuff -- must be first */
    struct vb2_v4l2_buffer vb;
    struct list_head list;
};

struct xxx_fmt {
    u32    pixelformat;
    u32    bit_depth;
};

struct xxx_fmt xxx_formats[] = {
    {
        .pixelformat = V4L2_PIX_FMT_RGB565,
        .bit_depth   = 16,
    },
};

int xxx_queue_setup(struct vb2_queue *q,
        unsigned int *num_buffers, unsigned int *num_planes,
        unsigned int sizes[], struct device *alloc_devs[])
{
    // 一帧数据大小sizeimage(bytes)
    sizes[0] = sizeimage;

    return 0;
}

int xxx_buf_prepare(struct vb2_buffer *vb)
{
    // 一帧数据大小sizeimage(bytes)
    vb2_set_plane_payload(vb, 0, sizeimage);

    return 0;
}

void xxx_buf_queue(struct vb2_buffer *vb)
{
    struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
    struct xxx_buffer *buf = container_of(vbuf, struct xxx_buffer, vb);

    // 将buffer存放到链表中
    list_add_tail(&buf->list, &list);
}

static int xxx_kthread(void *data)
{
    struct xxx_buffer *buf = NULL;
    void *vbuf;

    for (;;) {
        if (kthread_should_stop())
            break;

        // 获得链表第一个buffer
        buf = list_entry(list.next, struct xxx_buffer, list);
        // 从链表中删除buffer
        list_del(&buf->list);

        // 获得buffer地址
        vbuf = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
        // 这里为了简单,直接填充数据0x88,一帧数据大小sizeimage(bytes)
        // 实际中通过此处填充摄像头数据
        memset(vbuf, 0x88, sizeimage);

        // 通知应用层buffer已经准备完毕,可以读取
        vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);

        // 这里为了简单,直接调用msleep(),实际应用中不能这样做
        // 实际中通过此处实现摄像头帧率
        msleep(100);
    }

    return 0;
}

int xxx_start_streaming(struct vb2_queue *q, unsigned int count)
{
    /* 创建并启动内核线程
     * xxx_kthread : 内核线程函数
     * xxx         : 传递给内核线程的参数,
     * xxx-kthread : 内核线程的名字
     */
    kthread = kthread_run(xxx_kthread, xxx, "xxx-kthread");

    return 0;
}

void xxx_stop_streaming(struct vb2_queue *q)
{
    // 删除链表中所有buffers
    while (!list_empty(&list)) {
        struct xxx_buffer *buf;

        buf = list_entry(list.next, struct xxx_buffer, list);
        list_del(&buf->list);
        vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
    }

    kthread_stop(kthread);
}

const struct vb2_ops xxx_queue_ops = {
    .queue_setup     = xxx_queue_setup,
    .buf_prepare     = xxx_buf_prepare,
    .buf_queue       = xxx_buf_queue,
    .start_streaming = xxx_start_streaming,
    .stop_streaming  = xxx_stop_streaming,
};

/*
 * 获得摄像头信息
 */
int xxx_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
    strcpy(cap->driver, "xxx_driver");
    strcpy(cap->card, "xxx_card");
    strcpy(cap->bus_info, "xxx_bus_info");

    return 0;
}

/*
 * 枚举摄像头支持的像素格式
 */
int xxx_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
{
    struct xxx_fmt *fmt;

    if (f->index >= ARRAY_SIZE(xxx_formats))
        return -EINVAL;

    fmt = &xxx_formats[f->index];
    f->pixelformat = fmt->pixelformat;

    return 0;
}

/*
 * 获得目前摄像头的分辨率width*height, 像素格式pixelformat,一帧数据大小sizeimage(bytes)
 */
int xxx_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
{
    struct v4l2_pix_format pix = f->fmt.pix;

    pix.width = width;
    pix.height = height;
    pix.pixelformat = pixelformat;

    pix.sizeimage = sizeimage;

    return 0;
}

/*
 * 设置摄像头的分辨率width*height, 像素格式pixelformat,一帧数据大小sizeimage(bytes)
 */
int xxx_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
{
    struct v4l2_pix_format pix = f->fmt.pix;
    int i;

    width = pix.width;
    height = pix.height;
    pixelformat = pix.pixelformat;

    for(i=0; i<ARRAY_SIZE(xxx_formats); i++) {
        if(pix.pixelformat == xxx_formats[i].pixelformat)
            break;
    }
    sizeimage = width * (xxx_formats[i].bit_depth >> 3) * height;

    return 0;
}

const struct v4l2_file_operations xxx_fops = {
    .owner          = THIS_MODULE,
    .open           = v4l2_fh_open,
    .release        = v4l2_fh_release,
    .poll           = vb2_fop_poll,
    .unlocked_ioctl = video_ioctl2,
    .mmap           = vb2_fop_mmap,
};

const struct v4l2_ioctl_ops xxx_ioctl_ops = {
    .vidioc_querycap         = xxx_querycap,

    .vidioc_enum_fmt_vid_cap = xxx_enum_fmt_vid_cap,
    .vidioc_g_fmt_vid_cap    = xxx_g_fmt_vid_cap,
    .vidioc_s_fmt_vid_cap    = xxx_s_fmt_vid_cap,

    .vidioc_reqbufs          = vb2_ioctl_reqbufs,
    .vidioc_querybuf         = vb2_ioctl_querybuf,
    .vidioc_qbuf             = vb2_ioctl_qbuf,
    .vidioc_dqbuf            = vb2_ioctl_dqbuf,
    .vidioc_streamon         = vb2_ioctl_streamon,
    .vidioc_streamoff        = vb2_ioctl_streamoff,
};

void xxx_release(struct video_device *vdev)
{

}

3. 注销

video_unregister_device(&vdev);
v4l2_device_put(&v4l2_dev);

linux应用层

1. 通过v4l2-ctl捕捉RAW图

分辨率为640x480,像素格式为RGBP(rgb565),使用mmap方法捕捉一张RAW图,保存为grab-640x360-rgb565.raw

$ v4l2-ctl --set-fmt-video=width=640,height=360,pixelformat=RGBP --stream-mmap --stream-count=1 --stream-to=grab-640x360-rgb565.raw

2. 通过gst-launch-1.0转换成.jpeg格式

blocksize=460800,代表RAW图的大小,即width * height * bytes_per_pix

bytes_per_pix是多少?因为rgb565 = 16bits,即16/8=2bytes

即 640 360 2 = 460800

$ gst-launch-1.0 filesrc location=grab-640x360-rgb565.raw blocksize=460800 ! "video/x-raw, format=(string)RGB16, width=(int)640, height=(int)360, framerate=(fraction)30/1" ! videoconvert ! jpegenc ! filesink location=grab-640x360-rgb565.jpeg

参考网址

PreviousdebugfsNextmmap

Last updated 4 years ago

Was this helpful?

V4L2摄像头概述
V4L2框架-videobuf2