PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusVirtualCapture.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 #include "PlusConfigure.h"
8 #include "igsioTrackedFrame.h"
9 #include "vtkIGSIOMetaImageSequenceIO.h"
10 #include "vtkObjectFactory.h"
11 #include "vtkPlusChannel.h"
12 #include "vtkPlusDataSource.h"
13 #include "vtkIGSIOSequenceIO.h"
14 #include "vtkIGSIOTrackedFrameList.h"
15 #include "vtkPlusVirtualCapture.h"
16 #include "vtksys/SystemTools.hxx"
17 
18 #ifdef PLUS_USE_VTKVIDEOIO_MKV
19 // #include "vtkPlusMkvSequenceIO.h"
20 #endif
21 
22 //----------------------------------------------------------------------------
23 
25 
26 //----------------------------------------------------------------------------
27 
28 namespace
29 {
30  static const double WARNING_RECORDING_LAG_SEC = 1.0; // if the recording lags more than this then a warning message will be displayed
31  static const double MAX_ALLOWED_RECORDING_LAG_SEC = 3.0; // if the recording lags more than this then it'll skip frames to catch up
32  static const unsigned int DISABLE_FRAME_BUFFER = std::numeric_limits<unsigned int>::max();
33 }
34 
35 //----------------------------------------------------------------------------
37  : vtkPlusDevice()
38  , RecordedFrames(vtkIGSIOTrackedFrameList::New())
39  , LastAlreadyRecordedFrameTimestamp(UNDEFINED_TIMESTAMP)
40  , NextFrameToBeRecordedTimestamp(0.0)
41  , RequestedFrameRate(15.0)
42  , ActualFrameRate(0.0)
43  , FirstFrameIndexInThisSegment(0)
44  , TimeWaited(0.0)
45  , LastUpdateTime(0.0)
46  , CurrentFilename("")
47  , BaseFilename("TrackedImageSequence.nrrd")
48  , Writer(NULL)
49  , EnableFileCompression(false)
50  , IsHeaderPrepared(false)
51  , TotalFramesRecorded(0)
52  , EnableCapturingOnStart(false)
53  , EnableCapturing(false)
54  , FrameBufferSize(DISABLE_FRAME_BUFFER)
55  , IsData3D(false)
56  , WriterAccessMutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
57  , GracePeriodLogLevel(vtkPlusLogger::LOG_LEVEL_DEBUG)
58  , EncodingFourCC("VP90")
59 {
60  this->AcquisitionRate = 30.0;
61  this->MissingInputGracePeriodSec = 2.0;
62  this->RecordedFrames->SetValidationRequirements(REQUIRE_UNIQUE_TIMESTAMP);
63 
64  // The data capture thread will be used to regularly read the frames and write to disk
65  this->StartThreadForInternalUpdates = true;
66 }
67 
68 //----------------------------------------------------------------------------
70 {
71  if (IsHeaderPrepared)
72  {
73  this->CloseFile();
74  }
75 
76  if (RecordedFrames != NULL)
77  {
78  this->RecordedFrames->Delete();
79  this->RecordedFrames = NULL;
80  }
81 
82  if (Writer != NULL)
83  {
84  this->Writer->Delete();
85  this->Writer = NULL;
86  }
87 }
88 
89 //----------------------------------------------------------------------------
90 void vtkPlusVirtualCapture::PrintSelf(ostream& os, vtkIndent indent)
91 {
92  this->Superclass::PrintSelf(os, indent);
93 }
94 
95 //----------------------------------------------------------------------------
96 PlusStatus vtkPlusVirtualCapture::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
97 {
98  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
99  const char* deviceType = deviceConfig->GetAttribute("Type");
100  if (deviceType && deviceType != std::string("VirtualCapture"))
101  {
102  LOG_WARNING("Device type \"" << deviceType << "\" is deprecated. Use \"VirtualCapture\" instead.");
103  deviceConfig->SetAttribute("Type", "VirtualCapture");
104  }
105 
106  XML_READ_STRING_ATTRIBUTE_OPTIONAL(BaseFilename, deviceConfig);
107  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(EnableFileCompression, deviceConfig);
108  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(EnableCapturingOnStart, deviceConfig);
109  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, RequestedFrameRate, deviceConfig);
110  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, FrameBufferSize, deviceConfig);
111  XML_READ_STRING_ATTRIBUTE_OPTIONAL(EncodingFourCC, deviceConfig);
112 
113  return PLUS_SUCCESS;
114 }
115 
116 //----------------------------------------------------------------------------
118 {
119  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceElement, rootConfig);
120  deviceElement->SetAttribute("EnableCapturing", this->EnableCapturing ? "TRUE" : "FALSE");
121  deviceElement->SetAttribute("EnableFileCompression", this->EnableFileCompression ? "TRUE" : "FALSE");
122  deviceElement->SetAttribute("EnableCaptureOnStart", this->EnableCapturingOnStart ? "TRUE" : "FALSE");
123  deviceElement->SetDoubleAttribute("RequestedFrameRate", this->GetRequestedFrameRate());
124 
125  return PLUS_SUCCESS;
126 }
127 
128 
129 //----------------------------------------------------------------------------
131 {
132  if (OpenFile() != PLUS_SUCCESS)
133  {
134  return PLUS_FAIL;
135  }
136 
137  if (this->GetEnableCapturingOnStart())
138  {
139  this->SetEnableCapturing(true);
140  }
141 
142  this->LastUpdateTime = vtkIGSIOAccurateTimer::GetSystemTime();
143 
144  return PLUS_SUCCESS;
145 }
146 
147 //----------------------------------------------------------------------------
149 {
150  this->EnableCapturing = false;
151 
152  // If outstanding frames to be written, deal with them
153  if (this->RecordedFrames->GetNumberOfTrackedFrames() != 0 && this->IsHeaderPrepared)
154  {
155  if (this->Writer->AppendImagesToHeader() != PLUS_SUCCESS)
156  {
157  LOG_ERROR("Unable to append image data to header.");
158  this->Disconnect();
159  return PLUS_FAIL;
160  }
161  if (this->Writer->WriteImages() != PLUS_SUCCESS)
162  {
163  LOG_ERROR("Unable to append images. Stopping recording at timestamp: " << this->LastAlreadyRecordedFrameTimestamp);
164  this->Disconnect();
165  return PLUS_FAIL;
166  }
167 
168  this->ClearRecordedFrames();
169  }
170  PlusStatus status = this->CloseFile();
171  return status;
172 }
173 
174 //----------------------------------------------------------------------------
176 {
177  igsioLockGuard<vtkIGSIORecursiveCriticalSection> writerLock(this->WriterAccessMutex);
178 
179  if (aFilename == NULL || strlen(aFilename) == 0)
180  {
181  std::string filenameRoot = igsioCommon::GetSequenceFilenameWithoutExtension(this->BaseFilename);
182  std::string ext = igsioCommon::GetSequenceFilenameExtension(this->BaseFilename);
183  if (ext.empty())
184  {
185  // default to nrrd
186  ext = ".nrrd";
187  }
188  else if (vtkIGSIOMetaImageSequenceIO::CanWriteFile(this->BaseFilename) && this->GetEnableFileCompression())
189  {
190  // they've requested mhd/mha with compression, no can do, yet
191  LOG_WARNING("Compressed saving of metaimage file requested. This is not supported. Reverting to uncompressed metaimage file.");
192  this->SetEnableFileCompression(false);
193  }
194  this->CurrentFilename = filenameRoot + "_" + vtksys::SystemTools::GetCurrentDateTime("%Y%m%d_%H%M%S") + ext;
195  aFilename = this->CurrentFilename.c_str();
196  }
197  else
198  {
199  if (vtkIGSIOMetaImageSequenceIO::CanWriteFile(aFilename) && this->GetEnableFileCompression())
200  {
201  // they've requested mhd/mha with compression, no can do, yet
202  LOG_WARNING("Compressed saving of metaimage file requested. This is not supported. Reverting to uncompressed metaimage file.");
203  this->SetEnableFileCompression(false);
204  }
205  this->CurrentFilename = aFilename;
206  }
207 
208  this->Writer = vtkIGSIOSequenceIO::CreateSequenceHandlerForFile(aFilename);
209  if (!this->Writer)
210  {
211  LOG_ERROR("Could not create writer for file: " << aFilename);
212  return PLUS_FAIL;
213  }
214  this->Writer->SetUseCompression(this->EnableFileCompression);
215  this->Writer->SetTrackedFrameList(this->RecordedFrames);
216  // Need to set the filename before finalizing header, because the pixel data file name depends on the file extension
217  this->Writer->SetFileName(vtkPlusConfig::GetInstance()->GetOutputPath(aFilename));
218 
219  return PLUS_SUCCESS;
220 }
221 
222 //----------------------------------------------------------------------------
223 PlusStatus vtkPlusVirtualCapture::CloseFile(const char* aFilename /* = NULL */, std::string* resultFilename /* = NULL */)
224 {
225  // Fix the header to write the correct number of frames
226  igsioLockGuard<vtkIGSIORecursiveCriticalSection> writerLock(this->WriterAccessMutex);
227 
228  if (!this->IsHeaderPrepared)
229  {
230  // nothing has been prepared, so nothing to finalize
231  return PLUS_SUCCESS;
232  }
233 
234  if (aFilename != NULL && strlen(aFilename) != 0)
235  {
236  // Need to set the filename before finalizing header, because the pixel data file name depends on the file extension
237  this->Writer->SetFileName(vtkPlusConfig::GetInstance()->GetOutputPath(aFilename));
238  this->CurrentFilename = aFilename;
239  }
240 
241  // Do we have any outstanding unwritten data?
242  if (this->RecordedFrames->GetNumberOfTrackedFrames() != 0)
243  {
244  this->WriteFrames(true);
245  }
246 
247  this->Writer->UpdateDimensionsCustomStrings(this->TotalFramesRecorded, this->GetIsData3D());
248  this->Writer->UpdateFieldInImageHeader(this->Writer->GetDimensionSizeString());
249  this->Writer->UpdateFieldInImageHeader(this->Writer->GetDimensionKindsString());
250  this->Writer->FinalizeHeader();
251 
252  if (resultFilename != NULL)
253  {
254  (*resultFilename) = this->Writer->GetFileName();
255  }
256 
257  this->Writer->Close();
258 
259  std::string fullPath = vtkPlusConfig::GetInstance()->GetOutputPath(this->CurrentFilename);
260  std::string path = vtksys::SystemTools::GetFilenamePath(fullPath);
261  std::string filename = vtksys::SystemTools::GetFilenameWithoutExtension(fullPath);
262  std::string configFileName = path + "/" + filename + "_config.xml";
263  igsioCommon::XML::PrintXML(configFileName.c_str(), vtkPlusConfig::GetInstance()->GetDeviceSetConfigurationData());
264 
265  this->IsHeaderPrepared = false;
266  this->TotalFramesRecorded = 0;
267  this->RecordedFrames->Clear();
268 
269  if (this->OpenFile() != PLUS_SUCCESS)
270  {
271  return PLUS_FAIL;
272  }
273 
274  return PLUS_SUCCESS;
275 }
276 
277 //----------------------------------------------------------------------------
278 
280 {
281  if (!this->EnableCapturing)
282  {
283  // Capturing is disabled
284  return PLUS_SUCCESS;
285  }
286 
287  double samplingPeriodSec = 0.1;
288  if (this->AcquisitionRate > 0)
289  {
290  samplingPeriodSec = 1.0 / this->AcquisitionRate;
291  }
292  else
293  {
294  LOG_WARNING("AcquisitionRate value is invalid " << this->AcquisitionRate << ". Use default sampling period of " << samplingPeriodSec << " sec");
295  }
296 
297  if (this->LastUpdateTime == 0.0)
298  {
299  this->LastUpdateTime = vtkIGSIOAccurateTimer::GetSystemTime();
300  }
301  if (this->NextFrameToBeRecordedTimestamp == 0.0)
302  {
303  this->NextFrameToBeRecordedTimestamp = vtkIGSIOAccurateTimer::GetSystemTime();
304  }
305  double startTimeSec = vtkIGSIOAccurateTimer::GetSystemTime();
306 
307  this->TimeWaited += startTimeSec - LastUpdateTime;
308 
309  if (this->TimeWaited < samplingPeriodSec)
310  {
311  // Nothing to do yet
312  return PLUS_SUCCESS;
313  }
314 
315  this->TimeWaited = 0.0;
316 
317  double maxProcessingTimeSec = samplingPeriodSec * 2.0; // put a hard limit on the max processing time to make sure the application remains responsive during recording
318  double requestedFramePeriodSec = 0.1;
319  if (this->RequestedFrameRate > 0)
320  {
321  requestedFramePeriodSec = 1.0 / this->RequestedFrameRate;
322  }
323  else
324  {
325  LOG_WARNING("RequestedFrameRate is invalid");
326  }
327 
328  if (this->HasGracePeriodExpired())
329  {
330  this->GracePeriodLogLevel = vtkPlusLogger::LOG_LEVEL_WARNING;
331  }
332 
333  igsioLockGuard<vtkIGSIORecursiveCriticalSection> writerLock(this->WriterAccessMutex);
334  if (!this->EnableCapturing)
335  {
336  // While this thread was waiting for the unlock, capturing was disabled, so cancel the update now
337  return PLUS_SUCCESS;
338  }
339 
340  int nbFramesBefore = this->RecordedFrames->GetNumberOfTrackedFrames();
341  if (this->GetInputTrackedFrameListSampled(this->LastAlreadyRecordedFrameTimestamp, this->NextFrameToBeRecordedTimestamp, this->RecordedFrames, requestedFramePeriodSec, maxProcessingTimeSec) != PLUS_SUCCESS)
342  {
343  LOG_ERROR("Error while getting tracked frame list from data collector during capturing. Last recorded timestamp: " << std::fixed << this->NextFrameToBeRecordedTimestamp);
344  }
345  int nbFramesAfter = this->RecordedFrames->GetNumberOfTrackedFrames();
346 
347  // Compute the average frame rate from the ratio of recently acquired frames
348  int frame1Index = this->RecordedFrames->GetNumberOfTrackedFrames() - 1; // index of the latest frame
349  int frame2Index = frame1Index - this->RequestedFrameRate * 5.0 - 1; // index of an earlier acquired frame (go back by approximately 5 seconds + one frame)
350  if (frame2Index < this->FirstFrameIndexInThisSegment)
351  {
352  // make sure we stay in the current recording segment
353  frame2Index = this->FirstFrameIndexInThisSegment;
354  }
355  if (frame1Index > frame2Index)
356  {
357  igsioTrackedFrame* frame1 = this->RecordedFrames->GetTrackedFrame(frame1Index);
358  igsioTrackedFrame* frame2 = this->RecordedFrames->GetTrackedFrame(frame2Index);
359  if (frame1 != NULL && frame2 != NULL)
360  {
361  double frameTimeDiff = frame1->GetTimestamp() - frame2->GetTimestamp();
362  if (frameTimeDiff > 0)
363  {
364  this->ActualFrameRate = (frame1Index - frame2Index) / frameTimeDiff;
365  }
366  else
367  {
368  this->ActualFrameRate = 0;
369  }
370  }
371  }
372 
373  if (this->WriteFrames() != PLUS_SUCCESS)
374  {
375  LOG_ERROR(this->GetDeviceId() << ": Unable to write " << nbFramesAfter - nbFramesBefore << " frames.");
376  return PLUS_FAIL;
377  }
378 
379  this->TotalFramesRecorded += nbFramesAfter - nbFramesBefore;
380 
381  if (this->TotalFramesRecorded == 0)
382  {
383  // We haven't received any data so far
384  LOG_DYNAMIC("No input data available to capture thread. Waiting until input data arrives.", this->GracePeriodLogLevel);
385  }
386 
387  // Check whether the recording needed more time than the sampling interval
388  double recordingTimeSec = vtkIGSIOAccurateTimer::GetSystemTime() - startTimeSec;
389  double currentSystemTime = vtkIGSIOAccurateTimer::GetSystemTime();
390  double recordingLagSec = currentSystemTime - this->NextFrameToBeRecordedTimestamp;
391 
392  if (recordingTimeSec > samplingPeriodSec)
393  {
394  // Log too long recording as warning only if the recording is falling behind
395  vtkPlusLogger::LogLevelType logLevel = (recordingLagSec > WARNING_RECORDING_LAG_SEC ? vtkPlusLogger::LOG_LEVEL_WARNING : vtkPlusLogger::LOG_LEVEL_DEBUG);
396  LOG_DYNAMIC("Recording of frames takes too long time (" << recordingTimeSec << "sec instead of the allocated " << samplingPeriodSec << "sec, recording lags by " << recordingLagSec << "sec). This can cause slow-down of the application and non-uniform sampling. Reduce the acquisition rate or sampling rate to resolve the problem.", logLevel);
397  }
398 
399  if (recordingLagSec > MAX_ALLOWED_RECORDING_LAG_SEC)
400  {
401  double acquisitionLagSec = recordingLagSec;
402  double latestInputTimestamp = this->NextFrameToBeRecordedTimestamp;
403  if (GetLatestInputItemTimestamp(latestInputTimestamp) == PLUS_SUCCESS)
404  {
405  acquisitionLagSec = currentSystemTime - latestInputTimestamp;
406  }
407  if (acquisitionLagSec < MAX_ALLOWED_RECORDING_LAG_SEC)
408  {
409  // Frames are available (because acquisitionLagSec < MAX_ALLOWED_RECORDING_LAG_SEC) but recording is falling behind
410  // (because acquisitionLagSec < MAX_ALLOWED_RECORDING_LAG_SEC)
411  LOG_ERROR("Recording cannot keep up with the acquisition. Skip " << recordingLagSec << " seconds of the data stream to catch up.");
412  }
413  this->NextFrameToBeRecordedTimestamp = vtkIGSIOAccurateTimer::GetSystemTime();
414  }
415 
416  this->LastUpdateTime = vtkIGSIOAccurateTimer::GetSystemTime();
417 
418  return PLUS_SUCCESS;
419 }
420 
421 //-----------------------------------------------------------------------------
423 {
424  if (!this->OutputChannels.empty())
425  {
426  LOG_WARNING("vtkPlusVirtualCapture is expecting no output channel(s) and there are " << this->OutputChannels.size() << " channels. Output channel information will be dropped.");
427  this->OutputChannels.clear();
428  }
429 
430  if (this->InputChannels.empty())
431  {
432  LOG_ERROR("No input channel set for vtkPlusVirtualCapture. Unable to save anything.");
433  return PLUS_FAIL;
434  }
435  vtkPlusChannel* inputChannel = this->InputChannels[0];
436 
437  if (this->InputChannels.size() > 1)
438  {
439  LOG_WARNING("vtkPlusVirtualCapture is expecting one input channel and there are " << this->InputChannels.size() << " channels. First output channel will be used, all other are ignored.");
440  }
441 
442  // GetTrackedFrame reads from the OutputChannels
443  // For now, place the input stream as an output stream so its data is read
444  this->OutputChannels.push_back(inputChannel);
445  inputChannel->Register(this); // this device uses this channel, too, se we need to update the reference count to avoid double delete in the destructor
446 
447  return PLUS_SUCCESS;
448 }
449 
450 //-----------------------------------------------------------------------------
452 {
453  return this->IsHeaderPrepared;
454 }
455 
456 //-----------------------------------------------------------------------------
458 {
459  this->RecordedFrames->Clear();
460 
461  return PLUS_SUCCESS;
462 }
463 
464 //-----------------------------------------------------------------------------
465 void vtkPlusVirtualCapture::InternalWriteOutputChannels(vtkXMLDataElement* rootXMLElement)
466 {
467  // Do not write anything out, disc capture devices don't have output channels in the config
468 }
469 
470 //----------------------------------------------------------------------------
472 {
473  if (this->Writer != NULL)
474  {
475  this->Writer->SetUseCompression(aFileCompression);
476  }
477 
478  this->EnableFileCompression = aFileCompression;
479 }
480 
481 //-----------------------------------------------------------------------------
483 {
484  this->EnableCapturing = aValue;
485 
486  if (this->EnableCapturing)
487  {
488  this->LastUpdateTime = 0.0;
489  this->TimeWaited = 0.0;
490  this->LastAlreadyRecordedFrameTimestamp = UNDEFINED_TIMESTAMP;
491  this->NextFrameToBeRecordedTimestamp = 0.0;
492  this->FirstFrameIndexInThisSegment = this->RecordedFrames->GetNumberOfTrackedFrames();
493  this->RecordingStartTime = vtkIGSIOAccurateTimer::GetSystemTime(); // reset the starting time for the grace period
494  }
495 }
496 
497 //-----------------------------------------------------------------------------
499 {
500  {
501  igsioLockGuard<vtkIGSIORecursiveCriticalSection> writerLock(this->WriterAccessMutex);
502 
503  this->SetEnableCapturing(false);
504 
505  if (this->IsHeaderPrepared)
506  {
507  this->Writer->Discard();
508  }
509 
510  this->ClearRecordedFrames();
511  this->Writer->GetTrackedFrameList()->Clear();
512  this->IsHeaderPrepared = false;
513  this->TotalFramesRecorded = 0;
514  }
515 
516  if (this->OpenFile() != PLUS_SUCCESS)
517  {
518  LOG_ERROR("Unable to reset device " << this->GetDeviceId() << ".");
519  return PLUS_FAIL;
520  }
521 
522  this->LastUpdateTime = vtkIGSIOAccurateTimer::GetSystemTime();
523 
524  return PLUS_SUCCESS;
525 }
526 
527 //-----------------------------------------------------------------------------
529 {
530  return this->FrameBufferSize != DISABLE_FRAME_BUFFER;
531 }
532 
533 //-----------------------------------------------------------------------------
535 {
536  if (this->EnableCapturing)
537  {
538  LOG_ERROR(this->GetDeviceId() << ": Cannot take snapshot while the device is recording.");
539  return PLUS_FAIL;
540  }
541 
542  igsioTrackedFrame trackedFrame;
543  if (this->GetInputTrackedFrame(trackedFrame) != PLUS_SUCCESS)
544  {
545  LOG_ERROR(this->GetDeviceId() << ": Failed to get tracked frame for the snapshot!");
546  return PLUS_FAIL;
547  }
548 
549  // Check if there are any valid transforms
550  std::vector<igsioTransformName> transformNames;
551  trackedFrame.GetFrameTransformNameList(transformNames);
552  bool validFrame = false;
553 
554  if (transformNames.size() == 0)
555  {
556  validFrame = true;
557  }
558  else
559  {
560  for (std::vector<igsioTransformName>::iterator it = transformNames.begin(); it != transformNames.end(); ++it)
561  {
562  ToolStatus status(TOOL_INVALID);
563  trackedFrame.GetFrameTransformStatus(*it, status);
564 
565  if (status == TOOL_OK)
566  {
567  validFrame = true;
568  break;
569  }
570  }
571  }
572 
573  if (!validFrame)
574  {
575  LOG_WARNING(this->GetDeviceId() << ": Unable to record tracked frame: All the tool transforms are invalid!");
576  return PLUS_FAIL;
577  }
578 
579  // Add tracked frame to the list
580  // Snapshots are triggered manually, so the additional copying in AddTrackedFrame compared to TakeTrackedFrame is not relevant.
581  if (this->RecordedFrames->AddTrackedFrame(&trackedFrame, vtkIGSIOTrackedFrameList::SKIP_INVALID_FRAME) != PLUS_SUCCESS)
582  {
583  LOG_WARNING(this->GetDeviceId() << ": Frame could not be added because validation failed");
584  return PLUS_FAIL;
585  }
586 
587  if (this->WriteFrames() != PLUS_SUCCESS)
588  {
589  LOG_ERROR(this->GetDeviceId() << ": Failed to write snapshot frame");
590  return PLUS_FAIL;
591  }
592 
593  this->TotalFramesRecorded += 1;
594 
595  return PLUS_SUCCESS;
596 }
597 
598 //-----------------------------------------------------------------------------
599 PlusStatus vtkPlusVirtualCapture::SetCustomHeaderField(const std::string& fieldName, const std::string& fieldValue)
600 {
601  return this->Writer->GetTrackedFrameList()->SetCustomString(fieldName, fieldValue);
602 }
603 
604 //-----------------------------------------------------------------------------
606 {
607  if (!this->IsHeaderPrepared && this->RecordedFrames->GetNumberOfTrackedFrames() != 0)
608  {
609  if (this->Writer->PrepareHeader() != PLUS_SUCCESS)
610  {
611  LOG_ERROR("Unable to prepare header");
612  this->StopRecording();
613  return PLUS_FAIL;
614  }
615  this->IsHeaderPrepared = true;
616  }
617 
618  if (this->RecordedFrames->GetNumberOfTrackedFrames() == 0)
619  {
620  return PLUS_SUCCESS;
621  }
622 
623  this->SetIsData3D(this->RecordedFrames->GetTrackedFrame(0)->GetFrameSize()[2] > 1);
624 
625  if (force || !this->IsFrameBuffered() ||
626  (this->IsFrameBuffered() && this->RecordedFrames->GetNumberOfTrackedFrames() > this->GetFrameBufferSize()))
627  {
628  if (this->Writer->AppendImagesToHeader() != PLUS_SUCCESS)
629  {
630  LOG_ERROR("Unable to append image data to header.");
631  this->StopRecording();
632  return PLUS_FAIL;
633  }
634  if (this->Writer->WriteImages() != PLUS_SUCCESS)
635  {
636  LOG_ERROR("Unable to append images. Stopping recording at timestamp: " << LastAlreadyRecordedFrameTimestamp);
637  this->StopRecording();
638  return PLUS_FAIL;
639  }
640 
641  this->ClearRecordedFrames();
642  }
643 
644  return PLUS_SUCCESS;
645 }
646 
647 //-----------------------------------------------------------------------------
649 {
650  // Even though we fake one output channel for easy GetTrackedFrame ability,
651  // we shouldn't return actual output channel size
652  return 0;
653 }
654 
655 //-----------------------------------------------------------------------------
657 {
658  if (this->OutputChannels.empty())
659  {
660  LOG_ERROR("No output channels defined");
661  return PLUS_FAIL;
662  }
663 
664  return this->OutputChannels[0]->GetTrackedFrame(aFrame);
665 }
666 
667 //-----------------------------------------------------------------------------
668 PlusStatus vtkPlusVirtualCapture::GetInputTrackedFrameListSampled(double& lastAlreadyRecordedFrameTimestamp, double& nextFrameToBeRecordedTimestamp, vtkIGSIOTrackedFrameList* recordedFrames, double requestedFramePeriodSec, double maxProcessingTimeSec)
669 {
670  if (this->OutputChannels.empty())
671  {
672  LOG_ERROR("No output channels defined");
673  return PLUS_FAIL;
674  }
675 
676  return this->OutputChannels[0]->GetTrackedFrameListSampled(lastAlreadyRecordedFrameTimestamp, nextFrameToBeRecordedTimestamp, recordedFrames, requestedFramePeriodSec, maxProcessingTimeSec);
677 }
678 
679 //-----------------------------------------------------------------------------
681 {
682  if (this->OutputChannels.empty())
683  {
684  LOG_ERROR("No output channels defined");
685  return PLUS_FAIL;
686  }
687  return this->OutputChannels[0]->GetLatestTimestamp(timestamp);
688 }
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
std::string GetOutputPath(const std::string &subPath)
Abstract interface for tracker and video devices.
Definition: vtkPlusDevice.h:60
virtual PlusStatus InternalUpdate()
vtkIGSIOSequenceIOBase * Writer
double * timestamp
Definition: phidget22.h:3432
vtkStandardNewMacro(vtkPlusVirtualCapture)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
Class to abstract away specific sequence file read/write details.
Definition: vtkPlusLogger.h:21
virtual PlusStatus InternalConnect()
igsioStatus PlusStatus
Definition: PlusCommon.h:40
ChannelContainer InputChannels
virtual std::string GetDeviceId() const
virtual bool GetEnableFileCompression()
virtual bool IsFrameBuffered() const
PlusStatus GetInputTrackedFrameListSampled(double &lastAlreadyRecordedFrameTimestamp, double &nextFrameToBeRecordedTimestamp, vtkIGSIOTrackedFrameList *recordedFrames, double requestedFramePeriodSec, double maxProcessingTimeSec)
double AcquisitionRate
virtual PlusStatus InternalDisconnect()
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual PlusStatus Reset()
void PrintSelf(ostream &os, vtkIndent indent)
virtual PlusStatus ClearRecordedFrames()
static vtkPlusConfig * GetInstance()
virtual PlusStatus Disconnect()
double MissingInputGracePeriodSec
virtual bool GetEnableCapturingOnStart()
void SetEnableFileCompression(bool aFileCompression)
vtkIGSIOTrackedFrameList * RecordedFrames
virtual PlusStatus TakeSnapshot()
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
double RecordingStartTime
virtual bool HasUnsavedData() const
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *)
virtual PlusStatus CloseFile(const char *aFilename=NULL, std::string *resultFilename=NULL)
virtual PlusStatus StopRecording()
virtual PlusStatus WriteFrames(bool force=false)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
virtual PlusStatus NotifyConfigured()
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *)
virtual void SetIsData3D(bool)
void SetEnableCapturing(bool aValue)
bool StartThreadForInternalUpdates
vtkSmartPointer< vtkIGSIORecursiveCriticalSection > WriterAccessMutex
virtual void InternalWriteOutputChannels(vtkXMLDataElement *rootXMLElement)
bool HasGracePeriodExpired()
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
virtual vtkXMLDataElement * GetDeviceSetConfigurationData()
ChannelContainer OutputChannels
virtual PlusStatus OpenFile(const char *aFilename=NULL)
PlusStatus GetLatestInputItemTimestamp(double &timestamp)
virtual double GetRequestedFrameRate()
virtual bool GetIsData3D()
PlusStatus GetInputTrackedFrame(igsioTrackedFrame &aFrame)
virtual PlusStatus SetCustomHeaderField(const std::string &fieldName, const std::string &fieldValue)
virtual unsigned int GetFrameBufferSize()
vtkPlusLogger::LogLevelType GracePeriodLogLevel
virtual int OutputChannelCount() const