从本篇开始,作者的音视频编程之旅即将进入第二阶段,在之前采集桌面、麦克风、扬声器写成mp4|mkv等可播放文件的基础上,添加了摄像头的采集,可以将摄像头替换桌面作为视频端输入源,也可以将摄像头和桌面两股视频流混合作为一股视频输入源。
技术简介
采集摄像头,应该有人马上会想起dshow,是的,不过我用到了ffmpeg里面的dshow技术,ffmpeg集成了dshow使得使用更加方便了,不过要得到更详细的摄像头设备信息,还是需要使用到com知识。
使用模块(库)
主要使用的是ffmpeg的format、device库以及windows系统库
主要流程和代码
1、初始化摄像头采集模块。首先一定要调用avdevice_register_all和avformat_network_all,才可以初始化dshow的AVInputFormat
int CameraCaptor::init(const std::string& deviceId, const int fps)
{
int err = ERROR_CODE_OK;
if (m_inited) {
return err;
}
do {
m_deviceId = deviceId;
avdevice_register_all();
avformat_network_init();
m_inputFmt = av_find_input_format("dshow");
if (m_inputFmt == nullptr) {
err = ERROR_CODE_FFMPEG_FIND_INPUT_FORMAT_FAILED;
break;
}
std::string camera;
int number;
err = findCameraById(camera, number);
HCMDR_ERROR_CODE_BREAK(err);
std::map<std::string, std::multimap<AVCodecID, DEVICE::VIDEO_MEDIADATA>> mediadataList;
err = DEVICE::VideoDevice::getCameraMediadataList(mediadataList);
HCMDR_ERROR_CODE_BREAK(err);
size_t captureId;
std::multimap<AVCodecID, DEVICE::VIDEO_MEDIADATA>::const_iterator it;
auto mediadatas = mediadataList.at(m_deviceId);
for (captureId = 0; captureId < m_captureCodecs.size(); captureId++) {
auto itBeg = mediadatas.lower_bound(m_captureCodecs.at(captureId));
auto itEnd = mediadatas.upper_bound(m_captureCodecs.at(captureId));
for (it = itBeg; it != itEnd; it++) {
if (it->second.isDefault) {
std::string resolution;
resolution.assign(std::to_string(it->second.width)).append("x").append(std::to_string(it->second.height));
err = initCamera(camera, number, resolution, it->second.fps, it->first);
break;
}
}
if (err == ERROR_CODE_OK) {
break;
}
}
if (err != ERROR_CODE_OK) {
break;
}
m_fps = it->second.fps;
m_rect = {
0, 0, it->second.width, it->second.height };
m_pixelFmt = m_capturePixFmts.at(captureId);
m_inited = true;
} while (0);
if (err != ERROR_CODE_OK) {
LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] init camera captor error: %s, last error: %lu",
__FUNCTION__, HCMDR_GET_ERROR_DESC(err), GetLastError());
cleanup();
}
return err;
}
接着根据设备id找到摄像头设备,如何获取设备id可以看《【音视频】获取视频设备-MMDeviceAPI&MONITORINFOEX(2-3)》这篇文章,这里同样获取了设备列表跟设备id比对拿到摄像头名称camera以及摄像头序号number,number表示该摄像头是同名摄像头的第几个
int CameraCaptor::findCameraById(std::string& camera, int& number)
{
int err = ERROR_CODE_OK;
do {
std::list<DEVICE::VIDEO_DEVICE> devices;
err = DEVICE::VideoDevice::getCameraDevices(