Bitmaps to videos
Written by Harry Fairhead   
Tuesday, 21 July 2009
Article Index
Bitmaps to videos
Bitmap Class
Making the video
Compression
Action

 

Making the video

Now we have a bitmap class we can move on to make use of it within a BmpToAVI class.

Add a new class called BmpToAVI to the project. The new class is going to make use of a number of API calls within the VfW API but rather than translate them all it makes more sense to create definitions for only those that are needed.

When you use VfW you first have to initialise the system and when you have finished you have to free it. These are most sensibly done in the class constructor and destructor as these are called automatically when the class is created and destroyed respectively:


class BmpToAVI
{
[DllImport("avifil32.dll")]
extern static void AVIFileInit();
[DllImport("avifil32.dll")]
extern static void AVIFileExit();
 public BmpToAVI(IntPtr hwnd)
{
m_hWnd = hwnd;
AVIFileInit();
}

~BmpToAVI()
{
AVIFileExit();
}
}

 

The need for the hwnd parameter will become apparent later. For now we simply store it in a private variable:

private IntPtr m_hWnd;

The API definitions are the first of several and they simply define the DLL in which the functions reside. Things get more complicated when we have parameters to translate to C# data types. To make these DllImport commands work we also need to add:

using System.Runtime.InteropServices;

The VfW AVI system works in terms of multimedia data streams stored in a single file. Streams can be video, audio or text. First we have to open, and in this case create, an AVI file and this can be done as part of adding the first bitmap to the video.

Add the following function to the class:

 

public void FirstFrame(
string AVIfile,
string BMPfile)
{
int result = AVIFileOpen(
ref pFile,
AVIfile,
OF_WRITE | OF_CREATE,
0);

This returns a pointer to the AVI file that is used in other API calls and this has to be declared as a private global variable along with the other pointers to the streams that we are going to create – one standard and one compressed:

private IntPtr pFile = IntPtr.Zero;
private IntPtr pStream = IntPtr.Zero;
private IntPtr psComp = IntPtr.Zero;

The definition of the API call is:

 

[DllImport("avifil32.dll")]
extern static int AVIFileOpen(
ref IntPtr pfile,
string File,
int Mode,
int clsidHandler);

and the constants are:

private const int OF_WRITE=0x00000001;
private const int OF_CREATE=0x00001000;

The values for constants and error codes can be found in the file VFW.h file contained in the platform SDK which can be downloaded from the Microsoft website. The translation of the DLL function is relatively easy. The IntPtr type is useful for almost any 32-bit handle or pointer. The result returned should be zero if the function has worked.

 

Now that we have the file open we have to create a video stream to store in it. This requires us to provide information about the format of the video. Not difficult but the API does seem to need this or very similar information supplied to it more often than seems strictly necessary. Much if the format information is concerned with the details of the bitmaps that are going to be used to create the stream so now is a good time to load the first bitmap:

 RawBitmap bm = new RawBitmap();
bm.LoadFromFile(BMPfile);

Next we need to fill in the details in an AVISTREAMINFO struct. This is fairly easy to translate from the C++ definition:

 

StructLayout(LayoutKind.Sequential, 
Pack = 1)]
public unsafe struct AVISTREAMINFO
{
public Int32 fccType;
public Int32 fccHandler;
public Int32 dwFlags;
public Int32 dwCaps;
public Int16 wPriority;
public Int16 wLanguage;
public Int32 dwScale;
public Int32 dwRate;
public Int32 dwStart;
public Int32 dwLength;
public Int32 dwInitialFrames;
public Int32 dwSuggestedBufferSize;
public Int32 dwQuality;
public Int32 dwSampleSize;
public AVI_RECT rcFrame;
public Int32 dwEditCount;
public Int32 dwFormatChangeCount;
public fixed char szName[64];
};
[StructLayout(LayoutKind.Sequential, 
Pack = 1)]
public struct AVI_RECT
{
public Int32 left;
public Int32 top;
public Int32 right;
public Int32 bottom;
};

The only complication is the need to use a fixed sized character array. This is considered “unsafe” – hence you have to remember to allow unsafe code in the project properties. We also need an AVI_RECT structure. Filling in the details is fairly easy but it is difficult knowing which parameters are important and which aren’t:

 

AVISTREAMINFO Sinfo = 
new AVISTREAMINFO();
Sinfo.fccType =
mmioStringToFOURCC("vids", 0);
Sinfo.fccHandler = 0;
Sinfo.dwScale = 1;
Sinfo.dwRate = 10;
Sinfo.dwSuggestedBufferSize =
bm.bmIH.biSizeImage;
Sinfo.rcFrame.top = 0;
Sinfo.rcFrame.left = 0;
Sinfo.rcFrame.right = bm.bmIH.biWidth;
Sinfo.rcFrame.bottom = bm.bmIH.biHeight;

It is clear that we do need to specify the type of the stream “vids” for video in this case, the size of the frame and the frame rate, set to 10 frames per second in this case. We also need the mmioStringToFOURCC to convert the string “vids” to the multimedia code for a video stream:

 

[DllImport("winmm.dll", EntryPoint =
"mmioStringToFOURCCA")]
extern static int mmioStringToFOURCC(
string sz,
int Flags)

In this case we use an EntryPoint to specify the function in the DLL because it has a slightly different name to the C# function.

The stream can now be created using the format information:

result = AVIFileCreateStream(
pFile,
ref pStream,
ref Sinfo);

This returns a pointer to the video stream within the AVI file. The definition of the API call is:

[DllImport("avifil32.dll")]
extern static int AVIFileCreateStream(
IntPtr pfile,
ref IntPtr pavi,
ref AVISTREAMINFO lParam);

<ASIN:0735619115>

<ASIN:0735614555>

<ASIN:073561945X>



Last Updated ( Tuesday, 21 July 2009 )