/* VIDEO.C */
/* by S.Metrot@Cryo-interactive.com */

//
// Modification History
//
// $LB (27/10/2004)  : the 16bits webcam data is converted to 24bits before being returned to the user.
//                     the process is still in 16bits 'cause there are a lot of changes to do, in order to turn into a total 24bits process.
//                     TODO : make the whole process in 24bits
//
//
//$LB (12/11/2004) : _c15to24, _c24to15, _c15to32, and _c32to15 become deprecated
//
//


#include "x/Version.h"

#include "x/scolplugin.h"

#include <stdio.h>
#include <VFW.h>

#include <stdlib.h>
#include <malloc.h>

#include "objstr.h"
#include "../../include/objects/bitmap.h"
#include "colors.h"

//$BLG - v5.2.06: Add
#include "../../include/blg_memory.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;
  //$LB (27/10/2004)  : added to convert the process results in 24bits
  PtrObjBitmap dst24;
} capture;



int _ENUMCapDevices(mmachine m)
{
  char szDeviceName[255], szDeviceVersion[255];
  //$BLG - v5.2.06: Add
  int n;
  
  //$BLG - v5.2.06: Modif
  /*
  if (capGetDriverDescription (MMpull(m)>>1, szDeviceName, sizeof (szDeviceName), szDeviceVersion, sizeof (szDeviceVersion)))
  {
    Mpushstrbloc(m, szDeviceName);
  }
  else MMpush(m,NIL);
  */
  n = MMpull(m)>>1;
  if ((n >= 0) && (n <= 9))
	  if (capGetDriverDescription (n, szDeviceName, sizeof(szDeviceName), szDeviceVersion, sizeof(szDeviceVersion)))
	    Mpushstrbloc(m, szDeviceName);
	  else 
	  	MMpush(m, NIL);
  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;
  }
  
  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);

	//$BLG - Note: For debug/information only
  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;
  //$BLG - v5.2.06: Modif biBitCount
  //cap->bmInfoDst.bmiHeader.biBitCount = 16;
  cap->bmInfoDst.bmiHeader.biBitCount = 24;
  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;

  //$LB (27/10/2004) 
  cap->dst24 = (PtrObjBitmap)malloc(sizeof(struct ObjBitmap));
  //$BLG - v5.2.06: Add verification
	if (!cap->dst24)
  {
    MMset(m, 0, NIL);
    return MERRMEM;
  }  
  cap->dst24->TailleH = hh;
  cap->dst24->TailleW = w;
  cap->dst24->BPP = 24; 
  cap->dst24->BytesPP = 3;
  cap->dst24->handler = ObjBitmap_New(cap->dst24, NULL);
	//$BLG - v5.2.06: Add verification
	if (!cap->dst24->handler)
  {
    MMset(m, 0, NIL);
    return MERRMEM;
  }
  //$LB //////////////

  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;
  
  //$BLG - v5.2.06: Modif
  //memcpy(lpVHdr2, lpVHdr, sizeof(VIDEOHDR));
  BLG_memcpy8(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;
  }

  //$BLG - v5.2.06: Modif
  //memcpy(lpVHdr2->lpData, lpVHdr->lpData, lpVHdr->dwBufferLength);
  BLG_memcpy8(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;
}


//$BLG - v5.2.06: Modif
/*
void StretchBitmap(int w1, int h1, short *data1, int w2, int h2, short *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;
  }
  */
void StretchBitmap(int w1, int h1, char *data1, int w2, int h2, char *data2)
{
  int x, y, xx, yy, xinc, yinc, p, offs0, offs, align;
  
  if ((w1 == w2) && (h1 == h2))
  	BLG_memcpy8(data2, data1, w1 * h1 * 3);
  else
  {
	  xinc = (w1<<16) / w2;
	  yinc = (h1<<16) / h2;
	
		if (w2 % 4) 
			align = 4 - (w2 % 4);
		else
			align = 0;
	
	  yy = 0;
	  p = 0;
	  for (y = 0; y < h2; y++, p += align)
	  {
	    xx = 0;
			offs0 = (w1 + align) * (yy>>16);
	    for (x = 0; x < w2; x++)
	    {
	      offs = 3 * (offs0 + (xx>>16));
	      data2[p++] = data1[offs++];
	      data2[p++] = data1[offs++];
	      data2[p++] = data1[offs];
	      xx += xinc;
	    }
	    yy += yinc;
  	}
  }
}


//$BLG - v5.2.06: Add
int convbuf15to24(char *dst, short *src, int size, int bpl, int w, int h)
{
  int i, j, rest;
  unsigned char r, g, b;
  
  rest = bpl - (w * 3);

  for (j = 0; j < h; j++, dst += rest)
  {
		for (i = 0; i < w; i++, dst += 3 )
		{
			_COLOR_I16_TO_BGR (src[j*w+i], &b, &g, &r);
			*(dst)   = r;
			*(dst+1) = g;
			*(dst+2) = b;
		}
  }
  
  return 0;
}


//$BLG - v5.2.06: Del (No more used)
/*
int convbuf24to15(short *dst, char *src, int size)
{
	int i, r, g, b;
//	MMechostr(1,"conv24to15 %d\n",size);
	for(i = 0; i < size; i += 3)
	{
		r = src[i] & 255;
		g = src[i+1] & 255;
		b = src[i+2] & 255;
		*(dst++) = (r>>3) + ((g>>3)<<5) + ((b>>3)<<10);
	}
	return 0;
}
*/


int _ProcessVideoFrame2(mmachine m, capture *cap, LPVIDEOHDR lpVHdr)
{
  int k, flagok;
  DWORD dwBufferLength;
  
  //$BLG - v5.2.06: Add
  int align;

//  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)
			  {
				  //$BLG - v5.2.06: Modif
				  //memcpy(cap->lpTemp, lpVHdr->lpData, cap->bmInfoSrc.bmiHeader.biSizeImage);
				  //$BLG - v5.2.06: Modif
				  //BLG_memcpy8(cap->lpTemp, lpVHdr->lpData, cap->bmInfoSrc.bmiHeader.biSizeImage);
				  convbuf15to24(cap->lpTemp, (short*)lpVHdr->lpData, cap->bmInfoSrc.bmiHeader.biSizeImage, cap->dst24->BPL, cap->dst24->TailleW, cap->dst24->TailleH);
				  
				  flagok = 1;
			  }
			  else if (cap->bmInfoSrc.bmiHeader.biBitCount == 24)
			  {
				  //$BLG - v5.2.06: Modif
				  //convbuf24to15((short*)cap->lpTemp, lpVHdr->lpData, cap->bmInfoSrc.bmiHeader.biSizeImage);
				  BLG_memcpy8(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)
	  {
		  //$BLG - v5.2.06: Del
		  /*
		  //$LB (27/10/2004)
		  short *srcptr = (short*)cap->lpData;
		  OBJBITMAP_BUFFER dstptr = (OBJBITMAP_BUFFER)cap->dst24->bits;
		  short color;
		  int i, j, rest;
		  unsigned char r, g, b;
		  */

			//$BLG - v5.2.06: Modif
			/*
		  StretchBitmap(cap->bmInfoSrc.bmiHeader.biWidth, cap->bmInfoSrc.bmiHeader.biHeight, (short*)cap->lpTemp,
			  cap->bmInfoDst.bmiHeader.biWidth, cap->bmInfoDst.bmiHeader.biHeight, (short*)cap->lpData);
		  dwBufferLength = (cap->bmInfoDst.bmiHeader.biWidth * cap->bmInfoDst.bmiHeader.biHeight * 2);
		  */
		  StretchBitmap(cap->bmInfoSrc.bmiHeader.biWidth, cap->bmInfoSrc.bmiHeader.biHeight, cap->lpTemp,
			  cap->bmInfoDst.bmiHeader.biWidth, cap->bmInfoDst.bmiHeader.biHeight, cap->dst24->bits);
			if (cap->bmInfoDst.bmiHeader.biWidth % 4)
				align = 4 - (cap->bmInfoDst.bmiHeader.biWidth % 4);
			else
				align = 0;
		  dwBufferLength = ((cap->bmInfoDst.bmiHeader.biWidth + align) * cap->bmInfoDst.bmiHeader.biHeight * 3);


			//$BLG - v5.2.06: Del (Removed/Modified 16bits step)
			/*
		  //$LB (27/10/2004) : convert 16bits to 24bits 
		  rest = cap->dst24->BPL - (cap->dst24->TailleW * 3);

		  for (j = 0; j < cap->dst24->TailleH; j++, dstptr += rest)
		  {
				for (i = 0; i < cap->dst24->TailleW; i++, dstptr += 3 )
				{
					color = srcptr [j * cap->dst24->TailleW + i];
					_COLOR_I16_TO_BGR (color, &b, &g, &r);

					(*dstptr) = r;
					*(dstptr+1) = g;
					*(dstptr+2) = b;
				}
		  }
		  */

		  Mpushstrblocn (m, cap->dst24->bits, cap->dst24->TailleH * cap->dst24->BPL);
	  } 
	  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;
  //$BLG - v5.2.06: Add
  int align;
  
  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;
  };

  //$BLG - v5.2.06: Modif
  //memcpy(&cap->bmInfoTmp, &cap->bmInfoDst, sizeof(BITMAPINFO));
  BLG_memcpy8(&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 * 2;
  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);
  //$BLG - v5.2.06: Modif
  //cap->lpData = malloc(cap->bmInfoDst.bmiHeader.biWidth * cap->bmInfoDst.bmiHeader.biHeight * 2);
  if (cap->bmInfoDst.bmiHeader.biWidth % 4)
  	align = 4 - (cap->bmInfoDst.bmiHeader.biWidth % 4);
  else
  	align = 0;
  cap->lpData = malloc((cap->bmInfoDst.bmiHeader.biWidth + align) * 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);
  //$BLG - v5.2.06: Modif
  //cap->lpTemp = malloc(cap->bmInfoTmp.bmiHeader.biWidth * cap->bmInfoTmp.bmiHeader.biHeight * 2);
  if (cap->bmInfoTmp.bmiHeader.biWidth % 4)
  	align = 4 - (cap->bmInfoTmp.bmiHeader.biWidth % 4);
  else
  	align = 0;
  cap->lpTemp = malloc((cap->bmInfoTmp.bmiHeader.biWidth + align) * 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;
}


//$LB (27/10/2004) : update to 24bits
int _InvertCapBitmap(mmachine m)
{
  int s, width, bpl, slen, nlines, n;
  char *S;
  char *temp;

  width = MMpull(m)>>1;
  
  s = MMget(m, 0)>>1;
  if ((s == NIL) || (width <= 0)) 
    return 0;

  S = MMstartstr(m, s);
  if (!S) 
  {
    MMset(m, 0, NIL);
    return 0;
  }

  slen = MMsizestr(m, s);

  bpl = width * 3;
  if (bpl % 4) bpl += 4 - (bpl % 4); // 32bits aligned

  nlines = slen / bpl;

  temp = malloc(bpl);
  if (!temp)
    return 0;

  for (n = 0; n < nlines/2; n++)
  {
    //$BLG - v5.2.06: Modif
    /*
    memcpy (temp, S+(n*bpl), bpl);
    memcpy (S+(n*bpl), S+((nlines-n-1)*bpl), bpl);
    memcpy (S+((nlines-n-1)*bpl), temp, bpl);
    */
    BLG_memcpy8(temp, S+(n*bpl), bpl);
    BLG_memcpy8(S+(n*bpl), S+((nlines-n-1)*bpl), bpl);
    BLG_memcpy8(S+((nlines-n-1)*bpl), temp, bpl);
  }

  free(temp);
  
  return 0;
} 


//$LB (12/11/2004) : becomes deprecated
int c15to24 (mmachine m)
{
	/*
  int s1,len1,len2,i,o1,o2;
  unsigned short *S1;
  char *S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=(unsigned short *)MMstartstr(m,s1);

  len1=MMsizestr(m,s1)/2;
  len2=(len1*3);
  o1=len1;
  o2=o1*2;
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1);i++)
  {
    S2[i]  =((S1[i])&31);
    S2[i+o1]=((S1[i]>>5)&31);
    S2[i+o2]=((S1[i]>>10)&31);
//    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;
  */
	return 0;
}


//$LB (12/11/2004) : becomes deprecated
int c24to15 (mmachine m)
{
	/*
  int s1,len1,len2,i,o1,o2;
  char *S1;
  unsigned short *S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=MMstartstr(m,s1);

  len1=MMsizestr(m,s1);
  len2=(len1*2)/3;
  o1=len1/3;
  o2=o1*2;
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1/3);i++)
  {
    S2[i] =((unsigned short)(S1[i+00]));
    S2[i]|=((unsigned short)(S1[i+o1]))<<5;
    S2[i]|=((unsigned short)(S1[i+o2]))<<10;
  }

  i= Mpushstrblocn(m,(char*)S2,len2);
  free (S2);
  return i;
  */
	return 0;
}


//$LB (12/11/2004) : becomes deprecated
int c15to32 (mmachine m)
{
	/*
  int s1,len1,len2,i;
  unsigned short *S1;
  char *S2;

  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=(unsigned short *)MMstartstr(m,s1);

  len1=MMsizestr(m,s1)/2;
  len2=(len1*4);
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1);i++)
  {
    S2[i*4]  =((S1[i])&31)<<3;
    S2[i*4+1]=((S1[i]>>5)&31)<<3;
    S2[i*4+2]=((S1[i]>>10)&31)<<3;
    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;
  */
  return 0;
}


//$LB (12/11/2004) : becomes deprecated
int c32to15 (mmachine m)
{

/*
  int s1,len1,len2,i;
  char *S1;
  unsigned short *S2;

  
  s1=MMpull (m)>>1;

  if (s1==NIL) return MMpush(m,NIL);

  S1=MMstartstr(m,s1);

  len1=MMsizestr(m,s1);
  len2=(len1/2);
  S2=malloc(len2);
  if (S2==NULL) return MMpush(m,NIL);

  for (i=0;i<(len1/4);i++)
  {          
    S2[i] =(((unsigned short)(S1[i*4+0])>>3)&31);
    S2[i]|=(((unsigned short)(S1[i*4+1])>>3)&31)<<5;
    S2[i]|=(((unsigned short)(S1[i*4+2])>>3)&31)<<10;
  }

  i= Mpushstrblocn(m,(char*)S2,len2);
  free (S2);
  return i;
  */
  return 0;
}


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);
  //$BLG - v5.2.06: Modif ???????????????????????????
  //if (len>len) len=len2;
  if (len2 > 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);
  //$BLG - v5.2.06: Modif ?????????????????????
  //if (len>len) len=len2;
  if (len2 > 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;
}