add dshow api.
This commit is contained in:
parent
28f2f60532
commit
ce673bf330
@ -119,12 +119,15 @@ QStringList Application::availableSerialPorts() const {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Application::availableUsbVideoCameras() const {
|
QVariantList Application::availableUsbVideoCameras() const {
|
||||||
QStringList ret;
|
QVariantList ret;
|
||||||
DeviceDiscovery d;
|
DeviceDiscovery d;
|
||||||
auto devices = d.devices();
|
auto devices = d.getDevices();
|
||||||
for (auto &device : devices) {
|
for (auto &device : devices) {
|
||||||
ret << QString::fromStdString(device);
|
QVariantMap item;
|
||||||
|
item.insert("name", QString::fromStdString(device.friendlyName));
|
||||||
|
item.insert("path", QString::fromStdString(device.alternativeName));
|
||||||
|
ret << item;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ public:
|
|||||||
int exec();
|
int exec();
|
||||||
static Application *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
|
static Application *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
|
||||||
Q_INVOKABLE QStringList availableSerialPorts() const;
|
Q_INVOKABLE QStringList availableSerialPorts() const;
|
||||||
Q_INVOKABLE QStringList availableUsbVideoCameras() const;
|
Q_INVOKABLE QVariantList availableUsbVideoCameras() const;
|
||||||
Q_INVOKABLE bool open(const QString &portName, int baudRate);
|
Q_INVOKABLE bool open(const QString &portName, int baudRate);
|
||||||
Q_INVOKABLE bool openUVC(const QString &deviceName);
|
Q_INVOKABLE bool openUVC(const QString &deviceName);
|
||||||
Q_INVOKABLE void close();
|
Q_INVOKABLE void close();
|
||||||
|
@ -32,7 +32,7 @@ bool VideoPlayer::open(const std::string &deviceName) {
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
constexpr auto format = "dshow";
|
constexpr auto format = "dshow";
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "video=" << deviceName;
|
oss << "video=@device_pnp_" << deviceName;
|
||||||
auto device = oss.str();
|
auto device = oss.str();
|
||||||
#else
|
#else
|
||||||
constexpr auto format = "v4l2";
|
constexpr auto format = "v4l2";
|
||||||
@ -41,7 +41,10 @@ bool VideoPlayer::open(const std::string &deviceName) {
|
|||||||
auto inputFormat = av_find_input_format(format);
|
auto inputFormat = av_find_input_format(format);
|
||||||
|
|
||||||
AVDictionary *dictionary = nullptr;
|
AVDictionary *dictionary = nullptr;
|
||||||
|
// clang-format off
|
||||||
|
// ffplay -f dshow -i video="@device_pnp_\\?\usb#vid_3346&pid_0001&mi_00#6&6ff22b9&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
|
||||||
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
|
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
|
||||||
|
// clang-format on
|
||||||
av_dict_set(&dictionary, "video_size", "800*600", 0);
|
av_dict_set(&dictionary, "video_size", "800*600", 0);
|
||||||
|
|
||||||
int status = avformat_open_input(&m_formatContext, device.c_str(), inputFormat, &dictionary);
|
int status = avformat_open_input(&m_formatContext, device.c_str(), inputFormat, &dictionary);
|
||||||
|
@ -57,6 +57,7 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: uvcs
|
id: uvcs
|
||||||
|
textRole: "name"
|
||||||
enabled: !App.uvcOpened
|
enabled: !App.uvcOpened
|
||||||
implicitWidth: 150
|
implicitWidth: 150
|
||||||
}
|
}
|
||||||
@ -67,8 +68,7 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: App.uvcOpened ? "关闭" : "连接"
|
text: App.uvcOpened ? "关闭" : "连接"
|
||||||
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(
|
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(uvcs.currentValue.path)
|
||||||
uvcs.currentText)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,6 @@ target_include_directories(Peripheral
|
|||||||
target_link_libraries(Peripheral
|
target_link_libraries(Peripheral
|
||||||
PUBLIC Universal
|
PUBLIC Universal
|
||||||
PRIVATE Encrypt
|
PRIVATE Encrypt
|
||||||
$<$<PLATFORM_ID:Windows>:Mfreadwrite Mf mfplat mfuuid>
|
$<$<PLATFORM_ID:Windows>:Mfreadwrite Mf mfplat mfuuid strmiids comsuppw>
|
||||||
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
|
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#include "DeviceDiscovery.h"
|
#include "DeviceDiscovery.h"
|
||||||
#include "BoostLog.h"
|
#include "BoostLog.h"
|
||||||
#include "StringUtility.h"
|
|
||||||
#include <boost/scope/scope_exit.hpp>
|
#include <boost/scope/scope_exit.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
#include <comutil.h>
|
||||||
|
#include <dshow.h>
|
||||||
#include <mfapi.h>
|
#include <mfapi.h>
|
||||||
#include <mfcaptureengine.h>
|
#include <mfcaptureengine.h>
|
||||||
|
#include <strmif.h>
|
||||||
#else
|
#else
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
@ -27,6 +29,23 @@ DeviceDiscovery::DeviceDiscovery() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
||||||
|
void DeleteMediaType(AM_MEDIA_TYPE *pmt) {
|
||||||
|
if (pmt != nullptr) {
|
||||||
|
if (pmt->cbFormat != 0) {
|
||||||
|
CoTaskMemFree((PVOID)pmt->pbFormat);
|
||||||
|
pmt->cbFormat = 0;
|
||||||
|
pmt->pbFormat = nullptr;
|
||||||
|
}
|
||||||
|
if (pmt->pUnk != nullptr) {
|
||||||
|
// Unecessary because pUnk should not be used, but safest.
|
||||||
|
pmt->pUnk->Release();
|
||||||
|
pmt->pUnk = nullptr;
|
||||||
|
}
|
||||||
|
CoTaskMemFree((PVOID)pmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static std::string deviceName(IMFActivate *device) {
|
static std::string deviceName(IMFActivate *device) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
WCHAR *friendlyName = nullptr;
|
WCHAR *friendlyName = nullptr;
|
||||||
@ -34,7 +53,7 @@ static std::string deviceName(IMFActivate *device) {
|
|||||||
auto result = device->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLength);
|
auto result = device->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLength);
|
||||||
|
|
||||||
if (SUCCEEDED(result)) {
|
if (SUCCEEDED(result)) {
|
||||||
ret = Amass::StringUtility::wstringToString(std::wstring(friendlyName, nameLength));
|
ret = _com_util::ConvertBSTRToString(friendlyName);
|
||||||
}
|
}
|
||||||
if (friendlyName != nullptr) {
|
if (friendlyName != nullptr) {
|
||||||
CoTaskMemFree(friendlyName);
|
CoTaskMemFree(friendlyName);
|
||||||
@ -160,6 +179,110 @@ std::vector<std::string> DeviceDiscovery::devices() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<DeviceDiscovery::Device> DeviceDiscovery::getDevices() {
|
||||||
|
std::vector<Device> devices;
|
||||||
|
ICreateDevEnum *pDevEnum = nullptr;
|
||||||
|
IEnumMoniker *pEnum = nullptr;
|
||||||
|
|
||||||
|
HRESULT hr =
|
||||||
|
CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
pDevEnum->Release();
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMoniker *pMoniker = nullptr;
|
||||||
|
while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
|
||||||
|
Device info;
|
||||||
|
GetDeviceNames(pMoniker, info);
|
||||||
|
|
||||||
|
IBaseFilter *pFilter;
|
||||||
|
hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void **)&pFilter);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
IEnumPins *pEnumPins;
|
||||||
|
hr = pFilter->EnumPins(&pEnumPins);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
IPin *pPin;
|
||||||
|
while (pEnumPins->Next(1, &pPin, nullptr) == S_OK) {
|
||||||
|
PIN_DIRECTION pinDir;
|
||||||
|
pPin->QueryDirection(&pinDir);
|
||||||
|
if (pinDir == PINDIR_OUTPUT) {
|
||||||
|
GetSupportedResolutions(pPin, info.resolutions);
|
||||||
|
}
|
||||||
|
pPin->Release();
|
||||||
|
}
|
||||||
|
pEnumPins->Release();
|
||||||
|
}
|
||||||
|
pFilter->Release();
|
||||||
|
}
|
||||||
|
devices.push_back(info);
|
||||||
|
pMoniker->Release();
|
||||||
|
}
|
||||||
|
pEnum->Release();
|
||||||
|
pDevEnum->Release();
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceDiscovery::SetResolution(const std::string &deviceName, int width, int height) {
|
||||||
|
ICreateDevEnum *pDevEnum = nullptr;
|
||||||
|
IEnumMoniker *pEnum = nullptr;
|
||||||
|
|
||||||
|
HRESULT hr =
|
||||||
|
CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
pDevEnum->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMoniker *pMoniker = nullptr;
|
||||||
|
while (pEnum->Next(1, &pMoniker, nullptr) == S_OK) {
|
||||||
|
IPropertyBag *pPropBag;
|
||||||
|
hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
VARIANT varName;
|
||||||
|
VariantInit(&varName);
|
||||||
|
hr = pPropBag->Read(L"FriendlyName", &varName, nullptr);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
std::string currentName = _com_util::ConvertBSTRToString(varName.bstrVal);
|
||||||
|
if (currentName == deviceName) {
|
||||||
|
IBaseFilter *pFilter;
|
||||||
|
hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void **)&pFilter);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
if (SetPinResolution(pFilter, width, height)) {
|
||||||
|
pFilter->Release();
|
||||||
|
VariantClear(&varName);
|
||||||
|
pPropBag->Release();
|
||||||
|
pMoniker->Release();
|
||||||
|
pEnum->Release();
|
||||||
|
pDevEnum->Release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pFilter->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VariantClear(&varName);
|
||||||
|
pPropBag->Release();
|
||||||
|
}
|
||||||
|
pMoniker->Release();
|
||||||
|
}
|
||||||
|
pEnum->Release();
|
||||||
|
pDevEnum->Release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) {
|
DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) {
|
||||||
DeviceDiscovery::Resolutions ret;
|
DeviceDiscovery::Resolutions ret;
|
||||||
HRESULT result = S_OK;
|
HRESULT result = S_OK;
|
||||||
@ -182,6 +305,89 @@ DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::share
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeviceDiscovery::GetDeviceNames(IMoniker *pMoniker, Device &info) {
|
||||||
|
IPropertyBag *pPropBag;
|
||||||
|
HRESULT hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
VARIANT varName;
|
||||||
|
VariantInit(&varName);
|
||||||
|
hr = pPropBag->Read(L"FriendlyName", &varName, nullptr);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
|
||||||
|
info.friendlyName = _com_util::ConvertBSTRToString(varName.bstrVal);
|
||||||
|
}
|
||||||
|
VariantClear(&varName);
|
||||||
|
|
||||||
|
hr = pPropBag->Read(L"DevicePath", &varName, nullptr);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
info.alternativeName = _com_util::ConvertBSTRToString(varName.bstrVal);
|
||||||
|
}
|
||||||
|
VariantClear(&varName);
|
||||||
|
|
||||||
|
pPropBag->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceDiscovery::GetSupportedResolutions(IPin *pPin, std::vector<std::pair<int, int>> &resolutions) {
|
||||||
|
IEnumMediaTypes *pEnumMediaTypes = nullptr;
|
||||||
|
HRESULT hr = pPin->EnumMediaTypes(&pEnumMediaTypes);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AM_MEDIA_TYPE *pMediaType = nullptr;
|
||||||
|
while (pEnumMediaTypes->Next(1, &pMediaType, nullptr) == S_OK) {
|
||||||
|
if (pMediaType->formattype == FORMAT_VideoInfo) {
|
||||||
|
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER *>(pMediaType->pbFormat);
|
||||||
|
int width = pVih->bmiHeader.biWidth;
|
||||||
|
int height = pVih->bmiHeader.biHeight;
|
||||||
|
resolutions.emplace_back(width, height);
|
||||||
|
}
|
||||||
|
DeleteMediaType(pMediaType);
|
||||||
|
}
|
||||||
|
pEnumMediaTypes->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeviceDiscovery::SetPinResolution(IBaseFilter *pFilter, int width, int height) {
|
||||||
|
IEnumPins *pEnumPins;
|
||||||
|
HRESULT hr = pFilter->EnumPins(&pEnumPins);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPin *pPin;
|
||||||
|
bool success = false;
|
||||||
|
while (pEnumPins->Next(1, &pPin, nullptr) == S_OK) {
|
||||||
|
PIN_DIRECTION pinDir;
|
||||||
|
pPin->QueryDirection(&pinDir);
|
||||||
|
if (pinDir == PINDIR_OUTPUT) {
|
||||||
|
IEnumMediaTypes *pEnumMediaTypes = nullptr;
|
||||||
|
hr = pPin->EnumMediaTypes(&pEnumMediaTypes);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
AM_MEDIA_TYPE *pMediaType = nullptr;
|
||||||
|
while (pEnumMediaTypes->Next(1, &pMediaType, nullptr) == S_OK) {
|
||||||
|
if (pMediaType->formattype == FORMAT_VideoInfo) {
|
||||||
|
VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER *>(pMediaType->pbFormat);
|
||||||
|
if (pVih->bmiHeader.biWidth == width && pVih->bmiHeader.biHeight == height) {
|
||||||
|
hr = pPin->QueryAccept(pMediaType);
|
||||||
|
if (hr == S_OK) {
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeleteMediaType(pMediaType);
|
||||||
|
if (success) break;
|
||||||
|
}
|
||||||
|
pEnumMediaTypes->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pPin->Release();
|
||||||
|
if (success) break;
|
||||||
|
}
|
||||||
|
pEnumPins->Release();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
DeviceDiscovery::Device::Device(IMFMediaSource *source) : source(source) {
|
DeviceDiscovery::Device::Device(IMFMediaSource *source) : source(source) {
|
||||||
source->AddRef();
|
source->AddRef();
|
||||||
auto result = MFCreateSourceReaderFromMediaSource(source, nullptr, &reader);
|
auto result = MFCreateSourceReaderFromMediaSource(source, nullptr, &reader);
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <mfidl.h>
|
#include <mfidl.h>
|
||||||
#include <mfreadwrite.h>
|
#include <mfreadwrite.h>
|
||||||
|
|
||||||
|
class IPin;
|
||||||
|
class IBaseFilter;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class DeviceDiscovery {
|
class DeviceDiscovery {
|
||||||
@ -15,25 +18,34 @@ class DeviceDiscovery {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr static auto DeviceName = "UVC Camera";
|
constexpr static auto DeviceName = "UVC Camera";
|
||||||
|
using Resolution = std::pair<int32_t, int32_t>;
|
||||||
|
using Resolutions = std::vector<Resolution>;
|
||||||
struct Device {
|
struct Device {
|
||||||
|
Device() = default;
|
||||||
|
std::string friendlyName;
|
||||||
|
std::string alternativeName;
|
||||||
|
Resolutions resolutions;
|
||||||
~Device();
|
~Device();
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
Device(IMFMediaSource *source);
|
Device(IMFMediaSource *source);
|
||||||
IMFMediaSource *source = nullptr;
|
IMFMediaSource *source = nullptr;
|
||||||
IMFSourceReader *reader = nullptr;
|
IMFSourceReader *reader = nullptr;
|
||||||
#else
|
|
||||||
std::string name;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
using Resolution = std::pair<int32_t, int32_t>;
|
|
||||||
using Resolutions = std::vector<Resolution>;
|
|
||||||
DeviceDiscovery();
|
DeviceDiscovery();
|
||||||
std::shared_ptr<Device> find(const std::string &deviceName, std::error_code &error);
|
std::shared_ptr<Device> find(const std::string &deviceName, std::error_code &error);
|
||||||
void enterOtaMode(const std::shared_ptr<Device> &device, std::error_code &error);
|
void enterOtaMode(const std::shared_ptr<Device> &device, std::error_code &error);
|
||||||
std::vector<std::string> devices();
|
std::vector<std::string> devices();
|
||||||
|
std::vector<Device> getDevices();
|
||||||
|
bool SetResolution(const std::string &deviceName, int width, int height);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Resolutions deviceResolutions(const std::shared_ptr<Device> &source);
|
Resolutions deviceResolutions(const std::shared_ptr<Device> &source);
|
||||||
|
#ifdef WIN32
|
||||||
|
void GetDeviceNames(IMoniker *pMoniker, Device &info);
|
||||||
|
void GetSupportedResolutions(IPin *pPin, std::vector<std::pair<int, int>> &resolutions);
|
||||||
|
bool SetPinResolution(IBaseFilter *pFilter, int width, int height);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
#endif // __DEVICEDISCOVERY_H__
|
#endif // __DEVICEDISCOVERY_H__
|
||||||
|
Loading…
Reference in New Issue
Block a user