Menu

Custom Sample Grabber Callback

Voc
2010-08-31
2012-10-29
  • Voc

    Voc - 2010-08-31

    Hi forum,

    I've written a custom sample grabber (in C++) to handle the VideoInfoHeader2
    format and have registered it as a COM object. I want to use it in a C#
    application in a similar way that one would use the standard DirectShow
    SampleGrabber in order to grab frame data of video from a live capture source.

    I can successfully add my custom filter to the graph, however when I try
    casting it as an ISampleGrabber filter in order to set the callback and
    intercept video data I get the following exception:

    Guid CLSID_VI2SampleGrabber = new Guid("{574AA56A-62D7-4EDE-
    9E90-CCAA9C811D0D}");
    object grabberObject =
    Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_VI2SampleGrabber));
    ISampleGrabber sampleGrabber = (ISampleGrabber)grabberObject; **{<-------
    EXCEPTION THROWN HERE}

    The exception is:

    Unable to cast COM object of type 'System.__ComObject' to interface type
    'DirectShowLib.ISampleGrabber'. This operation failed because the
    QueryInterface call on the COM component for the interface with IID
    '{6B652FFF-11FE-4FCE-92AD-0266B5D7C78F}' failed due to the following error: No
    such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

    If anyone can help me out or provide any pointers as to why I'm getting this
    exception, or how to acquire the set callback method, I would be greatly
    appreciative.

    Yours faithfully,
    Voc.**

     
  • snarfle

    snarfle - 2010-08-31

    Does the filter you wrote actually support ISampleGrabber? Have you tried
    putting a breakpoint on your NonDelegatingQueryInterface to see what is
    happening?

     
  • Voc

    Voc - 2010-08-31

    HI,

    Solved it. I made a stupid mistake in thinking I had to have a different GUID
    for the sample grabber IID. I can cast my custom fitler as a sample grabber. I
    have a new problem however. When I call SetCallback on my filter I get the
    following exception:

    Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

    Is this behaviour a typical error?

    Kind regards,
    Voc.

     
  • snarfle

    snarfle - 2010-09-01

    Have you set a breakpoint on the SetCallback method? That will tell you if the
    problem is coming in managed or unmanaged code.

     
  • Voc

    Voc - 2010-09-01

    Hi Snarfle,

    Thanks for the responses. I put a breakpoint in the SetCallback method of the
    C++ code, however I'm not sure how to step into it whilst debugging my C#
    code. Can you help me with this?

    Thanks in advance,
    Voc.

     
  • snarfle

    snarfle - 2010-09-01

    We're wandering a little far afield from the forum topic. However, I'll point
    you to Project Properties/Debug/Enable unmanaged code debugging.

     
  • The March Hare

    The March Hare - 2010-09-01

    Snarfle: OP multi-posted here:

    http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/th
    read/387eb9f1-bcb5-4ed7-a2f7-d8c4235671a7

    My guess is that he hasn't got the signature correct for the custom interface
    in the grabber sample:

    // We define the interface the app can use to program us
    MIDL_INTERFACE("6B652FFF-11FE-4FCE-92AD-0266B5D7C78F")
    IGrabberSample : public IUnknown
    {
        public:
    
            virtual HRESULT STDMETHODCALLTYPE SetAcceptedMediaType( 
                const CMediaType *pType) = 0;
    
            virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( 
                CMediaType *pType) = 0;
    
            virtual HRESULT STDMETHODCALLTYPE SetCallback( 
                SAMPLECALLBACK Callback) = 0;
    
            virtual HRESULT STDMETHODCALLTYPE SetDeliveryBuffer( 
                ALLOCATOR_PROPERTIES props,
                BYTE *pBuffer) = 0;
    };
    
     
  • Voc

    Voc - 2010-09-28

    Hi guys,

    I know it's been a while, I was working on other projects and had to leave
    this one, but now I'm back on it. The signature was correct as provided in the
    sample code and the post above. The problem with memory to my understanding
    was how I was building my graph with another custom transform filter which I
    have rectified.

    I have a new problem however, the setcallback method in the unmanaged code is
    never called. I will be posting this in the msdn directshow forum for obvious
    reasons, but felt it was necessary to bring closure to this thread.

    Kind regards and thanks,
    Voc.

     
  • snarfle

    snarfle - 2010-09-30

    Have you tried setting a breakpoint in your filter at the point where it
    should be calling your c# code?

     
  • Voc

    Voc - 2010-09-30

    Hi Snarfle,

    I posted on the msdn forum and the experts there suggest that it is how I
    configure my callback in my C# code.

    I'm not quite sure where my C++ filter would be calling C# code, I thought it
    worked the other way around. I think I'm misunderstanding something.

    To ensure it wasn't my custom filter that was problematic I built the example
    sample grabber in the 2004 Summer SDK and registered it as a COM object. I
    then used GraphEditPlus to construct a graph and generate the code in C#. The
    source filter was configured to deliver VideoInfo samples which was connected
    to an AVI Decompressor and Color Space Converter and then to the example
    sample grabber followed by a VMR9 Renderer.

    I then added code to configure the sample grabber callback. It is this which I
    am lead to believe I have done incorrectly. I have configured sample grabbers
    before in a similar fashion, so I'm really confused as to what I'm doing wrong
    here. The code is posted below:


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices.ComTypes;
    using System.Runtime.InteropServices;
    using DirectShowLib;

    namespace ExampleSampleGrabber
    {
    public partial class Form1 : Form, ISampleGrabberCB
    {
    // Sample grabber.
    ISampleGrabber sampleGrabber = null;
    IGraphBuilder graph = null;

    private void checkHR(int hr, string msg)
    {
    if (hr < 0)
    {
    Console.WriteLine(msg);
    DsError.ThrowExceptionForHR(hr);
    }
    }

    private IPin GetPin(IBaseFilter filter, string pinname)
    {
    IEnumPins epins;
    int hr = filter.EnumPins(out epins);
    checkHR(hr, "Can't enumerate pins");
    IntPtr fetched = Marshal.AllocCoTaskMem(4);
    IPin pins = new IPin;
    while (epins.Next(1, pins, fetched) == 0)
    {
    PinInfo pinfo;
    pins.QueryPinInfo(out pinfo);
    bool found = (pinfo.name == pinname);
    DsUtils.FreePinInfo(pinfo);
    if (found)
    return pins;
    }
    checkHR(-1, "Pin not found");
    return null;
    }

    private void BuildGraph(IGraphBuilder pGraph)
    {
    int hr = 0;

    //graph builder
    ICaptureGraphBuilder2 pBuilder = (ICaptureGraphBuilder2)new
    CaptureGraphBuilder2();
    hr = pBuilder.SetFiltergraph(pGraph);
    checkHR(hr, "Can't SetFiltergraph");

    Guid CLSID_VideoMixingRenderer9 = new Guid("{51B4ABF3-748F-
    4E3B-A276-C828330E926A}"); //quartz.dll
    Guid CLSID_SampleGrabberExample = new
    Guid("{2FA4F053-6D60-4CB0-9503-8E89234F3F73}"); //grabber.ax

    //add Video Mixing Renderer 9
    IBaseFilter pVideoMixingRenderer9 = (IBaseFilter)Activator.CreateInstance(Type
    .GetTypeFromCLSID(CLSID_VideoMixingRenderer9));
    hr = pGraph.AddFilter(pVideoMixingRenderer9, "Video Mixing Renderer 9");
    checkHR(hr, "Can't add Video Mixing Renderer 9 to graph");

    //add SampleGrabber Example
    IBaseFilter pSampleGrabberExample = (IBaseFilter)Activator.CreateInstance(Type
    .GetTypeFromCLSID(CLSID_SampleGrabberExample));
    hr = pGraph.AddFilter(pSampleGrabberExample, "SampleGrabber Example");
    checkHR(hr, "Can't add SampleGrabber Example to graph");

    sampleGrabber = (ISampleGrabber)new SampleGrabber();
    ConfigureSampleGrabber();

    //add Pinnacle 510-USB
    IBaseFilter pPinnacle510USB =
    CreateFilter(@"@device:pnp:\?\usb#vid_2304&pid_0223#6&2f8c4b4&0&7#{65e8773d-
    8f56-11d0-a3b9-00a0c9223196}{fb112213-ff34-40a9-bb84-f2c76b349ebb}");
    hr = pGraph.AddFilter(pPinnacle510USB, "Pinnacle 510-USB");
    checkHR(hr, "Can't add Pinnacle 510-USB to graph");

    //add AVI Decompressor
    IBaseFilter pAVIDecompressor = (IBaseFilter)new AVIDec();
    hr = pGraph.AddFilter(pAVIDecompressor, "AVI Decompressor");
    checkHR(hr, "Can't add AVI Decompressor to graph");

    //add Color Space Converter
    IBaseFilter pColorSpaceConverter = (IBaseFilter)new Colour();
    hr = pGraph.AddFilter(pColorSpaceConverter, "Color Space Converter");
    checkHR(hr, "Can't add Color Space Converter to graph");

    AMMediaType pmt = new AMMediaType();
    pmt.majorType = MediaType.Video;
    pmt.subType = MediaSubType.YUY2;
    pmt.formatType = FormatType.VideoInfo;
    pmt.fixedSizeSamples = true;
    pmt.formatSize = 88;
    pmt.sampleSize = 829440;
    pmt.temporalCompression = false;
    VideoInfoHeader format = new VideoInfoHeader();
    format.SrcRect = new DsRect();
    format.TargetRect = new DsRect();
    format.BitRate = 165888000;
    format.AvgTimePerFrame = 400000;
    format.BmiHeader = new BitmapInfoHeader();
    format.BmiHeader.Size = 40;
    format.BmiHeader.Width = 720;
    format.BmiHeader.Height = 576;
    format.BmiHeader.Planes = 1;
    format.BmiHeader.BitCount = 16;
    format.BmiHeader.Compression = 844715353;
    format.BmiHeader.ImageSize = 829440;
    pmt.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(format));
    Marshal.StructureToPtr(format, pmt.formatPtr, false);
    hr = ((IAMStreamConfig)GetPin(pPinnacle510USB, "Video out")).SetFormat(pmt);
    DsUtils.FreeAMMediaType(pmt);
    checkHR(hr, "Can't set format");

    //connect Pinnacle 510-USB and AVI Decompressor
    hr = pGraph.ConnectDirect(GetPin(pPinnacle510USB, "Video out"),
    GetPin(pAVIDecompressor, "XForm In"), null);
    checkHR(hr, "Can't connect Pinnacle 510-USB and AVI Decompressor");

    //connect AVI Decompressor and Color Space Converter
    hr = pGraph.ConnectDirect(GetPin(pAVIDecompressor, "XForm Out"),
    GetPin(pColorSpaceConverter, "Input"), null);
    checkHR(hr, "Can't connect AVI Decompressor and Color Space Converter");

    //connect Color Space Converter and SampleGrabber Example
    hr = pGraph.ConnectDirect(GetPin(pColorSpaceConverter, "XForm Out"),
    GetPin(pSampleGrabberExample, "Input"), null);
    checkHR(hr, "Can't connect Color Space Converter and SampleGrabber Example");

    //connect SampleGrabber Example and Video Mixing Renderer 9
    hr = pGraph.ConnectDirect(GetPin(pSampleGrabberExample, "Output"),
    GetPin(pVideoMixingRenderer9, "VMR Input0"), null);
    checkHR(hr, "Can't connect SampleGrabber Example and Video Mixing Renderer
    9");

    }

    public Form1()
    {
    InitializeComponent();
    }

    public IBaseFilter CreateFilter(string displayName)
    {
    int hr = 0;
    IBaseFilter filter = null;
    IBindCtx bindCtx = null;
    IMoniker moniker = null;

    try
    {
    hr = CreateBindCtx(0, out bindCtx);
    Marshal.ThrowExceptionForHR(hr);

    int eaten;
    hr = MkParseDisplayName(bindCtx, displayName, out eaten, out moniker);
    Marshal.ThrowExceptionForHR(hr);

    Guid guid = typeof(IBaseFilter).GUID;
    object obj;
    moniker.BindToObject(bindCtx, null, ref guid, out obj);
    filter = (IBaseFilter)obj;
    }
    finally
    {
    if (bindCtx != null) Marshal.ReleaseComObject(bindCtx);
    if (moniker != null) Marshal.ReleaseComObject(moniker);
    }

    return filter;
    }

    public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);

    public static extern int MkParseDisplayName(IBindCtx pcb, string szUserName,
    out int pchEaten, out IMoniker ppmk);

    private void Form1_Load(object sender, EventArgs e)
    {
    try
    {
    graph = (IGraphBuilder)new FilterGraph();
    Console.WriteLine("Building graph...");
    BuildGraph(graph);
    Console.WriteLine("Running...");
    IMediaControl mediaControl = (IMediaControl)graph;
    IMediaEvent mediaEvent = (IMediaEvent)graph;
    int hr = mediaControl.Run();
    checkHR(hr, "Can't run the graph");
    bool stop = false;
    int n = 0;
    while (!stop)
    {
    System.Threading.Thread.Sleep(500);
    Console.Write(".");
    EventCode ev;
    IntPtr p1, p2;
    if (mediaEvent.GetEvent(out ev, out p1, out p2, 0) == 0)
    {
    if (ev == EventCode.Complete || ev == EventCode.UserAbort)
    {
    Console.WriteLine("Done!");
    stop = true;
    }
    else
    if (ev == EventCode.ErrorAbort)
    {
    Console.WriteLine("An error occured: HRESULT={0:X}", p1);
    mediaControl.Stop();
    stop = true;
    }
    mediaEvent.FreeEventParams(ev, p1, p2);
    }
    // stop after 10 seconds
    n++;
    if (n > 20)
    {
    Console.WriteLine("stopping..");
    mediaControl.Stop();
    stop = true;
    }
    }
    }
    catch (COMException ex)
    {
    Console.WriteLine("COM error: " + ex.ToString());
    }
    catch (Exception ex)
    {
    Console.WriteLine("Error: " + ex.ToString());
    }
    }

    private void ConfigureSampleGrabber()
    {
    // Initialise error code.
    AMMediaType media;
    int hr = 0;
    media = new AMMediaType();
    media.majorType = MediaType.Video;
    media.subType = MediaSubType.YUY2;
    media.formatType = FormatType.VideoInfo;
    hr = sampleGrabber.SetMediaType(media);
    DsError.ThrowExceptionForHR(hr);
    DsUtils.FreeAMMediaType(media);
    media = null;
    hr = sampleGrabber.SetBufferSamples(true);
    DsError.ThrowExceptionForHR(hr);
    {
    int a = 1;
    }

    // Set sample grabber call back.
    hr = sampleGrabber.SetOneShot(false);
    DsError.ThrowExceptionForHR(hr);
    ISampleGrabberCB sampleGrabberCB = graph as ISampleGrabberCB;
    hr = sampleGrabber.SetCallback(sampleGrabberCB, 0);
    DsError.ThrowExceptionForHR(hr);
    }

    region ISampleGrabberCB Members

    int ISampleGrabberCB.BufferCB(double SampleTime, IntPtr pBuffer, int
    BufferLen)
    {
    throw new NotImplementedException();
    }

    int ISampleGrabberCB.SampleCB(double SampleTime, IMediaSample pSample)
    {
    Marshal.ReleaseComObject(pSample);
    return 1;
    }

    endregion

    }
    }


    I am well and truly puzzled. If you have any ideas on how I can spot my error
    or any advice at all I'd really appreciate it. My post in the msdn forum can
    be found here:

    http://social.msdn.microsoft.com/Forums/en-
    AU/windowsdirectshowdevelopment/thread/a4c92ec9-a3c2-49fd-
    a7e0-478a99e87c43?prof=required

    Again many thanks for your input thus far Snarfle.
    Sincerely,
    Voc.

     
  • Voc

    Voc - 2010-09-30

    I forgot to mention that I ran the graph and it displays video fine, however
    the code in the SampleCB interface which was implemented explicitly is never
    hit.

    Voc.

     
  • snarfle

    snarfle - 2010-10-01

    It appears to me that this code will pass null to
    ISampleGrabber.SetCallback(). Have you actually checked the value? Take a look
    at some of our samples for how to pass the correct value here.

     
  • Voc

    Voc - 2010-10-05

    Hi Snarfle,

    The sampleGrabber was not null but it also was not correct based on the sample
    code on this site. I edited my code accordingly but now have new (and strange)
    problems. I can only call my ConfigureSampleGrabber() method after I connect
    the baseGrabberFilter in the graph. I have a feeling this is due to how the
    DirectX SDK Sample Grabber filter is written because it seems that the
    standard SG filter used in the code on this site can be configured prior to
    connection .

    If I call method prior to connecting the filter I get the following error:
    hr = -2147220983
    "The operation cannot be performed because the pins are not connected."

    If I configure the sample grabber after I connect the pins the SetCallback
    method causes an exception:

    "First-chance exception at 0x03b98ed4 (grabber.ax) in
    ExampleSampleGrabber.exe: 0xC0000005: Access violation writing location
    0x00000000.
    A first chance exception of type 'System.AccessViolationException' occurred in
    ExampleSampleGrabber.exe"

    I can't step into the unmanaged code at the line where SetCallback is called
    either which is perplexing. After some searching, people have mentioned that
    memory access violations when Setcallback is used is due to qedit.h, although
    my call stack mentions nothing of the like.

    This is the edited BuildGraph() method:


    private void BuildGraph(IGraphBuilder pGraph)
    {
    int hr = 0;

    //graph builder
    ICaptureGraphBuilder2 pBuilder = (ICaptureGraphBuilder2)new
    CaptureGraphBuilder2();
    hr = pBuilder.SetFiltergraph(pGraph);
    checkHR(hr, "Can't SetFiltergraph");

    Guid CLSID_VideoMixingRenderer9 = new Guid("{51B4ABF3-748F-
    4E3B-A276-C828330E926A}"); //quartz.dll
    Guid CLSID_SampleGrabberExample = new
    Guid("{2FA4F053-6D60-4CB0-9503-8E89234F3F73}"); //grabber.ax

    //add Video Mixing Renderer 9
    IBaseFilter pVideoMixingRenderer9 = (IBaseFilter)Activator.CreateInstance(Type
    .GetTypeFromCLSID(CLSID_VideoMixingRenderer9));
    hr = pGraph.AddFilter(pVideoMixingRenderer9, "Video Mixing Renderer 9");
    checkHR(hr, "Can't add Video Mixing Renderer 9 to graph");

    //add SampleGrabber Example
    IBaseFilter pSampleGrabberExample = (IBaseFilter)Activator.CreateInstance(Type
    .GetTypeFromCLSID(CLSID_SampleGrabberExample));

    sampleGrabber = (ISampleGrabber)pSampleGrabberExample;
    baseGrabberFilter = sampleGrabber as IBaseFilter;
    hr = pGraph.AddFilter(baseGrabberFilter, "Grabber");
    checkHR(hr, "Can't add Grabber to graph");

    //add Pinnacle 510-USB
    IBaseFilter pPinnacle510USB =
    CreateFilter(@"@device:pnp:\?\usb#vid_2304&pid_0223#6&2f8c4b4&0&7#{65e8773d-
    8f56-11d0-a3b9-00a0c9223196}{fb112213-ff34-40a9-bb84-f2c76b349ebb}");
    hr = pGraph.AddFilter(pPinnacle510USB, "Pinnacle 510-USB");
    checkHR(hr, "Can't add Pinnacle 510-USB to graph");

    //add AVI Decompressor
    IBaseFilter pAVIDecompressor = (IBaseFilter)new AVIDec();
    hr = pGraph.AddFilter(pAVIDecompressor, "AVI Decompressor");
    checkHR(hr, "Can't add AVI Decompressor to graph");

    //add Color Space Converter
    IBaseFilter pColorSpaceConverter = (IBaseFilter)new Colour();
    hr = pGraph.AddFilter(pColorSpaceConverter, "Color Space Converter");
    checkHR(hr, "Can't add Color Space Converter to graph");

    AMMediaType pmt = new AMMediaType();
    pmt.majorType = MediaType.Video;
    pmt.subType = MediaSubType.YUY2;
    pmt.formatType = FormatType.VideoInfo;
    pmt.fixedSizeSamples = true;
    pmt.formatSize = 88;
    pmt.sampleSize = 829440;
    pmt.temporalCompression = false;
    VideoInfoHeader format = new VideoInfoHeader();
    format.SrcRect = new DsRect();
    format.TargetRect = new DsRect();
    format.BitRate = 165888000;
    format.AvgTimePerFrame = 400000;
    format.BmiHeader = new BitmapInfoHeader();
    format.BmiHeader.Size = 40;
    format.BmiHeader.Width = 720;
    format.BmiHeader.Height = 576;
    format.BmiHeader.Planes = 1;
    format.BmiHeader.BitCount = 16;
    format.BmiHeader.Compression = 844715353;
    format.BmiHeader.ImageSize = 829440;
    pmt.formatPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(format));
    Marshal.StructureToPtr(format, pmt.formatPtr, false);
    hr = ((IAMStreamConfig)GetPin(pPinnacle510USB, "Video out")).SetFormat(pmt);
    DsUtils.FreeAMMediaType(pmt);
    checkHR(hr, "Can't set format");

    //connect Pinnacle 510-USB and AVI Decompressor
    hr = pGraph.ConnectDirect(GetPin(pPinnacle510USB, "Video out"),
    GetPin(pAVIDecompressor, "XForm In"), null);
    checkHR(hr, "Can't connect Pinnacle 510-USB and AVI Decompressor");

    //connect AVI Decompressor and Color Space Converter
    hr = pGraph.ConnectDirect(GetPin(pAVIDecompressor, "XForm Out"),
    GetPin(pColorSpaceConverter, "Input"), null);
    checkHR(hr, "Can't connect AVI Decompressor and Color Space Converter");

    //connect Color Space Converter and SampleGrabber Example
    hr = pGraph.ConnectDirect(GetPin(pColorSpaceConverter, "XForm Out"),
    GetPin(baseGrabberFilter, "Input"), null);
    checkHR(hr, "Can't connect Color Space Converter and SampleGrabber Example");

    //connect SampleGrabber Example and Video Mixing Renderer 9
    hr = pGraph.ConnectDirect(GetPin(baseGrabberFilter, "Output"),
    GetPin(pVideoMixingRenderer9, "VMR Input0"), null);
    checkHR(hr, "Can't connect SampleGrabber Example and Video Mixing Renderer
    9");

    ConfigureSampleGrabber(sampleGrabber);
    }


    This is the edited ConfigureSampleGrabber() method:


    private void ConfigureSampleGrabber(ISampleGrabber sampleGrabber)
    {
    // Initialise error code.
    AMMediaType media;
    int hr = 0;
    media = new AMMediaType();
    media.majorType = MediaType.Video;
    media.subType = MediaSubType.YUY2;
    media.formatType = FormatType.VideoInfo;
    hr = sampleGrabber.SetMediaType(media);
    DsError.ThrowExceptionForHR(hr);
    DsUtils.FreeAMMediaType(media);
    media = null;

    hr = sampleGrabber.SetCallback(this, 0);
    DsError.ThrowExceptionForHR(hr);
    }


    Can anyone help?
    Thanks for your patience and time,
    Voc.

     
  • snarfle

    snarfle - 2010-10-05

    Have you Enabled unmanaged code debugging?

     
  • Voc

    Voc - 2010-10-05

    Yes. I can step into other parts of the unmanaged code or place breakpoints in
    the unmanaged code with _asm int 3. This doesn't work for the line in the
    managed code where I use SetCallback.

    Thanks for the prompt reply!

     
  • snarfle

    snarfle - 2010-10-05

    int 3? You should be able just use F9 to set breakpoints. Sounds like the
    symbols for your provider aren't being loaded correctly.

     
  • Voc

    Voc - 2010-10-06

    Hi Snarfle,

    I can place breakpoints the normal way (using F9) as you suggest, forgive my
    convolution. However I still cannot step into the StepCallback() method in C#.
    Is this line meant to execute the SetCallback method of the C++ code? Below is
    the code snippet from the DirectX SDK code sample:


    STDMETHODIMP CSampleGrabber::SetCallback( SAMPLECALLBACK Callback )
    {

    CAutoLock lock( &m_Lock );

    m_callback = Callback;

    return NOERROR;
    }


    Does the C# SetCallback method wrap the SetCallback method of the C++ filter?
    Also I'm not quite sure about what you mean by the symbols for my provider not
    loading properly.

    Thanks for the constant help. I feel that when this gets nutted out I'm going
    to do something out of sheer joy like propose or ask permission to bear your
    children.

    Yours faithfully,
    Voc.

     
  • snarfle

    snarfle - 2010-10-06

    Not having that source code in front of me, what is SAMPLECALLBACK defined
    as? In the DS SampleGrabber filter it's an interface pointer. This isn't a
    class name, is it?

     
  • Voc

    Voc - 2010-10-06

    It's a typedef. Here is the header file of the DirectX sample code:


    //----------------------------------------------------------------------------

    // File: Grabber.h
    //
    // Desc: DirectShow sample code - Header file for the SampleGrabber
    // example filter
    //
    // Copyright (c) Microsoft Corporation. All rights reserved.
    //----------------------------------------------------------------------------
    --

    //----------------------------------------------------------------------------

    // Define new GUID and IID for the sample grabber example so that they do NOT
    // conflict with the official DirectX SampleGrabber filter
    //----------------------------------------------------------------------------
    --
    // {2FA4F053-6D60-4cb0-9503-8E89234F3F73}
    DEFINE_GUID(CLSID_GrabberSample,
    0x2fa4f053, 0x6d60, 0x4cb0, 0x95, 0x3, 0x8e, 0x89, 0x23, 0x4f, 0x3f, 0x73);

    DEFINE_GUID(IID_IGrabberSample,
    0x6b652fff, 0x11fe, 0x4fce, 0x92, 0xad, 0x02, 0x66, 0xb5, 0xd7, 0xc7, 0x8f);

    // We define a callback typedef for this example.
    // Normally, you would make the SampleGrabber support a COM interface,
    // and in one of its methods you would pass in a pointer to a COM interface
    // used for calling back. See the DirectX documentation for the SampleGrabber
    // for more information.

    *typedef HRESULT (SAMPLECALLBACK) (
    IMediaSample * pSample,
    REFERENCE_TIME * StartTime,
    REFERENCE_TIME * StopTime,
    BOOL TypeChanged );

    // We define the interface the app can use to program us
    MIDL_INTERFACE("6B652FFF-11FE-4FCE-92AD-0266B5D7C78F")
    IGrabberSample : public IUnknown
    {
    public:

    virtual HRESULT STDMETHODCALLTYPE SetAcceptedMediaType(
    const CMediaType *pType) = 0;

    virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType(
    CMediaType *pType) = 0;

    virtual HRESULT STDMETHODCALLTYPE SetCallback(
    SAMPLECALLBACK Callback) = 0;

    virtual HRESULT STDMETHODCALLTYPE SetDeliveryBuffer(
    ALLOCATOR_PROPERTIES props,
    BYTE *pBuffer) = 0;
    };

    class CSampleGrabberInPin;
    class CSampleGrabber;

    //----------------------------------------------------------------------------
    // This is a special allocator that KNOWS that the person who is creating it
    // will only create one of them. It allocates CMediaSamples that only
    // reference the buffer location that is set in the pin's renderer's
    // data variable
    //----------------------------------------------------------------------------

    class CSampleGrabberAllocator : public CMemAllocator
    {
    friend class CSampleGrabberInPin;
    friend class CSampleGrabber;

    protected:

    // our pin who created us
    //
    CSampleGrabberInPin * m_pPin;

    public:

    CSampleGrabberAllocator( CSampleGrabberInPin * pParent, HRESULT *phr )
    : CMemAllocator( TEXT("SampleGrabberAllocator\0"), NULL, phr )
    , m_pPin( pParent )
    {
    };

    ~CSampleGrabberAllocator( )
    {
    // wipe out m_pBuffer before we try to delete it. It's not an allocated
    // buffer, and the default destructor will try to free it!
    m_pBuffer = NULL;
    }

    HRESULT Alloc( );

    void ReallyFree();

    // Override this to reject anything that does not match the actual buffer
    // that was created by the application
    STDMETHODIMP SetProperties(ALLOCATOR_PROPERTIES pRequest,
    ALLOCATOR_PROPERTIES
    pActual);

    };

    //----------------------------------------------------------------------------
    // we override the input pin class so we can provide a media type
    // to speed up connection times. When you try to connect a filesourceasync
    // to a transform filter, DirectShow will insert a splitter and then
    // start trying codecs, both audio and video, video codecs first. If
    // your sample grabber's set to connect to audio, unless we do this, it
    // will try all the video codecs first. Connection times are sped up x10
    // for audio with just this minor modification!
    //----------------------------------------------------------------------------

    class CSampleGrabberInPin : public CTransInPlaceInputPin
    {
    friend class CSampleGrabberAllocator;
    friend class CSampleGrabber;

    CSampleGrabberAllocator * m_pPrivateAllocator;
    ALLOCATOR_PROPERTIES m_allocprops;
    BYTE * m_pBuffer;
    BOOL m_bMediaTypeChanged;

    protected:

    CSampleGrabber * SampleGrabber( ) { return (CSampleGrabber*) m_pFilter; }
    HRESULT SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE * m_pBuffer );

    public:

    CSampleGrabberInPin( CTransInPlaceFilter * pFilter, HRESULT * pHr )
    : CTransInPlaceInputPin( TEXT("SampleGrabberInputPin\0"), pFilter, pHr,
    L"Input\0" )
    , m_pPrivateAllocator( NULL )
    , m_pBuffer( NULL )
    , m_bMediaTypeChanged( FALSE )
    {
    memset( &m_allocprops, 0, sizeof( m_allocprops ) );
    }

    ~CSampleGrabberInPin( )
    {
    if( m_pPrivateAllocator ) delete m_pPrivateAllocator;
    }

    // override to provide major media type for fast connects

    HRESULT GetMediaType( int iPosition, CMediaType *pMediaType );

    // override this or GetMediaType is never called

    STDMETHODIMP EnumMediaTypes( IEnumMediaTypes **ppEnum );

    // override this to refuse any allocators besides
    // the one the user wants, if this is set

    STDMETHODIMP NotifyAllocator( IMemAllocator *pAllocator, BOOL bReadOnly );

    // override this so we always return the special allocator, if necessary

    STDMETHODIMP GetAllocator( IMemAllocator **ppAllocator );

    HRESULT SetMediaType( const CMediaType *pmt );

    // we override this to tell whoever's upstream of us what kind of
    // properties we're going to demand to have
    //
    STDMETHODIMP GetAllocatorRequirements( ALLOCATOR_PROPERTIES *pProps );

    };

    //----------------------------------------------------------------------------
    //
    //----------------------------------------------------------------------------

    class CSampleGrabber : public CTransInPlaceFilter,
    public IGrabberSample
    {

    friend class CSampleGrabberInPin;
    friend class CSampleGrabberAllocator;

    protected:

    CMediaType m_mtAccept;
    SAMPLECALLBACK m_callback;
    CCritSec m_Lock; // serialize access to our data

    BOOL IsReadOnly( ) { return !m_bModifiesData; }

    // PURE, override this to ensure we get
    // connected with the right media type
    HRESULT CheckInputType( const CMediaType * pmt );

    // PURE, override this to callback
    // the user when a sample is received
    HRESULT Transform( IMediaSample * pms );

    // override this so we can return S_FALSE directly.
    // The base class CTransInPlace
    // Transform( ) method is called by it's
    // Receive( ) method. There is no way
    // to get Transform( ) to return an S_FALSE value
    // (which means "stop giving me data"),
    // to Receive( ) and get Receive( ) to return S_FALSE as well.

    HRESULT Receive( IMediaSample * pms );

    public:

    static CUnknown WINAPI CreateInstance(LPUNKNOWN punk, HRESULT phr);

    // Expose ISampleGrabber
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
    DECLARE_IUNKNOWN;

    CSampleGrabber( IUnknown * pOuter, HRESULT * pHr, BOOL ModifiesData );

    // IGrabberSample
    STDMETHODIMP SetAcceptedMediaType( const CMediaType * pmt );
    STDMETHODIMP GetConnectedMediaType( CMediaType * pmt );
    STDMETHODIMP SetCallback( SAMPLECALLBACK Callback );
    STDMETHODIMP SetDeliveryBuffer( ALLOCATOR_PROPERTIES props, BYTE * m_pBuffer
    );
    };

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------**

     
  • snarfle

    snarfle - 2010-10-06

    Well, that's the source of your problem.

    The DS SampleGrabber's SetCallback method takes an interface pointer
    (ISampleGrabberCB). So, if you have a COM object that implements the COM
    interface ISampleGrabberCB in any language, you can pass a pointer to that
    interface to ISampleGrabber.SetCallback.

    However, that's not what this code does. As the comments above this typedef
    warn, Normally, you would.... However, that's not what they did. Instead
    they just declared a function pointer. While .Net performs all the necessary
    magic to call and be called by COM objects, having unmanaged code make calls
    to a c# method... I don't even know if you can.

     
  • Voc

    Voc - 2010-10-10

    Ah OK, thanks for the clarification. I think I'll have to find some other
    means of writing my custom sample grabber. I tried searching for the source
    code of the SampleGrabber that DirectShow provides (C1F400A0-3F08-11D3-9F0B-
    006008039E37), but I could not find it. Is it available somewhere?

    I also read in an MSDN forum that the December 2002 DirectX SDK has a sample
    grabber sample, but I couldn't find this on the DirectX SDK downloads site.

    Thanks again!
    Voc

     
  • snarfle

    snarfle - 2010-10-10

    Have you considered writing a DMO? There are a couple in the samples you can
    look at.

     
  • Voc

    Voc - 2010-10-24

    Hi Snarfle,

    I know it's been a while. Are you saying that if I were to write a DMO, I
    would be able to register it as a COM object and be able to implement the
    ISampleCallback in managed code as I describe in my OP?

    Kind regards,
    Voc

     
  • snarfle

    snarfle - 2010-10-24

    A DMO might be described as a light-weight filter, except that unlike DS
    filters, DMOs can readily be written in c#..

    Read the docs for the DMO Wrapper filter (http://msdn.microsoft.com/en-
    us/library/dd375519%28v=VS.85%29.aspx
    ).

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.