|
|
STEP 3 - Working with frames in existing AVI filesHERE ARE THE PROJECT FILES FOR STEP 3 AVITutr3.zip UPDATE! - I have noticed that some systems (like my RIVA TNT on Win2000) seem unable to decompress palletized video to 24bit RGB. The AVIStreamGetFrameOpen() fails when passing in the BITMAPINFOHEADER UDT. I originally made the sample use 24bit RGB because I thought that all modern graphics cards would support it in any case. But it seems I may have been wrong. I am investigating now and I plan to put a revised sample up soon. -Ray The next step in working with AVI files is to get down to the sample/frame level. Since we have already opened a video stream in the previous sample, let's try to save each frame in the stream as separate bitmap files. This will involve a bit more code than the previous steps, so instead of copying from the HTML here, this time download the full sample project first here (AVITutr3.zip) and I will highlight some of the important parts of the code as we go through it together. Assuming you have now downloaded and opened the project in either VB5 or VB6, you will notice that I have added a textbox to the form (this is just for status information) and a new class called cDIB.cls . This class is just to wrap some of the messy APIs involved in dealing with DIBs (Device Independent Bitmaps) in VB. It is especially tricky working with the variable-length BitmapInfo structure, since the size varies depending on the color-depth of the bitmap image. Anyway, ignoring the implementation details of the cDIB class for now (maybe I will write some *more* HTML someday <groan>), you will notice that it doesn't take too many more lines of code in the command button's click event to do what we want. You will see 5 new variables dimensioned at the top of the Sub: Dim dib As cDIB The dib variable is, of course, an instance of the new class that let's us keep our code relatively neat. The pGetFrameObj variable is a new kind of handle which is required by the AVIStreamGetFrame functions. These functions automatically allocate the resources to decompress one frame into any type DIB that we specify and return a pointer to it so we can process it and use it how we want. To use these functions I first allocate the resources and get a pointer to the GetFrame interface like this: 'init AVISTreamGetFrame* functions and create GETFRAME object Before that line of code, I initialized a BitmapInfoHeader UDT to the format I wanted the GetFrame functions to return (in this case 24-bit, uncompressed RGB): 'set bih attributes which we want GetFrame functions to return This makes it possible to know what DIB format is coming in advance, so that my DIB class can handle it (right now the DIB class can only handle unpalletized memory DIBs - RGB 16 or 24bit color). If you are going to pass the returned DIB pointer to another function like DrawDIB, that understands any type of format then you might want to call the function like this instead: 'tell AVIStream API to choose the best display format automatically This is easier than setting up the BitmapInfoHeader UDT, but it means that you cannot be sure what format you will get back - it depends on the current display settings and hardware. Now that we have succesfully initialize the GetFrame API to the format we want and received a valid GetFrame interface pointer in the pGetFrameObj variable, all we need to do is pass the handle and the frame number that we want to the AVIStreamGetFrame function. I then use my handy cDIB class to copy the Bitmap headers and bits and write the new data out to disk as a Bitmap file. It only takes a few lines of code: 'create a DIB class to load the frames into That's all there is to it! As you can see, the i variable is just counter that I used to loop through the entire file. Be careful using this example on very large AVI files, since there is no way to stop the loop until it writes every frame to disk once you start ;-) If you run this on an AVI you will find the application folder filled with RGB 24bit bitmaps named after the frame numbers. If you want to see how I wrote out the Bitmap to disk you can take a look at the cDIB class implementation code. Only 2 functions were necessary - .CreateFromPackedDIBPointer and .WriteToFile. The other functions in cDIB are mostly for creating DIBs from bitmap files, which we will do in a later step. In your own program, you might want to create an hDC and blit each frame to a picture control instead of saving them to disk.. The only other thing to remember is to tell the FrameGet API to deallocate the resources when you are done. To do this just pass the same handle to AVIStreamGetFrameClose: Call AVIStreamGetFrameClose(pGetFrameObj) '//deallocates the GetFrame resources When this function returns the pointer will no longer be valid. It would be a good idea to set it to 0 if you plan on reusing it. Because my pGetFrameObj variable is only in scope for one subroutine I didn't bother. Go back to the Table of Contents
|