Skip to main content
gamedev.net
🔒 Locked

AVI problems

Started by weasalmongler Jun 29, 2006 at 7:24 PM 11 replies 2.4k views
Original Post
weasalmongler
weasalmongler
Hi everyone. I am trying to load an uncompressed AVI file but have run into a few problems. I open an AVI stream with AVIStreamOpenFromFile(); (from vfw.h) and then proceed to grab a frame from it using AVIStreamGetFrame. This basically gives me a frame of the animation in standard uncompressed BMP format. What I am after is getting this data into an array of pixels, each with 3 components, red, green and blue, each ranging from 0-255. The file that I am trying to load returns 16 bit frames so I know that each pixel in the file is encoded into a WORD, with 5 bits for blue (low bits), green and red, with the high bit unused. I have tried this to decompress it (pixel is the pixel data encoded into a WORD): blue = ( (pixel & 0x001F) >> 0 ); green = ( (pixel & 0x03E0) >> 5 ); red = ( (pixel & 0x7C00) >> 10); This works fine for the blue channel, but the green and red channels come out horribly distorted (although you can still sort of make out what is supposed to be) and I have absolutely no idea why. Does anyone have any tips or ideas on how to sort this problem out? Thanks for you time.
weasalmongler
weasalmongler
My code is something like this


Initialization stuff :

AVIStreamInfo(pavi, ψ, sizeof(psi));width = psi.rcFrame.right - psi.rcFrame.left;height = psi.rcFrame.bottom - psi.rcFrame.top;lastframe = AVIStreamLength(pavi);mpf = AVIStreamSampleToTime(pavi, lastframe)/lastframe;pgf = AVIStreamGetFrameOpen(pavi, NULL);


Rendering each frame stuff:

lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(pgf, frame); //Grab data from avi streampdata = (char*)lpbi + lpbi->biSize + lpbi->biClrUsed*sizeof(RGBQUAD);//Make sure we are compressed as BI_RGBif( lpbi->biCompression != BI_RGB){    return;}//Now copy the data to othe new bufferif( lpbi->biBitCount == 24 ){  RGBQUAD* quad;  for(i=0; i<height; i++)  {    for(j=0; j<width; j++)    {      quad = (RGBQUAD*)&pdata[ sizeof(RGBQUAD)*(i*width + j) ];      //We have 4 bytes per pixel, the left hand side of the equals sign is correct, i have tested it on other inputs, and it works fine      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 0] = quad->rgbBlue;    //Blue      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 1] = quad->rgbGreen;   //Green      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 2] = quad->rgbRed;     //Red      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 3] = quad->rgb255;     //Alpha    }  }}else if( lpbi->biBitCount == 16 )   //THIS is the problem area, the BMP is 16 bit{  char red, green, blue;  WORD pixel;  for(i=0; i<height; i++)  {    for(j=0; j<width; j++)    {      pixel = (WORD)&pdata[ sizeof(WORD)*(i*width + j) ];      //Extract the colours      blue  = ((pixel & 0x001F) >> 0 );      green = ((pixel & 0x03E0) >> 5 );      red   = ((pixel & 0x7C00) >> 10);      //We have 4 bytes per pixel, the left hand side of the equals sign is correct, i have tested it on other inputs, and it works fine      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 0] = blue;    //Blue      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 1] = green;   //Green      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 2] = red;     //Red      ((char*)rect.pBits)[i*rect.Pitch + 4*j + 3] = quad->rgb255;     //Alpha    }  }}


The blue channel comes out fine as I said, but all the other channels don't work properly.

Can anyone help futher? It still doesn't work.

Thanks.
yadango
yadango
get rid of "lpbi->biClrUsed*sizeof(RGBQUAD)" biClrUsed for 16-bit and 24-bit bitmaps is not and indicator of the number of elements in the color table, it's possible it can even be filled with junk (though it should be 0 for 16 and 24-bit bitmaps).
weasalmongler
weasalmongler
With 16 bit bitmaps lpbi->biClrUsed is just zero, so surely it shouldn't make a difference? I removed it anyway and nothing changed, but thanks for the input :).

- J
yadango
yadango
also, for your 24-bit bitmap data, you are using "quad = (RGBQUAD*)&pdata[ sizeof(RGBQUAD)*(i*width + j) ];" which is incorrect. 24-bit bitmap data does not consist of an array of RGBQUADS (which would imply 32-bits). it should be something like:

int pitch_bytes = ((24*lpbi->biWidth + 31) & (~31))/8;

BYTE* triple = &pdata[pitch_bytes*i + 3*j];
BYTE B = triple[0];
BYTE G = triple[1];
BYTE R = triple[2];

for the 16-bit one it should be:

int pitch_bytes = ((16*lpbi->biWidth + 31) & (~31))/8;

WORD* pixel = (WORD*)(&pdata[pitch_bytes*i + 2*j]);
blue = ((*pixel & 0x001F) >> 0 );
green = ((*pixel & 0x03E0) >> 5 );
red = ((*pixel & 0x7C00) >> 10);

i'm also wondering why in your code you convert the address of an element in pdata to a WORD (i'm suprised you're not seeing blackness):

WORD pixel;
pixel = (WORD)&pdata[ sizeof(WORD)*(i*width + j) ];

weasalmongler
weasalmongler
I've changed my code to incorporate that now, I can't really test it yet as I dont have any 24 bpp AVI videos.

I've also now changed the 16 bit code, still works but makes no difference to the problem.

Thanks
yadango
yadango
how about this? i tried this on my own 555 avi and it worked.

int pitch_bytes = ((16*lpbi->biWidth + 31) & (~31))/8;

WORD* pixel = (WORD*)(&pdata[pitch_bytes*i + 2*j]);
BYTE blue = (BYTE)((*pixel & 0x001F) << 3);
BYTE green = (BYTE)((*pixel & 0x03E0) >> 2);
BYTE red = (BYTE)((*pixel & 0x7C00) >> 7);

i know, you're thinking 3, 2, and 7, what the? but this is why it works:


B
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- - - - - - - - - - - - - - - -
B B B B B G G G G G R R R R R X (WORD color)
1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 (mask 0x001F)
B B B B B 0 0 0 0 0 0 0 0 0 0 0 (masked)
0 0 0 B B B B B 0 0 0 0 0 0 0 0 (shifted << 3)
0 0 0 B B B B B (casted to BYTE)

G
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- - - - - - - - - - - - - - - -
B B B B B G G G G G R R R R R X (WORD color)
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 (mask 0x07E0)
0 0 0 0 0 G G G G G 0 0 0 0 0 0 (masked)
0 0 0 G G G G G 0 0 0 0 0 0 0 0 (shifted >> 2)
0 0 0 G G G G G (casted to BYTE)

R
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- - - - - - - - - - - - - - - -
B B B B B G G G G G R R R R R X (WORD color)
0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 (mask 0x7C00)
0 0 0 0 0 0 0 0 0 0 R R R R R 0 (masked)
0 0 0 R R R R R 0 0 0 0 0 0 0 0 (shifted >> 7)
0 0 0 R R R R R (casted to BYTE)


[Edited by - yadango on June 30, 2006 8:43:50 AM]
weasalmongler
weasalmongler
This also didn't work leading me to believe the problem must lie in obtaining the bitmap data from the AVI file. Has anyone else used these functions before, and could you indicate whether I am using it correctly please?

Thank you for your time
yadango
yadango
you appear to be doing it correctly. a thing here or a thing there that I might normally do differently, but it looks ok to me. here is the code i used to test it:

in InitAVIData I force the VFW library to convert an AVI file to a 16-bit bitmap compressed with BI_RGB by passing a BITMAPINFOHEADER to AVIStreamGetFrameOpen with the following set:

bminfo.biBitCount = 16;bminfo.biCompression = BI_RGB;


then, in ExtractAVIFrame, I extract the frame data using AVIStreamGetFrame and offsetting the data by the size of a BITMAPINFOHEADER (appears to be the same thing you did also):

 // extract the frame LPVOID bmdata = AVIStreamGetFrame(aviFrame, pos); if(!bmdata) return false; // copy extracted frame data into dibsection memory int offset = sizeof(BITMAPINFOHEADER); // no color table for 16 or 24-bit bitmaps memmove((char*)aviFrameMemory, (char*)bmdata + offset, aviFrameTotalBytes);


then in WM_PAINT, I take bitmap memory (from the dibsection) and decompress it myself and plot it pixel-by-pixel. Note that if you throw away the loop and uncomment the other code it renders with the dibsection (which is faster instead, loop was just thrown in to demonstrate).

if you just replace my "movie.avi" with yours, and it doesn't work, then you'll have to check the AVISTREAMINFO and examine the contents. maybe it's a 5-6-5 with BI_BITFIELDS??? i dunno, i'd need the avi file to check it out.

        PAINTSTRUCT ps;        HDC winDC = BeginPaint(window, &ps);        for(int y = 0; y < aviFrameHeight; y++) {            for(int x = 0; x < aviFrameWidth; x++) {                WORD* pixel = reinterpret_cast<WORD*>(&aviFrameMemory[aviFramePitchBytes*y + 2*x]);                BYTE B = (BYTE)(((pixel[0] & 0x001F) << 3));                BYTE G = (BYTE)(((pixel[0] & 0x03E0) >> 2));                BYTE R = (BYTE)(((pixel[0] & 0x7C00) >> 7));                SetPixel(winDC, x, aviFrameHeight - y - 1, RGB(R, G, B));               }           }        EndPaint(window, &ps);


Full Code:

#undef UNICODE#include<windows.h>#include<commctrl.h>#include<vfw.h>static const char* aviFileName = "movie.avi";static IAVIFile* aviFile = 0;static IAVIStream* aviStreamVideo = 0;static IAVIStream* aviStreamAudio = 0;static IGetFrame* aviFrame = 0;static HBITMAP aviFrameBitmap = 0;static BYTE* aviFrameMemory = 0;static int aviFrameWidth = 0;static int aviFrameHeight = 0;static int aviFramePitchBytes = 0;static int aviFrameTotalBytes = 0;static int aviNumberOfFrames = 0;static int aviCurrentFrame = 0;// forward declarationsLRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);bool InitAVIData(void);bool FreeAVIData(void);bool ExtractAVIFrame(LONG pos);void FourCCToString(DWORD code, char* buffer);int WINAPI WinMain(HINSTANCE instance, HINSTANCE, char*, int){ WNDCLASS wc; wc.style         = CS_DBLCLKS; wc.lpfnWndProc   = MainWndProc; wc.cbClsExtra    = 0; wc.cbWndExtra    = 0; wc.hInstance     = instance; wc.hIcon         = 0; wc.hCursor       = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName  = 0; wc.lpszClassName = "Fundamental Window"; if(!RegisterClass(&wc))    return MessageBox(0, "Could not register main window.", "Error", MB_ICONSTOP); int x = CW_USEDEFAULT; int w = CW_USEDEFAULT; int y = CW_USEDEFAULT; int h = CW_USEDEFAULT; HWND window = CreateWindow("Fundamental Window", "Fundamental Window", WS_OVERLAPPEDWINDOW, x, y, w, h, 0, 0, instance, 0); if(!window) return MessageBox(0, "Could not create main window.", "Error", MB_ICONSTOP); ShowWindow(window, SW_SHOW); UpdateWindow(window); MSG msg; while(GetMessage(&msg, 0, 0, 0)) {       TranslateMessage(&msg);       DispatchMessage(&msg);      } return (int)msg.wParam;}LRESULT CALLBACK MainWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam){ static HWND trackbar = 0; switch(message) {   case(WM_CREATE) : {        if(!InitAVIData()) return -1;        trackbar = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE | TBS_RIGHT, 0, aviFrameHeight + 5, aviFrameWidth, 25, window, 0, GetModuleHandle(0), 0);        SendMessage(trackbar, TBM_SETRANGE, (WPARAM)FALSE, (LPARAM)MAKELONG(0, aviNumberOfFrames));        break;       }   case(WM_DESTROY) : {        DestroyWindow(trackbar);        FreeAVIData();        PostQuitMessage(0);        break;       }   case(WM_HSCROLL) : {        if((HWND)lparam != trackbar) break;        switch(LOWORD(wparam)) {          case(TB_THUMBTRACK) : {               // clamp current frame               int newpos = (int)SendMessage(trackbar, TBM_GETPOS, 0, 0);               if(newpos >= aviNumberOfFrames) newpos = aviNumberOfFrames - 1;               else if(newpos < 0) newpos = 0;               aviCurrentFrame = newpos;               SendMessage(trackbar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)aviCurrentFrame);               // extract current frame and redraw               ExtractAVIFrame(aviCurrentFrame);               InvalidateRect(window, 0, TRUE);               break;              }          case(TB_ENDTRACK) : {               // clamp current frame               int newpos = (int)SendMessage(trackbar, TBM_GETPOS, 0, 0);               if(newpos >= aviNumberOfFrames) newpos = aviNumberOfFrames - 1;               else if(newpos < 0) newpos = 0;               aviCurrentFrame = newpos;               SendMessage(trackbar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)aviCurrentFrame);               // extract current frame and redraw               ExtractAVIFrame(aviCurrentFrame);               InvalidateRect(window, 0, TRUE);               break;              }         }        break;       }   case(WM_PAINT) : {        if(!aviFrameBitmap) break;        PAINTSTRUCT ps;        HDC winDC = BeginPaint(window, &ps);        for(int y = 0; y < aviFrameHeight; y++) {            for(int x = 0; x < aviFrameWidth; x++) {                WORD* pixel = reinterpret_cast<WORD*>(&aviFrameMemory[aviFramePitchBytes*y + 2*x]);                BYTE B = (BYTE)(((pixel[0] & 0x001F) << 3));                BYTE G = (BYTE)(((pixel[0] & 0x03E0) >> 2));                BYTE R = (BYTE)(((pixel[0] & 0x7C00) >> 7));                SetPixel(winDC, x, aviFrameHeight - y - 1, RGB(R, G, B));               }           }        //HDC memDC = CreateCompatibleDC(winDC);        //HBITMAP oldbitmap = (HBITMAP)SelectObject(memDC, aviFrameBitmap);        //BitBlt(winDC, 0, 0, aviFrameWidth, aviFrameHeight, memDC, 0, 0, SRCCOPY);        //SelectObject(memDC, oldbitmap);        //DeleteDC(memDC);        EndPaint(window, &ps);        return 0;       }  } return DefWindowProc(window, message, wparam, lparam);}bool InitAVIData(void){ // initialize VFW AVIFileInit(); // open AVI file HRESULT result = AVIFileOpen(&aviFile, aviFileName, OF_READ, 0); if(FAILED(result)) return false; // extract AVI file information AVIFILEINFO fileinfo; result = AVIFileInfo(aviFile, &fileinfo, sizeof(fileinfo)); if(FAILED(result)) { FreeAVIData(); return false; } // extract AVI video stream result = AVIFileGetStream(aviFile, &aviStreamVideo, streamtypeVIDEO, 0); if(FAILED(result)) { FreeAVIData(); return false; } // extract AVI audio stream result = AVIFileGetStream(aviFile, &aviStreamAudio, streamtypeAUDIO, 0); if(FAILED(result)) { FreeAVIData(); return false; } // extract AVI video stream information AVISTREAMINFO vidsinfo; result = AVIStreamInfo(aviStreamVideo, &vidsinfo, sizeof(vidsinfo)); if(FAILED(result)) { FreeAVIData(); return false; } // set bitmap information header BITMAPINFOHEADER bminfo; bminfo.biSize = sizeof(BITMAPINFOHEADER); bminfo.biWidth = vidsinfo.rcFrame.right - vidsinfo.rcFrame.left; bminfo.biHeight = vidsinfo.rcFrame.bottom - vidsinfo.rcFrame.top; bminfo.biPlanes = 1; bminfo.biBitCount = 16; bminfo.biCompression = BI_RGB; bminfo.biSizeImage = 0; bminfo.biXPelsPerMeter = 0; bminfo.biYPelsPerMeter = 0; bminfo.biClrUsed = 0; bminfo.biClrImportant = 0; // create AVI video stream frame buffer aviFrame = AVIStreamGetFrameOpen(aviStreamVideo, &bminfo); if(!aviFrame) { FreeAVIData(); return false; } // set AVI frame dimensions aviFrameWidth = bminfo.biWidth; aviFrameHeight = bminfo.biHeight; aviFramePitchBytes = (((16*aviFrameWidth + 31) & (~31))/8); aviFrameTotalBytes = (((16*aviFrameWidth + 31) & (~31))/8)*aviFrameHeight; // set the number of AVI frames aviNumberOfFrames = vidsinfo.dwLength; // create dibsection BITMAPINFO bi; bi.bmiHeader = bminfo; HDC winDC = GetDC(0); aviFrameBitmap = CreateDIBSection(winDC, &bi, DIB_RGB_COLORS, (LPVOID*)&aviFrameMemory, 0, 0); ReleaseDC(0, winDC); if(!aviFrameBitmap) { FreeAVIData(); return false; } // extract and set current AVI frame aviCurrentFrame = 0; ExtractAVIFrame(aviCurrentFrame); return true;}bool FreeAVIData(void){ // free global variables if(aviFrameBitmap) { DeleteObject(aviFrameBitmap); aviFrameBitmap = 0; aviFrameMemory = 0; } if(aviFrame) { AVIStreamGetFrameClose(aviFrame); aviFrame = 0; } if(aviStreamAudio) { AVIStreamClose(aviStreamAudio); aviStreamAudio = 0; } if(aviStreamVideo) { AVIStreamClose(aviStreamVideo); aviStreamVideo = 0; } if(aviFile) { AVIFileClose(aviFile); aviFile = 0; } aviCurrentFrame = 0; aviNumberOfFrames = 0; aviFrameWidth = 0; aviFrameHeight = 0; aviFrameTotalBytes = 0; // free VFW AVIFileExit(); return true;}bool ExtractAVIFrame(LONG pos){ // must have AVI frame data and dibsection data if(!aviFrame) return false; if(!aviFrameMemory) return false; // extract the frame LPVOID bmdata = AVIStreamGetFrame(aviFrame, pos); if(!bmdata) return false; // copy extracted frame data into dibsection memory int offset = sizeof(BITMAPINFOHEADER); // no color table for 16 or 24-bit bitmaps memmove((char*)aviFrameMemory, (char*)bmdata + offset, aviFrameTotalBytes); return true;}void FourCCToString(DWORD code, char* buffer){ buffer[0] = (char)((code)       & 0xff); buffer[1] = (char)((code >>  8) & 0xff); buffer[2] = (char)((code >> 16) & 0xff); buffer[3] = (char)((code >> 24) & 0xff); buffer[4] = '\0';}
weasalmongler
weasalmongler
Well, I've compared your code to mine, and the only difference I can think of is that I am testing my code on the Beta Release of windows Vista, I will reboot my pc at some point and test on XP, but I've just run into some problems on that as well.

Do you think that the different operating system would mess it up?
weasalmongler
weasalmongler
OK, no problem any more guys. I just specified to have the Bitmap brought back in 24 bit colour and that didn't have any problems. I still haven't sorted the 16 bit problem though, it's a bit weird.

Thanks anyway to everyone though.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.