概述
之前在https://blog.csdn.net/fengbingchun/article/details/102641967中介绍过通过DShow获取Camera视频的实现,即调用VideoCapture类。在OpenCV的VideoCapture类中并没有提供获取Camera设备列表、支持的编解码类型列表及支持的video size列表接口,这里基于已有的VideoCapture类增加对这些的实现。
获取Camera设备名:在videoInput类中有两个函数,一个是getDeviceCount用于获取设备数,一个是getDeviceName用于根据索引获取设备名。在CvCaputreCAM_DShow类中增加函数getDevicesList,实现如下,在此函数中调用videoInput中的getDeviceCount和getDeviceName。
bool CvCaptureCAM_DShow::getDevicesList(std::map<int, std::string>& devicelist) const
{
devicelist.clear();
for (int i = 0; i < VI.getDeviceCount(); ++i) {
devicelist[i] = VI.getDeviceName(i);
}
return true;
}
增加一个对外调用的接口get_camera_names,实现如下:
bool FBC_EXPORTS get_camera_names(std::map<int, std::string>& names)
{
VideoCapture capture(0);
if (!capture.isOpened()) {
fprintf(stderr, "fail to open capturen");
return false;
}
return capture.getDevicesList(names);
}
获取Camera支持的编解码类型和video size:这部分的实现参考了FFmpeg中的源码libavdevice/dshow.c,并把其中的一些code移了过来。首先在videoInput类中增加一个变量infolist,用于存放设备索引名、编解码类型、视频size信息;增加一个函数getCodecAndVideoSize,用于实现获取编解码和video size;增加一个getCodecList用于供CvCaptureCAM_DShow调用获取编解码列表;增加一个getVideoSizeList用于供CvCaptureCAM_DShow调用获取video size列表。CvCaptureCAM_DShow中也有对应的这两个函数,用于供VideoCapture调用。它们的实现如下:执行完VD->streamConf->GetStreamCaps后,结构体scc中的MinOutputSize和MaxOutputSize成员变量值即是video size。通过计算结构体pmtConfig中的BITMAPINFOHEADER即可获取codec type。
void videoInput::getCodecAndVideoSize(videoDevice *VD)
{
infolist[VD->myID].clear();
int iCount = 0;
int iSize = 0;
HRESULT hr = VD->streamConf->GetNumberOfCapabilities(&iCount, &iSize);
if (hr != S_OK) {
fprintf(stderr, "fail to get number of capabilitiesn");
return;
}
if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) {
std::map<int, std::map<int, std::set<std::vector<int>>>> info;
//For each format type RGB24 YUV2 etc
for (int iFormat = 0; iFormat < iCount; iFormat++) {
VIDEO_STREAM_CONFIG_CAPS scc;
AM_MEDIA_TYPE *pmtConfig;
hr = VD->streamConf->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
if (SUCCEEDED(hr)){
//his is how many diff sizes are available for the format
int stepX = scc.OutputGranularityX;
int stepY = scc.OutputGranularityY;
//Don't want to get stuck in a loop
if (stepX < 1 || stepY < 1) continue;
std::vector<int> size_min{ scc.MinOutputSize.cx, scc.MinOutputSize.cy }, size_max{ scc.MaxOutputSize.cx, scc.MaxOutputSize.cy };
VIDEOINFOHEADER* pVih = reinterpret_cast<VIDEOINFOHEADER*>(pmtConfig->pbFormat);
BITMAPINFOHEADER* bih = &pVih->bmiHeader;
const AVCodecTag *const tags[] = { avformat_get_riff_video_tags(), NULL };
enum AVPixelFormat pix_fmt = dshow_pixfmt(bih->biCompression, bih->biBitCount);
if (pix_fmt == AV_PIX_FMT_NONE) {
enum AVCodecID codec_id = av_codec_get_id(tags, bih->biCompression);
if (codec_id != AV_CODEC_ID_NONE) {
if (codec_id == AV_CODEC_ID_MJPEG) {
info[VD->myID][VIDEO_CODEC_TYPE_MJPEG].insert(size_min);
info[VD->myID][VIDEO_CODEC_TYPE_MJPEG].insert(size_max);
}
else if (codec_id == AV_CODEC_ID_H264) {
info[VD->myID][VIDEO_CODEC_TYPE_H264].insert(size_min);
info[VD->myID][VIDEO_CODEC_TYPE_H264].insert(size_max);
}
else if (codec_id == AV_CODEC_ID_H265) {
info[VD->myID][VIDEO_CODEC_TYPE_H265].insert(size_min);
info[VD->myID][VIDEO_CODEC_TYPE_H265].insert(size_max);
}
}
}
else {
info[VD->myID][VIDEO_CODEC_TYPE_RAWVIDEO].insert(size_min);
info[VD->myID][VIDEO_CODEC_TYPE_RAWVIDEO].insert(size_max);
}
MyDeleteMediaType(pmtConfig);
}
}
for (auto codec_id = 0; codec_id < info[VD->myID].size(); ++codec_id) {
for (auto it = info[VD->myID][codec_id].cbegin(); it != info[VD->myID][codec_id].cend(); ++it) {
std::string size = std::to_string((*it)[0]);
size += "x";
size += std::to_string((*it)[1]);
infolist[VD->myID][codec_id].push_back(size);
}
}
}
else {
fprintf(stderr, "fail to get codec and video sizen");
return;
}
}
bool videoInput::getCodecList(int device_id, std::vector<int>& codecids)
{
codecids.clear();
if (infolist[device_id].size() == 0) return false;
for (auto it = infolist[device_id].cbegin(); it != infolist[device_id].cend(); ++it) {
codecids.push_back(it->first);
}
return true;
}
bool videoInput::getVideoSizeList(int device_id, int codec_id, std::vector<std::string>& sizelist)
{
if (device_id >= getDeviceCount()) return false;
sizelist = this->infolist[device_id][codec_id];
return true;
}
OpenCV中的VideoCapture类中虽然有接收设备名的接口,但是好像并不能运行,只能通过设备索引来获取视频。在一台PC机上插入多个USB摄像头时并不清楚哪个索引对应哪个设备名,这里对VideoCapture(const std::string& filename)的实现进行了修改,使其可以通过输入设备名获取视频,实现如下:首先通过索引0获取到设备列表,然后根据输入的设备名在设备列表中是否可以映射到对应的索引值,如果有匹配的,则再通过对应的索引值打开设备。
bool VideoCapture::open(const std::string& filename)
{
if (isOpened()) release();
open(0);
if (!cap) return false;
std::map<int, std::string> devices_name;
cap->getDevicesList(devices_name);
if (devices_name.size() == 0) return false;
if (isOpened()) release();
int device = -1;
for (auto it = devices_name.cbegin(); it != devices_name.cend(); ++it) {
if (filename.compare((*it).second) == 0)
device = (*it).first;
}
if (device == -1) return false;
return open(device);
}
测试代码如下:
#include "fbc_cv_funset.hpp"
#include <videocapture.hpp>
#include <opencv2/opencv.hpp>
int test_get_camera_info()
{
#ifdef _MSC_VER
std::map<int, std::string> camera_names;
fbc::get_camera_names(camera_names);
fprintf(stdout, "camera count: %dn", camera_names.size());
for (auto it = camera_names.cbegin(); it != camera_names.cend(); ++it) {
fprintf(stdout, "camera index: %d, name: %sn", (*it).first, (*it).second.c_str());
}
fbc::VideoCapture capture(0);
if (!capture.isOpened()) {
fprintf(stderr, "fail to open capturen");
return -1;
}
std::vector<int> codecids;
capture.getCodecList(codecids);
fprintf(stdout, "support codec type(video_codec_type_t: 0: h264; 1: h265; 2: mjpeg; 3: rawvideo): ");
for (auto i = 0; i < codecids.size(); ++i) {
fprintf(stdout, "%d ", codecids[i]);
}
fprintf(stdout, "n");
std::vector<std::string> sizelist;
int codec_type = fbc::VIDEO_CODEC_TYPE_MJPEG;
capture.getVideoSizeList(codec_type, sizelist);
fprintf(stdout, "codec type: %d, support video size list:(width*height)n", codec_type);
for (auto it = sizelist.cbegin(); it != sizelist.cend(); ++it) {
fprintf(stdout, "t%sn", (*it).c_str());
}
return 0;
#else
fprintf(stderr, "Error: only support windows platformn");
return -1;
#endif
}
执行结果如下:
GitHub:https://github.com//fengbingchun/OpenCV_Test
最后
以上就是端庄钢笔为你收集整理的通过Windows DShow获取设备名、支持的编解码及视频size列表实现的全部内容,希望文章能够帮你解决通过Windows DShow获取设备名、支持的编解码及视频size列表实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复