add dshow api.
Some checks failed
Build Applications / Build (push) Failing after 1m17s
Windows CI / build (push) Successful in 3m4s

This commit is contained in:
luocai 2024-11-21 19:34:13 +08:00
parent 28f2f60532
commit ce673bf330
7 changed files with 239 additions and 15 deletions

View File

@ -119,12 +119,15 @@ QStringList Application::availableSerialPorts() const {
return ret;
}
QStringList Application::availableUsbVideoCameras() const {
QStringList ret;
QVariantList Application::availableUsbVideoCameras() const {
QVariantList ret;
DeviceDiscovery d;
auto devices = d.devices();
auto devices = d.getDevices();
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;
}

View File

@ -43,7 +43,7 @@ public:
int exec();
static Application *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
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 openUVC(const QString &deviceName);
Q_INVOKABLE void close();

View File

@ -32,7 +32,7 @@ bool VideoPlayer::open(const std::string &deviceName) {
#ifdef WIN32
constexpr auto format = "dshow";
std::ostringstream oss;
oss << "video=" << deviceName;
oss << "video=@device_pnp_" << deviceName;
auto device = oss.str();
#else
constexpr auto format = "v4l2";
@ -41,7 +41,10 @@ bool VideoPlayer::open(const std::string &deviceName) {
auto inputFormat = av_find_input_format(format);
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"
// clang-format on
av_dict_set(&dictionary, "video_size", "800*600", 0);
int status = avformat_open_input(&m_formatContext, device.c_str(), inputFormat, &dictionary);

View File

@ -57,6 +57,7 @@ RowLayout {
}
ComboBox {
id: uvcs
textRole: "name"
enabled: !App.uvcOpened
implicitWidth: 150
}
@ -67,8 +68,7 @@ RowLayout {
}
Button {
text: App.uvcOpened ? "关闭" : "连接"
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(
uvcs.currentText)
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(uvcs.currentValue.path)
}
}
}

View File

@ -15,6 +15,6 @@ target_include_directories(Peripheral
target_link_libraries(Peripheral
PUBLIC Universal
PRIVATE Encrypt
$<$<PLATFORM_ID:Windows>:Mfreadwrite Mf mfplat mfuuid>
$<$<PLATFORM_ID:Windows>:Mfreadwrite Mf mfplat mfuuid strmiids comsuppw>
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
)

View File

@ -1,13 +1,15 @@
#include "DeviceDiscovery.h"
#include "BoostLog.h"
#include "StringUtility.h"
#include <boost/scope/scope_exit.hpp>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#ifdef WIN32
#include <comutil.h>
#include <dshow.h>
#include <mfapi.h>
#include <mfcaptureengine.h>
#include <strmif.h>
#else
#include <fcntl.h>
#include <linux/videodev2.h>
@ -27,6 +29,23 @@ DeviceDiscovery::DeviceDiscovery() {
}
#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) {
std::string ret;
WCHAR *friendlyName = nullptr;
@ -34,7 +53,7 @@ static std::string deviceName(IMFActivate *device) {
auto result = device->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLength);
if (SUCCEEDED(result)) {
ret = Amass::StringUtility::wstringToString(std::wstring(friendlyName, nameLength));
ret = _com_util::ConvertBSTRToString(friendlyName);
}
if (friendlyName != nullptr) {
CoTaskMemFree(friendlyName);
@ -160,6 +179,110 @@ std::vector<std::string> DeviceDiscovery::devices() {
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 ret;
HRESULT result = S_OK;
@ -182,6 +305,89 @@ DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::share
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) {
source->AddRef();
auto result = MFCreateSourceReaderFromMediaSource(source, nullptr, &reader);

View File

@ -8,6 +8,9 @@
#ifdef WIN32
#include <mfidl.h>
#include <mfreadwrite.h>
class IPin;
class IBaseFilter;
#endif
class DeviceDiscovery {
@ -15,25 +18,34 @@ class DeviceDiscovery {
public:
constexpr static auto DeviceName = "UVC Camera";
using Resolution = std::pair<int32_t, int32_t>;
using Resolutions = std::vector<Resolution>;
struct Device {
Device() = default;
std::string friendlyName;
std::string alternativeName;
Resolutions resolutions;
~Device();
#ifdef WIN32
Device(IMFMediaSource *source);
IMFMediaSource *source = nullptr;
IMFSourceReader *reader = nullptr;
#else
std::string name;
#endif
};
using Resolution = std::pair<int32_t, int32_t>;
using Resolutions = std::vector<Resolution>;
DeviceDiscovery();
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);
std::vector<std::string> devices();
std::vector<Device> getDevices();
bool SetResolution(const std::string &deviceName, int width, int height);
protected:
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__