save frames of mp4 file (The media does not have a video stream exception)

Aug 27, 2010 at 7:13 PM

First of all, great job on WPF Media Kit! The code is cleanly written and your library is easy to use.

I am trying to save thumbnails of a video at 5 second intervals. I am using the following code (which is very short thanks to your library!):

 

        private void SaveVideoFrame(string videoFilePath, System.TimeSpan position, string imageFilePath)
        {
            WPFMediaKit.DirectShow.MediaPlayers.MediaDetector detector = new MediaDetector();
            detector.LoadMedia(videoFilePath);
            var src = detector.GetImage(position);

            var encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(src));

            using (FileStream file = File.OpenWrite(imageFilePath))
            {
                encoder.Save(file);
            }
        }

 

It works flawlessly for .avi files but I get a "The media does not have a video stream" exception for mp4 files. Stepping through the code I found that get_OutputStreams returned 1. That 1 stream is an audio stream. I am able to play mp4 files through the MediaUriPlayer element without a problem.

Do you know what could be causing this problem? Also, is this the most efficient way to be extracting frames from a video file? Please excuse any dumb questions because I am new to video and DirectShow.

 

Thanks!

Aug 27, 2010 at 7:21 PM
Glad you like the library!  It's always fun working with multimedia.

I can only guess at this point what the mp4 issue is..The IMediaDet interface, internally in dshow will create the graph and insert the SampleGrabber filter.  This can sometimes be problematic because the SampleGrabber will not support VIDEOINFOHEADER2 media subtypes.  Is it possible your mp4s are using VIDEOINFOHEADER2?  You usually see VIDEOINFOHEADER2 with video that is interlaced or has copy protection...like DVD vob files. 

On Fri, Aug 27, 2010 at 11:13 AM, vendiddy <notifications@codeplex.com> wrote:

From: vendiddy

First of all, great job on WPF Media Kit! The code is cleanly written and your library is easy to use.

I am trying to save thumbnails of a video at 5 second intervals. I am using the following code (which is very short thanks to your library!):

 

        private void SaveVideoFrame(string videoFilePath, System.TimeSpan position, string imageFilePath)
        {
            WPFMediaKit.DirectShow.MediaPlayers.MediaDetector detector = new MediaDetector();
            detector.LoadMedia(videoFilePath);
            var src = detector.GetImage(position);

            var encoder = new JpegBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(src));

            using (FileStream file = File.OpenWrite(imageFilePath))
            {
                encoder.Save(file);
            }
        }

 

It works flawlessly for .avi files but I get a "The media does not have a video stream" exception for mp4 files. Stepping through the code I found that get_OutputStreams returned 1. That 1 stream is an audio stream. I am able to play mp4 files through the MediaUriPlayer element without a problem.

Do you know what could be causing this problem? Also, is this the most efficient way to be extracting frames from a video file? Please excuse any dumb questions because I am new to video and DirectShow.

 

Thanks!

Read the full discussion online.

To add a post to this discussion, reply to this email (WPFMediaKit@discussions.codeplex.com)

To start a new discussion for this project, email WPFMediaKit@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe or change your settings on codePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at codeplex.com




--
Microsoft MVP - Client Application Development
HJT, Inc Software Developer

Aug 27, 2010 at 7:52 PM

Thanks for the response. Just out of curiosity, do you know why the MediaUriPlayer is able to play the file?

Aug 27, 2010 at 7:56 PM
MediaUriElement/Player can play it because it does not use the SampleGrabber interface.  I use the VMR9 or EVR with a custom allocator/presenter and render to WPF using D3DImage.
Aug 27, 2010 at 11:56 PM

Is there a more universal way to capture a frame? I was thinking I would somehow instantiate a hidden MediaUriElement, seek to the position in the video, and save the rendered image to a disk. Is this possible?

If so, could you please point me in the right direction (classes, methods, etc, that would be helpful to me)?

Thanks!

Aug 28, 2010 at 12:06 AM
This is all just in theory...but here's a way I would try:

Modify the D3DRenderer class.  When you want to take a snapshot, with some library like SlimDX, wrap the surface (ie var surface = new Surface(IntPtr)).  Create a new system memory surface and copy the GPU surface to the system memory surface.  Once copied, save the surface to disk, memory, etc.  IIRC, there are D3D methods (and wrapped in SlimDX) to do this.

You may also look into RenderTargetBitmap which would be easier, but I've never tried RTB with a D3DImage.
Aug 28, 2010 at 11:31 AM

Hi,

it's easy to get a snapshot from the Renderer:

        /// <summary>
        /// Get the current image from renderer
        /// </summary>
        /// <returns></returns>
        public BitmapSource GetCurrentImage()
        {
            if (pBackBuffer == IntPtr.Zero) return null;

            int width = D3DImage.PixelWidth;
            int height = D3DImage.PixelHeight;

            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext dc = dv.RenderOpen())
                dc.DrawImage(D3DImage, new Rect(0, 0, width, height));

            RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            rtb.Render(dv);

            return BitmapFrame.Create(rtb);
        }

Aug 28, 2010 at 10:57 PM

Thanks, I'll try this out.

Aug 30, 2010 at 12:28 AM
Thanks for the help, I am able to extract frames from the D3DImage. I have run into an another obstacle. Basically, I am trying to seek to many different times within a video and capture a frame for each time. Seeking seems to occur asynchronously. If I seek to a position and I immediately capture a frame, I capture a frame before the seek has completed. Is there a way to wait until a seek has completed before capturing a frame? I noticed a MediaPositionChanged event but that is part of MediaUriPlayer, not MediaUriElement.
Aug 30, 2010 at 2:51 AM
Edited Aug 30, 2010 at 3:24 AM

I don't know if this is a good way to do this but modified the D3DRenderer class to raise an event in the InternalImageInvalidateVideoImage method:

            /* Only render the video image if possible, or if IsRenderingEnabled is true */
            if (D3DImage.IsFrontBufferAvailable && IsRenderingEnabled && m_pBackBuffer != IntPtr.Zero)
            {
                try
                {
                    /* Invalidate the entire image */
                    D3DImage.Lock();
                    D3DImage.AddDirtyRect(new Int32Rect(0, /* Left */
                                                        0, /* Top */
                                                        D3DImage.PixelWidth, /* Width */
                                                        D3DImage.PixelHeight /* Height */));
                    D3DImage.Unlock();
                }
                catch (Exception)
                { }

                if (ImageChanged != null)
                    ImageChanged(this, EventArgs.Empty);

            }

I pause the video, add a handler to ImageChanged, and seek to all of the times I want thumbnails for. In the ImageChanged event handler, I use code similar to SiggiK's to capture the frame. I don't perform a seek until the ImageChanged event has been fired and this prevents any synchronization issues.

 

Feb 18, 2013 at 10:09 AM
Edited Feb 18, 2013 at 10:13 AM
Hi all may i know what is the best way to get a thumbnail image from any video using this library? I tried using the MediaDetector but has problems with mp4 and mov files...