使用c++/winrt API获取RGB相机视频流

1、前提条件

该示例使用c++/winrt进行开发,需要编译器支持c++17,本人使用Visual Studio2017,系统版本为Windows10 21H2,由于UWP API是从Windows10系统进行支持的,故而Windows7及Windows8等系统可能不能正常使用。

注:遇到未定义行为请先检查该API是从哪个Windows版本支持的,可能你当前的系统版本还不支持该API。

2、使用MediaCapture获取RGB相机视频的流程

  • 使用FindAllAsync接口获取所有的VideoCapture设备,选择你想要的设备;
  • 根据选好的设备ID及自定义配置初始化MediaCapture对象;
  • 使用刚刚初始化的MediaCapture获取所有的帧源,我们这里选择RGB视频流这个帧源;
  • 为选择好的MeidaFrameSource设置指定的format(width,height);
  • 获取读取视频流帧对象MediaFrameReader;
  • 使用MediaFrameReader读取相机的视频帧;

3、代码演示

注:演示代码中还需要依赖opencv进行nv12->bgr的转换工作,以及使用opencv进行实时显示取到的视频帧工作。

头文件

//MediaFrameCapture.h#pragma once
#ifdef _WIN32
// winrt
#include <mfapi.h>
#include <mfidl.h>
#include <winrt/base.h>
#include <winrt/Windows.Media.Core.h>
#include <winrt/Windows.Media.Devices.h>
#include <winrt/Windows.Media.Capture.h>
#include <winrt/Windows.Media.Capture.Frames.h>
#include <winrt/Windows.Media.Mediaproperties.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Graphics.h>
#include <winrt/Windows.Graphics.Imaging.h>
using namespace winrt;
#include "opencv2/opencv.hpp"
// std
#include <vector>class MediaFrameCapture
{
public:MediaFrameCapture();~MediaFrameCapture();/*** @brief 获取已连接的设备数* * @return 返回已连接的mipi RGB的设备数量* @note*/int listDevices();/*** @brief 更新已连接mipi RGB相机列表** @return void* @note */void updateListDevices();/*** @brief 连接指定deviceId的设备** 通过deviceId获取指定设备并初始MediaCapture对象** @param[in] deviceId RGB设备的PID,通过该deviceId查询设备是否存在* @return 返回设置是否被正确的打开和初始化* @note */bool setupDevice(const std::string& deviceId);/*** @brief 连接指定deviceId的设备并指定帧的分辨率** 通过deviceId获取指定设备并初始MediaCapture对象;指定帧的分辨率,若指定的大小格式不支持则失败** @param[in] deviceId RGB设备的PID,通过该deviceId查询设备是否存在* @param[in] width 分辨率宽* @param[in] height 分辨率高* @return 返回设置是否被正确的打开和初始化* @note*/bool setupDevice(const std::string& deviceId, int width, int height);bool startCapture();bool stopCapture();/*** @brief 提供给外部调用返回读取到的新帧*** @param[out] oneFrame 返回读取到的新帧* @return false没有获取到新帧,true获取到新帧* @note*/bool read(cv::Mat&);/*** @brief 用于通过设备PID号选择指定的设备,初始化m_selectedDevice字段*** @param[in] deviceId 设备的PID号* @return false指定的设备不存在,获取失败;true指定的设备存在* @note*/bool selectDeviceByDeviceId(const std::string& deviceId);/*** @brief 判断已选设备是否被占用** @param[in] selectedDevice 传入选中的设备信息* @return false设备未被占用,可以使用;true设备已被占用或互斥打开* @note*/bool isDeviceInUse(Windows::Devices::Enumeration::DeviceInformation selectedDevice);/*** @brief 获取设备列表** @return 返回设备列表结果集* @note*/Windows::Devices::Enumeration::DeviceInformationCollection getDeviceList();
private:/*** @brief 通过选好的设备初始化MediaCapture对象,该对象用于媒体设备流采集*** @param[in] selectedDevice 选择的设备信息* @return false初始化失败,true初始化成功* @note*/bool initMediaCapture(const Windows::Devices::Enumeration::DeviceInformation& selectedDevice);/*** @brief 选择Color帧源,由于一个设备中可能有color、depth、ir等源,选择color帧** @return false不存在color源,true存在并初始化成功* @note*/bool chooseMediaFrameSource();/*** @brief 获取最合适的分辨率** 若width和height为0时,选择最高分辨率,不为0时,选择与width、height对应的分辨率** @param[in] width 需要适配的分辨率宽* @param[in] height 需要适配的分辨率高* @return 为nullptr表示没有找到适配的format* @note*/Windows::Media::Capture::Frames::MediaFrameFormat getSupportFormat(int width = 0, int height = 0);private:int m_width;            // 分辨率-宽int m_height;           // 分辨率-高int m_deviceNums;       // 设备数std::string m_deviceId; // 设备的PIDparam::hstring m_subType;std::map<uint32_t, std::pair<int, int>> m_supportFormatMap;// 支持的NV12格式static std::map <std::string, bool> m_deviceOpenedMap;Windows::Devices::Enumeration::DeviceInformation m_selectedDevice;        // 被选择的设备Windows::Devices::Enumeration::DeviceInformationCollection m_deviceList; // 设备列表Windows::Media::Capture::MediaCapture m_mediaCapture;  // 媒体流采集Windows::Media::Capture::Frames::MediaFrameSource m_mediaFrameSource;       // 媒体流源,从mediacapture获取Windows::Media::Capture::Frames::MediaFrameFormat m_defaultFormat;            // 媒体格式,用于自定义视频流的分辨率Windows::Media::Capture::Frames::MediaFrameReader m_mediaFrameReader;        // 读取视频流};#endif

实现

#ifdef _WIN32
#include "MediaFrameCapture.h"
#include <iostream> // 后期使用log代替
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc.hpp"
std::map<std::string,bool> MediaFrameCapture::m_deviceOpenedMap;MediaFrameCapture::MediaFrameCapture():m_selectedDevice(nullptr),m_mediaFrameSource(nullptr), m_defaultFormat(nullptr), m_deviceList(nullptr),m_mediaFrameReader(nullptr),//m_mediaCapture(nullptr),m_subType(L"NV12")
{init_apartment();updateListDevices();
}MediaFrameCapture::~MediaFrameCapture()
{if (m_mediaFrameReader){stopCapture();m_mediaFrameReader.Close();m_mediaFrameReader = nullptr;}if (m_mediaCapture){m_mediaCapture.Close();}m_deviceOpenedMap[m_deviceId] = false;
}void MediaFrameCapture::updateListDevices()
{// 选择设备类型为视频auto selector = Windows::Devices::Enumeration::DeviceClass::VideoCapture;m_deviceList = Windows::Devices::Enumeration::DeviceInformation::FindAllAsync(selector).get();
}int MediaFrameCapture::listDevices()
{updateListDevices();return m_deviceList.Size();
}bool MediaFrameCapture::selectDeviceByDeviceId(const std::string& deviceId)
{bool haveFound = false;for (const Windows::Devices::Enumeration::DeviceInformation& device : m_deviceList){std::wstring wDeviceId(device.Id().c_str());std::string devId(wDeviceId.begin(), wDeviceId.end());if (m_deviceOpenedMap.find(devId) != m_deviceOpenedMap.end()){if (m_deviceOpenedMap.at(devId)) continue;}if (devId.find("PID_" + deviceId) != std::string::npos){haveFound = true;m_selectedDevice = device;m_deviceId = devId;}}return haveFound;
}bool MediaFrameCapture::initMediaCapture(const Windows::Devices::Enumeration::DeviceInformation& selectedDevice)
{Windows::Media::Capture::MediaCaptureInitializationSettings settings;settings.VideoDeviceId(selectedDevice.Id());settings.SharingMode(winrt::Windows::Media::Capture::MediaCaptureSharingMode::ExclusiveControl);settings.MemoryPreference(winrt::Windows::Media::Capture::MediaCaptureMemoryPreference::Cpu);settings.StreamingCaptureMode(winrt::Windows::Media::Capture::StreamingCaptureMode::Video);try {m_mediaCapture.InitializeAsync(settings).get();}catch (...){std::cout << "MediaCapture初始化失败" << std::endl;return false;}m_deviceOpenedMap[m_deviceId] = true;return true;
}bool MediaFrameCapture::isDeviceInUse(Windows::Devices::Enumeration::DeviceInformation selectedDevice)
{std::wstring wDeviceId(selectedDevice.Id().c_str());std::string devId(wDeviceId.begin(), wDeviceId.end());if (m_deviceOpenedMap.find(devId) != m_deviceOpenedMap.end() && m_deviceOpenedMap.at(devId))return true;return false;
}Windows::Devices::Enumeration::DeviceInformationCollection MediaFrameCapture::getDeviceList()
{updateListDevices();return m_deviceList;
}bool MediaFrameCapture::chooseMediaFrameSource()
{if (m_mediaCapture == nullptr){return false;}auto frameSources = m_mediaCapture.FrameSources();Windows::Media::Capture::Frames::MediaFrameSource mediaFrameSource = nullptr;for (auto frameSource : frameSources){mediaFrameSource = frameSource.Value();if (mediaFrameSource.Info().MediaStreamType() == Windows::Media::Capture::MediaStreamType::VideoRecord&& mediaFrameSource.Info().SourceKind() == Windows::Media::Capture::Frames::MediaFrameSourceKind::Color){m_mediaFrameSource = mediaFrameSource;return true;}}return false;
}Windows::Media::Capture::Frames::MediaFrameFormat MediaFrameCapture::getSupportFormat(int t_width, int t_height)
{if (!m_mediaFrameSource) return nullptr;int max_width = 0;int max_height = 0;Windows::Media::Capture::Frames::MediaFrameFormat preffered_format = nullptr;for (Windows::Media::Capture::Frames::MediaFrameFormat format : m_mediaFrameSource.SupportedFormats()){std::wcout << format.Subtype().data() << " "<< format.VideoFormat().Width() << " "<< format.VideoFormat().Height() << std::endl;std::wstring subType = format.Subtype().data();auto width = format.VideoFormat().Width();auto height = format.VideoFormat().Height();if (L"NV12" == subType){if (t_width == 0 && t_height == 0){if (max_width < width && max_height < height){max_width = width;max_height = height;preffered_format = format;}}else if (t_width == width && t_height == height){preffered_format = format;break;}}}if (preffered_format){if (t_width == 0 && t_height == 0){m_width = max_width;m_height = max_height;}else{m_width = t_width;m_height = t_height;}}return preffered_format;
}bool MediaFrameCapture::setupDevice(const std::string& deviceId)
{if (!initMediaCapture(m_selectedDevice)){return false;}if (!chooseMediaFrameSource()){return false;}m_defaultFormat = getSupportFormat();if (!m_defaultFormat){return false;}try{m_mediaFrameSource.SetFormatAsync(m_defaultFormat).get();m_mediaFrameReader = m_mediaCapture.CreateFrameReaderAsync(m_mediaFrameSource, m_subType).get();}catch (...){return false;}return startCapture();
}bool MediaFrameCapture::setupDevice(const std::string& deviceId, int t_width, int t_height)
{if (!initMediaCapture(m_selectedDevice)){return false;}if (!chooseMediaFrameSource()){return false;}m_defaultFormat = getSupportFormat(t_width, t_height);if (!m_defaultFormat){return false;}try{m_mediaFrameSource.SetFormatAsync(m_defaultFormat).get();m_mediaFrameReader = m_mediaCapture.CreateFrameReaderAsync(m_mediaFrameSource, m_subType).get();}catch (...){return false;}return startCapture();
}bool MediaFrameCapture::startCapture()
{if (!m_mediaFrameReader) return false;try {m_mediaFrameReader.StartAsync().get();}catch (...){return false;}return true;
}bool MediaFrameCapture::stopCapture()
{if (!m_mediaFrameReader) return false;try {m_mediaFrameReader.StopAsync().get();}catch (...){return false;}return true;
}bool MediaFrameCapture::read(cv::Mat& oneFrame)
{Windows::Media::Capture::Frames::MediaFrameReference videoMediaFrame = m_mediaFrameReader.TryAcquireLatestFrame();if (videoMediaFrame){auto videoFrame = videoMediaFrame.VideoMediaFrame().GetVideoFrame();cv::Mat bgrMat;if (videoFrame != nullptr){auto ff = videoMediaFrame.Format();std::wcout << ff.Subtype().data()<< " " << ff.VideoFormat().Width()<< " " << ff.VideoFormat().Height() << std::endl;uint32_t cap = m_width * m_height * 1.5;Windows::Storage::Streams::Buffer buffer(cap);videoFrame.SoftwareBitmap().CopyToBuffer(buffer);uint8_t *data = buffer.data();cv::Mat yuvMat(m_height * 3 / 2, m_width, CV_8UC1, data);cv::cvtColor(yuvMat, bgrMat, cv::COLOR_YUV2BGR_NV12);oneFrame = bgrMat.clone();return true;}}return false;
}#endif

测试代码

#include "MediaFrameCapture.h"
#include <opencv2/opencv.hpp>int main(int argc, char*argv[])
{std::string PID = "";if (argc > 1){PID = argv[1];}MediaFrameCapture frameCapture;if (!frameCapture.selectDeviceByDeviceId(PID)){std::cout << "没有找到对应PID为<" << PID << ">的相机" << std::endl;return false;}bool ret = frameCapture.setupDevice(PID, 1280, 720);if (!ret){std::cout << "open PID : " << PID << " failed" << std::endl;return 0;}while (1){cv::Mat readFrame;ret = frameCapture.read(readFrame);if (ret){cv::imshow("NV12 Image", readFrame);cv::waitKey(50);}}return 0;
}

使用c++/winrt API获取RGB相机视频流相关推荐

  1. Drift 运动相机 获取相机视频流进行二次开发教程

    Drift运动相机获取视频流开发教程 简介 Drift动动相机支持TCP, RTSP以及RTMP传输协议提供视频流. 其中TCP及RTSP协议,是相机做流服务器端,手机APP端采用拉流的方式获取视频流 ...

  2. RGB相机、深度相机以及LiDAR成像原理

    RGB相机成像原理 相机结构和原理(入门简版)知乎 摄像机模型数学推导 相机结构 成像原理 大名鼎鼎的"拜尔滤镜" CMOS滤镜排列 像素尺寸.大小关系 景深 景深原理 卷帘相机( ...

  3. 使用FFMpeg API 获取摄像头的图像数据

    文章目录 1. 获取摄像头的信息 2. 打开并初始化摄像头 3. 获取摄像头数据及渲染 摄像头是我们比较常用的外设,很多场景我们都会用到摄像头.比如视频直播.视频监控等各个领域都会用到摄像头.摄像头图 ...

  4. 【python读取小觅智能双目相机视频流】

    python读取小觅智能双目相机视频流 小觅智能双目相机产品详细参数 分辨率:2560x720; 1280x480 深度分辨率:1280x720; 640x480 帧率:60FPS 双目相机可用于双目 ...

  5. 相册获取、相机拍摄,裁剪圆形头像

    相册获取.相机拍摄,裁剪圆形头像 应用场景 很多应用都有个人中心,个人中心就会有头像,现在一般都流行圆形头像,那么怎么设置呢 使用步骤 这里参考了网上各位大神的文章,因为中途遇到几个坑,折磨了一天,快 ...

  6. 前端js调用后端API获取数据的三种方法(2022.7.25)

    前端js调用后台API获取数据的三种方法(2022.7.25) 前言 需求分析 一个Get实例 浏览器请求 SoapUI软件请求 一个Post实例 浏览器请求 SoapUI软件请求 1.Http简介( ...

  7. 通过聚合数据API获取微信精选文章

    思路 在聚合数据申请账号(https://www.juhe.cn/) 通过聚合数据api获取微信精选文章api 通过newspaper库提取相应的文本内容,关于newspaper库的使用方法可以参考这 ...

  8. redux 局部刷新_如何使用Redux Observables和刷新令牌API获取新的访问令牌

    redux 局部刷新 by Sachin Kumar 由Sachin Kumar 如何使用Redux Observables和刷新令牌API获取新的访问令牌 (How to get a new acc ...

  9. python使用openweathermap API获取全世界主要城市天气信息

    python使用openweathermap API获取全世界主要城市天气信息 官方网址: http://www.openweathermap.org/api 注意:使用gmail注册成功,然后到gm ...

最新文章

  1. ajax 提交订单,php-在Woocommerce 3中通过ajax提交并在结帐时创建订单
  2. c c 语言程序设计同步,第一部分C语言程序设计C语言程序设计同步练习答案.PDF...
  3. 西安交大计算机考研分数线2020院线,西安交通大学2020研究生复试分数线预计4月中旬左右公布...
  4. Jquery对象本质和隐式迭代
  5. SpringBoot基础重难点
  6. FLV文件中VideoPacket的详解
  7. google 插件_google这4款插件我每天都用,省时无数
  8. 自定义服务器网址,小白新手如何在服务器上搭建一个自己的网站
  9. 让用户感到体贴登录页设计灵感
  10. 常用傅里叶变换公式大全_高二数学常用导数公式大全
  11. python写飞机大战游戏_python实现飞机大战游戏
  12. 用 Python 分析了 1982 场英雄联盟数据,开局前预测游戏对局胜负!
  13. 2019 Flutter 心愿单
  14. go 实现单链表反转
  15. 暴增14倍!这家港股最大基金公司,1年净利20亿,竟是因为这个!
  16. visio-软件程序流程图规范
  17. 批量删除QQ空间说说代码
  18. 海尔电商峰值系统架构设计最佳实践
  19. Gradle | Gradle构建之Groovy语言基础
  20. Python中的self,类中的self?

热门文章

  1. python学习笔记之word文档提取
  2. 如何搭建vue脚手架
  3. 从万达百货到家乐福中国,苏宁如何快速消化“大块头”?
  4. java+jsp基于ssm的智慧医疗系统医院挂号就诊系统-计算机毕业设计
  5. 移动端UI设计规范模板参考以及设计规范的好处
  6. WiFi信号弱,选穿墙路由器还是放大器,WiFi信号放大器真的有用吗?
  7. 【Java 基础】字符串StringBuilder、StringBuffer,工具StringJoiner
  8. php 敏感关键词检测,字符串 - PHP 敏感词违法关键字检测 算法方案
  9. 【行业标准】YBT092-2019-合金铸铁磨球(高中低铬铸造钢球)
  10. caspase家族的特点_caspase家族