Linux下通过v4l2获取视频设备名、支持的编解码及视频size列表实现
早些时候给出了在Windows下通过dshow获取视频设备信息的实现,包括获取视频设备名、获取每种视频设备支持的编解码格式列表、每种编解码格式支持的video size列表,见:https://blog.csdn.net/fengbingchun/article/details/102806822
下面给出在Linux下通过v4l2获取这些信息的实现,代码如下:
#include "funset.hpp"
#include <string.h>
#include <assert.h>
#include <iostream>
#include <algorithm>
#include <set>#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <dirent.h>#include "v4l2_common.hpp"namespace {//static const __u32 v4l2_pixel_format_map[4] = {875967048, 0, 1196444237, 1448695129};
static const __u32 v4l2_pixel_format_map[] = {V4L2_PIX_FMT_H264, 0, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUYV};int v4l2_is_v4l_dev(const char *name)
{return !strncmp(name, "video", 5) ||!strncmp(name, "radio", 5) ||!strncmp(name, "vbi", 3) ||!strncmp(name, "v4l-subdev", 10);
}int device_open(const char* device_path)
{int fd = open(device_path, O_RDWR, 0);if (fd < 0) {fprintf(stderr, "Error: cannot open video device %s\n", device_path);goto fail;}struct v4l2_capability cap;if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {fprintf(stderr, "Error: cam_info: can't open device: %s\n", device_path);goto fail;}if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {fprintf(stderr, "Error: Not a video capture device\n");goto fail;}if (!(cap.capabilities & V4L2_CAP_STREAMING)) {fprintf(stderr, "Error: The device does not support the streaming I/O method.\n");goto fail;}return fd;fail:close(fd);return -1;
}const struct fmt_map ff_fmt_conversion_table[] = {//ff_fmt codec_id v4l2_fmt{ AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV420 },{ AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YVU420 },{ AV_PIX_FMT_YUV422P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV422P },{ AV_PIX_FMT_YUYV422, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUYV },{ AV_PIX_FMT_UYVY422, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_UYVY },{ AV_PIX_FMT_YUV411P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV411P },{ AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV410 },{ AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YVU410 },{ AV_PIX_FMT_RGB555LE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB555 },{ AV_PIX_FMT_RGB555BE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB555X },{ AV_PIX_FMT_RGB565LE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB565 },{ AV_PIX_FMT_RGB565BE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB565X },{ AV_PIX_FMT_BGR24, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_BGR24 },{ AV_PIX_FMT_RGB24, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB24 },
#ifdef V4L2_PIX_FMT_XBGR32{ AV_PIX_FMT_BGR0, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_XBGR32 },{ AV_PIX_FMT_0RGB, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_XRGB32 },{ AV_PIX_FMT_BGRA, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_ABGR32 },{ AV_PIX_FMT_ARGB, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_ARGB32 },
#endif{ AV_PIX_FMT_BGR0, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_BGR32 },{ AV_PIX_FMT_0RGB, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB32 },{ AV_PIX_FMT_GRAY8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_GREY },
#ifdef V4L2_PIX_FMT_Y16{ AV_PIX_FMT_GRAY16LE,AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_Y16 },
#endif{ AV_PIX_FMT_NV12, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV12 },{ AV_PIX_FMT_NONE, AV_CODEC_ID_MJPEG, V4L2_PIX_FMT_MJPEG },{ AV_PIX_FMT_NONE, AV_CODEC_ID_MJPEG, V4L2_PIX_FMT_JPEG },
#ifdef V4L2_PIX_FMT_H264{ AV_PIX_FMT_NONE, AV_CODEC_ID_H264, V4L2_PIX_FMT_H264 },
#endif
#ifdef V4L2_PIX_FMT_MPEG4{ AV_PIX_FMT_NONE, AV_CODEC_ID_MPEG4, V4L2_PIX_FMT_MPEG4 },
#endif
#ifdef V4L2_PIX_FMT_CPIA1{ AV_PIX_FMT_NONE, AV_CODEC_ID_CPIA, V4L2_PIX_FMT_CPIA1 },
#endif
#ifdef V4L2_PIX_FMT_SRGGB8{ AV_PIX_FMT_BAYER_BGGR8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_SBGGR8 },{ AV_PIX_FMT_BAYER_GBRG8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_SGBRG8 },{ AV_PIX_FMT_BAYER_GRBG8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_SGRBG8 },{ AV_PIX_FMT_BAYER_RGGB8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_SRGGB8 },
#endif{ AV_PIX_FMT_NONE, AV_CODEC_ID_NONE, 0 },
};enum AVCodecID ff_fmt_v4l2codec(uint32_t v4l2_fmt)
{for (int i = 0; ff_fmt_conversion_table[i].codec_id != AV_CODEC_ID_NONE; i++) {if (ff_fmt_conversion_table[i].v4l2_fmt == v4l2_fmt) {return ff_fmt_conversion_table[i].codec_id;}}return AV_CODEC_ID_NONE;
}enum AVPixelFormat ff_fmt_v4l2ff(uint32_t v4l2_fmt, enum AVCodecID codec_id)
{for (int i = 0; ff_fmt_conversion_table[i].codec_id != AV_CODEC_ID_NONE; i++) {if (ff_fmt_conversion_table[i].v4l2_fmt == v4l2_fmt &&ff_fmt_conversion_table[i].codec_id == codec_id) {return ff_fmt_conversion_table[i].ff_fmt;}}return AV_PIX_FMT_NONE;
}}; // namespaceint test_v4l2_get_device_list(std::map<std::string, std::string>& device_list)
{device_list.clear();const char* dir_name = "/dev";DIR* dir = opendir(dir_name);if (!dir) {fprintf(stderr, "Error: couldn't open the directory: %s\n", dir_name);return -1;}struct dirent* entry = nullptr;int fd;while ((entry = readdir(dir))) {char device_name[256];if (!v4l2_is_v4l_dev(entry->d_name))continue;snprintf(device_name, sizeof(device_name), "/dev/%s", entry->d_name);if ((fd = device_open(device_name)) < 0)continue;struct v4l2_capability cap;if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {fprintf(stderr, "Error: cam_info: can't open device: %s\n", device_name);goto fail;}device_list[device_name] = reinterpret_cast<char*>(cap.card);close(fd);continue;fail:if (fd >= 0) close(fd);break;}closedir(dir);return 0;
}int test_v4l2_get_codec_type_list(const std::string& device_name, std::vector<int>& codec_list)
{codec_list.clear();int fd = device_open(device_name.c_str());if (fd < 0) {fprintf(stderr, "Error: fail to open device: %s\n", device_name.c_str());return -1;}struct v4l2_capability cap;if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {fprintf(stderr, "Error: cam_info: can't open device: %s\n", device_name.c_str());return -1;}struct v4l2_fmtdesc vfd;vfd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;vfd.index = 0;while(!ioctl(fd, VIDIOC_ENUM_FMT, &vfd)) {enum AVCodecID codec_id = ff_fmt_v4l2codec(vfd.pixelformat);enum AVPixelFormat pix_fmt = ff_fmt_v4l2ff(vfd.pixelformat, codec_id);vfd.index++;if (!(vfd.flags & V4L2_FMT_FLAG_COMPRESSED)) {if (pix_fmt != AV_PIX_FMT_NONE)codec_list.emplace_back(VIDEO_CODEC_TYPE_RAWVIDEO);} else if (vfd.flags & V4L2_FMT_FLAG_COMPRESSED) {if (codec_id == AV_CODEC_ID_MJPEG)codec_list.emplace_back(VIDEO_CODEC_TYPE_MJPEG);else if (codec_id == AV_CODEC_ID_H264)codec_list.emplace_back(VIDEO_CODEC_TYPE_H264);else if (codec_id == AV_CODEC_ID_H265)codec_list.emplace_back(VIDEO_CODEC_TYPE_H265);elsefprintf(stdout, "WARNING: support codec type: %d\n", codec_id);}}std::sort(codec_list.begin(), codec_list.end());close(fd);return 0;
}int test_v4l2_get_video_size_list(const std::string& device_name, int codec_type, std::vector<std::string>& size_list)
{size_list.clear();if (codec_type < 0 || codec_type > 3) return -1;int fd = device_open(device_name.c_str());if (fd < 0) {fprintf(stderr, "Error: fail to open device: %s\n", device_name.c_str());return -1;}struct v4l2_capability cap;if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {fprintf(stderr, "Error: cam_info: can't open device: %s\n", device_name.c_str());return -1;}struct v4l2_frmsizeenum vfse;vfse.pixel_format = v4l2_pixel_format_map[codec_type];vfse.index = 0;std::set<std::vector<unsigned int>> list;while(!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) {switch (vfse.type) {case V4L2_FRMSIZE_TYPE_DISCRETE:list.insert({vfse.discrete.width, vfse.discrete.height});break;}vfse.index++;}for (auto it = list.cbegin(); it != list.cend(); ++it) {std::string str = std::to_string((*it)[0]);str +="x";str += std::to_string((*it)[1]);size_list.emplace_back(str);}close(fd);return 0;
}int test_v4l2_get_video_device_info()
{std::map<std::string, std::string> device_list;test_v4l2_get_device_list(device_list);fprintf(stdout, "device count: %d\n", device_list.size());for (auto it = device_list.cbegin(); it != device_list.cend(); ++it) {fprintf(stdout, "device name: %s, description: %s\n", (*it).first.c_str(), (*it).second.c_str());std::vector<int> codec_list;test_v4l2_get_codec_type_list((*it).first, codec_list);for (auto it2 = codec_list.cbegin(); it2 != codec_list.cend(); ++it2) {fprintf(stdout, " support codec type(0: h264; 1: h265; 2: mjpeg; 3: rawvideo):%d\n", (*it2));std::vector<std::string> size_list;test_v4l2_get_video_size_list((*it).first, (*it2), size_list);fprintf(stdout, " support video size(width*height):\n");for (auto it3 = size_list.cbegin(); it3 != size_list.cend(); ++it3) {fprintf(stdout, " %s\n", (*it3).c_str());}}}return 0;
}
以上代码参考了ffmpeg中libavdevice/v4l2.c的实现。
获取视频设备:在Linux下是通过遍历/dev目录下的所有文件来查找视频设备的,遍历目录是通过调用opendir和readdir函数,首先找出子目录名是video、radio、vbi、v4l-subden的目录。然后通过open函数打开此设备,如果打开成功并且通过调用ioctl函数获取结构体v4l2_capability内容,判断其成员capabilities属于V4L2_CAP_VIDEO_CAPTURE和V4L2_CAP_STREAMING,则最终判断此名字为视频设备。
获取支持的编解码格式列表:通过ioctl函数获取结构体v4l2_fmtdesc内容,主要通过v4l2_fmtdesc中的成员pixelformat来判断支持的编解码格式,这里有个映射,见ff_fmt_v4l2codec函数,即每个v4l2_fmtdesc.pixelformat对应ffmpeg中的AVPixelFormat、AVCodecID。注意:test_v4l2_get_codec_type_list函数中vfd.index=0,如果没有此语句,在同时存在多个视频设备时会获取不到后面设备的编解码格式列表。
获取video size列表:通过ioctl函数获取结构体v4l2_frmsizeenum内容,然后成员discrete.width和discrete.height即为支持的video size,通过vfse.index++循环获取支持的列表。注意:test_v4l2_get_video_size_list函数中的vfse.index=0,如果没有此语句,在同时存在多个视频设备时会获取不到后面设备的video size列表。
以上测试代码执行结果如下:
GitHub:https://github.com/fengbingchun/OpenCV_Test
相关文章:
12种主流编程语言输出“ Hello World ”,把我给难住了!
作为一名程序员,在初步学习编程想必都绕不开一个最为基础的入门级示例“Hello World”,那么,你真的了解各个语言“Hello World”的正确写法吗?在我们刚开始打开编程世界的时候,往往写的第一个程序都是简单的文本输出&a…

军哥lnmp一键安装包nginx支持pathinfo配置
ssh里执行:cat > /usr/local/nginx/conf/pathinfo.conf << EOF set $real_script_name $fastcgi_script_name; if ($fastcgi_script_name ~ "(.?\.php)(/.*)") { set $real_script_name $1; set $path_info $2; } fastcgi_param SCRIPT_FILENAM…

Effective STL 50条有效使用STL的经验笔记
Scott Meyers大师Effective三部曲:Effective C、More Effective C、Effective STL,这三本书出版已很多年,后来又出版了Effective Modern C。 Effective C的笔记见:https://blog.csdn.net/fengbingchun/article/details/102761542…

HTTPS网络加密双向验证-使用AFNetworking封装
1.首先使用OC封装请求头 #import <Foundation/Foundation.h> #import "AFNetworking.h" interface HttpsHandler : NSObject (AFHTTPSessionManager *)setHttpsMange; end 2.实现方法 (AFHTTPSessionManager *)setHttpsMange; { NSString *certFilePath […
30分钟搞定数据竞赛刷分夺冠神器LightGBM!
作者 | 梁云1991来源 | Python与算法之美(ID:Python_Ai_Road)【导读】LightGBM可以看成是XGBoost的升级加强版本,2017年经微软推出后,便成为各种数据竞赛中刷分夺冠的神兵利器。一,LightGBM和XGBoost对比正如其名字中的Light所蕴含…

js模块化例子
最近在看一本书,里面提到js的模块化,觉得很有必要,所以记录下来 Game.js /*** This is the main class that handles the game life cycle. It initializes* other components like Board and BoardModel, listens to the DOM events and* tr…

swift3.0提示框新用法
var alert: UIAlertController! alert UIAlertController(title: "提示", message: "添加照片", preferredStyle: UIAlertControllerStyle.actionSheet) let cleanAction UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel,han…

FFmpeg在Windows上通过dshow编解码方式设置为mjpeg并实时显示测试代码
Windows上默认的内置摄像头一般支持两种编解码格式:rawvideo和mjpeg。在调用FFmpeg接口时默认的采用rawvideo。这里通过DirectShow实现为mjpeg进行编解码。 通过命令行调用FFmpeg可执行文件: (1). 可获取Windows上连接的视频设备,命令如下&…
基于深度学习的低光照图像增强方法总结(2017-2019)| CSDN博文精选
扫码参与CSDN“原力计划”作者 | hyk_1996来源 | CSDN博客精选之前在做光照对于高层视觉任务的影响的相关工作,看了不少基于深度学习的低光照增强(low-light enhancement)的文章[3,4,5,7,8,9,10],于是决定简单梳理一下。光照估计&…

ios多线程和进程的区别(转载)
很想写点关于多进程和多线程的东西,我确实很爱他们。但是每每想动手写点关于他们的东西,却总是求全心理作祟,始终动不了手。 今天终于下了决心,写点东西,以后可以再修修补补也无妨。 一.为何需要多进程(或者…

OC封装的轮播图-只用调用即可
先来使用方法 1.//创建显示本地图片view UIView *imageScorll[WTImageScroll ShowLocationImageScrollWithFream:CGRectMake(0, 0, SCREENWIDTH, 200) andImageArray:array andBtnClick:^(NSInteger tagValue) { NSLog("点击的图片----%",(tagValue)); self.didSele…
多核时代,并行编程为何“臭名昭著”?
作者 | Yan Gu来源 | 转载自知乎用户Yan Gu【导读】随着计算机技术的发展,毫无疑问现代计算机的处理速度和计算能力也越来越强。然而细心的同学们可能早已注意到,从2005年起,单核的 CPU 性能就没有显著的提升了。究其原因,是人们发…

Linux下获取usb视频设备vendor id和product id的8种方法
在使用usb摄像头获取视频时,有时需要获取此摄像头供应商ID(vendor id, vid)和产品ID(product id, pid),这里在Linux下提供获取vid和pid的8种方法: 1. 通过v4l2中结构体v4l2_capability的成员变量card:此变量中会包含设备名、vid、…

JAVA 设计模式 模板方法模式
定义 模板方法模式 (Template Method) 定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代…
这类程序员成华为宠儿,分分钟秒杀众应届毕业生
近日,华为20亿奖励员工的新闻频频刷屏。其中20亿奖金不是面向所有的华为员工,20亿奖金包涉及到的是研发体系、造AI芯片和建设生态的员工。从5G开始部署以来,华为获得了来自全球各地运营商的订单,签订了40多个5G商用合同。另外华为…

Swift 使用CoreLocation获取定位与位置信息
大多数情况下APP会在开启应用的时候获取当前的位置,所以我写在APPDelegate里第一步 import CoreLocationvar locationManager CLLocationManager() 第二步func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: …

FFmpeg在Windows上设置dshow mjpeg编码+libyuv解码显示测试代码
之前在https://blog.csdn.net/fengbingchun/article/details/103444891中介绍过在Windows上通过ffmpeg dshow设置为mjpeg编解码方式进行实时显示的测试代码。这里测试仅调用ffmpeg的mjpeg编码接口,获取到packet后,通过libyuvlibjpeg-turbo对mjpeg进行解码…

转:浅谈Linux的内存管理机制
一 物理内存和虚拟内存 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念。 物理内存就是系统硬件提…

swift3.0阿里百川反馈
闲言少叙 直接上不熟 1.导入自己工程阿里百川demo中的Util文件,并引用其中的头文件 2.剩余就是swift3.0代码.在自己需要的地方书写 (前提是你已经申请了APPKey) 3.代码 //调用意见反馈 func actionOpenFeedback(){ //key self.appKey "此处填写自己申请的key" s…
通俗易懂:8大步骤图解注意力机制
作者 | Raimi Karim译者 | 夕颜出品 | AI科技大本营(ID:rgznai100)【导读】这是一份用图片和代码详解自注意力机制的指南,请收好。BERT、RoBERTa、ALBERT、SpanBERT、DistilBERT、SesameBERT、SemBERT、MobileBERT、TinyBERT和CamemBERT的共同…

Windows上VS2017单步调试FFmpeg源码的方法
之前在https://blog.csdn.net/fengbingchun/article/details/90114411 介绍过如何在Windows7/10上通过MinGW方式编译FFmpeg 4.1.3源码生成库的步骤,那时只能生成最终的库,却不能产生vs工程,无法进行单步调试。GitHub上有个项目ShiftMediaProj…

ormlite 多表联合查询
ormlite 多表联合查询 QueryBuilder shopBrandQueryBuilder shopBrandDao.queryBuilder(); QueryBuilder shopQueryBuilder shopDao.queryBuilder();Where shopBrandWhere shopBrandQueryBuilder.where(); shopBrandWhere .eq(ShopBrand.SHOP_NO, shopNo);Where shopWhere …

C++中关键字volatile和mutable用法
C/C中的volatile关键字和const对应,用来修饰变量,用于告诉编译器该变量值是不稳定的,可能被更改。使用volatile注意事项: (1). 编译器会对带有volatile关键字的变量禁用优化(A volatile specifier is a hint to a compiler that …
基于生成对抗网络(GAN)的人脸变形(附链接) | CSDN博文精选
扫码参与CSDN“原力计划”翻译 | 张一豪校对 | 吴金笛来源 | 数据派THU*点击阅读原文,查看「CSDN原力计划」详细说明。本文详细介绍了生成对抗网络(GAN)的知识,并用其变换人脸,并探寻如何利用StyleGAN生成不同属性&…

Jmeter连接Oracle数据库
一、Jmeter要连接oracle数据库,就必须复制JDBC驱动jar包文件ojdbc14.jar到Jmeter的lib目录下二、进入Jmeter的bin目录运行Jmeter.bat,启动Jmeter三、Jmeter软件配置如下:1、添加线程组右击线程组,选择“添加--配置元件--JDBC Conn…

swift3.0友盟分享
经过(一)的讲解,大家应该可以按照友盟提供的测试账号可以集成友盟分享了,友盟目前集合了18个APP共27种分享,可以授权的有10个App:微信、QQ、新浪微博、腾讯微博、人人网、豆瓣、Facebook、Twitter、Linkedi…

C++11中std::future的使用
C11中的std::future是一个模板类。std::future提供了一种用于访问异步操作结果的机制。std::future所引用的共享状态不能与任何其它异步返回的对象共享(与std::shared_future相反)( std::future references shared state that is not shared with any other asynchronous retur…
给算法工程师和研究员的「霸王餐」| 附招聘信息
现在的算法工程师真的是太难了!要让AI会看人眼都分辨不清的医疗影像!数据又不够,还得用前沿技术!好不容易学会看片,还要让AI会分析病理!然后模型搞出来了,还要把几十种模型,做N次计算…

swift3.0三种反向传值
一 :通知 1.通知传值所用方法 // MARK: - private methods(内部接口) let NotifMycation NSNotification.Name(rawValue:"MyNSNotification") func tempbuttonAction() { //这个方法可以传一个值 NotificationCenter.default.post(name: NotifMycation, object: &q…

C++11中std::shared_future的使用
C11中的std::shared_future是个模板类。与std::future类似,std::shared_future提供了一种访问异步操作结果的机制;不同于std::future,std::shared_future允许多个线程等待同一个共享状态;不同于std::future仅支持移动操作…