FFmpeg进阶: 获取音视频设备列表

在使用ffmpeg进行音视频开发的时候,很多时候我们需要和各种各样的硬件设备进行交互,包括摄像头、麦克风、各种采集卡等等。当设备数量比较多的时候,设备处理就比较麻烦,这时候我们一般需要枚举设备列表,针对不同的设备采用不同的处理方式。

FFmpeg并没有提供直接的获取硬件设备列表的API,只提供了list_devices配置可以将硬件设备信息输出到终端。为了获取音视频设备列表供后续调用,我们需要捕获终端的日志输出,并把对应的日志信息格式化成我们需要的硬件设备信息。对应的具体操作流程是,在设备信息输出之前调用av_log_set_callback(),设置自定义的日志输出回调函数,将设备信息输出到我们自定义的函数中并进行存储,捕获完毕之后再将回调函数设置回来。这样我们就得到了硬件设备列表信息,然后通过对文本内容进行处理,我们就得到了对应的设备信息。

详细的音视频设备列表的获取流程如下:

#define _CRT_SECURE_NO_WARNINGS

extern "C"
{
    
    
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>
#include <libavutil/pixfmt.h>
#include <libavutil/display.h>
#include <libavutil/avstring.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/imgutils.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavdevice/avdevice.h>

}
#include <string>
#include <stdio.h>
#include <sstream>
#include <vector>
#include <map>
#include <iostream>
#include <Windows.h>
#include <memory>


//windows输出需要转编码
std::string utf8_to_multibytes(const std::string& utf8)
{
    
    
	int lenWC = ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), static_cast<int>(utf8.length()), NULL, 0);
	std::unique_ptr<wchar_t> wc(new wchar_t[lenWC]());
	::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), static_cast<int>(utf8.length()), wc.get(), lenWC);

	int lenMB = ::WideCharToMultiByte(CP_ACP, 0, wc.get(), lenWC, NULL, 0, NULL, NULL);
	std::unique_ptr<char> mb(new char[lenMB]());
	::WideCharToMultiByte(CP_ACP, 0, wc.get(), lenWC, mb.get(), lenMB, NULL, NULL);

	return std::string(mb.get(), lenMB);
}

//设备类型的枚举
enum MediaType
{
    
    
	UNKNOWN = -1, AUDIO, VIDEO
};

struct Device
{
    
    
	Device() : deviceType(UNKNOWN) {
    
    }
	//设备类型
	int deviceType;
	//设备的缩略名称
	std::string shortName;
	//设备完整名称
	std::string longName;
	//设备的数字编号
	std::string numString;
};

//定义回调函数
typedef void(*FFmpegLogCallbackFunc)(void*, int, const char*, va_list);
FFmpegLogCallbackFunc ffmpegLogCallback = av_log_default_callback;

//字符串流用来捕获日志信息
static std::stringstream device_sstrm;

//捕获输出的日志内容
static void logCallbackForDshowInfo(void* ptr, int level, const char* fmt, va_list vl)
{
    
    
	char buf[1024];
	vsprintf(buf, fmt, vl);
	device_sstrm << buf;
}


//分割设备的日志信息
static bool getInsideQuote(const std::string& src, std::string& dst)
{
    
    
	dst.clear();
	std::string::size_type posBeg = src.find("\"");
	if (posBeg == std::string::npos)
		return false;
	std::string::size_type posEnd = src.find("\"", posBeg + 1);
	if (posEnd == std::string::npos)
		return false;
	dst = src.substr(posBeg + 1, posEnd - posBeg - 1);
	return true;
}

void listDirectShowDevices(std::vector<Device> &devices)
{
    
    
	//注册设备信息
	avdevice_register_all();
	devices.clear();

	std::string line, name;
	Device d;
	std::map<std::string, int> mapNameCount;
	int numDevices = 0;

	AVInputFormat *iformat = av_find_input_format("dshow");
	if (!iformat)
	{
    
    
		return;
	}

	AVFormatContext *formatCtx = avformat_alloc_context();
	if (!formatCtx)
		return;

	//获取设备列表并输出到日志回调函数中
	AVDictionary* options = NULL;
	device_sstrm = std::stringstream();
	av_log_set_callback(logCallbackForDshowInfo);
	av_dict_set(&options, "list_devices", "true", 0);
	avformat_open_input(&formatCtx, "dummy", iformat, &options);
	//输出完毕之后重置回调函数
	av_log_set_callback(ffmpegLogCallback);

	//解析输出日志当中的视频设备
	while (true)
	{
    
    
		if (device_sstrm.eof())
			break;
		std::getline(device_sstrm, line);
		if (line.find("DirectShow video") != std::string::npos)
			break;
	}

	bool beginParse = true;
	//解析设备信息中的视频设备
	while (true)
	{
    
    
		d.deviceType = VIDEO;
		std::getline(device_sstrm, line);
		if (!line.size())
			break;
		if (line.find("Could not enumerate") != std::string::npos)
			continue;
		if (line.find("DirectShow audio") != std::string::npos)
			break;
		if (beginParse)
		{
    
    
			if (getInsideQuote(line, name))
			{
    
    
				d.shortName = name;
				beginParse = false;
			}
			if (device_sstrm.eof())
			{
    
    
				devices.clear();
				goto END;
			}
		}
		else
		{
    
    
			if (getInsideQuote(line, name))
			{
    
    
				d.longName = name;
				beginParse = true;
				devices.push_back(d);
			}
		}
		if (device_sstrm.eof())
			break;
	}
	//解析设备信息中的音频设备
	beginParse = true;
	while (true)
	{
    
    
		d.deviceType = AUDIO;
		std::getline(device_sstrm, line);
		if (!line.size())
			break;
		if (line.find("Could not enumerate") != std::string::npos)
			continue;
		if (beginParse)
		{
    
    
			if (getInsideQuote(line, name))
			{
    
    
				d.shortName = name;
				beginParse = false;
			}
			if (device_sstrm.eof())
			{
    
    
				devices.clear();
				goto END;
			}
		}
		else
		{
    
    
			if (getInsideQuote(line, name))
			{
    
    
				d.longName = name;
				beginParse = true;
				devices.push_back(d);
			}
		}
		if (device_sstrm.eof())
			break;
	}

	//获取设备的缩略名称
	numDevices = devices.size();
	for (int i = 0; i < numDevices; i++)
	{
    
    
		std::map<std::string, int>::iterator itr = mapNameCount.find(devices[i].shortName);
		if (itr == mapNameCount.end())
		{
    
    
			devices[i].numString = std::to_string(0);
			mapNameCount[devices[i].shortName] = 0;
		}
		else
		{
    
    
			++(itr->second);
			devices[i].numString = std::to_string(itr->second);
		}
	}

END:
	avformat_close_input(&formatCtx);
	av_dict_free(&options);
}

这里在主函数中调用对应的方法输出本机的音视频设备列表:

int main(int argc, char* argv[])
{
    
    
	std::vector<Device> device_vector;
	listDirectShowDevices(device_vector);
	for (int index = 0; index<device_vector.size(); ++index)
	{
    
    
		Device index_device = device_vector.at(index);
		std::cout << "==================" << std::endl;
		if (index_device.deviceType == VIDEO)
		{
    
    
			std::cout << "video device" << std::endl;
			std::cout << index_device.shortName << std::endl;
			std::cout << index_device.longName << std::endl;
			std::cout << index_device.numString << std::endl;
		}
		else if (index_device.deviceType == AUDIO)
		{
    
    
			std::cout << "audio device" << std::endl;
			std::cout << utf8_to_multibytes(index_device.shortName) << std::endl;
			std::cout << index_device.longName << std::endl;
			std::cout << index_device.numString << std::endl;
		}
	}
	getchar();
}

猜你喜欢

转载自blog.csdn.net/yang1fei2/article/details/129110255