4

I am trying to use the GMFBuilder so that i can preview a stream from a webcam and save it periodically without restarting the whole graph. However Im not sure if this is correct or not, I was trying to follow examples but the codes been updated and things have changed.

I try and create:

WEbcam -> Smart Tee (preview) -> AVI Decompressor -> Video Renderer
          Smart Tee (Capture) -> BridgeSinkFilter

and also:

BridgeSourceFilter -> ffdshow video encoder -> haali mastroska muxer 

(just cause its easy to use)

Input regarding getting the code to run properly would be greatly appreciated.

    private void button2_Click(object sender, EventArgs e)
    {
        IGraphBuilder firstGraph = (IGraphBuilder)new FilterGraph();
        IGraphBuilder secondGraph = (IGraphBuilder)new FilterGraph();

        IBaseFilter BridgeSinkFilter;
        IBaseFilter BridgeSourceFilter;

        IBaseFilter Source;
        IBaseFilter Mux;
        IBaseFilter FileWriter;

        IGMFBridgeController bridge = (IGMFBridgeController)new GMFBridgeController();

        bridge.AddStream(1, eFormatType.eMuxInputs, 1);

        BridgeSinkFilter = (IBaseFilter)bridge.InsertSinkFilter(firstGraph);

        Source = FindFilter(FilterCategory.VideoInputDevice, "SG330");
        firstGraph.AddFilter(Source, "source");

        IBaseFilter SmartTee = FindFilter(FilterCategory.LegacyAmFilterCategory, "Smart Tee");
        firstGraph.AddFilter(SmartTee, "Smart Tee");

        IPin pinin, pinout;

        pinout = FindPinByDirection( Source, PinDirection.Output);
        pinin = FindPinByDirection( SmartTee, PinDirection.Input);

        firstGraph.Connect(pinout, pinin);
        pinout = FindPinByDirection(SmartTee, PinDirection.Output);
        pinin = FindPinByDirection(BridgeSinkFilter, PinDirection.Input);

        firstGraph.Connect(pinout, pinin);

        IBaseFilter Decomp = FindFilter(FilterCategory.LegacyAmFilterCategory, "AVI Decompressor");
        firstGraph.AddFilter(Decomp, "Avi Decompressor");

        pinout = FindPinByDirection(SmartTee, PinDirection.Output);
        pinin = FindPinByDirection(Decomp, PinDirection.Input);

        firstGraph.Connect(pinout, pinin);

        IBaseFilter Renderer = FindFilter(FilterCategory.LegacyAmFilterCategory, "Video Renderer");
        firstGraph.AddFilter(Renderer, "Video Renderer");

        pinout = FindPinByDirection(Decomp, PinDirection.Output);
        pinin = FindPinByDirection(Renderer, PinDirection.Input);

        firstGraph.Connect(pinout, pinin);

        DsROTEntry g = new DsROTEntry(firstGraph);

        BridgeSourceFilter =  (IBaseFilter)bridge.InsertSourceFilter(BridgeSinkFilter, secondGraph);
        DsROTEntry h = new DsROTEntry(secondGraph);

        IBaseFilter Muxe = FindFilter(FilterCategory.VideoCompressorCategory, "ffdshow video encoder");
        secondGraph.AddFilter(Muxe, "Mux");

        pinout = FindPinByDirection(BridgeSourceFilter, PinDirection.Output);
        pinin = FindPinByDirection(Muxe, PinDirection.Input);

        secondGraph.Connect(pinout, pinin);

        IBaseFilter MKV = FindFilter(FilterCategory.LegacyAmFilterCategory, "Haali Matroska Muxer");
        IFileSinkFilter fs = (IFileSinkFilter)MKV;
        fs.SetFileName("c:\\cool.mkv", null);

        secondGraph.AddFilter(MKV, "mux");

        pinout = FindPinByDirection(Muxe, PinDirection.Output);
        pinin = FindPinByDirection(MKV, PinDirection.Input);
        secondGraph.Connect(pinout, pinin);

        bridge.BridgeGraphs(BridgeSinkFilter, BridgeSourceFilter);



        IMediaControl mediacontrolforpartone = (IMediaControl)firstGraph;
        mediacontrolforparttwo = (IMediaControl)secondGraph;
        mediacontrolforpartone.Run();
        mediacontrolforparttwo.Run();

    }
Luke Girvin
  • 12,672
  • 8
  • 57
  • 79
Grant
  • 684
  • 1
  • 7
  • 23
  • Is this working or not? If not remove the call to `bridge.BridgeGraphs` and check if the preview of the first graph is working. – wimh Jun 22 '11 at 13:38
  • with the bridge.bridgegraphs in the code the program takes 1 frame puts it on screen then freezes and never makes a video. if i comment out the bridgegraphs then the preview does work – Grant Jun 22 '11 at 16:43
  • 1
    Can you also try to put the `bridge.BridgeGraphs` after both graphs run? I can image the first graph blocks when the second is connected, but not running. – wimh Jun 22 '11 at 17:23
  • it still just produces 1 frame, freeze the stream and doesnt make a video :( – Grant Jun 22 '11 at 17:43
  • 1
    I have seen more issues where the graph was freezing after 1 frame. Those were almost everytime related to audio. You don't have any audio, so that does not look the cause. But maybe it is afterall. You don't configure the haali mastroska muxer, maybe it expects an audio stream too in it's default settings. I have never used the haali filter, but I know for example that the asf writer uses a default profile which includes audio+video. In that case it will not work at all if you only connect video. Maybe this helps... – wimh Jun 22 '11 at 18:05
  • would i expect better results if i switched to just avi muxer -> filewriter then? – Grant Jun 22 '11 at 18:13
  • 2
    I don't know. I would expect better results if you use `BridgeSourceFilter ->NullRenderer`, but then the second graph does nothing usefull :) – wimh Jun 22 '11 at 20:57
  • The Null Renderer on the second graph does allow the first graph to preview without too much issue. – Grant Jun 23 '11 at 17:30
  • 2
    So it looks like your problem is in the second graph. You can first try to create a graph without GMBBridge; `Webcam -> Smart Tee (Capture) -> ffdshow video encoder -> haali mastroska muxer`. – wimh Jun 23 '11 at 17:37
  • MKV does not save or create a video. meaning something is wrong with it or the way its working. webcam -> Smart Tee (capture) -> ffdshow video encoder -> avi mux ->file writer does work however :) – Grant Jun 23 '11 at 17:45
  • 2
    You can publish your graph to the ROT (AddGraphToRot), and use GraphStudio to view it to see if there is something strange. Such as an unconnected pin which should be connected, or property windows with wrong settings. GraphStudio: http://blog.monogram.sk/janos/tools/monogram-graphstudio/ – wimh Jun 23 '11 at 17:51
  • Graphedit can do the same thing no? i add the dsrot and i can view it on graphedit. When i stop the second graph, should i just bridge(null,null) secondgraph.stop() or should i also do something to the Smart Tee capture pin? – Grant Jun 23 '11 at 17:56
  • 2
    Yes, Graphedit can do the same. But look at the running graph with graphedit. If you want to stop the second graph, first bridge(null,null), then you can stop it. No need to do anything with the smart tee. GMFBridge will simply drop samples if there is no second graph to send them to, that way the preview can continue. – wimh Jun 23 '11 at 18:07
  • thanks for your help Wimmel, you should make an answer and say something so i can tag it :p atleast you'd get some rep that way :) – Grant Jun 23 '11 at 18:13
  • it's more interesting (for others) how you finally solved it. And you can use the rep better then me :) – wimh Jun 23 '11 at 18:23
  • do you know if there is a simple and easy way to embed the renderer to the form? – Grant Jun 23 '11 at 18:30
  • No, I have never used directshow in c#.... – wimh Jun 23 '11 at 18:33
  • 2
    @Grant embedding the renderer to the form should be the same as in any other directshow application, every form in C# has a Handle property that can be used while creating the graph, and combined with the IVideoWindow interface. Take a look at this sample for details: http://www.codeproject.com/KB/directx/directshowmediaplayer.aspx – yms Jun 23 '11 at 18:41
  • Thanks yms, the video window was just 4 lines of code and didnt need to alter my existing code :) – Grant Jun 23 '11 at 22:41
  • 1
    This is not a chatroom or discussion forum. @Grant: If you need to clarify your question, edit it to do so. Posting comment after comment to explain is noise, and makes things unreadable and unsearchable. If you need to post additional questions, make them separate questions so they're more clear and can also be searched later. The noise on this question are drawing requests for moderator review (which is how I found it). – Ken White Jun 24 '11 at 03:38

1 Answers1

5

To use GMFBridge correctly to my current knowledge:

Grab the GMFBridge DLL from: http://www.gdcl.co.uk/gmfbridge/ Grab the DirectShowLib DLL from: https://sourceforge.net/projects/directshownet/files/DirectShowNET/

include them both in your project.

Create 2 graphs. 1 is for preview the 2nd is for capture.

IGraphBuilder firstgraph = (IGraphBuilder) new FilterGraph();
IGraphBuilder secondgraph = (IGraphBuilder) new FilterGraph();

Create a Bridge which from the GMFBridge dll, will connect the two graphs

IGMFBridgeController Bridge = (IGMFBridgeController) new GMFBridgeController();

From here you setup the bridge to allow for muxed inputs

Bridge.AddStream(1, eFormatType.eMuxInputs, 1);

from here you can add your source video filter, it doesnt need connected to the bridge, add Smart Tee, and connect source to Smart Tee.

then create a filter to house the first bridge filter that will do the work

BridgeSinkFilter = (IBaseFilter)Bridge.InsertSinkFilter(firstgraph);

This filter will continuously accept video from the capture pin of Smart Tee. If the 2nd graph bridge filter is connected and running it will pass the video from BridgeSInkFilter to the 2nd graph. otherwise, it just throws it out, but it is always running.

Connect the BridgeSinkFilter to the capture pin of Smart Tee. I found the best way to connect the pins is to use FindPinByDirection by https://splicer.svn.codeplex.com/svn/src/Splicer/Utilities/FilterGraphTools.cs and then just call

firstgraph.connect(pinoutput, pininput)

From here, to preview the video, the AVI Decompressor filter from FilterCategory.LegacyAmFilterCategory should be addedd and connected to the Preview pin of Smart Tee. Then Video Renderer added and connected to AVI Decompressor.

That should take care of the first graph.

The second graph needs to start with the bridge. it will create a bridge from the first graph bridgesinkfilter and pull it to a second graph in which we can do anything we want to it. to do this we need the other side of the bridge.

IBaseFilter BridgeSourceFilter = (IBaseFilter)Bridge.InsertSourceFilter(BridgeSinkFilter,secondgraph);

this sets the source as the sinkfilter from the firstgraph but puts it on our second graph under the guise of BridgeSourceFilter.

Now connect an encoder, ffdshow video encoder, etc.. connect it to the BridgeSourceFilter.

add in a muxer, AVI Mux, and file writer. Connect them. Thats everything for the second graph.

To finish the graphs off, we need to create 2 mediacontrollers that can start and stop the graphs.

IMediaControl MediaControl_FirstGraph = (IMediaControl)firstgraph;
IMediaControl MediaControl_SecondGraph = (IMediaControl)secondgraph;

now we can call

MediaControl_FirstGraph.Run() to start previewing the video.

and then to capture that video, we need to connect the bridge between the first and second graph, then run the second graph.

Bridge.BridgeGraphs(BridgeSinkFilter,BridgeSourceFilter);
MediaControl_SecondGraph.Run()

at any point, you can stop capturing by breaking the bridge connection, then stopping the second graph.

Bridge.BridgeGraphs(null, null);
MediaControl_SecondGraph.Stop();

I think that about covers what ive found out about GMF bridge control :)

Hopefully this is up to Geraint Davies's standards

if there is even the slightest error anywhere in the second graph, when you run the second graph, it will stop the first graph. its a good indication something is wrong. If you give it an invalid name like making Avi mux -> file writer try to save in a location that isnt real, itll stop the graph

This code would create a new window and stick the video in that new window. To stream the video into a panel box of a form you just really need to add 4 lines of code.

IVideoWindow var = firstgraph as IVideoWindow();
var.put_Owner(panel1.handle);
var.put_windowstyle( windowstyle.child | windowstyle.clipchildren );
var.SetWindowPosition( panel1.clientrectangle.left, panel1.clientrectangle.top, panel1.clientrectangle.width, panel1.clientrectangle.height);    
Grant
  • 684
  • 1
  • 7
  • 23