From 384bc1e20d3617940d5a89cf481ff54cbd61a21b Mon Sep 17 00:00:00 2001 From: wangshaohui Date: Wed, 28 Apr 2021 13:55:52 +0800 Subject: [PATCH 1/2] Add feature to detect device's remove/insert events. --- dshowcapture.hpp | 11 +++- source/device.cpp | 109 ++++++++++++++++++++++++++++++++++++++- source/device.hpp | 19 ++++++- source/dshow-base.cpp | 13 ++++- source/dshow-base.hpp | 2 +- source/dshowcapture.cpp | 7 +-- source/encoder.cpp | 2 +- source/output-filter.cpp | 1 + 8 files changed, 153 insertions(+), 11 deletions(-) diff --git a/dshowcapture.hpp b/dshowcapture.hpp index 6936fe9..503caf2 100644 --- a/dshowcapture.hpp +++ b/dshowcapture.hpp @@ -204,11 +204,20 @@ struct AudioConfig : Config { AudioMode mode = AudioMode::Capture; }; +class IDeviceCallback { +public: + virtual ~IDeviceCallback() {} + + virtual void OnDeviceRemoved() = 0; + virtual void OnDeviceInserted() = 0; +}; + class DSHOWCAPTURE_EXPORT Device { HDevice *context; + IDeviceCallback *callback; public: - Device(InitGraph initialize = InitGraph::False); + Device(InitGraph initialize = InitGraph::False, IDeviceCallback *cb = NULL); ~Device(); bool Valid() const; diff --git a/source/device.cpp b/source/device.cpp index 84c9eb3..07cd7a8 100644 --- a/source/device.cpp +++ b/source/device.cpp @@ -30,13 +30,24 @@ namespace DShow { bool SetRocketEnabled(IBaseFilter *encoder, bool enable); -HDevice::HDevice() : initialized(false), active(false) {} +HDevice::HDevice(IDeviceCallback *cb) + : initialized(false), + active(false), + callback(cb), + msgEvt(0), + msgThread(0) +{ + exitEvt = ::CreateEvent(NULL, TRUE, FALSE, NULL); +} HDevice::~HDevice() { if (active) Stop(); + StopEventThread(); + CloseHandle(exitEvt); + DisconnectFilters(); /* @@ -53,6 +64,90 @@ HDevice::~HDevice() } } +unsigned HDevice::EventThread(void *pParam) +{ + HDevice *self = reinterpret_cast(pParam); + CoInitialize(nullptr); + self->EventThreadInner(); + CoUninitialize(); + return 0; +} + +void HDevice::EventThreadInner() +{ + HANDLE events[] = { + exitEvt, // must be first one + msgEvt, + }; + + DWORD count = sizeof(events) / sizeof(HANDLE); + + while (true) { + DWORD res = + WaitForMultipleObjects(count, events, FALSE, INFINITE); + if (res == WAIT_OBJECT_0) + break; // exitEvt is set for exiting thread + + if (!ReadAllEvents()) + break; + } +} + +#define FLAG_REMOVE_DEVICE 0 +#define FLAG_INSERT_DEVICE 1 + +bool HDevice::ReadAllEvents() +{ + long eventCode = 0; + LONG_PTR param1 = 0; + LONG_PTR param2 = 0; + + while (SUCCEEDED(eventEx->GetEvent(&eventCode, ¶m1, ¶m2, 0))) { + if (EC_DEVICE_LOST == eventCode) { + if (FLAG_REMOVE_DEVICE == param2) { + Warning(L"Device is removed."); + callback->OnDeviceRemoved(); + } else if (FLAG_INSERT_DEVICE == param2) { + Info(L"Device is inserted again."); + callback->OnDeviceInserted(); + } + } + + eventEx->FreeEventParams(eventCode, param1, param2); + + if (WAIT_OBJECT_0 == WaitForSingleObject(exitEvt, 0)) + return false; + } + + return true; +} + +void HDevice::StartEventThread() +{ + if (msgThread && (msgThread != INVALID_HANDLE_VALUE)) { + Warning(L"Message thread is running!"); + return; + } + + if (!callback) { + Info(L"Won't create message thread because no callback."); + return; + } + + ::ResetEvent(exitEvt); + msgThread = (HANDLE)_beginthreadex(0, 0, EventThread, this, 0, 0); +} + +void HDevice::StopEventThread() +{ + ::SetEvent(exitEvt); + if (msgThread && (msgThread != INVALID_HANDLE_VALUE)) { + WaitForSingleObject(msgThread, INFINITE); + CloseHandle(msgThread); + msgThread = 0; + } +} + bool HDevice::EnsureInitialized(const wchar_t *func) { if (!initialized) { @@ -601,9 +696,19 @@ bool HDevice::CreateGraph() return false; } - if (!CreateFilterGraph(&graph, &builder, &control)) + StopEventThread(); + + if (!CreateFilterGraph(&graph, &builder, &control, &eventEx)) return false; + HRESULT hr = eventEx->GetEventHandle((OAEVENT *)&msgEvt); + if (FAILED(hr)) { + ErrorHR(L"Failed to get event handle", hr); + return false; + } + + StartEventThread(); + initialized = true; return true; } diff --git a/source/device.hpp b/source/device.hpp index d02de0a..375c36c 100644 --- a/source/device.hpp +++ b/source/device.hpp @@ -21,7 +21,8 @@ #include "../dshowcapture.hpp" #include "capture-filter.hpp" - +#include +#include #include #include using namespace std; @@ -50,6 +51,7 @@ struct HDevice { ComPtr graph; ComPtr builder; ComPtr control; + ComPtr eventEx; ComPtr videoFilter; ComPtr audioFilter; @@ -70,9 +72,22 @@ struct HDevice { EncodedData encodedVideo; EncodedData encodedAudio; - HDevice(); + // handle insert/remove events + HANDLE msgEvt; + HANDLE exitEvt; + HANDLE msgThread; + IDeviceCallback *callback; + + HDevice(IDeviceCallback *cb); ~HDevice(); + // handle insert/remove events + static unsigned EventThread(void *pParam); + void EventThreadInner(); + bool ReadAllEvents(); + void StartEventThread(); + void StopEventThread(); + void ConvertVideoSettings(); void ConvertAudioSettings(); diff --git a/source/dshow-base.cpp b/source/dshow-base.cpp index c5bdf69..dfc5c2f 100644 --- a/source/dshow-base.cpp +++ b/source/dshow-base.cpp @@ -39,11 +39,12 @@ using namespace std; namespace DShow { bool CreateFilterGraph(IGraphBuilder **pgraph, ICaptureGraphBuilder2 **pbuilder, - IMediaControl **pcontrol) + IMediaControl **pcontrol, IMediaEventEx **pevent) { ComPtr graph; ComPtr builder; ComPtr control; + ComPtr event; HRESULT hr; hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, @@ -73,6 +74,16 @@ bool CreateFilterGraph(IGraphBuilder **pgraph, ICaptureGraphBuilder2 **pbuilder, return false; } + if (pevent) { + hr = graph->QueryInterface(IID_IMediaEventEx, (void **)&event); + if (FAILED(hr)) { + ErrorHR(L"Failed to get media event interface", hr); + return false; + } + + *pevent = event.Detach(); + } + *pgraph = graph.Detach(); *pbuilder = builder.Detach(); *pcontrol = control.Detach(); diff --git a/source/dshow-base.hpp b/source/dshow-base.hpp index 5e90f8d..cb920a7 100644 --- a/source/dshow-base.hpp +++ b/source/dshow-base.hpp @@ -40,7 +40,7 @@ using namespace std; namespace DShow { bool CreateFilterGraph(IGraphBuilder **graph, ICaptureGraphBuilder2 **builder, - IMediaControl **control); + IMediaControl **control, IMediaEventEx **pevent); void LogFilters(IGraphBuilder *graph); diff --git a/source/dshowcapture.cpp b/source/dshowcapture.cpp index 7087c3c..ae4289c 100644 --- a/source/dshowcapture.cpp +++ b/source/dshowcapture.cpp @@ -28,7 +28,8 @@ namespace DShow { -Device::Device(InitGraph initialize) : context(new HDevice) +Device::Device(InitGraph initialize, IDeviceCallback *cb) + : context(new HDevice(cb)), callback(cb) { if (initialize == InitGraph::True) context->CreateGraph(); @@ -48,7 +49,7 @@ bool Device::ResetGraph() { /* cheap and easy way to clear all the filters */ delete context; - context = new HDevice; + context = new HDevice(callback); return context->CreateGraph(); } @@ -56,7 +57,7 @@ bool Device::ResetGraph() void Device::ShutdownGraph() { delete context; - context = new HDevice; + context = new HDevice(callback); } bool Device::SetVideoConfig(VideoConfig *config) diff --git a/source/encoder.cpp b/source/encoder.cpp index 3734118..3bdaf28 100644 --- a/source/encoder.cpp +++ b/source/encoder.cpp @@ -25,7 +25,7 @@ namespace DShow { HVideoEncoder::HVideoEncoder() { - initialized = CreateFilterGraph(&graph, &builder, &control); + initialized = CreateFilterGraph(&graph, &builder, &control, NULL); } HVideoEncoder::~HVideoEncoder() diff --git a/source/output-filter.cpp b/source/output-filter.cpp index f5252cf..7257bb4 100644 --- a/source/output-filter.cpp +++ b/source/output-filter.cpp @@ -22,6 +22,7 @@ #include "log.hpp" #include +#include namespace DShow { From 574ec32230177ca6051c297fad382533b4991440 Mon Sep 17 00:00:00 2001 From: wangshaohui Date: Wed, 28 Apr 2021 13:57:53 +0800 Subject: [PATCH 2/2] Fix compile error. --- source/device.cpp | 2 +- source/device.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/device.cpp b/source/device.cpp index 07cd7a8..495c2cb 100644 --- a/source/device.cpp +++ b/source/device.cpp @@ -64,7 +64,7 @@ HDevice::~HDevice() } } -unsigned HDevice::EventThread(void *pParam) +unsigned __stdcall HDevice::EventThread(void *pParam) { HDevice *self = reinterpret_cast(pParam); CoInitialize(nullptr); diff --git a/source/device.hpp b/source/device.hpp index 375c36c..fd8e5a5 100644 --- a/source/device.hpp +++ b/source/device.hpp @@ -82,7 +82,7 @@ struct HDevice { ~HDevice(); // handle insert/remove events - static unsigned EventThread(void *pParam); + static unsigned __stdcall EventThread(void *pParam); void EventThreadInner(); bool ReadAllEvents(); void StartEventThread();