namespace CamAlarm
{
#region Directives
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endregion
public class AviReader : IDisposable
{
#region Enums
[Flags]
private enum OpenFileMode
{
Read = 0x00000000,
Write = 0x00000001,
ReadWrite = 0x00000002,
ShareCompat = 0x00000000,
ShareExclusive = 0x00000010,
ShareDenyWrite = 0x00000020,
ShareDenyRead = 0x00000030,
ShareDenyNone = 0x00000040,
Parse = 0x00000100,
Delete = 0x00000200,
Verify = 0x00000400,
Cancel = 0x00000800,
Create = 0x00001000,
Prompt = 0x00002000,
Exist = 0x00004000,
Reopen = 0x00008000
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct RECT
{
[MarshalAs(UnmanagedType.I4)]
public int left;
[MarshalAs(UnmanagedType.I4)]
public int top;
[MarshalAs(UnmanagedType.I4)]
public int right;
[MarshalAs(UnmanagedType.I4)]
public int bottom;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
private struct AVISTREAMINFO
{
[MarshalAs(UnmanagedType.I4)]
public int fccType;
[MarshalAs(UnmanagedType.I4)]
public int fccHandler;
[MarshalAs(UnmanagedType.I4)]
public int dwFlags;
[MarshalAs(UnmanagedType.I4)]
public int dwCaps;
[MarshalAs(UnmanagedType.I2)]
public short wPriority;
[MarshalAs(UnmanagedType.I2)]
public short wLanguage;
[MarshalAs(UnmanagedType.I4)]
public int dwScale;
[MarshalAs(UnmanagedType.I4)]
public int dwRate; // dwRate / dwScale == samples/second
[MarshalAs(UnmanagedType.I4)]
public int dwStart;
[MarshalAs(UnmanagedType.I4)]
public int dwLength;
[MarshalAs(UnmanagedType.I4)]
public int dwInitialFrames;
[MarshalAs(UnmanagedType.I4)]
public int dwSuggestedBufferSize;
[MarshalAs(UnmanagedType.I4)]
public int dwQuality;
[MarshalAs(UnmanagedType.I4)]
public int dwSampleSize;
[MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
public RECT rcFrame;
[MarshalAs(UnmanagedType.I4)]
public int dwEditCount;
[MarshalAs(UnmanagedType.I4)]
public int dwFormatChangeCount;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szName;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct BITMAPINFOHEADER
{
[MarshalAs(UnmanagedType.I4)]
public int biSize;
[MarshalAs(UnmanagedType.I4)]
public int biWidth;
[MarshalAs(UnmanagedType.I4)]
public int biHeight;
[MarshalAs(UnmanagedType.I2)]
public short biPlanes;
[MarshalAs(UnmanagedType.I2)]
public short biBitCount;
[MarshalAs(UnmanagedType.I4)]
public int biCompression;
[MarshalAs(UnmanagedType.I4)]
public int biSizeImage;
[MarshalAs(UnmanagedType.I4)]
public int biXPelsPerMeter;
[MarshalAs(UnmanagedType.I4)]
public int biYPelsPerMeter;
[MarshalAs(UnmanagedType.I4)]
public int biClrUsed;
[MarshalAs(UnmanagedType.I4)]
public int biClrImportant;
}
#endregion
#region API
[DllImport("avifil32.dll")]
private static extern void AVIFileInit();
[DllImport("avifil32.dll")]
private static extern void AVIFileExit();
[DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
private static extern int AVIFileOpen(out IntPtr ppfile, String szFile, OpenFileMode mode, IntPtr pclsidHandler);
[DllImport("avifil32.dll")]
private static extern int AVIFileGetStream(IntPtr pfile, out IntPtr ppavi, int fccType, int lParam);
[DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
private static extern int AVIStreamInfo(IntPtr pavi, ref AVISTREAMINFO psi,int lSize);
[DllImport("avifil32.dll")]
private static extern IntPtr AVIStreamGetFrameOpen(IntPtr pavi, ref BITMAPINFOHEADER lpbiWanted);
[DllImport("avifil32.dll")]
private static extern IntPtr AVIStreamGetFrameOpen(IntPtr pavi, int lpbiWanted);
[DllImport("avifil32.dll")]
private static extern int AVIStreamGetFrameClose(IntPtr pget);
[DllImport("avifil32.dll")]
private static extern int AVIStreamRelease(IntPtr pavi);
[DllImport("avifil32.dll")]
private static extern int AVIFileRelease(IntPtr pfile);
[DllImport("avifil32.dll")]
private static extern IntPtr AVIStreamGetFrame(IntPtr pget, int lPos);
[DllImport("ntdll.dll")]
private static extern IntPtr memcpy(IntPtr dst, IntPtr src, int count);
[DllImport("ntdll.dll")]
private static extern int memcpy(int dst, int src, int count);
#endregion
#region Fields
private IntPtr _hFile;
private IntPtr _pStream;
private IntPtr _pFrame;
private int _iFrameWidth;
private int _iFrameHeight;
private int _iPosition;
private int _iStart;
private int _iLength;
private float _fRate;
private string _sCodec;
#endregion
#region Constructor
public AviReader()
{
AVIFileInit();
}
~AviReader()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
//
}
Close();
AVIFileExit();
}
#endregion
#region Properties
/// <summary>Width</summary>
public int Width
{
get { return _iFrameWidth; }
}
/// <summary>Height</summary>
public int Height
{
get { return _iFrameHeight; }
}
/// <summary>FramesRate</summary>
public float FrameRate
{
get { return _fRate; }
}
/// <summary>CurrentPosition</summary>
public int CurrentPosition
{
get { return _iPosition; }
set
{
if ((value < _iStart) || (value >= _iStart + _iLength))
_iPosition = _iStart;
else
_iPosition = value;
}
}
/// <summary>Length</summary>
public int Length
{
get { return _iLength; }
}
/// <summary>Codec</summary>
public string Codec
{
get { return _sCodec; }
}
#endregion
#region Methods
private static int mmioFOURCC(string str)
{
return (
((int)(byte)(str[0])) |
((int)(byte)(str[1]) << 8) |
((int)(byte)(str[2]) << 16) |
((int)(byte)(str[3]) << 24));
}
private static string decode_mmioFOURCC(int code)
{
char[] chs = new char[4];
for (int i = 0; i < 4; i++)
{
chs[i] = (char)(byte)((code >> (i << 3)) & 0xFF);
if (!char.IsLetterOrDigit(chs[i]))
chs[i] = ' ';
}
return new string(chs);
}
public bool Open(string fname)
{
// close previous file
Close();
// open file
if (AVIFileOpen(out _hFile, fname, OpenFileMode.ShareDenyWrite, IntPtr.Zero) != 0)
throw new ApplicationException("Failed opening file");
// get first video stream
if (AVIFileGetStream(_hFile, out _pStream, mmioFOURCC("vids"), 0) != 0)
throw new ApplicationException("Failed getting video stream");
// get stream info
AVISTREAMINFO info = new AVISTREAMINFO();
AVIStreamInfo(_pStream, ref info, Marshal.SizeOf(info));
_iFrameWidth = info.rcFrame.right;
_iFrameHeight = info.rcFrame.bottom;
_iPosition = info.dwStart;
_iStart = info.dwStart;
_iLength = info.dwLength;
_fRate = (float) info.dwRate / (float) info.dwScale;
_sCodec = decode_mmioFOURCC(info.fccHandler);
// prepare decompressor
BITMAPINFOHEADER bih = new BITMAPINFOHEADER();
bih.biSize = Marshal.SizeOf(bih.GetType());
bih.biWidth = _iFrameWidth;
bih.biHeight = _iFrameHeight;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0; // BI_RGB
// get frame open object
if ((_pFrame = AVIStreamGetFrameOpen(_pStream, ref bih)) == IntPtr.Zero)
{
bih.biHeight = -_iFrameHeight;
if ((_pFrame = AVIStreamGetFrameOpen(_pStream, ref bih)) == IntPtr.Zero)
return false;
}
return true;
}
// Close file
public void Close()
{
// release frame open object
if (_pFrame != IntPtr.Zero)
{
AVIStreamGetFrameClose(_pFrame);
_pFrame = IntPtr.Zero;
}
// release stream
if (_pStream != IntPtr.Zero)
{
AVIStreamRelease(_pStream);
_pStream = IntPtr.Zero;
}
// release file
if (_hFile != IntPtr.Zero)
{
AVIFileRelease(_hFile);
_hFile = IntPtr.Zero;
}
}
// Get next video frame
public Bitmap GetNextFrame()
{
// get frame at specified position
IntPtr pdib = AVIStreamGetFrame(_pFrame, _iPosition);
if (pdib == IntPtr.Zero)
throw new ApplicationException("Failed getting frame");
BITMAPINFOHEADER bih;
// copy BITMAPINFOHEADER from unmanaged memory
bih = (BITMAPINFOHEADER) Marshal.PtrToStructure(pdib, typeof(BITMAPINFOHEADER));
// create new bitmap
Bitmap bmp = new Bitmap(_iFrameWidth, _iFrameHeight, PixelFormat.Format24bppRgb);
// lock bitmap data
BitmapData bmData = bmp.LockBits(
new Rectangle(0, 0, _iFrameWidth, _iFrameHeight),
ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
// copy image data
int srcStride = bmData.Stride; // width * 3;
int dstStride = bmData.Stride;
// check image direction
if (bih.biHeight > 0)
{
// it`s a bottom-top image
int dst = bmData.Scan0.ToInt32() + dstStride * (_iFrameHeight - 1);
int src = pdib.ToInt32() + Marshal.SizeOf(typeof(BITMAPINFOHEADER));
for (int y = 0; y < _iFrameHeight; y++)
{
memcpy(dst, src, srcStride);
dst -= dstStride;
src += srcStride;
}
}
else
{
// it`s a top-bottom image
int dst = bmData.Scan0.ToInt32();
int src = pdib.ToInt32() + Marshal.SizeOf(typeof(BITMAPINFOHEADER));
if (srcStride != dstStride)
{
// copy line by line
for (int y = 0; y < _iFrameHeight; y++)
{
memcpy(dst, src, srcStride);
dst += dstStride;
src += srcStride;
}
}
else
{
// copy the whole image
memcpy(dst, src, srcStride * _iFrameHeight);
}
}
// unlock bitmap data
bmp.UnlockBits(bmData);
_iPosition++;
return bmp;
}
#endregion
}
}