/******************************************************************/
/* Name: Asymrec.scr                                              */
/*                                                                */
/* This sample source code illustrates some of functions that     */
/* could be used when creating a asymmetric recorder application. */
/* or a application that uses asymmetric recording.               */
/* These sample functions can be used as provided, or they can    */
/* be modified to suit your specific needs.                       */
/*                                                                */
/* Refer to the Asymrec.h file for header information.            */
/*                                                                */
/* Note: Because this is complex sample code most error checking  */
/*       has been removed.                                        */
/*                                                                */
/*                                                                */
/* Functions :  OpenMMIO                                          */
/*              IdentifyIOProc                                    */
/*              AssociateCodec                                    */
/*              InitiateFrameStepRecord                           */
/*              CompressBuffer                                    */
/*              StartVideoThread                                  */
/*                                                                */
/******************************************************************/

/******************************************************************/
/* Name : OpenMMIO                                                */
/*                                                                */
/* This function initializes the environment for frame step       */
/* record.                                                        */
/*                                                                */
/******************************************************************/

RC OpenMMIO(PSWVRCB pCB)
{
  /**********************************************************************/
  /*  Do an MMIOOpen                                                    */
  /*  Save the mmio handle into our SWVRCB.  Use this handle later      */
  /*  to do MMIO_COMPRESS.                                              */
  /**********************************************************************/
   MMMOVIEHEADER     mmMovieHdr;                /* Movie header structure   */
   MMVIDEOHEADER     mmVideoHdr;                /* Video header structure   */
   MMAUDIOHEADER     mmAudioHdr;                /* Audio header structure   */
   LONG              lBytesWritten;             /* Number of bytes to write */
   ULONG             ulReserved=0;
   ULONG             ulFlags=0;
   USHORT            rc=0;
   MMIOINFO          mmioinfo;                  /* MMIO info structure      */


  /**********************************************************************/
  /* Identify and load the IO Proc                                      */
  /**********************************************************************/

   rc = IdentifyIOProc( (PSZ) TEMP_VIDEO_FILENAME,
                         &pCB->hmmioTemp,
                         MMIO_READWRITE |
                         MMIO_CREATE |
                         MMIO_TRANSLATEHEADER,
                         &mmioinfo );

   mmMovieHdr.ulStructLen           = sizeof(MMMOVIEHEADER);
   mmMovieHdr.ulReserved            = 0L;
   mmMovieHdr.ulMediaType           = MMIO_MEDIATYPE_MOVIE;
   mmMovieHdr.ulMovieCapsFlags      = MOVIE_HAS_VIDEO |
                                      MOVIE_HAS_AUDIO |
                                      MOVIE_CAN_SEEK  |
                                      MOVIE_CAN_SCAN;
   mmMovieHdr.ulMaxBytesPerSec      = 0L;
   mmMovieHdr.ulPaddingGranularity  = 4096;
   mmMovieHdr.ulSuggestedBufferSize = 4096;
   mmMovieHdr.ulStart               = 0L;
   mmMovieHdr.ulLength              = 100L;
   mmMovieHdr.ulNextTrackID         = 0L;
   mmMovieHdr.ulNumEntries          = 2L;
   mmMovieHdr.pmmTrackInfoList      = NULL;
   mmMovieHdr.pszMovieTitle         = NULL;
   mmMovieHdr.ulCountry             = 0L;
   mmMovieHdr.ulCodePage            = 0L;


  /**********************************************/
  /* This function is called to set the track.  */
  /**********************************************/
   SetActiveTrack ( pCB->hmmioTemp, MMIO_RESETTRACKS );

  /*****************************************/
  /* Translate test for MOVIE Header       */
  /*****************************************/
   mmioSetHeader( pCB->hmmioTemp, &mmMovieHdr, sizeof(MMMOVIEHEADER),
                  &lBytesWritten, ulReserved, ulFlags );

   mmVideoHdr.ulStructLen        = sizeof(MMVIDEOHEADER);
   mmVideoHdr.ulContentType      = MMIO_VIDEO_DATA;
   mmVideoHdr.ulMediaType        = MMIO_MEDIATYPE_DIGITALVIDEO;
   mmVideoHdr.ulVideoCapsFlags   = 0L;
   mmVideoHdr.ulWidth            = pCB->recopts.usMovieSizeX;
   mmVideoHdr.ulHeight           = pCB->recopts.usMovieSizeY;
   mmVideoHdr.ulScale            = 01L;
   mmVideoHdr.ulRate             = pCB->recopts.MovieFPS;
   mmVideoHdr.ulStart            = 0L;
   mmVideoHdr.ulLength           = 0L;
   mmVideoHdr.ulTotalFrames      = 0L;
   mmVideoHdr.ulInitialFrames    = 0L;
   mmVideoHdr.mmtimePerFrame     = 3000/pCB->recopts.MovieFPS;
   mmVideoHdr.genpalVideo.ulStartIndex    = 0L;
   mmVideoHdr.genpalVideo.ulNumColors     = 0L;
   mmVideoHdr.genpalVideo.prgb2Entries    = NULL;
   mmVideoHdr.pmmXDIBHeader               = 0L;

  /**********************************************/
  /* This function is called to set the track.  */
  /**********************************************/
   SetActiveTrack ( pCB->hmmioTemp, 0L );

  /*****************************************/
  /* Translate test for VIDEO TRACK Header */
  /*****************************************/

   mmioSetHeader( pCB->hmmioTemp, &mmVideoHdr, sizeof(MMVIDEOHEADER),
                &lBytesWritten, ulReserved, ulFlags );

   mmAudioHdr.dwHeaderLength  = sizeof(MMAUDIOHEADER);
   mmAudioHdr.dwContentType   = MMIO_AUDIO_MUSIC;
   mmAudioHdr.dwMediaType     = MMIO_MEDIATYPE_AUDIO;

  /*****************************************/
  /* Assign information to PCMWAVEFORMAT.  */
  /*****************************************/

  /*********************************************************/
  /* Assign values to - mmAudioHdr.mmXWAVHeader.WAVRHeader */
  /*********************************************************/
   mmAudioHdr.mmXWAVHeader.WAVEHeader.usFormatTag      = 01L;
   mmAudioHdr.mmXWAVHeader.WAVEHeader.usChannels       = 02L;
   mmAudioHdr.mmXWAVHeader.WAVEHeader.ulSamplesPerSec  = 22050L;
   mmAudioHdr.mmXWAVHeader.WAVEHeader.ulAvgBytesPerSec = 0L;
   mmAudioHdr.mmXWAVHeader.WAVEHeader.usBlockAlign     = 02L;
   mmAudioHdr.mmXWAVHeader.WAVEHeader.usBitsPerSample  = 16L;

  /***************************************************************/
  /* Assign values to - mmAudioHdr.mmXWAVHeader.XWAVEHeaderInfo  */
  /***************************************************************/

   mmAudioHdr.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInMS    = 0L;
   mmAudioHdr.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes = 0L;
   mmAudioHdr.mmXWAVHeader.XWAVHeaderInfo.ulReserved           = 0L;

  /**********************************************/
  /* This function is called to set the track.  */
  /**********************************************/
   SetActiveTrack ( pCB->hmmioTemp, 1L );


  /******************************************/
  /* Translate test for AUDIO TRACK Header  */
  /******************************************/
   rc = mmioSetHeader( pCB->hmmioTemp, &mmAudioHdr, sizeof(MMAUDIOHEADER),
                       &lBytesWritten, ulReserved, ulFlags );
  /**********************************************/
  /* This function is called to set the track.  */
  /**********************************************/
   SetActiveTrack ( pCB->hmmioTemp, MMIO_RESETTRACKS );
   return(rc);

}  /* OpenMMIO */

/******************************************************************/
/* Name : IdentifyIOProc                                          */
/*                                                                */
/* This function identifies the AVIO procedure.                   */
/*                                                                */
/******************************************************************/
RC   IdentifyIOProc( PSZ       fname,
                     HMMIO    *phmmio,
                     ULONG     ulOpenFlags,
                     MMIOINFO *pmmioinfo )
{
  APIRET             rc = MCIERR_SUCCESS;
  MMIOINFO           mmioinfo;
  LPMMIOPROC         pAnswer = 0;
  CHAR               LoadError[100];
  FOURCC             fcc;
  ULONG              ulrc;
  HMODULE            hmod;

  /***********************************/
  /* Open/Identify IOProc to install */
  /***********************************/
  memset(pmmioinfo, '\0', sizeof(MMIOINFO));  /* Reset mmioinfo area         */

  pmmioinfo->dwTranslate = MMIO_TRANSLATEHEADER;
  pmmioinfo->adwInfo[3] = MMIO_MEDIATYPE_MOVIE;

  rc = 0;  /* reset DosLoadModule call */

  /********************************************/
  /* Check to see if it is a known IO proc.   */
  /********************************************/
  fcc = mmioStringToFOURCC( (PSZ) "AVI ", MMIO_TOUPPER);
  if (!(pAnswer = mmioInstallIOProc(fcc, NULL, MMIO_FINDPROC)))
    {
    /********************************************/
    /* Load The IO Proc                         */
    /********************************************/
    rc = DosLoadModule( (PSZ)LoadError, sizeof(LoadError), (PSZ) "AVIO", &hmod);

    /********************************************/
    /* Obtain Procedure Entry Point             */
    /********************************************/
    if (rc = DosQueryProcAddr(hmod, 0L, (PSZ) "IOProc_Entry",
                              (PFN *)&(pmmioinfo->pIOProc)))
      {
      ulrc = DosFreeModule(hmod);
      return(rc);
      }

    /********************************************/
    /* Install the IO Proc                      */
    /********************************************/
    pAnswer = mmioInstallIOProc(fcc,
                                pmmioinfo->pIOProc,
                                MMIO_INSTALLPROC);
    if ((pAnswer != pmmioinfo->pIOProc) || (!pAnswer))
      {
      ulrc = DosFreeModule(hmod);
      return (MCIERR_CANNOT_LOAD_DRIVER);
      }
    } /* FindProc */

   /************************************************/
   /* Try to Open The Movie file using this IOProc */
   /************************************************/
  pmmioinfo->pIOProc = pAnswer;  /* Install worked! */
  pmmioinfo->fccIOProc = 0;
  pmmioinfo->dwTranslate = MMIO_TRANSLATEHEADER;
  pmmioinfo->hmmio = *phmmio;
  *phmmio = mmioOpen((PSZ) fname, pmmioinfo, ulOpenFlags );
  if (!*phmmio)
     rc = MCIERR_CANNOT_LOAD_DRIVER;
  return(rc);

}  /* end: IdentifyIOProc               */

/**********************************************************************/
/* Name : AssociateCodec                                              */
/*                                                                    */
/* This function gets the selected codec and tells MMIO to associate  */
/* it for later use in compression.  Sets up color coding             */
/* from the card.                                                     */
/*                                                                    */
/**********************************************************************/
LONG AssociateCodec( HMMIO              hmmio,
                     PSWVRCB            pCB,
                     CODECINIFILEINFO  *pcifi,
                     PVOID              pControlHdr,
                     ULONG              ulCODECFlags )
{
   RC                rc;

   MMEXTENDINFO      mmextendinfo;
   MMVIDEOOPEN       mmvideoopen;

   CODECASSOC        codecassoc;
   CODECOPEN         codecopen;

   CODECVIDEOHEADER  cvhSrc;
   CODECVIDEOHEADER  cvhDst;
   MCI_STATUS_PARMS  mciStatusParms;
   ULONG             ulSrcColorEncoding = MMIO_RGB_5_6_5;

  /*****************************************************************/
  /* Get the color encoding of the video capture card              */
  /* otherwise hardcode to RGB                                     */
  /*****************************************************************/
   memset(&mciStatusParms, 0, sizeof(MCI_STATUS_PARMS));
   mciStatusParms.dwCallback   = 0;
   mciStatusParms.dwItem       = MCI_DGV_STATUS_IMAGE_PELFORMAT;
   rc = mciSendCommand (pCB->OutputMovie.wDeviceID,     /* Device ID */
                        MCI_STATUS,                     /* Command   */
                        MCI_WAIT | MCI_STATUS_ITEM,     /* dwFlags   */
                        (DWORD) (&mciStatusParms),      /* In/Out Parameters */
                         0 );                            /* User Parameter    */
   rc &= 0x0000FFFF ;                   /* Mask out the high (device ID) word */
   if (!rc)
     ulSrcColorEncoding = mciStatusParms.dwReturn;

  /*****************************************************************/
  /* Initialize source codec video header                          */
  /*****************************************************************/
   cvhSrc.ulStructLen      = sizeof(CODECVIDEOHEADER);
   cvhSrc.cx               = pCB->recopts.usMovieSizeX;  /* 320 Default     */
   cvhSrc.cy               = pCB->recopts.usMovieSizeY;  /* 240 Default     */
   cvhSrc.cPlanes          = 1;
   cvhSrc.cBitCount        = 16;
   cvhSrc.ulColorEncoding  = ulSrcColorEncoding;

  /*****************************************************************/
  /* Initialize destination codec video header                     */
  /*****************************************************************/
   cvhDst.ulStructLen      = sizeof(CODECVIDEOHEADER);
   cvhDst.cx               = pCB->recopts.usMovieSizeX;
   cvhDst.cy               = pCB->recopts.usMovieSizeY;
   cvhDst.cPlanes          = 1;
   cvhDst.cBitCount        = 16;
   cvhDst.ulColorEncoding  = MMIO_COMPRESSED;

  /*****************************************************************/
  /* Initialize mmvideoopen structure                              */
  /*****************************************************************/
   mmvideoopen.ulStructLen = sizeof(MMVIDEOOPEN);

  /*****************************************************************/
  /* This is max quality derived from quality slider               */
  /* in options notebook. 10,000 for high quality.                 */
  /*****************************************************************/
   mmvideoopen.ulQuality   = pCB->recopts.usQuality;

   if ( pCB->recopts.fInsertRefFrames )
   /*****************************************/
   /* Maximum refresh time = 30 for default */
   /*****************************************/
      mmvideoopen.ulKeyFrameRate    = pCB->recopts.usMaxRefreshTime;
   else
      mmvideoopen.ulKeyFrameRate    = 0;
   mmvideoopen.ulScale              = 1;
   /****************************************/
   /* MovieFPS = input 15 output 15.       */
   /* This is to compensate frame rate for */
   /* difference in input and output.      */
   /****************************************/
   mmvideoopen.ulRate               = pCB->recopts.MovieFPS;
   /****************************************/
   /* MaxDataRate = 1000 bytes/sec         */
   /****************************************/
   mmvideoopen.ulDataConstraint     = pCB->recopts.usMaxDataRate << 10;
   mmvideoopen.ulConstraintInterval = pCB->recopts.MovieFPS;

  /****************************************************************/
  /* Initialize codec open structure                              */
  /****************************************************************/
   memset((PVOID)&codecopen,'\0', sizeof(CODECOPEN));

  /****************************************************************/
  /* Codec - ulFlags need to be specific if the codec supports    */
  /* compress and decompress.                                     */
  /****************************************************************/
   codecopen.ulFlags                = ulCODECFlags;

   codecopen.pControlHdr            = pControlHdr;
   codecopen.pSrcHdr                = (PVOID)&cvhSrc;
   codecopen.pDstHdr                = (PVOID)&cvhDst;
   codecopen.pOtherInfo             = (PVOID)&mmvideoopen;
   codecassoc.pCodecOpen            = &codecopen;
   codecassoc.pCODECIniFileInfo     = pcifi;

  /****************************************************************/
  /* Initialize mmextedinfo structure for MMIO_SET                */
  /****************************************************************/
   memset((PVOID)&mmextendinfo,'\0', sizeof(MMEXTENDINFO));
   mmextendinfo.ulStructLen         = sizeof(MMEXTENDINFO);
   mmextendinfo.ulFlags             = MMIO_CODEC_ASSOC;
   mmextendinfo.ulNumCODECs         = 1;
   mmextendinfo.pCODECAssoc         = &codecassoc;

  /****************************************************************/
  /* Send MIOM_SET to IOproc to associate the codec               */
  /****************************************************************/
   rc =  mmioSendMessage( hmmio,
                          MMIOM_SET,
                          (LONG)&mmextendinfo,
                          MMIO_SET_EXTENDEDINFO);
   return(rc);

} /* AssociateCodec */

/******************************************************************/
/* Name : InitiateFrameStepRecord                                 */
/*                                                                */
/* This function initializes the environment for frame step       */
/* record.                                                        */
/*                                                                */
/******************************************************************/
LONG InitiateFrameStepRecord( PSWVRCB pswvrcb)
{
  ULONG    rc;                             /* return code for random usage   */
  BOOL     fInitErr = FALSE;
  PRECCB   pRecCB;                         /* ptr to a recording cb structure*/


 /********************************************************************/
 /* save a global pointer to the control block                       */
 /********************************************************************/
  threadpointer = pswvrcb;

 /********************************************************************/
 /* Allocate record control if it doesn't exist and set it           */
 /* into our control block.  Then clear it out before we start       */
 /* using it.                                                        */
 /********************************************************************/
  if (!pswvrcb->pRecCB)

  {
    pswvrcb->pRecCB = malloc( sizeof(RECCB));         /* Allocate record CB  */
  } /* endif */
  pRecCB = pswvrcb->pRecCB;

  memset(pRecCB, '\0', sizeof(RECCB));

 /********************************************************************/
 /* Set the threads running state as ok                              */
 /********************************************************************/
  pRecCB->fStillRunning= TRUE;

 /********************************************************************/
 /* Create an event semaphore to indicate paused recording           */
 /* Set error flag for failure                                       */
 /********************************************************************/
  rc = DosCreateEventSem((PSZ) "\\SEM32\\ASYM\\RECPAUSE", /*semaphore name   */
                         &pRecCB->hevRecordPaused,        /*semaphore handle */
                         (ULONG) 0,                    /* attribute - unused */
                         (ULONG) 0);                   /* initial state set  */

  if (rc) fInitErr = TRUE;

 /********************************************************************/
 /* Create an event semaphore to signal frame step complete          */
 /* Set error flag for failure                                       */
 /********************************************************************/
  rc = DosCreateEventSem((PSZ) "\\SEM32\\ASYM\\FRMSTEP",  /* semaphore name  */
                         &pRecCB->hevFrameStep,           /* semaphore handle*/
                         (ULONG) 0,                    /* attribute - unused */
                         (ULONG) 0);                   /* initial state set  */

  if (rc) fInitErr = TRUE;

 /********************************************************************/
 /* Get a buffer for capturing the raw image                         */
 /* Set error flag for failure                                       */
 /********************************************************************/
  rc = DosAllocMem ( (PPVOID) &pRecCB->rawimagebuf, (ULONG) 0xA0000
                     , (ULONG) PAG_COMMIT | PAG_READ | PAG_WRITE);

  if (rc) fInitErr = TRUE;

 /********************************************************************/
 /* Get a buffer for holding the compressed image                    */
 /* Set error flag for failure                                       */
 /********************************************************************/
  rc = DosAllocMem ( (PPVOID) &pRecCB->compimagebuf, (ULONG) 0xA0000
                     , (ULONG) PAG_COMMIT | PAG_READ | PAG_WRITE);

  if (rc) fInitErr = TRUE;

 /*********************************************************************/
 /* If an error occured return error, so that recording will not start*/
 /*********************************************************************/
  if (fInitErr) {
     return(1);
  } /* endif */


  if (!pswvrcb->pCODECData      ||
      (rc = OpenMMIO(pswvrcb)) ||
      (rc =  AssociateCodec( pswvrcb->hmmioTemp,
                             pswvrcb,
                             &pswvrcb->pCODECData->cifi,
                             (PVOID)NULL,
                             CODEC_COMPRESS )))  /* MMIO_RGB_5_6_5  */
  {
  return(rc);
  }

 /********************************************************************/
 /* Everything is ok so far, start the frame step recording thread   */
 /********************************************************************/
  rc = DosCreateThread ((PTID) &pRecCB->RecordThreadID,   /* Thread ID       */
                        (PFNTHREAD) StartVideoThread,     /* Thread addr     */
                        (ULONG) pswvrcb,
                        (ULONG) 0,                        /* Flags           */
                        (ULONG) RecordThreadStackSize);   /* stack size      */

 /********************************************************************/
 /* Change record thread to regular class, delta = -4.               */
 /* Set error flag for failure                                       */
 /********************************************************************/
  rc = DosSetPriority ( 2L, 2L, -4L, pRecCB->RecordThreadID);

  if (rc)
  {
     fInitErr = TRUE;
     return(rc);
  }

  return(0L);
} /* end: InitiateFrameStepRecord      */

/*********************************************************************/
/* Name : CompressBuffer                                             */
/*                                                                   */
/* This function calls the IO proc to compress a raw image buffer    */
/*                                                                   */
/*********************************************************************/
RC CompressBuffer (HMMIO         hmmio,
                   PSWVRCB       pCB,
                   PRECCB        pRecCB,
                   PULONG        pulCompressFlags)
{
 /********************************************************************/
 /* Compression variables                                            */
 /********************************************************************/
  RC                        rcode = NO_ERROR;
  MCI_IMAGE_PARMS           mciImageParms;
  IRECT                     dest_rect;
  IRECT                     source_rect;
  MMCOMPRESS                CompressInfo;
  MMVIDEOCOMPRESS           VideoCompressInfo;
  ULONG                     NumBytes;
  ULONG                     ulBytes;

  /*******************************************************************/
  /* Compress with target compression                                */
  /*                                                                 */
  /* If we are compressing during record, compress the image         */
  /* contained in rawimagebuf and put the compressed output into     */
  /* the stream buffer                                               */
  /*******************************************************************/

  /*******************************************************************/
  /* Initialize the compression information                          */
  /*******************************************************************/
   memset( &VideoCompressInfo, 0, sizeof(MMVIDEOCOMPRESS));
   CompressInfo.ulStructLen   = sizeof( MMCOMPRESS );
   CompressInfo.pRunTimeInfo  = &VideoCompressInfo;
   CompressInfo.ulSrcBufLen   = 0xA0000;
   CompressInfo.pSrcBuf       = pRecCB->rawimagebuf;
   CompressInfo.ulDstBufLen   = 0xA0000;
   CompressInfo.pDstBuf       = pRecCB->streambuf;

  /*******************************************************************/
  /* If we are inserting reference frames, check to see if it's now. */
  /*                                                                 */
  /* If its the 15th frame or 1st frame insert a reference frame.    */
  /*******************************************************************/
   if ( pCB->recopts.fInsertRefFrames )
      if ( !( pRecCB->FramesCounted % pCB->recopts.usMaxRefreshTime ) )
      {
        /**************************************************************/
        /* Compress a reference frame                                 */
        /**************************************************************/
         CompressInfo.ulFlags = MMIO_IS_KEY_FRAME;
         rcode = mmioSendMessage( pCB->hmmioTemp, MMIOM_COMPRESS,
                               (ULONG)&CompressInfo, 0L);
      }
      else
      {
        /**************************************************************/
        /* Compress a delta frame                                     */
        /**************************************************************/
         CompressInfo.ulFlags = 0;
         rcode = mmioSendMessage( pCB->hmmioTemp, MMIOM_COMPRESS,
                               (ULONG)&CompressInfo, 0L);
      }
  /**************************************************************/
  /* Increment our frame counter                                */
  /**************************************************************/
   pRecCB->FramesCounted++;

  /**************************************************************/
  /* Determine total data rate written for this frame           */
  /**************************************************************/
   pRecCB->outbuflen  = CompressInfo.ulDstBufLen;
   pRecCB->outbuflen  = CompressInfo.ulDstBufLen + sizeof(VFHTEMPLATE);

   return(rcode);

} /* Compress Buffer */

/***************************************************************************/
/* Name : StartVideoThread                                                 */
/*                                                                         */
/* Function: Recording thread for frame step.  This function is assuming   */
/* that we have previously recorded the audio to a temporary file that     */
/* we will be used in this function.  We also create a temporary file that */
/* is used for the data after it has been interleaved with the audio.      */
/*                                                                         */
/* InputMovie is referencing the laser disk device and OutputMovie is      */
/* referencing the digitalvideo02 adapter.                                 */
/*                                                                         */
/* This function is using the MMIOM_MULTITRACKWRITE message.  Since this   */
/* is only a sample we are only writing out 1 frame of video and whatever  */
/* audio that goes with that video.  This structure may be set up for      */
/* multiple records of video and audio.                                    */
/*                                                                         */
/* Once everything is setup, send the message to interleave and            */
/* compress the video and audio data to a temporary file.                  */
/***************************************************************************/
VOID StartVideoThread( VOID )
{
 /*****************************************************************/
 /* Various return code variables and strings                     */
 /*****************************************************************/
  DWORD                  rcode = NO_ERROR;
  ULONG                  rc;
  char                   errnostr[20];

 /*****************************************************************/
 /* Semaphore variables                                           */
 /*****************************************************************/
  ULONG                  Postcount;
  HMQ                    hmq = WinCreateMsgQueue( hab, 0 );

 /******************************************************************/
 /* External device mci variables structs for mcisendcommand       */
 /******************************************************************/
  MCI_SEEK_PARMS         lpExtdevSeek;
  MCI_STEP_PARMS         lpExtdevStep;
  MCI_STATUS_PARMS       lpStatusParms;

 /******************************************************************/
 /* Audio device mci variables structs for mcisendcommand          */
 /******************************************************************/
  MCI_SAVE_PARMS         lpWaveSave;
  MCI_GENERIC_PARMS      lpGenparms;

 /******************************************************************/
 /* Variables for maintaining data rate requested.                 */
 /* Must use for skipping frames to maintain desired data rate     */
 /******************************************************************/
  ULONG                  ulCurrentSkipPct,
                         ulSkipPercentage;
  ULONG                  ulFrames;

 /******************************************************************/
 /* Compression variables                                          */
 /******************************************************************/
  MCI_IMAGE_PARMS        mciImageParms;
  IRECT                  dest_rect;
  IRECT                  source_rect;

 /******************************************************************/
 /* Varibles needed to frame step record video and interleave      */
 /* the audio.                                                     */
 /******************************************************************/
  MMMULTITRACKWRITE      MMMultiTrackWrite; /* MMMultiTrackWrite   */

  TRACKMAP               TrackMap[2]; /* TrackMapList              */

  RECORDTAB              Aud, Vid;   * Audio and Video RecordTabs  */

 /******************************************************************/
 /* Varibles needed to break up Audio into 1 frame chunks.         */
 /******************************************************************/
  HMMIO                  hmmiowav;
  PMMAUDIOHEADER         pMMAudioHeader;
  LONG                   lBytes;
  LONG                   lBytesRead;
  LONG                   lSize;
  ULONG                  ulAudioBytesPerFrame;
  ULONG                  ulReserved;
  ULONG                  ulFlags;
  PSZ                    pAudioBuffer;

 /******************************************************************/
 /* Main control block and record control block                    */
 /* Get main from global threadpointer since it can't be passed in */
 /******************************************************************/
  PRECCB                 pRecCB;
  PSWVRCB                pswvrcb = threadpointer;

 /******************************************************************/
 /* Get record control block off of main control block             */
 /******************************************************************/
  pRecCB = pswvrcb->pRecCB;

 /******************************************************************/
 /* Set the time format on Audio recorder                          */
 /******************************************************************/
  lpExtdevSet.dwCallback   = 0;
  lpExtdevSet.dwTimeFormat = MCI_FORMAT_MMTIME;
  rcode = mciSendCommand (pswvrcb->wWaveDID,  /* Device ID */
                          MCI_SET,                        /* Command   */
                          MCI_WAIT | MCI_SET_TIME_FORMAT, /* dwFlags   */
                          (DWORD) (&lpExtdevSet),         /* In/Out Parameters */
                          0 );                            /* User Parameter    */

  if ( !rcode)
  {
     lpStatusParms.dwItem = MCI_STATUS_POSITION;
     rcode=mciSendCommand( pswvrcb->InputMovie.wDeviceID,
                           MCI_STATUS,
                           MCI_WAIT | MCI_STATUS_ITEM,
                           (DWORD) &lpStatusParms,
                           0);

     if (pswvrcb->dwSrcRecordTo > lpStatusParms.dwReturn)
         pswvrcb->dwSrcRecordTo = lpStatusParms.dwReturn;
  }

 /******************************************************************/
 /* Save the Wave file                                             */
 /******************************************************************/
  lpWaveSave.dwCallback = 0;
  lpWaveSave.lpFileName = 0;
  rcode =
          mciSendCommand (
          pswvrcb->wWaveDID,   /* Device ID returned from OPEN */
          MCI_SAVE,            /* Command */
          MCI_WAIT,            /* dwFlags */
         (DWORD)&lpWaveSave,   /* In/Out Parameters */
           0 );                 /* User Parameter */

 /******************************************************************/
 /* We need to close the waveaudio so we can open digitalvideo02.  */
 /* This is done because some audio adapters do not support        */
 /* multiple instances.                                            */
 /******************************************************************/
  rcode = mciSendCommand( pswvrcb->wWaveDID,
                          MCI_CLOSE,
                          MCI_WAIT,
                          (DWORD) &lpGenparms, 0 );

  if (SendString ((PSZ) "OPEN",
                        (PSZ) pswvrcb->OutputMovie.szDeviceName,
                        (PSZ) "ALIAS",
                        (PSZ) pswvrcb->OutputMovie.szDeviceAlias,
                        (PSZ) "WAIT",
                         NULL,
                         NULL))
    {
       WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,
          (PSZ) "error opening MCD",
          (PSZ) errnostr, 0, MB_OK | MB_MOVEABLE);
    }
    else
    {
     pswvrcb->OutputMovie.wDeviceID =
                   mciGetDeviceID( (LPSTR) pswvrcb->OutputMovie.szDeviceAlias);
    }

 /******************************************************************/
 /* Get exclusive control of the Output device now                 */
 /******************************************************************/
  lpGenparms.dwCallback = 0;
  rcode = mciSendCommand( pswvrcb->OutputMovie.wDeviceID,
                          MCI_ACQUIREDEVICE,
                          MCI_WAIT | MCI_EXCLUSIVE,
                          (DWORD) &lpGenparms, 0 );

 /******************************************************************/
 /* Get exclusive control of the Input device now if we have one   */
 /******************************************************************/
  if (!rcode && pswvrcb->InputMovie.wDeviceID)
  {
    lpGenparms.dwCallback = 0;
    rcode = mciSendCommand( pswvrcb->InputMovie.wDeviceID,
                            MCI_ACQUIREDEVICE,
                            MCI_WAIT | MCI_EXCLUSIVE,
                            (DWORD) &lpGenparms, 0 );
  }

 /******************************************************************/
 /* Initialize current source/movie positions                      */
 /******************************************************************/
  if (!rcode)
  {
    pRecCB->dwSrcCurrentPosition   = pswvrcb->dwSrcRecordFrom;
    pRecCB->dwMovieRecordFrom      = 0;
    pRecCB->dwMovieCurrentPosition = 0;

  } /* endif */

 /******************************************************************/
 /* Set up the external source device if we are using one          */
 /******************************************************************/
  if (!rcode && pswvrcb->fSourceControlled)
  {

    /******************************************************************/
    /*    Set the time format on external video device                */
    /******************************************************************/
     lpExtdevSet.dwCallback   = 0;
     lpExtdevSet.dwTimeFormat = MCI_FORMAT_MMTIME;
     rcode = mciSendCommand (pswvrcb->InputMovie.wDeviceID,     /* Device ID */
                             MCI_SET,                           /* Command   */
                             MCI_WAIT | MCI_SET_TIME_FORMAT,    /* dwFlags   */
                             (DWORD) (&lpExtdevSet),    /* In/Out Parameters */
                             0 );                       /* User Parameter    */

    /******************************************************************/
    /*    Seek external video device to first frame                   */
    /******************************************************************/
     lpExtdevSeek.dwCallback = 0;
     lpExtdevSeek.dwTo       = pswvrcb->dwSrcRecordFrom;
     rcode = mciSendCommand ( pswvrcb->InputMovie.wDeviceID,  /* Device ID   */
                              MCI_SEEK,                       /* Command     */
                              MCI_WAIT | MCI_TO,              /* dwFlags     */
                              (DWORD) (&lpExtdevSeek), /* In/Out Parameters  */
                              0 );                     /* User Parameter     */

  } /* endif - source is being controlled */

  if (!rcode)
    {
    /******************************************************************/
    /* Calculate the number of frames on the source device we need to */
    /* skip per movie frame to maintain the desired capture frame rate*/
    /******************************************************************/
    ulCurrentSkipPct = 0L;
    ulSkipPercentage = (((pswvrcb->recopts.SrcFPS -
                          pswvrcb->recopts.MovieFPS) * 1000 ) +
                          pswvrcb->recopts.MovieFPS - 1 ) /
                          pswvrcb->recopts.MovieFPS;

    /******************************************************************/
    /* Set the source rectangle size for the capture.                 */
    /* This is for how much we are capturing.                         */
    /******************************************************************/
    source_rect.X_left   =  pswvrcb->recopts.usCapPosX; /* Default 0         */
    source_rect.Y_top    =  pswvrcb->recopts.usCapPosY; /* Default 0         */
    source_rect.X_width  =  pswvrcb->recopts.usCapSizeX;/* Default 640       */
    source_rect.Y_height =  pswvrcb->recopts.usCapSizeY;/* Default 480       */

    /******************************************************************/
    /* Set the destination rectangle size to the desired movie size   */
    /******************************************************************/
    dest_rect.X_left   = 0;
    dest_rect.Y_top    = 0;
                                  /* Destination rectangle width for capture */
    dest_rect.X_width  = pswvrcb->recopts.usMovieSizeX;  /* default 320      */
                                 /* Destination rectangle height for capture */
    dest_rect.Y_height = pswvrcb->recopts.usMovieSizeY;  /* default 240      */
  } /* endif */

  /******************************************************************/
  /* open up the Audio file.  This will be used later.              */
  /******************************************************************/
   hmmiowav = mmioOpen( TempAudioFilename,
                        NULL,
                        0);

 /******************************************************************/
 /* Starting here we will setting  up the output (digitalvideo02)  */
 /* for the movie.                                                 */
 /******************************************************************/

 /******************************************************************/
 /* Get main header length from input movie                        */
 /******************************************************************/
  if (!rcode)
    rcode = mmioQueryHeaderLength(hmmiowav,&lSize,0L,0L);

  if (!rcode)
    pMMAudioHeader = malloc(lSize);

 /******************************************************************/
 /* Get main header from Audiofile                                 */
 /******************************************************************/
  if (!rcode)
    rcode = mmioGetHeader(hmmiowav,
                          (PVOID)(pMMAudioHeader),
                          lSize, &lBytesRead, 0L, 0L);

  /**********************************************/
  /* This function is called to set the track.  */
  /**********************************************/
   SetActiveTrack ( pswvrcb->hmmioTemp, 1L );

  /******************************************************************/
  /* Set the audio header info for the movie                        */
  /******************************************************************/
   rc = mmioSetHeader( pswvrcb->hmmioTemp,
                                pMMAudioHeader,
                                sizeof(MMAUDIOHEADER),
                                &lBytes,
                                ulReserved,
                                ulFlags );

  /**********************************************/
  /* This function is called to set the track.  */
  /**********************************************/
   SetActiveTrack ( pswvrcb->hmmioTemp, MMIO_RESETTRACKS );

  /******************************************************************/
  /* Calculate Number of Audio Bytes Per Frame of Video             */
  /******************************************************************/
   ulAudioBytesPerFrame = pMMAudioHeader->mmXWAVHeader.
                                          WAVEHeader.
                                          ulAvgBytesPerSec /
                                          pswvrcb->recopts.MovieFPS;
   pAudioBuffer = malloc(ulAudioBytesPerFrame);
   memset(pAudioBuffer, 0, ulAudioBytesPerFrame);

   free(pMMAudioHeader);

 /******************************************************************/
 /* This is the frame step recording loop                          */
 /* Recording continues until we reach the recording end point     */
 /* on the source device, or until the user presses stop.          */
 /* If the source device is not controllable continue until        */
 /* we are stopped.                                                */
 /******************************************************************/

  rcode = SetActiveTrack(pswvrcb->hmmioTemp, MMIO_RESETTRACKS);

  while (!rcode &&
          pswvrcb->fRecordActive &&
         (pswvrcb->InputMovie.wDeviceID ?
         (pRecCB->dwSrcCurrentPosition <= pswvrcb->dwSrcRecordTo) : TRUE) )
  {
   /******************************************************************/
   /*    Block at this point if record operation is paused           */
   /******************************************************************/
     while ( pswvrcb->fRecordPaused && pRecCB->fStillRunning )
     {
        rc = DosResetEventSem( pRecCB->hevRecordPaused, &Postcount);
        rc = DosWaitEventSem( pRecCB->hevRecordPaused, (ULONG) -1);
     } /* endwhile */

     /******************************************************************/
     /* make sure we were not stopped while we were paused             */
     /******************************************************************/
     if ( pswvrcb->fRecordActive && !pswvrcb->fRecordPaused )
     {

        /******************************************************************/
        /* Advance output stream pointer to leave room for video frame    */
        /* header before writing actual video data to the stream          */
        /******************************************************************/
        pRecCB->streambuf = pRecCB->compimagebuf + sizeof (VFHTEMPLATE);

        /******************************************************************/
        /* Recording from a video capture device we are compressing data  */
        /******************************************************************/
        if (pswvrcb->recopts.fCompress)
        {

          /*****************************************************************/
          /* Initialize mciImageParm variable before sending to MCD        */
          /*****************************************************************/
           memset( &mciImageParms, 0, sizeof(mciImageParms));
           mciImageParms.dwBufLen = pswvrcb->recopts.usMovieSizeX *
                                    pswvrcb->recopts.usMovieSizeY * 2;
          /*****************************************************************/
          /* rawimagebuf is where we put our output from the GetImageBuffer*/
          /*****************************************************************/
           mciImageParms.pPelBuffer=(PVOID *)pRecCB->rawimagebuf;
           mciImageParms.rect.xLeft        = source_rect.X_left;
           mciImageParms.rect.yBottom      = source_rect.Y_top;
           mciImageParms.rect.xRight       = source_rect.X_left +
                                             source_rect.X_width;
           mciImageParms.rect.yTop         = source_rect.Y_top  +
                                             source_rect.Y_height;
           mciImageParms.dwPelBufferWidth  = pswvrcb->recopts.usMovieSizeX;
           mciImageParms.dwPelBufferHeight = pswvrcb->recopts.usMovieSizeY;

          /*****************************************************************/
          /* Get image data for this frame into raw image buffer           */
          /*****************************************************************/
           rcode = mciSendCommand( pswvrcb->OutputMovie.wDeviceID,
                                   MCI_GETIMAGEBUFFER,
                                   MCI_WAIT | MCI_USE_HW_BUFFER,
                                   (ULONG)&mciImageParms,
                                   0);

        }

       /*******************************************************************/
       /* Figure out how many frames to advance the external video device */
       /* to maintain desired data rate                                   */
       /*******************************************************************/
        ulCurrentSkipPct           += ulSkipPercentage;
        ulFrames                    = ulCurrentSkipPct / 1000L;
        ulCurrentSkipPct           -= ulFrames * 1000L;
        if ( ulCurrentSkipPct < 50L )
           ulCurrentSkipPct = 0L;

       /*******************************************************************/
       /* Advance the source device.
       /* If device is not controllable, ask the user to do it.
       /*******************************************************************/
        if (!rcode && pswvrcb->InputMovie.wDeviceID)
        {

          /*****************************************************************/
          /* Clear the semaphore that signals the external device          */
          /* frame step complete                                           */
          /*****************************************************************/
           rc = DosResetEventSem( pRecCB->hevFrameStep, &Postcount);

          /*****************************************************************/
          /* Send STEP to external video device.  This is stepping the     */
          /* input device (laser disk).                                    */
          /*****************************************************************/
           lpExtdevStep.dwCallback = vhwndPlayerDialog;
           ulFrames++;
           lpExtdevStep.dwStep     = ulFrames * 3000/pswvrcb->recopts.SrcFPS;
           mciSendCommand ( pswvrcb->InputMovie.wDeviceID,
                            MCI_STEP,
                            MCI_NOTIFY | MCI_STEP_FRAMES,
                            (DWORD) (&lpExtdevStep),
                            lpExtdevStep.dwStep );
        }

       /*****************************************************************/
       /* Since we are compressing during record,                       */
       /* compress the image contained in rawimagebuf and put the       */
       /* compressed output into the steam buffer                       */
       /*****************************************************************/
        if (!rcode && pswvrcb->recopts.fCompress)
        {

           /*****************************************************************/
           /* Compress - Input raw image buffer is in pRecCB->rawimagebuf.  */
           /*****************************************************************/
            rcode = CompressBuffer (pswvrcb->hmmioTemp,
                                    pswvrcb,
                                    pRecCB,
                                    dest_rect,
                                    0L);    /* pulCompressFlags        */

           /*****************************************************/
           /* Get a chunk of Audio data                         */
           /*****************************************************/
            lBytes = mmioRead( hmmiowav,
                             (PSZ)pAudioBuffer,
                             ulAudioBytesPerFrame);


            MMMultiTrackWrite.ulNumTracks = 2;

           /*****************************************************************/
           /* We do not need to assign/specify the next element.  Using     */
           /* the MMMULTITRACKWRITE the IO proc handles this.               */
           /* The MMMULTITRACKWRITE does the interleaving of the video data */
           /* and audio data for you.                                       */
           /*****************************************************************/

            MMMultiTrackWrite.pTrackMapList = &TrackMap[0];

           /*****************************************************/
           /* Setup the number of entries for video data        */
           /*****************************************************/

            TrackMap[0].ulTrackID = 0;      /* video data              */

            TrackMap[0].ulNumEntries = 1;   /* Number of video entries */

            TrackMap[0].pRecordTabList = &Vid;

            /**************************************************/
            /* Setup the RECORDTAB table entries for video    */
            /**************************************************/

            Vid.pRecord = pRecCB->streambuf;

            Vid.ulLength = pRecCB->outbuflen;

            Vid.ulParm1 = 0L;

            Vid.ulParm2 = 0L;

            /**********************************************/
            /* Setup the number of entries for audio data */
            /**********************************************/

            TrackMap[1].ulTrackID = 1;      /* audio data */

            TrackMap[1].ulNumEntries = 1;   /* Number of audio entries */


            TrackMap[1].pRecordTabList = &Aud;

            /**************************************************/
            /* Setup the RECORDTAB table entries for audio    */
            /**************************************************/

            Aud.pRecord = pAudioBuffer;

            Aud.ulLength = ulAudioBytesPerFrame;

            Aud.ulParm1 = 0L;

            Aud.ulParm2 = 0L;

            /***************************************************************/
            /* Once everything is setup,send the message to interleave and */
            /* compress the video and audio data                           */
            /***************************************************************/

             rcode = mmioSendMessage(pswvrcb->hmmioTemp,
                                     MMIOM_MULTITRACKWRITE,
                                     (LONG)&MMMultiTrackWrite,
                                     sizeof(MMMultiTrackWrite));

        } /* End if - if compressing during record */

         /******************************************************************/
         /* Wait here until the external video device completes the        */
         /* frame step                                                     */
         /******************************************************************/
          DosWaitEventSem ( pRecCB->hevFrameStep, (ULONG)-1 );

     } /* End - if not stopped while paused */

  }   /* End while - frame step recording frame loop */

  free(pAudioBuffer);
 /*****************************************************************/
 /* Close the temp video output file since                        */
 /* recording is finished                                         */
 /*****************************************************************/
  if (!rcode)
  {
    mmioClose( pswvrcb->hmmioTemp, 0);    /* Close the final file            */
    mmioClose( hmmiowav, 0);              /* Close the audio file            */

  } /* endif */


 /*****************************************************************/
 /* Close event semaphore to signal frame step complete           */
 /*****************************************************************/
  DosCloseEventSem ( pRecCB->hevFrameStep );

 /*****************************************************************/
 /* Close event semaphore to signal recording paused              */
 /*****************************************************************/
  DosCloseEventSem ( pRecCB->hevRecordPaused );

 /*****************************************************************/
 /* Free buffer for capturing the raw image                       */
 /*****************************************************************/
  DosFreeMem ( pRecCB->rawimagebuf );

 /*****************************************************************/
 /* Free buffer for capturing the compressed image                */
 /*****************************************************************/
  DosFreeMem ( pRecCB->compimagebuf );

 /******************************************************************/
 /* Send a message back to our window so it knows recording is done*/
 /******************************************************************/
  WinSendMsg( vhwndPlayerDialog, WM_FINISHEDRECORDING,
              (MPARAM) rcode, (MPARAM) 0);

 /*****************************************************************/
 /* Destroy message queue                                         */
 /*****************************************************************/
  WinDestroyMsgQueue( hmq );

 /*****************************************************************/
 /* Exit thread                                                   */
 /*****************************************************************/
  DosExit( (ULONG) 0, (ULONG) 0);

} /* end: StartVideoThread          */
