v4l2采集视频
Video4Linux2(v4l2)是用于Linux系统的视频设备驱动框架,它允许用户空间应用程序直接与视频设备(如摄像头、视频采集卡等)进行交互。
linux系统下一切皆文件,对视频设备的操作就像对文件的操作一样,使用类似读取、写入文件的方式来进行,v4l2也都是通过open()、ioctl()、read()、close()来实现对视频设备的操作。
以下是使用v4l2获取视频流的一般流程:
1、打开设备:
首先,应用程序需要打开要使用的视频设备。通常,这可以通过调用open()系统调用来完成,传递设备文件的路径作为参数。例如,摄像头通常会以/dev/videoX的形式出现,其中X是数字。

// O_NONBLOCK以非阻塞方式打开
fd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
2、查询设备能力、设置视频格式:
一旦设备打开,应用程序通常会查询设备的能力,例如支持的视频格式、分辨率、帧率等信息。这可以通过调用ioctl()系统调用来执行VIDIOC_QUERYCAP操作来完成。根据设备的能力和应用程序的需求,可以设置所需的视频格式。这包括像素格式、分辨率、帧率等。通常,可以使用VIDIOC_S_FMT操作来设置视频格式。
ioctl第二个参数表示命令类型,第三个参数是变参,不同请求命令对应不同的参数
int ioctl(int fd, unsigned long request, ...);
v4l2请求命令和请求参数在/usr/include/linux/videodev2.h里面定义

查询设备信息:
/** struct v4l2_capability {* __u8 driver[16]; // 驱动模块的名称(例如 "bttv")* __u8 card[32]; // 设备的名称(例如 "Hauppauge WinTV")* __u8 bus_info[32]; // 总线的名称(例如 "PCI:" + pci_name(pci_dev))* __u32 version; // KERNEL_VERSION* __u32 capabilities; // 整个物理设备的功能* __u32 device_caps; // 通过此特定设备(节点)访问的功能* __u32 reserved[3]; // 保留字段,用于未来扩展* };*/
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {perror("VIDIOC_QUERYCAP");return -1;
}
查询摄像头支持的格式:
/** struct v4l2_fmtdesc {* __u32 index; // 格式编号,摄像头可能支持多种格式,查看的时候需要指定格式编号,循环遍历获取所有支持的格式 * __u32 type; // 枚举 v4l2_buf_type * __u32 flags;* __u8 description[32]; // 描述字符串 * __u32 pixelformat; // 格式 FourCC,通常由四个ASCII字符组成,用于唯一地标识特定的数据格式或编码方式* __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("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description);fmtdesc.index++;
}
设置采集的视频格式:
struct v4l2_format fmt;
CLEAN(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = WIDTH;
fmt.fmt.pix.height = HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {printf("VIDIOC_S_FMT IS ERROR! LINE:%d\n", __LINE__);//return -1;
}
struct v4l2_format是v4l2中一个很重要的结构体,用于视频格式设置,定义如下:
struct v4l2_format {__u32 type;union {struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */struct v4l2_sdr_format sdr; /* V4L2_BUF_TYPE_SDR_CAPTURE */struct v4l2_meta_format meta; /* V4L2_BUF_TYPE_META_CAPTURE */__u8 raw_data[200]; /* user-defined */} fmt;
};
type是枚举类型enum v4l2_buf_type用于表示数据流格式,定义如下:
enum v4l2_buf_type {V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, // 视频捕获类型,用于从视频设备捕获图像数据(从摄像头获取实时视频、视频编解码)V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, // 视频输出类型,用于将图像数据输出到视频设备(视频编解码)V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, // 视频叠加类型,用于叠加图像或视频V4L2_BUF_TYPE_VBI_CAPTURE = 4, // 垂直空白间隔(VBI)捕获类型,用于捕获VBI数据V4L2_BUF_TYPE_VBI_OUTPUT = 5, // VBI输出类型,用于输出VBI数据V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, // 切片VBI捕获类型,用于捕获切片VBI数据V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, // 切片VBI输出类型,用于输出切片VBI数据V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, // 视频输出叠加类型,用于输出叠加图像或视频V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9, // 多平面视频捕获类型,用于从多平面视频设备捕获图像数据(视频编解码)V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10, // 多平面视频输出类型,用于将图像数据输出到多平面视频设备(视频编解码)V4L2_BUF_TYPE_SDR_CAPTURE = 11, // SDR捕获类型,用于从SDR设备捕获数据V4L2_BUF_TYPE_SDR_OUTPUT = 12, // SDR输出类型,用于将数据输出到SDR设备V4L2_BUF_TYPE_META_CAPTURE = 13, // 元数据捕获类型,用于从设备捕获元数据/* 已废弃,请勿使用 */V4L2_BUF_TYPE_PRIVATE = 0x80, // 私有类型,用于自定义和扩展目的
};
v4l2_format中的fmt是联合体,不同的type使用不用类型的结构体,V4L2_BUF_TYPE_VIDEO_CAPTURE使用v4l2_pix_format。定义如下:
struct v4l2_pix_format {__u32 width; // 图像宽度__u32 height; // 图像高度__u32 pixelformat; // 像素格式,使用 FOURCC 表示__u32 field; // 视频场类型,枚举 v4l2_field__u32 bytesperline; // 每行字节数,用于填充,如果未使用则为零__u32 sizeimage; // 图像数据大小__u32 colorspace; // 颜色空间,枚举 v4l2_colorspace__u32 priv; // 私有数据,依赖于像素格式__u32 flags; // 格式标志(V4L2_PIX_FMT_FLAG_*)union {// YCbCr 编码__u32 ycbcr_enc;// HSV 编码__u32 hsv_enc;};__u32 quantization; // 量化方式,枚举 v4l2_quantization__u32 xfer_func; // 传输函数,枚举 v4l2_xfer_func
};
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE使用struct v4l2_pix_format_mplane,一般用于v4l2视频编解码中,定义如下:
struct v4l2_pix_format_mplane {__u32 width; // 图像宽度__u32 height; // 图像高度__u32 pixelformat; // 图像像素格式 小端四字符代码(FourCC)__u32 field; // 图像字段顺序 enum v4l2_field; 字段顺序(用于隔行扫描视频)__u32 colorspace; // 图像色彩空间 enum v4l2_colorspace; 与 pixelformat 相关的补充信息struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; // 平面格式数组 每个平面的信息__u8 num_planes; // 平面数量 此格式的平面数量__u8 flags; // 格式标志(V4L2_PIX_FMT_FLAG_*)union {__u8 ycbcr_enc; // enum v4l2_ycbcr_encoding, Y'CbCr 编码__u8 hsv_enc; // enum v4l2_quantization, 色彩空间量化};__u8 quantization; // 色彩空间量化__u8 xfer_func; // enum v4l2_xfer_func, 色彩空间传输函数__u8 reserved[7]; // 保留字段
} __attribute__ ((packed));struct v4l2_plane_pix_format {__u32 sizeimage; // 用于此平面的数据所需的最大字节数__u32 bytesperline; // 相邻两行中最左侧像素之间的字节距离__u16 reserved[6]; // 保留字段
} __attribute__ ((packed));
3、请求和分配缓冲区:
应用程序需要请求并分配用于存储视频数据的缓冲区。这可以通过调用VIDIOC_REQBUFS操作来请求缓冲区,并使用VIDIOC_QUERYBUF操作来获取每个缓冲区的详细信息。然后,应用程序将缓冲区映射到当前进程空间。
向内核申请多个缓冲区:
/**struct v4l2_requestbuffers {* __u32 count; // 请求的缓冲区数量* __u32 type; // 数据流类型,枚举 v4l2_buf_type* __u32 memory; // 缓冲区的内存类型,枚举 v4l2_memory* __u32 reserved[2]; /* 保留字段,用于未来扩展*};*/
struct v4l2_requestbuffers req;
CLEAN(req);
req.count = 4;
req.memory = V4L2_MEMORY_MMAP; // 使用内存映射缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// 申请4个帧缓冲区,在内核空间中
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {printf("VIDIOC_REQBUFS IS ERROR! LINE:%d\n", __LINE__);return -1;
}
把内核缓冲区映射到当前进程空间,这样进程就可以直接读写这个地址的数据,之后缓冲区入内核队列,准备采集视频:
/** typedef struct BufferSt {* void *start;* unsigned int length;* } BufferSt;*/
buffer = (BufferSt *)calloc(req.count, sizeof(BufferSt));
if (buffer == NULL) {printf("calloc is error! LINE:%d\n", __LINE__);return -1;
}struct v4l2_buffer buf;
int buf_index = 0;
for (buf_index = 0; buf_index < req.count; buf_index++) {CLEAN(buf);buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.index = buf_index;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) // 获取每个帧缓冲区的信息 如length和offset{printf("VIDIOC_QUERYBUF IS ERROR! LINE:%d\n", __LINE__);return -1;}// 将内核空间中的帧缓冲区映射到用户空间buffer[buf_index].length = buf.length;buffer[buf_index].start = mmap(NULL, // 由内核分配映射的起始地址buf.length, // 长度PROT_READ | PROT_WRITE, // 可读写MAP_SHARED, // 可共享fd,buf.m.offset);if (buffer[buf_index].start == MAP_FAILED) {printf("MAP_FAILED LINE:%d\n", __LINE__);return -1;}// 将帧缓冲区放入视频输入队列if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {printf("VIDIOC_QBUF IS ERROR! LINE:%d\n", __LINE__);return -1;}printf("Frame buffer :%d address :0x%x length:%d\n", buf_index, (__u32)buffer[buf_index].start, buffer[buf_index].length);
}
struct v4l2_buffer是v4l2中另一个重要的结构体,用来定义缓冲区:
struct v4l2_buffer {__u32 index; // 缓冲区的ID号__u32 type; // 枚举 v4l2_buf_type; 缓冲区类型(type == *_MPLANE 表示多平面缓冲区)__u32 bytesused; // 枚举 v4l2_field; 缓冲区中图像的场序__u32 field; // 缓冲区中图像的场序struct timeval timestamp; // 帧时间戳struct v4l2_timecode timecode; // 帧时间码__u32 sequence; // 本帧的序列计数/* 内存位置 */__u32 memory; // 枚举 v4l2_memory; 传递实际视频数据的方法union {__u32 offset; // 对于 memory == V4L2_MEMORY_MMAP 的非多平面缓冲区;从设备内存开始的偏移量unsigned long userptr; // 对于 memory == V4L2_MEMORY_USERPTR 的非多平面缓冲区;指向该缓冲区的用户空间指针struct v4l2_plane *planes; // 对于多平面缓冲区;指向该缓冲区的平面信息结构数组的用户空间指针__s32 fd; // 对于 memory == V4L2_MEMORY_DMABUF 的非多平面缓冲区;与该缓冲区相关联的用户空间文件描述符} m;__u32 length; // 单平面缓冲区的缓冲区大小(而不是有效载荷)的字节数(当 type != *_MPLANE 时) 对于多平面缓冲区,表示平面数组中的元素数(当 type == *_MPLANE 时)__u32 reserved2;__u32 reserved;
};
当是多平面是,v4l2_buffer的m使用struct v4l2_plane,这个一般在v4l2视频编解码中使用,用于存储原始视频的Y U V分量,定义如下:
struct v4l2_plane {__u32 bytesused; // 平面中数据所占用的字节数(有效载荷)__u32 length; // 该平面的大小(而不是有效载荷)的字节数union {__u32 mem_offset; // 当 memory 为 V4L2_MEMORY_MMAP 时,从设备内存开始的偏移量unsigned long userptr; // 当 memory 为 V4L2_MEMORY_USERPTR 时,指向该平面的用户空间指针__s32 fd; // 当 memory 为 V4L2_MEMORY_DMABUF 时,与该平面相关联的用户空间文件描述符} m;__u32 data_offset; // 平面中数据开始的偏移量__u32 reserved[11];
};
4、开始捕获视频:
一旦缓冲区准备就绪,应用程序可以调用VIDIOC_STREAMON操作来开始捕获视频流。此时,设备将开始向分配的缓冲区写入视频数据。
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {printf("VIDIOC_STREAMON IS ERROR! LINE:%d\n", __LINE__);exit(1);
}
5、获取和处理视频帧:
应用程序可以轮询或使用异步IO等机制从缓冲区中获取视频帧数据。获取数据后,应用程序可以对视频帧进行处理,例如显示、存储或传输等操作。
static int read_frame()
{struct v4l2_buffer buf;int ret = 0;CLEAN(buf);buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {printf("VIDIOC_DQBUF! LINEL:%d\n", __LINE__);return -1;}ret = write(out_fd, buffer[buf.index].start, buf.bytesused);if (ret == -1) {printf("write is error !\n");return -1;}if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {printf("VIDIOC_QBUF! LINE:%d\n", __LINE__);return -1;}return 0;
}
static int capture_frame()
{struct timeval tvptr;int ret;tvptr.tv_usec = 0;tvptr.tv_sec = 2;fd_set fdread;FD_ZERO(&fdread);FD_SET(fd, &fdread);ret = select(fd + 1, &fdread, NULL, NULL, &tvptr);if (ret == -1) {perror("select");exit(1);}if (ret == 0) {printf("timeout! \n");return -1;}read_frame();
}
过程为:从内核队列中取出准备好buf、从buf映射到进程空间的内存中读取视频数据、buf重新送入内核缓冲队列中,因为open的时候使用的非阻塞IO,所以这里使用select监听。
6、停止捕获视频:
当视频采集完成时,应用程序可以调用VIDIOC_STREAMOFF操作来停止视频捕获。
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {printf("VIDIOC_STREAMOFF IS ERROR! LINE:%d\n", __LINE__);exit(1);
}
7、解除缓冲区内存映射:
结束内核缓冲区到当前进程空间的内存映射
int i = 0;
for (i = 0; i < 4; i++) {munmap(buffer[i].start, buffer[i].length);
}
free(buffer);
8、关闭设备:
最后,应用程序应该关闭视频设备,释放所有相关的资源。这可以通过调用close()系统调用来完成。
close(fd);
9、完整代码:
代码流程图如下:

保存的视频用yuvplayer播放。
#include <asm/types.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/fb.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define CLEAN(x) (memset(&(x), 0, sizeof(x)))
#define WIDTH 640
#define HEIGHT 480
typedef struct BufferSt {void *start;unsigned int length;
} BufferSt;
int fd;
int out_fd;
static BufferSt *buffer = NULL;
static int query_set_format()
{// 查询设备信息struct v4l2_capability cap;if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {perror("VIDIOC_QUERYCAP");return -1;}printf("DriverName:%s\nCard Name:%s\nBus info:%s\nDriverVersion:%u.%u.%u\n",cap.driver, cap.card, cap.bus_info, (cap.version >> 16) & 0xFF, (cap.version >> 8) & 0xFF, (cap.version) & 0xFF);// 查询帧格式struct v4l2_fmtdesc fmtdesc;fmtdesc.index = 0;fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) {printf("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description);fmtdesc.index++;}// 设置帧格式struct v4l2_format fmt;CLEAN(fmt);fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = WIDTH;fmt.fmt.pix.height = HEIGHT;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {printf("VIDIOC_S_FMT IS ERROR! LINE:%d\n", __LINE__);//return -1;}// 上面设置帧格式可能失败,这里需要查看一下实际帧格式fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {printf("VIDIOC_G_FMT IS ERROR! LINE:%d\n", __LINE__);return -1;}printf("width:%d\nheight:%d\npixelformat:%c%c%c%c\n",fmt.fmt.pix.width, fmt.fmt.pix.height,fmt.fmt.pix.pixelformat & 0xFF,(fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF,(fmt.fmt.pix.pixelformat >> 24) & 0xFF);return 0;
}
static int request_allocate_buffers()
{// 申请帧缓冲区struct v4l2_requestbuffers req;CLEAN(req);req.count = 4;req.memory = V4L2_MEMORY_MMAP; // 使用内存映射缓冲区req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;// 申请4个帧缓冲区,在内核空间中if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {printf("VIDIOC_REQBUFS IS ERROR! LINE:%d\n", __LINE__);return -1;}// 获取每个帧信息,并映射到用户空间buffer = (BufferSt *)calloc(req.count, sizeof(BufferSt));if (buffer == NULL) {printf("calloc is error! LINE:%d\n", __LINE__);return -1;}struct v4l2_buffer buf;int buf_index = 0;for (buf_index = 0; buf_index < req.count; buf_index++) {CLEAN(buf);buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.index = buf_index;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) // 获取每个帧缓冲区的信息 如length和offset{printf("VIDIOC_QUERYBUF IS ERROR! LINE:%d\n", __LINE__);return -1;}// 将内核空间中的帧缓冲区映射到用户空间buffer[buf_index].length = buf.length;buffer[buf_index].start = mmap(NULL, // 由内核分配映射的起始地址buf.length, // 长度PROT_READ | PROT_WRITE, // 可读写MAP_SHARED, // 可共享fd,buf.m.offset);if (buffer[buf_index].start == MAP_FAILED) {printf("MAP_FAILED LINE:%d\n", __LINE__);return -1;}// 将帧缓冲区放入视频输入队列if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {printf("VIDIOC_QBUF IS ERROR! LINE:%d\n", __LINE__);return -1;}printf("Frame buffer :%d address :0x%x length:%d\n", buf_index, (__u32)buffer[buf_index].start, buffer[buf_index].length);}
}static void start_capture()
{enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {printf("VIDIOC_STREAMON IS ERROR! LINE:%d\n", __LINE__);exit(1);}
}static void end_capture()
{enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {printf("VIDIOC_STREAMOFF IS ERROR! LINE:%d\n", __LINE__);exit(1);}
}static int read_frame()
{struct v4l2_buffer buf;int ret = 0;CLEAN(buf);buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {printf("VIDIOC_DQBUF! LINEL:%d\n", __LINE__);return -1;}ret = write(out_fd, buffer[buf.index].start, buf.bytesused);if (ret == -1) {printf("write is error !\n");return -1;}if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {printf("VIDIOC_QBUF! LINE:%d\n", __LINE__);return -1;}return 0;
}static void unmap_buffer()
{int i = 0;for (i = 0; i < 4; i++) {munmap(buffer[i].start, buffer[i].length);}free(buffer);
}
static int capture_frame()
{struct timeval tvptr;int ret;tvptr.tv_usec = 0;tvptr.tv_sec = 2;fd_set fdread;FD_ZERO(&fdread);FD_SET(fd, &fdread);ret = select(fd + 1, &fdread, NULL, NULL, &tvptr);if (ret == -1) {perror("select");exit(1);}if (ret == 0) {printf("timeout! \n");return -1;}read_frame();
}int main(int argc, char *argv[])
{// 1、打开摄像头fd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);if (fd == -1) {printf("can not open '%s'\n", "/dev/video0");return -1;}out_fd = open("./out.yuv", O_RDWR | O_CREAT, 0777);if (out_fd == -1) {printf("open out file is error!\n");return -1;}// 2、查询设备、设置视频格式query_set_format();// 3、请求和分配缓冲区request_allocate_buffers();// 4 、开始捕获视频start_capture();// 5、获取和处理视频帧for (int i = 0; i < 20; i++) {capture_frame();printf("frame:%d\n", i);}// 6、停止捕获视频end_capture();// 7、解除缓冲区内存映射unmap_buffer();// 8、关闭摄像头close(fd);close(out_fd);return 0;
}
相关文章:
v4l2采集视频
Video4Linux2(v4l2)是用于Linux系统的视频设备驱动框架,它允许用户空间应用程序直接与视频设备(如摄像头、视频采集卡等)进行交互。 linux系统下一切皆文件,对视频设备的操作就像对文件的操作一样ÿ…...
Spring Cloud 八:微服务架构中的数据管理
Spring Cloud 一:Spring Cloud 简介 Spring Cloud 二:核心组件解析 Spring Cloud 三:API网关深入探索与实战应用 Spring Cloud 四:微服务治理与安全 Spring Cloud 五:Spring Cloud与持续集成/持续部署(CI/C…...
Chrome/Edge 使用 Markdown Viewer 查看 Markdown 格式文件
Chrome/Edge 使用 Markdown Viewer 查看 Markdown 格式文件 0. 引言1. 安装 Markdown Viewer 插件2. 使用 Markdown Viewer 阅读 Markdown 格式文件 0. 引言 大部分程序员都喜欢 Markdown 格式的文件,这时给一些没有在电脑上安装 Markdown 编辑器的同事分享资料时&…...
flutter 弹窗之系列一
自定义不受Navigator影响的弹窗 class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;overrideState<MyHomePage> createState() > _MyHomePageState(); }class _MyHomePageState extends State<MyH…...
【Flink实战】Flink hint更灵活、更细粒度的设置Flink sql行为与简化hive连接器参数设置
文章目录 一. create table hints1. 语法2. 示例3. 注意 二. 实战:简化hive连接器参数设置三. select hints(ing) SQL 提示(SQL Hints)是和 SQL 语句一起使用来改变执行计划的。本章介绍如何使用 SQL 提示来实现各种干预。 SQL 提示一般可以…...
【python从入门到精通】-- 第二战:注释和有关量的解释
🌈 个人主页:白子寰 🔥 分类专栏:python从入门到精通,魔法指针,进阶C,C语言,C语言题集,C语言实现游戏👈 希望得到您的订阅和支持~ 💡 坚持创作博文…...
【手写AI代码目录】准备发布的教程
文章目录 1. tensorboard2. F.cross_entropy(input_tensor, target) F.log_softmax() F.nll_loss() 1. tensorboard from torch.utils.tensorboard import SummaryWriter# TensorBoard writer SummaryWriter(runs/mnist_experiment_1) ...if i % 100 99: # 每 100 个 b…...
2024.3.9|第十五届蓝桥杯模拟赛(第三期)
2024.3.9|十五届蓝桥杯模拟赛(第三期) 第一题 第二题 第三题 第四题 第五题 第六题 第七题 第八题 第九题 第十题 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C学习笔记,常言道,不积跬步无以至千里&…...
搭建PHP本地开发环境:看这一篇就够了
什么是PHP本地开发环境 PHP本地开发环境是指在个人计算机上模拟的服务器环境,这使得开发者能够在没有网络连接的情况下也能开发、测试和调试PHP应用程序。就像在你的电脑里装个小“服务器”,即使没网也能搞定PHP程序的开发和修修补补。这就是PHP本地开发…...
[蓝桥杯 2015]机器人数目
机器人数目 题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 少年宫新近邮购了小机器人配件,共有3类。 A 类含有:8个轮子,1个传感器; B 类含有: 6个轮子࿰…...
Codeforces Round 935 (Div. 3)
A. Setting up Camp(模拟) #include<iostream> #include<algorithm> using namespace std; const int N 2e5 10;int main(){int t, n;scanf("%d", &t);int a, b, c;while(t--){scanf("%d%d%d", &a, &b, …...
自然语言处理下载nltk模块库
nltk安装 目录 nltk安装 1.官方下载 2.离线下载 2.1 下载nltk资料包 2.2 解压下载的资料包重命名 2.2.1 将解压后的packages文件夹重命名为nltk_data 2.2.2 查看将重命名的文件夹放在那个位置 2.2.3 将上述nltk_data 文件夹放在 2.2.2 打印的位置处 3.验证是否下载成…...
题解:CF1937B(Binary Path)
题解:CF1937B(Binary Path) 一、 理解题意 1. 题目链接 CodeForces; 洛谷。 2. 题目翻译 给定一个 由 0 0 0 和 1 1 1 组成的 2 2 2 行 n n n 列的网格上寻找一条路径,使得这条路径上所有的数串联起来形成的0…...
JS——9大陷阱
一、警惕A>X>B写法 3>2>1 返回值为false(原因:3>2为true,会默认转成数字1,1>1为false) 1<4<3 返回值为true(原因:1<4为true,会默认转成数字1ÿ…...
USB - 通过configfs配置Linux USB Gadget
Linux USB gadget configured through configfs Overview USB Linux 小工具是一种具有 UDC(USB 设备控制器)的设备,可连接到 USB 主机,以扩展其附加功能,如串行端口或大容量存储功能。 A USB Linux Gadget is a device…...
迷宫与陷阱(蓝桥杯)
文章目录 迷宫与陷阱问题描述bfs解题思路代码 迷宫与陷阱 问题描述 小明在玩一款迷宫游戏,在游戏中他要控制自己的角色离开一间由 N x N 个格子组成的2D迷宫。 小明的起始位置在左上角,他需要到达右下角的格子才能离开迷宫,每一步…...
Temple of Doom靶场nodejs获取shellss-manager漏洞tcpdump提权
下载链接: Temple of Doom: 1 ~ VulnHub 下载完成后直接在vxbox中导入即可,网络链接模式根据自身情况而定(我采用的桥接模式) 正文: 先用nmap进行扫描靶机ip nmap -sn 192.168.1.1/24 对192.168.1.5进行端口探测&a…...
day03_mysql_课后练习 - 参考答案
文章目录 day03_mysql_课后练习mysql练习题第1题第2题第3题第4题第5题 day03_mysql_课后练习 mysql练习题 第1题 案例: 1、创建一个数据库:day03_test01_school 2、创建如下表格 表1 Department表的定义 字段名字段描述数据类型主键外键非空唯一D…...
creator-webview与Android交互
title: creator-webview与Android交互 categories: Cocos2dx tags: [cocos2dx, creator, webview, 交互] date: 2024-03-23 13:17:20 comments: false mathjax: true toc: true creator-webview与Android交互 前篇 Android:你要的WebView与 JS 交互方式 都在这里了…...
22.WEB渗透测试-BurpSuite(一)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:21.WEB渗透测试-HTTP协议(下)-CSDN博客 工具的使用需要先搭建靶场…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
