Hello all, and congratulations to dslib developers for such a great library.
I have a c# class that is mostly a rip from an example of the lib, the one that searches for black frames in a video. I have added an event to the class that should be fired when ISampleGrabber.BufferCB is called, unfortunately each time I fire the event in there i get a nullreference exception ( the event is null ), however I can fire the event from other methods in the class like SetupGraph and it works just fine, the problem is when fired in BufferCB.
Does anyone knows why it's failling and/or how can i make it work?
I would prefer not to poll for the buffer at each frame but maybe is the only solution...
Please help!
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well, I have discovered one thing, The above mentioned class is registered for COM Interop, if I add an event in c++ code using com then firing the event in BufferCB fails, but firing from another method works. If the event is implemented in managed code it works at all times. The bad thing is I need to receive the event in c++ COM.
Still needing help!
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Threading issues in managed code can present challenges, and I'm not qualified to try to sort them out for you. However, let me give you a few thoughts:
1) In the SampleGrabberCB routine, you want to keep your processing to a minimum. You are holding up the graph.
2) c# has several ways of passing info out. Look at WaitHandle for instance. Or you could modify member variables and monitor those variables from a different thread.
3) Remember that when the callback routine exits, the contents of the parameters passed in may be changed by other filters downstream. If you want to do things with them, you will need to make a copy.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Snarfle helped me on this one as well some time back. You want to avoid raising an event directly in the callback because the listener will block the thread, and your graph will deadlock. I worked around this using a threaded timer. The timer goes off, and returns immediately. Raise your custom event from the timer.
There are certainly other methods as well, but this one is simple, and allows for the per-frame event you're looking for.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've included example code below for an ISampleGrabberCB implementation that fires an asynch delegate when it detects scene changes.
Ashley
====================================
/// <summary>
/// Represents the method that will handle scene-change events.
/// </summary>
/// <param name="sampleTime"></param>
public delegate void SceneChangeEventHandler(double sampleTime);
/// <summary>
///
/// </summary>
public class SceneDetector : ISampleGrabberCB
{
// todo: apparently when the videoWidth is not divisible by 4, padding bytes are added to the end // of the data for each row of pixels. we could throw these away// todo: experiment with alternate detection strategies. rather than taking the average difference. it might// be more effective to look for the proportion of sampled pixels that have a very high difference// todo: another way might be to sample entire pixels of the bitmap together, rather than just random rbg values.// todo: perhaps could implement all three strategies and go with the result of a two-way vote/// <summary>/// Occurs when a new scene is detected./// </summary>publiceventSceneChangeEventHandlerNewScene;privateISceneDetectionStrategydetectionStrategy;privateboolfirstFrame=true;/// <summary>/// Initializes a new instance of the <see cref="SceneDetector"/> class./// </summary>/// <param name="videoWidth">Width of the video.</param>/// <param name="videoHeight">Height of the video.</param>/// <param name="bitsPerPixel">The bits per pixel.</param>publicSceneDetector(intvideoWidth,intvideoHeight,intbitsPerPixel){Debug.WriteLine("videoWidth:"+videoWidth,GetType().Name);Debug.WriteLine("videoHeight:"+videoHeight,GetType().Name);Debug.WriteLine("bitsPerPixel:"+bitsPerPixel,GetType().Name);detectionStrategy=newAverageRGBDiffDetectionStrategy(videoWidth,videoHeight,bitsPerPixel);}publicISceneDetectionStrategyDetectionStrategy{get{returndetectionStrategy;}set{detectionStrategy=value;}}/// <summary>/// Implementation of ISampleGrabberCB./// </summary>intISampleGrabberCB.SampleCB(doublesampleTime,IMediaSamplepSample){IntPtrpBuffer;inthr=pSample.GetPointer(outpBuffer);Analyze(sampleTime,pBuffer,pSample.GetSize());Marshal.ReleaseComObject(pSample);return0;}/// <summary>/// Implementation of ISampleGrabberCB./// </summary>intISampleGrabberCB.BufferCB(doublesampleTime,IntPtrpBuffer,intbufferLen){Analyze(sampleTime,pBuffer,bufferLen);return0;}/// <summary>/// Analyzes media samples looking for scene changes./// </summary>/// <param name="sampleTime">The sample time, in seconds.</param>/// <param name="pBuffer">The pointer to the media sample data.</param>/// <param name="bufferLength">The buffer length.</param>/// <remarks>/// This method accepts media sample data provided via either of the <see cref="DirectShowLib.ISampleGrabberCB"/> callback methods./// </remarks>publicunsafevoidAnalyze(doublesampleTime,IntPtrpBuffer,intbufferLength){boolsceneChanged=detectionStrategy.SceneChanged(sampleTime,pBuffer,bufferLength);if(firstFrame||sceneChanged){OnNewScene(sampleTime);}firstFrame=false;}/// <summary>/// Called when a scene change is detected./// </summary>/// <param name="sampleTime">The sample time, in seconds.</param>protectedvoidOnNewScene(doublesampleTime){if(NewScene!=null){NewScene.BeginInvoke(sampleTime,ProcessedSceneChange,this);}}/// <summary>/// Handler to call EndInvoke() for the asynchronous invocations of NewScene event./// </summary>/// <param name="result"></param>privatevoidProcessedSceneChange(IAsyncResultresult){NewScene.EndInvoke(result);}
}
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks! The scene detection implementation is something I'm working on for my program, but I'll probably clean it up and post it before long as it seems generally useful.
Ashley
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello all, and congratulations to dslib developers for such a great library.
I have a c# class that is mostly a rip from an example of the lib, the one that searches for black frames in a video. I have added an event to the class that should be fired when ISampleGrabber.BufferCB is called, unfortunately each time I fire the event in there i get a nullreference exception ( the event is null ), however I can fire the event from other methods in the class like SetupGraph and it works just fine, the problem is when fired in BufferCB.
Does anyone knows why it's failling and/or how can i make it work?
I would prefer not to poll for the buffer at each frame but maybe is the only solution...
Please help!
Well, I have discovered one thing, The above mentioned class is registered for COM Interop, if I add an event in c++ code using com then firing the event in BufferCB fails, but firing from another method works. If the event is implemented in managed code it works at all times. The bad thing is I need to receive the event in c++ COM.
Still needing help!
Threading issues in managed code can present challenges, and I'm not qualified to try to sort them out for you. However, let me give you a few thoughts:
1) In the SampleGrabberCB routine, you want to keep your processing to a minimum. You are holding up the graph.
2) c# has several ways of passing info out. Look at WaitHandle for instance. Or you could modify member variables and monitor those variables from a different thread.
3) Remember that when the callback routine exits, the contents of the parameters passed in may be changed by other filters downstream. If you want to do things with them, you will need to make a copy.
Snarfle helped me on this one as well some time back. You want to avoid raising an event directly in the callback because the listener will block the thread, and your graph will deadlock. I worked around this using a threaded timer. The timer goes off, and returns immediately. Raise your custom event from the timer.
There are certainly other methods as well, but this one is simple, and allows for the per-frame event you're looking for.
You can also do this with an asynchronous delegate. Chris Sells walks you through an example of this here:
http://www.sellsbrothers.com/writing/default.aspx?content=delegates.htm
I've included example code below for an ISampleGrabberCB implementation that fires an asynch delegate when it detects scene changes.
Ashley
====================================
/// <summary>
/// Represents the method that will handle scene-change events.
/// </summary>
/// <param name="sampleTime"></param>
public delegate void SceneChangeEventHandler(double sampleTime);
/// <summary>
///
/// </summary>
public class SceneDetector : ISampleGrabberCB
{
}
Ashley, this is cool.
Is the scene change detection code itself shown anywhere, or is this something unique to your program?
Thanks! The scene detection implementation is something I'm working on for my program, but I'll probably clean it up and post it before long as it seems generally useful.
Ashley