BMPs to AVI
Video Capture Help ] AVIFile Tutorial ] Licensing and Distribution ] Resources ] Awards ] Latest News ] Source Code ] Free Controls ]

 

STEP 5 - Creating new AVI files from individual samples/frames

HERE ARE THE PROJECT FILES FOR STEP 5 AVITutr5.zip

By now, you should have a good idea of what is possible with the AVIFile functions.  The final sample will demonstrate how to create an AVI file from a sequence of individual bitmap file.  If you don't have a video capture device or commercial graphics software you can still prepare a series of bitmaps with the mspaint.exe program included with windows.  Just save several different files the same width, height and color depth and you will have something to work with as you experiment with the code here (AVITutr5.zip).  You might even want to use the utility we built in step three to create bitmaps from an existing AVI file you have.  Then you could mix up the frames any way you want and write a new AVI file with this sample.

The only new things in this sample's code are that I create a stream from "scratch" (instead of just copying and existing stream), and use a different method of saving the stream to an AVI file (I don't use AVISave in this sample).  The rest of the sample should be fairly easy to understand if you have gone through the other steps before this one.

A stream can be created by initializing the AVI_STREAM_INFO UDT and passing it into the AVIFileCreateStream function.  When this function returns, the second parameter will contain a valid pointer to a newly allocated stream interface pointer:

' Fill in the header for the video stream
With strhdr
    .fccType = mmioStringToFOURCC("vids", 0&)         
'// stream type video
    .fccHandler = 0&                                  
'// default AVI handler
    .dwScale = 1
    .dwRate = Val(txtFPS)                             
'// fps
    .dwSuggestedBufferSize = bmp.SizeImage             '// size of one frame pixels
    Call SetRect(.rcFrame, 0, 0, bmp.Width, bmp.Height)
'// rectangle for stream
End With

'validate user input
If strhdr.dwRate < 1 Then strhdr.dwRate = 1
If strhdr.dwRate > 30 Then strhdr.dwRate = 30

' And create the stream
res = AVIFileCreateStream(pfile, ps, strhdr)
If (res <> AVIERR_OK) Then GoTo error

Now that the ps variable points to a stream, we can use that to create a compressed stream which contains all the information neccesary to write a file with a codec-compressed video stream:

First we let the user decide which codec and settings to use:

'get the compression options from the user
'Careful! this API requires a pointer to a pointer to a UDT

pOpts = VarPtr(opts)
res = AVISaveOptions(Me.hWnd, _
                            ICMF_CHOOSE_KEYFRAME Or ICMF_CHOOSE_DATARATE, _
                            1, _
                            ps, _
                            pOpts)
'returns TRUE if User presses OK, FALSE if Cancel
If res <> 1 Then 'In C TRUE = 1
    Call AVISaveOptionsFree(1, pOpts)
    GoTo error
End If

Then we pass this information, along with the stream to the AVIMakeCompressedStream function:

'make compressed stream
res = AVIMakeCompressedStream(psCompressed, ps, opts, 0&)
If res <> AVIERR_OK Then GoTo error

Once this function returns sucessfully, we know that psCompressed points to a valid compressed stream interface which can write out a compressed video stream in the format we specified with the AVI_STREAM_INFO and AVI_COMPRESS_OPTIONS UDTs.  All we have to do then is to set the format of the DIBs that the stream will expect us to pass in.  This requires a BITMAPINFO UDT which is a variable sized UDT and difficult to use from VB.  Fortunately the cDIB class will help us again here.  The cDIB class has a function which receives a bmp file from the disk.  Earlier in the sub I called this function and loaded the first bitmap from the list in order to get the width, height and size of the data buffer for the AVI_STREAM_INFO UDT:

'Get the first bmp in the list for setting format
Set bmp = New cDIB
lstDIBList.ListIndex = 0
If bmp.CreateFromFile(lstDIBList.Text) <> True Then
    MsgBox "Could not load first bitmap file in list!", vbExclamation, App.title
    GoTo error
End If

Now we can use the same UDT to set the format of the video stream.  First we will store all the necessary information in a BITMAPINFOHEADER UDT:

'set format of stream according to the bitmap
With BI
    .biBitCount = bmp.BitCount
    .biClrImportant = bmp.ClrImportant
    .biClrUsed = bmp.ClrUsed
    .biCompression = bmp.Compression
    .biHeight = bmp.Height
    .biWidth = bmp.Width
    .biPlanes = bmp.Planes
    .biSize = bmp.SizeInfoHeader
    .biSizeImage = bmp.SizeImage
    .biXPelsPerMeter = bmp.XPPM
    .biYPelsPerMeter = bmp.YPPM
End With

And then pass the UDT along with the stream to the AVIStreamSetFormat function:

'set the format of the compressed stream
res = AVIStreamSetFormat(psCompressed, 0, ByVal bmp.PointerToBitmapInfo, bmp.SizeBitmapInfo)
If (res <> AVIERR_OK) Then GoTo error

If this function returns successfully then the compressed stream is fully initialized and ready to be passed some DIBs for writing.  You do this by passing it and the stream, along with the position you what it written, to the AVIStreamWrite function.  Notice that the cDIB class comes in handy here again because it has a method which returns a direct pointer to the exact bits that you need to pass in to the function:

' Now write out each video frame
For i = 0 To lstDIBList.ListCount - 1
    lstDIBList.ListIndex = i
    bmp.CreateFromFile (lstDIBList.Text)
'load the bitmap (ignore errors)
    res = AVIStreamWrite(psCompressed, _
                                    i, _
                                    1, _
                                    bmp.PointerToBits, _
                                    bmp.SizeImage, _
                                    AVIIF_KEYFRAME, _
                                    ByVal 0&, _
                                    ByVal 0&)
    If res <> AVIERR_OK Then GoTo error
    'Show user feedback
    imgPreview.Picture = LoadPicture(lstDIBList.Text)
    imgPreview.Refresh
    lblStatus = "Frame number " & i & " saved"
    lblStatus.Refresh
Next
lblStatus = "Finished!"

Once this loop is finished writing the bitmaps, all you have to do is clean up afterwards and release the stream and file handles.  The file you created at the beginning of the sub will then contain a valid video stream and you can play it back in the Windows Media Player or other AVI player.  Please see the code in the sample for all the cleanup details.  When you run the sample, you will have to select the individual bitmaps one at a time then type in a frame rate between 1fps and 30fps before writing the new file.  Have fun!

Go to the contact information page

Go back to the Table of Contents

This Shrinkwrap Visual Basic AVIFile Tutorial is Copyright (C) 2000 by Ray Mercer
Redistribution of the tutorial text and/or samples is prohibited.  Please contact the author Ray Mercer <raymer@shrinkwrapvb.com> if you have a question about this policy.