Clint Olsen
Clint Olsen

Reputation: 61

Parsing BITMAPINFO structure from an AVI file

I'm trying to learn more about the AVI structure, so I started walking through Microsoft's obtuse documentation located here:

https://msdn.microsoft.com/en-us/library/ms779636.aspx

My hope is to write an AVI creator based on uncompressed bitmap images. But I've found it's pretty hard to get my head around the format without having tried decomposing an existing AVI. So, I grabbed one laying around on my hard drive and started extracting bytes from the file. Here's the code and commands to build:

// Read an unsigned
//
static unsigned get_dword(FILE *fp)
{
    unsigned tmp;
    unsigned char buf[4];
    unsigned char *p;

    p = (unsigned char *) &tmp;

    if (fread(&buf, 1, sizeof(buf), fp) != sizeof(buf)) {
        printf("Error: Unexpected EOF\n");
        exit(1);
    }

    p[0] = buf[0];
    p[1] = buf[1];
    p[2] = buf[2];
    p[3] = buf[3];

    return tmp;
}

static unsigned short get_word(FILE *fp)
{
    unsigned short tmp;
    unsigned char buf[2];
    unsigned char *p;

    p = (unsigned char *) &tmp;

    if (fread(&buf, 1, sizeof(buf), fp) != sizeof(buf)) {
        printf("Error: Unexpected EOF\n");
        exit(1);
    }

    p[0] = buf[0];
    p[1] = buf[1];

    return tmp;
}

static char *get_fourcc(char *str, FILE *fp)
{
    if (fread(str, 1, 4, fp) != 4) {
        printf("Error: Unexpected EOF\n");
        exit(1);
    }

    return str;
}

//
//
int main(int argc, char *argv[])
{
    if (argc > 1) {
        FILE *fp = fopen(argv[1], "rb");

        if (fp) {
            char buf[5] = { 0 };

            printf("%s (\n", get_fourcc(buf, fp));

            printf("fileSize=%u\n", get_dword(fp));

            printf("fileType='%s'\n", get_fourcc(buf, fp));

            printf("'%s'\n", get_fourcc(buf, fp));

            printf("listSize=%u\n", get_dword(fp));

            printf("listType='%s'\n", get_fourcc(buf, fp));
            // avih

            printf("listData='%s'\n", get_fourcc(buf, fp));

            printf("cb=%u\n", get_dword(fp));
            printf("dwMicroSecPerFrame=%u\n", get_dword(fp));
            printf("dwMaxBytesPerSec=%u\n", get_dword(fp));
            printf("dwPaddingGranularity=%u\n", get_dword(fp));
            printf("dwFlags=0x%x\n", get_dword(fp));
            printf("dwTotalFrames=%u\n", get_dword(fp));
            printf("dwInitialFrames=%u\n", get_dword(fp));
            printf("dwStreams=%u\n", get_dword(fp));
            printf("dwSuggestedBufferSize=%u\n", get_dword(fp));
            printf("dwWidth=%u\n", get_dword(fp));
            printf("dwHeight=%u\n", get_dword(fp));
            printf("dwReserved = { %u, %u, %u, %u }\n", get_dword(fp),
                get_dword(fp), get_dword(fp), get_dword(fp));

            printf("'%s'\n", get_fourcc(buf, fp));
            printf("listSize=%u\n", get_dword(fp));
            printf("listType='%s'\n", get_fourcc(buf, fp));

            // strh
            printf("listData='%s'\n", get_fourcc(buf, fp));
            printf("cb=%u\n", get_dword(fp));
            printf("fccType='%s'\n", get_fourcc(buf, fp));
            printf("fccHandler='%s'\n", get_fourcc(buf, fp));
            printf("dwFlags=0x%x\n", get_dword(fp));
            printf("wPriority=%d\n", get_word(fp));
            printf("wLanguage=%d\n", get_word(fp));
            printf("dwInitialFrames=%u\n", get_dword(fp));
            printf("dwScale=%u\n", get_dword(fp));
            printf("dwRate=%u\n", get_dword(fp));
            printf("dwStart=%u\n", get_dword(fp));
            printf("dwLength=%u\n", get_dword(fp));
            printf("dwSuggestedBufferSize=%u\n", get_dword(fp));
            printf("dwQuality=%u\n", get_dword(fp));
            printf("dwSampleSize=%u\n", get_dword(fp));
            printf("rcFrame={ %u, %u, %u, %u }\n", get_word(fp),
                get_word(fp), get_word(fp), get_word(fp));

            // strf
            printf("'%s'\n", get_fourcc(buf, fp));
            printf("biSize=%u\n", get_dword(fp));
            printf("?=%d\n", get_dword(fp));
            printf("biWidth=%d\n", get_dword(fp));
            printf("biHeight=%d\n", get_dword(fp));
            printf("biPlanes=%d\n", get_word(fp));
            printf("biBitCount=%d\n", get_word(fp));
            printf("biCompression=0x%x\n", get_dword(fp));
            printf("biSizeImage=%u\n", get_dword(fp));
            printf("biXPelsPerMeter=%d\n", get_dword(fp));
            printf("biYPelsPerMeter=%d\n", get_dword(fp));
            printf("biClrUsed=%u\n", get_dword(fp));
            printf("biClrImportant=%u\n", get_dword(fp));

            printf("%s\n", get_fourcc(buf, fp));

            fclose(fp);
        } else {
            printf("Unable to open '%s'\n", argv[1]);
        }
    }
}

$ g++ -g -o avi -Wall -ansi avi.cc
$ ./avi avifile.avi
RIFF (
fileSize=68054008
fileType='AVI '
'LIST'
listSize=796
listType='hdrl'
listData='avih'
cb=56
dwMicroSecPerFrame=33367
dwMaxBytesPerSec=3724404
dwPaddingGranularity=512
dwFlags=0x810
dwTotalFrames=545
dwInitialFrames=0
dwStreams=2
dwSuggestedBufferSize=120008
dwWidth=720
dwHeight=480
dwReserved = { 0, 0, 0, 0 }
'LIST'
listSize=228
listType='strl'
listData='strh'
cb=56
fccType='vids'
fccHandler='dvsd'
dwFlags=0x0
wPriority=0
wLanguage=0
dwInitialFrames=0
dwScale=1001
dwRate=30000
dwStart=0
dwLength=545
dwSuggestedBufferSize=120008
dwQuality=4294967295
dwSampleSize=0
rcFrame={ 0, 0, 720, 480 }
'strf'
biSize=40
?=40
biWidth=720
biHeight=480
biPlanes=1
biBitCount=24
biCompression=0x64737664
biSizeImage=120000
biXPelsPerMeter=0
biYPelsPerMeter=0
biClrUsed=0
biClrImportant=0
indx

My concern is the particular entry with the '?' by it. 40 bytes should be correct for the header size, but the 40 is repeated a second time before you get into the width and height of the bitmap frames. I ran hexdump and it's not a bug in the code. I see it twice. I'm trying to understand if this is just an error in my interpretation of the format or if this is something else.

The BITMAPINFOHEADER structure is documented here:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx

If anyone has any pointers, that would be awesome...

Upvotes: 2

Views: 690

Answers (1)

Ross Ridge
Ross Ridge

Reputation: 39621

The first number 40 isn't biBytes the second 40 is. The first number is actually the size of the data contained in the strf chunk. These two numbers happen to be the same for this chunk.

AVI files are based on Microsoft's RIFF format (which in turn is based on the Electronic Arts's IFF format). In the RIFF format everything is stored in chunks. Each chunk starts with 4 bytes that identify the type of chunk, which are immediately followed by 4 bytes that specify the length of the data in the chunk as 32-bit unsigned little-endian integer. I'd recommend rewriting your program to process each chunk individually rather than assuming a fixed sequence of chunks. That way you can skip over chunks you're not interested in or haven't figured out how to completely decode yet.

Upvotes: 2

Related Questions