#define PFNC_INCLUDE_HELPERS
#define M_MIL_USE_OS_SCREEN_FUNCTIONS 1
#include <mil.h>
void PrintHeader()
{
MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n\n"));
MosPrintf(MIL_TEXT("MultiCameraDisplay\n\n"));
MosPrintf(MIL_TEXT("[SYNOPSIS]\n\n"));
MosPrintf(
MIL_TEXT("This program detects all the cameras attached to all the installed\n")\
MIL_TEXT("Matrox systems and starts grabbing from them using MdigProcess().\n\n")\
MIL_TEXT("Features include:\n")\
MIL_TEXT(" - Displaying multiple live streams from multiple boards.\n")\
MIL_TEXT(" - No tearing video output.\n")\
MIL_TEXT(" - Low latency video output.\n")\
MIL_TEXT(" - Live camera addition and removal.\n")\
MIL_TEXT(" - Changing the display between windowed and full screen mode.\n")\
MIL_TEXT(" - Changing grab buffer pixel formats.\n")\
MIL_TEXT(" - Activating image processing on a live stream.\n")\
MIL_TEXT(" - Activating H264 encoding on a live stream.\n")\
MIL_TEXT(" - Displaying the Feature browser so that the user can control the digitizer\n")\
MIL_TEXT(" and camera settings.\n")\
MIL_TEXT("\n\n")\
MIL_TEXT("Press <Enter> to start.\n\n")\
);
MosScreenRefresh();
}
void printCommands()
{
MosPrintf(MIL_TEXT("Matrox MultiCameraDisplay\n")\
MIL_TEXT("-------------------------\n\n")\
MIL_TEXT("Cameras can be added or removed at any time.\n\n")\
MIL_TEXT("Commands on a specific camera(s):\n")\
MIL_TEXT("---------------------------------\n")\
MIL_TEXT(" <a> to activate image processing.\n")\
MIL_TEXT(" <e> to activate H264 encoding.\n")\
MIL_TEXT(" <b> to open the feature browser.\n")\
MIL_TEXT(" <d> to free a camera.\n")\
MIL_TEXT(" <p> to change the pixel format of the grab buffers.\n")\
MIL_TEXT(" <t> to toggle the display of information in the overlay.\n\n")\
MIL_TEXT("Commands on window:\n")\
MIL_TEXT("-------------------\n")\
MIL_TEXT(" <f> to switch between full-screen and windowed mode.\n")\
MIL_TEXT(" <g> to switch the display render source.\n")\
MIL_TEXT(" <r> to rearrange the tiles on the display.\n")\
MIL_TEXT(" <s> to toggle scaling between fit_to_screen or no-scaling.\n\n")\
MIL_TEXT("Other commands:\n")\
MIL_TEXT("---------------\n")\
MIL_TEXT(" <n> to auto detect new cameras.\n")\
MIL_TEXT(" <q> to quit.\n\n")\
MIL_TEXT("Camera(s):\n")\
MIL_TEXT("--------\n")
);
MosScreenRefresh();
}
#include <set>
#include <algorithm>
using namespace std;
#include "../DisplayGL/C++/displayGLexport.h"
#include "MdigHandler.h"
#if M_MIL_USE_WINDOWS
#include <Windows.h>
#endif
typedef struct _SYSTEM_DATA
{
typedef struct
{
_SYSTEM_DATA *pSystem;
MIL_ID systemID;
MIL_ID threadcameradetectId;
} CAMERA_DETECT_PARAM;
_SYSTEM_DATA()
{
_mutex = M_NULL;
}
~_SYSTEM_DATA()
{
Free();
}
void Free()
{
for (auto dig_iter = _digitizers.begin(); dig_iter != _digitizers.end(); ++dig_iter)
(*dig_iter)->StopGrab();
for (auto dig_iter = _digitizers.begin(); dig_iter != _digitizers.end(); ++dig_iter)
delete *dig_iter;
_digitizers.clear();
if (_display)
_display->Release();
_display = NULL;
if (_mutex)
MthrFree(_mutex);
_mutex = M_NULL;
for (auto iter_system = _systemIDs.begin(); iter_system != _systemIDs.end(); ++iter_system)
MsysFree(*iter_system);
_systemIDs.clear();
}
vector<MIL_ID> _systemIDs;
list<CMILDigitizerHandler *> _digitizers;
IMilDisplayEx* _display;
vector<CAMERA_DETECT_PARAM> _threadcameradetects;
MIL_ID _mutex;
} SYSTEM_DATA;
void ProcessUserInput(MIL_INT keyPressed, SYSTEM_DATA &systemData, bool &sortAndRearrangeDisplay);
bool compare_digitizers_for_sorting(const CMILDigitizerHandler *first, const CMILDigitizerHandler *second);
MIL_INT MFTYPE CamPresentFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr);
void StartCameraDetectionThreads(SYSTEM_DATA &systemData, bool wait);
void FreeCameraDetectionThreads(_SYSTEM_DATA &SystemData);
class MilMutexLockGuard
{
public:
MilMutexLockGuard(MIL_ID mutex)
{
_mutex = mutex;
MthrControl(_mutex, M_LOCK, M_DEFAULT);
}
~MilMutexLockGuard()
{
MthrControl(_mutex, M_UNLOCK, M_DEFAULT);
}
MIL_ID _mutex;
};
int MosMain(void)
{
MosScreenInit();
PrintHeader();
MIL_ID MilApplication;
SYSTEM_DATA _systemData;
MappAlloc(M_DEFAULT, &MilApplication);
MappControl(M_ERROR, M_PRINT_DISABLE);
MIL_INT NbAvailableSystems = 0;
MappInquire(M_DEFAULT, M_INSTALLED_SYSTEM_COUNT, &NbAvailableSystems);
MIL_STRING systemDescriptor;
vector<MIL_STRING> excluded_systems;
excluded_systems.push_back(MIL_TEXT("M_SYSTEM_HOST"));
excluded_systems.push_back(MIL_TEXT("M_SYSTEM_GENTL"));
for(MIL_INT i = 0; i < NbAvailableSystems; i++)
{
MappInquire(M_DEFAULT, M_INSTALLED_SYSTEM_DESCRIPTOR + i, systemDescriptor);
if (std::find(excluded_systems.begin(), excluded_systems.end(), systemDescriptor) == excluded_systems.end())
{
MIL_ID milSystem = M_NULL;
MIL_INT sysdevnum = 0;
do
{
MsysAlloc(systemDescriptor.c_str(), M_DEV0 + sysdevnum, M_DEFAULT, &milSystem);
if (milSystem == M_NULL)
break;
if (MsysInquire(milSystem, M_LOCATION, M_NULL) == M_REMOTE)
{
MsysFree(milSystem);
break;
}
_systemData._systemIDs.push_back(milSystem);
sysdevnum++;
} while (milSystem);
}
}
MosGetch();
if (_systemData._systemIDs.size() == 0)
{
MIL_ID milSystem = M_NULL;
MsysAlloc(M_SYSTEM_HOST, M_DEV0, M_DEFAULT, &milSystem);
_systemData._systemIDs.push_back(milSystem);
}
MthrAlloc(M_DEFAULT_HOST, M_MUTEX, M_DEFAULT, M_NULL, M_NULL, &_systemData._mutex);
_systemData._display = GetMilDisplayEx("Matrox MultiCameraDisplay", 0, 0);
_systemData._display->SetRenderSource(eRenderFromGrabCallBack);
_systemData._display->OpenWindow();
for (auto iter_system = _systemData._systemIDs.begin(); iter_system != _systemData._systemIDs.end(); ++iter_system)
MsysHookFunction(*iter_system, M_CAMERA_PRESENT, CamPresentFunction, &_systemData);
StartCameraDetectionThreads(_systemData, false);
MIL_INT keyPressed = 0;
bool sortCameraListInConsole = true;
MIL_DOUBLE startTime, currentTime;
MappTimer(M_TIMER_READ, &startTime);
auto lastdigitizerCount = _systemData._digitizers.size();
while (keyPressed != 'q')
{
MosSleep(50);
if (_systemData._display->IsWindowClosing())
break;
_systemData._display->PollEvents();
MappTimer(M_TIMER_READ, ¤tTime);
if (keyPressed || ((currentTime - startTime) > 1.0))
{
startTime = currentTime;
if (keyPressed)
{
MosScreenScroll(TRUE);
ProcessUserInput(keyPressed, _systemData, sortCameraListInConsole);
keyPressed = 0;
}
if (lastdigitizerCount != _systemData._digitizers.size())
{
sortCameraListInConsole = true;
lastdigitizerCount = _systemData._digitizers.size();
}
if (sortCameraListInConsole)
{
MilMutexLockGuard Lock(_systemData._mutex);
_systemData._digitizers.sort(compare_digitizers_for_sorting);
MosScreenClear();
printCommands();
sortCameraListInConsole = false;
}
int i = 0;
MosScreenSetPosition(0,27);
{
MilMutexLockGuard Lock(_systemData._mutex);
for (auto dig_iter = _systemData._digitizers.begin(); dig_iter != _systemData._digitizers.end(); ++dig_iter)
{
auto pdig = *dig_iter;
auto bufferFormat = pdig->GetPixelFormatString();
MIL_STRING processing;
if (pdig->IsProcessing())
processing = MIL_TEXT("proc ");
if (pdig->IsEncoding())
processing += MIL_TEXT("encoding ");
MIL_STRING description = pdig->GetInputDescription();
description.resize(20, ' ');
MIL_STRING_STREAM sstats;
MIL_STRING stat = sstats.str();
stat.resize(79, ' ');
MosPrintf(MIL_TEXT("%s\n"), stat.c_str());
MosScreenRefresh();
}
}
auto renderSource = _systemData._display->GetRenderSource();
if (renderSource == eRenderFromThread)
MosPrintf(MIL_TEXT("\nDisplay rendered from independent thread (rendering at display rate). \n"));
else
MosPrintf(MIL_TEXT("\nDisplay rendered from grab callback (rendering at frame rate). \n"));
MosScreenRefresh();
{
int latencyCount = 0;
int latencyDropCount = 0;
double curavgLatency_ms = 0;
double avgLatency_ms = 0;
const char *latencySrc = NULL;
const char *latencyDest = NULL;
if (_systemData._display->LatenciesGet(&latencySrc, &latencyDest, curavgLatency_ms, latencyCount, latencyDropCount, avgLatency_ms))
{
MosPrintf(MIL_TEXT("\nLatency in ms between input %s and display %s:\n"), latencySrc, latencyDest);
MosPrintf(MIL_TEXT(" Avg latency of %.2f (cur: %.2f) on %d grabbed frames, %d frame(s) drop. \n"), avgLatency_ms, curavgLatency_ms, latencyCount, latencyDropCount);
}
}
}
MosScreenScroll(FALSE);
if (MosKbhit())
keyPressed = MosGetch();
}
MosPrintf(MIL_TEXT("\nExiting.\n"));
FreeCameraDetectionThreads(_systemData);
for (auto iter_system = _systemData._systemIDs.begin(); iter_system != _systemData._systemIDs.end(); ++iter_system)
MsysHookFunction(*iter_system, M_CAMERA_PRESENT + M_UNHOOK, CamPresentFunction, &_systemData);
_systemData.Free();
MosScreenRelease();
MappFree(MilApplication);
return 0;
}
bool compare_digitizers_for_sorting(const CMILDigitizerHandler *first, const CMILDigitizerHandler *second)
{
if (*first < *second)
return true;
return false;
}
MIL_UINT32 MFTYPE CameraDetectThread(void *pCAMERA_DETECT_PARAM_VOID)
{
_SYSTEM_DATA::CAMERA_DETECT_PARAM *pcameraDetectParam = (_SYSTEM_DATA::CAMERA_DETECT_PARAM *)pCAMERA_DETECT_PARAM_VOID;
if (pcameraDetectParam)
{
MIL_INT numberOfDigitizers = 0;
CmilDigitizerFactory dig_factory;
auto psystem = pcameraDetectParam->pSystem;
MsysInquire(pcameraDetectParam->systemID, M_DIGITIZER_NUM, &numberOfDigitizers);
for (int i = 0; i < numberOfDigitizers; i++)
{
auto pdig = dig_factory.AllocateMILDigHandler(pcameraDetectParam->systemID, i);
if (pdig->DigAlloc())
{
MilMutexLockGuard Lock(psystem->_mutex);
psystem->_digitizers.push_back(pdig);
pdig->SetDisplay(psystem->_display);
pdig->StartGrab();
}
else
delete pdig;
}
psystem->_display->RearrangeTiles(eSIDE_BY_SIDE_BOTTOM);
}
return 0;
}
void StartCameraDetectionThreads(_SYSTEM_DATA &SystemData, bool wait)
{
for (auto itcamDetect = SystemData._threadcameradetects.begin(); itcamDetect != SystemData._threadcameradetects.end(); ++itcamDetect)
{
MthrWait(itcamDetect->threadcameradetectId, M_THREAD_END_WAIT, M_NULL);
MthrFree(itcamDetect->threadcameradetectId);
}
SystemData._threadcameradetects.clear();
if (SystemData._mutex == M_NULL)
MthrAlloc(M_DEFAULT_HOST, M_MUTEX, M_DEFAULT, M_NULL, M_NULL, &SystemData._mutex);
for (auto itsystem = SystemData._systemIDs.begin(); itsystem != SystemData._systemIDs.end(); ++itsystem)
{
_SYSTEM_DATA::CAMERA_DETECT_PARAM param = { &SystemData, *itsystem, 0 };
SystemData._threadcameradetects.push_back(param);
}
for (auto itcamDetect = SystemData._threadcameradetects.begin(); itcamDetect != SystemData._threadcameradetects.end(); ++itcamDetect)
{
auto &cameraDetectParam = *itcamDetect;
MthrAlloc(M_DEFAULT_HOST, M_THREAD, M_DEFAULT, &CameraDetectThread, &cameraDetectParam, &cameraDetectParam.threadcameradetectId);
}
if (wait)
FreeCameraDetectionThreads(SystemData);
}
void FreeCameraDetectionThreads(_SYSTEM_DATA &SystemData)
{
for (auto itcamDetect = SystemData._threadcameradetects.begin(); itcamDetect != SystemData._threadcameradetects.end(); ++itcamDetect)
{
MthrWait(itcamDetect->threadcameradetectId, M_THREAD_END_WAIT, M_NULL);
MthrFree(itcamDetect->threadcameradetectId);
}
SystemData._threadcameradetects.clear();
}
MIL_INT MFTYPE CamPresentFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
{
MIL_ID milsystem;
MobjInquire(HookId, M_OWNER_SYSTEM, &milsystem);
if (HookType == M_CAMERA_PRESENT)
{
auto &systemData = *(SYSTEM_DATA *)HookDataPtr;
MilMutexLockGuard Lock(systemData._mutex);
MIL_INT isCamPresent = 0;
MIL_INT digitizerDeviceNbr = 0;
auto &digitizers = systemData._digitizers;
MsysGetHookInfo(milsystem, HookId, M_CAMERA_PRESENT, &isCamPresent);
MsysGetHookInfo(milsystem, HookId, M_NUMBER, &digitizerDeviceNbr);
CMILDigitizerHandler *pcurrentDig = NULL;
auto iter_dig = digitizers.begin();
for (; iter_dig != digitizers.end(); ++iter_dig)
{
if (((*iter_dig)->GetSysId() == milsystem) && ((*iter_dig)->GetDevNum() == digitizerDeviceNbr))
{
pcurrentDig = *iter_dig;
break;
}
}
if (isCamPresent)
{
if (pcurrentDig)
{
pcurrentDig->DigFree();
if (pcurrentDig->DigAlloc())
{
pcurrentDig->StartGrab();
}
else
{
delete pcurrentDig;
digitizers.erase(iter_dig);
}
}
else
{
CmilDigitizerFactory dig_factory;
auto dig = dig_factory.AllocateMILDigHandler(milsystem, M_DEV0 + digitizerDeviceNbr);
if (dig && dig->DigAlloc())
{
digitizers.push_back(dig);
dig->SetDisplay(systemData._display);
dig->StartGrab();
}
}
}
else if (pcurrentDig)
{
delete pcurrentDig;
digitizers.erase(iter_dig);
}
}
return 0;
}
void ProcessUserInput(MIL_INT keyPressed, SYSTEM_DATA &systemData, bool &sortAndRearrangeDisplay)
{
auto &digitizers = systemData._digitizers;
decltype(systemData._digitizers) supportedDigitizers;
PIXEL_FORMAT pixelFormat = eMono8;
sortAndRearrangeDisplay = true;
switch (keyPressed)
{
case 'a':
case 'd':
case 'e':
case 'b':
case 't':
case 'p':
{
if (keyPressed == 'p')
{
std::set<PIXEL_FORMAT> pixelFormats;
for (auto iter_dig = digitizers.begin(); iter_dig != digitizers.end(); ++iter_dig)
{
auto dig = *iter_dig;
auto digpixelFormats = dig->SupportedPixelFormats();
for (auto iter_pixelformats = digpixelFormats.begin(); iter_pixelformats != digpixelFormats.end(); ++iter_pixelformats)
pixelFormats.insert(*iter_pixelformats);
}
int i = 0;
MosPrintf(MIL_TEXT("\n\nSelect pixel format: \n"));
for (auto iter_pf = pixelFormats.begin(); iter_pf != pixelFormats.end(); ++iter_pf)
{
MIL_STRING_STREAM pixelformat;
pixelformat << i++ << ": " << GetPixelFormatName(PfncFormat(*iter_pf)) << "\t\t" << GetPixelFormatDescription(PfncFormat(*iter_pf)) << std::endl;
MosPrintf(pixelformat.str().c_str());
}
MosScreenRefresh();
auto secondKey = MosGetch();
auto pixelFormatNumber = int(secondKey - '0');
if (pixelFormatNumber >= 0 && pixelFormatNumber < int(pixelFormats.size()))
pixelFormat = PIXEL_FORMAT(*std::next(pixelFormats.begin(), pixelFormatNumber));
}
CMILDigitizerHandler *pselectedCamera = NULL;
bool allCameras = false;
if (digitizers.size() > 1)
{
MosPrintf(MIL_TEXT("\nSelect camera number (0 - %d) or 'a' for all cameras: "), digitizers.size() - 1);
MosScreenRefresh();
MIL_INT Key = MosGetch();
int cameraNumber = int(Key - '0');
if (Key == 'a')
allCameras = true;
else if (cameraNumber >= 0 && (cameraNumber < int(digitizers.size())))
{
MilMutexLockGuard Lock(systemData._mutex);
auto it = digitizers.begin();
std::advance(it, cameraNumber);
pselectedCamera = *it;
}
}
else if (digitizers.size() == 1)
{
allCameras = true;
}
if (pselectedCamera || allCameras)
{
MilMutexLockGuard Lock(systemData._mutex);
auto iter = digitizers.begin();
while (iter != digitizers.end())
{
auto current = iter++;
auto dig = *current;
if (allCameras || pselectedCamera == dig)
{
if (keyPressed == 'd')
{
delete dig;
digitizers.erase(current);
}
else if (keyPressed == 'b')
MdigControl(dig->GetDigId(), M_GC_FEATURE_BROWSER, M_OPEN + M_ASYNCHRONOUS);
else if (keyPressed == 'p')
dig->SetPixelFormat(pixelFormat);
else if (keyPressed == 'a')
dig->SetProcessing(!dig->IsProcessing());
else if (keyPressed == 'e')
{
if (!(MappInquire(M_DEFAULT, M_LICENSE_MODULES, M_NULL) & M_LICENSE_JPEGSTD))
{
MosPrintf(MIL_TEXT("Sorry, no encoding license present. Press <Enter> to continue.\n"));
MosScreenRefresh();
MosGetch();
}
else
{
dig->SetEncoding(!dig->IsEncoding());
}
}
else if (keyPressed == 't')
{
if (dig->GetOverlayText().size())
dig->SetOverlayText(MIL_TEXT(""));
else
dig->SetOverlayText(dig->GetInputDescriptionBrief());
}
}
}
}
}
break;
case 'r':
systemData._display->RearrangeTiles(eNEXTPATTERN);
break;
case 'g':
{
auto renderSource = systemData._display->GetRenderSource();
if (renderSource == eRenderFromThread)
systemData._display->SetRenderSource(eRenderFromGrabCallBack);
else
systemData._display->SetRenderSource(eRenderFromThread);
}
break;
case 'f':
{
int monitorcount = systemData._display->GetMonitorCount();
MosPrintf(MIL_TEXT("\nSelect monitor number to display window (0 - %d): \n"), monitorcount - 1);
MosPrintf(MIL_TEXT("0: Windowed mode\n"));
for (int i = 1; i < monitorcount; i++)
{
MIL_STRING_STREAM monitorstr;
monitorstr << i << ": " << systemData._display->GetMonitorName(i) << std::endl;
MosPrintf(monitorstr.str().c_str());
}
MosScreenRefresh();
auto secondKey = MosGetch();
auto monitorNumber = int(secondKey - '0');
if((monitorNumber >= 0) & (monitorNumber < monitorcount))
systemData._display->SetWindowMonitor(monitorNumber);
}
break;
case 's':
{
auto FitToScreen = systemData._display->GetScalingFitToWindow();
FitToScreen = !FitToScreen;
systemData._display->SetScalingFitToWindow(FitToScreen);
}
break;
case 'n':
{
StartCameraDetectionThreads(systemData, false);
MosPrintf(MIL_TEXT("\nDetecting new cameras... please wait...\n"));
}
break;
default:
sortAndRearrangeDisplay = false;
}
}