文章目录
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