Menu

Why can't I fire an event inside BufferCB?

2007-02-15
2012-10-29
  • programator

    programator - 2007-02-15

    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!

     
    • programator

      programator - 2007-02-15

      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!

       
    • snarfle

      snarfle - 2007-02-15

      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.

       
    • Gordon

      Gordon - 2007-02-16

      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.

       
      • Ashley Tate

        Ashley Tate - 2007-02-16

        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
        {

        // 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
        
        /// &lt;summary&gt;
        /// Occurs when a new scene is detected.
        /// &lt;/summary&gt;
        public event SceneChangeEventHandler NewScene;
        
        private ISceneDetectionStrategy detectionStrategy;
        private bool firstFrame = true;
        
        /// &lt;summary&gt;
        /// Initializes a new instance of the &lt;see cref=&quot;SceneDetector&quot;/&gt; class.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;videoWidth&quot;&gt;Width of the video.&lt;/param&gt;
        /// &lt;param name=&quot;videoHeight&quot;&gt;Height of the video.&lt;/param&gt;
        /// &lt;param name=&quot;bitsPerPixel&quot;&gt;The bits per pixel.&lt;/param&gt;
        public SceneDetector(int videoWidth, int videoHeight, int bitsPerPixel)
        {
            Debug.WriteLine(&quot;videoWidth: &quot; + videoWidth, GetType().Name);
            Debug.WriteLine(&quot;videoHeight: &quot; + videoHeight, GetType().Name);
            Debug.WriteLine(&quot;bitsPerPixel: &quot; + bitsPerPixel, GetType().Name);
        
            detectionStrategy = new AverageRGBDiffDetectionStrategy(videoWidth, videoHeight, bitsPerPixel);
        }
        
        public ISceneDetectionStrategy DetectionStrategy
        {
            get { return detectionStrategy; }
            set { detectionStrategy = value; }
        }
        
        /// &lt;summary&gt;
        /// Implementation of ISampleGrabberCB.
        /// &lt;/summary&gt;
        int ISampleGrabberCB.SampleCB(double sampleTime, IMediaSample pSample)
        {
            IntPtr pBuffer;
            int hr = pSample.GetPointer(out pBuffer);
        
            Analyze(sampleTime, pBuffer, pSample.GetSize());
        
            Marshal.ReleaseComObject(pSample);
            return 0;
        }
        
        /// &lt;summary&gt;
        /// Implementation of ISampleGrabberCB.
        /// &lt;/summary&gt;
        int ISampleGrabberCB.BufferCB(double sampleTime, IntPtr pBuffer, int bufferLen)
        {
            Analyze(sampleTime, pBuffer, bufferLen);
            return 0;
        }
        
        /// &lt;summary&gt;
        /// Analyzes media samples looking for scene changes.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;sampleTime&quot;&gt;The sample time, in seconds.&lt;/param&gt;
        /// &lt;param name=&quot;pBuffer&quot;&gt;The pointer to the media sample data.&lt;/param&gt;
        /// &lt;param name=&quot;bufferLength&quot;&gt;The buffer length.&lt;/param&gt;
        /// &lt;remarks&gt;
        /// This method accepts media sample data provided via either of the &lt;see cref=&quot;DirectShowLib.ISampleGrabberCB&quot;/&gt; callback methods.
        /// &lt;/remarks&gt;
        public unsafe void Analyze(double sampleTime, IntPtr pBuffer, int bufferLength)
        {
        
            bool sceneChanged = detectionStrategy.SceneChanged(sampleTime, pBuffer, bufferLength);
        
            if (firstFrame || sceneChanged)
            {
                OnNewScene(sampleTime);
            }
        
            firstFrame = false;
        }
        
        /// &lt;summary&gt;
        /// Called when a scene change is detected.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;sampleTime&quot;&gt;The sample time, in seconds.&lt;/param&gt;
        protected void OnNewScene(double sampleTime)
        {
            if (NewScene != null)
            {
                NewScene.BeginInvoke(sampleTime, ProcessedSceneChange, this);
            }
        }
        
        /// &lt;summary&gt;
        /// Handler to call EndInvoke() for the asynchronous invocations of NewScene event.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;result&quot;&gt;&lt;/param&gt;
        private void ProcessedSceneChange(IAsyncResult result)
        {
            NewScene.EndInvoke(result);
        }
        

        }

         
    • Gordon

      Gordon - 2007-02-17

      Ashley, this is cool.

      Is the scene change detection code itself shown anywhere, or is this something unique to your program?

       
      • Ashley Tate

        Ashley Tate - 2007-02-19

        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

         

Log in to post a comment.