/* VIDEO.C */
/* by S.Metrot@Cryo-interactive.com */

#include "x/Version.h"
#include "x/scolplugin.h"

#include <stdio.h>
#include <VFW.h>

#include <stdlib.h>
#include <malloc.h>

#include "objstr.h"

int WM_VIDEO_FRAME;
int OBJTYPVIDEO;

/* definition des reflexes pour la capture video */
#define RFLVIDEO_NB 1
#define RFLVIDEO_ONFRAME 0



static volatile int dummy=0; /* Semaphore !!! */

typedef struct capture 
{
  HWND h;
  CAPDRIVERCAPS CapDrvCaps; 
  CAPTUREPARMS CapParms;
  BITMAPINFO bmInfoSrc;
  BITMAPINFO bmInfoTmp;
  BITMAPINFO bmInfoDst;
  BOOL convertdest;
  HANDLE hIC;
  int format;
  char* lpData;
  char* lpTemp;
} capture;

int _ENUMCapDevices (mmachine m)
{
  char szDeviceName[255],szDeviceVersion[255];
  if (capGetDriverDescription (MMpull(m)>>1, szDeviceName, 
      sizeof (szDeviceName), szDeviceVersion, 
      sizeof (szDeviceVersion)))
  {
    Mpushstrbloc(m,szDeviceName);
  }
  else MMpush(m,NIL);
  return 0;
}

int fooBmiHeader(char* title,BITMAPINFOHEADER *h)
{
	MMechostr(1,">>>>>%s\n",title);
	MMechostr (1,"bmiHeader.biSize:         %d\n",h->biSize);
	MMechostr (1,"bmiHeader.biWidth:        %d\n",h->biWidth);
	MMechostr (1,"bmiHeader.biHeight:       %d\n",h->biHeight);
	MMechostr (1,"bmiHeader.biPlanes:       %d\n",h->biPlanes);
	MMechostr (1,"bmiHeader.biBitCount:     %d\n",h->biBitCount);
	MMechostr (1,"bmiHeader.biCompression:  %d\n",h->biCompression);
	MMechostr (1,"bmiHeader.biSizeImage:    %d\n",h->biSizeImage);
	MMechostr (1,"bmiHeader.biClrUsed:      %d\n",h->biClrUsed);
	MMechostr (1,"bmiHeader.biClrImportant: %d\n",h->biClrImportant);
	return 0;
}
int _CRCapWindow (mmachine m)
{
  HWND wn;
  PtrObjWindow Window;
  PtrObjVoid OB;
  short wIndex;
  char szDeviceName[255];
  char szDeviceVersion[255];
  RECT r;
  int obj;

  int framerate,x,y,w,hh,xx,yy;
  int visible;
  int drv;
  capture* cap;

  framerate=MMpull(m)>>1;
  visible=MMpull(m)>>1;
  hh=MMpull(m)>>1;
  w=MMpull(m)>>1;
  y=MMpull(m)>>1;
  x=MMpull(m)>>1;
  drv=MMpull(m)>>1;

  if (MMget(m,0)!=NIL) 
  {
    OB = ( PtrObjVoid ) MMstart(m,MMpull(m)>>1) ;
    Window = ( PtrObjWindow ) MMstart (m,OB->Buffer>>1);
    wn=Window->WHandler;
  }
  else
  {
    MMpull(m);
    wn=NULL;
  }
/*  wn=NULL;*/
  
  cap=(capture*)malloc(sizeof(capture));
//  MMechostr(MSKDEBUG,"CAPTURE VAR: cap=%x\n",cap);
  if (!cap) 
  {
    MMechostr (1,"Error on capture memory allocation\n");
    MMset(m,0,NIL);
    return 0;
  }

/*  h=capCreateCaptureWindow("Ma fenetre de capture",WS_VISIBLE,700,100,390,315,NULL,0); */

//  MMechostr(MSKDEBUG,"sizecapture %d %d\n",w,hh);
  cap->h=capCreateCaptureWindow("SCOL Video Monitor",(WS_CHILD*(wn!=NULL))|(WS_VISIBLE*(visible>0)),x,y,w,hh,wn,0); 
  if (!cap->h) 
  {
    MMechostr (1,"Error on capCreateCaptureWindow\n");
    free(cap);
    MMset(m,0,NIL);
    return 0;
  }
  GetClientRect(cap->h,&r);
  xx=w-r.right;
  yy=hh-r.bottom;
  SetWindowPos(cap->h,NULL,x,y,w+xx,hh+yy,0);
 
  SetWindowLong(cap->h,GWL_USERDATA,(long)cap);

  for (wIndex = 0; wIndex < 10; wIndex++) 
    if (capGetDriverDescription (wIndex, szDeviceName, 
        sizeof (szDeviceName), szDeviceVersion, 
        sizeof (szDeviceVersion)))
      MMechostr (1,"%d : Device : %s   description : %s\n",wIndex,szDeviceName,szDeviceVersion);

  if (!capDriverConnect(cap->h,drv)) 
  {
    DestroyWindow(cap->h);
    MMechostr (1,"Error on capDriverConnect\n");
    free(cap);
    MMset(m,0,NIL);
    return 0;
  }

  if (!capDriverGetCaps(cap->h, &cap->CapDrvCaps, sizeof (CAPDRIVERCAPS))) 
  {
    capDriverDisconnect(cap->h);
    DestroyWindow(cap->h);
    MMechostr (1,"Error on capDriverGetCaps\n");
    free(cap);
    MMset(m,0,NIL);
    return 0;
  }
/*  else
  {
    MMechostr(MSKDEBUG,"::CapDrvCaps.wDeviceIndex:             %d\n",cap->CapDrvCaps.wDeviceIndex); 
    MMechostr(MSKDEBUG,"::CapDrvCaps.fHasOverlay:              %s\n",cap->CapDrvCaps.fHasOverlay?"TRUE":"FALSE"); 
    MMechostr(MSKDEBUG,"::CapDrvCaps.fHasDlgVideoSource:       %s\n",cap->CapDrvCaps.fHasDlgVideoSource?"TRUE":"FALSE"); 
    MMechostr(MSKDEBUG,"::CapDrvCaps.fHasDlgVideoFormat:       %s\n",cap->CapDrvCaps.fHasDlgVideoFormat?"TRUE":"FALSE");
    MMechostr(MSKDEBUG,"::CapDrvCaps.fHasDlgVideoDisplay:      %s\n",cap->CapDrvCaps.fHasDlgVideoDisplay?"TRUE":"FALSE");
    MMechostr(MSKDEBUG,"::CapDrvCaps.fCaptureInitialized:      %s\n",cap->CapDrvCaps.fCaptureInitialized?"TRUE":"FALSE");
    MMechostr(MSKDEBUG,"::CapDrvCaps.fDriverSuppliesPalettes:  %s\n",cap->CapDrvCaps.fDriverSuppliesPalettes?"TRUE":"FALSE");
    MMechostr(MSKDEBUG,"::CapDrvCaps.hVideoIn:                 %x\n",cap->CapDrvCaps.hVideoIn); 
    MMechostr(MSKDEBUG,"::CapDrvCaps.hVideoOut:                %x\n",cap->CapDrvCaps.hVideoOut); 
    MMechostr(MSKDEBUG,"::CapDrvCaps.hVideoExtIn:              %x\n",cap->CapDrvCaps.hVideoExtIn); 
    MMechostr(MSKDEBUG,"::CapDrvCaps.hVideoExtOut:             %x\n",cap->CapDrvCaps.hVideoExtOut); 

  }
*/
  if (visible) capPreviewRate(cap->h,framerate/1000);     // rate, in milliseconds

  capCaptureGetSetup(cap->h, &cap->CapParms, sizeof (CAPTUREPARMS)); 
/*
  MMechostr(MSKDEBUG,"::::CapParms.fAbortLeftMouse:            %s\n",cap->CapParms.fAbortLeftMouse?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.fAbortRightMouse:           %s\n",cap->CapParms.fAbortRightMouse?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.fStepCaptureAt2x:           %s\n",cap->CapParms.fStepCaptureAt2x?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.fCaptureAudio:              %s\n",cap->CapParms.fCaptureAudio?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.fMakeUserHitOKToCapture:    %s\n",cap->CapParms.fMakeUserHitOKToCapture?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.dwRequestMicroSecPerFrame:  %d\n",cap->CapParms.dwRequestMicroSecPerFrame);
  MMechostr(MSKDEBUG,"::::CapParms.wPercentDropForError:       %d\n",cap->CapParms.wPercentDropForError);
  MMechostr(MSKDEBUG,"::::CapParms.fYield:                     %s\n",cap->CapParms.fYield?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.dwIndexSize:                %d\n",cap->CapParms.dwIndexSize);
  MMechostr(MSKDEBUG,"::::CapParms.wChunkGranularity:          %d\n",cap->CapParms.wChunkGranularity);
  MMechostr(MSKDEBUG,"::::CapParms.fUsingDOSMemory:            %s\n",cap->CapParms.fUsingDOSMemory?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.wNumVideoRequested:         %d\n",cap->CapParms.wNumVideoRequested);
  MMechostr(MSKDEBUG,"::::CapParms.wNumAudioRequested:         %d\n",cap->CapParms.wNumAudioRequested);
  MMechostr(MSKDEBUG,"::::CapParms.fLimitEnabled:              %s\n",cap->CapParms.fLimitEnabled?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.wTimeLimit:                 %d\n",cap->CapParms.wTimeLimit);
  MMechostr(MSKDEBUG,"::::CapParms.fMCIControl:                %s\n",cap->CapParms.fMCIControl?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.fStepMCIDevice:             %s\n",cap->CapParms.fStepMCIDevice?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.wStepCaptureAverageFrames:  %d\n",cap->CapParms.wStepCaptureAverageFrames);
  MMechostr(MSKDEBUG,"::::CapParms.dwAudioBufferSize:          %d\n",cap->CapParms.dwAudioBufferSize);
  MMechostr(MSKDEBUG,"::::CapParms.fDisableWriteCache:         %s\n",cap->CapParms.fDisableWriteCache?"TRUE":"FALSE");
  MMechostr(MSKDEBUG,"::::CapParms.AVStreamMaster:             %d\n",cap->CapParms.AVStreamMaster);
*/
  cap->CapParms.fAbortLeftMouse=FALSE;
  cap->CapParms.fAbortRightMouse=FALSE;
  cap->CapParms.fStepCaptureAt2x=TRUE;
  cap->CapParms.fCaptureAudio=FALSE;
  cap->CapParms.fMakeUserHitOKToCapture=FALSE;
  cap->CapParms.dwRequestMicroSecPerFrame=framerate;
  cap->CapParms.wPercentDropForError=0;
  cap->CapParms.fYield=TRUE;
 
  cap->CapParms.dwIndexSize=0; 
  cap->CapParms.wChunkGranularity=0; 
  cap->CapParms.fUsingDOSMemory=TRUE; 
  cap->CapParms.wNumVideoRequested=10; 
  cap->CapParms.wNumAudioRequested=0; 
  cap->CapParms.vKeyAbort=0; 
  cap->CapParms.fLimitEnabled=FALSE; 
  cap->CapParms.wTimeLimit=0; 
  cap->CapParms.fMCIControl=FALSE; 
  cap->CapParms.fStepMCIDevice=FALSE; 
//  CapParms.dwMCIStartTime; 
//  CapParms.dwMCIStopTime; 
  cap->CapParms.wStepCaptureAverageFrames=5; 
  cap->CapParms.dwAudioBufferSize=0; 
  cap->CapParms.fDisableWriteCache=FALSE; 
  cap->CapParms.AVStreamMaster=AVSTREAMMASTER_NONE; 
  
  if (!capCaptureSetSetup(cap->h, &cap->CapParms, sizeof (CAPTUREPARMS))) 
  {
    capDriverDisconnect(cap->h);
    DestroyWindow(cap->h);
    MMechostr (1,"Error on capCaptureSetup\n");
    free(cap);
    MMset(m,0,NIL);
    return 0;
  }

//  capDlgVideoDisplay(cap->h);
//  capDlgVideoCompression(cap->h);
//  capDlgVideoSource(cap->h);
//  capDlgVideoFormat(cap->h);

  capGetVideoFormat(cap->h,&cap->bmInfoSrc,sizeof(BITMAPINFOHEADER));

  cap->bmInfoDst.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
  cap->bmInfoDst.bmiHeader.biWidth=w;
  cap->bmInfoDst.bmiHeader.biHeight=hh;
  cap->bmInfoDst.bmiHeader.biPlanes=1;
  cap->bmInfoDst.bmiHeader.biBitCount=16;
  cap->bmInfoDst.bmiHeader.biCompression=BI_RGB;
  //  bmInfoDst.bmiHeader.biCompression=BI_BITFIELDS;
  cap->bmInfoDst.bmiHeader.biSizeImage=0;
  cap->bmInfoDst.bmiHeader.biClrUsed=0;
  cap->bmInfoDst.bmiHeader.biClrImportant=0;
/*
  if (!capSetVideoFormat(cap->h,&cap->bmInfoDst,sizeof(BITMAPINFOHEADER)))
  {
    capGetVideoFormat(cap->h,&cap->bmInfoSrc,sizeof(BITMAPINFOHEADER));
    cap->bmInfoDst.bmiHeader.biPlanes=1;
    cap->bmInfoDst.bmiHeader.biBitCount=16;
    if (!capSetVideoFormat(cap->h,&cap->bmInfoDst,sizeof(BITMAPINFOHEADER)))
      cap->convertdest=TRUE;
//    capDriverDisconnect(cap->h);
    DestroyWindow(cap->h);
    MMechostr (1,"Error on capSetVideoFormat\n");
    free(cap);
    MMset(m,0,NIL);
    return 0;//
  }
  else
  {
    cap->convertdest=FALSE;
  }*/

  cap->hIC=NULL;

  if (visible)
    if (cap->CapDrvCaps.fHasOverlay) 
      capOverlay(cap->h, TRUE);  
    else
    {
      capPreview(cap->h, TRUE);       // starts preview 
      capPreviewScale(cap->h,TRUE);
    }

  obj=MMmalloc(m,1,TYPEBUF); if (obj==NIL) return MERRMEM;
  MMstore(m,obj,0,(long)cap);

  if (MMpush(m,obj*2+1) ) 
  {
    MMset(m,0,NIL);
    return MERRMEM;
  }

  cap->format=0;
  cap->lpData=NULL;
  cap->lpTemp=NULL;
  return OBJcreate(m,OBJTYPVIDEO,(long)cap,-1,0);
}

int _DSCapWindow (mmachine m)
{
  capture *cap;
  int obj;

  obj=MMget(m,0)>>1;
  if (obj==NIL) return 0;
  cap=(capture*)MMfetch(m,obj,0);
  OBJdelTH(m,OBJTYPVIDEO,(LONG)cap);
  
//  free(cap); // SH000406
  
  MMset(m,0,NIL);
  return 0;
}

int DestroyVideo (mmachine m,capture* cap)
{
  if (cap==NULL) return 0;

  if (cap->CapDrvCaps.fHasOverlay) 
  {
    capOverlay(cap->h, FALSE);  
  }
  else
  {
    capPreview(cap->h, FALSE);       // stop preview 
    capPreviewScale(cap->h,FALSE);
  }
  
  capDriverDisconnect(cap->h);
  DestroyWindow(cap->h);

  if (cap->hIC)
  {
    ICDecompressEnd(cap->hIC); 
    ICClose(cap->hIC);
    cap->hIC=NULL;
  }

  if (cap->lpData) free(cap->lpData);
  if (cap->lpTemp) free(cap->lpTemp);
//  MMechostr(MSKDEBUG,"Freeing capture memory %x.\n",cap);
  free(cap); // SH000406
  return 0;
}

LRESULT PASCAL ErrorCallbackProc(HWND hWnd, int nErrID, LPSTR lpErrorText) 
{ 
//  char gachBuffer [255];

    if (nErrID == 0)            // Starting a new major function. 
        return TRUE;            // Clear out old errors. 
 
    // Show the error identifier and text. 
//    wsprintf(gachBuffer, "Error# %d", nErrID); 
//    MessageBox(hWnd, lpErrorText, gachBuffer, MB_OK | MB_ICONEXCLAMATION); 

    MMechostr(MSKDEBUG,"Error# %d: %s\n",nErrID,lpErrorText);
 
    return (LRESULT) TRUE; 
} 


LRESULT CALLBACK capVideoStreamCallback (HWND h,LPVIDEOHDR lpVHdr)
{
  LPVIDEOHDR lpVHdr2;
//  int t;

  if (dummy) return (LRESULT) TRUE;

//  	MMechostr(1,"start cb Frame\n");
//	t=GetTickCount();

  dummy=1;
  lpVHdr2=(LPVIDEOHDR) malloc (sizeof(VIDEOHDR));

  if (lpVHdr2==NULL) return (LRESULT) TRUE;
  memcpy(lpVHdr2,lpVHdr,sizeof(VIDEOHDR));
  
  lpVHdr2->lpData=(LPBYTE) malloc(lpVHdr->dwBufferLength);
  if (!lpVHdr2->lpData) 
  {
//    MMechostr(MSKDEBUG,"No mem to copy video buffer!!\n");
    free (lpVHdr2);
    return (LRESULT) TRUE;
  }

  memcpy(lpVHdr2->lpData,lpVHdr->lpData,lpVHdr->dwBufferLength);

  PostMessage ((HWND)SCgetExtra("hscol"),WM_VIDEO_FRAME,(UINT)GetWindowLong(h,GWL_USERDATA),(LONG)lpVHdr2);
//	MMechostr(1,"process Frame %d ms\n",GetTickCount()-t);
  return (LRESULT) TRUE;
}


//$LB TODO ??
void StretchBitmap (int w1,int h1, OBJBITMAP_BUFFER data1,int w2,int h2, OBJBITMAP_BUFFER data2)
{
  int x,y,xx,yy,xinc,yinc,p,offs;


  xinc=(w1<<16)/w2;
  yinc=(h1<<16)/h2;

  yy=0;
  p=0;
  for (y=0;y<h2;y++)
  {
    xx=0;
	offs=w1*(yy>>16);
    for (x=0;x<w2;x++)
    {
      data2[p++]=data1[offs+(xx>>16)];
      xx+=xinc;
    }
    yy+=yinc;
  }
}


//$LB (17/12/2002)
int convbuf24to24(OBJBITMAP_BUFFER dst, char *src, int size)
{
	int i;
//	MMechostr(1,"conv24to15 %d\n",size);
	for(i=0;i<size;i+=3)
	{
		*(dst++) = src[i]&255;
		*(dst++) = src[i+1]&255;
		*(dst++) = src[i+2]&255;
	}
	return 0;
}

int _ProcessVideoFrame2 (mmachine m,capture* cap,LPVIDEOHDR lpVHdr)
{
  int k,flagok;
  DWORD dwBufferLength;

//  MMechostr (1,"----ProcessVideoFrame\n");

  k=OBJbeginreflex(m,OBJTYPVIDEO,(LONG)cap,RFLVIDEO_ONFRAME);
  if (k==0)
  {
	  //  MMechostr (1,"----ProcessVideoFrame\n");
	  dwBufferLength=lpVHdr->dwBufferLength;
	  flagok=0;
	  //    MMechostr(MSKDEBUG,"---->> Converting Video Frame...\n");
	  
	  //    MMechostr(MSKDEBUG,"----Initialize the bitmap structure\n");
	  
//	  MMechostr(MSKDEBUG,"---->> hIC=%d type=%x bitcount=%d\n",cap->hIC,cap->bmInfoSrc.bmiHeader.biCompression,cap->bmInfoSrc.bmiHeader.biBitCount);
	  if (!cap->hIC)
	  {
		  if (cap->bmInfoSrc.bmiHeader.biCompression==BI_RGB)
		  {
			  if (cap->bmInfoSrc.bmiHeader.biBitCount==16)
			  {
				  memcpy(cap->lpTemp,lpVHdr->lpData,cap->bmInfoSrc.bmiHeader.biSizeImage);
				  flagok=1;
			  }
			  else if (cap->bmInfoSrc.bmiHeader.biBitCount==24)
			  {
				  convbuf24to24((OBJBITMAP_BUFFER)cap->lpTemp,lpVHdr->lpData,cap->bmInfoSrc.bmiHeader.biSizeImage);
				  flagok=1;
			  }
		  }
	  }
	  else if (ICDecompress(cap->hIC, 0, &cap->bmInfoSrc.bmiHeader, lpVHdr->lpData, &cap->bmInfoTmp.bmiHeader,cap->lpTemp)
		  == ICERR_OK) 
	  { 
		  flagok=1;
	  }

	  if (flagok)
	  {
		  StretchBitmap (cap->bmInfoSrc.bmiHeader.biWidth,cap->bmInfoSrc.bmiHeader.biHeight,(OBJBITMAP_BUFFER)cap->lpTemp,
			  cap->bmInfoDst.bmiHeader.biWidth,cap->bmInfoDst.bmiHeader.biHeight,(OBJBITMAP_BUFFER)cap->lpData);
		  dwBufferLength=(cap->bmInfoDst.bmiHeader.biWidth * cap->bmInfoDst.bmiHeader.biHeight * 3);
		  Mpushstrblocn(m,cap->lpData,dwBufferLength);
	  } 
	  else 
	  { 
		  MMechostr(MSKDEBUG,"----Frame ERROR\n");
		  MMpush(m,NIL);
	  }
	  OBJcallreflex(m,1);
  }
  return 0;
}

int _ProcessVideoFrame (mmachine m,capture* cap,LPVIDEOHDR lpVHdr)
{
	int k;

//	MMechostr(1,"start process Video\n");
//	t=GetTickCount();
	k=_ProcessVideoFrame2 (m,cap,lpVHdr);
	free(lpVHdr->lpData);
    free(lpVHdr);
//	t=GetTickCount()-t;
//	MMechostr(1,"process Video %d ms\n",t);
	dummy=0;
	return k;
}


int _SETcapVideoStart (mmachine m)
{
  capture* cap;
  int obj;
  
  obj=MMget(m,2)>>1;
  
/*  MMechostr(MSKDEBUG,"HWND: %X\n",(LONG)h);*/
  MMechostr(MSKDEBUG,"SetCapVideoStart\n");
  if (obj==NIL) 
  {
    MMpull(m);
    MMpull(m);
    MMset(m,0,NIL);
    MMechostr(MSKDEBUG,"Capture ERROR on add reflex : object nil\n");
    return 0;
  }

  cap=(capture*)MMfetch(m,obj,0);
  if (!cap)
  {
    MMechostr(MSKDEBUG,"Capture ERROR on add reflex : object nil\n");
    MMpull(m);
    MMpull(m);
    MMset(m,0,NIL);
    return 0;
  }

//  MMechostr(MSKDEBUG,"SetCapVideoStart: cap=%x\n",cap);
//  MMechostr(MSKDEBUG,"SetCapVideoStart: capSetCallbackOnVideoStream\n");

  if (cap->format)
  {
    MMechostr(MSKDEBUG,"Capture ERROR , close format or source window before capture...\n");
    MMpull(m);
    MMpull(m);
    MMset(m,0,NIL);
    return 0;
  };

  if (!capSetCallbackOnVideoStream(cap->h,&capVideoStreamCallback)) 
  {
    MMechostr(MSKDEBUG,"Capture ERROR on set callback1\n");
    MMpull(m);
    MMpull(m);
    MMset(m,0,NIL);
    return 0;
  };

//  MMechostr(MSKDEBUG,"SetCapVideoStart: capSetCallbackOnError\n");
  if (!capSetCallbackOnError(cap->h,&ErrorCallbackProc)) 
  {
    MMechostr(MSKDEBUG,"Capture ERROR on set callback2\n");
    MMpull(m);
    MMpull(m);
    MMset(m,0,NIL);
    return 0;
  };

  memcpy(&cap->bmInfoTmp,&cap->bmInfoDst,sizeof(BITMAPINFO));
  cap->bmInfoTmp.bmiHeader.biWidth  = cap->bmInfoSrc.bmiHeader.biWidth;
  cap->bmInfoTmp.bmiHeader.biHeight = cap->bmInfoSrc.bmiHeader.biHeight;
  cap->bmInfoTmp.bmiHeader.biSizeImage = cap->bmInfoTmp.bmiHeader.biWidth*cap->bmInfoTmp.bmiHeader.biHeight * 3;
  
  //  if (cap->convertdest)	// SH000406
  //  {
  fooBmiHeader("locate src",(LPBITMAPINFOHEADER)&cap->bmInfoSrc);
  fooBmiHeader("locate dst",(LPBITMAPINFOHEADER)&cap->bmInfoTmp);
  cap->hIC = ICLocate (ICTYPE_VIDEO,0L,(LPBITMAPINFOHEADER)&cap->bmInfoSrc,(LPBITMAPINFOHEADER)&cap->bmInfoTmp,ICMODE_DECOMPRESS/*ICMODE_FASTDECOMPRESS*/); 

  if (!cap->hIC)
  {
      MMechostr(MSKDEBUG,"Cannot find decompressor... hope it is rgb\n");
  }

  if (cap->lpData) free(cap->lpData);
  cap->lpData = malloc(cap->bmInfoDst.bmiHeader.biWidth*cap->bmInfoDst.bmiHeader.biHeight * 3);
  if (!cap->lpData) 
  {
      MMechostr(MSKDEBUG,"----Error creating video buffer...:-( \n");
      MMpull(m);
      MMpull(m);
      MMset(m,0,NIL);
      return 0;
  }
  if (cap->lpTemp) free(cap->lpTemp);
  cap->lpTemp=malloc(cap->bmInfoTmp.bmiHeader.biWidth*cap->bmInfoTmp.bmiHeader.biHeight *3);
  if (!cap->lpTemp) 
  {
      MMechostr(MSKDEBUG,"----Error creating temp video buffer...:-( \n");
      MMpull(m);
      MMpull(m);
      MMset(m,0,NIL);
      return 0;
  }
  
  //    MMechostr(MSKDEBUG,"----Located\n");
  // Assume lpbiIn and lpbiOut are initialized to the input and output 
  // format and lpIn and lpOut are pointing to the buffers. 

  if ((cap->hIC)&&(ICDecompressBegin(cap->hIC, &cap->bmInfoSrc, &cap->bmInfoTmp) != ICERR_OK))
  {
      MMechostr(MSKDEBUG,"Capture ERROR ICDecompressBegin\n");
      ICClose(cap->hIC);
      MMpull(m);
      MMpull(m);
      MMset(m,0,NIL);
      return 0;
  }
//  }

//  MMechostr(MSKDEBUG,"SetCapVideoStart: capCaptureSequenceNoFile\n");
  if (!capCaptureSequenceNoFile(cap->h))
/*  if (!capCaptureSequence(h))*/
  {
    MMechostr(MSKDEBUG,"Capture ERROR on start capture no file\n");
    MMpull(m);
    MMpull(m);
    MMset(m,0,NIL);
    return 0;
  };

//  MMechostr(MSKDEBUG,"SetCapVideoStart: OBJaddreflex\n");
	if (OBJaddreflex(m,OBJTYPVIDEO,RFLVIDEO_ONFRAME))
  {
    MMechostr(MSKDEBUG,"Capture ERROR on add reflex\n");
    MMset(m,0,NIL);
    return 0;
  }

  cap->format=1;
//  MMechostr(MSKDEBUG,"Capture started allright\n");
  return 0;
}

int _SETcapVideoStop (mmachine m)
{
  capture* cap;
  int obj;
  
  obj=(MMget(m,0)>>1);
  
  MMechostr(MSKDEBUG,"SetCapVideoStop\n");
  if (MMget(m,2)>>1==NIL) 
  {
    MMechostr(MSKDEBUG,"Capture ERROR on add reflex : object nil\n");
    return 0;
  }

  cap=(capture*)MMfetch(m,obj,0);
  if (!cap)
  {
    MMset(m,0,NIL);
    return 0;
  }

  if (cap->hIC)
  {
    ICDecompressEnd(cap->hIC); 
    ICClose(cap->hIC);
  }

  capCaptureStop(cap->h);
  cap->format=0;
  
  return 0;
}


DWORD WINAPI VideoFormatThread(LPVOID lpParameter)
{
  capture* cap=lpParameter;
  
  capDlgVideoFormat(cap->h);
  capGetVideoFormat(cap->h,&cap->bmInfoSrc,sizeof(BITMAPINFOHEADER));
  cap->format=0;
  ExitThread(0);
  return 0;
}

int _DLGcapFormat (mmachine m)
{
  capture* cap;
  int obj;
  DWORD tid;
  
  obj=(MMget(m,0)>>1);
  
  MMechostr(MSKDEBUG,"_DLGcapFormat\n");

  cap=(capture*)MMfetch(m,obj,0);
  if (!cap)
  {
    MMset(m,0,NIL);
    return 0;
  }

  if (!(cap->format))
  {
    cap->format=1;
    CreateThread(NULL,0,VideoFormatThread,cap,0,&tid);
  }
  
  return 0;
}

DWORD WINAPI VideoSourceThread(LPVOID lpParameter)
{
  capture* cap=lpParameter;
  
  capDlgVideoSource(cap->h);
  capGetVideoFormat(cap->h,&cap->bmInfoSrc,sizeof(BITMAPINFOHEADER));
  cap->format=0;
  ExitThread(0);
  return 0;
}

int _DLGcapSource (mmachine m)
{
  capture* cap;
  int obj;
  DWORD tid;
  
  obj=(MMget(m,0)>>1);
  
  MMechostr(MSKDEBUG,"_DLGcapSource\n");

  cap=(capture*)MMfetch(m,obj,0);
  if (!cap)
  {
    MMset(m,0,NIL);
    return 0;
  }

  if (!(cap->format))
  {
    cap->format=1;
    CreateThread(NULL,0,VideoSourceThread,cap,0,&tid);
  }
  
  return 0;
}

int _InvertCapBitmap (mmachine m)
{
  int s,llen,slen,nlines,n;
  char *S;
  char *temp;

  llen=MMpull(m);
  s=MMget(m,0)>>1;
  if (s==NIL || llen<=0) 
    return 0;

  S=MMstartstr(m,s);
  if (!S) 
  {
    MMset(m,0,NIL);
    return 0;
  }

  slen=MMsizestr(m,s);

  nlines=slen/llen;
  
  if ((nlines*llen)!=slen)
  {
    MMset(m,0,NIL);
    return 0;
  }

  temp=malloc(llen);
  if (!temp)
    return 0;

  for (n=0;n<(nlines/2);n++)
  {
    memcpy(temp,S+n*llen,llen);
    memcpy(S+n*llen,S+(nlines-n-1)*llen,llen);
    memcpy(S+(nlines-n-1)*llen,temp,llen);
  }

  free(temp);

  return 0;
}


//$LB (17/12/2002) : 16bits to 24bits : do a simple copy 
// instead of a color conversion
int c15to24 (mmachine m)
{
  int s1,len1,len2,i;
  OBJBITMAP_BUFFER S1;
  OBJBITMAP_BUFFER S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=(OBJBITMAP_BUFFER)MMstartstr(m,s1);

  len1=MMsizestr(m,s1);
  len2=(len1);
  S2=malloc(len2);

  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1);i+=3)
  {
    S2[i]   = S1[i];
    S2[i+1] = S1[i+1];
    S2[i+2] = S1[i+2];
/*    MMechostr (0,"pixel #%d -- Source: %X  Result %X %X %X\n",i,S1[i],S2[i],S2[i+len1/2],S2[i+len1]);*/

  }

  i= Mpushstrblocn(m,S2,len2);
  free (S2);
  return i;
}



//$LB (17/12/2002) : 16bits to 24bits : do a simple copy 
// instead of a color conversion
int c24to15 (mmachine m)
{
  int s1,len1,len2,i;
  OBJBITMAP_BUFFER S1;
  OBJBITMAP_BUFFER S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=MMstartstr(m,s1);

  len1 = MMsizestr(m,s1);
  len2 = len1;
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<len1;i+=3)
  {
    S2[i]   = S1[i];
    S2[i+1] = S1[i+1];
    S2[i+2] = S1[i+2];
  }

  i= Mpushstrblocn(m,(char*)S2,len2);
  free (S2);
  return i;
}



//$LB (17/12/2002) : 16bits to 24bits : 15to32 becomes 24to32
int c15to32 (mmachine m)
{
  int s1,len1,len2,i;
  OBJBITMAP_BUFFER S1;
  char *S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1 = (OBJBITMAP_BUFFER) MMstartstr(m,s1);

  len1=MMsizestr(m,s1);
  len2=(len1/3*4);
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1);i+=3)
  {
    S2[i*4]   = S1[i];
    S2[i*4+1] = S1[i+1];
    S2[i*4+2] = S1[i+2];
    S2[i*4+3] = 0;
/*    MMechostr (0,"pixel #%d -- Source: %X  Result %X %X %X\n",i,S1[i],S2[i],S2[i+len1/2],S2[i+len1]);*/

  }

  i= Mpushstrblocn(m,S2,len2);
  free (S2);
  return i;
}



//$LB (17/12/2002) : 16bits to 24bits : 32to15 becomes 32to24
int c32to15 (mmachine m)
{
  int s1,len1,len2,i;
  char *S1;
  OBJBITMAP_BUFFER S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=MMstartstr(m,s1);

  len1 = MMsizestr(m,s1);
  len2 = len1;
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1/4*3);i+=3)
  {          
    S2[i]   = S1[i*4+0];
    S2[i+1] = S1[i*4+1];
    S2[i+2] = S1[i*4+2];
  }

  i= Mpushstrblocn(m,(char*)S2,len2);
  free (S2);
  return i;
}

int DifStr (mmachine m)
{
  int s1,s2,len,len2,i;
  char *S1;
  char *S2;
  char *D;

  s2=MMpull (m)>>1;
  s1=MMpull (m)>>1;

  if ((s1==NIL)||(s2==NIL)) return MMpush(m,NIL);

  S1=MMstartstr(m,s1);
  S2=MMstartstr(m,s2);

  len=MMsizestr(m,s1);
  len2=MMsizestr(m,s1);
  if (len>len) len=len2;

  D=malloc(len);
  if (D==NULL) return MMpush(m,NIL);

//  for (i=0;i<len;i++)
//    if (i&1) D[i]=S2[i]>>2;
//    else D[i]=S2[i]>>4;

  for (i=0;i<len;i++)
  {
/* EXACT DELTA !!! */
/*    if (S1[i]!=S2[i]) D[i]=S2[i];
    else D[i]=-1;*/
/* DELTA w/ Error */
/*    if (abs(S1[i]-S2[i])<16) D[i]=S2[i];
    else D[i]=-1;*/
    D[i]=(S2[i]-S1[i]);
  }

  i= Mpushstrblocn(m,D,len);
  free (D);
  return i;
}

int AddStr (mmachine m)
{
  int s1,s2,len,len2,i;
  char *S1;
  char *S2;
  char *D;

  s1=MMpull (m)>>1;
  s2=MMpull (m)>>1;

  if ((s1==NIL)||(s2==NIL)) return MMpush(m,NIL);

  S1=MMstartstr(m,s1);
  S2=MMstartstr(m,s2);

  len=MMsizestr(m,s1);
  len2=MMsizestr(m,s1);
  if (len>len) len=len2;

  D=malloc(len);
  if (D==NULL) return MMpush(m,NIL);

/*
  for (i=0;i<len;i++)
  {
    if (S1[i]<0) D[i]=S2[i];
    else D[i]=S1[i];
  }*/

//  for (i=0;i<len;i++)
//    if (i&1) D[i]=S1[i]<<2; 
//    else D[i]=S1[i]<<4;

  for (i=0;i<len;i++)
    D[i]=(S1[i]+S2[i]);

  i= Mpushstrblocn(m,D,len);
  free (D);
  return i;
}

int ScolVideo(mmachine m,HWND hwnd,unsigned msg,UINT wParam, LONG lParam,int *ret)
{
	return _ProcessVideoFrame (m,(capture*)wParam,(LPVIDEOHDR) lParam);
}

int D2VIDEO(mmachine m,int handsys,int objm)
{
    DestroyVideo (m,(struct capture *)handsys);
	MMstore(m,objm>>1,0,0);
	return 0;
}

int IniVIDEO(mmachine m)
{
	OBJTYPVIDEO=OBJregister(RFLVIDEO_NB,0,D2VIDEO,"OBJTYPVIDEO");
	WM_VIDEO_FRAME=OBJgetUserEvent();
	OBJdefEvent(WM_VIDEO_FRAME,ScolVideo);
	dummy=0;
	return 0;
}
