#define PFNC_INCLUDE_HELPERS
#include <mil.h>
#include <iomanip>
#include <algorithm>
using namespace std;
#include "MdigHandler.h"
template<typename T>
T roundUp(T numToRound, size_t multiple)
{
if (multiple == 0)
return numToRound;
T remainder = numToRound % multiple;
if (remainder == 0)
return numToRound;
return numToRound + T(multiple) - remainder;
}
#if M_MIL_UNICODE_API
#include <codecvt>
#endif
MIL_STRING str2Mstr(const std::string& str)
{
#if M_MIL_UNICODE_API
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converterX;
return converterX.from_bytes(str);
#else
return str;
#endif
}
std::string Mstr2str(const MIL_STRING& milstr)
{
#if M_MIL_UNICODE_API
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converterX;
return converterX.to_bytes(milstr);
#else
return milstr;
#endif
}
bool CMILDigitizerHandler::DigAlloc()
{
_pixelFormatString.clear();
MdigAlloc(_milSystemId, _digDevNum, GetDCFName().c_str(), M_DEFAULT, &_milDigitizerId);
if (_milDigitizerId)
{
MdigInquire(_milDigitizerId, M_SIZE_BAND, &_sizeBand);
MdigInquire(_milDigitizerId, M_SIZE_X, &_sizeX);
MdigInquire(_milDigitizerId, M_SIZE_Y, &_sizeY);
if (MdigInquire(_milDigitizerId, M_CAMERA_PRESENT, M_NULL) == M_NO)
{
MdigFree(_milDigitizerId);
_milDigitizerId = M_NULL;
_inputDescription.clear();
_inputDescriptionBrief.clear();
}
MdigControl(_milDigitizerId, M_PROCESS_GRAB_MONITOR, M_DISABLE);
if (_pdisplay && (_tileId == 0))
{
_tileId = _pdisplay->TileAlloc(int(_sizeX), int(_sizeY));
std::stringstream tilestr;
tilestr << GetInputDescriptionBrief().c_str();
_pdisplay->TileIdentificationString(_tileId, tilestr.str().c_str());
}
}
return (_milDigitizerId ? true : false);
}
void CMILDigitizerHandler::DigFree()
{
_pixelFormatString.clear();
if (_pdisplay && _tileId)
_pdisplay->TileFree(_tileId);
_tileId = 0;
if (_milDigitizerId)
{
if (_isGrabbing)
StopGrab();
MdigControl(_milDigitizerId, M_GC_FEATURE_BROWSER, M_CLOSE);
MdigFree(_milDigitizerId);
}
FreeBuffers();
_inputDescription.clear();
_inputDescriptionBrief.clear();
_milDigitizerId = M_NULL;
_pdisplay = NULL;
}
void CMILDigitizerHandler::AllocateBuffer(eBUFFER_MAPPING mapping, PIXEL_FORMAT pixelFormat, MIL_INT sizeBand, MIL_INT dynamicSizeByte, MIL_ID &milBuffer, int &gpuBuffer)
{
MIL_INT64 Attribute = 0;
MIL_INT sizeX = _sizeX;
MIL_INT sizeY = _sizeY;
switch (pixelFormat)
{
case eMono8: Attribute = 0; break;
case eYUV422: Attribute = M_YUV16 + M_PACKED; break;
case eYUV422_10p: Attribute = M_DYNAMIC; break;
case eRGB24_planar: Attribute = M_RGB24 + M_PLANAR; break;
case eBGR32: Attribute = M_BGR32 + M_PACKED; break;
case eBGRa10p: Attribute = M_DYNAMIC; break;
case eYUV411_8p: Attribute = M_DYNAMIC; break;
default: break;
}
if ((Attribute & M_DYNAMIC) && dynamicSizeByte == 0)
{
MosPrintf(MIL_TEXT("Buffer allocation error when allocate DYNAMIC buffer on dig num %d. \n"), _digDevNum);
return;
}
if (mapping == eMIL_BUFFER_MAPPED_ON_A_GPU_BUFFER)
{
void *pHostAddress[3] = { NULL, NULL, NULL };
int pitchByte = 0;
gpuBuffer = _pdisplay->BufAlloc(int(sizeX), int(sizeY), pixelFormat, &pitchByte, pHostAddress);
if (gpuBuffer)
{
MbufCreateColor(_milSystemId, sizeBand, sizeX, sizeY, 8, M_IMAGE + M_PROC + M_GRAB + M_PAGED + Attribute, M_HOST_ADDRESS + M_PITCH_BYTE, pitchByte, pHostAddress, &milBuffer);
if (milBuffer == M_NULL)
{
_pdisplay->BufFree(gpuBuffer);
gpuBuffer = 0;
}
}
}
else if (mapping == eGPU_BUFFER_MAPPED_ON_A_MIL_BUFFER)
{
auto pitchPixel = sizeX;
if (dynamicSizeByte)
{
MbufAlloc1d(_milSystemId, dynamicSizeByte, 8, M_IMAGE + M_GRAB + M_DYNAMIC, &milBuffer);
}
else
{
MIL_INT _4KAlignement = (M_ALIGNMENT_RESERVED_BITS & 0xA);
MbufCreateColor(_milSystemId, sizeBand, sizeX, sizeY, 8, M_IMAGE + M_PROC + M_GRAB + Attribute, M_ALLOCATION + M_PITCH + _4KAlignement, pitchPixel, NULL, &milBuffer);
if (milBuffer == M_NULL)
{
pitchPixel = MIL_INT(roundUp(sizeX, 128));
MbufCreateColor(_milSystemId, sizeBand, sizeX, sizeY, 8, M_IMAGE + M_PROC + M_GRAB + Attribute, M_ALLOCATION + M_PITCH + _4KAlignement, pitchPixel, NULL, &milBuffer);
}
}
if (milBuffer)
{
void *pHostAddress[3] = { NULL, NULL, NULL };
MIL_INT pitchByte = 0;
MbufInquire(milBuffer, M_HOST_ADDRESS, pHostAddress);
if (pixelFormat == eYUV411_8p)
{
pitchByte = roundUp(int(_sizeX), 128);
auto plane1address = ((MIL_INT64)pHostAddress[0]) + (pitchByte * sizeY);
pHostAddress[1] = (void *)roundUp(plane1address, 0x1000);
}
if (pHostAddress[0] == NULL)
for (int ii = 0; ii < sizeBand; ii++)
{
MIL_ID bandID;
MIL_INT bandValue[3] = { M_RED, M_GREEN, M_BLUE };
MbufChildColor(milBuffer, bandValue[ii], &bandID);
MbufInquire(bandID, M_HOST_ADDRESS, &pHostAddress[ii]);
MbufFree(bandID);
}
MbufInquire(milBuffer, M_PITCH_BYTE, &pitchByte);
gpuBuffer = _pdisplay->BufCreate(int(sizeX), int(sizeY), pixelFormat, int(pitchByte), pHostAddress);
}
}
else if ((mapping == eMIL_BUFFER_HOST) || (mapping == eMIL_BUFFER_ON_BOARD))
{
bool isOnBoard = (mapping == eMIL_BUFFER_ON_BOARD) ? true : false;
if (dynamicSizeByte)
MbufAlloc1d(_milSystemId, dynamicSizeByte, 8, M_IMAGE + M_GRAB + M_DYNAMIC + (isOnBoard?M_ON_BOARD:0), &milBuffer);
else
MbufAllocColor(_milSystemId, sizeBand, sizeX, sizeY, 8, M_IMAGE + M_GRAB + Attribute + (isOnBoard ? M_ON_BOARD : M_PROC), &milBuffer);
}
}
void CMILDigitizerHandler::AllocateBuffers()
{
FreeBuffers();
MIL_INT dynamicBufferSizeByte = 0;
_sizeX = roundUp(int(_sizeX), 4);
auto grabPixelFormat = _pixelFormat;
{
auto pixelFormats = SupportedPixelFormats();
auto ispixelFormatSupported = (std::find(pixelFormats.begin(), pixelFormats.end(), _pixelFormat) != pixelFormats.end());
if (!ispixelFormatSupported)
grabPixelFormat = *pixelFormats.begin();
}
auto sizeBand = _sizeBand;
if (sizeBand == 1)
grabPixelFormat = eMono8;
if (grabPixelFormat == eMono8)
sizeBand = 1;
switch (grabPixelFormat)
{
case eYUV411_8p:
{
MdigControl(GetDigId(), M_PFNC_TARGET_FORMAT, PFNC_YCbCr411_8);
int pitchByte = roundUp(int(_sizeX), 128);
dynamicBufferSizeByte = (pitchByte * _sizeY) + ((pitchByte *2) * (_sizeY / 2)) + 0x1000;
}
break;
case eYUV422_10p:
{
MdigControl(GetDigId(), M_PFNC_TARGET_FORMAT, PFNC_YCbCr422_10p);
MIL_INT stride = ((_sizeX + 47) / 48) * 128;
int pitchByte = roundUp(int(stride), 128);
dynamicBufferSizeByte = pitchByte * _sizeY * 2;
}
break;
case eBGRa10p:
{
MdigControl(GetDigId(), M_PFNC_TARGET_FORMAT, PFNC_BGRa10p);
int pitchByte = roundUp(int(_sizeX), 128);
dynamicBufferSizeByte = pitchByte * _sizeY * 4;
}
break;
}
int bufSize = 0;
if (IsEncoding())
bufSize = _bufferingSizeWhenEncoding;
else
bufSize = _bufferingSizeWhenGrabbing;
for (int i = 0; i < bufSize; i++)
{
BUFFER buf;
int gpuBuffer = 0;
MIL_ID milBuffer = M_NULL;
if (dynamicBufferSizeByte)
AllocateBuffer(eGPU_BUFFER_MAPPED_ON_A_MIL_BUFFER, grabPixelFormat, 1, dynamicBufferSizeByte, milBuffer, gpuBuffer);
else
{
if (IsGrabInPagedMemorySupported() && _pdisplay->isAllocBufferSupported())
AllocateBuffer(eMIL_BUFFER_MAPPED_ON_A_GPU_BUFFER, grabPixelFormat, sizeBand, 0, milBuffer, gpuBuffer);
if (milBuffer == M_NULL)
AllocateBuffer(eGPU_BUFFER_MAPPED_ON_A_MIL_BUFFER, grabPixelFormat, sizeBand, 0, milBuffer, gpuBuffer);
}
if (milBuffer)
{
buf.dispid = gpuBuffer;
buf.tileId = (int)_digDevNum;
buf.pixelFormat = grabPixelFormat;
buf.milGrabBufferForProcessing = M_NULL;
buf.milGrabBufferMappedOnDisplay = milBuffer;
MbufClear(buf.milGrabBufferMappedOnDisplay, M_COLOR_DARK_BLUE);
MbufAllocColor(_milSystemId, sizeBand, _sizeX, _sizeY, 8, M_IMAGE + M_GRAB + M_PROC, &buf.milGrabBufferForProcessing);
MbufClear(buf.milGrabBufferForProcessing, M_COLOR_DARK_BLUE);
if (IsEncoding())
{
int gpuBufferNotUsed;
if(_seqHandler.isH264Board())
AllocateBuffer(eMIL_BUFFER_ON_BOARD, grabPixelFormat, sizeBand, dynamicBufferSizeByte, buf.milGrabBufferForEncoding, gpuBufferNotUsed);
else
AllocateBuffer(eMIL_BUFFER_HOST, grabPixelFormat, sizeBand, dynamicBufferSizeByte, buf.milGrabBufferForEncoding, gpuBufferNotUsed);
}
_allocatedBuffers.push_back(buf);
}
else
{
MosPrintf(MIL_TEXT("Buffer allocation error on dig num %d.\n"), _digDevNum);
break;
}
}
}
void CMILDigitizerHandler::SetDisplay(IMilDisplayEx *pdispOpenGL)
{
if (_pdisplay && _tileId && _pdisplay != pdispOpenGL)
{
_pdisplay->TileFree(_tileId);
_tileId = 0;
}
_pdisplay = pdispOpenGL;
if (_pdisplay && _tileId == 0)
{
_tileId = _pdisplay->TileAlloc(int(_sizeX), int(_sizeY));
std::stringstream tilestr;
tilestr << GetInputDescriptionBrief().c_str();
_pdisplay->TileIdentificationString(_tileId, tilestr.str().c_str());
SetOverlayText(GetInputDescriptionBrief());
}
}
void CMILDigitizerHandler::FreeBuffers()
{
if (_isGrabbing)
StopGrab();
for (auto iterBuf = _allocatedBuffers.begin(); iterBuf != _allocatedBuffers.end(); ++iterBuf)
{
if (_pdisplay)
_pdisplay->BufFree(iterBuf->dispid);
if (iterBuf->milGrabBufferForProcessing)
MbufFree(iterBuf->milGrabBufferForProcessing);
if (iterBuf->milGrabBufferForEncoding)
MbufFree(iterBuf->milGrabBufferForEncoding);
MbufFree(iterBuf->milGrabBufferMappedOnDisplay);
}
_allocatedBuffers.clear();
}
void CMILDigitizerHandler::StartGrab()
{
_pixelFormatString.clear();
_milDigProcessBuffers.clear();
_frameCountTotal = 0;
_frameRateCurrent = 0.0;
_startTime = 0;
_skipNextDisplay = false;
if (_milDigitizerId == M_NULL)
return;
if (_allocatedBuffers.size() == 0)
AllocateBuffers();
if (_allocatedBuffers.size())
{
_milDigProcessBuffers.clear();
_milDigProcessBufferMap.clear();
for (auto iterBuf = _allocatedBuffers.begin(); iterBuf != _allocatedBuffers.end(); ++iterBuf)
{
MIL_ID grabBuffer = M_NULL;
if (iterBuf->milGrabBufferForProcessing == M_NULL)
_processing = false;
if(iterBuf->milGrabBufferForEncoding == M_NULL)
_encoding = false;
if (MbufInquire(iterBuf->milGrabBufferMappedOnDisplay, M_EXTENDED_ATTRIBUTE, M_NULL) & M_DYNAMIC)
_processing = false;
if (IsEncoding())
grabBuffer = iterBuf->milGrabBufferForEncoding;
else if (IsProcessing())
grabBuffer = iterBuf->milGrabBufferForProcessing;
else
grabBuffer = iterBuf->milGrabBufferMappedOnDisplay;
_milDigProcessBuffers.push_back(grabBuffer);
#if !M_MIL_USE_LINUX
_milDigProcessBufferMap[grabBuffer] = iterBuf._Ptr;
#else
_milDigProcessBufferMap[grabBuffer]= iterBuf.base();
#endif
}
if (IsEncoding())
{
MIL_DOUBLE frameRate;
MdigInquire(_milDigitizerId, M_SELECTED_FRAME_RATE, &frameRate);
_seqHandler.SetFrameRate(frameRate);
_seqHandler.Start(GetInputDescription(), _milDigProcessBuffers[0]);
}
_isGrabbing = true;
MdigProcess(_milDigitizerId, &_milDigProcessBuffers[0], MIL_INT(_milDigProcessBuffers.size()),
M_START, M_DEFAULT, MILGrabCallbackFunction, this);
}
}
void CMILDigitizerHandler::StopGrab()
{
_isGrabbing = false;
if (_milDigProcessBuffers.size())
MdigProcess(_milDigitizerId, &_milDigProcessBuffers[0], MIL_INT(_milDigProcessBuffers.size()),
M_STOP, M_DEFAULT, MILGrabCallbackFunction, this);
_seqHandler.Stop();
_milDigProcessBuffers.clear();
}
void CMILDigitizerHandler::SetOverlayText(const MIL_STRING& itext)
{
if (_tileId)
_pdisplay->SetText(_tileId, Mstr2str(itext).c_str(), 10, 18);
}
MIL_STRING CMILDigitizerHandler::GetOverlayText() const
{
char text[255] = { 0 };
_pdisplay->GetTile(_tileId, NULL, NULL, text, sizeof(text), NULL, NULL, NULL, NULL);
return str2Mstr(text);
}
const MIL_STRING& CMILDigitizerHandler::GetGrabStats()
{
if (_allocatedBuffers.size() == 0)
{
_statText = MIL_TEXT("Not enough memory to allocate grab buffers.");
}
else
{
MIL_STRING_STREAM ss;
ss << _frameCountTotal << MIL_TEXT(" frames at ") << std::setprecision(4) << _frameRateCurrent << MIL_TEXT(" fps. ");
_statText = ss.str();
}
return _statText;
}
void CMILDigitizerHandler::UpdateBufferPixelFormat(BUFFER &buf)
{
if (_pixelFormatString.size() == 0 && _frameCountTotal > 0)
{
MIL_ID bufId = buf.milGrabBufferMappedOnDisplay;
MIL_INT64 Format = 0;
_pixelFormatString = str2Mstr(GetPixelFormatName((PfncFormat)buf.pixelFormat));
_bufferColorSpaceFormat = eCSC_FULL;
MbufInquire(bufId, M_EXTENDED_FORMAT, &Format);
if (M_IS_FORMAT_YUV(Format) || buf.pixelFormat == PFNC_YCbCr422_10p || buf.pixelFormat == PFNC_YCbCr411_8)
{
MIL_INT CbCRRange = 0;
MbufInquire(bufId, M_YCBCR_RANGE, &CbCRRange);
MIL_STRING_STREAM ss;
if (CbCRRange == M_YCBCR_SD)
{
_bufferColorSpaceFormat = eCSC_ITU_601;
ss << _pixelFormatString << MIL_TEXT(" ITU-601");
}
else if (CbCRRange == M_YCBCR_HD)
{
_bufferColorSpaceFormat = eCSC_ITU_709;
ss << _pixelFormatString << MIL_TEXT(" ITU-709");
}
else if (CbCRRange == M_YCBCR_UHD)
{
_bufferColorSpaceFormat = eCSC_ITU_2020;
ss << _pixelFormatString << MIL_TEXT(" ITU-2020");
}
else
{
ss << _pixelFormatString;
_bufferColorSpaceFormat = eCSC_FULL;
}
_pixelFormatString = ss.str();
for (auto iterBuffer = _allocatedBuffers.begin(); iterBuffer != _allocatedBuffers.end(); ++iterBuffer)
_pdisplay->BufSetColorSpace(iterBuffer->dispid, _bufferColorSpaceFormat);
}
}
return;
}
void CMILDigitizerHandler::RestartGrab()
{
bool tile_isMainTile = false;
int tile_posX, tile_posY, tile_sizeX, tile_sizeY;
tile_posX = tile_posY = tile_sizeX = tile_sizeY = 0;
auto currentRenderSource = _pdisplay->GetRenderSource();
if (currentRenderSource == eRenderFromGrabCallBack)
_pdisplay->SetRenderSource(eRenderFromThread);
if (_tileId)
_pdisplay->GetTile(_tileId, NULL, &tile_isMainTile, NULL, 0, &tile_posX, &tile_posY, &tile_sizeX, &tile_sizeY);
StopGrab();
FreeBuffers();
AllocateBuffers();
if (_tileId)
_pdisplay->SetTile(_tileId, true, tile_isMainTile, NULL, tile_posX, tile_posY, tile_sizeX, tile_sizeY);
StartGrab();
if (currentRenderSource == eRenderFromGrabCallBack)
_pdisplay->SetRenderSource(eRenderFromGrabCallBack);
}
void CMILDigitizerHandler::SetPixelFormat(PIXEL_FORMAT pixelFormat)
{
_pixelFormat = pixelFormat;
RestartGrab();
}
void CMILDigitizerHandler::SetProcessing(bool activate)
{
_processing = activate;
RestartGrab();
}
void CMILDigitizerHandler::SetEncoding(bool activate)
{
_encoding = activate;
RestartGrab();
}
const MIL_STRING& CMILDigitizerHandler::GetInputDescription()
{
if (_milDigitizerId && _inputDescription.size() == 0)
{
MIL_INT sizeX;
MIL_INT sizeY;
MIL_INT scanMode;
MIL_DOUBLE FrameRate;
auto brief = GetInputDescriptionBrief();
MdigInquire(_milDigitizerId, M_SIZE_X, &sizeX);
MdigInquire(_milDigitizerId, M_SIZE_Y, &sizeY);
MdigInquire(_milDigitizerId, M_SCAN_MODE, &scanMode);
MdigInquire(_milDigitizerId, M_SELECTED_FRAME_RATE, &FrameRate);
FrameRate += 0.01;
MIL_STRING_STREAM ss;
ss << GetInputDescriptionBrief() << MIL_TEXT(" ") << sizeX << MIL_TEXT("x") << sizeY << (scanMode == M_INTERLACE ? MIL_TEXT("i") : MIL_TEXT("p")) << std::setprecision(4) << FrameRate;
_inputDescription = ss.str();
}
return _inputDescription;
}
MIL_INT MFTYPE CMILDigitizerHandler::MILGrabCallbackFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
{
CMILDigitizerHandler *pThis = (CMILDigitizerHandler *)HookDataPtr;
pThis->GrabCallbackFunction(HookType, HookId);
return 0;
}
void CMILDigitizerHandler::GrabCallbackFunction(MIL_INT HookType, MIL_ID HookId)
{
if (HookType == M_MODIFIED_BUFFER)
{
if (!_isGrabbing)
return;
MIL_ID ModifiedBufferId;
MIL_DOUBLE grab_hw_timestamp = 0.0;
MdigGetHookInfo(HookId, M_MODIFIED_BUFFER + M_BUFFER_ID, &ModifiedBufferId);
MdigGetHookInfo(HookId, M_TIME_STAMP, &grab_hw_timestamp);
if (ModifiedBufferId)
{
const auto frameRateCount = 120;
if (_frameCountTotal % frameRateCount == 0)
{
if (_startTime)
{
auto deltaTime = grab_hw_timestamp - _startTime;
_frameRateCurrent = frameRateCount / deltaTime;
}
_startTime = grab_hw_timestamp;
}
auto buf_iter = _milDigProcessBufferMap.find(ModifiedBufferId);
if (buf_iter != _milDigProcessBufferMap.end())
{
auto &buf = *buf_iter->second;
if (buf.dispid)
{
if (_frameCountTotal == 1)
UpdateBufferPixelFormat(buf);
_frameCountTotal++;
if (!_skipNextDisplay)
{
if (IsProcessing())
MimArith(ModifiedBufferId, M_NULL, buf.milGrabBufferMappedOnDisplay, M_NOT);
else if (IsEncoding())
MbufCopy(ModifiedBufferId, buf.milGrabBufferMappedOnDisplay);
_pdisplay->UpdateDisplay(_tileId, buf.dispid, grab_hw_timestamp);
}
if (IsEncoding() && _frameCountTotal > 30)
_seqHandler.Feed(ModifiedBufferId);
}
}
if (_skipNextDisplay == false)
{
MIL_INT buffering_size_total = MdigInquire(_milDigitizerId, M_PROCESS_TOTAL_BUFFER_NUM, M_NULL);
MIL_INT buffering_size = MdigInquire(_milDigitizerId, M_PROCESS_PENDING_GRAB_NUM, M_NULL);
MIL_INT currentGrabbedFrames = buffering_size_total - buffering_size;
if (currentGrabbedFrames > getFrameBufferingLatency())
_skipNextDisplay = true;
}
else
_skipNextDisplay = false;
}
}
return;
}