Fraxinus  16.5.0-fx-rc5
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 #include "cxSavingVideoRecorder.h"
34 
35 #include <QDir>
36 #include <QFile>
37 #include <QFileInfo>
38 #include <QTextStream>
39 
40 #include <vtkImageChangeInformation.h>
41 #include <vtkImageLuminance.h>
42 #include <vtkImageData.h>
43 #include "vtkImageAppend.h"
44 #include "vtkMetaImageWriter.h"
45 
46 #include "cxTypeConversions.h"
47 #include "cxLogger.h"
48 #include "cxSettings.h"
49 #include "cxXmlOptionItem.h"
50 #include "cxImageDataContainer.h"
51 #include "cxVideoSource.h"
52 
53 namespace cx
54 {
55 
56 VideoRecorderSaveThread::VideoRecorderSaveThread(QObject* parent, QString saveFolder, QString prefix, bool compressed, bool writeColor) :
57  QThread(parent),
58  mSaveFolder(saveFolder),
59  mPrefix(prefix),
60  mImageIndex(0),
61  mMutex(QMutex::Recursive),
62  mStop(false),
63  mCancel(false),
64  mTimestampsFile(saveFolder+"/"+prefix+".fts"),
65  mCompressed(compressed),
66  mWriteColor(writeColor)
67 {
68  this->setObjectName("org.custusx.resource.videorecordersave"); // becomes the thread name
69 }
70 
72 {
73 }
74 
76 {
77  if (!image)
78  return "";
79 
80  DataType data;
81  data.mTimestamp = timestamp;
82  data.mImage = vtkImageDataPtr::New();
83  data.mImage->DeepCopy(image);
84  data.mImageFilename = QString("%1/%2_%3.mhd").arg(mSaveFolder).arg(mPrefix).arg(mImageIndex++);
85 
86  {
87  QMutexLocker sentry(&mMutex);
88  mPendingData.push_back(data);
89  }
90 
91  return data.mImageFilename;
92 }
93 
95 {
96  mStop = true;
97 }
98 
100 {
101  mCancel = true;
102  mStop = true;
103 }
104 
106 {
107  if(!mTimestampsFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
108  {
109  reportError("Cannot open "+mTimestampsFile.fileName());
110  return false;
111  }
112  return true;
113 }
114 
115 
117 {
118  mTimestampsFile.close();
119 
120 // QFileInfo info(mTimestampsFile);
121 // if (!mCancel)
122 // {
123 // report(QString("Saved %1 timestamps to file %2")
124 // .arg(mImageIndex)
125 // .arg(info.fileName()));
126 // }
127  return true;
128 }
129 
131 {
132  this->writeTimeStampsFile(data.mTimestamp);
133 
134  // convert to 8 bit data if applicable.
135  if (!mWriteColor && data.mImage->GetNumberOfScalarComponents()>2)
136  {
137  vtkSmartPointer<vtkImageLuminance> luminance = vtkSmartPointer<vtkImageLuminance>::New();
138  luminance->SetInputData(data.mImage);
139  luminance->Update();
140  data.mImage = luminance->GetOutput();
141 // data.mImage->Update();
142  }
143 
144  // write image
145  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
146  writer->SetInputData(data.mImage);
147  writer->SetFileName(cstring_cast(data.mImageFilename));
148  writer->SetCompression(mCompressed);
149  writer->Write();
150 }
151 
153 {
154  QTextStream stream(&mTimestampsFile);
155  stream << qstring_cast(timeStamps.getAcquisitionTime());
156  stream << endl;
157 }
158 
163 {
164  while(!mPendingData.empty())
165  {
166  if (mCancel)
167  return;
168 
169  DataType current;
170 
171  {
172  QMutexLocker sentry(&mMutex);
173  current = mPendingData.front();
174  mPendingData.pop_front();
175  }
176 
177  this->write(current);
178  }
179 }
180 
182 {
183  this->openTimestampsFile();
184  while (!mStop)
185  {
186  this->writeQueue();
187  this->msleep(20);
188  }
189 
190  this->writeQueue();
191  this->closeTimestampsFile();
192 }
193 
194 //---------------------------------------------------------
195 //---------------------------------------------------------
196 //---------------------------------------------------------
197 
198 
199 SavingVideoRecorder::SavingVideoRecorder(VideoSourcePtr source, QString saveFolder, QString prefix, bool compressed, bool writeColor) :
200 // mLastPurgedImageIndex(-1),
201  mSource(source)
202 {
203  mImages.reset(new cx::CachedImageDataContainer());
204  mImages->setDeleteFilesOnRelease(true);
205 
206  mPrefix = prefix;
207  mSaveFolder = saveFolder;
208  mSaveThread.reset(new VideoRecorderSaveThread(NULL, saveFolder, prefix, compressed, writeColor));
209  mSaveThread->start();
210 }
211 
213 {
214  mSaveThread->cancel();
215  mSaveThread->wait(); // wait indefinitely for thread to finish
216 }
217 
219 {
220  connect(mSource.get(), &VideoSource::newFrame, this, &SavingVideoRecorder::newFrameSlot);
221 }
222 
224 {
225  disconnect(mSource.get(), &VideoSource::newFrame, this, &SavingVideoRecorder::newFrameSlot);
226 }
227 
228 void SavingVideoRecorder::newFrameSlot()
229 {
230  if (!mSource->validData())
231  return;
232 
233  vtkImageDataPtr image = mSource->getVtkImageData();
234  TimeInfo timestamp = mSource->getAdvancedTimeInfo();
235  QString filename = mSaveThread->addData(timestamp, image);
236 
237  mImages->append(filename);
238  mTimestamps.push_back(timestamp);
239 }
240 
242 {
243  return mImages;
244 }
245 
246 std::vector<TimeInfo> SavingVideoRecorder::getTimestamps()
247 {
248  return mTimestamps;
249 }
250 
252 {
253  this->stopRecord();
254 
255  mSaveThread->cancel();
256  mSaveThread->wait(); // wait indefinitely for thread to finish
257 
258  this->deleteFolder(mSaveFolder);
259 }
260 
263 void SavingVideoRecorder::deleteFolder(QString folder)
264 {
265  QStringList filters;
266  filters << "*.fts" << "*.mhd" << "*.raw" << "*.zraw";
267  for (int i=0; i<filters.size(); ++i) // prepend prefix, ensuring files from other savers are not deleted.
268  filters[i] = mPrefix + filters[i];
269 
270  QDir dir(folder);
271  QStringList files = dir.entryList(filters);
272 
273  for (int i=0; i<files.size(); ++i)
274  dir.remove(files[i]);
275  dir.rmdir(folder);
276 }
277 
279 {
280  mSaveThread->stop();
281  mSaveThread->wait(); // wait indefinitely for thread to finish
282 }
283 
284 } // namespace cx
285 
286 
QString qstring_cast(const T &val)
std::vector< TimeInfo > getTimestamps()
void reportError(QString msg)
Definition: cxLogger.cpp:92
void writeTimeStampsFile(TimeInfo timeStamps)
cstring_cast_Placeholder cstring_cast(const T &val)
QString addData(TimeInfo timestamp, vtkImageDataPtr data)
double getAcquisitionTime() const
Definition: cxData.h:82
VideoRecorderSaveThread(QObject *parent, QString saveFolder, QString prefix, bool compressed, bool writeColor)
boost::shared_ptr< class VideoSource > VideoSourcePtr
vtkSmartPointer< class vtkMetaImageWriter > vtkMetaImageWriterPtr
SavingVideoRecorder(VideoSourcePtr source, QString saveFolder, QString prefix, bool compressed, bool writeColor)
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