Search code examples
c#visual-studio-2015windows-10directshowdirectshow.net

Unable to capture a camera feed while previewing using DirectShow


The application is implemented in C# using DirectShowLib and a USB camera (Logitech C930e). The graph is compiled using RenderStream method. SmartTee filter is automatically generated as there is no preview pin provided.

RenderStream is called once for preview and then for capture. However, it fails to connect the second call to the SmartTee even if swapped (capture then preview). The error handler thrown is: -2147024809 (0x80070057; E_INVALIDARG; The parameter is incorrect)

code snippet as follows

    DirectShowLib.ISampleGrabber sg = null;
    DirectShowLib.ICaptureGraphBuilder2 cg = null;
    DirectShowLib.IGraphBuilder fg = null;

    IBaseFilter capFilter;
    IBaseFilter videoCompressorFilter;
    IBaseFilter muxFilter;
    IBaseFilter grabFilter;
    IBaseFilter VideoRendererFilter;

    Guid captureCat = PinCategory.Capture;
    Guid previewCat = PinCategory.Preview;
    Guid med = MediaType.Video;              

    DsGuid DSCaptureCat = (DsGuid)captureCat;
    DsGuid DSPreviewCat = (DsGuid)previewCat;
    DsGuid DSmed = (DsGuid)med;

    /*GetInterfaces*/
    Type comType = null;
    object comObj = null;
    fg = (IGraphBuilder)new FilterGraph();
    comType = Type.GetTypeFromCLSID(CgGuid);
    comObj = Activator.CreateInstance(comType);
    cg = (ICaptureGraphBuilder2)comObj; comObj = null;
    sg = (ISampleGrabber)new SampleGrabber();
    grabFilter = (IBaseFilter)sg;
    VideoRendererFilter = (IBaseFilter)new VideoRenderer();

    /*CreateCaptureDevice*/
    object capObj = null;
    capFilter = (IBaseFilter)capObj;

    /*SetupGraph*/
    hr = cg.SetFiltergraph(fg);

    if (renderFromDevice && deviceSet)
    {
        hr = fg.AddFilter(capFilter, "CapFilter");
    }

    AMMediaType media = new AMMediaType();
    media.majorType = MediaType.Video;
    media.subType = MediaSubType.RGB24;
    media.formatType = FormatType.VideoInfo;
    hr = sg.SetMediaType(media);

    DsUtils.FreeAMMediaType(media);
    media = null;


[1]
    /*RenderToScreen*/ 
    hr = fg.AddFilter(grabFilter, "FrameGrabFilter");

    hr = cg.RenderStream(DSPreviewCat, DSmed, capFilter, grabFilter, null);

[2]
    /*DerenderGraph*/
    if (renderFromDevice)
        removeDownstream(capFilter, videoCompressorFilter == null);
    else if (grabFilter != null)
        removeDownstream(grabFilter, true);

[3] 
    /*RenderToMovie*/
    videoFilename = Path.Combine(dirname, "interview.avi");

    cg.SetOutputFileName(MediaSubType.Avi, videoFilename, out muxFilter, out fileWriterFilter); //this automatically adds muxFilter to graph!

    string s;
    AMMediaType media = new AMMediaType();
    hr = fileWriterFilter.GetCurFile(out s, media);
    hr = fileWriterFilter.SetFileName(videoFilename, media);
    DsUtils.FreeAMMediaType(media);
    media = null;

    hr = fg.AddFilter(muxFilter, "MuxFilter"); //this adds the second muxFilter! now removed

    hr = cg.RenderStream(DSCaptureCat, DSmed, capFilter, null, muxFilter);

[4]     
    /*RenderToScreen2*/
    hr = fg.AddFilter(grabFilter, "FrameGrabFilter");

    hr = fg.AddFilter(VideoRendererFilter, "VideoRendererFilter");

    IEnumFilters enumFilters = null; 
    FilterInfo pInfo;
    IBaseFilter pFilter1, pFilter2, pFilter3;
    IPin outPin1, inPin1, outPin2, inPin2, inPin3;

    hr = fg.EnumFilters(out enumFilters);

    IBaseFilter[] filters = new IBaseFilter[1];
    int fetched;

    while (enumFilters.Next(1, filters, out fetched) == 0)
    {
        hr = filters[0].QueryFilterInfo(out pInfo);

        hr = fg.FindFilterByName("Smart Tee", out pFilter1);

        inPin1 = DsFindPin.ByDirection(pFilter1, PinDirection.Input, 0);
        outPin1 = DsFindPin.ByName(pFilter1, "Preview");

        hr = fg.FindFilterByName("FrameGrabFilter", out pFilter2);

        inPin2 = DsFindPin.ByDirection(pFilter2, PinDirection.Input, 0);
        outPin2 = DsFindPin.ByDirection(pFilter2, PinDirection.Output, 0);

        hr = fg.FindFilterByName("VideoRendererFilter", out pFilter3);

        inPin3 = DsFindPin.ByDirection(pFilter3, PinDirection.Input, 0);

        hr = fg.Connect(outPin1, inPin2);

        hr = fg.Connect(outPin2, inPin3);
    }

GraphEdit below shows the remote connection to the graph. The graph runs fine by manually linking SmartTee capture to MuxFilter within GraphEdit.

Any hints regarding the possible error cause will be very much appreciated.


Solution

  • Without seeing the actual call to ICaptureGRaphBuilder2.RenderStream, I can only make some assumptions about the E_INVALIDARG error. However, from the remote connection to the graph, I can see that there is something terribly wrong with your graph. Two output pins shouldn't be connected to the same input pin. You should always call the RenderStream for the Capture path first, because it's the Capture pin that negotiates the media type. You already have Capture pin on your source filter (even 2 of them why?). I think that you need to call your Capture RenderStream like this:

    RenderStream(PIN_CATEGORY_CAPTURE, MEDIATYPE_Video, pSource, pMux, pWriter);

    Where: pSource is the IBaseFilter interface of the Video Camera Terminal CapFilter, pMux is the IBaseFilter interface to the MuxFilter (there should be only one MuxFilter added to the graph!), pWriter is the IBaseFilter interface to the Writer filter (interview.avi).

    You should add only one instance of the Video Camera Terminal CapFilter, only one instance of the MuxFilter (AVI Mux) and only one instance of the File Writer filter (interview.avi). With this setup the RenderStream call for the Capture path should succeed. After that you can try calling RenderStream for the preview path and the Smart tee should be added if not already added with the first call. Let me know if I can help you further with this.