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
Related
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.
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!
Does anyone know which relation may exist between registration-free COM and drag/drop functionality?
Specifically, we have a huge C++ CAD/CAM application comprising a number of EXEs and several hundreds DLLs. Many of them serve as COM servers (both in-proc and out-of-proc) and/or clients, and also implement ActiveX controls.
The most of ActiveX controls and the main CMDIFrameWnd-based window of one of EXEs implement drag/drop functionality. ActiveX controls implement the both drop source and drop target, and the main window is only drop target, in particular, for files from Windows Explorer.
The drag/drop implementation is pretty standard and based on two data members derived from COleDataSource and COleDropTarget for drop source and drop target respectively. The COleDropTarget-derived member is registered with respective window in the window's OnCreate method. It also overrides OnDragEnter, OnDragOver and OnDrop methods in a similar way. Namely, the system-supplied COleDataObject parameter is asked for specific format (in particular, CF_HDROP), and in the case of positive answer, the data (e.g., file path) is extracted from the clipboard. The code looks like the following:
static FORMATETC g_FileFmt = {CF_HDROP, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
....
// Inside OnDragEnter, OnDragOver or OnDrop method
STGMEDIUM stgmedium = {0,0,0};
if (pDataObject->IsDataAvailable(g_FileFmt.cfFormat))
{
HRESULT hr = pDataObject->GetData(g_FileFmt.cfFormat, &stgmedium);
HDROP hdrop = (HDROP)GlobalLock(stgmedium.hGlobal);
if (hdrop != 0)
{
int FilesCount = DragQueryFile(hdrop, (UINT)-1, 0, 0);
if (FilesCount != 0)
{
TCHAR FileName[_MAX_PATH];
DragQueryFile(hdrop, 0, FileName, _MAX_PATH);
// Check file extension and store the file name for farther use.
}
GlobalUnlock(hdrop);
}
}
The drop source implementation is also straightforward and looks like the following:
void CDmDocListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1 && m_pOleDataSource && prv_BeginDrag())
{
DROPEFFECT DE = m_pOleDataSource->DoDragDrop(
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, 0);
}
*pResult = 0;
}
where prv_BeginDrag() function collects dragged data, packs it and puts on the clipboard by calling SetData method from the m_pOleDataSource object's IDataObject interface.
The all this stuff worked perfectly until it was decided to make the whole application registration-free. It took me three months to force the application run isolated (without registration of COM components) by embedding manifests, launching out-of-proc COM servers on demand and altering CLSID of some classes in order to separate instances of the same server launched from different folders. At last it begins to work - but without drag/drop functionality, despite it wasn't even touched by my changes.
On the drop target side, when I drag file from Windows Explorer, depicted above call to COleDataObject::IsDataAvailable returns false, although before my changes returned true. At the same time, if I add a single line of code "DragAcceptFiles();" to the main window's OnCreate method, drag/drop begins working via the standard CFrameWnd's WM_DROPFILE message handler.
On the drop source side, the dragged data are successfully packed and placed on the clipboard, but COleDataSource::DoDragDrop method fails, because a call to ::DoDragDrop API inside MFC implementation returns REGDB_E_CLASSNOTREG "Class not registered" result.
It means, that COM activation changes somehow influence drag/drop behavior. How?
P.S. 1) The EXE, to which I drag files from Windows Explorer, has in its project properties "UAC Execution Level = asInvoker". As far as I understand, it tells that the EXE will run at the same UAC level as Windows Explorer when launched by double-click on the file.
2) Quite surprisingly, although drag/drop stopped working with symptoms described above, Copy/Paste continues work well, despite the both technologies have similar implementation.
3) I believe, that if find out when ::DoDragDrop API returns "Class not registered" error, and which class it is looking for, it would be possible to solve the problem.
Thanks for help,
Ilia.
Following to MartinBa advice, I solved the problem with the help of Process Monitor. The Process Monitor showed me that while I drag an item in the ActiveX control (mentioned in the question), the system unsuccessfully tries get access to a class ID in the Registry. Looking for that ID, I found that it is really not class ID, but IDataObject interface ID. It was referenced in one of my manifest files.
The most of manifests I have written by hand, but a few, especially at the beginning of the project having no experience in the area, I generated automatically by Visual Studio from existing type library. In one of them Studio included the comInterfaceExternalProxyStub statement for a couple of system interfaces, in which proxyStubClsid32 element was (erroneously) equal to the interface ID.
I'm still not sure whether those system interfaces should present in the manifest; for example, the IDataObject is only mentioned as a method's parameter in one of IDL definitions. Anyway, I corrected only the proxyStubClsid32 value, and the problem disappeared...
The moral of this very painful for me story is to always check output of automatic tools...
I need to iterate the IMFMediaSource pins and get the specific category name of each one of them, I am working in win 8, using the cpp interface of media foundation.
I saw that theres a IBaseFilter::EnumPins function for IBaseFilter, but IMFMediaSource dont support IBaseFilter interface :
CComPtr<IBaseFilter> pFilter;
HRESULT hr = m_pMediaSource->QueryInterface(__uuidof(IBaseFilter), (void **)&pFilter);
fails with NO_INTERFACE errorcode :(
how can I get the pins categories from a Media Source??
This does not work because it does not have to work. IMFMediaSource belongs to Media Foundation and IBaseFilter belongs to DirectShow. The different APIs don't have to share or otherwise duplicate implementation. Media Foundation objects don't even have pins to start with.
Since there can be no answer to the question asked, the only solution here is to step back and review what exactly you are trying to achieve in first place.
I need to write a C++ GUI such that user can make a flowchart / pipeline by selecting several blocks from a toolbar and putting them into a window and connecting them in some order which he wants and then program runs the flowchart. (For simplicity just consider each block's task is to print some number. My problem is GUI)
Does anyone ever try a similar thing / any experience?
Is it possible to make such a GUI in WxWidget or any other Graphics/Window-form library?
Is it possible to use VTK to make the GUI?
Do you know of any similar open source work?
I have developed several apps with GUIs that do this sort of thing.
The one I am most pleased with is called VASE: A GUI used to create the layout, set parameters and view results of a process flow simulator.
It is not a trivial task, though once you have done one or two, there are many ideas that you can reuse and it goes quickly.
The two biggest challenges drawing the lines connecting the objects ( as you can see, even in VASE, this problem is not completely solved ) and storing that layout in a format that can be easily recovered and redrawn.
Is there any particular issue you need help with?
If you want a really, really, simple example to get you started I have re-implemented a couple of basic features ( all nice and clean, no copyright restrictions ) - left click to select, drag to move, right click to connect.
Here is the source code repository - http://66.199.140.183/cgi-bin/vase.cgi/home
Here's what it looks like
I have implemented a simplified connector, which I call a pipe. To give you a flavour of how to do this kind of stuff, here is the code to add a pipe when the user right clicks
/**
User has right clicked
If he right clicks on a flower
and there is a different flower selected
then connect the selected flower to the right clicked flower
if he right clicks on empty background
create a new flower
*/
void cVase::MouseRightDown( wxMouseEvent& event )
{
// find flower under click
iterator iter_flower_clicked = find( event.GetPosition() );
// check there was a flower under click
if( iter_flower_clicked != end() ) {
// check that we have a selected flower
if( ! mySelected )
return;
// check that selected flower is different from one clicked
if( mySelected == (*iter_flower_clicked) )
return;
// construct pipe from selected flower to clicked flower
myPipe.push_back(cPipe( mySelected, *iter_flower_clicked ));
} else {
// no flower under click
// make one appear!
cFlower * pflower = Add();
pflower->setLocation( event.GetPosition() );
}
// redraw everything
Refresh();
}
And here is the code to draw a pipe
/**
Draw the pipe
From starting flower's exit port to ending flower's entry port
*/
void cPipe::Paint( wxPaintDC& dc )
{
dc.SetPen( *wxBLUE_PEN );
dc.DrawLine( myStart->getExitPort(), myEnd->getEntryPort() );
}
You can see the rest of the wxWidgets code that ties all this together by browsing the source code repository.
I think using a library such as wxArt2D would be easier than using the standard wxWidgets drawing classes. The wires sample of wxArt2D looks similar to what you are looking for.
Maybe you can have a try with the tiny tool called Flowchart to Code, you can get the flowchart needed,like this. It can be downloaded here:http://www.athtek.com/flowchart-to-code.html#.Ug4z29JPTfI