Mogre to AVI (without external dependencies)

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

/* 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

smernesto

13-08-2007 21:43:51

Do you have the link to the codeproject article?.

If you want I can put your code in the wiki.

Ernesto

nataz

14-08-2007 05:32:03

If you want fell free to do so. Cant recall the link right now sorry

smernesto

14-08-2007 06:34:28

Don´t worry , I will search codeproject.