using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using DirectShowLib;
using System.Windows.Forms;
using System.Collections.Generic;
namespace Polkan.DataSource
{
internal class WmvAdapter : ISampleGrabberCB, IDisposable
{
#region Fields
//public Image img;
private IFilterGraph2 _filterGraph;
private IMediaControl _mediaCtrl;
private IMediaEvent _mediaEvent;
private int _width;
private int _height;
private readonly string _outFolder;
private int _frameId;
#endregion
#region Constructors and Destructors
public WmvAdapter(string file, string outFolder)
{
_outFolder = outFolder;
try
{
SetupGraph(file);
}
catch
{
Dispose();
MessageBox.Show("A codec is required to load this video file. Please use http://www.headbands.com/gspot/ or search the web for the correct codec");
throw;
}
}
~WmvAdapter()
{
CloseInterfaces();
}
#endregion
public void Dispose()
{
CloseInterfaces();
}
public void Start()
{
int hr = _mediaCtrl.Run();
WaitUntilDone();
DsError.ThrowExceptionForHR(hr);
}
public void WaitUntilDone()
{
int hr;
const int eAbort = unchecked((int)0x80004004);
do
{
System.Windows.Forms.Application.DoEvents();
EventCode evCode;
hr = _mediaEvent.WaitForCompletion(100, out evCode);
} while
(hr == eAbort);
DsError.ThrowExceptionForHR(hr);
}
/// <summary> build the capture graph for grabber. </summary>
private void SetupGraph(string file)
{
ISampleGrabber sampGrabber = null;
IBaseFilter capFilter = null;
IBaseFilter nullrenderer = null;
_filterGraph = (IFilterGraph2)new FilterGraph();
_mediaCtrl = (IMediaControl)_filterGraph;
_mediaEvent = (IMediaEvent)_filterGraph;
var mediaFilt = (IMediaFilter)_filterGraph;
try
{
// Add the video source
int hr = _filterGraph.AddSourceFilter(file, "Ds.NET FileFilter", out capFilter);
DsError.ThrowExceptionForHR(hr);
// Get the SampleGrabber interface
sampGrabber = new SampleGrabber() as ISampleGrabber;
var baseGrabFlt = sampGrabber as IBaseFilter;
ConfigureSampleGrabber(sampGrabber);
// Add the frame grabber to the graph
hr = _filterGraph.AddFilter(baseGrabFlt, "Ds.NET Grabber");
DsError.ThrowExceptionForHR(hr);
// ---------------------------------
// Connect the file filter to the sample grabber
// Hopefully this will be the video pin, we could check by reading it's mediatype
IPin iPinOut = DsFindPin.ByDirection(capFilter, PinDirection.Output, 0);
// Get the input pin from the sample grabber
IPin iPinIn = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
hr = _filterGraph.Connect(iPinOut, iPinIn);
DsError.ThrowExceptionForHR(hr);
// Add the null renderer to the graph
nullrenderer = new NullRenderer() as IBaseFilter;
hr = _filterGraph.AddFilter(nullrenderer, "Null renderer");
DsError.ThrowExceptionForHR(hr);
// ---------------------------------
// Connect the sample grabber to the null renderer
iPinOut = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
iPinIn = DsFindPin.ByDirection(nullrenderer, PinDirection.Input, 0);
hr = _filterGraph.Connect(iPinOut, iPinIn);
DsError.ThrowExceptionForHR(hr);
// Turn off the clock. This causes the frames to be sent
// thru the graph as fast as possible
hr = mediaFilt.SetSyncSource(null);
DsError.ThrowExceptionForHR(hr);
// Read and cache the image sizes
SaveSizeInfo(sampGrabber);
}
finally
{
if (capFilter != null)
{
Marshal.ReleaseComObject(capFilter);
}
if (sampGrabber != null)
{
Marshal.ReleaseComObject(sampGrabber);
}
if (nullrenderer != null)
{
Marshal.ReleaseComObject(nullrenderer);
}
GC.Collect();
}
}
private void SaveSizeInfo(ISampleGrabber sampGrabber)
{
// Get the media type from the SampleGrabber
var media = new AMMediaType();
int hr = sampGrabber.GetConnectedMediaType(media);
DsError.ThrowExceptionForHR(hr);
if ((media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero))
{
throw new NotSupportedException("Unknown Grabber Media Format");
}
// Grab the size info
var videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(VideoInfoHeader));
_width = videoInfoHeader.BmiHeader.Width;
_height = videoInfoHeader.BmiHeader.Height;
DsUtils.FreeAMMediaType(media);
GC.Collect();
}
private void ConfigureSampleGrabber(ISampleGrabber sampGrabber)
{
var media = new AMMediaType
{
majorType = MediaType.Video,
subType = MediaSubType.RGB24,
formatType = FormatType.VideoInfo
};
int hr = sampGrabber.SetMediaType(media);
DsError.ThrowExceptionForHR(hr);
DsUtils.FreeAMMediaType(media);
GC.Collect();
hr = sampGrabber.SetCallback(this, 1);
DsError.ThrowExceptionForHR(hr);
}
private void CloseInterfaces()
{
try
{
if (_mediaCtrl != null)
{
_mediaCtrl.Stop();
_mediaCtrl = null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
if (_filterGraph != null)
{
Marshal.ReleaseComObject(_filterGraph);
_filterGraph = null;
}
GC.Collect();
}
int ISampleGrabberCB.SampleCB(double sampleTime, IMediaSample pSample)
{
Marshal.ReleaseComObject(pSample);
return 0;
}
//add a boolean property to indicate the save-mode
public bool SaveToDisc { get; set; }
//the list for the bitmaps
public List<Bitmap> Images { get; set; }
int ISampleGrabberCB.BufferCB(double sampleTime, IntPtr pBuffer, int bufferLen)
{
using (var bitmap = new Bitmap(_width, _height, _width * 3, PixelFormat.Format24bppRgb, pBuffer))
{
bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
if (SaveToDisc)
{
String tempFile = _outFolder + _frameId + ".bmp";
if (File.Exists(tempFile))
{
}
else
{
bitmap.Save(Path.Combine(_outFolder, _frameId.ToString("D6") + ".bmp"));
}
_frameId++;
}
else
{
if (Images == null)
Images = new List<Bitmap>();
Images.Add((Bitmap)bitmap.Clone());
}
}
return 0;
}
}
}
The class is exctracting frames from video file to the hard disk.
IN my case for example 47 files on hard disk.
In the bottom of the class im doing the saving to the hard disk:
bitmap.Save(Path.Combine(_outFolder, _frameId.ToString("D6") + ".bmp"));
And above in the class there a function called: WaitUntillDone() wich dosent help me.
What i want is that when its finishing to extract all the files to the hard disk it will give me a messagebox.show or just a message on a label or something say that " the process has done"
In Form1 button click event im using the class like this:
wmv = new Polkan.DataSource.WmvAdapter(@"d:\VIDEO0040.3gp", sf);
wmv.SaveToDisc = true;
wmv.Start();
The video name , sf is the directory where to extract on hard disk.
Then true so it will save it to the hard disk not ot memory the frames.
And start.
I can also do in Form1 in the button click event wmv.WaitUntillDone(); but it wont throw any message or something when done.
You need to use events for telling your UI component that the process has finished:
public event EventHandler ProcessFinished;
Inside your method, when it finishes let it raise the event:
if(ProcessFinished != null)
ProcessFinished(this, EventArgs.Empty);
Finally, in the class that is calling the process:
wmv = new Polkan.DataSource.WmvAdapter(@"d:\VIDEO0040.3gp", sf);
wmv.SaveToDisc = true;
wmv.ProcessFinished += OnProcessFinished;
wmv.Start();
Where OnProcessFinished will be something like:
public void OnProcessFinished(object sender, EventArgs e){
MessageBox.Show("done!");
}
Hope it helps.
You have implemented your own WaitUntil method that uses DoEvents(). This is tricky and hard to get right.
It is much simpler to move the relevant code to a Backgoundworker. And then just use the Completed event to signal the end.
WaitUntilDone
loops leaving your UI responsive until the whole file plays to completion, with all your calls aback for every video frame. You could add MessageBox.Show
to the bottom of WaitUntilDone
.
To avoid this looping without yielding control to outer message loop, the applications usually do it in a different way. They use IMediaEventEx.SetNotifyWindow
to subscribe to filter graph events and have a window message received when the playback is completed. That is, once the processing of the file is completed, you have a message sent to you and your handler has a chance to do what it has to do.
A perhaps simpler option for you here is to not do any WaitUntilDone
and instead have a timer to poll, for example once a second, with IMediaEvent.WaitForCompletion(0, ...
to see if the processing is done.
User contributions licensed under CC BY-SA 3.0