Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion dshowcapture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
109 changes: 107 additions & 2 deletions source/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

/*
Expand All @@ -53,6 +64,90 @@ HDevice::~HDevice()
}
}

unsigned __stdcall HDevice::EventThread(void *pParam)
{
HDevice *self = reinterpret_cast<HDevice *>(pParam);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to use reinterpret_cast here? Would static_cast suffice?

CoInitialize(nullptr);
self->EventThreadInner();
CoUninitialize();
return 0;
}

void HDevice::EventThreadInner()
{
HANDLE events[] = {
exitEvt, // must be first one
msgEvt,
};

DWORD count = sizeof(events) / sizeof(HANDLE);
Comment on lines +78 to +83

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The array is static and the count never changes, so an std::array could be used, which has a size() method to get the count for WaitForMultipleObjects and the data() method to get a raw pointer.


while (true) {
DWORD res =
WaitForMultipleObjects(count, events, FALSE, INFINITE);
if (res == WAIT_OBJECT_0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per Microsoft's documentation states this should use the logic res - WAIT_OBJECT_0 == <expected array index>.

break; // exitEvt is set for exiting thread

if (!ReadAllEvents())
break;
Comment on lines +91 to +92

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!ReadAllEvents())
break;
if (!ReadAllEvents()) {
break;
}

Needs to use curly braces for all blocks per current code style guidelines. Applies throughout.

}
}

#define FLAG_REMOVE_DEVICE 0
#define FLAG_INSERT_DEVICE 1
Comment on lines +96 to +97

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with constexpr int.


bool HDevice::ReadAllEvents()
{
long eventCode = 0;
LONG_PTR param1 = 0;
LONG_PTR param2 = 0;

while (SUCCEEDED(eventEx->GetEvent(&eventCode, &param1, &param2, 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;
Comment on lines +118 to +119

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is to check whether the exit event was signalled after the current media event and thus make the media event thread exit?

}

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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it necessary to use the global scope identifier explicitly here and for other functions like ResetEvent above?

if (msgThread && (msgThread != INVALID_HANDLE_VALUE)) {
WaitForSingleObject(msgThread, INFINITE);
CloseHandle(msgThread);
msgThread = 0;
}
}

bool HDevice::EnsureInitialized(const wchar_t *func)
{
if (!initialized) {
Expand Down Expand Up @@ -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;
}
Expand Down
19 changes: 17 additions & 2 deletions source/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

#include "../dshowcapture.hpp"
#include "capture-filter.hpp"

#include <Windows.h>
#include <process.h>
#include <string>
#include <vector>
using namespace std;
Expand Down Expand Up @@ -50,6 +51,7 @@ struct HDevice {
ComPtr<IGraphBuilder> graph;
ComPtr<ICaptureGraphBuilder2> builder;
ComPtr<IMediaControl> control;
ComPtr<IMediaEventEx> eventEx;

ComPtr<IBaseFilter> videoFilter;
ComPtr<IBaseFilter> audioFilter;
Expand All @@ -70,9 +72,22 @@ struct HDevice {
EncodedData encodedVideo;
EncodedData encodedAudio;

HDevice();
// handle insert/remove events
HANDLE msgEvt;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HANDLE msgEvt;
HANDLE mediaEvent;

HANDLE exitEvt;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HANDLE exitEvt;
HANDLE exitEvent;

HANDLE msgThread;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HANDLE msgThread;
HANDLE mediaEventThread;

IDeviceCallback *callback;

HDevice(IDeviceCallback *cb);
~HDevice();

// handle insert/remove events
static unsigned __stdcall EventThread(void *pParam);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use a static class function rather than a free-standing function in an anonymous namespace for this? Also why use _beginthreadex over the CreateThread API?

void EventThreadInner();
bool ReadAllEvents();
void StartEventThread();
void StopEventThread();

void ConvertVideoSettings();
void ConvertAudioSettings();

Expand Down
13 changes: 12 additions & 1 deletion source/dshow-base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ using namespace std;
namespace DShow {

bool CreateFilterGraph(IGraphBuilder **pgraph, ICaptureGraphBuilder2 **pbuilder,
IMediaControl **pcontrol)
IMediaControl **pcontrol, IMediaEventEx **pevent)
{
ComPtr<IGraphBuilder> graph;
ComPtr<ICaptureGraphBuilder2> builder;
ComPtr<IMediaControl> control;
ComPtr<IMediaEventEx> event;
HRESULT hr;

hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
Expand Down Expand Up @@ -73,6 +74,16 @@ bool CreateFilterGraph(IGraphBuilder **pgraph, ICaptureGraphBuilder2 **pbuilder,
return false;
}

if (pevent) {
hr = graph->QueryInterface(IID_IMediaEventEx, (void **)&event);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hr = graph->QueryInterface(IID_IMediaEventEx, (void **)&event);
hr = graph->QueryInterface(IID_IMediaEventEx, static_cast<void **>(&event));

Use static_cast instead of C-style casts in C++ code.

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();
Expand Down
2 changes: 1 addition & 1 deletion source/dshow-base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
7 changes: 4 additions & 3 deletions source/dshowcapture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -48,15 +49,15 @@ bool Device::ResetGraph()
{
/* cheap and easy way to clear all the filters */
delete context;
context = new HDevice;
context = new HDevice(callback);

return context->CreateGraph();
}

void Device::ShutdownGraph()
{
delete context;
context = new HDevice;
context = new HDevice(callback);
}

bool Device::SetVideoConfig(VideoConfig *config)
Expand Down
2 changes: 1 addition & 1 deletion source/encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace DShow {

HVideoEncoder::HVideoEncoder()
{
initialized = CreateFilterGraph(&graph, &builder, &control);
initialized = CreateFilterGraph(&graph, &builder, &control, NULL);
}

HVideoEncoder::~HVideoEncoder()
Expand Down
1 change: 1 addition & 0 deletions source/output-filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "log.hpp"

#include <strsafe.h>
#include <stdint.h>

namespace DShow {

Expand Down