如何使用 directshowlib-2005 从视频中提取帧?

How can i extract frames from video using directshowlib-2005?

使用此代码,我可以播放硬盘中的视频文件并在 pictureBox1 中显示视频。但我想知道如何将视频的所有帧保存到硬盘上的图像文件中?在播放视频或不播放视频时,我需要以某种方式提取帧并保存它们。

这是我目前使用的代码:

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {

        IGraphBuilder m_graphBuilder = null;
        IMediaControl m_mediaCtrl = null;
        IMediaEventEx m_mediaEvt = null;
        IMediaPosition m_mediaPos = null;
        IMediaSeeking m_mediaSeeking = null;

        public Form1()
        {
            InitializeComponent();
        }

        void InitInterfaces()
        {
            try
            {
                m_graphBuilder = (IGraphBuilder)new FilterGraph();
                m_mediaCtrl = (IMediaControl)m_graphBuilder;
                m_mediaEvt = (IMediaEventEx)m_graphBuilder;
                m_mediaPos = (IMediaPosition)m_graphBuilder;
                m_mediaSeeking = (IMediaSeeking)m_graphBuilder;
            }
            catch (Exception)
            {
                MessageBox.Show("Couldn't start directshow graph");
            }
        }


        void CloseInterfaces()
        {
            if (m_mediaCtrl != null)
            {
                m_mediaCtrl.StopWhenReady();
            }
            m_mediaCtrl = null;
            m_mediaEvt = null;
            m_mediaPos = null;

            m_mediaSeeking = null;

            if (m_graphBuilder != null)
                Marshal.ReleaseComObject(this.m_graphBuilder);
            m_graphBuilder = null;
        }   


        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void SetuupVideoRenderer()
        {
            IBaseFilter vmrFilter = null;

            vmrFilter = (IBaseFilter)new VideoMixingRenderer();
            m_graphBuilder.AddFilter(vmrFilter, "Video Renderer");

            IVMRFilterConfig FilterConfig = (IVMRFilterConfig)vmrFilter;
            FilterConfig.SetRenderingMode(VMRMode.Windowless);

            IVMRWindowlessControl windowlessCtrl = (IVMRWindowlessControl)vmrFilter;
            windowlessCtrl.SetVideoClippingWindow(this.pictureBox1.Handle);
            windowlessCtrl.SetVideoPosition(null, DsRect.FromRectangle(pictureBox1.ClientRectangle));
            windowlessCtrl.SetAspectRatioMode(VMRAspectRatioMode.LetterBox);

        }
        private void buttonLoad_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "All Files (*.*)|*.*|mp4 (*.mp4)|*.mp4|mov (*.mov)|*.mov||";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                CloseInterfaces();
                InitInterfaces();

                SetuupVideoRenderer();
                m_graphBuilder.RenderFile(openFileDialog1.FileName, null);

                textBoxDur.Text = ( getDuration() * 0.0000001).ToString();
                m_mediaCtrl.Run();

                timer1.Enabled = true;

            }
        }

        private void GetPosition(out long CurrentPos,out long StopPos)
        {

            m_mediaSeeking.GetPositions(out CurrentPos, out StopPos);

        }
        private long getDuration()
        {
            long duration;
            m_mediaSeeking.GetDuration(out duration);

            return duration;

        }

        private void SetPos(double fPos)
        {
             DsLong startPosition = (DsLong)(10000000 * fPos); 

            m_mediaSeeking.SetPositions(startPosition, AMSeekingSeekingFlags.AbsolutePositioning, null, AMSeekingSeekingFlags.NoPositioning);


        }
        private void buttonPause_Click(object sender, EventArgs e)
        {
            m_mediaCtrl.Pause();
        }

        private void buttonPlay_Click(object sender, EventArgs e)
        {
            m_mediaCtrl.Run();

        }
        private void OnVideoCompleted()
        {
            MessageBox.Show("Video Playback Completed");
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            long iCurPos, iStopPos;

            GetPosition(out iCurPos, out iStopPos);

            if (iCurPos >= iStopPos)
            {
                timer1.Enabled = false;
                OnVideoCompleted();
                return;
            }
            textBoxCurPos.Text = (iCurPos * 0.0000001 ).ToString();


        }

        private void buttonGo_Click(object sender, EventArgs e)
        {
            SetPos(Convert.ToDouble(textBoxNewPos.Text));
            timer1.Enabled = true;
        }
    }
}

我想这正是您要找的:

也看看这个 SO question 和这个网页上提供的链接。

The easiest way to do it is indeed using an FFMPEG, since its alredy includes some of the most common codecs (if you dont mind extra 30+Mb added to your app). As for wrappers, i used AForge wrapper in the past and really liked it, because of how simple it is to work with. Here is an example from its docs:

// create instance of video reader
VideoFileReader reader = new VideoFileReader();

// open video file
reader.Open( "test.avi");

// read 100 video frames out of it
for ( int i = 0; i < 100; i++)
{
    Bitmap videoFrame = reader.ReadVideoFrame();

    videoFrame.Save(i + ".bmp")

    // dispose the frame when it is no longer required
    videoFrame.Dispose( );
}

reader.Close();