Salome HOME
Merge from V6_main_20120808 08Aug12
[modules/gui.git] / src / SVTK / SVTK_Recorder.cxx
1 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SVTK_Recorder.h"
21
22 #include "SVTK_ImageWriter.h"
23 #include "SVTK_ImageWriterMgr.h"
24
25 #include <vtkObjectFactory.h>
26 #include <vtkObject.h>
27 #include <vtkCallbackCommand.h>
28 #include <vtkRenderWindow.h>
29 #include <vtkTimerLog.h>
30 #include <vtkWindowToImageFilter.h>
31 #include <vtkJPEGWriter.h>
32 #include <vtkImageData.h>
33
34 #include <sstream>
35 #include <iomanip>
36 #include <iostream>
37
38 #ifndef WIN32
39 #include <unistd.h>
40 #endif
41
42 #include <QApplication>
43 #include <QFileInfo>
44 #include <QDir>
45
46 //#include "utilities.h"
47
48 #ifdef _DEBUG_
49 static int MYDEBUG = 0;
50 #else
51 static int MYDEBUG = 0;
52 #endif
53
54
55 namespace
56 {
57   //----------------------------------------------------------------------------
58   inline
59   void
60   GetNameJPEG(const std::string& thePreffix,  
61               const int theIndex,
62               std::string& theName)
63   {
64     std::ostringstream aStream;
65     aStream<<thePreffix<<"_"<<setw(6)<<setfill('0')<<theIndex<<".jpeg";
66     theName = aStream.str();
67   }
68 }
69
70 //----------------------------------------------------------------------------
71 vtkCxxRevisionMacro(SVTK_Recorder,"$Revision$");
72 vtkStandardNewMacro(SVTK_Recorder);
73
74
75 //----------------------------------------------------------------------------
76 SVTK_Recorder
77 ::SVTK_Recorder():
78   myRenderWindow(NULL),
79   myState(SVTK_Recorder_Stop),
80   myNbFPS(5.5),
81   myQuality(100),
82   myProgressiveMode(true),
83   myUseSkippedFrames(true),
84   myErrorStatus(0),
85   myCommand(vtkCallbackCommand::New()),
86   myPriority(0.0),
87   myTimeStart(0.0),
88   myFrameIndex(0),
89   myPaused(0),
90   myFilter(vtkWindowToImageFilter::New()),
91   myWriterMgr(new SVTK_ImageWriterMgr),
92   myNbWrittenFrames(0),
93   myNameAVIMaker("jpeg2yuv")
94 {
95   myCommand->SetClientData(this); 
96   myCommand->SetCallback(SVTK_Recorder::ProcessEvents);
97 }
98
99
100 //----------------------------------------------------------------------------
101 SVTK_Recorder
102 ::~SVTK_Recorder()
103 {
104   myCommand->Delete();
105   myFilter->Delete();
106   delete myWriterMgr;
107 }
108
109
110 //----------------------------------------------------------------------------
111 void
112 SVTK_Recorder
113 ::CheckExistAVIMaker()
114 {
115   myErrorStatus = 0;
116   std::ostringstream aStream;
117 #ifndef WIN32
118   aStream<<"which "<<myNameAVIMaker<<" 2> /dev/null";
119 #else
120   aStream<<"setlocal & set P2=.;%PATH% & (for %e in (%PATHEXT%) do @for %i in ("<<myNameAVIMaker<<"%e) do @if NOT \"%~$P2:i\"==\"\" exit /b 0) & exit /b 1";
121 #endif
122   std::string anAVIMakeCheck = aStream.str();
123   int iErr = system(anAVIMakeCheck.c_str());
124   if(iErr != 0)
125     myErrorStatus = 127;
126 }
127
128
129 //----------------------------------------------------------------------------
130 void
131 SVTK_Recorder
132 ::SetName(const char* theName)
133 {
134   myName = theName;
135 }
136
137 const char* 
138 SVTK_Recorder::Name() const
139 {
140   return myName.c_str();
141 }
142
143
144 //----------------------------------------------------------------------------
145 void
146 SVTK_Recorder
147 ::SetNbFPS(const double theNbFPS)
148 {
149   myNbFPS = theNbFPS;
150 }
151
152 double
153 SVTK_Recorder
154 ::NbFPS() const
155 {
156   return myNbFPS;
157 }
158
159
160 //----------------------------------------------------------------------------
161 void
162 SVTK_Recorder
163 ::SetQuality(int theQuality)
164 {
165   myQuality = theQuality;
166 }
167
168 int
169 SVTK_Recorder
170 ::GetQuality() const
171 {
172   return myQuality;
173 }
174
175
176 //----------------------------------------------------------------------------
177 void 
178 SVTK_Recorder
179 ::SetRenderWindow(vtkRenderWindow* theRenderWindow)
180 {
181   myRenderWindow = theRenderWindow;
182 }
183
184 vtkRenderWindow* 
185 SVTK_Recorder
186 ::RenderWindow()
187 {
188   return myRenderWindow;
189 }
190
191
192 //----------------------------------------------------------------------------
193 void
194 SVTK_Recorder
195 ::SetProgressiveMode(bool theProgressiveMode)
196 {
197   myProgressiveMode = theProgressiveMode;
198 }
199
200 bool
201 SVTK_Recorder
202 ::GetProgressiveMode() const
203 {
204   return myProgressiveMode;
205 }
206
207
208 //----------------------------------------------------------------------------
209 void
210 SVTK_Recorder
211 ::SetUseSkippedFrames(bool theUseSkippedFrames)
212 {
213   myUseSkippedFrames = theUseSkippedFrames;
214 }
215
216 bool
217 SVTK_Recorder
218 ::UseSkippedFrames() const
219 {
220   return myUseSkippedFrames;
221 }
222
223
224 //----------------------------------------------------------------------------
225 int
226 SVTK_Recorder
227 ::ErrorStatus() const
228 {
229   return myErrorStatus;
230 }
231
232 int
233 SVTK_Recorder
234 ::State() const
235 {
236   return myState;
237 }
238
239
240 //----------------------------------------------------------------------------
241 void
242 SVTK_Recorder
243 ::ProcessEvents(vtkObject* vtkNotUsed(theObject), 
244                 unsigned long theEvent,
245                 void* theClientData, 
246                 void* vtkNotUsed(theCallData))
247 {
248   if(vtkObject* anObj = reinterpret_cast<vtkObject*>(theClientData)){ 
249     if(SVTK_Recorder* aSelf = dynamic_cast<SVTK_Recorder*>(anObj)){
250       if(theEvent==vtkCommand::EndEvent){
251         if(aSelf->State() == SVTK_Recorder::SVTK_Recorder_Record){
252           aSelf->DoRecord();
253         }
254       }
255     }
256   }
257 }
258
259
260 //----------------------------------------------------------------------------
261 void
262 SVTK_Recorder
263 ::Record()
264 {
265   if(myState == SVTK_Recorder_Stop){
266     if(myRenderWindow){
267       myState = SVTK_Recorder_Record;
268       myFilter->SetInput(myRenderWindow);
269       myFrameIndex = -1;
270       myNbWrittenFrames = 0;
271       myRenderWindow->RemoveObserver(myCommand);
272       myRenderWindow->AddObserver(vtkCommand::EndEvent,
273                                   myCommand,
274                                   myPriority);
275       myRenderWindow->Render();
276     }
277   }
278 }
279
280
281 //----------------------------------------------------------------------------
282 void
283 SVTK_Recorder
284 ::Stop()
285 {
286   QApplication::setOverrideCursor( Qt::WaitCursor );
287
288   if(myState == SVTK_Recorder_Record){ 
289     if(!myPaused)
290       DoRecord();
291
292     myWriterMgr->Stop();
293
294     if(myUseSkippedFrames)
295       AddSkippedFrames();
296
297     myFrameIndexes.clear();
298
299     MakeFileAVI();
300   }
301   myState = SVTK_Recorder_Stop;
302   myPaused = 0;
303
304   QApplication::restoreOverrideCursor();
305 }
306
307
308 //----------------------------------------------------------------------------
309 void
310 SVTK_Recorder
311 ::Pause()
312 {
313   myPaused = myPaused ? 0 : 1;
314   if(myPaused && !myFrameIndexes.empty()){
315     myFrameIndexes.back() *= -1;
316     if(MYDEBUG) cout<<"SVTK_Recorder::Pause - myFrameIndexes.back() = "<<myFrameIndexes.back()<<endl;
317   }
318 }
319
320
321 //----------------------------------------------------------------------------
322 inline 
323 int
324 GetFrameIndex(double theStartTime,
325               double theFPS)
326 {
327   double aTimeNow = vtkTimerLog::GetUniversalTime();
328   double aDelta = aTimeNow - theStartTime;
329   return int(aDelta*theFPS);
330 }
331
332 void
333 SVTK_Recorder
334 ::DoRecord()
335 {
336   if(myPaused)
337     return;
338
339   if(myFrameIndex < 0){
340     myFrameIndex = 0;
341     myTimeStart = vtkTimerLog::GetUniversalTime();
342   }else{
343     int aFrameIndex = GetFrameIndex(myTimeStart,myNbFPS);
344     if(aFrameIndex <= myFrameIndex)
345       return;
346
347     // If there was a "pause" we correct the myTimeStart
348     int aLastFrameIndex = myFrameIndexes.back();
349     if(aLastFrameIndex < 0){
350       myFrameIndexes.back() = abs(myFrameIndexes.back());
351       double aPauseTime = fabs((double)(aFrameIndex - myFrameIndex - 1)) / myNbFPS;
352       if(MYDEBUG) 
353         cout<<"SVTK_Recorder::DoRecord - aFrameIndex = "<<aFrameIndex<<
354           "; aPauseTime = "<<aPauseTime<<endl;
355       myTimeStart += aPauseTime;
356     }
357
358     aFrameIndex = GetFrameIndex(myTimeStart,myNbFPS);
359     if(aFrameIndex <= myFrameIndex)
360       return;
361
362     myFrameIndex = aFrameIndex;
363   }
364
365   myFrameIndexes.push_back(myFrameIndex);
366   if(MYDEBUG) cout<<"SVTK_Recorder::DoRecord - myFrameIndex = "<<myFrameIndex<<endl;
367
368   myRenderWindow->RemoveObserver(myCommand);
369   myFilter->Modified();
370
371   std::string aName;
372   GetNameJPEG(myName,myFrameIndex,aName);
373
374   PreWrite();
375
376   vtkImageData *anImageData = vtkImageData::New(); 
377   anImageData->DeepCopy(myFilter->GetOutput());
378
379   myWriterMgr->StartImageWriter(anImageData,aName,myProgressiveMode,myQuality);
380   myNbWrittenFrames++;
381
382   myRenderWindow->AddObserver(vtkCommand::EndEvent,
383                               myCommand,
384                               myPriority);
385 }
386
387
388 //----------------------------------------------------------------------------
389 void
390 SVTK_Recorder
391 ::PreWrite()
392 {
393   vtkImageData *anImageData = myFilter->GetOutput();
394   //
395   if(!anImageData){
396     myErrorStatus = 20;
397     return;
398   }
399   anImageData->UpdateInformation();
400   int *anExtent = anImageData->GetWholeExtent();
401   anImageData->SetUpdateExtent(anExtent[0], anExtent[1],
402                                anExtent[2], anExtent[3],
403                                0,0);
404   anImageData->UpdateData();
405 }
406
407
408 //----------------------------------------------------------------------------
409 void
410 SVTK_Recorder
411 ::AddSkippedFrames()
412 {
413   myErrorStatus = 0;
414
415   if(myFrameIndexes.size() < 2)
416     return;
417
418   size_t anId = 0, anEnd = myFrameIndexes.size() - 1;
419   for(; anId < anEnd; anId++){
420     int aStartIndex = myFrameIndexes[anId];
421     if(aStartIndex < 0)
422       continue;
423
424     int aFinishIndex = abs(myFrameIndexes[anId + 1]);
425     if(aStartIndex + 1 == aFinishIndex)
426       continue;
427
428     std::string anInitialName;
429     std::ostringstream aStream;
430     GetNameJPEG(myName,aStartIndex,anInitialName);
431     for(int anIndex = aStartIndex + 1; anIndex < aFinishIndex; anIndex++){
432       myNbWrittenFrames++;
433       std::string anCurrentName;
434       GetNameJPEG(myName,anIndex,anCurrentName);
435   #ifndef WIN32
436       aStream<<"ln -s "<< anInitialName<<" "<<anCurrentName<<";";
437   #else
438       aStream<<"COPY /Y "<<QString::fromStdString(anInitialName).replace("/","\\\\").toStdString()<<
439                   " "<<QString::fromStdString(anCurrentName).replace("/","\\\\").toStdString()<<" > NUL";
440   #endif
441       if(anIndex + 1 < aFinishIndex)
442   #ifndef WIN32
443         aStream<<" \\";
444         aStream<<endl;
445   #else
446         aStream<<" & ";
447   #endif
448     }
449     std::string aString(aStream.str());
450     system(aString.c_str());
451     if(MYDEBUG) cout<<"SVTK_Recorder::AddSkippedFrames - "<<aString<<endl;
452   }
453 }
454
455
456 //----------------------------------------------------------------------------
457 void
458 SVTK_Recorder
459 ::MakeFileAVI()
460 {
461   myErrorStatus = 0;
462   std::ostringstream aStream;
463   aStream<<myNameAVIMaker<<
464     " -I p"<<
465     " -v 0"<<
466     //" -f "<<int(myNbFPS)<<" "<<
467     " -f "<<myNbFPS<<" "<<
468     " -n "<<myNbWrittenFrames<<" "<<
469     " -j \""<<myName<<"_\%06d.jpeg\" "<<
470     "| yuv2lav"<<" -o \""<<myName<<"\"";
471 #ifdef WIN32
472   aStream<<" -f aA";   
473 #endif
474   std::string aString(aStream.str());
475   myErrorStatus = system(aString.c_str());
476
477   if(MYDEBUG) cout<<"SVTK_Recorder::MakeFileAVI - "<<aString<<endl;
478
479   QFileInfo aFileInfo(myName.c_str());
480   QString aDirPath = aFileInfo.absoluteDir().path();
481   QString aBaseName = aFileInfo.fileName();
482   QString aCommand;
483 #ifndef WIN32
484   aCommand = QString("(cd ") + aDirPath + 
485     "; ls " +
486     " | egrep '" + aBaseName + "_[0-9]*.jpeg'" +
487     " | xargs rm " +
488     ")";
489 #else
490   QString tmpFile = QString("_") + aBaseName + "_tempfile";
491   QString diskName = aDirPath.split("/")[0];
492   aCommand = diskName + " && (cd " + aDirPath.replace("/","\\\\") + 
493         " && ((dir /b | findstr " + aBaseName + "_[0-9]*.jpeg > " + tmpFile + 
494         ") & (for /f %i in (" + tmpFile + ") do (del \"%i\")) & (del " + tmpFile + "))) > NUL";
495 #endif
496
497   if(MYDEBUG) cout<<"SVTK_Recorder::MakeFileAVI - "<<(const char*)aCommand.toLatin1()<<endl;
498   system((const char*)aCommand.toLatin1());
499 }