CustusX  2020.12.15-dev+develop.686e7
An IGT application
cxSavingVideoRecorder.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxSavingVideoRecorder.h"
13 
14 #include <QDir>
15 #include <QFile>
16 #include <QFileInfo>
17 #include <QTextStream>
18 
19 #include <vtkImageChangeInformation.h>
20 #include <vtkImageLuminance.h>
21 #include <vtkImageData.h>
22 #include "vtkImageAppend.h"
23 #include "vtkMetaImageWriter.h"
24 
25 #include "cxTypeConversions.h"
26 #include "cxLogger.h"
27 #include "cxSettings.h"
28 #include "cxXmlOptionItem.h"
29 #include "cxImageDataContainer.h"
30 #include "cxVideoSource.h"
31 
32 namespace cx
33 {
34 
35 VideoRecorderSaveThread::VideoRecorderSaveThread(QObject* parent, QString saveFolder, QString prefix, bool compressed, bool writeColor) :
36  QThread(parent),
37  mSaveFolder(saveFolder),
38  mPrefix(prefix),
39  mImageIndex(0),
40  mMutex(QMutex::Recursive),
41  mStop(false),
42  mCancel(false),
43  mTimestampsFile(saveFolder+"/"+prefix+".fts"),
44  mCompressed(compressed),
45  mWriteColor(writeColor)
46 {
47  this->setObjectName("org.custusx.resource.videorecordersave"); // becomes the thread name
48 }
49 
51 {
52 }
53 
55 {
56  if (!image)
57  return "";
58 
59  DataType data;
60  data.mTimestamp = timestamp;
61  data.mImage = vtkImageDataPtr::New();
62  data.mImage->DeepCopy(image);
63  data.mImageFilename = QString("%1/%2_%3.mhd").arg(mSaveFolder).arg(mPrefix).arg(mImageIndex++);
64 
65  {
66  QMutexLocker sentry(&mMutex);
67  mPendingData.push_back(data);
68  }
69 
70  return data.mImageFilename;
71 }
72 
74 {
75  mStop = true;
76 }
77 
79 {
80  mCancel = true;
81  mStop = true;
82 }
83 
85 {
86  if(!mTimestampsFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
87  {
88  reportError("Cannot open "+mTimestampsFile.fileName());
89  return false;
90  }
91  return true;
92 }
93 
94 
96 {
97  mTimestampsFile.close();
98 
99 // QFileInfo info(mTimestampsFile);
100 // if (!mCancel)
101 // {
102 // report(QString("Saved %1 timestamps to file %2")
103 // .arg(mImageIndex)
104 // .arg(info.fileName()));
105 // }
106  return true;
107 }
108 
110 {
111  this->writeTimeStampsFile(data.mTimestamp);
112 
113  // convert to 8 bit data if applicable.
114  if (!mWriteColor && data.mImage->GetNumberOfScalarComponents()>2)
115  {
116  vtkSmartPointer<vtkImageLuminance> luminance = vtkSmartPointer<vtkImageLuminance>::New();
117  luminance->SetInputData(data.mImage);
118  luminance->Update();
119  data.mImage = luminance->GetOutput();
120 // data.mImage->Update();
121  }
122 
123  // write image
124  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
125  writer->SetInputData(data.mImage);
126  writer->SetFileName(cstring_cast(data.mImageFilename));
127  writer->SetCompression(mCompressed);
128  writer->Write();
129 }
130 
132 {
133  QTextStream stream(&mTimestampsFile);
134  stream << qstring_cast(timeStamps.getAcquisitionTime());
135  stream << endl;
136 }
137 
142 {
143  while(!mPendingData.empty())
144  {
145  if (mCancel)
146  return;
147 
148  DataType current;
149 
150  {
151  QMutexLocker sentry(&mMutex);
152  current = mPendingData.front();
153  mPendingData.pop_front();
154  }
155 
156  this->write(current);
157  }
158 }
159 
161 {
162  this->openTimestampsFile();
163  while (!mStop)
164  {
165  this->writeQueue();
166  this->msleep(20);
167  }
168 
169  this->writeQueue();
170  this->closeTimestampsFile();
171 }
172 
173 //---------------------------------------------------------
174 //---------------------------------------------------------
175 //---------------------------------------------------------
176 
177 
178 SavingVideoRecorder::SavingVideoRecorder(VideoSourcePtr source, QString saveFolder, QString prefix, bool compressed, bool writeColor, FileManagerServicePtr filemanagerservice) :
179 // mLastPurgedImageIndex(-1),
180  mSource(source)
181 {
182  mImages.reset(new cx::CachedImageDataContainer(filemanagerservice));
183  mImages->setDeleteFilesOnRelease(true);
184 
185  mPrefix = prefix;
186  mSaveFolder = saveFolder;
187  mSaveThread.reset(new VideoRecorderSaveThread(NULL, saveFolder, prefix, compressed, writeColor));
188  mSaveThread->start();
189 }
190 
192 {
193  mSaveThread->cancel();
194  mSaveThread->wait(); // wait indefinitely for thread to finish
195 }
196 
198 {
199  connect(mSource.get(), &VideoSource::newFrame, this, &SavingVideoRecorder::newFrameSlot);
200 }
201 
203 {
204  disconnect(mSource.get(), &VideoSource::newFrame, this, &SavingVideoRecorder::newFrameSlot);
205 }
206 
207 void SavingVideoRecorder::newFrameSlot()
208 {
209  if (!mSource->validData())
210  return;
211 
212  vtkImageDataPtr image = mSource->getVtkImageData();
213  TimeInfo timestamp = mSource->getAdvancedTimeInfo();
214  QString filename = mSaveThread->addData(timestamp, image);
215 
216  mImages->append(filename);
217  mTimestamps.push_back(timestamp);
218 }
219 
221 {
222  return mImages;
223 }
224 
225 std::vector<TimeInfo> SavingVideoRecorder::getTimestamps()
226 {
227  return mTimestamps;
228 }
229 
231 {
232  this->stopRecord();
233 
234  mSaveThread->cancel();
235  mSaveThread->wait(); // wait indefinitely for thread to finish
236 
237  this->deleteFolder(mSaveFolder);
238 }
239 
242 void SavingVideoRecorder::deleteFolder(QString folder)
243 {
244  QStringList filters;
245  filters << "*.fts" << "*.mhd" << "*.raw" << "*.zraw";
246  for (int i=0; i<filters.size(); ++i) // prepend prefix, ensuring files from other savers are not deleted.
247  filters[i] = mPrefix + filters[i];
248 
249  QDir dir(folder);
250  QStringList files = dir.entryList(filters);
251 
252  for (int i=0; i<files.size(); ++i)
253  dir.remove(files[i]);
254  dir.rmdir(folder);
255 }
256 
258 {
259  mSaveThread->stop();
260  mSaveThread->wait(); // wait indefinitely for thread to finish
261 }
262 
263 } // namespace cx
264 
265 
QString qstring_cast(const T &val)
boost::shared_ptr< class FileManagerService > FileManagerServicePtr
std::vector< TimeInfo > getTimestamps()
void reportError(QString msg)
Definition: cxLogger.cpp:71
void writeTimeStampsFile(TimeInfo timeStamps)
cstring_cast_Placeholder cstring_cast(const T &val)
QString addData(TimeInfo timestamp, vtkImageDataPtr data)
SavingVideoRecorder(VideoSourcePtr source, QString saveFolder, QString prefix, bool compressed, bool writeColor, FileManagerServicePtr filemanagerservice)
double getAcquisitionTime() const
Definition: cxData.h:62
VideoRecorderSaveThread(QObject *parent, QString saveFolder, QString prefix, bool compressed, bool writeColor)
boost::shared_ptr< class VideoSource > VideoSourcePtr
vtkSmartPointer< class vtkMetaImageWriter > vtkMetaImageWriterPtr
std::list< DataType > mPendingData
CachedImageDataContainerPtr getImageData()
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
boost::shared_ptr< class CachedImageDataContainer > CachedImageDataContainerPtr
void newFrame()
emitted when a new frame has arrived (getVtkImageData() returns something new). info/status/name/vali...
QMutex mMutex
protects the mPendingData
Namespace for all CustusX production code.