views:

322

answers:

3

Hey,

I am writing an application that needs to record video using DirectShow - to do this, I am using the interop library DirectShowLib, which seems to work great.

However, I now have the need to get a callback notification as samples are written to a file, so I can add data unit extensions. According to the msdn documentation, in C++ this is done by implementing the IAMWMBufferPassCallback interface, and passing the resulting object to the SetNotify method of a pin's IAMWMBufferPass interface.

So, I created a small class that implements the IAMWMBufferPassCallback interface from DirectShowLib:

 class IAMWMBufferPassCallbackImpl : IAMWMBufferPassCallback    
 {  
        private RecordingPlayer player;

        public IAMWMBufferPassCallbackImpl(RecordingPlayer player)
        {
            this.player = player;
        }

        public int Notify(INSSBuffer3 pNSSBuffer3, IPin pPin, long prtStart, long prtEnd)
        {
            if (player.bufferPin == pPin && !player.firstBufferHandled)
            {
                player.firstBufferHandled = true;

                //do stuff with the buffer....
            }

            return 0;
        }

}

I then retrieved the IAMWMBufferPass interface for the required pin, and passed an instance of that class to the SetNotify method:

bufferPassCallbackInterface = new IAMWMBufferPassCallbackImpl(this);

IAMWMBufferPass bPass = (IAMWMBufferPass)DSHelper.GetPin(pWMASFWriter, "Video Input 01");

hr = bPass.SetNotify(bufferPassCallbackInterface);
DsError.ThrowExceptionForHR(hr);  

No exception is thrown, indicating that the SetNotify method succeeded.

Now, the problem is, that the Notify method in my callback object never gets called. The video records without a problem, except for the fact that the callback is not getting executed at all.

Is it a problem with the way I am doing the interop?

A: 

I usually work with delegates for function pointers in unsafe C# code.
The only trick is that you need to make sure that the reference to the delegate does not get garbage-collected, as the reference in the unsafe code is not used when counting the references inside the garbage collector. Thus the function either needs to be either static or the object which holds the reference needs an additional artificial reference to ensure its availability.
This is a tutorial for using delegates on MSDN.

weismat
bufferPassCallbackInterface is a class variable in the code I provided, so it shouldn't get garbage collected. Thanks for the suggestion though.
Kazar
A: 

The way you are doing the interop looks totally fine. I looked at the MSDN docs and it also looks like you are going the right direction, so I don't know why it wouldn't make the callback.

Once you do get it doing the callback, however, you might want to make sure to call Marshal.ReleaseComObject(pNSSBuffer3). Sometimes sample buffers aren't released for processing until all other references have been removed. This is the case with the SampleGrabber, so might be the same for this.

Jeremiah Morrill
A: 

You should probably be using a delegate:

delegate int NotifyDelegate(INSSBuffer3 pNSSBuffer3, IPin pPin, long prtStart, long prtEnd);

void run(){
  bufferPassCallbackInterface = new IAMWMBufferPassCallbackImpl(this);

  IAMWMBufferPass bPass = (IAMWMBufferPass)DSHelper.GetPin(pWMASFWriter, "Video Input 01");
  NotifyDelegate d = new NotifyDelegate(bufferPassCallbackInterface.Notify);

  hr = bPass.SetNotify(d);
  DsError.ThrowExceptionForHR(hr);  
}

However, I'm not quite sure how your implementing an unmanaged interface on a managed class. An INSSBuffer3* in C++ does not directly correlate to new INNSBuffer3() in C#.

Edit:

In response to your comment, the SetNotify function is defined as

HRESULT SetNotify(
  [in]  IAMWMBufferPassCallback *pCallback
);

So, it requires a pointer to an implementation of IAMWMBufferPassCallback.

MSDN says that pCallback is pCallback a

Pointer to the application's IAMWMBufferPassCallback interface.

The delegate is (an attempt to be) the managed equivalent to a pointer to IAMWMBufferPassCallback. A call back is, generally, a method, not an object. So, passing in an instance of the delegate should work for you. Another similar example is with InternetSetStatusCallback, where you pass a pointer to an implementation of InternetStatusCallback.

I would try it, if it doesn't work, why not try a library where someone has already done all the hard work? DirectShowNet may be an option (I've never used it).

scottm
How can this work if the SetNotify function requires an implementation of IAMWMBufferPassCallback, and I pass it a function pointer (which I believe is how the interop layer handles delegates)?
Kazar