CustusX  15.8
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 
75 QString VideoRecorderSaveThread::addData(double timestamp, vtkImageDataPtr image)
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  // write timestamp
133  QTextStream stream(&mTimestampsFile);
134  stream << qstring_cast(data.mTimestamp);
135  stream << endl;
136 
137  // convert to 8 bit data if applicable.
138  if (!mWriteColor && data.mImage->GetNumberOfScalarComponents()>2)
139  {
140  vtkSmartPointer<vtkImageLuminance> luminance = vtkSmartPointer<vtkImageLuminance>::New();
141  luminance->SetInputData(data.mImage);
142  luminance->Update();
143  data.mImage = luminance->GetOutput();
144 // data.mImage->Update();
145  }
146 
147  // write image
148  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
149  writer->SetInputData(data.mImage);
150  writer->SetFileName(cstring_cast(data.mImageFilename));
151  writer->SetCompression(mCompressed);
152  writer->Write();
153 }
154 
159 {
160  while(!mPendingData.empty())
161  {
162  if (mCancel)
163  return;
164 
165  DataType current;
166 
167  {
168  QMutexLocker sentry(&mMutex);
169  current = mPendingData.front();
170  mPendingData.pop_front();
171  }
172 
173  this->write(current);
174  }
175 }
176 
178 {
179  this->openTimestampsFile();
180  while (!mStop)
181  {
182  this->writeQueue();
183  this->msleep(20);
184  }
185 
186  this->writeQueue();
187  this->closeTimestampsFile();
188 }
189 
190 //---------------------------------------------------------
191 //---------------------------------------------------------
192 //---------------------------------------------------------
193 
194 
195 SavingVideoRecorder::SavingVideoRecorder(VideoSourcePtr source, QString saveFolder, QString prefix, bool compressed, bool writeColor) :
196 // mLastPurgedImageIndex(-1),
197  mSource(source)
198 {
199  mImages.reset(new cx::CachedImageDataContainer());
200  mImages->setDeleteFilesOnRelease(true);
201 
202  mPrefix = prefix;
203  mSaveFolder = saveFolder;
204  mSaveThread.reset(new VideoRecorderSaveThread(NULL, saveFolder, prefix, compressed, writeColor));
205  mSaveThread->start();
206 }
207 
209 {
210  mSaveThread->cancel();
211  mSaveThread->wait(); // wait indefinitely for thread to finish
212 }
213 
215 {
216  connect(mSource.get(), &VideoSource::newFrame, this, &SavingVideoRecorder::newFrameSlot);
217 }
218 
220 {
221  disconnect(mSource.get(), &VideoSource::newFrame, this, &SavingVideoRecorder::newFrameSlot);
222 }
223 
224 void SavingVideoRecorder::newFrameSlot()
225 {
226  if (!mSource->validData())
227  return;
228 
229  vtkImageDataPtr image = mSource->getVtkImageData();
230 // image->Update();
231  double timestamp = mSource->getTimestamp();
232  QString filename = mSaveThread->addData(timestamp, image);
233 
234  mImages->append(filename);
235  mTimestamps.push_back(timestamp);
236 }
237 
239 {
240  return mImages;
241 }
242 
244 {
245  return mTimestamps;
246 }
247 
249 {
250  this->stopRecord();
251 
252  mSaveThread->cancel();
253  mSaveThread->wait(); // wait indefinitely for thread to finish
254 
255  this->deleteFolder(mSaveFolder);
256 }
257 
260 void SavingVideoRecorder::deleteFolder(QString folder)
261 {
262  QStringList filters;
263  filters << "*.fts" << "*.mhd" << "*.raw" << "*.zraw";
264  for (int i=0; i<filters.size(); ++i) // prepend prefix, ensuring files from other savers are not deleted.
265  filters[i] = mPrefix + filters[i];
266 
267  QDir dir(folder);
268  QStringList files = dir.entryList(filters);
269 
270  for (int i=0; i<files.size(); ++i)
271  dir.remove(files[i]);
272  dir.rmdir(folder);
273 }
274 
276 {
277  mSaveThread->stop();
278  mSaveThread->wait(); // wait indefinitely for thread to finish
279 }
280 
281 } // namespace cx
282 
283 
QString qstring_cast(const T &val)
void reportError(QString msg)
Definition: cxLogger.cpp:92
cstring_cast_Placeholder cstring_cast(const T &val)
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()
std::vector< double > getTimestamps()
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
QString addData(double timestamp, vtkImageDataPtr data)
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