How to get a "Camera removed" event in MFCaptureEngine - c++

Is there any possibility to get an information that a camera was removed during preview in IMFCaptureEngine?
I am using a code from this sample CaptureEngine video capture sample. There is an EventCallback connected to the MFCaptureEngine instance:
hr = m_pEngine->Initialize(m_pCallback, pAttributes, NULL, pUnk);
But no event is received in the callback function after the webcam is removed.
I tried to add an extra callback function for engine's IMFMediaSource, which should, as I was expecting, generate MEVideoCaptureDeviceRemoved event. Look at the code called after MFCaptureEngine instance is initialized:
m_pEngine->GetSource(&pCapSource);
pCapSource->GetCaptureDeviceSource(MF_CAPTURE_ENGINE_DEVICE_TYPE_VIDEO, &pMediaSource);
pMediaSource.QueryInterface(&m_pSourceEventGenerator);
hr = m_pSourceEventGenerator->BeginGetEvent(OnSourceCB, NULL);
The hr value is MF_E_MULTIPLE_SUBSCRIBERS, which gives me a sence, because there are mixed two callbacks objects (first for the MFCaptureEngine as a whole, second for IMediaSource only).
Why I don't get any information about the device was removed? How can I get this information?
PS. I know the WM_DEVICECHANGE message, but I would like to avoid this if possible to get an event from media foundation.

You expectedly hit MF_E_MULTIPLE_SUBSCRIBERS because it's capture engine who is the subscriber here. The engine is supposed to handle the event internally and forward it to the owner in the form of IMFMediaEvent of extended type MF_CAPTURE_ENGINE_ERROR, with HRESULT code indicated by IMFMediaEvent::GetStatus call of MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED (0xC00D3EA2): "The video recording device is no longer present.".
You receive the event in your IMFCaptureEngineOnEventCallback implementation supplied to capture engine on initialization step.

Related

Receiving notification when device registry property changes

I need to receive current power state of a device. I was able to retrieve it using the code below:
ULONG ulRegDataType;
CM_POWER_DATA powerData;
ULONG ulLength;
auto ret = CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_DEVICE_POWER_DATA, &ulRegDataType, &powerData, &ulLength, 0);
I wasn't able, however, to install notification callback with CM_Register_Notification that would notify me about the property change. I tried both CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES and CM_NOTIFY_FILTER_FLAG_ALL_DEVICE_INSTANCES but the callback is never called when device enters D3 state.

DirectShow Amcap works finde, black screen with playcap

I want to stream a preview video from my image sensor to my pc.
Later I want to add custom filters.
First I used Amcap to get a preview video. It works fine.
However I want my project to be baased on playcap (not as complicated as amcap).
When I start playcap it detects a device, however shows just a black screen.
I haven't modified the code in both examples.
Does anyone know how to fix this problem?
Or perhaps does anyone can describe how I can add a custom filter to amcap.
What does the Samplecgb part do in amcap?
King Regards,
afo
I'm going to try to distill the steps to creating a capture graph bellow. This is a complex process and usually there's multiple ways to accomplish most of the steps so you'll have to do your own research from here on and ask specific questions.
In the code snippets bellow I'll use _com_ptr_t smart pointers, which are defined using the _COM_SMARTPTR_TYPEDEF(IGraphBuilder, __uuidof(IGraphBuilder)) macro. So, do define the IGraphBuilderPtr you'd do something like this:
_COM_SMARTPTR_TYPEDEF(IGraphBuilder, __uuidof(IGraphBuilder))
// which defines IGraphBuilderPtr
You'll always need to have a graph so the first step is pretty universal:
IGraphBuilderPtr graph;
graph.CreateInstance(CLSID_FilterGraph);
Then, if you're building a capture graph, you'll most likely want to use the following interface:
ICaptureGraphBuilder2Ptr cg;
cg.CreateInstance(CLSID_CaptureGraphBuilder2);
cg->SetFiltergraph(graph);
After that, you'll need to add one or more input sources to the graph. You'll need to find the filter that wraps your image sensor as a video capture device and add that to the graph.
This is going to be a multi-step process which will likely involve doing something like this:
Enumerate all video capture devices:
IBaseFilterPtr fVideoInput; // will hold the video input source
ICreateDevEnumPtr pCreate(CLSID_SystemDeviceEnum);
IEnumMonikerPtr pEnum;
HRESULT hr = pCreate->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEnum,
0);
if (hr == S_OK)
{
IMonikerPtr pMon;
while (pEnum->Next(1, &pMon, NULL) == S_OK)
{
// inspect the moniker of each devices to determine if it's the one you're after
// if it's the right device, then..
HRESULT hr = mon->BindToObject(NULL,
NULL,
__uuidof(IBaseFilter),
(void**)&fVideoInput);
}
}
Once you have the interface to the video input filter, you'll have to enumerate it's pins and find the correct output pin to connect the rest of the graph to. This can be as simple as enumerating all pins and picking the first output pin (if there is only one), or enumerating all pins and querying the media types of each output pin and selecting the right one that way, or, if you know the name of the pin (and it's always the same) calling FindPin. Below is an example of enumerating the pins and selecting the first output one:
IEnumPinsPtr pEnum;
fVideoInput->EnumPins(&pEnum);
IPinPtr pin;
while (pEnum->Next(1, &pin, NULL) == S_OK)
{
PIN_DIRECTION dir;
pin->QueryDirection(&dir);
if (dir == PINDIR_OUTPUT)
{
// this is the first output pin
break;
}
}
Once you have the output pin, you may insert another filter, find the appropriate pin (similar to above but looking for input pins) and then connect the two pins directly using:
// assuming pinOut is an output pin
// and pinIn is an input pin, this method will try to connect them
HRESULT hr = graph->Connect(pinOut, pinIn);
Or, you may try to simply render the pin:
hr = graph->Render(pinOut);
And here's an example of inserting a custom filter in the graph. If the filter is already registered in the system (it shows up in the list in GraphEdit) then all you need to know is the class id of the filter. This is a GUID that uniquely identifies the filter, and if you don't already know it, you can find it using GraphEdit (create new graph, insert the custom filter, right-click and see properties, there should be the class id of the filter):
IBaseFilterPtr fCustomFilter;
fCustomFilter.CreateInstance(__uuidof(CLSID_OF_YOUR_CUSTOM_FILTER));
graph->AddFilter(fCustomFilter, L"Custom Filter Name");
Then, you can proceed in a similar maner as above and find a suitable input pin for the filter, and a suitable output pin, and connect them as you see fit.
Finally, this is entirely optional, and only useful for debugging (so don't use in production), you can register the graph with the ROT to make it possible to study the final graph in a tool such as GraphEdit (or GraphStudioNext or the like).
Sample code take from MSDN for AddToROT and RemoveFromRot:
DWORD dwRegistration = 0;
HRESULT hr = AddToRot(graph, &dwRegistration);
// hold on to dwRegistration, and use it later when you tear down the graph
// to unregister it using the RemoveFromRot(&dwRegister) method

MSHTML: Callback on image insertion

Currently I am working on embedding a HTML editor into a C++ WinAPI application using the MSHTML component.
I got everything set up (activating editing mode, changing font face, etc.), but now I have to support inserting images. MSHTML already has support for it built in, but this support is - to my knowledge - not enough.
Somehow I need a way to intercept the insertion of images into the HTML-editor, since I have to create a list of images in the UI of our application. So, whenever the user uses the default-dialog of the MSHTML-component to insert an image or updates its source (e.g. from file://xyz.jpg to file://abc.jpg), I want my code to be notified.
I already looked at the conecpt of "Edit Designers", the implementation of IHTMLEditHost, or the DWebBrowserEvents2 interface. But nothing seems to do the trick.
Perhaps someone can give me a hint?
Okay,
it looks like you cannot explicitly subscribe for specific changes of the document. What you can do is to create a so-called "Change Sink".
Everytime you change the document, either by user input or programmatically, you can get a notification that "something" changed in your document. This can be done by implementing the IHTMLChangeSink interface and attaching it to the IMarkupContainer2 of the IHTMLDocument2.
Example code (not complete):
class CHTMLChangeSink : public IHTMLChangeSink
{
public:
// Implement IUnknown: QueryInterface, AddRef, Release
// Implement IHTMLChangeSink:
HRESULT STDMETHODCALLTYPE Notify()
{
// Notify() is called everytime the document changes
return S_OK;
}
};
CHTMLChangeSink * pChangeSink = new CHTMLChangeSink();
IHTMLDocument2 * pDoc; // lets suppose we already have it
IMarkupContainer2 * pMarkupContainer = nullptr;
if (pDoc->QueryInterface(IID_IMarkupContainer2, reinterpret_cast<void **>(&pMarkupContainer)) == S_OK) {
DWORD dwCookie = 1;
// registration is done here
pMarkupContainer->RegisterForDirtyRange(pChangeSink, &dwCookie);
}
Please be aware, that the document has to be loaded completely (register for DIID_DWebBrowserEvents2::DocumentComplete).
From now on, whenever a change in the document occurs, your Notify-method will be called and you can do further processing to find out what has changed (e.g. process the list of images in the document).
Have fun!

Passing Data to Windows Console Control Handler

I am working on writing a simple game engine and I am having trouble handling Windows console events; specifically, I cannot figure out how to pass custom data to the callback handler.
I first call this code to specify my callback function:
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WindowsSystemManager::ConsoleControlHandler, true);
My static-member callback function is defined as:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
if(controlType == CTRL_CLOSE_EVENT){
MessageBox(NULL, L"Close Event Captured", L"Close Event Captured", NULL);
}
return true;
}
Everything works fine - when I click on the close button in the console, this MessageBox pops up. Only problem is, I need to call code that flushes a logging buffer to a log file on this type of shutdown (as well as other clean-up), and the Logger instance is a member in my WindowsSystemManager.
I have dealt with a similar problem of passing custom data to window handles by using SetWindowLongPtr and GetWindowLongPtr successfully, but I can't find any information on how to do this type of thing with console control handlers. Any thoughts?
EDIT: I got this functionality working based on MSalters' suggestions. The final code for the console control handler is here:
bool WINAPI WindowsSystemManager::ConsoleControlHandler(DWORD controlType){
BerserkEngine* engine = (BerserkEngine*)GetWindowLongPtr(GetConsoleWindow(), GWLP_USERDATA);
if(controlType == CTRL_CLOSE_EVENT){
engine->~BerserkEngine();
PostQuitMessage(0);
}
return true;
}
Where I set this custom data pointer in the WindowsSystemManager constructor:
SetWindowLongPtr(GetConsoleWindow(), GWL_USERDATA, (LONG_PTR)this->engine);
I'm not sure why you'd need this. You can have multiple windows, but only one console.
However, GetConsoleWindow will give you the console HWND, on which you might call SetWindowLongPtr. Not very clean (you're not supposed to do this on windows that you don't manage), but it might just work.

Callback Routine Not Getting Triggered

I've created a very simple one-button MFC dialog app that attempts to utilize a callback function. The app complies and runs just fine, but the callback routine never gets triggered.
What needs to be modified in order to get the callback to trigger properly?
You can download the test.zip file here (the test app is in VS 2003 to ensure more people can try it out): http://tinyurl.com/testfile-zip
The code utilizes an alarm class on CodeProject, and the callback function is suppsed to get triggered every 3 seconds (as determined by the code being passed in).
Thanks!
I've looked at your code and the I believe the Function called from the button is the problem
void CTestDlg::OnBnClickedButton1()
{
CAlarmClock clock;
REPEAT_PARMS rp;
ZeroMemory(&rp, sizeof(REPEAT_PARMS));
rp.bRepeatForever = TRUE;
rp.Type = Repeat_Interval;
rp.ss = 3;
clock.SetRepeatAlarm(0, 0, 0, rp, CallbackRtn);
}
This creates the Alarm clock on the function stack.
This CAlarmclock object is therefore destroyed at the end of the function along with its contents.
For it to be able to exist for long enough to actually do the callback
you need to add it as a member variable of your dialog class for it to exist and callback for as long as the dialog exists.
See the example code on the CAlarmclock codeproject page for how to use this class correctly.