使用c++/winrt API获取RGB相机视频流
使用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相机视频流相关推荐
- Drift 运动相机 获取相机视频流进行二次开发教程
Drift运动相机获取视频流开发教程 简介 Drift动动相机支持TCP, RTSP以及RTMP传输协议提供视频流. 其中TCP及RTSP协议,是相机做流服务器端,手机APP端采用拉流的方式获取视频流 ...
- RGB相机、深度相机以及LiDAR成像原理
RGB相机成像原理 相机结构和原理(入门简版)知乎 摄像机模型数学推导 相机结构 成像原理 大名鼎鼎的"拜尔滤镜" CMOS滤镜排列 像素尺寸.大小关系 景深 景深原理 卷帘相机( ...
- 使用FFMpeg API 获取摄像头的图像数据
文章目录 1. 获取摄像头的信息 2. 打开并初始化摄像头 3. 获取摄像头数据及渲染 摄像头是我们比较常用的外设,很多场景我们都会用到摄像头.比如视频直播.视频监控等各个领域都会用到摄像头.摄像头图 ...
- 【python读取小觅智能双目相机视频流】
python读取小觅智能双目相机视频流 小觅智能双目相机产品详细参数 分辨率:2560x720; 1280x480 深度分辨率:1280x720; 640x480 帧率:60FPS 双目相机可用于双目 ...
- 相册获取、相机拍摄,裁剪圆形头像
相册获取.相机拍摄,裁剪圆形头像 应用场景 很多应用都有个人中心,个人中心就会有头像,现在一般都流行圆形头像,那么怎么设置呢 使用步骤 这里参考了网上各位大神的文章,因为中途遇到几个坑,折磨了一天,快 ...
- 前端js调用后端API获取数据的三种方法(2022.7.25)
前端js调用后台API获取数据的三种方法(2022.7.25) 前言 需求分析 一个Get实例 浏览器请求 SoapUI软件请求 一个Post实例 浏览器请求 SoapUI软件请求 1.Http简介( ...
- 通过聚合数据API获取微信精选文章
思路 在聚合数据申请账号(https://www.juhe.cn/) 通过聚合数据api获取微信精选文章api 通过newspaper库提取相应的文本内容,关于newspaper库的使用方法可以参考这 ...
- redux 局部刷新_如何使用Redux Observables和刷新令牌API获取新的访问令牌
redux 局部刷新 by Sachin Kumar 由Sachin Kumar 如何使用Redux Observables和刷新令牌API获取新的访问令牌 (How to get a new acc ...
- python使用openweathermap API获取全世界主要城市天气信息
python使用openweathermap API获取全世界主要城市天气信息 官方网址: http://www.openweathermap.org/api 注意:使用gmail注册成功,然后到gm ...
最新文章
- ajax 提交订单,php-在Woocommerce 3中通过ajax提交并在结帐时创建订单
- c c 语言程序设计同步,第一部分C语言程序设计C语言程序设计同步练习答案.PDF...
- 西安交大计算机考研分数线2020院线,西安交通大学2020研究生复试分数线预计4月中旬左右公布...
- Jquery对象本质和隐式迭代
- SpringBoot基础重难点
- FLV文件中VideoPacket的详解
- google 插件_google这4款插件我每天都用,省时无数
- 自定义服务器网址,小白新手如何在服务器上搭建一个自己的网站
- 让用户感到体贴登录页设计灵感
- 常用傅里叶变换公式大全_高二数学常用导数公式大全
- python写飞机大战游戏_python实现飞机大战游戏
- 用 Python 分析了 1982 场英雄联盟数据,开局前预测游戏对局胜负!
- 2019 Flutter 心愿单
- go 实现单链表反转
- 暴增14倍!这家港股最大基金公司,1年净利20亿,竟是因为这个!
- visio-软件程序流程图规范
- 批量删除QQ空间说说代码
- 海尔电商峰值系统架构设计最佳实践
- Gradle | Gradle构建之Groovy语言基础
- Python中的self,类中的self?
热门文章
- python学习笔记之word文档提取
- 如何搭建vue脚手架
- 从万达百货到家乐福中国,苏宁如何快速消化“大块头”?
- java+jsp基于ssm的智慧医疗系统医院挂号就诊系统-计算机毕业设计
- 移动端UI设计规范模板参考以及设计规范的好处
- WiFi信号弱,选穿墙路由器还是放大器,WiFi信号放大器真的有用吗?
- 【Java 基础】字符串StringBuilder、StringBuffer,工具StringJoiner
- php 敏感关键词检测,字符串 - PHP 敏感词违法关键字检测 算法方案
- 【行业标准】YBT092-2019-合金铸铁磨球(高中低铬铸造钢球)
- caspase家族的特点_caspase家族