【genius_platform软件平台开发】第五十七讲:Linux系统之V4L2视频驱动-VIDIOC_ENUM_FMT枚举、VIDIOC_S_FMT、VIDIOC_S_FMT视频格式代码详解

  • Post author:
  • Post category:linux




1.应用层代码



1.1 struct v4l2_fmtdesc结构体

struct v4l2_fmtdesc {  
    __u32           	index;             		/* Format number      */  
    enum v4l2_buf_type  type;              		/* buffer type        */  
    /*
     * enum v4l2_buf_type
     * {
     *    V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,          // 指定buf的类型为capture,用于视频捕获设备
     *    V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,           // 指定buf的类型为output,用于视频输出设备
     *    V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,          // 指定buf的类型为overlay,用于可预览设备
     *    V4L2_BUF_TYPE_VBI_CAPTURE = 4,            // 用于vbi捕获设备
     *    V4L2_BUF_TYPE_VBI_OUTPUT = 5,             // 用于vbi输出设备
     *    V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,     // 用于切片vbi获取设备
     *    V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,      // 用于切片vbi输出设备
     *    V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,   // 用于视频输出overlay设备
     *    V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,   // 用于多平面存储格式的视频捕获设备
     *    V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,   // 用于多平面存储格式的视频输出设备
     *    V4L2_BUF_TYPE_PRIVATE = 0x80,
     * };
     */
    __u32               flags;  
    __u8            	description[32];   		/* Description string */  
    __u32           	pixelformat;       		/* Format fourcc :V4L2_PIX_FMT_YUYV、V4L2_PIX_FMT_YVU420...  */  
    __u32           	reserved[4];  
};  
  
struct v4l2_fmtdesc fmtdesc;  
fmtdesc.index=0;  
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) !=- 1)  
{  
    printf("SUPPORT\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);  
    fmtdesc.index++;  
} 



2. 驱动层代码



2.1 struct usbvision_v4l2_format_st 结构体

// ./ambarella/kernel/linux-4.14/drivers/media/usb/usbvision/usbvision.h
struct usbvision_v4l2_format_st {
        int             supported;
        int             bytes_per_pixel;
        int             depth;
        int             format;
        char            *desc;
};

// ./ambarella/kernel/linux-4.14/drivers/media/usb/usbvision/usbvision-video.c
static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
        { 1, 1,  8, V4L2_PIX_FMT_GREY    , "GREY" },
        { 1, 2, 16, V4L2_PIX_FMT_RGB565  , "RGB565" },
        { 1, 3, 24, V4L2_PIX_FMT_RGB24   , "RGB24" },
        { 1, 4, 32, V4L2_PIX_FMT_RGB32   , "RGB32" },
        { 1, 2, 16, V4L2_PIX_FMT_RGB555  , "RGB555" },
        { 1, 2, 16, V4L2_PIX_FMT_YUYV    , "YUV422" },
        { 1, 2, 12, V4L2_PIX_FMT_YVU420  , "YUV420P" }, /* 1.5 ! */
        { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
};



2.2 vidioc_enum_fmt_vid_cap函数

// 枚举设备fmt,应用层从0开始遍历数组
static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv, struct v4l2_fmtdesc *vfd)
{
		// ./ambarella/kernel/linux-4.14/drivers/media/usb/usbvision/usbvision.h
		// #define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format)
		// 检查数组下标,别越界
        if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1)
                return -EINVAL;
                
        // 全局变量usbvision_v4l2_format数组
        // 下面就是简单的信息复制
        strcpy(vfd->description, usbvision_v4l2_format[vfd->index].desc);
        vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
        return 0;
}



2.3 vidioc_g_fmt_vid_cap函数

static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
{
  		// 获取指定device fmt,赋值各种成员变量
        struct usb_usbvision *usbvision = video_drvdata(file);
        
        // 设置图像的宽、高
        vf->fmt.pix.width = usbvision->curwidth;
        vf->fmt.pix.height = usbvision->curheight;
        vf->fmt.pix.pixelformat = usbvision->palette.format;
        // 计算每行多少bit = width * 位深,赋值各成员变量struct usbvision_v4l2_format_st ---> int bytes_per_pixel;(1、2、3、4、2.....)
        vf->fmt.pix.bytesperline = usbvision->curwidth * usbvision->palette.bytes_per_pixel;
        // 计算图像大小: 每行多少bit * height(行数) 单位:bit
        vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight;
        // ./ambarella/kernel/linux-4.14/include/uapi/linux/videodev2.h:192:       V4L2_COLORSPACE_SMPTE170M     = 1,
        // enum v4l2_colorspace ---> V4L2_COLORSPACE_SMPTE170M
        vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
        /*
        * 注意
        * 1. 针对这个字段后面会有详细的讲解
        */       
        vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image :enum v4l2_field*/

        return 0;
}



2.4 vidioc_try_fmt_vid_cap函数

struct usb_usbvision {
        struct v4l2_device v4l2_dev;
        struct v4l2_ctrl_handler hdl;
        struct video_device vdev;                                       /* Video Device */
        struct video_device rdev;                                       /* Radio Device */
		...
        unsigned int nr;                                                /* Number of the device */

        /* Device structure */
        struct usb_device *dev;
       ...
        struct usbvision_v4l2_format_st palette;

        struct v4l2_capability vcap;                                    /* Video capabilities */
        ...
}static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
{
        struct usb_usbvision *usbvision = video_drvdata(file);
        int format_idx;

        /* Find requested format in available ones */
        for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) {
                if (vf->fmt.pix.pixelformat ==usbvision_v4l2_format[format_idx].format) {
                		// usb_usbvision  ---> struct usbvision_v4l2_format_st palette;
                        usbvision->palette = usbvision_v4l2_format[format_idx];
                        break;
                }
        }
        //检查是否越界
        if (format_idx == USBVISION_SUPPORTED_PALETTES)
                return -EINVAL;
                
        // 检查成员有必要的话跟新
        // #define MIN_FRAME_WIDTH                 64
		// #define MAX_USB_WIDTH                   320
        RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
        RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);

		// 计算每行多少bit = width * 位深,赋值各成员变量struct usbvision_v4l2_format_st ---> int bytes_per_pixel;(1、2、3、4、2.....)
        vf->fmt.pix.bytesperline = vf->fmt.pix.width * usbvision->palette.bytes_per_pixel;
        // 计算图像大小: 每行多少bit * height(行数) 单位:bit
        vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * vf->fmt.pix.height;
        
        // ./ambarella/kernel/linux-4.14/include/uapi/linux/videodev2.h:192:       V4L2_COLORSPACE_SMPTE170M     = 1,
        // enum v4l2_colorspace ---> V4L2_COLORSPACE_SMPTE170M
        vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
        vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */

        return 0;
}

/* Deprecated, do not use */
#define V4L2_TUNER_ADC  V4L2_TUNER_SDR

enum v4l2_memory {
        V4L2_MEMORY_MMAP             = 1,
        V4L2_MEMORY_USERPTR          = 2,
        V4L2_MEMORY_OVERLAY          = 3,
        V4L2_MEMORY_DMABUF           = 4,
};

/* see also http://vektor.theorem.ca/graphics/ycbcr/ */
enum v4l2_colorspace {
        /*
         * Default colorspace, i.e. let the driver figure it out.
         * Can only be used with video capture.
         */
        V4L2_COLORSPACE_DEFAULT       = 0,

        /* SMPTE 170M: used for broadcast NTSC/PAL SDTV */
        V4L2_COLORSPACE_SMPTE170M     = 1,

        /* Obsolete pre-1998 SMPTE 240M HDTV standard, superseded by Rec 709 */
        V4L2_COLORSPACE_SMPTE240M     = 2,

        /* Rec.709: used for HDTV */
        V4L2_COLORSPACE_REC709        = 3,

        /*
         * Deprecated, do not use. No driver will ever return this. This was
         * based on a misunderstanding of the bt878 datasheet.
         */
        V4L2_COLORSPACE_BT878         = 4,

        /*
         * NTSC 1953 colorspace. This only makes sense when dealing with
         * really, really old NTSC recordings. Superseded by SMPTE 170M.
         */
        V4L2_COLORSPACE_470_SYSTEM_M  = 5,

        /*
         * EBU Tech 3213 PAL/SECAM colorspace. This only makes sense when
         * dealing with really old PAL/SECAM recordings. Superseded by
         * SMPTE 170M.
         */
        V4L2_COLORSPACE_470_SYSTEM_BG = 6,

        /*
         * Effectively shorthand for V4L2_COLORSPACE_SRGB, V4L2_YCBCR_ENC_601
         * and V4L2_QUANTIZATION_FULL_RANGE. To be used for (Motion-)JPEG.
         */
        V4L2_COLORSPACE_JPEG          = 7,

        /* For RGB colorspaces such as produces by most webcams. */
        V4L2_COLORSPACE_SRGB          = 8,

        /* AdobeRGB colorspace */
        V4L2_COLORSPACE_ADOBERGB      = 9,

        /* BT.2020 colorspace, used for UHDTV. */
        V4L2_COLORSPACE_BT2020        = 10,

        /* Raw colorspace: for RAW unprocessed images */
        V4L2_COLORSPACE_RAW           = 11,

        /* DCI-P3 colorspace, used by cinema projectors */
        V4L2_COLORSPACE_DCI_P3        = 12,
};



2.5 vidioc_s_fmt_vid_cap函数

设置fmt

static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
{
        struct usb_usbvision *usbvision = video_drvdata(file);
        int ret;

        ret = vidioc_try_fmt_vid_cap(file, priv, vf);
        if (ret)
                return ret;

        /* stop io in case it is already in progress */
        if (usbvision->streaming == stream_on) {
                ret = usbvision_stream_interrupt(usbvision);
                if (ret)
                        return ret;
        }
        usbvision_frames_free(usbvision);
        usbvision_empty_framequeues(usbvision);

        usbvision->cur_frame = NULL;

        /* by now we are committed to the new data... */
        usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);

        return 0;
}




2.6 enum v4l2_field枚举

     enum v4l2_field {
           V4L2_FIELD_ANY           = 0,    // driver can choose from none,
                                            // top, bottom, interlaced
                                            // depending on whatever it thinks
                                            // is approximate ...
           V4L2_FIELD_NONE = 1,             // this device has no fields ...
           V4L2_FIELD_TOP = 2,              // top field only
           V4L2_FIELD_BOTTOM = 3,           // bottom field only
           V4L2_FIELD_INTERLACED = 4,       //  both fields interlaced
           V4L2_FIELD_SEQ_TB = 5,           // both fields sequential into one buffer, top-bottom order
           V4L2_FIELD_SEQ_BT = 6,           // same as above + bottom-top order
           V4L2_FIELD_ALTERNATE = 7,        // both fields alternating into separate buffers
           V4L2_FIELD_INTERLACED_TB = 8,    // both fields interlaced, top field first and the top field is transmitted first
           V4L2_FIELD_INTERLACED_BT = 9,    // both fields interlaced, top field first and the bottom field is transmitted first
    };
  • 视频帧可以分为

    隔行



    逐行


  • 逐行顺序

    的传输一帧所有的行,而

    隔行

    则把一帧划分成

    两个fields

    ,分别保存帧的

    奇数行



    偶数行

    ,被称作

    奇场



    偶场

    ;在刷新率接近电影时,图片会

    消退的过快

    。使用奇偶场可以避免使用双倍的buffer以及额外的带宽需求。

  • 首先要明确模拟camera(数字摄像头不在这个讨论之列。)并不是在同一时间曝光一帧,camera通过场来传输这些帧的,这些场是在不同瞬间拍摄的。屏幕上的一个运动对象因此会在两个field之间产生动画效果。这种情况下需要识别哪一帧更老一点,也称作“瞬间序“

  • 当驱动通过fields 提供或者接收images,应用应该知道如何通过这些fields组合成帧,通过划分为top bottom field, “空间序”: top field的第一行是帧的第一行, bottom field的第一行是帧的第二行。

  • 然而因为field是一个跟着一个拍的,争论帧是由top还是bottom开始的是没意义的,任何两个相邻的top bottom field, 或者 bottom top field都可以组成一个有效的帧。

  • 与直觉相反top field不一定是older field, older filed是否包含top 或者bottom lines是由video标准决定的. 因此要区分瞬间序和空间序。下面的图会给出清晰的解释。

  • 所有的video capture和out devices必须汇报当前的field顺序。 一些驱动可能允许选择不同的序,end应用可以在调用VIDIOC_S_FMT前初始化struct v4l2_pix_format的 field成员。否则可以使用V4L2_FIELD_ANY

  • 所有的video capture和out devices必须汇报当前的field顺序。 一些驱动可能允许选择不同的序,终端应用可以在调用VIDIOC_S_FMT前初始化struct v4l2_pix_format的 field成员。否则可以使用V4L2_FIELD_ANY。下面列出了可能的field类型



2.6.1 V4L2_FIELD_ANY

  • Application 可以请求使用这个参数,如果V4L2_FIELD_NONE, V4L2_FIELD_TOP, V4L2_FIELD_BOTTOM V4L2_FIELD_INTERLACE 中任何一个格式都支持.驱动选择使用哪一个格式依赖于硬件能力,以及请求的image尺寸,驱动选择一个然后返回这个格式。struct_buffer的field成员不可以为V4L2_FIELD_ANY.



2.6.1 V4L2_FIELD_NONE

  • Images是逐行格式,当驱动无法区分V4L2_FIELD_TOP和V4L2_FIELD_BOTTOM,可以使用这种field类型



2.6.1 V4L2_FIELD_TOP

  • Images仅仅包含top field



2.6.1 V4L2_FIELD_BOTTOM

  • Images仅仅包含bottom field,应用可能希望防止设备捕获interlaced的图片,因为这种图片会在运动物体周围产生毛状特效



2.6.1 V4L2_FIELD_INTERLACED

  • Images包含top和bottom field, 隔行交替,场序依赖于当前video的标准。NTSC首先传输bottom field, PAL则先传输top field。



2.6.1 V4L2_FIELD_SEQ_TB

  • Images包含top和bottom field, top field的行首先存放在memory中,然后紧跟着bottom field的行。 Fields一直以瞬间序存储,较老的放在内存前面。Images的尺寸和帧相关,而不是field



2.6.1 V4L2_FIELD_SEQ_BT

  • Images包含top和bottom field, bottom field的行首先存放在memory中,然后紧跟着top field的行。 Fields一直以瞬间序存储,较老的放在内存前面。Images的尺寸和帧相关,而不是field



2.6.1 V4L2_FIELD_ALTERATE

  • 一个帧的两个field分别放在不同的buffer, 按照瞬间序,也就是说老的一个是第一个。driver或者应用指明field的奇偶性(奇偶性:当前的field是top 还是bottom field). 任何两个连续的field构成一个frame,是否两个field是连续的,不需要drop掉他们,可以通过v4l2_buffer中的sequence 成员判定。Images的尺寸和frame相关而不是fields相关



2.6.8 V4L2_FIELD_INTERLACED_TB

  • Images 包含top和bottom field, 每行交替, top field在前面。top field首先传送



2.6.9 V4L2_FIELD_INTERLACED_BT

  • Images 包含top和bottom field, 每行交替, bottom field在前面。bottom field首先传送

    在这里插入图片描述

    在这里插入图片描述

    参考文献:

    https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/field-order.html#v4l2-field

    https://www.linuxtv.org/downloads/v4l-dvb-apis-old/index.html



版权声明:本文为Windgs_YF原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。