/*****************************************************************************
CIsimpleFile.c Source for BitFlow CI lib simple file save program
Oct 20, 2015 CIW/SJT
© Copyright 2015, BitFlow, Inc. All rights reserved.
Tabstops are 4
$Author: steve $
$Date: 2020/10/02 23:30:13 $
$Id: CIsimpleFile.c,v 1.7 2020/10/02 23:30:13 steve Exp $
*****************************************************************************/
/*==========================================================================*/
/*
** For access to command line display.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
/*
** For checking for keypress
*/
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
/*
** For access to BitFlow camera interface library.
*/
#include "BFciLib.h"
/*==========================================================================*/
static int sExitAns = 0; /* program exit code */
static tCIp sCIp = NULL; /* device open token */
static int sNdx = 0; /* device index */
static int sMaxFrames = 100; /* total frames to capture */
static int sDidFrames = 0; /* total frames handled */
static char sPrefix[1024] = ""; /* for files */
static char sCfgFN[1024] = ""; /* for special config file */
static int sLibDebug = 0; /* enable library display */
static tCIU32 sTimeoutMS = 10000; /* for catching errors */
/*--------------------------------------------------------------------------*/
#define SHOW(x) { (void)printf x ; (void)fflush(stdout); }
#define ERR(x) { SHOW(("ERR: ")); SHOW(x); }
static char *sArgv0 = NULL; /* name of executable */
static void ShowHelp(void)
{
SHOW(("%s of " __DATE__ " at " __TIME__ "\n", sArgv0));
SHOW((" -h display this message and exit\n"));
SHOW(("\n"));
SHOW((" -D display library debug info ('LDB' prefix)\n"));
SHOW((" -x ndx choose available device ndx (default 0)\n"));
SHOW((" -m maxFrames max frames to save (default 100)\n"));
SHOW((" -c cfgFN CiVFGinitialize() w/this configfile\n"));
SHOW((" -p prefix for file name (default is date/time)\n"));
SHOW((" -t toutMsec set the timeout (%d)\n", sTimeoutMS));
SHOW(("\n"));
SHOW((" CIsimpleFile: initialize an interface and save frames\n"));
SHOW(("\n"));
}
/*==========================================================================*/
static void LDBdisplay(char *format, ...)
/*
** Library debug display function.
**
** This will display progress/information/error strings from BFciLib
*/
{
va_list val;
char buff[8192];
va_start(val, format);
(void)vsprintf(buff, format, val);
va_end(val);
/*
** buff now contains the library display string
**
** This function simply prints it to the console.
*/
(void)printf("LDB: %s\n", buff);
(void)fflush(stdout);
}
/*--------------------------------------------------------------------------*/
#include <sys/timeb.h>
static tCIDOUBLE GetTime(void)
/*
** Return fractional seconds
*/
{
tCIDOUBLE ans = 0.0;
#ifdef _POSIX_TIMERS
struct timespec tp;
(void)clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
ans = (tCIDOUBLE) tp.tv_sec;
ans += ((tCIDOUBLE) tp.tv_nsec) / 1000000000.0;
#else
struct timeb tb;
(void)ftime(&tb);
ans = tb.millitm;
ans /= 1000.0;
ans += tb.time;
#endif
return (ans);
}
/*--------------------------------------------------------------------------*/
static int DecodeArgs(int argc, char **argv)
/*
** Parse the input arguments.
*/
{
char *str;
argv += 1;
argc -= 1; /* skip program name */
while (argc-- > 0)
{
str = *argv++;
if (str[0] != '-')
{
ERR(("Do not know '%s' arg\n", str));
ShowHelp();
return (1);
}
switch (str[1])
{
case 'h':
ShowHelp();
return (1);
case 'D':
sLibDebug = 1;
break;
case 'x':
(void)sscanf(*argv, "%d", &sNdx);
argv += 1;
argc -= 1;
break;
case 'm':
(void)sscanf(*argv, "%d", &sMaxFrames);
argv += 1;
argc -= 1;
break;
case 'c':
(void)strncpy(sCfgFN, *argv, sizeof(sCfgFN));
argv += 1;
argc -= 1;
break;
case 'p':
(void)strncpy(sPrefix, *argv, sizeof(sPrefix));
argv += 1;
argc -= 1;
break;
case 't':
(void)sscanf(*argv, "%d", &sTimeoutMS);
argv += 1;
argc -= 1;
break;
default:
ERR(("Do not know arg '%s'\n", str));
ShowHelp();
sExitAns = 1;
return (sExitAns);
}
}
return (kCIEnoErr);
}
/*--------------------------------------------------------------------------*/
#include <tiffio.h>
static void SaveOneTIFF(tCIU8 * buff, int stride, int ppr, int rpf, int bpp)
{
static int sID = 0;
char fn[sizeof(sPrefix) + 64];
TIFF *tiff;
int i;
(void)snprintf(fn, sizeof(fn), "%s_%04d.tiff", sPrefix, sID++);
tiff = TIFFOpen(fn, "w");
if (NULL == tiff)
{
ERR(("SaveOneTIFF: cannot TIFFOpen() on '%s'\n", fn));
return;
}
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, ppr);
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, rpf);
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, bpp);
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE);
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
for (i = 0; i < (int)rpf; i++)
{
if (1 != TIFFWriteScanline(tiff, (tdata_t) buff, i, 0))
{
ERR(("SaveOneTIFF: %d of %d scanline not OK\n", i, rpf));
}
buff += stride;
}
TIFFClose(tiff);
SHOW(("SaveToFile: saved %dx%d(%d)/%d (%d) to '%s'\n", ppr, rpf, i, bpp, stride, fn));
return;
}
/*--------------------------------------------------------------------------*/
static void InitAndGetDataUntilKeyPress(void)
/*
** Illustrate a simple example VFG interaction sequence.
*/
{
tCIRC circ;
tCIDOUBLE a = -1.0, b, c, d;
tCIU64 totalBytes = 0, totalLines = 0;
tCIU32 nPtrs, counter = sMaxFrames;
tCIU8 **uPtrs = NULL;
tCIU32 frameID;
tCIU8 *frameP;
tCIU32 nFrames, bitsPerPix, hROIoffset, hROIsize, vROIoffset, vROIsize, stride;
/*
** Set library debug function if '-D' was specified.
*/
circ = CiSetDebug(NULL, -1, (0 == sLibDebug) ? NULL : LDBdisplay);
if (kCIEnoErr != circ)
{
ERR(("CiSetDebug of %d gave '%s'\n", sNdx, CiErrStr(circ)));
}
/*
** Open the ndx'th frame grabber with exclusive write permission.
*/
circ = CiVFGopen(sNdx, kCIBO_exclusiveWrAccess, &sCIp);
if (kCIEnoErr != circ)
{
ERR(("CiVFGopen gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
return;
}
/*
** Init the VFG with the config file specified by argv (or DIP switches)
*/
circ = CiVFGinitialize(sCIp, sCfgFN);
if (kCIEnoErr != circ)
{
ERR(("CiVFGinitialize gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
/*
** Configure the VFG for 2 extra frame buffers.
*/
circ = CiDrvrBuffConfigure(sCIp, sMaxFrames + 2, 0, 0, 0, 0);
if (kCIEnoErr != circ)
{
ERR(("CiDrvrBuffConfigure(1) gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
/*
** Determine buffer configuration.
*/
circ = CiBufferInterrogate(sCIp, &nFrames, &bitsPerPix, &hROIoffset, &hROIsize, &vROIoffset, &vROIsize, &stride);
if (kCIEnoErr != circ)
{
ERR(("CiBufferInterrogate gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
/*
** Get the buffer pointers for read access to buffers.
*/
circ = CiMapFrameBuffers(sCIp, 0, &nPtrs, &uPtrs);
if (kCIEnoErr != circ)
{
ERR(("CiMapFrameBuffers gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
/*
** Reset acquisition and clear all error conditions.
*/
circ = CiAqSWreset(sCIp);
if (kCIEnoErr != circ)
{
ERR(("CiAqSWreset gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
/*
** Start acquisition of the desired number of frames
*/
circ = CiAqStart(sCIp, sMaxFrames);
if (kCIEnoErr != circ)
{
ERR(("CiAqStart gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
a = GetTime();
/*
** Save the frames in a loop.
*/
while (1)
{
/*
** Check to see if a frame is already available before waiting.
*/
checkAgain:
circ = CiGetOldestNotDeliveredFrame(sCIp, &frameID, &frameP);
switch (circ)
{
case kCIEnoErr:
/*
** We have the frame.
*/
break;
case kCIEnoNewData:
/*
** We need to wait for another frame.
*/
circ = CiWaitNextUndeliveredFrame(sCIp, sTimeoutMS);
if (kCIEnoErr != circ)
{
switch (circ)
{
case kCIEaqAbortedErr:
SHOW(("CiWaitNextUndeliveredFrame gave '%s'\n", CiErrStr(circ)));
break;
default:
ERR(("CiWaitNextUndeliveredFrame gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
}
goto andOut;
}
goto checkAgain;
case kCIEaqAbortedErr:
SHOW(("CiGetOldestNotDeliveredFrame: acqistion aborted\n"));
goto andOut;
default:
ERR(("CiGetOldestNotDeliveredFrame gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
goto andOut;
}
SaveOneTIFF(frameP, stride, hROIsize, vROIsize, bitsPerPix);
totalLines += vROIsize;
totalBytes += stride * vROIsize;
sDidFrames += 1;
/*
** Break out of loop when countdown hits zero
*/
if ((0 != counter) && (--counter == 0))
{
break;
}
}
andOut:
/*
** Stop acquisition since we are about to free the DMA store
*/
circ = CiAqAbort(sCIp);
if (kCIEnoErr != circ)
{
ERR(("CiAqAbort gave '%s'\n", CiErrStr(circ)));
}
/*
** Unmap the frame buffers.
*/
circ = CiUnmapFrameBuffers(sCIp);
if ((NULL != uPtrs) && (kCIEnoErr != circ))
{
ERR(("CiUnmapFrameBuffers gave '%s'\n", CiErrStr(circ)));
}
/*
** Release the DMA storage.
*/
circ = CiDrvrBuffConfigure(sCIp, 0, 0, 0, 0, 0);
if (kCIEnoErr != circ)
{
ERR(("CiDrvrBuffConfigure(2) gave '%s'\n", CiErrStr(circ)));
sExitAns = 1;
}
/*
** Close the access.
*/
circ = CiVFGclose(sCIp);
if ((NULL != sCIp) && (kCIEnoErr != circ))
{
ERR(("CiVFGclose gave '%s'\n", CiErrStr(circ)));
}
/*
** Show data rate
*/
c = b = GetTime() - a;
if ((a < 0.0) || (c < 0.001))
{
a = 0.0;
b = 0.0;
c = 0.0;
d = 0.0;
}
else
{
a = ((tCIDOUBLE) totalBytes) / c;
b = ((tCIDOUBLE) totalLines) / c;
d = ((tCIDOUBLE) sDidFrames) / c;
}
SHOW(("%d: Data rate %.1lf ln/s (%.1lf b/s) (%.1lf FPS) (%.1lf sec) after %d fr\n", sNdx, b, a, d, c, sDidFrames));
return;
}
/*==========================================================================*/
int main(int argc, char **argv)
/*
** Decode command line and acquire/display frames until EOF
*/
{
static const char *sMo[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec"
};
struct tm *p;
time_t sec;
sec = (time_t) GetTime();
p = localtime(&sec);
(void)sprintf(sPrefix, "%04d%s%02d_%02d%02d%02d", 1900 + p->tm_year, sMo[p->tm_mon], p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
sArgv0 = *argv;
if (kCIEnoErr == DecodeArgs(argc, argv))
{
InitAndGetDataUntilKeyPress();
}
return (sExitAns);
}
/*==========================================================================*/
/*
$Log: CIsimpleFile.c,v $
Revision 1.7 2020/10/02 23:30:13 steve
CLOCK_MONOTONIC is not always monotonic, so prefer CLOCK_MONOTONIC_RAW.
Revision 1.6 2020/10/02 01:17:23 steve
ftime is deprecated. Use clock_gettime.
Revision 1.5 2017/12/12 11:05:52 steve
Fixes for axn+vivid
Revision 1.4 2017/10/04 08:38:45 steve
Ready for rt.
Revision 1.3 2016/07/11 20:02:58 steve
Gn2 xx-2Y/2YE, GPUD, DGMA
Revision 1.2 2016/04/01 23:22:13 steve
Fix Axn INT
Revision 1.1 2016/03/06 23:31:46 steve
Support CXP_usualINit and Axion
*/