Peter
Peter

Reputation: 61

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

Upvotes: 0

Views: 430

Answers (1)

Mike Dinescu
Mike Dinescu

Reputation: 55760

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

Upvotes: 1

Related Questions