/* 
 * This MEX-file is an interface to the Microsoft AVIFILE routines to read 
 * compressed and uncompressed AVI files on Windows.  
 *
 * MOVIEDATA = readavi(FILENAME,INDEX) reads from the AVI file FILENAME.  If
 * INDEX is 0, all frames in the movie are read. Otherwise, only frame number
 * INDEX is read.  The MEX-file returns a MATLAB structure MOVIEDATA with 
 * fields cdata and colormap.  cdata contains frame data that must be rotated 
 * and reshaped.  colormap contains the colormap if the frame is an Indexed 
 * image.  colormap must also be massaged into the correct size.
 *
 *   Copyright 1984-2000 The MathWorks, Inc.
 *   $Revision: 1.2 $  $Date: 2000/03/10 22:05:05 $
 */

#include <windows.h>
#include <vfw.h>
#include "mex.h"

static char rcsid[] = "$Id: readavi.c,v 1.2 2000/03/10 22:05:05 clawton Exp $";

void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
{

    int	i,
        j,
        start,
        end,
        frameCount,
        formatSize,
        n;
    char *filename;
    int dims[2];
    int ColormapDims[2];
    char *fieldNames[2] = {"cdata","colormap"};
    double *index;
    PAVIFILE pfile;
    PAVISTREAM vidStream;
    HRESULT hr;
    PBITMAPINFO bi;
    LPBITMAPINFOHEADER lpbi = NULL;
    PGETFRAME pframe[1];
    mxArray *mxframe;
    mxArray *mxcolormap;
    uint8_T *frame;
    uint8_T *colormap;
#ifdef DEBUG
    HDRAWDIB	    ghdd[1];	/* drawdib handles */
    RECT        rcFrame;
    HDC hdc;
#endif

    /* Input argument checking */
    if(!mxIsChar(prhs[0]))
        {
            mexErrMsgTxt("First input to the READAVI MEX-file must be the filename.");
        }
    if(!mxIsNumeric(prhs[1]))
        {
            mexErrMsgTxt("Second input to the READAVI MEX-file must be an index number.");
        }
	
    filename = mxArrayToString(prhs[0]);
    index = mxGetPr(prhs[1]);
    n = mxGetNumberOfElements(prhs[1]);
	
    AVIFileInit();
    hr = AVIFileOpen(&pfile,filename, OF_READ, NULL);
    if(hr != AVIERR_OK)
        {
            mexErrMsgTxt("Unable to open file.");
        }
    mxFree(filename);

    hr = AVIFileGetStream(pfile, &vidStream, streamtypeVIDEO, 0);
    if(hr == AVIERR_NODATA)
        {
            mexErrMsgTxt("Unable to locate a video stream.");
        }
    else if (hr == AVIERR_MEMORY)
        {
            mexErrMsgTxt("Out of memory error when opening the video stream."); 
        }

    hr = AVIStreamFormatSize(vidStream, 0, &formatSize);
    if(hr != AVIERR_OK)
        {
            mexErrMsgTxt("Unable to read stream format.");
        }

    bi = (PBITMAPINFO) mxMalloc(formatSize);

    hr = AVIStreamReadFormat(vidStream, 0, bi, &formatSize);

    if(hr != AVIERR_OK)
        {
            mexErrMsgTxt("Unable to read stream format.");
        }
	
    bi->bmiHeader.biCompression = mmioFOURCC('N','o','n','e'); 
    
    start = AVIStreamStart(vidStream);
    if (start == -1)
        {
            mexErrMsgTxt("Unable to locate starting point of video stream.");
        }

    end = AVIStreamEnd(vidStream);
    if (end == -1)
        {
            mexErrMsgTxt("Unable to locate ending point of video stream.");
        }
    
    if(index[0] == -1)
        {
            plhs[0] = mxCreateStructMatrix(1,end-start,2,fieldNames);
        }
    else
        {
            plhs[0] = mxCreateStructMatrix(1,n,2,fieldNames);
        }


    dims[0] = 1;
		
    pframe[0] = AVIStreamGetFrameOpen(vidStream, NULL);
    if (pframe[0] == NULL)
        {
            mexErrMsgTxt("Unable to locate decompressor to decompress video stream");
        }
    
    frameCount = 0;
    for(j=0;j<n;j++)
        {
            if( index[0] != -1 )
                {
                    start = (int) index[j];
                    end = (int) index[j]+1;
                }
            
            for(i=start; i<end; i++)
                {
                    lpbi = AVIStreamGetFrame(pframe[0],i);
                    if(lpbi == NULL)
                        {
                            mexErrMsgTxt("Error getting frame data.");
                        }
                    
                    if(lpbi->biSizeImage != lpbi->biWidth*lpbi->biHeight && (lpbi->biBitCount == 8))
                        {
                            lpbi->biWidth = lpbi->biWidth + 4-(lpbi->biWidth%4);
                        }
                    else if(lpbi->biSizeImage != lpbi->biWidth*lpbi->biHeight*3 && (lpbi->biBitCount == 24))
                        {
                            lpbi->biWidth = lpbi->biWidth + 4-(lpbi->biWidth%4);
                        }
                    
                    if(lpbi->biBitCount == 8)
                        {
                            dims[1] = lpbi->biWidth*lpbi->biHeight;
                        }
                    else if(lpbi->biBitCount == 24)
                        {
                            dims[1] = lpbi->biWidth*lpbi->biHeight*3;
                        }
                    else
                        {
                            mexErrMsgTxt("Frames must be either 8-bit (Indexed) or 24-bit (TrueColor).");
                        }
                    
                    mxframe = mxCreateNumericArray(2,dims,mxUINT8_CLASS,mxREAL);
                    frame = mxGetData(mxframe);

#ifdef DEBUG
                    rcFrame.left = 111;
                    rcFrame.top = 56;
                    rcFrame.right = 201;
                    rcFrame.bottom = 122;
                    hdc = GetDC(NULL);
                    ghdd[1] = DrawDibOpen();
                    DrawDibDraw(ghdd[1],hdc,
                                rcFrame.left, rcFrame.top,
                                rcFrame.right - rcFrame.left,
                                rcFrame.bottom - rcFrame.top,
                                lpbi, NULL,
                                0, 0, -1, -1,
                                0);
                    DrawDibClose(ghdd[i]);
#endif

                    if(lpbi->biBitCount == 8)
                        {
                            ColormapDims[0] = 1;
                            ColormapDims[1] = 4*lpbi->biClrUsed;
                            mxcolormap = mxCreateNumericArray(2,ColormapDims,mxUINT8_CLASS,mxREAL);
                            colormap = mxGetData(mxcolormap);
                            memcpy(colormap,(uint8_T *)lpbi+lpbi->biSize,lpbi->biClrUsed*sizeof(RGBQUAD)*sizeof(unsigned char));
                            mxSetField(plhs[0],frameCount,fieldNames[1],mxcolormap);
                            memcpy(frame,(uint8_T *)lpbi+lpbi->biSize+lpbi->biClrUsed*sizeof(RGBQUAD),lpbi->biWidth*lpbi->biHeight*sizeof(unsigned char));
                        }
                    else if(lpbi->biBitCount == 24)
                        {
                            memcpy(frame,(uint8_T *)lpbi + lpbi->biSize,lpbi->biWidth*lpbi->biHeight*3*sizeof(unsigned char));
                        }

                    mxSetField(plhs[0],frameCount,fieldNames[0],mxframe);
                    frameCount++;
                }
	}
    mxFree(bi);

    AVIStreamGetFrameClose(pframe[0]);
    AVIStreamRelease(vidStream);
    AVIFileRelease(pfile);
}














