mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
Add support for USB Webcams on Windows (#1390)
This commit is contained in:
committed by
Peter Johnson
parent
70a66fc943
commit
69cb53b51b
@@ -5,6 +5,7 @@ plugins {
|
||||
id 'edu.wpi.first.GradleJni' version '0.3.1'
|
||||
id 'edu.wpi.first.GradleVsCode' version '0.6.1'
|
||||
id 'idea'
|
||||
id 'visual-studio'
|
||||
id 'com.gradle.build-scan' version '1.15.1'
|
||||
id 'net.ltgt.errorprone' version '0.6' apply false
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ ext {
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
cscoreMacCpp(CppSourceSet) {
|
||||
@@ -38,6 +39,7 @@ ext {
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include', 'src/main/native/cpp'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +52,7 @@ ext {
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include', 'src/main/native/cpp'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +65,7 @@ ext {
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include', 'src/main/native/cpp'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,11 +98,11 @@ model {
|
||||
x86ExcludeSymbols = ['_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure',
|
||||
'_CT??_R0?AVbad_cast',
|
||||
'_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure',
|
||||
'_TI5?AVfailure']
|
||||
'_TI5?AVfailure', '==']
|
||||
x64ExcludeSymbols = ['_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure',
|
||||
'_CT??_R0?AVbad_cast',
|
||||
'_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure',
|
||||
'_TI5?AVfailure']
|
||||
'_TI5?AVfailure', '==']
|
||||
}
|
||||
cscoreJNI(ExportsConfig) {
|
||||
x86SymbolFilter = { symbols ->
|
||||
|
||||
@@ -69,7 +69,8 @@ enum CS_StatusValue {
|
||||
CS_SOURCE_IS_DISCONNECTED = -2005,
|
||||
CS_EMPTY_VALUE = -2006,
|
||||
CS_BAD_URL = -2007,
|
||||
CS_TELEMETRY_NOT_ENABLED = -2008
|
||||
CS_TELEMETRY_NOT_ENABLED = -2008,
|
||||
CS_UNSUPPORTED_MODE = -2009
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,6 +79,13 @@ struct VideoMode : public CS_VideoMode {
|
||||
fps = fps_;
|
||||
}
|
||||
explicit operator bool() const { return pixelFormat == kUnknown; }
|
||||
|
||||
bool operator==(const VideoMode& other) const {
|
||||
return pixelFormat == other.pixelFormat && width == other.width &&
|
||||
height == other.height && fps == other.fps;
|
||||
}
|
||||
|
||||
bool operator!=(const VideoMode& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
158
cscore/src/main/native/windows/COMCreators.cpp
Normal file
158
cscore/src/main/native/windows/COMCreators.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#include <shlwapi.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "UsbCameraImpl.h"
|
||||
|
||||
// https://github.com/opencv/opencv/blob/master/modules/videoio/src/cap_msmf.cpp
|
||||
|
||||
#include <mfidl.h>
|
||||
#include <mfapi.h>
|
||||
#include <Dbt.h>
|
||||
#include <ks.h>
|
||||
#include <ksmedia.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
#include "COMCreators.h"
|
||||
#include "ComPtr.h"
|
||||
|
||||
#pragma comment(lib, "Mfplat.lib")
|
||||
#pragma comment(lib, "Mf.lib")
|
||||
#pragma comment(lib, "mfuuid.lib")
|
||||
#pragma comment(lib, "Ole32.lib")
|
||||
#pragma comment(lib, "User32.lib")
|
||||
#pragma comment(lib, "Mfreadwrite.lib")
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
namespace cs {
|
||||
|
||||
SourceReaderCB::SourceReaderCB(std::weak_ptr<cs::UsbCameraImpl> source,
|
||||
const cs::VideoMode& mode)
|
||||
: m_nRefCount(1), m_source(source), m_mode{mode} {}
|
||||
|
||||
// IUnknown methods
|
||||
STDMETHODIMP SourceReaderCB::QueryInterface(REFIID iid, void** ppv) {
|
||||
static const QITAB qit[] = {
|
||||
QITABENT(SourceReaderCB, IMFSourceReaderCallback),
|
||||
{0},
|
||||
};
|
||||
return QISearch(this, qit, iid, ppv);
|
||||
}
|
||||
STDMETHODIMP_(ULONG) SourceReaderCB::AddRef() {
|
||||
return InterlockedIncrement(&m_nRefCount);
|
||||
}
|
||||
STDMETHODIMP_(ULONG) SourceReaderCB::Release() {
|
||||
ULONG uCount = InterlockedDecrement(&m_nRefCount);
|
||||
if (uCount == 0) {
|
||||
delete this;
|
||||
}
|
||||
return uCount;
|
||||
}
|
||||
|
||||
STDMETHODIMP SourceReaderCB::OnEvent(DWORD, IMFMediaEvent*) { return S_OK; }
|
||||
|
||||
STDMETHODIMP SourceReaderCB::OnFlush(DWORD) { return S_OK; }
|
||||
|
||||
void SourceReaderCB::NotifyError(HRESULT hr) {
|
||||
wprintf(L"Source Reader error: 0x%X\n", hr);
|
||||
}
|
||||
|
||||
STDMETHODIMP SourceReaderCB::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
|
||||
DWORD dwStreamFlags,
|
||||
LONGLONG llTimestamp,
|
||||
IMFSample* pSample // Can be NULL
|
||||
) {
|
||||
auto source = m_source.lock();
|
||||
if (!source) return S_OK;
|
||||
if (SUCCEEDED(hrStatus)) {
|
||||
if (pSample) {
|
||||
// Prcoess sample
|
||||
source->ProcessFrame(pSample, m_mode);
|
||||
// DO NOT release the frame
|
||||
}
|
||||
} else {
|
||||
// Streaming error.
|
||||
NotifyError(hrStatus);
|
||||
}
|
||||
// Trigger asking for a new frame.
|
||||
// This is piped through the message pump for concurrency reasons
|
||||
source->PostRequestNewFrame();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Create a Source Reader COM Smart Object
|
||||
ComPtr<SourceReaderCB> CreateSourceReaderCB(
|
||||
std::weak_ptr<cs::UsbCameraImpl> source, const cs::VideoMode& mode) {
|
||||
SourceReaderCB* ptr = new SourceReaderCB(source, mode);
|
||||
ComPtr<SourceReaderCB> sourceReaderCB;
|
||||
sourceReaderCB.Attach(ptr);
|
||||
return sourceReaderCB;
|
||||
}
|
||||
|
||||
ComPtr<IMFMediaSource> CreateVideoCaptureDevice(LPCWSTR pszSymbolicLink) {
|
||||
ComPtr<IMFAttributes> pAttributes;
|
||||
ComPtr<IMFMediaSource> pSource;
|
||||
|
||||
HRESULT hr = MFCreateAttributes(pAttributes.GetAddressOf(), 2);
|
||||
|
||||
// Set the device type to video.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
|
||||
}
|
||||
|
||||
// Set the symbolic link.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pAttributes->SetString(
|
||||
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
|
||||
pszSymbolicLink);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = MFCreateDeviceSource(pAttributes.Get(), pSource.GetAddressOf());
|
||||
}
|
||||
|
||||
// No need to check last HR, as the source would be null anyway.
|
||||
return pSource;
|
||||
}
|
||||
|
||||
ComPtr<IMFSourceReader> CreateSourceReader(IMFMediaSource* mediaSource,
|
||||
IMFSourceReaderCallback* callback) {
|
||||
HRESULT hr = S_OK;
|
||||
ComPtr<IMFAttributes> pAttributes;
|
||||
ComPtr<IMFSourceReader> sourceReader;
|
||||
|
||||
hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hr = pAttributes->SetUINT32(
|
||||
MF_SOURCE_READER_DISCONNECT_MEDIASOURCE_ON_SHUTDOWN, TRUE);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MFCreateSourceReaderFromMediaSource(mediaSource, pAttributes.Get(),
|
||||
sourceReader.GetAddressOf());
|
||||
|
||||
// No need to check last HR, as the sourceReader would be null anyway.
|
||||
return sourceReader;
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
58
cscore/src/main/native/windows/COMCreators.h
Normal file
58
cscore/src/main/native/windows/COMCreators.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "ComPtr.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class UsbCameraImpl;
|
||||
|
||||
// Source callback used by the source reader.
|
||||
// COM object, so it needs a to ref count itself.
|
||||
class SourceReaderCB : public IMFSourceReaderCallback {
|
||||
public:
|
||||
explicit SourceReaderCB(std::weak_ptr<cs::UsbCameraImpl> source,
|
||||
const cs::VideoMode& mode);
|
||||
void SetVideoMode(const VideoMode& mode) { m_mode = mode; }
|
||||
|
||||
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent*);
|
||||
STDMETHODIMP OnFlush(DWORD);
|
||||
STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex,
|
||||
DWORD dwStreamFlags, LONGLONG llTimestamp,
|
||||
IMFSample* pSample // Can be NULL
|
||||
);
|
||||
|
||||
void InvalidateCapture() { m_source = std::weak_ptr<cs::UsbCameraImpl>(); }
|
||||
|
||||
private:
|
||||
// Destructor is private. Caller should call Release.
|
||||
virtual ~SourceReaderCB() {}
|
||||
void NotifyError(HRESULT hr);
|
||||
|
||||
ULONG m_nRefCount;
|
||||
std::weak_ptr<cs::UsbCameraImpl> m_source;
|
||||
cs::VideoMode m_mode;
|
||||
};
|
||||
|
||||
ComPtr<SourceReaderCB> CreateSourceReaderCB(
|
||||
std::weak_ptr<cs::UsbCameraImpl> source, const cs::VideoMode& mode);
|
||||
ComPtr<IMFSourceReader> CreateSourceReader(IMFMediaSource* mediaSource,
|
||||
IMFSourceReaderCallback* callback);
|
||||
ComPtr<IMFMediaSource> CreateVideoCaptureDevice(LPCWSTR pszSymbolicLink);
|
||||
} // namespace cs
|
||||
152
cscore/src/main/native/windows/ComPtr.h
Normal file
152
cscore/src/main/native/windows/ComPtr.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <comdef.h>
|
||||
#include <shlwapi.h> // QISearch
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace cs {
|
||||
|
||||
template <typename Interface>
|
||||
class RemoveAddRefRelease : public Interface {
|
||||
ULONG __stdcall AddRef();
|
||||
ULONG __stdcall Release();
|
||||
virtual ~RemoveAddRefRelease();
|
||||
};
|
||||
|
||||
template <typename Interface>
|
||||
class ComPtr {
|
||||
public:
|
||||
template <typename T>
|
||||
friend class ComPtr;
|
||||
|
||||
ComPtr(std::nullptr_t = nullptr) noexcept {} // NOLINT(runtime/explicit)
|
||||
ComPtr(const ComPtr& other) noexcept : m_ptr(other.m_ptr) {
|
||||
InternalAddRef();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComPtr(const ComPtr<T>& other) noexcept : m_ptr(other.m_ptr) {
|
||||
InternalAddRef();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComPtr(ComPtr<T>&& other) noexcept : m_ptr(other.m_ptr) {
|
||||
other.m_ptr = nullptr;
|
||||
}
|
||||
|
||||
~ComPtr() noexcept { InternalRelease(); }
|
||||
|
||||
ComPtr& operator=(const ComPtr& other) noexcept {
|
||||
InternalCopy(other.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComPtr& operator=(const ComPtr<T>& other) noexcept {
|
||||
InternalCopy(other.m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComPtr& operator=(ComPtr<T>&& other) noexcept {
|
||||
InternalMove(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Swap(ComPtr& other) noexcept {
|
||||
Interface* temp = m_ptr;
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = temp;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept { return nullptr != m_ptr; }
|
||||
|
||||
void Reset() noexcept { InternalRelease(); }
|
||||
|
||||
Interface* Get() const noexcept { return m_ptr; }
|
||||
|
||||
Interface* Detach() noexcept {
|
||||
Interface* temp = m_ptr;
|
||||
m_ptr = nullptr;
|
||||
return temp;
|
||||
}
|
||||
|
||||
void Copy(Interface* other) noexcept { InternalCopy(other); }
|
||||
|
||||
void Attach(Interface* other) noexcept {
|
||||
InternalRelease();
|
||||
m_ptr = other;
|
||||
}
|
||||
|
||||
Interface** GetAddressOf() noexcept {
|
||||
assert(m_ptr == nullptr);
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
void CopyTo(Interface** other) const noexcept {
|
||||
InternalAddRef();
|
||||
*other = m_ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ComPtr<T> As() const noexcept {
|
||||
ComPtr<T> temp;
|
||||
m_ptr->QueryInterface(temp.GetAddressOf());
|
||||
return temp;
|
||||
}
|
||||
|
||||
RemoveAddRefRelease<Interface>* operator->() const noexcept {
|
||||
return static_cast<RemoveAddRefRelease<Interface>*>(m_ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
Interface* m_ptr = nullptr;
|
||||
|
||||
void InternalAddRef() const noexcept {
|
||||
if (m_ptr) {
|
||||
m_ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
void InternalRelease() noexcept {
|
||||
Interface* temp = m_ptr;
|
||||
if (temp) {
|
||||
m_ptr = nullptr;
|
||||
temp->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void InternalCopy(Interface* other) noexcept {
|
||||
if (m_ptr != other) {
|
||||
InternalRelease();
|
||||
m_ptr = other;
|
||||
InternalAddRef();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void InternalMove(ComPtr<T>& other) noexcept {
|
||||
if (m_ptr != other.m_ptr) {
|
||||
InternalRelease();
|
||||
m_ptr = other.m_ptr;
|
||||
other.m_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Interface>
|
||||
void swap(
|
||||
ComPtr<Interface>& left,
|
||||
ComPtr<Interface>& right) noexcept { // NOLINT(build/include_what_you_use)
|
||||
left.Swap(right);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
@@ -7,14 +7,55 @@
|
||||
|
||||
#include "NetworkListener.h"
|
||||
|
||||
#include <winsock2.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <windows.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <ws2def.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <ws2ipdef.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <iphlpapi.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <netioapi.h> // NOLINT(build/include_order)
|
||||
|
||||
#include "Instance.h"
|
||||
#include "Log.h"
|
||||
#include "Notifier.h"
|
||||
|
||||
#pragma comment(lib, "Iphlpapi.lib")
|
||||
|
||||
using namespace cs;
|
||||
|
||||
class NetworkListener::Impl {};
|
||||
class NetworkListener::Impl {
|
||||
public:
|
||||
Impl(wpi::Logger& logger, Notifier& notifier)
|
||||
: m_logger(logger), m_notifier(notifier) {}
|
||||
wpi::Logger& m_logger;
|
||||
Notifier& m_notifier;
|
||||
HANDLE eventHandle = 0;
|
||||
};
|
||||
|
||||
NetworkListener::NetworkListener(wpi::Logger& logger, Notifier& notifier) {}
|
||||
// Static Callback function for NotifyIpInterfaceChange API.
|
||||
static void WINAPI OnInterfaceChange(PVOID callerContext,
|
||||
PMIB_IPINTERFACE_ROW row,
|
||||
MIB_NOTIFICATION_TYPE notificationType) {
|
||||
Notifier* notifier = reinterpret_cast<Notifier*>(callerContext);
|
||||
notifier->NotifyNetworkInterfacesChanged();
|
||||
}
|
||||
|
||||
NetworkListener::~NetworkListener() {}
|
||||
NetworkListener::NetworkListener(wpi::Logger& logger, Notifier& notifier)
|
||||
: m_impl(std::make_unique<Impl>(logger, notifier)) {}
|
||||
|
||||
void NetworkListener::Start() {}
|
||||
NetworkListener::~NetworkListener() { Stop(); }
|
||||
|
||||
void NetworkListener::Stop() {}
|
||||
void NetworkListener::Start() {
|
||||
NotifyIpInterfaceChange(AF_INET, OnInterfaceChange, &m_impl->m_notifier, true,
|
||||
&m_impl->eventHandle);
|
||||
}
|
||||
|
||||
void NetworkListener::Stop() {
|
||||
if (m_impl->eventHandle) {
|
||||
CancelMibChangeNotify2(m_impl->eventHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,115 @@
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <winsock2.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <iphlpapi.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <ws2tcpip.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
#pragma comment(lib, "IPHLPAPI.lib")
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
#define WORKING_BUFFER_SIZE 15000
|
||||
#define MAX_TRIES 3
|
||||
|
||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
||||
|
||||
namespace cs {
|
||||
|
||||
std::vector<std::string> GetNetworkInterfaces() {
|
||||
return std::vector<std::string>{}; // TODO
|
||||
uv_interface_address_t* adrs;
|
||||
int counts = 0;
|
||||
|
||||
std::vector<std::string> addresses{};
|
||||
|
||||
uv_interface_addresses(&adrs, &counts);
|
||||
|
||||
char ip[50];
|
||||
|
||||
for (int i = 0; i < counts; i++) {
|
||||
if (adrs[i].is_internal) continue;
|
||||
std::cout << adrs[i].name << std::endl;
|
||||
InetNtop(PF_INET, &(adrs[i].netmask.netmask4.sin_addr.s_addr), ip,
|
||||
sizeof(ip) - 1);
|
||||
ip[49] = '\0';
|
||||
std::cout << ip << std::endl;
|
||||
InetNtop(PF_INET, &(adrs[i].address.address4.sin_addr.s_addr), ip,
|
||||
sizeof(ip) - 1);
|
||||
ip[49] = '\0';
|
||||
std::cout << ip << std::endl;
|
||||
addresses.emplace_back(std::string{ip});
|
||||
}
|
||||
|
||||
uv_free_interface_addresses(adrs, counts);
|
||||
|
||||
std::cout << "finished\n";
|
||||
|
||||
return addresses;
|
||||
|
||||
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
|
||||
PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
|
||||
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
|
||||
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
|
||||
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
|
||||
unsigned int i = 0;
|
||||
|
||||
ULONG outBufLen = 0;
|
||||
ULONG Iterations = 0;
|
||||
DWORD dwRetVal = 0;
|
||||
|
||||
// Allocate a 15 KB buffer to start with.
|
||||
outBufLen = WORKING_BUFFER_SIZE;
|
||||
|
||||
do {
|
||||
pAddresses = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(MALLOC(outBufLen));
|
||||
if (pAddresses == NULL) {
|
||||
std::printf("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL,
|
||||
pAddresses, &outBufLen);
|
||||
|
||||
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
|
||||
FREE(pAddresses);
|
||||
pAddresses = NULL;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
Iterations++;
|
||||
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
|
||||
|
||||
if (dwRetVal == NO_ERROR) {
|
||||
pCurrAddresses = pAddresses;
|
||||
while (pCurrAddresses) {
|
||||
pUnicast = pCurrAddresses->FirstUnicastAddress;
|
||||
while (pUnicast != NULL) {
|
||||
sockaddr_in* address =
|
||||
reinterpret_cast<sockaddr_in*>(pUnicast->Address.lpSockaddr);
|
||||
InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
|
||||
ip[49] = '\0';
|
||||
addresses.emplace_back(std::string{ip});
|
||||
pUnicast = pUnicast->Next;
|
||||
}
|
||||
|
||||
pCurrAddresses = pCurrAddresses->Next;
|
||||
}
|
||||
}
|
||||
|
||||
if (pAddresses) {
|
||||
FREE(pAddresses);
|
||||
}
|
||||
|
||||
return addresses; // TODO
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
184
cscore/src/main/native/windows/UsbCameraImpl.h
Normal file
184
cscore/src/main/native/windows/UsbCameraImpl.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_USBCAMERAIMPL_H_
|
||||
#define CSCORE_USBCAMERAIMPL_H_
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
#include <ks.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <ksmedia.h> // NOLINT(build/include_order)
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <Dbt.h>
|
||||
#include <wpi/STLExtras.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/condition_variable.h>
|
||||
#include <wpi/mutex.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "ComCreators.h"
|
||||
#include "ComPtr.h"
|
||||
#include "SourceImpl.h"
|
||||
#include "UsbCameraProperty.h"
|
||||
#include "WindowsMessagePump.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class UsbCameraImpl : public SourceImpl,
|
||||
public std::enable_shared_from_this<UsbCameraImpl> {
|
||||
public:
|
||||
UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry, const wpi::Twine& path);
|
||||
UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry, int deviceId);
|
||||
~UsbCameraImpl() override;
|
||||
|
||||
void Start();
|
||||
|
||||
// Property functions
|
||||
void SetProperty(int property, int value, CS_Status* status) override;
|
||||
void SetStringProperty(int property, const wpi::Twine& value,
|
||||
CS_Status* status) override;
|
||||
|
||||
// Standard common camera properties
|
||||
void SetBrightness(int brightness, CS_Status* status) override;
|
||||
int GetBrightness(CS_Status* status) const override;
|
||||
void SetWhiteBalanceAuto(CS_Status* status) override;
|
||||
void SetWhiteBalanceHoldCurrent(CS_Status* status) override;
|
||||
void SetWhiteBalanceManual(int value, CS_Status* status) override;
|
||||
void SetExposureAuto(CS_Status* status) override;
|
||||
void SetExposureHoldCurrent(CS_Status* status) override;
|
||||
void SetExposureManual(int value, CS_Status* status) override;
|
||||
|
||||
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
|
||||
bool SetPixelFormat(VideoMode::PixelFormat pixelFormat,
|
||||
CS_Status* status) override;
|
||||
bool SetResolution(int width, int height, CS_Status* status) override;
|
||||
bool SetFPS(int fps, CS_Status* status) override;
|
||||
|
||||
void NumSinksChanged() override;
|
||||
void NumSinksEnabledChanged() override;
|
||||
|
||||
void ProcessFrame(IMFSample* sample, const VideoMode& mode);
|
||||
void PostRequestNewFrame();
|
||||
|
||||
std::string GetPath() { return m_path; }
|
||||
|
||||
// Messages passed to/from camera thread
|
||||
struct Message {
|
||||
enum Kind {
|
||||
kNone = 0,
|
||||
kCmdSetMode,
|
||||
kCmdSetPixelFormat,
|
||||
kCmdSetResolution,
|
||||
kCmdSetFPS,
|
||||
kCmdSetProperty,
|
||||
kCmdSetPropertyStr,
|
||||
kNumSinksChanged, // no response
|
||||
kNumSinksEnabledChanged, // no response
|
||||
// Responses
|
||||
kOk,
|
||||
kError
|
||||
};
|
||||
|
||||
explicit Message(Kind kind_)
|
||||
: kind(kind_), from(std::this_thread::get_id()) {}
|
||||
|
||||
Kind kind;
|
||||
int data[4];
|
||||
std::string dataStr;
|
||||
std::thread::id from;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::unique_ptr<PropertyImpl> CreateEmptyProperty(
|
||||
const wpi::Twine& name) const override;
|
||||
|
||||
// Cache properties. Immediately successful if properties are already cached.
|
||||
// If they are not, tries to connect to the camera to do so; returns false and
|
||||
// sets status to CS_SOURCE_IS_DISCONNECTED if that too fails.
|
||||
bool CacheProperties(CS_Status* status) const override;
|
||||
|
||||
private:
|
||||
// The camera processing thread
|
||||
void CameraThreadMain();
|
||||
|
||||
LRESULT PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
bool CheckDeviceChange(WPARAM wParam, DEV_BROADCAST_HDR* pHdr,
|
||||
bool* connected);
|
||||
|
||||
// Functions used by CameraThreadMain()
|
||||
void DeviceDisconnect();
|
||||
bool DeviceConnect();
|
||||
bool DeviceStreamOn();
|
||||
bool DeviceStreamOff();
|
||||
CS_StatusValue DeviceSetMode();
|
||||
void DeviceCacheMode();
|
||||
void DeviceCacheProperty(std::unique_ptr<UsbCameraProperty> rawProp,
|
||||
IAMVideoProcAmp* pProcAmp);
|
||||
void DeviceCacheProperties();
|
||||
void DeviceCacheVideoModes();
|
||||
void DeviceAddProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
||||
IAMVideoProcAmp* pProcAmp);
|
||||
|
||||
ComPtr<IMFMediaType> DeviceCheckModeValid(const VideoMode& toCheck);
|
||||
|
||||
// Command helper functions
|
||||
CS_StatusValue DeviceProcessCommand(std::unique_lock<wpi::mutex>& lock,
|
||||
Message::Kind msgKind,
|
||||
const Message* msg);
|
||||
CS_StatusValue DeviceCmdSetMode(std::unique_lock<wpi::mutex>& lock,
|
||||
const Message& msg);
|
||||
CS_StatusValue DeviceCmdSetProperty(std::unique_lock<wpi::mutex>& lock,
|
||||
const Message& msg);
|
||||
|
||||
// Property helper functions
|
||||
int RawToPercentage(const UsbCameraProperty& rawProp, int rawValue);
|
||||
int PercentageToRaw(const UsbCameraProperty& rawProp, int percentValue);
|
||||
|
||||
//
|
||||
// Variables only used within camera thread
|
||||
//
|
||||
bool m_streaming{false};
|
||||
bool m_wasStreaming{false};
|
||||
bool m_modeSet{false};
|
||||
int m_connectVerbose{1};
|
||||
bool m_deviceValid{false};
|
||||
|
||||
ComPtr<IMFMediaSource> m_mediaSource;
|
||||
ComPtr<IMFSourceReader> m_sourceReader;
|
||||
ComPtr<SourceReaderCB> m_imageCallback;
|
||||
std::unique_ptr<cs::WindowsMessagePump> m_messagePump;
|
||||
ComPtr<IMFMediaType> m_currentMode;
|
||||
|
||||
//
|
||||
// Path never changes, so not protected by mutex.
|
||||
//
|
||||
std::string m_path;
|
||||
|
||||
std::wstring m_widePath;
|
||||
int m_deviceId;
|
||||
|
||||
std::vector<std::pair<VideoMode, ComPtr<IMFMediaType>>> m_windowsVideoModes;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_USBCAMERAIMPL_H_
|
||||
69
cscore/src/main/native/windows/UsbCameraProperty.cpp
Normal file
69
cscore/src/main/native/windows/UsbCameraProperty.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "UsbCameraProperty.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
|
||||
tagVideoProcAmpProperty tag, bool autoProp,
|
||||
IAMVideoProcAmp* pProcAmp, bool* isValid)
|
||||
: PropertyImpl{autoProp ? name_ + "_auto" : name_} {
|
||||
this->tag = tag;
|
||||
this->isAutoProp = autoProp;
|
||||
long paramVal, paramFlag; // NOLINT(runtime/int)
|
||||
HRESULT hr;
|
||||
long minVal, maxVal, stepVal; // NOLINT(runtime/int)
|
||||
hr = pProcAmp->GetRange(tag, &minVal, &maxVal, &stepVal, ¶mVal,
|
||||
¶mFlag); // Unable to get the property, trying to
|
||||
// return default value
|
||||
if (SUCCEEDED(hr)) {
|
||||
minimum = minVal;
|
||||
maximum = maxVal;
|
||||
hasMaximum = true;
|
||||
hasMinimum = true;
|
||||
defaultValue = paramVal;
|
||||
step = stepVal;
|
||||
value = paramVal;
|
||||
propKind = CS_PropertyKind::CS_PROP_INTEGER;
|
||||
*isValid = true;
|
||||
} else {
|
||||
*isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
|
||||
IAMVideoProcAmp* pProcAmp) {
|
||||
if (!pProcAmp) return true;
|
||||
|
||||
lock.unlock();
|
||||
long newValue = 0, paramFlag = 0; // NOLINT(runtime/int)
|
||||
if (SUCCEEDED(pProcAmp->Get(tag, &newValue, ¶mFlag))) {
|
||||
lock.lock();
|
||||
value = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||
IAMVideoProcAmp* pProcAmp) const {
|
||||
return DeviceSet(lock, pProcAmp, value);
|
||||
}
|
||||
bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||
IAMVideoProcAmp* pProcAmp,
|
||||
int newValue) const {
|
||||
if (!pProcAmp) return true;
|
||||
|
||||
lock.unlock();
|
||||
if (SUCCEEDED(pProcAmp->Set(tag, newValue, VideoProcAmp_Flags_Manual))) {
|
||||
lock.lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
72
cscore/src/main/native/windows/UsbCameraProperty.h
Normal file
72
cscore/src/main/native/windows/UsbCameraProperty.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <Dshow.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "PropertyImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
// Property data
|
||||
class UsbCameraProperty : public PropertyImpl {
|
||||
public:
|
||||
UsbCameraProperty() = default;
|
||||
explicit UsbCameraProperty(const wpi::Twine& name_) : PropertyImpl{name_} {}
|
||||
|
||||
// Software property constructor
|
||||
UsbCameraProperty(const wpi::Twine& name_, unsigned id_,
|
||||
CS_PropertyKind kind_, int minimum_, int maximum_,
|
||||
int step_, int defaultValue_, int value_)
|
||||
: PropertyImpl(name_, kind_, minimum_, maximum_, step_, defaultValue_,
|
||||
value_),
|
||||
device{false},
|
||||
id{id_} {}
|
||||
|
||||
// Normalized property constructor
|
||||
UsbCameraProperty(const wpi::Twine& name_, int rawIndex_,
|
||||
const UsbCameraProperty& rawProp, int defaultValue_,
|
||||
int value_)
|
||||
: PropertyImpl(name_, rawProp.propKind, 1, defaultValue_, value_),
|
||||
percentage{true},
|
||||
propPair{rawIndex_},
|
||||
id{rawProp.id},
|
||||
type{rawProp.type} {
|
||||
hasMinimum = true;
|
||||
minimum = 0;
|
||||
hasMaximum = true;
|
||||
maximum = 100;
|
||||
}
|
||||
|
||||
UsbCameraProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
|
||||
bool autoProp, IAMVideoProcAmp* pProcAmp, bool* isValid);
|
||||
|
||||
bool DeviceGet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp);
|
||||
bool DeviceSet(std::unique_lock<wpi::mutex>& lock,
|
||||
IAMVideoProcAmp* pProcAmp) const;
|
||||
bool DeviceSet(std::unique_lock<wpi::mutex>& lock, IAMVideoProcAmp* pProcAmp,
|
||||
int newValue) const;
|
||||
|
||||
// If this is a device (rather than software) property
|
||||
bool device{true};
|
||||
bool isAutoProp{true};
|
||||
tagVideoProcAmpProperty tag;
|
||||
|
||||
// If this is a percentage (rather than raw) property
|
||||
bool percentage{false};
|
||||
|
||||
// If not 0, index of corresponding raw/percentage property
|
||||
int propPair{0};
|
||||
|
||||
unsigned id{0}; // implementation-level id
|
||||
int type{0}; // implementation type, not CS_PropertyKind!
|
||||
};
|
||||
} // namespace cs
|
||||
152
cscore/src/main/native/windows/WindowsMessagePump.cpp
Normal file
152
cscore/src/main/native/windows/WindowsMessagePump.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WindowsMessagePump.h"
|
||||
|
||||
#include <ks.h>
|
||||
#include <ksmedia.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <Dbt.h>
|
||||
|
||||
#pragma comment(lib, "Mfplat.lib")
|
||||
#pragma comment(lib, "Mf.lib")
|
||||
#pragma comment(lib, "mfuuid.lib")
|
||||
#pragma comment(lib, "Ole32.lib")
|
||||
#pragma comment(lib, "User32.lib")
|
||||
|
||||
namespace cs {
|
||||
|
||||
static LRESULT CALLBACK pWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
WindowsMessagePump* pumpContainer;
|
||||
// Our "this" parameter is passed only during WM_CREATE
|
||||
// If it is create, store in our user parameter
|
||||
// Otherwise grab from our user parameter
|
||||
if (uiMsg == WM_CREATE) {
|
||||
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
|
||||
pumpContainer =
|
||||
reinterpret_cast<WindowsMessagePump*>(pCreate->lpCreateParams);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pumpContainer);
|
||||
SetWindowPos(hwnd, HWND_MESSAGE, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
} else {
|
||||
pumpContainer = reinterpret_cast<WindowsMessagePump*>(
|
||||
GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
// Run the callback
|
||||
bool hasCalledBack = false;
|
||||
LRESULT result;
|
||||
|
||||
if (pumpContainer) {
|
||||
hasCalledBack = true;
|
||||
result = pumpContainer->m_callback(hwnd, uiMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
// Handle a close message
|
||||
if (uiMsg == WM_CLOSE) {
|
||||
return HANDLE_WM_CLOSE(hwnd, 0, 0, [](HWND) { PostQuitMessage(0); });
|
||||
}
|
||||
|
||||
// Return message, otherwise return the base handler
|
||||
if (hasCalledBack) {
|
||||
return result;
|
||||
}
|
||||
return DefWindowProc(hwnd, uiMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct ClassHolder {
|
||||
HINSTANCE current_instance;
|
||||
WNDCLASSEX wx;
|
||||
const char* class_name = "DUMMY_CLASS";
|
||||
ClassHolder() {
|
||||
current_instance = (HINSTANCE)GetModuleHandle(NULL);
|
||||
wx = {};
|
||||
wx.cbSize = sizeof(WNDCLASSEX);
|
||||
wx.lpfnWndProc = pWndProc; // function which will handle messages
|
||||
wx.hInstance = current_instance;
|
||||
wx.lpszClassName = class_name;
|
||||
RegisterClassEx(&wx);
|
||||
}
|
||||
~ClassHolder() { UnregisterClass(class_name, current_instance); }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static std::shared_ptr<ClassHolder> GetClassHolder() {
|
||||
static std::shared_ptr<ClassHolder> clsHolder =
|
||||
std::make_shared<ClassHolder>();
|
||||
return clsHolder;
|
||||
}
|
||||
|
||||
WindowsMessagePump::WindowsMessagePump(
|
||||
std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)> callback) {
|
||||
m_callback = callback;
|
||||
auto handle = CreateEvent(NULL, true, false, NULL);
|
||||
m_mainThread = std::thread([=]() { ThreadMain(handle); });
|
||||
auto waitResult = WaitForSingleObject(handle, 1000);
|
||||
if (waitResult == WAIT_OBJECT_0) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
WindowsMessagePump::~WindowsMessagePump() {
|
||||
auto res = SendMessage(hwnd, WM_CLOSE, NULL, NULL);
|
||||
if (m_mainThread.joinable()) m_mainThread.join();
|
||||
}
|
||||
|
||||
void WindowsMessagePump::ThreadMain(HANDLE eventHandle) {
|
||||
// Initialize COM
|
||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
// Initialize MF
|
||||
MFStartup(MF_VERSION);
|
||||
|
||||
auto classHolder = GetClassHolder();
|
||||
hwnd = CreateWindowEx(0, classHolder->class_name, "dummy_name", 0, 0, 0, 0, 0,
|
||||
HWND_MESSAGE, NULL, NULL, this);
|
||||
|
||||
// Register for device notifications
|
||||
HDEVNOTIFY g_hdevnotify = NULL;
|
||||
HDEVNOTIFY g_hdevnotify2 = NULL;
|
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE di = {0};
|
||||
di.dbcc_size = sizeof(di);
|
||||
di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
di.dbcc_classguid = KSCATEGORY_CAPTURE;
|
||||
|
||||
g_hdevnotify =
|
||||
RegisterDeviceNotification(hwnd, &di, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE di2 = {0};
|
||||
di2.dbcc_size = sizeof(di2);
|
||||
di2.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||||
di2.dbcc_classguid = KSCATEGORY_VIDEO_CAMERA;
|
||||
|
||||
g_hdevnotify2 =
|
||||
RegisterDeviceNotification(hwnd, &di2, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
|
||||
SetEvent(eventHandle);
|
||||
|
||||
MSG Msg;
|
||||
while (GetMessage(&Msg, NULL, 0, 0) > 0) {
|
||||
TranslateMessage(&Msg);
|
||||
DispatchMessage(&Msg);
|
||||
}
|
||||
UnregisterDeviceNotification(g_hdevnotify);
|
||||
UnregisterDeviceNotification(g_hdevnotify2);
|
||||
|
||||
MFShutdown();
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
66
cscore/src/main/native/windows/WindowsMessagePump.h
Normal file
66
cscore/src/main/native/windows/WindowsMessagePump.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
namespace cs {
|
||||
class WindowsMessagePump {
|
||||
public:
|
||||
WindowsMessagePump(
|
||||
std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)> callback);
|
||||
~WindowsMessagePump();
|
||||
|
||||
friend LRESULT CALLBACK pWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam,
|
||||
LPARAM lParam);
|
||||
|
||||
template <typename RetVal = LRESULT, typename FirstParam = WPARAM,
|
||||
typename SecondParam = LPARAM>
|
||||
RetVal SendWindowMessage(UINT msg, FirstParam wParam, SecondParam lParam) {
|
||||
static_assert(sizeof(FirstParam) <= sizeof(WPARAM),
|
||||
"First Parameter Does Not Fit");
|
||||
static_assert(sizeof(SecondParam) <= sizeof(LPARAM),
|
||||
"Second Parameter Does Not Fit");
|
||||
static_assert(sizeof(RetVal) <= sizeof(LRESULT),
|
||||
"Return Value Does Not Fit");
|
||||
WPARAM firstToSend = 0;
|
||||
LPARAM secondToSend = 0;
|
||||
std::memcpy(&firstToSend, &wParam, sizeof(FirstParam));
|
||||
std::memcpy(&secondToSend, &lParam, sizeof(SecondParam));
|
||||
LRESULT result = SendMessage(hwnd, msg, firstToSend, secondToSend);
|
||||
RetVal toReturn;
|
||||
std::memset(&toReturn, 0, sizeof(RetVal));
|
||||
std::memcpy(&toReturn, &result, sizeof(RetVal));
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
template <typename FirstParam = WPARAM, typename SecondParam = LPARAM>
|
||||
BOOL PostWindowMessage(UINT msg, FirstParam wParam, SecondParam lParam) {
|
||||
static_assert(sizeof(FirstParam) <= sizeof(WPARAM),
|
||||
"First Parameter Does Not Fit");
|
||||
static_assert(sizeof(SecondParam) <= sizeof(LPARAM),
|
||||
"Second Parameter Does Not Fit");
|
||||
WPARAM firstToSend = 0;
|
||||
LPARAM secondToSend = 0;
|
||||
std::memcpy(&firstToSend, &wParam, sizeof(FirstParam));
|
||||
std::memcpy(&secondToSend, &lParam, sizeof(SecondParam));
|
||||
return PostMessage(hwnd, msg, firstToSend, secondToSend);
|
||||
}
|
||||
|
||||
private:
|
||||
void ThreadMain(HANDLE eventHandle);
|
||||
|
||||
HWND hwnd;
|
||||
std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)> m_callback;
|
||||
|
||||
std::thread m_mainThread;
|
||||
};
|
||||
} // namespace cs
|
||||
@@ -60,6 +60,7 @@ model {
|
||||
if (project.hasProperty('generatedHeaders')) {
|
||||
srcDir generatedHeaders
|
||||
}
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +119,9 @@ model {
|
||||
if (project.hasProperty('generatedHeaders')) {
|
||||
srcDir generatedHeaders
|
||||
}
|
||||
include '**/*.h'
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
binaries.all {
|
||||
@@ -154,6 +157,7 @@ model {
|
||||
if (project.hasProperty('generatedHeaders')) {
|
||||
srcDir generatedHeaders
|
||||
}
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user