Home
Research
Photography
Projects
Code Samples
Custom Digital Video Effects

If you've come across a video transition or effect that your video editing software doesn't include, or if you want to create your own unique and imaginative effects, this page is for you.

Creating your own effects for digital video is easy using the AVIFile functions, but the overhead code required becomes tedious. Wrapper classes, such as the ones shown here, tremendously simplify the process of creating custom effects.

Original Video Clip Old Film Effect
Horizontal Stripes Effect Shaking Camera Effect

NOTE: The code on this page is often slow. It is designed to be easy and flexible, not optimized for speed. It is best suited for use by programmers producing their own digital videos, not as an end-user product.

Download the Sample Code Download the Sample Program including 8 Custom Effects!
(Requires mfc42.dll)

Limitations

The code on this page is designed to be as simple and straightforward as possible, therefore it is limited to certain types of input videos:

All videos (input and output) must be in 24-bit color! The wrapper classes will not work with palletized videos.

If two input files are required for a video effect, they must have the same dimentions! The wrapper classes will not scale videos.


Wrapper Classes

Three wrapper classes are used to encompass the AVIFile functions:
CInputVideo - Handles reading from AVI files
COutputVideo - Handles writing and compressing AVI files
CVideoFrame - Hides the actual bitmap data behind each frame, and provides access through simple functions, GetPixel() and SetPixel()

Most of the functions in the wrapper classes are fairly self-explanitory, so they will not be covered here, but there are are few which may need some clarification:

CVideoFrame CInputVideo::GetFrame() vs.
LPBITMAPINFOHEADER CInputVideo::GetFrameB()
Both of these functions return the current frame, but they have different return types. The first, GetFrame() creates a new, non-editable CVideoFrame class and fills it with the bitmap data from the current frame. The second function, GetFrameB() returns a pointer to the actual bitmap data.

BOOL COutputVideo::DisplayCompressionOptions(CWnd *pParent = NULL)
Displays a standard video compression dialog box, allowing the user to select the compression options for the output video. This function should be called before the first frame of data is written to the output video.

CVideoFrame::CVideoFrame(LPBITMAPINFOHEADER pBitmap, BOOL bNew = FALSE)
The constructor of the CVideoFrame class takes two inputs: a pointer to the initial bitmap data for the frame, and a boolean value, indicating whether the frame should be editable. If bNew is set to TRUE, the constructor will create a copy of the bitmap data, so the frame can be edited. Otherwise, it will simply use the pointer directly to the bitmap data, which is suitable for read-only frames.

LPBITMAPINFOHEADER CVideoFrame::GetFrame() vs.
unsigned char* CVideoFrame::GetData()
GetFrame() returns a pointer to the header of the bitmap (which is immediately followed by the actual bitmap data). The GetData() function returns a pointer directly to the bitmap data.

void CVideoFrame::PaintWindow(CWnd *pWnd)
Used for painting preview windows. The AVIFile functions include code for painting bitmaps, without going through the Windows GDI. Once a video frame is ready to be written to the output video, simply call PaintWindow() and pass a pointer to a CFrame object to it. PaintWindow() will fill the frame with the current video frame.


Creating a Simple Effect

Let's look at how to create a simple video effect using the wrapper classes.

First, create the CInputVideo and COutputVideo objects:
CInputVideo Input(sInput);
COutputVideo Output(sOutput, &Input);

Next, display the Compression Options dialog to allow the user to change the compression of the output file:
if (!Output.DisplayCompressionOptions())
  return;

Next, we create a CVideoFrame that we can write to, named Dest. We will initialize it with the first frame of the input video, to automatically set the dimentions and other options of the frame.
Input.SelectFrame(0);
CVideoFrame Dest(Input.GetFrameB(), TRUE);

Next, we begin a loop to cycle through each frame. We then create a new CVideoFrame object, Src, containing the bitmap data from the current frame of the input video.
int nFrames = Input.GetLength();
for (int nCurFrame=0; nCurFrame<nFrames; nCurFrame++)
{
  Input.SelectFrame(nCurFrame);
  CVideoFrame Src(Input.GetFrameB());

We can now create a loop to cycle through every pixel in the frame:
  for (int y=0; y<Src.GetHeight(); y++)
  {
    for (int x=0; x<Src.GetWidth(); x++)
    {

Here is where we perform the actual effect on our video frame:
      COLORREF srccolor = Src.GetPixel(x, y);
      
      // perform effect here by manipulating
      // srccolor to get destcolor
      COLORREF destcolor = srccolor;
      
      Dest.SetPixel(x, y, destcolor);
    }
  }

Once we are done editing the video frame, we can write it to our output AVI file.
  Output.WriteFrame(&Dest);

This is also a good time to update our preview dialog box:
  Dest.PaintWindow(pPreviewDlg->m_pPreviewFrame);
}

The classes take care of the memory deallocation in the destructors, so we're done!


Sample Code

The sample code, available for download from this site, contains the wrapper classes, plus a simple application containing 8 custom effects. The application is designed to make adding new effects easy, but you may choose to design your own application from scratch. Remember to link your project with vfw32.lib!

Download the Sample Code Download the Sample Program including 8 Custom Effects!
(Requires mfc42.dll)
© 1998-2007 Leo C. Singleton IV