16 #include "PlusConfigure.h" 23 #include <vtkObjectFactory.h> 26 #include <MediaFoundationVideoCaptureApi.h> 27 #include <FormatReader.h> 34 #include <Mfreadwrite.h> 46 const unsigned int DEFAULT_DEVICE_ID = 0;
47 const FrameSizeType DEFAULT_FRAME_SIZE = {640, 480, 1};
48 const double DEFAULT_ACQUISITION_RATE = 30;
49 const std::wstring DEFAULT_PIXEL_TYPE_NAME = L
"YUY2";
50 const GUID DEFAULT_PIXEL_TYPE = MFVideoFormat_YUY2;
51 const std::wstring MF_VIDEO_FORMAT_PREFIX = L
"MFVideoFormat_";
52 const int ERRMSGBUFFERSIZE = 1024;
53 template <
class T>
void SafeRelease(T** ppT)
64 class MmfVideoSourceReader :
public IMFSourceReaderCallback
70 , CaptureSourceReader(NULL)
72 , PlusDevice(plusDevice)
77 STDMETHODIMP QueryInterface(REFIID iid,
void** ppv);
78 STDMETHOD_(
ULONG, AddRef)();
79 STDMETHOD_(
ULONG, Release)();
80 STDMETHODIMP OnEvent(
DWORD, IMFMediaEvent*);
81 STDMETHODIMP OnFlush(
DWORD);
82 STDMETHODIMP OnReadSample(HRESULT hrStatus,
DWORD dwStreamIndex,
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample* pSample);
84 IMFMediaSource* CaptureSource;
85 IMFSourceReader* CaptureSourceReader;
92 STDMETHODIMP MmfVideoSourceReader::QueryInterface(REFIID iid,
void** ppv)
94 static const QITAB qit[] =
96 QITABENT(MmfVideoSourceReader, IMFSourceReaderCallback),
99 return QISearch(
this, qit, iid, ppv);
105 LONG uCount = InterlockedIncrement(&RefCount);
108 this->PlusDevice->Register(NULL);
116 ULONG uCount = InterlockedDecrement(&RefCount);
119 LOG_DEBUG(
"vtkPlusMmfVideoSource::Release - unregister");
120 this->PlusDevice->UnRegister(NULL);
126 STDMETHODIMP MmfVideoSourceReader::OnEvent(
DWORD, IMFMediaEvent*)
132 STDMETHODIMP MmfVideoSourceReader::OnFlush(
DWORD)
138 STDMETHODIMP MmfVideoSourceReader::OnReadSample(HRESULT hrStatus,
DWORD dwStreamIndex,
DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample* pSample)
140 igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->PlusDevice->Mutex);
142 if (!SUCCEEDED(hrStatus))
145 LOG_ERROR(
"Source Reader error: " << std::hex << hrStatus);
149 if (!this->PlusDevice->IsRecording())
154 if (this->CaptureSourceReader == NULL)
163 IMFMediaType* pType = NULL;
164 this->CaptureSourceReader->GetCurrentMediaType(this->PlusDevice->ActiveVideoFormat.StreamIndex, &pType);
167 LOG_ERROR(
"Cannot get current media type");
173 UINT32 actualWidth = 0;
174 UINT32 actualHeight = 0;
175 ::MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &actualWidth, &actualHeight);
176 if (actualWidth != this->PlusDevice->ActiveVideoFormat.FrameSize[0] ||
177 actualHeight != this->PlusDevice->ActiveVideoFormat.FrameSize[1])
179 LOG_ERROR(
"Unexpected frame size: " << actualWidth <<
"x" << actualHeight <<
" (expected: " << this->PlusDevice->ActiveVideoFormat.FrameSize[0] <<
"x" << this->PlusDevice->ActiveVideoFormat.FrameSize[1] <<
")");
186 GUID videoFormat = DEFAULT_PIXEL_TYPE;
187 pType->GetGUID(MF_MT_SUBTYPE, &videoFormat);
188 std::wstring videoFormatWStr = MfVideoCapture::FormatReader::StringFromGUID(videoFormat);
189 if (igsioCommon::HasSubstrInsensitive(videoFormatWStr, MF_VIDEO_FORMAT_PREFIX))
192 videoFormatWStr.erase(0, MF_VIDEO_FORMAT_PREFIX.size());
195 if (!igsioCommon::IsEqualInsensitive(videoFormatWStr, this->PlusDevice->ActiveVideoFormat.PixelFormatName))
197 LOG_ERROR_W(
"Unexpected video format: " << videoFormatWStr <<
" (expected: " << this->PlusDevice->ActiveVideoFormat.PixelFormatName <<
")");
201 IMFMediaBuffer* aBuffer = NULL;
202 DWORD bufferCount = 0;
203 pSample->GetBufferCount(&bufferCount);
206 LOG_ERROR(
"No buffer available in the sample.");
209 pSample->GetBufferByIndex(0, &aBuffer);
210 BYTE* bufferData = NULL;
212 DWORD currentLength = 0;
214 HRESULT hr = aBuffer->Lock(&bufferData, &maxLength, ¤tLength);
217 this->PlusDevice->AddFrame(bufferData, currentLength);
222 LOG_ERROR(
"Unable to lock the buffer.");
224 SafeRelease(&aBuffer);
227 if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
230 LOG_ERROR(
"End of stream reached. Capture device should never reach end of stream.");
231 this->PlusDevice->Disconnect();
236 this->CaptureSourceReader->ReadSample(this->PlusDevice->ActiveVideoFormat.StreamIndex, 0, NULL, NULL, NULL, NULL);
248 , Mutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
280 os << indent <<
"FrameIndex: " << (this->
FrameIndex ?
"On\n" :
"Off\n");
286 igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->
Mutex);
290 LOG_WARNING(
"There is a reference to this class from a previous connection");
295 GUID pixelFormat = DEFAULT_PIXEL_TYPE;
299 pixelFormat = MfVideoCapture::FormatReader::GUIDFromString(videoFormat);
300 if (pixelFormat == GUID_NULL)
302 LOG_ERROR_W(
"Cannot recognize requested pixel format: " << this->
RequestedVideoFormat.PixelFormatName <<
". Defaulting to \'" << DEFAULT_PIXEL_TYPE_NAME <<
"\'.");
303 pixelFormat = DEFAULT_PIXEL_TYPE;
307 if (!MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().SetupDevice(this->
RequestedVideoFormat.DeviceId, this->RequestedVideoFormat.StreamIndex,
308 this->RequestedVideoFormat.FrameSize[0], this->RequestedVideoFormat.FrameSize[1], this->AcquisitionRate, pixelFormat))
310 LOG_WARNING_W(
"Unable to init capture device with requested details:" 312 <<
", " << this->RequestedVideoFormat.FrameSize[0] <<
"x" << this->RequestedVideoFormat.FrameSize[1]
313 <<
", " << this->AcquisitionRate <<
"Hz, " << this->ActiveVideoFormat.PixelFormatName);
317 if (!MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().SetupDevice(DEFAULT_DEVICE_ID, 0, DEFAULT_FRAME_SIZE[0], DEFAULT_FRAME_SIZE[1], DEFAULT_ACQUISITION_RATE, DEFAULT_PIXEL_TYPE))
319 LOG_ERROR_W(
"Unable to initialize capture device with default details: device ID: " << DEFAULT_DEVICE_ID <<
" (" <<
GetCaptureDeviceName(DEFAULT_DEVICE_ID) <<
") stream 0, " << DEFAULT_FRAME_SIZE[0] <<
"x" << DEFAULT_FRAME_SIZE[1] <<
", " << DEFAULT_ACQUISITION_RATE <<
"Hz, " << DEFAULT_PIXEL_TYPE_NAME);
330 LOG_INFO_W(
"Backing up to connecting with default capture settings:" 332 <<
", " << this->
ActiveVideoFormat.FrameSize[0] <<
"x" << this->ActiveVideoFormat.FrameSize[1]
333 <<
", " << DEFAULT_ACQUISITION_RATE <<
"Hz, " << this->ActiveVideoFormat.PixelFormatName);
339 LOG_ERROR(
"Unable to request capture source from the media foundation library.");
343 unsigned int frameRate = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetFrameRate(this->
ActiveVideoFormat.DeviceId);
344 LOG_DEBUG_W(
"vtkPlusMmfVideoSource connected to device '" <<
GetActiveDeviceName() <<
"' at frame rate of " << frameRate <<
"Hz");
354 igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->
Mutex);
356 MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().CloseDevice(this->
ActiveVideoFormat.DeviceId);
365 igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->
Mutex);
371 MFCreateAttributes(&attr, 2);
372 hr = attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this->
MmfSourceReader);
373 hr = attr->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS,
TRUE);
375 hr = MFCreateSourceReaderFromMediaSource(this->
MmfSourceReader->CaptureSource, attr, &this->MmfSourceReader->CaptureSourceReader);
379 LOG_ERROR(
"Unable to create source reader from media source.");
388 IMFMediaType* pDecodeType = NULL;
389 hr = MFCreateMediaType(&pDecodeType);
390 hr = pDecodeType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
391 hr = pDecodeType->SetGUID(MF_MT_SUBTYPE, MfVideoCapture::FormatReader::GUIDFromString(videoFormat));
396 LOG_WARNING_W(
"Unable to set SourceReader output to requested format: " << this->
RequestedVideoFormat.PixelFormatName
397 <<
". Using device default.");
399 SafeRelease(&pDecodeType);
401 MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().StartRecording(this->
ActiveVideoFormat.DeviceId);
406 LOG_ERROR(
"Unable to request IMFMediaSource from the media foundation capture library. Unable to continue.");
416 LOG_DEBUG(
"vtkPlusMmfVideoSource::InternalStopRecording");
418 igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->
Mutex);
420 MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().StopRecording(this->
ActiveVideoFormat.DeviceId);
432 LOG_ERROR(
"No output channels defined for microsoft media foundation video source. Cannot proceed.");
446 for (
int i = 0;
i < numberOfVideoSources; ++
i)
452 if (currentFrameSize[0] != this->
ActiveVideoFormat.FrameSize[0] || currentFrameSize[1] != this->ActiveVideoFormat.FrameSize[1] || currentFrameSize[2] != 1)
459 unsigned int numberOfScalarComponents = (videoSource->
GetImageType() == US_IMG_RGB_COLOR ? 3 : 1);
463 this->
UncompressedVideoFrame.AllocateFrame(currentFrameSize, VTK_UNSIGNED_CHAR, numberOfScalarComponents);
477 if (deviceConfig->GetScalarAttribute(
"CaptureDeviceId", deviceId))
483 if (deviceConfig->GetScalarAttribute(
"CaptureStreamIndex", streamIndex))
488 int requestedFrameSize[2] = {static_cast<int>(DEFAULT_FRAME_SIZE[0]), static_cast<int>(DEFAULT_FRAME_SIZE[1])};
489 if (deviceConfig->GetVectorAttribute(
"FrameSize", 2, requestedFrameSize))
491 if (requestedFrameSize[0] < 0 || requestedFrameSize[1] < 0)
493 LOG_ERROR(
"Negative frame size defined in config file. Cannot continue.");
502 if (deviceConfig->GetAttribute(
"VideoFormat") != NULL)
504 auto attr = std::string(deviceConfig->GetAttribute(
"VideoFormat"));
523 XML_REMOVE_ATTRIBUTE(
"CaptureStreamIndex", deviceConfig);
526 deviceConfig->SetVectorAttribute(
"FrameSize", 2, frameSize);
528 deviceConfig->SetAttribute(
"VideoFormat", attr.c_str());
560 MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetDeviceNames(deviceNames);
567 unsigned int numberOfStreams = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetNumberOfStreams(deviceId);
568 for (
unsigned int streamIndex = 0; streamIndex < numberOfStreams; ++streamIndex)
570 unsigned int numberOfVideoFormats = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetNumberOfFormats(deviceId, streamIndex);
571 for (
unsigned int formatIndex = 0; formatIndex < numberOfVideoFormats; formatIndex++)
573 MfVideoCapture::MediaType type = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetFormat(deviceId, streamIndex, formatIndex);
574 std::wstring pixelType(type.MF_MT_SUBTYPEName.begin(), type.MF_MT_SUBTYPEName.end());
575 if (igsioCommon::HasSubstrInsensitive(pixelType, MF_VIDEO_FORMAT_PREFIX))
578 pixelType.erase(0, MF_VIDEO_FORMAT_PREFIX.size());
580 std::wostringstream strFriendlyName;
581 strFriendlyName <<
"Stream index " << streamIndex;
582 strFriendlyName <<
" - Frame size: " << type.width <<
"x" << type.height;
583 strFriendlyName <<
", video format: " << pixelType;
584 strFriendlyName <<
", frame rate: " << type.MF_MT_FRAME_RATE;
585 videoModes.push_back(strFriendlyName.str());
607 LOG_ERROR(
"vtkPlusMmfVideoSource::AddFrame skipped, not recording anymore");
613 for (
int i = 0;
i < numberOfVideoSources; ++
i)
626 if (igsioCommon::IsEqualInsensitive(this->
ActiveVideoFormat.PixelFormatName, L
"YUY2"))
628 if (bufferSize < frameSize[0] * frameSize[1] * 2)
630 LOG_ERROR(
"Failed to decode pixel data from YUY2 due to buffer size mismatch");
635 else if (igsioCommon::IsEqualInsensitive(this->
ActiveVideoFormat.PixelFormatName, L
"MJPG"))
639 else if (igsioCommon::IsEqualInsensitive(this->
ActiveVideoFormat.PixelFormatName, L
"RGB24"))
641 if (bufferSize < frameSize[0] * frameSize[1] * 3)
643 LOG_ERROR(
"Failed to decode pixel data from RGB24 due to buffer size mismatch");
650 LOG_ERROR_W(
"Unknown pixel type: " << this->
ActiveVideoFormat.PixelFormatName <<
" (only YUY2, MJPG and RGB24 are supported)");
665 LOG_ERROR(
"Error while decoding the grabbed image");
669 const double maximumFrameTimeVariance = 0.2;
671 double minimumTimeBetweenBetweenRecordedFramesSec = (1.0 - maximumFrameTimeVariance) / this->
GetAcquisitionRate();
672 double lastFrameTimeSec = -1.0;
673 double currentTime = vtkIGSIOAccurateTimer::GetSystemTime();
679 double secondsSinceLastFrame = currentTime - lastFrameTimeSec;
680 if (lastFrameTimeSec > 0 && secondsSinceLastFrame < minimumTimeBetweenBetweenRecordedFramesSec)
692 status = sourceStatus !=
PLUS_SUCCESS ? sourceStatus : status;
702 LOG_INFO_W(
"Supported video formats for Device Id " << deviceId <<
" (" <<
GetCaptureDeviceName(deviceId) <<
")");
703 std::vector<std::wstring> videoModes;
705 for (
auto modeIt = videoModes.begin(); modeIt != videoModes.end(); ++modeIt)
707 LOG_INFO_W(
" " << (*modeIt));
714 LOG_INFO(
"Found capture devices:");
715 std::vector<std::wstring> deviceNames;
718 for (
auto deviceNameIt = deviceNames.begin(); deviceNameIt != deviceNames.end(); ++deviceNameIt,
id++)
720 LOG_INFO_W(
" " <<
id <<
": " << (*deviceNameIt));
727 return MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetCaptureDeviceName(deviceId);
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *xmlElement)
US_IMAGE_TYPE GetImageType()
virtual PlusStatus InternalConnect()
vtkSmartPointer< vtkIGSIORecursiveCriticalSection > Mutex
virtual int GetNumberOfVideoSources() const
virtual void SetRequestedDeviceId(unsigned int deviceId)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
PlusStatus GetVideoSourceByIndex(const unsigned int index, vtkPlusDataSource *&aVideoSource)
PlusStatus SetInputFrameSize(unsigned int x, unsigned int y, unsigned int z)
virtual PlusStatus AddItem(vtkImageData *frame, US_IMAGE_ORIENTATION usImageOrientation, US_IMAGE_TYPE imageType, long frameNumber, double unfilteredTimestamp=UNDEFINED_TIMESTAMP, double filteredTimestamp=UNDEFINED_TIMESTAMP, const igsioFieldMapType *customFields=NULL)
STDMETHODIMP_(ULONG) MmfVideoSourceReader
static PlusStatus ConvertToBGR24(ComponentOrdering outputOrdering, PixelEncoding inputCompression, int width, int height, unsigned char *s, unsigned char *d)
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *xmlElement)
bool RequireImageOrientationInConfiguration
virtual PlusStatus NotifyConfigured()
std::wstring GetRequestedDeviceName()
PlusStatus SetPixelType(igsioCommon::VTKScalarPixelType pixelType)
virtual double GetAcquisitionRate() const
virtual void SetRequestedVideoFormat(const std::wstring &pixelFormatName)
virtual PlusStatus InternalStartRecording()
VideoFormat ActiveVideoFormat
void LogListOfCaptureDevices()
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
double GetUnfilteredTimestamp(double localTimeOffsetSec)
void LogListOfCaptureVideoFormats(unsigned int deviceId)
virtual US_IMAGE_ORIENTATION GetInputImageOrientation()
std::wstring GetCaptureDeviceName(unsigned int deviceId)
vtkStandardNewMacro(vtkPlusMmfVideoSource)
virtual ItemStatus GetLatestStreamBufferItem(StreamBufferItem *bufferItem)
PlusStatus UpdateFrameSize()
static PlusStatus ConvertToGray(int inputCompression, int width, int height, unsigned char *s, unsigned char *d)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
igsioVideoFrame UncompressedVideoFrame
void GetListOfCaptureVideoFormats(std::vector< std::wstring > &videoModes, unsigned int deviceId)
ChannelContainer OutputChannels
virtual void SetRequestedStreamIndex(unsigned int streamIndex)
virtual void SetRequestedFrameSize(const FrameSizeType &frameSize)
PlusStatus SetNumberOfScalarComponents(unsigned int numberOfScalarComponents)
virtual PlusStatus InternalStopRecording()
friend class MmfVideoSourceReader
VideoFormat RequestedVideoFormat
PlusStatus AddFrame(unsigned char *bufferData, DWORD bufferSize)
FrameSizeType GetInputFrameSize() const
Microsoft media foundation video digitizer.
virtual PlusStatus InternalDisconnect()
MmfVideoSourceReader * MmfSourceReader
virtual int GetNumberOfItems()
std::wstring GetActiveDeviceName()
void GetListOfCaptureDevices(std::vector< std::wstring > &deviceNames)
Interface to a 3D positioning tool, video source, or generalized data stream.