Now the Windows plugin can load PNG, JPEG and other image formats as a

place holder picture.
Settings moved from HKLM to HKCU, now it should be possible to register
the filter per user instead of globally, this way it won't require
administrative privileges.
Notify to the clients about virtual devices changes.
This commit is contained in:
Gonzalo Exequiel Pedone 2020-10-07 16:27:38 -03:00
parent 1cf50519bc
commit 7d38a15856
No known key found for this signature in database
GPG key ID: B8B09E63E9B85BAF
8 changed files with 146 additions and 19 deletions

View file

@ -338,11 +338,11 @@ AkVCam::VideoFrame AkVCam::loadPicture(const std::string &fileName)
CGImageRelease(cgImage); CGImageRelease(cgImage);
AkLogDebug() << "Picture loaded as: " AkLogDebug() << "Picture loaded as: "
<< VideoFormat::stringFromFourcc(videoFormat.fourcc()) << VideoFormat::stringFromFourcc(frame.format().fourcc())
<< " " << " "
<< videoFormat.width() << frame.format().width()
<< "x" << "x"
<< videoFormat.height() << frame.format().height()
<< std::endl; << std::endl;
return frame; return frame;

View file

@ -45,7 +45,8 @@ LIBS = \
-ladvapi32 \ -ladvapi32 \
-lkernel32 \ -lkernel32 \
-lgdi32 \ -lgdi32 \
-lshell32 -lshell32 \
-lwindowscodecs
SOURCES = \ SOURCES = \
src/messageserver.cpp \ src/messageserver.cpp \

View file

@ -230,10 +230,10 @@ void AkVCam::Preferences::deleteKey(const std::string &key)
splitSubKey(key, subKey, val); splitSubKey(key, subKey, val);
if (val.empty()) { if (val.empty()) {
deleteTree(HKEY_LOCAL_MACHINE, subKey.c_str(), KEY_WOW64_64KEY); deleteTree(HKEY_CURRENT_USER, subKey.c_str(), KEY_WOW64_64KEY);
} else { } else {
HKEY hkey = nullptr; HKEY hkey = nullptr;
auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, auto result = RegOpenKeyExA(HKEY_CURRENT_USER,
subKey.c_str(), subKey.c_str(),
0, 0,
KEY_ALL_ACCESS | KEY_WOW64_64KEY, KEY_ALL_ACCESS | KEY_WOW64_64KEY,
@ -255,7 +255,7 @@ void AkVCam::Preferences::move(const std::string &keyFrom,
std::string subKeyFrom = REG_PREFIX "\\" + keyFrom; std::string subKeyFrom = REG_PREFIX "\\" + keyFrom;
HKEY hkeyFrom = nullptr; HKEY hkeyFrom = nullptr;
auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, auto result = RegOpenKeyExA(HKEY_CURRENT_USER,
subKeyFrom.c_str(), subKeyFrom.c_str(),
0, 0,
KEY_READ | KEY_WOW64_64KEY, KEY_READ | KEY_WOW64_64KEY,
@ -264,7 +264,7 @@ void AkVCam::Preferences::move(const std::string &keyFrom,
if (result == ERROR_SUCCESS) { if (result == ERROR_SUCCESS) {
std::string subKeyTo = REG_PREFIX "\\" + keyTo; std::string subKeyTo = REG_PREFIX "\\" + keyTo;
HKEY hkeyTo = nullptr; HKEY hkeyTo = nullptr;
result = RegCreateKeyExA(HKEY_LOCAL_MACHINE, result = RegCreateKeyExA(HKEY_CURRENT_USER,
subKeyTo.c_str(), subKeyTo.c_str(),
0, 0,
nullptr, nullptr,
@ -655,7 +655,7 @@ bool AkVCam::Preferences::readValue(const std::string &key,
std::string val; std::string val;
splitSubKey(key, subKey, val); splitSubKey(key, subKey, val);
HKEY hkey = nullptr; HKEY hkey = nullptr;
auto result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, auto result = RegOpenKeyExA(HKEY_CURRENT_USER,
subKey.c_str(), subKey.c_str(),
0, 0,
KEY_READ | KEY_WOW64_64KEY, KEY_READ | KEY_WOW64_64KEY,
@ -685,7 +685,7 @@ void AkVCam::Preferences::setValue(const std::string &key,
std::string val; std::string val;
splitSubKey(key, subKey, val); splitSubKey(key, subKey, val);
HKEY hkey = nullptr; HKEY hkey = nullptr;
LONG result = RegCreateKeyExA(HKEY_LOCAL_MACHINE, LONG result = RegCreateKeyExA(HKEY_CURRENT_USER,
subKey.c_str(), subKey.c_str(),
0, 0,
nullptr, nullptr,

View file

@ -25,6 +25,7 @@
#include <dvdmedia.h> #include <dvdmedia.h>
#include <comdef.h> #include <comdef.h>
#include <shlobj.h> #include <shlobj.h>
#include <wincodec.h>
#include "utils.h" #include "utils.h"
#include "VCamUtils/src/utils.h" #include "VCamUtils/src/utils.h"
@ -966,8 +967,68 @@ AkVCam::VideoFrame AkVCam::loadPicture(const std::string &fileName)
return frame; return frame;
} }
AkLogDebug() << "Error loading picture: " IWICImagingFactory *imagingFactory = nullptr;
<< fileName auto hr = CoCreateInstance(CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&imagingFactory));
if (SUCCEEDED(hr)) {
auto wfileName = stringToWSTR(fileName);
IWICBitmapDecoder *decoder = nullptr;
hr = imagingFactory->CreateDecoderFromFilename(wfileName,
nullptr,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&decoder);
CoTaskMemFree(wfileName);
if (SUCCEEDED(hr)) {
IWICBitmapFrameDecode *bmpFrame = nullptr;
hr = decoder->GetFrame(0, &bmpFrame);
if (SUCCEEDED(hr)) {
IWICFormatConverter *formatConverter = nullptr;
hr = imagingFactory->CreateFormatConverter(&formatConverter);
if (SUCCEEDED(hr)) {
hr = formatConverter->Initialize(bmpFrame,
GUID_WICPixelFormat24bppRGB,
WICBitmapDitherTypeNone,
nullptr,
0.0,
WICBitmapPaletteTypeMedianCut);
if (SUCCEEDED(hr)) {
UINT width = 0;
UINT height = 0;
formatConverter->GetSize(&width, &height);
VideoFormat videoFormat(PixelFormatRGB24, width, height);
frame = VideoFrame(videoFormat);
formatConverter->CopyPixels(nullptr,
3 * width,
frame.data().size(),
frame.data().data());
}
formatConverter->Release();
}
bmpFrame->Release();
}
decoder->Release();
}
imagingFactory->Release();
}
AkLogDebug() << "Picture loaded as: "
<< VideoFormat::stringFromFourcc(frame.format().fourcc())
<< " "
<< frame.format().width()
<< "x"
<< frame.format().height()
<< std::endl; << std::endl;
return frame; return frame;

View file

@ -19,6 +19,7 @@
#include <algorithm> #include <algorithm>
#include <dshow.h> #include <dshow.h>
#include <dbt.h>
#include "basefilter.h" #include "basefilter.h"
#include "enumpins.h" #include "enumpins.h"
@ -75,6 +76,10 @@ namespace AkVCam
static void frameReady(void *userData, static void frameReady(void *userData,
const std::string &deviceId, const std::string &deviceId,
const VideoFrame &frame); const VideoFrame &frame);
static void pictureChanged(void *userData,
const std::string &picture);
static void devicesChanged(void *userData,
const std::vector<std::string> &devices);
static void setBroadcasting(void *userData, static void setBroadcasting(void *userData,
const std::string &deviceId, const std::string &deviceId,
const std::string &broadcasting); const std::string &broadcasting);
@ -357,8 +362,12 @@ AkVCam::BaseFilterPrivate::BaseFilterPrivate(AkVCam::BaseFilter *self,
this->m_ipcBridge.connectServerStateChanged(this, this->m_ipcBridge.connectServerStateChanged(this,
&BaseFilterPrivate::serverStateChanged); &BaseFilterPrivate::serverStateChanged);
this->m_ipcBridge.connectDevicesChanged(this,
&BaseFilterPrivate::devicesChanged);
this->m_ipcBridge.connectFrameReady(this, this->m_ipcBridge.connectFrameReady(this,
&BaseFilterPrivate::frameReady); &BaseFilterPrivate::frameReady);
this->m_ipcBridge.connectPictureChanged(this,
&BaseFilterPrivate::pictureChanged);
this->m_ipcBridge.connectBroadcastingChanged(this, this->m_ipcBridge.connectBroadcastingChanged(this,
&BaseFilterPrivate::setBroadcasting); &BaseFilterPrivate::setBroadcasting);
this->m_ipcBridge.connectControlsChanged(this, this->m_ipcBridge.connectControlsChanged(this,
@ -445,6 +454,40 @@ void AkVCam::BaseFilterPrivate::frameReady(void *userData,
AkVCamDevicePinCall(deviceId, self, frameReady, frame); AkVCamDevicePinCall(deviceId, self, frameReady, frame);
} }
void AkVCam::BaseFilterPrivate::pictureChanged(void *userData,
const std::string &picture)
{
AkLogFunction();
auto self = reinterpret_cast<BaseFilterPrivate *>(userData);
IEnumPins *pins = nullptr;
self->self->EnumPins(&pins);
if (pins) {
AkVCamPinCall(pins, setPicture, picture)
pins->Release();
}
}
void AkVCam::BaseFilterPrivate::devicesChanged(void *userData,
const std::vector<std::string> &devices)
{
UNUSED(userData);
UNUSED(devices);
AkLogFunction();
std::vector<HWND> handlers;
EnumWindows([] (HWND handler, LPARAM userData) -> BOOL {
auto handlers =
reinterpret_cast<std::vector<HWND> *>(userData);
handlers->push_back(handler);
return TRUE;
},
reinterpret_cast<LPARAM>(&handlers));
for (auto &handler: handlers)
SendMessage(handler, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
}
void AkVCam::BaseFilterPrivate::setBroadcasting(void *userData, void AkVCam::BaseFilterPrivate::setBroadcasting(void *userData,
const std::string &deviceId, const std::string &deviceId,
const std::string &broadcaster) const std::string &broadcaster)

View file

@ -290,6 +290,20 @@ void AkVCam::Pin::frameReady(const VideoFrame &frame)
this->d->m_mutex.unlock(); this->d->m_mutex.unlock();
} }
void AkVCam::Pin::setPicture(const std::string &picture)
{
AkLogFunction();
AkLogDebug() << "Picture: " << picture;
this->d->m_testFrame = loadPicture(picture);
this->d->updateTestFrame();
this->d->m_mutex.lock();
if (this->d->m_broadcaster.empty())
this->d->m_currentFrame = this->d->m_testFrameAdapted;
this->d->m_mutex.unlock();
}
void AkVCam::Pin::setBroadcasting(const std::string &broadcaster) void AkVCam::Pin::setBroadcasting(const std::string &broadcaster)
{ {
AkLogFunction(); AkLogFunction();

View file

@ -49,6 +49,7 @@ namespace AkVCam
static HRESULT stateChanged(void *userData, FILTER_STATE state); static HRESULT stateChanged(void *userData, FILTER_STATE state);
void serverStateChanged(IpcBridge::ServerState state); void serverStateChanged(IpcBridge::ServerState state);
void frameReady(const VideoFrame &frame); void frameReady(const VideoFrame &frame);
void setPicture(const std::string &picture);
void setBroadcasting(const std::string &broadcaster); void setBroadcasting(const std::string &broadcaster);
void setControls(const std::map<std::string, int> &controls); void setControls(const std::map<std::string, int> &controls);
bool horizontalFlip() const; bool horizontalFlip() const;

View file

@ -26,6 +26,14 @@
#include "PlatformUtils/src/utils.h" #include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/utils.h" #include "VCamUtils/src/utils.h"
#if 1
#define ROOT_HKEY HKEY_CURRENT_USER
#define SUBKEY_PREFIX "Software\\Classes\\CLSID"
#else
#define ROOT_HKEY HKEY_CLASSES_ROOT
#define SUBKEY_PREFIX "CLSID"
#endif
namespace AkVCam namespace AkVCam
{ {
class PluginInterfacePrivate class PluginInterfacePrivate
@ -70,11 +78,11 @@ bool AkVCam::PluginInterface::registerServer(const std::string &deviceId,
AkLogInfo() << "Description: " << description << std::endl; AkLogInfo() << "Description: " << description << std::endl;
AkLogInfo() << "Filename: " << fileName << std::endl; AkLogInfo() << "Filename: " << fileName << std::endl;
auto subkey = "CLSID\\" + clsid; auto subkey = SUBKEY_PREFIX "\\" + clsid;
HKEY keyCLSID = nullptr; HKEY keyCLSID = nullptr;
HKEY keyServerType = nullptr; HKEY keyServerType = nullptr;
LONG result = RegCreateKeyA(HKEY_CLASSES_ROOT, subkey.c_str(), &keyCLSID); LONG result = RegCreateKeyA(ROOT_HKEY, subkey.c_str(), &keyCLSID);
bool ok = false; bool ok = false;
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
@ -140,8 +148,8 @@ void AkVCam::PluginInterface::unregisterServer(const CLSID &clsid) const
auto clsidStr = stringFromClsid(clsid); auto clsidStr = stringFromClsid(clsid);
AkLogInfo() << "CLSID: " << clsidStr << std::endl; AkLogInfo() << "CLSID: " << clsidStr << std::endl;
auto subkey = "CLSID\\" + clsidStr; auto subkey = SUBKEY_PREFIX "\\" + clsidStr;
deleteTree(HKEY_CLASSES_ROOT, subkey.c_str(), 0); deleteTree(ROOT_HKEY, subkey.c_str(), 0);
} }
bool AkVCam::PluginInterface::registerFilter(const std::string &deviceId, bool AkVCam::PluginInterface::registerFilter(const std::string &deviceId,
@ -254,15 +262,14 @@ bool AkVCam::PluginInterface::setDevicePath(const std::string &deviceId) const
AkLogFunction(); AkLogFunction();
std::string subKey = std::string subKey =
"CLSID\\" SUBKEY_PREFIX "\\"
+ stringFromIid(CLSID_VideoInputDeviceCategory) + stringFromIid(CLSID_VideoInputDeviceCategory)
+ "\\Instance\\" + "\\Instance\\"
+ createClsidStrFromStr(deviceId); + createClsidStrFromStr(deviceId);
AkLogInfo() << "Key: HKEY_CLASSES_ROOT" << std::endl;
AkLogInfo() << "SubKey: " << subKey << std::endl; AkLogInfo() << "SubKey: " << subKey << std::endl;
HKEY hKey = nullptr; HKEY hKey = nullptr;
auto result = RegOpenKeyExA(HKEY_CLASSES_ROOT, auto result = RegOpenKeyExA(ROOT_HKEY,
subKey.c_str(), subKey.c_str(),
0, 0,
KEY_ALL_ACCESS, KEY_ALL_ACCESS,