nataz
13-08-2007 08:44:44
Hi all,
during the development of one of my Projects i needed to Render to AVI. There was a Post about this, but 1st: the Files are not valid, 2nd: it was not multithreaded, so the Rendering of the Main app slowed incredibly down.
Here is my approach, please note, that i had just a few quick tests and cant guarantee anything, use at your own risk:
AviWriter.cs
Note: Some parts are taken from a Codeproject Article, and i do not claim that these are my own work...
Usage:
Get a RTT Target up and Running and do the Following:
Now do aw.AviAddFrame(bmp); each frame.
To end a AviFile propperly do:
Note that you, in order to get a Propper AVI File, have to stabilize your Framerate to the framerate used in the Constructor, else the AVI may become async etc.
Have fun with it... nataz
during the development of one of my Projects i needed to Render to AVI. There was a Post about this, but 1st: the Files are not valid, 2nd: it was not multithreaded, so the Rendering of the Main app slowed incredibly down.
Here is my approach, please note, that i had just a few quick tests and cant guarantee anything, use at your own risk:
AviWriter.cs
/* Author: Daniel Koppers
* Date : 13.09.2007
* Desc : A Class to Wrap the Win32 AVI Functions into C# and provide a reliable AVI Writer.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace VirtualStudio
{
class AviWrite
{
#region AVI STRUCTS
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct RECT
{
public UInt32 left;
public UInt32 top;
public UInt32 right;
public UInt32 bottom;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private 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)]
private 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)]
private struct BITMAPFILEHEADER
{
public Int16 bfType; //"magic cookie" - must be "BM"
public Int32 bfSize;
public Int16 bfReserved1;
public Int16 bfReserved2;
public Int32 bfOffBits;
}
#endregion
#region WIN32 NATIVE IMPORTS
// Lib init
[DllImport("avifil32.dll")]
private static extern void AVIFileInit();
//Open an AVI file
[DllImport("avifil32.dll", PreserveSig = true)]
private static extern int AVIFileOpen(
ref int ppfile,
String szFile,
int uMode,
int pclsidHandler);
//Get a stream from an open AVI file
[DllImport("avifil32.dll")]
private static extern int AVIFileGetStream(
int pfile,
out IntPtr ppavi,
int fccType,
int lParam);
//Release an open AVI stream
[DllImport("avifil32.dll")]
private static extern int AVIStreamRelease(IntPtr aviStream);
//Release an ope AVI file
[DllImport("avifil32.dll")]
public static extern int AVIFileRelease(int pfile);
//Close the AVI library
[DllImport("avifil32.dll")]
private static extern void AVIFileExit();
//Create a new stream in an open AVI file
[DllImport("avifil32.dll")]
private static extern int AVIFileCreateStream(
int pfile,
out IntPtr ppavi,
ref AVISTREAMINFO ptr_streaminfo);
//Set the format for a new stream
[DllImport("avifil32.dll")]
private static extern int AVIStreamSetFormat(
IntPtr aviStream, Int32 lPos,
ref BITMAPINFOHEADER lpFormat, Int32 cbFormat);
//Write a sample to a stream
[DllImport("avifil32.dll")]
private static extern int AVIStreamWrite(
IntPtr aviStream, Int32 lStart, Int32 lSamples,
IntPtr lpBuffer, Int32 cbBuffer, Int32 dwFlags,
Int32 dummy1, Int32 dummy2);
#endregion
private int aviFile = 0;
private IntPtr aviStream = IntPtr.Zero;
private BackgroundWorker bgw;
private List<Bitmap> lstBmp;
private bool die = false;
public AviWrite(string FileName, int FrameRate, int Height, int Width, int Stride)
{
// Init the AVI Lib and open up the File
AVIFileInit();
int hr = AVIFileOpen(ref aviFile, FileName, 4097, 0); // 4097 should be the int bitmask for WRITE/CREATE
AVISTREAMINFO streaminfo = new AVISTREAMINFO();
streaminfo.fccType = 1935960438; // Video Type
streaminfo.fccHandler = 1668707181; // MS Video v1
streaminfo.dwScale = 1;
streaminfo.dwRate = (uint)FrameRate;
streaminfo.dwSuggestedBufferSize = (UInt32)(Height * Stride);
streaminfo.dwQuality = 10000; // We`ll assume that we use non compressed with highest quality
// else we would probably see aliasing or artifacts
streaminfo.rcFrame.bottom = (UInt32)Height;
streaminfo.rcFrame.right = (UInt32)Width;
streaminfo.szName = new UInt16[64];
// Create a Stream now
int res = AVIFileCreateStream(aviFile, out aviStream, ref streaminfo);
// Now we go ahead and create the image format header
BITMAPINFOHEADER binfo = new BITMAPINFOHEADER();
binfo.biSize = (UInt32)Marshal.SizeOf(binfo);
binfo.biWidth = (Int32)Width;
binfo.biHeight = (Int32)Height;
binfo.biPlanes = 1;
binfo.biBitCount = 24;
binfo.biSizeImage = (UInt32)(Stride * Height);
// and push in a stream
res = AVIStreamSetFormat(aviStream, 0, ref binfo, Marshal.SizeOf(binfo));
// We will use a Thread to write the AVI Data into the File...
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
lstBmp = new List<Bitmap>(); // We will add bmp`s here for the Thread to add
bgw.RunWorkerAsync();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
int countFrames = 0;
while (!die)
{
while(lstBmp.Count > 0)
{
Bitmap tmpBmp;
lock(lstBmp)
{
tmpBmp = lstBmp[0];
lstBmp.RemoveAt(0);
}
// We need to rotate and flip the image...
tmpBmp.RotateFlip(RotateFlipType.Rotate180FlipX);
BitmapData bmpDat = tmpBmp.LockBits(new Rectangle(0, 0, tmpBmp.Width, tmpBmp.Height),ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int result = AVIStreamWrite(aviStream, countFrames, 1, bmpDat.Scan0, (Int32)(bmpDat.Stride * bmpDat.Height), 0, 0, 0);
tmpBmp.UnlockBits(bmpDat);
countFrames++;
//MessageBox.Show("Added AVI!");
}
}
}
public void AviAddFrame(Bitmap bmp)
{
if(!die)
lstBmp.Add(bmp);
}
public void EndAvi()
{
die = true;
while (bgw.IsBusy) ;
AVIStreamRelease(aviStream);
AVIFileRelease(aviFile);
AVIFileExit();
}
}
}
Note: Some parts are taken from a Codeproject Article, and i do not claim that these are my own work...
Usage:
Get a RTT Target up and Running and do the Following:
texturePtr.GetBuffer().BlitToMemory(pb);
bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
Marshal.Copy(bytes, 0, bmpData.Scan0, (int)buffer.SizeInBytes);
bmp.UnlockBits(bmpData);
if (aw == null)
aw = new VirtualStudio.AviWrite(aviFileName, 25, bmp.Height, bmp.Width, bmpData.Stride);
aw.AviAddFrame(bmp);
Now do aw.AviAddFrame(bmp); each frame.
To end a AviFile propperly do:
aw.EndAvi();
Note that you, in order to get a Propper AVI File, have to stabilize your Framerate to the framerate used in the Constructor, else the AVI may become async etc.
Have fun with it... nataz