PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusMmfVideoSource.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 /*=========================================================================
8 The following copyright notice is applicable to parts of this file:
9 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
10 All rights reserved.
11 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
12 Authors include: Adam Rankin
13 =========================================================================*/
14 
15 // Local includes
16 #include "PlusConfigure.h"
17 #include "PixelCodec.h"
18 #include "vtkPlusChannel.h"
19 #include "vtkPlusDataSource.h"
20 #include "vtkPlusMmfVideoSource.h"
21 
22 // VTK includes
23 #include <vtkObjectFactory.h>
24 
25 // VideoCapture API includes
26 #include <MediaFoundationVideoCaptureApi.h>
27 #include <FormatReader.h>
28 
29 // Media foundation includes - require Microsoft Windows SDK 7.1 or later.
30 // Download from: http://www.microsoft.com/en-us/download/details.aspx?id=8279
31 #include <Mfapi.h>
32 #include <Mferror.h>
33 #include <Mfidl.h>
34 #include <Mfreadwrite.h>
35 
36 // Windows includes
37 #include <lmerr.h>
38 #include <shlwapi.h>
39 #include <tchar.h>
40 #include <windows.h>
41 
42 //----------------------------------------------------------------------------
43 
44 namespace
45 {
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; // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa370819(v=vs.85).aspx
51  const std::wstring MF_VIDEO_FORMAT_PREFIX = L"MFVideoFormat_";
52  const int ERRMSGBUFFERSIZE = 1024;
53  template <class T> void SafeRelease(T** ppT)
54  {
55  if (*ppT)
56  {
57  (*ppT)->Release();
58  *ppT = NULL;
59  }
60  }
61 }
62 
63 //----------------------------------------------------------------------------
64 class MmfVideoSourceReader : public IMFSourceReaderCallback
65 {
66 public:
67 
68  MmfVideoSourceReader(vtkPlusMmfVideoSource* plusDevice)
69  : CaptureSource(NULL)
70  , CaptureSourceReader(NULL)
71  , RefCount(0)
72  , PlusDevice(plusDevice)
73  {
74  };
75 
76  //------- IMFSourceReaderCallback functions ----------------------
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);
83 
84  IMFMediaSource* CaptureSource;
85  IMFSourceReader* CaptureSourceReader;
86 
87  long RefCount;
88  vtkPlusMmfVideoSource* PlusDevice;
89 };
90 
91 //----------------------------------------------------------------------------
92 STDMETHODIMP MmfVideoSourceReader::QueryInterface(REFIID iid, void** ppv)
93 {
94  static const QITAB qit[] =
95  {
96  QITABENT(MmfVideoSourceReader, IMFSourceReaderCallback),
97  { 0 },
98  };
99  return QISearch(this, qit, iid, ppv);
100 }
101 
102 //----------------------------------------------------------------------------
103 STDMETHODIMP_(ULONG) MmfVideoSourceReader::AddRef()
104 {
105  LONG uCount = InterlockedIncrement(&RefCount);
106  if (uCount == 1)
107  {
108  this->PlusDevice->Register(NULL);
109  }
110  return uCount;
111 }
112 
113 //----------------------------------------------------------------------------
114 STDMETHODIMP_(ULONG) MmfVideoSourceReader::Release()
115 {
116  ULONG uCount = InterlockedDecrement(&RefCount);
117  if (uCount == 0)
118  {
119  LOG_DEBUG("vtkPlusMmfVideoSource::Release - unregister");
120  this->PlusDevice->UnRegister(NULL);
121  }
122  return uCount;
123 }
124 
125 //----------------------------------------------------------------------------
126 STDMETHODIMP MmfVideoSourceReader::OnEvent(DWORD, IMFMediaEvent*)
127 {
128  return S_OK;
129 }
130 
131 //----------------------------------------------------------------------------
132 STDMETHODIMP MmfVideoSourceReader::OnFlush(DWORD)
133 {
134  return S_OK;
135 }
136 
137 //----------------------------------------------------------------------------
138 STDMETHODIMP MmfVideoSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample* pSample)
139 {
140  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->PlusDevice->Mutex);
141 
142  if (!SUCCEEDED(hrStatus))
143  {
144  // Streaming error
145  LOG_ERROR("Source Reader error: " << std::hex << hrStatus);
146  return S_FALSE;
147  }
148 
149  if (!this->PlusDevice->IsRecording())
150  {
151  return S_OK;
152  }
153 
154  if (this->CaptureSourceReader == NULL)
155  {
156  return S_FALSE;
157  }
158 
159  if (pSample != NULL)
160  {
161 
162  // Get the media type from the stream.
163  IMFMediaType* pType = NULL;
164  this->CaptureSourceReader->GetCurrentMediaType(this->PlusDevice->ActiveVideoFormat.StreamIndex, &pType);
165  if (pType == NULL)
166  {
167  LOG_ERROR("Cannot get current media type");
168  }
169 
170  // Check the image size, as it may be different from what we requested (even if setup does not give any error).
171  // Mostly happens when the native resolution has a different aspect ration (e.g., 640x480 is requested but actually 640x360 is received).
172  // The check has to be done here, the media type is not yet available at InternalConnect time.
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])
178  {
179  LOG_ERROR("Unexpected frame size: " << actualWidth << "x" << actualHeight << " (expected: " << this->PlusDevice->ActiveVideoFormat.FrameSize[0] << "x" << this->PlusDevice->ActiveVideoFormat.FrameSize[1] << ")");
180  return S_FALSE;
181  }
182 
183  // Check the pixel type, as it may be different from what we requested (even if setup does not give any error).
184  // Mostly happens for larger resolutions (e.g., when requesting webcam feed at 1280x720 with YUY then we get MJPG).
185  // The check has to be done here, the media type is not yet available at InternalConnect time.
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))
190  {
191  // found standard prefix, remove it
192  videoFormatWStr.erase(0, MF_VIDEO_FORMAT_PREFIX.size());
193  }
194 
195  if (!igsioCommon::IsEqualInsensitive(videoFormatWStr, this->PlusDevice->ActiveVideoFormat.PixelFormatName))
196  {
197  LOG_ERROR_W("Unexpected video format: " << videoFormatWStr << " (expected: " << this->PlusDevice->ActiveVideoFormat.PixelFormatName << ")");
198  return S_FALSE;
199  }
200 
201  IMFMediaBuffer* aBuffer = NULL;
202  DWORD bufferCount = 0;
203  pSample->GetBufferCount(&bufferCount);
204  if (bufferCount < 1)
205  {
206  LOG_ERROR("No buffer available in the sample.");
207  return S_FALSE;
208  }
209  pSample->GetBufferByIndex(0, &aBuffer);
210  BYTE* bufferData = NULL;
211  DWORD maxLength = 0;
212  DWORD currentLength = 0;
213 
214  HRESULT hr = aBuffer->Lock(&bufferData, &maxLength, &currentLength);
215  if (SUCCEEDED(hr))
216  {
217  this->PlusDevice->AddFrame(bufferData, currentLength);
218  aBuffer->Unlock();
219  }
220  else
221  {
222  LOG_ERROR("Unable to lock the buffer.");
223  }
224  SafeRelease(&aBuffer);
225  }
226 
227  if (MF_SOURCE_READERF_ENDOFSTREAM & dwStreamFlags)
228  {
229  // Reached the end of the stream.
230  LOG_ERROR("End of stream reached. Capture device should never reach end of stream.");
231  this->PlusDevice->Disconnect();
232  return S_FALSE;
233  // This should never occur under normal operation.
234  }
235 
236  this->CaptureSourceReader->ReadSample(this->PlusDevice->ActiveVideoFormat.StreamIndex, 0, NULL, NULL, NULL, NULL);
237 
238  return S_OK;
239 }
240 
241 //----------------------------------------------------------------------------
242 
244 
245 //----------------------------------------------------------------------------
247  : FrameIndex(0)
248  , Mutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
249 {
250  this->MmfSourceReader = new MmfVideoSourceReader(this);
252 
253  this->AcquisitionRate = DEFAULT_ACQUISITION_RATE;
254 
255  this->RequestedVideoFormat.DeviceId = DEFAULT_DEVICE_ID;
256  this->RequestedVideoFormat.StreamIndex = 0;
257  this->RequestedVideoFormat.FrameSize[0] = DEFAULT_FRAME_SIZE[0];
258  this->RequestedVideoFormat.FrameSize[1] = DEFAULT_FRAME_SIZE[1];
259  this->RequestedVideoFormat.FrameSize[2] = DEFAULT_FRAME_SIZE[2];
260  this->RequestedVideoFormat.PixelFormatName = DEFAULT_PIXEL_TYPE_NAME;
261 
262  this->ActiveVideoFormat.DeviceId = DEFAULT_DEVICE_ID;
263  this->ActiveVideoFormat.FrameSize[0] = 0;
264  this->ActiveVideoFormat.FrameSize[1] = 0;
265  this->ActiveVideoFormat.FrameSize[2] = 0;
266 }
267 
268 //----------------------------------------------------------------------------
270 {
271  delete this->MmfSourceReader;
272  this->MmfSourceReader = NULL;
273 }
274 
275 //----------------------------------------------------------------------------
276 void vtkPlusMmfVideoSource::PrintSelf(ostream& os, vtkIndent indent)
277 {
278  this->Superclass::PrintSelf(os, indent);
279 
280  os << indent << "FrameIndex: " << (this->FrameIndex ? "On\n" : "Off\n");
281 }
282 
283 //----------------------------------------------------------------------------
285 {
286  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
287 
288  if (this->MmfSourceReader->RefCount != 0)
289  {
290  LOG_WARNING("There is a reference to this class from a previous connection");
291  }
292 
294 
295  GUID pixelFormat = DEFAULT_PIXEL_TYPE;
296  if (!this->RequestedVideoFormat.PixelFormatName.empty())
297  {
298  std::wstring videoFormat = MF_VIDEO_FORMAT_PREFIX + this->RequestedVideoFormat.PixelFormatName;
299  pixelFormat = MfVideoCapture::FormatReader::GUIDFromString(videoFormat);
300  if (pixelFormat == GUID_NULL)
301  {
302  LOG_ERROR_W("Cannot recognize requested pixel format: " << this->RequestedVideoFormat.PixelFormatName << ". Defaulting to \'" << DEFAULT_PIXEL_TYPE_NAME << "\'.");
303  pixelFormat = DEFAULT_PIXEL_TYPE;
304  }
305  }
306 
307  if (!MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().SetupDevice(this->RequestedVideoFormat.DeviceId, this->RequestedVideoFormat.StreamIndex,
308  this->RequestedVideoFormat.FrameSize[0], this->RequestedVideoFormat.FrameSize[1], this->AcquisitionRate, pixelFormat))
309  {
310  LOG_WARNING_W("Unable to init capture device with requested details:"
311  << " device ID: " << this->RequestedVideoFormat.DeviceId << " (" << GetRequestedDeviceName() << ") stream " << this->RequestedVideoFormat.StreamIndex
312  << ", " << this->RequestedVideoFormat.FrameSize[0] << "x" << this->RequestedVideoFormat.FrameSize[1]
313  << ", " << this->AcquisitionRate << "Hz, " << this->ActiveVideoFormat.PixelFormatName);
314 
316 
317  if (!MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().SetupDevice(DEFAULT_DEVICE_ID, 0, DEFAULT_FRAME_SIZE[0], DEFAULT_FRAME_SIZE[1], DEFAULT_ACQUISITION_RATE, DEFAULT_PIXEL_TYPE))
318  {
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);
321  LogListOfCaptureVideoFormats(DEFAULT_DEVICE_ID);
322  return PLUS_FAIL;
323  }
324  this->ActiveVideoFormat.DeviceId = DEFAULT_DEVICE_ID;
325  this->ActiveVideoFormat.FrameSize[0] = DEFAULT_FRAME_SIZE[0];
326  this->ActiveVideoFormat.FrameSize[1] = DEFAULT_FRAME_SIZE[1];
327  this->ActiveVideoFormat.FrameSize[2] = DEFAULT_FRAME_SIZE[2];
328  this->ActiveVideoFormat.PixelFormatName = DEFAULT_PIXEL_TYPE_NAME;
329 
330  LOG_INFO_W("Backing up to connecting with default capture settings:"
331  << " device ID: " << this->ActiveVideoFormat.DeviceId << " (" << GetActiveDeviceName() << ")"
332  << ", " << this->ActiveVideoFormat.FrameSize[0] << "x" << this->ActiveVideoFormat.FrameSize[1]
333  << ", " << DEFAULT_ACQUISITION_RATE << "Hz, " << this->ActiveVideoFormat.PixelFormatName);
334  }
335 
336  this->MmfSourceReader->CaptureSource = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetMediaSource(this->ActiveVideoFormat.DeviceId);
337  if (this->MmfSourceReader->CaptureSource == NULL)
338  {
339  LOG_ERROR("Unable to request capture source from the media foundation library.");
340  return PLUS_FAIL;
341  }
342 
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");
345 
346  this->FrameIndex = 0;
347 
348  return PLUS_SUCCESS;
349 }
350 
351 //----------------------------------------------------------------------------
353 {
354  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
355 
356  MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().CloseDevice(this->ActiveVideoFormat.DeviceId);
357  this->MmfSourceReader->CaptureSource = NULL;
358 
359  return PLUS_SUCCESS;
360 }
361 
362 //----------------------------------------------------------------------------
364 {
365  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
366 
367  HRESULT hr;
368  if (this->MmfSourceReader->CaptureSource != NULL)
369  {
370  IMFAttributes* attr;
371  MFCreateAttributes(&attr, 2);
372  hr = attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this->MmfSourceReader);
373  hr = attr->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE);
374 
375  hr = MFCreateSourceReaderFromMediaSource(this->MmfSourceReader->CaptureSource, attr, &this->MmfSourceReader->CaptureSourceReader);
376 
377  if (FAILED(hr))
378  {
379  LOG_ERROR("Unable to create source reader from media source.");
380  return PLUS_FAIL;
381  }
382  this->UpdateFrameSize();
383 
384  attr->Release();
385 
386  std::wstring videoFormat = MF_VIDEO_FORMAT_PREFIX + this->RequestedVideoFormat.PixelFormatName;
387 
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));
392 
393  hr = this->MmfSourceReader->CaptureSourceReader->SetCurrentMediaType(this->ActiveVideoFormat.StreamIndex, NULL, pDecodeType);
394  if (FAILED(hr))
395  {
396  LOG_WARNING_W("Unable to set SourceReader output to requested format: " << this->RequestedVideoFormat.PixelFormatName
397  << ". Using device default.");
398  }
399  SafeRelease(&pDecodeType);
400 
401  MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().StartRecording(this->ActiveVideoFormat.DeviceId);
402  this->MmfSourceReader->CaptureSourceReader->ReadSample(this->ActiveVideoFormat.StreamIndex, 0, NULL, NULL, NULL, NULL);
403  }
404  else
405  {
406  LOG_ERROR("Unable to request IMFMediaSource from the media foundation capture library. Unable to continue.");
407  return PLUS_FAIL;
408  }
409 
410  return PLUS_SUCCESS;
411 }
412 
413 //----------------------------------------------------------------------------
415 {
416  LOG_DEBUG("vtkPlusMmfVideoSource::InternalStopRecording");
417 
418  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->Mutex);
419 
420  MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().StopRecording(this->ActiveVideoFormat.DeviceId);
421 
422  this->MmfSourceReader->CaptureSourceReader->Flush(this->ActiveVideoFormat.StreamIndex);
423  SafeRelease(&this->MmfSourceReader->CaptureSourceReader);
424  return PLUS_SUCCESS;
425 }
426 
427 //----------------------------------------------------------------------------
429 {
430  if (this->OutputChannels.size() == 0)
431  {
432  LOG_ERROR("No output channels defined for microsoft media foundation video source. Cannot proceed.");
433  this->CorrectlyConfigured = false;
434  return PLUS_FAIL;
435  }
436 
437  return PLUS_SUCCESS;
438 }
439 
440 //----------------------------------------------------------------------------
442 {
443  if (this->MmfSourceReader->CaptureSourceReader != NULL)
444  {
445  int numberOfVideoSources = this->GetNumberOfVideoSources();
446  for (int i = 0; i < numberOfVideoSources; ++i)
447  {
448  vtkPlusDataSource* videoSource(NULL);
449  this->GetVideoSourceByIndex(i, videoSource);
450 
451  FrameSizeType currentFrameSize = videoSource->GetInputFrameSize();
452  if (currentFrameSize[0] != this->ActiveVideoFormat.FrameSize[0] || currentFrameSize[1] != this->ActiveVideoFormat.FrameSize[1] || currentFrameSize[2] != 1)
453  {
454  currentFrameSize[0] = this->ActiveVideoFormat.FrameSize[0];
455  currentFrameSize[1] = this->ActiveVideoFormat.FrameSize[1];
456  currentFrameSize[2] = this->ActiveVideoFormat.FrameSize[2];
457  videoSource->SetInputFrameSize(currentFrameSize);
458  videoSource->SetPixelType(VTK_UNSIGNED_CHAR);
459  unsigned int numberOfScalarComponents = (videoSource->GetImageType() == US_IMG_RGB_COLOR ? 3 : 1);
460  videoSource->SetNumberOfScalarComponents(numberOfScalarComponents);
461  this->UncompressedVideoFrame.SetImageType(videoSource->GetImageType());
462  this->UncompressedVideoFrame.SetImageOrientation(videoSource->GetInputImageOrientation());
463  this->UncompressedVideoFrame.AllocateFrame(currentFrameSize, VTK_UNSIGNED_CHAR, numberOfScalarComponents);
464  }
465  }
466  }
467 
468  return PLUS_SUCCESS;
469 }
470 
471 //----------------------------------------------------------------------------
472 PlusStatus vtkPlusMmfVideoSource::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
473 {
474  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
475 
476  int deviceId = 0;
477  if (deviceConfig->GetScalarAttribute("CaptureDeviceId", deviceId))
478  {
479  this->RequestedVideoFormat.DeviceId = deviceId;
480  }
481 
482  int streamIndex = 0;
483  if (deviceConfig->GetScalarAttribute("CaptureStreamIndex", streamIndex))
484  {
485  this->RequestedVideoFormat.StreamIndex = (DWORD)streamIndex;
486  }
487 
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))
490  {
491  if (requestedFrameSize[0] < 0 || requestedFrameSize[1] < 0)
492  {
493  LOG_ERROR("Negative frame size defined in config file. Cannot continue.");
494  return PLUS_FAIL;
495  }
496  this->RequestedVideoFormat.FrameSize[0] = static_cast<unsigned int>(requestedFrameSize[0]);
497  this->RequestedVideoFormat.FrameSize[1] = static_cast<unsigned int>(requestedFrameSize[1]);
498  this->RequestedVideoFormat.FrameSize[2] = 1;
499  }
500 
501  this->RequestedVideoFormat.PixelFormatName.clear();
502  if (deviceConfig->GetAttribute("VideoFormat") != NULL)
503  {
504  auto attr = std::string(deviceConfig->GetAttribute("VideoFormat"));
505  this->RequestedVideoFormat.PixelFormatName = std::wstring(attr.begin(), attr.end());
506  }
507 
508  return PLUS_SUCCESS;
509 }
510 
511 //----------------------------------------------------------------------------
512 PlusStatus vtkPlusMmfVideoSource::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
513 {
514  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
515 
516  deviceConfig->SetIntAttribute("CaptureDeviceId", this->RequestedVideoFormat.DeviceId);
517  if (this->RequestedVideoFormat.StreamIndex != 0)
518  {
519  deviceConfig->SetIntAttribute("CaptureStreamIndex", this->RequestedVideoFormat.StreamIndex);
520  }
521  else
522  {
523  XML_REMOVE_ATTRIBUTE("CaptureStreamIndex", deviceConfig);
524  }
525  int frameSize[2] = { static_cast<int>(this->RequestedVideoFormat.FrameSize[0]), static_cast<int>(this->RequestedVideoFormat.FrameSize[1]) };
526  deviceConfig->SetVectorAttribute("FrameSize", 2, frameSize);
527  auto attr = std::string(this->RequestedVideoFormat.PixelFormatName.begin(), this->RequestedVideoFormat.PixelFormatName.end());
528  deviceConfig->SetAttribute("VideoFormat", attr.c_str());
529 
530  return PLUS_SUCCESS;
531 }
532 
533 //----------------------------------------------------------------------------
534 void vtkPlusMmfVideoSource::SetRequestedVideoFormat(const std::wstring& pixelFormatName)
535 {
536  this->RequestedVideoFormat.PixelFormatName = pixelFormatName;
537 }
538 
539 //----------------------------------------------------------------------------
541 {
542  this->RequestedVideoFormat.StreamIndex = (DWORD)streamIndex;
543 }
544 
545 //----------------------------------------------------------------------------
546 void vtkPlusMmfVideoSource::SetRequestedFrameSize(const FrameSizeType& frameSize)
547 {
548  this->RequestedVideoFormat.FrameSize = frameSize;
549 }
550 
551 //----------------------------------------------------------------------------
553 {
554  this->RequestedVideoFormat.DeviceId = deviceId;
555 }
556 
557 //----------------------------------------------------------------------------
558 void vtkPlusMmfVideoSource::GetListOfCaptureDevices(std::vector<std::wstring>& deviceNames)
559 {
560  MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetDeviceNames(deviceNames);
561 }
562 
563 //----------------------------------------------------------------------------
564 void vtkPlusMmfVideoSource::GetListOfCaptureVideoFormats(std::vector<std::wstring>& videoModes, unsigned int deviceId)
565 {
566  videoModes.clear();
567  unsigned int numberOfStreams = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetNumberOfStreams(deviceId);
568  for (unsigned int streamIndex = 0; streamIndex < numberOfStreams; ++streamIndex)
569  {
570  unsigned int numberOfVideoFormats = MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetNumberOfFormats(deviceId, streamIndex);
571  for (unsigned int formatIndex = 0; formatIndex < numberOfVideoFormats; formatIndex++)
572  {
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))
576  {
577  // found standard prefix, remove it
578  pixelType.erase(0, MF_VIDEO_FORMAT_PREFIX.size());
579  }
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());
586  }
587  }
588 }
589 
590 //----------------------------------------------------------------------------
592 {
593  return GetCaptureDeviceName(this->RequestedVideoFormat.DeviceId);
594 }
595 
596 //----------------------------------------------------------------------------
598 {
599  return GetCaptureDeviceName(this->ActiveVideoFormat.DeviceId);
600 }
601 
602 //----------------------------------------------------------------------------
603 PlusStatus vtkPlusMmfVideoSource::AddFrame(unsigned char* bufferData, DWORD bufferSize)
604 {
605  if (!this->Recording)
606  {
607  LOG_ERROR("vtkPlusMmfVideoSource::AddFrame skipped, not recording anymore");
608  return PLUS_SUCCESS;
609  }
610 
611  int numberOfVideoSources = this->GetNumberOfVideoSources();
612  PlusStatus status = PLUS_SUCCESS;
613  for (int i = 0; i < numberOfVideoSources; ++i)
614  {
615  vtkPlusDataSource* videoSource(NULL);
616  if (this->GetVideoSourceByIndex(i, videoSource) != PLUS_SUCCESS)
617  {
618  return PLUS_FAIL;
619  }
620  this->FrameIndex++;
621 
622  FrameSizeType frameSize = videoSource->GetInputFrameSize();
623 
624  PlusStatus decodingStatus(PLUS_SUCCESS);
626  if (igsioCommon::IsEqualInsensitive(this->ActiveVideoFormat.PixelFormatName, L"YUY2"))
627  {
628  if (bufferSize < frameSize[0] * frameSize[1] * 2)
629  {
630  LOG_ERROR("Failed to decode pixel data from YUY2 due to buffer size mismatch");
631  return PLUS_FAIL;
632  }
634  }
635  else if (igsioCommon::IsEqualInsensitive(this->ActiveVideoFormat.PixelFormatName, L"MJPG"))
636  {
638  }
639  else if (igsioCommon::IsEqualInsensitive(this->ActiveVideoFormat.PixelFormatName, L"RGB24"))
640  {
641  if (bufferSize < frameSize[0] * frameSize[1] * 3)
642  {
643  LOG_ERROR("Failed to decode pixel data from RGB24 due to buffer size mismatch");
644  return PLUS_FAIL;
645  }
647  }
648  else
649  {
650  LOG_ERROR_W("Unknown pixel type: " << this->ActiveVideoFormat.PixelFormatName << " (only YUY2, MJPG and RGB24 are supported)");
651  return PLUS_FAIL;
652  }
653 
654  if (videoSource->GetImageType() == US_IMG_RGB_COLOR)
655  {
656  decodingStatus = PixelCodec::ConvertToBGR24(PixelCodec::ComponentOrder_RGB, encoding, frameSize[0], frameSize[1], bufferData, (unsigned char*)this->UncompressedVideoFrame.GetScalarPointer());
657  }
658  else
659  {
660  decodingStatus = PixelCodec::ConvertToGray(encoding, frameSize[0], frameSize[1], bufferData, (unsigned char*)this->UncompressedVideoFrame.GetScalarPointer());
661  }
662 
663  if (decodingStatus != PLUS_SUCCESS)
664  {
665  LOG_ERROR("Error while decoding the grabbed image");
666  return PLUS_FAIL;
667  }
668 
669  const double maximumFrameTimeVariance = 0.2; // to make sure we don't drop frames because of slight variance in acquisition rate, we allow up to 20% higher frame rate before we start dropping frames
670  double acquisitionRate = this->GetAcquisitionRate();
671  double minimumTimeBetweenBetweenRecordedFramesSec = (1.0 - maximumFrameTimeVariance) / this->GetAcquisitionRate();
672  double lastFrameTimeSec = -1.0;
673  double currentTime = vtkIGSIOAccurateTimer::GetSystemTime();
674 
675  StreamBufferItem latestFrame;
676  if (videoSource->GetNumberOfItems() > 2 && videoSource->GetLatestStreamBufferItem(&latestFrame) == ITEM_OK)
677  {
678  lastFrameTimeSec = latestFrame.GetUnfilteredTimestamp(0.0);
679  double secondsSinceLastFrame = currentTime - lastFrameTimeSec;
680  if (lastFrameTimeSec > 0 && secondsSinceLastFrame < minimumTimeBetweenBetweenRecordedFramesSec)
681  {
682  // For some webcams, the requested acquistion rate may not be availiable (not supported, or error in configuration).
683  // In this case we can artificially limit frames to the requested acquisition rate by ignoring frames.
684 
685  // The required time has not elapsed between frames.
686  // Do not need to record this frame.
687  return PLUS_SUCCESS;
688  }
689  }
690 
691  PlusStatus sourceStatus = videoSource->AddItem(&this->UncompressedVideoFrame, this->FrameIndex, currentTime);
692  status = sourceStatus != PLUS_SUCCESS ? sourceStatus : status;
693  }
694 
695  this->Modified();
696  return status;
697 }
698 
699 //----------------------------------------------------------------------------
701 {
702  LOG_INFO_W("Supported video formats for Device Id " << deviceId << " (" << GetCaptureDeviceName(deviceId) << ")");
703  std::vector<std::wstring> videoModes;
704  GetListOfCaptureVideoFormats(videoModes, deviceId);
705  for (auto modeIt = videoModes.begin(); modeIt != videoModes.end(); ++modeIt)
706  {
707  LOG_INFO_W(" " << (*modeIt));
708  }
709 }
710 
711 //----------------------------------------------------------------------------
713 {
714  LOG_INFO("Found capture devices:");
715  std::vector<std::wstring> deviceNames;
716  GetListOfCaptureDevices(deviceNames);
717  int id = 0;
718  for (auto deviceNameIt = deviceNames.begin(); deviceNameIt != deviceNames.end(); ++deviceNameIt, id++)
719  {
720  LOG_INFO_W(" " << id << ": " << (*deviceNameIt));
721  }
722 }
723 
724 //----------------------------------------------------------------------------
725 std::wstring vtkPlusMmfVideoSource::GetCaptureDeviceName(unsigned int deviceId)
726 {
727  return MfVideoCapture::MediaFoundationVideoCaptureApi::GetInstance().GetCaptureDeviceName(deviceId);
728 }
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)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
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)
Definition: PixelCodec.h:197
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *xmlElement)
bool RequireImageOrientationInConfiguration
for i
double AcquisitionRate
virtual PlusStatus NotifyConfigured()
#define PLUS_FAIL
Definition: PlusCommon.h:43
PlusStatus SetPixelType(igsioCommon::VTKScalarPixelType pixelType)
virtual double GetAcquisitionRate() const
virtual void SetRequestedVideoFormat(const std::wstring &pixelFormatName)
virtual PlusStatus InternalStartRecording()
unsigned long DWORD
Definition: ATC3DGm.h:451
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
double GetUnfilteredTimestamp(double localTimeOffsetSec)
void LogListOfCaptureVideoFormats(unsigned int deviceId)
virtual US_IMAGE_ORIENTATION GetInputImageOrientation()
unsigned long ULONG
Definition: ATC3DGm.h:432
std::wstring GetCaptureDeviceName(unsigned int deviceId)
vtkStandardNewMacro(vtkPlusMmfVideoSource)
virtual ItemStatus GetLatestStreamBufferItem(StreamBufferItem *bufferItem)
static PlusStatus ConvertToGray(int inputCompression, int width, int height, unsigned char *s, unsigned char *d)
Definition: PixelCodec.h:146
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()
#define TRUE
Definition: ATC3DGm.h:219
unsigned char BYTE
Definition: ATC3DGm.h:449
PlusStatus AddFrame(unsigned char *bufferData, DWORD bufferSize)
FrameSizeType GetInputFrameSize() const
Microsoft media foundation video digitizer.
virtual PlusStatus InternalDisconnect()
MmfVideoSourceReader * MmfSourceReader
virtual int GetNumberOfItems()
void GetListOfCaptureDevices(std::vector< std::wstring > &deviceNames)
bool CorrectlyConfigured
Interface to a 3D positioning tool, video source, or generalized data stream.