lipsync/trunk/LipSync/IPlugin/AviReader.cs

484 lines
18 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
namespace Plugin {
public class Avi {
public const int StreamtypeVIDEO = 1935960438; //mmioStringToFOURCC("vids", 0)
public const int OF_SHARE_DENY_WRITE = 32;
public const int BMP_MAGIC_COOKIE = 19778; //ascii string "BM"
#region structure declarations
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct RECT {
public UInt32 left;
public UInt32 top;
public UInt32 right;
public UInt32 bottom;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct BITMAPINFOHEADER {
public UInt32 biSize;
public Int32 biWidth;
public Int32 biHeight;
public Int16 biPlanes;
public Int16 biBitCount;
public UInt32 biCompression;
public UInt32 biSizeImage;
public Int32 biXPelsPerMeter;
public Int32 biYPelsPerMeter;
public UInt32 biClrUsed;
public UInt32 biClrImportant;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct AVISTREAMINFO {
public UInt32 fccType;
public UInt32 fccHandler;
public UInt32 dwFlags;
public UInt32 dwCaps;
public UInt16 wPriority;
public UInt16 wLanguage;
public UInt32 dwScale;
public UInt32 dwRate;
public UInt32 dwStart;
public UInt32 dwLength;
public UInt32 dwInitialFrames;
public UInt32 dwSuggestedBufferSize;
public UInt32 dwQuality;
public UInt32 dwSampleSize;
public RECT rcFrame;
public UInt32 dwEditCount;
public UInt32 dwFormatChangeCount;
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 64 )]
public UInt16[] szName;
}
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
public struct BITMAPFILEHEADER {
public Int16 bfType; //"magic cookie" - must be "BM"
public Int32 bfSize;
public Int16 bfReserved1;
public Int16 bfReserved2;
public Int32 bfOffBits;
}
#endregion structure declarations
#region method declarations
//Initialize the AVI library
[DllImport( "avifil32.dll" )]
public static extern void AVIFileInit();
//Open an AVI file
[DllImport( "avifil32.dll", PreserveSig = true )]
public static extern int AVIFileOpen(
ref int ppfile,
String szFile,
int uMode,
int pclsidHandler );
//Get a stream from an open AVI file
[DllImport( "avifil32.dll" )]
public static extern int AVIFileGetStream(
int pfile,
out IntPtr ppavi,
int fccType,
int lParam );
//Get the start position of a stream
[DllImport( "avifil32.dll", PreserveSig = true )]
public static extern int AVIStreamStart( int pavi );
//Get the length of a stream in frames
[DllImport( "avifil32.dll", PreserveSig = true )]
public static extern int AVIStreamLength( int pavi );
//Get information about an open stream
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamInfo(
int pAVIStream,
ref AVISTREAMINFO psi,
int lSize );
//Get a pointer to a GETFRAME object (returns 0 on error)
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamGetFrameOpen(
IntPtr pAVIStream,
ref BITMAPINFOHEADER bih );
/*[DllImport("avifil32.dll")]
public static extern int AVIStreamGetFrameOpen(
IntPtr pAVIStream,
int dummy);*/
//Get a pointer to a packed DIB (returns 0 on error)
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamGetFrame(
int pGetFrameObj,
int lPos );
//Create a new stream in an open AVI file
[DllImport( "avifil32.dll" )]
public static extern int AVIFileCreateStream(
int pfile,
out IntPtr ppavi,
ref AVISTREAMINFO ptr_streaminfo );
//Set the format for a new stream
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamSetFormat(
IntPtr aviStream, Int32 lPos,
ref BITMAPINFOHEADER lpFormat, Int32 cbFormat );
//Write a sample to a stream
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamWrite(
IntPtr aviStream, Int32 lStart, Int32 lSamples,
IntPtr lpBuffer, Int32 cbBuffer, Int32 dwFlags,
Int32 dummy1, Int32 dummy2 );
//Release the GETFRAME object
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamGetFrameClose(
int pGetFrameObj );
//Release an open AVI stream
[DllImport( "avifil32.dll" )]
public static extern int AVIStreamRelease( IntPtr aviStream );
//Release an open AVI file
[DllImport( "avifil32.dll" )]
public static extern int AVIFileRelease( int pfile );
//Close the AVI library
[DllImport( "avifil32.dll" )]
public static extern void AVIFileExit();
#endregion methos declarations
#region other useful avi functions
//public const int StreamtypeAUDIO = 1935963489; //mmioStringToFOURCC("auds", 0)
//public const int StreamtypeMIDI = 1935960429; //mmioStringToFOURCC("mids", 0)
//public const int StreamtypeTEXT = 1937012852; //mmioStringToFOURCC("txts", 0)
/*[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct AVIFILEINFO{
public Int32 dwMaxBytesPerSecond;
public Int32 dwFlags;
public Int32 dwCaps;
public Int32 dwStreams;
public Int32 dwSuggestedBufferSize;
public Int32 dwWidth;
public Int32 dwHeight;
public Int32 dwScale;
public Int32 dwRate;
public Int32 dwLength;
public Int32 dwEditCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=64)]
public char[] szFileType;
}*/
/*[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct AVICOMPRESSOPTIONS {
public UInt32 fccType;
public UInt32 fccHandler;
public UInt32 dwKeyFrameEvery; // only used with AVICOMRPESSF_KEYFRAMES
public UInt32 dwQuality;
public UInt32 dwBytesPerSecond; // only used with AVICOMPRESSF_DATARATE
public UInt32 dwFlags;
public IntPtr lpFormat;
public UInt32 cbFormat;
public IntPtr lpParms;
public UInt32 cbParms;
public UInt32 dwInterleaveEvery;
}*/
/*[DllImport("avifil32.dll")]
public static extern int AVIMakeCompressedStream(
out IntPtr ppsCompressed, IntPtr aviStream,
ref AVICOMPRESSOPTIONS ao, int dummy);*/
/*[DllImport("avifil32.dll")]
public static extern int AVISaveOptions(
IntPtr hWnd,
int uiFlags,
int nStreams,
ref IntPtr ppavi,
ref IntPtr ppOptions);*/
/*[DllImport("avifil32.dll")]
public static extern int AVIFileInfo(
int pfile,
ref AVIFILEINFO pfi,
int lSize);*/
/*[DllImport("winmm.dll", EntryPoint="mmioStringToFOURCCA")]
public static extern int mmioStringToFOURCC(String sz, int uFlags);*/
/*[DllImport("avifil32.dll")]
public static extern int AVIStreamRead(
IntPtr pavi,
Int32 lStart,
Int32 lSamples,
IntPtr lpBuffer,
Int32 cbBuffer,
Int32 plBytes,
Int32 plSamples
);*/
#endregion other useful avi functions
}
/// <summary>Extract bitmaps from AVI files</summary>
public class AviReader {
//position of the first frame, count of frames in the stream
private int firstFrame = 0, countFrames = 0;
//pointers
private int aviFile = 0;
private int getFrameObject;
private IntPtr aviStream;
//stream and header info
private Avi.AVISTREAMINFO streamInfo;
public int CountFrames {
get {
return countFrames;
}
}
public UInt32 FrameRate {
get {
return streamInfo.dwRate / streamInfo.dwScale;
}
}
public Size BitmapSize {
get {
return new Size( (int)streamInfo.rcFrame.right, (int)streamInfo.rcFrame.bottom );
}
}
/// <summary>Opens an AVI file and creates a GetFrame object</summary>
/// <param name="fileName">Name of the AVI file</param>
public void Open( string fileName ) {
//Intitialize AVI library
Avi.AVIFileInit();
//Open the file
int result = Avi.AVIFileOpen(
ref aviFile, fileName,
Avi.OF_SHARE_DENY_WRITE, 0 );
if ( result != 0 ) {
throw new Exception( "Exception in AVIFileOpen: " + result.ToString() );
}
//Get the video stream
result = Avi.AVIFileGetStream(
aviFile,
out aviStream,
Avi.StreamtypeVIDEO, 0 );
if ( result != 0 ) {
throw new Exception( "Exception in AVIFileGetStream: " + result.ToString() );
}
firstFrame = Avi.AVIStreamStart( aviStream.ToInt32() );
countFrames = Avi.AVIStreamLength( aviStream.ToInt32() );
streamInfo = new Avi.AVISTREAMINFO();
result = Avi.AVIStreamInfo( aviStream.ToInt32(), ref streamInfo, Marshal.SizeOf( streamInfo ) );
if ( result != 0 ) {
throw new Exception( "Exception in AVIStreamInfo: " + result.ToString() );
}
//Open frames
Avi.BITMAPINFOHEADER bih = new Avi.BITMAPINFOHEADER();
bih.biBitCount = 24;
bih.biClrImportant = 0;
bih.biClrUsed = 0;
bih.biCompression = 0; //BI_RGB;
bih.biHeight = (Int32)streamInfo.rcFrame.bottom;
bih.biWidth = (Int32)streamInfo.rcFrame.right;
bih.biPlanes = 1;
bih.biSize = (UInt32)Marshal.SizeOf( bih );
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
getFrameObject = Avi.AVIStreamGetFrameOpen( aviStream, ref bih ); //force function to return 24bit DIBS
//getFrameObject = Avi.AVIStreamGetFrameOpen(aviStream, 0); //return any bitmaps
if ( getFrameObject == 0 ) {
throw new Exception( "Exception in AVIStreamGetFrameOpen!" );
}
}
/// <summary>Closes all streams, files and libraries</summary>
public void Close() {
if ( getFrameObject != 0 ) {
Avi.AVIStreamGetFrameClose( getFrameObject );
getFrameObject = 0;
}
if ( aviStream != IntPtr.Zero ) {
Avi.AVIStreamRelease( aviStream );
aviStream = IntPtr.Zero;
}
if ( aviFile != 0 ) {
Avi.AVIFileRelease( aviFile );
aviFile = 0;
}
Avi.AVIFileExit();
}
public Bitmap Export( int position ) {
if ( position > countFrames ) {
throw new Exception( "Invalid frame position" );
}
//Decompress the frame and return a pointer to the DIB
int pDib = Avi.AVIStreamGetFrame( getFrameObject, firstFrame + position );
//Copy the bitmap header into a managed struct
Avi.BITMAPINFOHEADER bih = new Avi.BITMAPINFOHEADER();
bih = (Avi.BITMAPINFOHEADER)Marshal.PtrToStructure( new IntPtr( pDib ), bih.GetType() );
/*if(bih.biBitCount < 24){
throw new Exception("Not enough colors! DIB color depth is less than 24 bit.");
}else */
if ( bih.biSizeImage < 1 ) {
throw new Exception( "Exception in AVIStreamGetFrame: Not bitmap decompressed." );
}
//Copy the image
byte[] bitmapData = new byte[bih.biSizeImage];
int address = pDib + Marshal.SizeOf( bih );
for ( int offset = 0; offset < bitmapData.Length; offset++ ) {
bitmapData[offset] = Marshal.ReadByte( new IntPtr( address ) );
address++;
}
//Copy bitmap info
byte[] bitmapInfo = new byte[Marshal.SizeOf( bih )];
IntPtr ptr;
ptr = Marshal.AllocHGlobal( bitmapInfo.Length );
Marshal.StructureToPtr( bih, ptr, false );
address = ptr.ToInt32();
for ( int offset = 0; offset < bitmapInfo.Length; offset++ ) {
bitmapInfo[offset] = Marshal.ReadByte( new IntPtr( address ) );
address++;
}
//Create file header
Avi.BITMAPFILEHEADER bfh = new Avi.BITMAPFILEHEADER();
bfh.bfType = Avi.BMP_MAGIC_COOKIE;
bfh.bfSize = (Int32)(55 + bih.biSizeImage); //size of file as written to disk
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = Marshal.SizeOf( bih ) + Marshal.SizeOf( bfh );
//Create or overwrite the destination file
MemoryStream fs = new MemoryStream();
//FileStream fs = new FileStream( dstFileName, System.IO.FileMode.Create );
BinaryWriter bw = new BinaryWriter( fs );
//Write header
bw.Write( bfh.bfType );
bw.Write( bfh.bfSize );
bw.Write( bfh.bfReserved1 );
bw.Write( bfh.bfReserved2 );
bw.Write( bfh.bfOffBits );
//Write bitmap info
bw.Write( bitmapInfo );
//Write bitmap data
bw.Write( bitmapData );
bw.Close();
// fs.Close();
fs.Flush();
fs.Seek( 0, SeekOrigin.Begin );
Bitmap bmp = new Bitmap( fs );
fs.Close();
return bmp;
}
/// <summary>Exports a frame into a bitmap file</summary>
/// <param name="position">Position of the frame</param>
/// <param name="dstFileName">Name ofthe file to store the bitmap</param>
public void ExportBitmap( int position, String dstFileName ) {
if ( position > countFrames ) {
throw new Exception( "Invalid frame position" );
}
//Decompress the frame and return a pointer to the DIB
int pDib = Avi.AVIStreamGetFrame( getFrameObject, firstFrame + position );
//Copy the bitmap header into a managed struct
Avi.BITMAPINFOHEADER bih = new Avi.BITMAPINFOHEADER();
bih = (Avi.BITMAPINFOHEADER)Marshal.PtrToStructure( new IntPtr( pDib ), bih.GetType() );
/*if(bih.biBitCount < 24){
throw new Exception("Not enough colors! DIB color depth is less than 24 bit.");
}else */
if ( bih.biSizeImage < 1 ) {
throw new Exception( "Exception in AVIStreamGetFrame: Not bitmap decompressed." );
}
//Copy the image
byte[] bitmapData = new byte[bih.biSizeImage];
int address = pDib + Marshal.SizeOf( bih );
for ( int offset = 0; offset < bitmapData.Length; offset++ ) {
bitmapData[offset] = Marshal.ReadByte( new IntPtr( address ) );
address++;
}
//Copy bitmap info
byte[] bitmapInfo = new byte[Marshal.SizeOf( bih )];
IntPtr ptr;
ptr = Marshal.AllocHGlobal( bitmapInfo.Length );
Marshal.StructureToPtr( bih, ptr, false );
address = ptr.ToInt32();
for ( int offset = 0; offset < bitmapInfo.Length; offset++ ) {
bitmapInfo[offset] = Marshal.ReadByte( new IntPtr( address ) );
address++;
}
//Create file header
Avi.BITMAPFILEHEADER bfh = new Avi.BITMAPFILEHEADER();
bfh.bfType = Avi.BMP_MAGIC_COOKIE;
bfh.bfSize = (Int32)(55 + bih.biSizeImage); //size of file as written to disk
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = Marshal.SizeOf( bih ) + Marshal.SizeOf( bfh );
//Create or overwrite the destination file
FileStream fs = new FileStream( dstFileName, System.IO.FileMode.Create );
BinaryWriter bw = new BinaryWriter( fs );
//Write header
bw.Write( bfh.bfType );
bw.Write( bfh.bfSize );
bw.Write( bfh.bfReserved1 );
bw.Write( bfh.bfReserved2 );
bw.Write( bfh.bfOffBits );
//Write bitmap info
bw.Write( bitmapInfo );
//Write bitmap data
bw.Write( bitmapData );
bw.Close();
fs.Close();
}
}
}