0

I am trying to follow through the DirectShow examples on the windows dev center to make my own application that can capture screen and audio to video: Capturing Video to an AVI File

When the below code runs it fails on the first RenderStream call with the error: + errMsg 0x09910DB8 "The parameter is incorrect." wchar_t*

Does anybody have any clue how to figure out which parameter is incorrect?

void AudioVideoBuilder::AVBuilder::MakeVideo()
{
    IGraphBuilder *pGraph = NULL;
    ICaptureGraphBuilder2 *pBuild = NULL;

    // Create the Filter Graph Manager.
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
        CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

    if (SUCCEEDED(hr))
    {
        // Create the Capture Graph Builder.
        hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
            CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2,
            (void **)&pBuild);
        if (SUCCEEDED(hr))
        {
            pBuild->SetFiltergraph(pGraph);
        }
    };
    if (SUCCEEDED(hr))
    {
        // Create the Capture Graph Builder.
        hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_ICaptureGraphBuilder2,
            (void**)&pBuild);
        IBaseFilter *pMux;
        if (SUCCEEDED(hr))
        {
            hr = pBuild->SetOutputFileName(
                &MEDIASUBTYPE_Avi,  // Specifies AVI for the target file.
                L"C:\\Temp\\Example.avi", // File name.
                &pMux,              // Receives a pointer to the mux.
                NULL);              // (Optional) Receives a pointer to the file sink.

            if (SUCCEEDED(hr))
            {
                IBaseFilter *audioFilter;
                IBaseFilter *videoFilter;
                //GetAudioAndVideoFilters(audioFilter, videoFilter);

                IEnumMoniker *pEnum;
                CaptureDeviceSelector deviceSelector;
                HRESULT hr = deviceSelector.EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
                if (SUCCEEDED(hr))
                {
                    IMoniker *pMoniker = NULL;
                    while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
                    {
                        IPropertyBag *pPropBag;
                        HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
                        if (FAILED(hr))
                        {
                            pMoniker->Release();
                            continue;
                        }
                        VARIANT var;
                        VariantInit(&var);

                        // Get description or friendly name.
                        hr = pPropBag->Read(L"Description", &var, 0);
                        if (FAILED(hr))
                        {
                            hr = pPropBag->Read(L"FriendlyName", &var, 0);
                        }
                        //if (var == "screen-capture-recorder")
                        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&videoFilter);
                        if (SUCCEEDED(hr))
                        {
                            break;
                        }
                    }
                    hr = deviceSelector.EnumerateDevices(CLSID_AudioInputDeviceCategory, &pEnum);
                    if (SUCCEEDED(hr))
                    {
                        while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
                        {
                            IPropertyBag *pPropBag;
                            HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
                            if (FAILED(hr))
                            {
                                pMoniker->Release();
                                continue;
                            }
                            VARIANT var;
                            VariantInit(&var);

                            // Get description or friendly name.
                            hr = pPropBag->Read(L"Description", &var, 0);
                            if (FAILED(hr))
                            {
                                hr = pPropBag->Read(L"FriendlyName", &var, 0);
                            }
                            hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&audioFilter);
                            if (SUCCEEDED(hr))
                            {
                                break;
                            }
                        }
                    }
                }
                            //ADDED BASED ON MARTIN'S SUGGESTED ANSWER
            hr = pGraph->AddFilter(audioFilter, NULL);
            if (FAILED(hr))
            {
                _com_error err(hr);
                LPCTSTR errMsg = err.ErrorMessage();
                return;
            }
            hr = pGraph->AddFilter(videoFilter, NULL);
            if (FAILED(hr))
            {
                _com_error err(hr);
                LPCTSTR errMsg = err.ErrorMessage();
                return;
            }       
                hr = pBuild->RenderStream(
                    &PIN_CATEGORY_CAPTURE, // Pin category.
                    &MEDIATYPE_Audio,      // Media type.
                    audioFilter,                  // Capture filter.
                    NULL,                  // Intermediate filter (optional).
                    pMux);                 // Mux or file sink filter.

                if (SUCCEEDED(hr))
                {
                    hr = pBuild->RenderStream(
                        &PIN_CATEGORY_CAPTURE, // Pin category.
                        &MEDIATYPE_Video,      // Media type.
                        videoFilter,                  // Capture filter.
                        NULL,                  // Intermediate filter (optional).
                        pMux);                 // Mux or file sink filter.

                    // Release the mux filter.
                    pMux->Release();

                    IConfigAviMux *pConfigMux = NULL;
                    hr = pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux);
                    if (SUCCEEDED(hr))
                    {
                        pConfigMux->SetMasterStream(0);
                        pConfigMux->Release();
                    }

                    IConfigInterleaving *pInterleave = NULL;
                    hr = pMux->QueryInterface(IID_IConfigInterleaving, (void**)&pInterleave);
                    if (SUCCEEDED(hr))
                    {
                        pInterleave->put_Mode(INTERLEAVE_CAPTURE);
                        pInterleave->Release();
                    }
                }
                else
                {
                    _com_error err(hr); 
                    LPCTSTR errMsg = err.ErrorMessage();
                }
            }
            else
            {
                DWORD error = HRESULT_CODE(hr);
            }
        }
    }
    else
    {
        DWORD error = HRESULT_CODE(hr);
    }
}
Roman R.
  • 68,205
  • 6
  • 94
  • 158
JustinC
  • 29
  • 5

2 Answers2

1

Apart from other issues in your code (see comments), the primary problem is copy/paste problem: you create CLSID_CaptureGraphBuilder2 twice.

So you create the object first and associate it with your filter graph, and then you create the other one. You add source filters to the first graph, and you request multiplexer filter chain to be created in the other. Those definitely cannot connect, belong to different graphs and hence the error.

Comment the second section, and you can move on:

if (SUCCEEDED(hr))
{
    //// Create the Capture Graph Builder.
    //hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,
    //    NULL,
    //    CLSCTX_INPROC_SERVER,
    //    IID_ICaptureGraphBuilder2,
    //    (void**)&pBuild);
    IBaseFilter *pMux;

Since you are going to deal with DirectShow filter graphs for some time, I suggest that you learn how to explore your graphs on runtime using GraphEdit (or, I would rather recommend GraphStudioNext instead).

You can add a MessageBox call at any point in your code and keeping the message window popped up you would look at graphs in your application and see the problem immediately.

Community
  • 1
  • 1
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • That I did not see that :-( – Martin Schlott Apr 16 '15 at 11:06
  • Thanks Roman and Martin for the help. My code now runs through without error. I don't end up with my AVI file but will take your suggestion and work to understand the graphs. – JustinC Apr 16 '15 at 11:40
  • Hi All, unfortunately I'm still failing to get anywhere with this. My code runs without any errors but doesn't seem to do anything. I've added the AddToROT methods and tried to find my graph in GraphEdit - shows no remote graphs - and GraphEditNext - can't find anything that may be my graph... If anyone can give anymore suggestions or tips they would be greatly appreciated.. Once I start seeing something happen I'm sure I will be able to get some momentum up.. – JustinC Apr 20 '15 at 08:33
  • Actually I've managed to put code in to save the graph out to a file and load it in GraphEditNext. When I run it it creates my AVI file! Great news... So the question I have now is - what in the code actually runs the graph programmatically?? – JustinC Apr 20 '15 at 09:08
  • Application with `AddToROT` and GraphEdit should be started both either with or without elevated permissions. Otherwise the apps don't see each other. Then, you need Windows SDK installed or at least have `proppage.dll` from it registered. It's hard to tell more because it's takes debugging to see the rest. – Roman R. Apr 20 '15 at 09:36
0

I am not used to RenderStream as I preferred building the Graph by hand. But for all Render methods you have to add the filter before rendering the graph. I think that includes RenderStream even it is not explicit pointed out in:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd390016(v=vs.85).aspx

Use

 pGraph->AddFilter(audioFilter,NULL);

before RenderStream . Of course do that also for video.

I strongly recommend that you at least change the error checking from

if(SUCCEED(hr))
{
// do stuff
    if(SUCCEED(hr))
    {
    // do stuff
        if(SUCCEED(hr))
        {
        // do stuff

        }
   }
}

to

// do stuff
if(FAILED(hr))
 return;
// do stuff
if(FAILED(hr))
 return;
// do stuff
if(FAILED(hr))
 return;

it is more readable.

Martin Schlott
  • 4,369
  • 3
  • 25
  • 49
  • Thanks Martin, I followed you suggestion but it is still failing in the same way. I'll update the question above to reflect the latest code. – JustinC Apr 16 '15 at 10:33
  • @JustinC: The code is actually `0x80070057` `ERROR_INVALID_PARAMETER` "The parameter is incorrect.". Look the codes up using http://alax.info/blog/1383 – Roman R. Apr 16 '15 at 10:46
  • Aha, so the code I am getting is 0x09078100 "The parameter is incorrect."... I tried the link and started the app but I couldn't get it to work. It's a shame, it sounded very useful. – JustinC Apr 16 '15 at 11:05