1.首先要引入DirectShowLib-2005.dll库
2.将工程的「Allow unsafe code」选上。
/// <summary>
/// Retrieves individual frames from a video file and returns them as bitmaps.
/// </summary>
public class FrameGrabber
{
// underlying variables for properties
private string fileName;
private int frameCount;
private double frameRate;
private int height;
// private variables
private IMediaDet mediaDet;
private double mediaLength;
private VideoInfoHeader videoInfo;
private int width;
/// <summary>
/// Creates a FrameGrabber based on the specified video file.
/// </summary>
public FrameGrabber(string fileName)
{
FileName = fileName;
}
/// <summary>
/// Creates a FrameGrabber with no video file set. Set FileName before calling other methods.
/// </summary>
public FrameGrabber()
: this("")
{
// nothing to do here
}
/// <summary>
/// Gets or sets the full path of the video file from which to grab frames.
/// </summary>
public string FileName
{
get
{
return fileName;
}
set
{
mediaDet = null;
fileName = value;
if (File.Exists(fileName))
{
AMMediaType mediaType = null;
try
{
mediaDet = (IMediaDet)new MediaDet();
DsError.ThrowExceptionForHR(mediaDet.put_Filename(fileName));
// find the video stream in the file
int index = 0;
Guid type = Guid.Empty;
while (type != MediaType.Video)
{
mediaDet.put_CurrentStream(index++);
mediaDet.get_StreamType(out type);
}
// retrieve some measurements from the video
mediaDet.get_FrameRate(out frameRate);
mediaType = new AMMediaType();
mediaDet.get_StreamMediaType(mediaType);
videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader));
DsUtils.FreeAMMediaType(mediaType);
mediaType = null;
width = videoInfo.BmiHeader.Width;
height = videoInfo.BmiHeader.Height;
mediaDet.get_StreamLength(out mediaLength);
frameCount = (int)(frameRate * mediaLength);
}
catch (Exception e)
{
if (mediaType != null)
{
DsUtils.FreeAMMediaType(mediaType);
}
fileName = "";
throw new ArgumentException(String.Format("unable to open the file \"{0}\", DirectShow reported the following error: {1}", value, e.Message));
}
}
}
}
/// <summary>
/// Gets the total number of frames in the video file.
/// </summary>
public int FrameCount
{
get
{
return frameCount;
}
}
/// <summary>
/// Gets the framerate of the video file. Some videos always report "0" regardless of their actual value.
/// </summary>
public double FrameRate
{
get
{
return frameRate;
}
}
/// <summary>
/// Gets the horizontal dimension of the video file in pixels.
/// </summary>
public int Width
{
get
{
return width;
}
}
/// <summary>
/// Gets the vertical dimension of the video file in pixels.
/// </summary>
public int Height
{
get
{
return height;
}
}
/// <summary>
/// Gets the duration of the video file in seconds.
/// </summary>
public double MediaLength
{
get
{
return mediaLength;
}
}
/// <summary>
/// Gets the bit depth of the captured frame (always 24 bpp RGB).
/// </summary>
public PixelFormat PixelFormat
{
get
{
return PixelFormat.Format24bppRgb;
}
}
/// <summary>
/// Gets the image at the specified time. Equivalent to calling "GetImageAtTime()".
/// </summary>
public Bitmap this[double seconds]
{
get
{
return GetImageAtTime(seconds);
}
}
/// <summary>
/// Gets the specified frame. Equivalent to calling "GetImage()".
/// </summary>
public Bitmap this[int frame]
{
get
{
return GetImage(frame);
}
}
/// <summary>
/// Converts the frame number to seconds.
/// </summary>
public double ConvertFrameNumberToSeconds(int frameNumber)
{
return (frameNumber / frameRate);
}
/// <summary>
/// Converts the seconds to frame number.
/// </summary>
public int ConvertSecondsToFrameNumber(double seconds)
{
return (int)Math.Floor(seconds * frameRate);
}
/// <summary>
/// Gets the image of the specified frame. Frames are indexed starting at 0 and go through (FrameCount - 1).
/// </summary>
public Bitmap GetImage(int frameNumber)
{
if (frameNumber < frameCount)
{
return GetImageAtTime(ConvertFrameNumberToSeconds(frameNumber));
}
else
{
throw new ArgumentException(String.Format("frameNumber must be between 0 and {0} inclusive, value was \"{1}\"", frameCount - 1, frameNumber));
}
}
/// <summary>
/// Gets the image at the specified time. Seconds must be less than or equal to MediaLength.
/// All other image and frame accessors call this method.
/// </summary>
public Bitmap GetImageAtTime(double seconds)
{
seconds += 1 / (frameRate * 2);
if (seconds <= mediaLength)
{
if (mediaDet != null)
{
IntPtr bufferPtr = IntPtr.Zero;
Bitmap returnValue = null;
try
{
// create a buffer to hold the image data from the MediaDet
int bufferSize;
mediaDet.GetBitmapBits(seconds, out bufferSize, IntPtr.Zero, width, height);
bufferPtr = Marshal.AllocHGlobal(bufferSize);
mediaDet.GetBitmapBits(seconds, out bufferSize, bufferPtr, width, height);
// compose a bitmap from the data in the managed buffer
unsafe
{
returnValue = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData imageData = returnValue.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int* imagePtr = (int*)imageData.Scan0;
int bitmapHeaderSize = Marshal.SizeOf(videoInfo.BmiHeader);
int* sourcePtr = (int*)((byte*)bufferPtr.ToPointer() + bitmapHeaderSize);
for (int i = 0; i < (bufferSize - bitmapHeaderSize) / 4; i++)
{
*imagePtr = *sourcePtr;
imagePtr++;
sourcePtr++;
}
returnValue.UnlockBits(imageData);
returnValue.RotateFlip(RotateFlipType.Rotate180FlipX); // DirectShow stores pixels in a different order than Bitmaps do
}
Marshal.FreeHGlobal(bufferPtr);
return returnValue;
}
catch
{
if (bufferPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(bufferPtr);
}
if (returnValue != null)
{
returnValue.Dispose();
}
throw;
}
}
else
{
throw new InvalidOperationException("cannot retrieve the frame because the FileName property has not been set yet");
}
}
else
{
throw new ArgumentException(String.Format("seconds must be between 0 and {0} inclusive, value was \"{1}\"", mediaLength, seconds));
}
}
}