Salome HOME
Unicode support: correct handling of unicode on GUI level
[modules/gui.git] / src / SVTK / SVTK_Recorder.cxx
1 // Copyright (C) 2007-2016  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, or (at your option) any later version.
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 vtkStandardNewMacro(SVTK_Recorder);
72
73
74 //----------------------------------------------------------------------------
75 SVTK_Recorder
76 ::SVTK_Recorder():
77   myRenderWindow(NULL),
78   myState(SVTK_Recorder_Stop),
79   myNbFPS(5.5),
80   myQuality(100),
81   myProgressiveMode(true),
82   myUseSkippedFrames(true),
83   myErrorStatus(0),
84   myCommand(vtkCallbackCommand::New()),
85   myPriority(0.0),
86   myTimeStart(0.0),
87   myFrameIndex(0),
88   myPaused(0),
89   myFilter(vtkWindowToImageFilter::New()),
90   myWriterMgr(new SVTK_ImageWriterMgr),
91   myNbWrittenFrames(0),
92   myNameAVIMaker("jpeg2yuv")
93 {
94   myCommand->SetClientData(this); 
95   myCommand->SetCallback(SVTK_Recorder::ProcessEvents);
96 }
97
98
99 //----------------------------------------------------------------------------
100 SVTK_Recorder
101 ::~SVTK_Recorder()
102 {
103   myCommand->Delete();
104   myFilter->Delete();
105   delete myWriterMgr;
106 }
107
108
109 //----------------------------------------------------------------------------
110 void
111 SVTK_Recorder
112 ::CheckExistAVIMaker()
113 {
114   myErrorStatus = 0;
115   std::ostringstream aStream;
116 #ifndef WIN32
117   aStream<<"which "<<myNameAVIMaker<<" 2> /dev/null";
118 #else
119   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";
120 #endif
121   std::string anAVIMakeCheck = aStream.str();
122   int iErr = system(anAVIMakeCheck.c_str());
123   if(iErr != 0)
124     myErrorStatus = 127;
125 }
126
127
128 //----------------------------------------------------------------------------
129 void
130 SVTK_Recorder
131 ::SetName(const char* theName)
132 {
133   myName = theName;
134 }
135
136 const char* 
137 SVTK_Recorder::Name() const
138 {
139   return myName.c_str();
140 }
141
142
143 //----------------------------------------------------------------------------
144 void
145 SVTK_Recorder
146 ::SetNbFPS(const double theNbFPS)
147 {
148   myNbFPS = theNbFPS;
149 }
150
151 double
152 SVTK_Recorder
153 ::NbFPS() const
154 {
155   return myNbFPS;
156 }
157
158
159 //----------------------------------------------------------------------------
160 void
161 SVTK_Recorder
162 ::SetQuality(int theQuality)
163 {
164   myQuality = theQuality;
165 }
166
167 int
168 SVTK_Recorder
169 ::GetQuality() const
170 {
171   return myQuality;
172 }
173
174
175 //----------------------------------------------------------------------------
176 void 
177 SVTK_Recorder
178 ::SetRenderWindow(vtkRenderWindow* theRenderWindow)
179 {
180   myRenderWindow = theRenderWindow;
181 }
182
183 vtkRenderWindow* 
184 SVTK_Recorder
185 ::RenderWindow()
186 {
187   return myRenderWindow;
188 }
189
190
191 //----------------------------------------------------------------------------
192 void
193 SVTK_Recorder
194 ::SetProgressiveMode(bool theProgressiveMode)
195 {
196   myProgressiveMode = theProgressiveMode;
197 }
198
199 bool
200 SVTK_Recorder
201 ::GetProgressiveMode() const
202 {
203   return myProgressiveMode;
204 }
205
206
207 //----------------------------------------------------------------------------
208 void
209 SVTK_Recorder
210 ::SetUseSkippedFrames(bool theUseSkippedFrames)
211 {
212   myUseSkippedFrames = theUseSkippedFrames;
213 }
214
215 bool
216 SVTK_Recorder
217 ::UseSkippedFrames() const
218 {
219   return myUseSkippedFrames;
220 }
221
222
223 //----------------------------------------------------------------------------
224 int
225 SVTK_Recorder
226 ::ErrorStatus() const
227 {
228   return myErrorStatus;
229 }
230
231 int
232 SVTK_Recorder
233 ::State() const
234 {
235   return myState;
236 }
237
238
239 //----------------------------------------------------------------------------
240 void
241 SVTK_Recorder
242 ::ProcessEvents(vtkObject* vtkNotUsed(theObject), 
243                 unsigned long theEvent,
244                 void* theClientData, 
245                 void* vtkNotUsed(theCallData))
246 {
247   if(vtkObject* anObj = reinterpret_cast<vtkObject*>(theClientData)){ 
248     if(SVTK_Recorder* aSelf = dynamic_cast<SVTK_Recorder*>(anObj)){
249       if(theEvent==vtkCommand::EndEvent){
250         if(aSelf->State() == SVTK_Recorder::SVTK_Recorder_Record){
251           aSelf->DoRecord();
252         }
253       }
254     }
255   }
256 }
257
258
259 //----------------------------------------------------------------------------
260 void
261 SVTK_Recorder
262 ::Record()
263 {
264   if(myState == SVTK_Recorder_Stop){
265     if(myRenderWindow){
266       myState = SVTK_Recorder_Record;
267       myFilter->SetInput(myRenderWindow);
268       myFrameIndex = -1;
269       myNbWrittenFrames = 0;
270       myRenderWindow->RemoveObserver(myCommand);
271       myRenderWindow->AddObserver(vtkCommand::EndEvent,
272                                   myCommand,
273                                   myPriority);
274       myRenderWindow->Render();
275     }
276   }
277 }
278
279
280 //----------------------------------------------------------------------------
281 void
282 SVTK_Recorder
283 ::Stop()
284 {
285   QApplication::setOverrideCursor( Qt::WaitCursor );
286
287   if(myState == SVTK_Recorder_Record){ 
288     if(!myPaused)
289       DoRecord();
290
291     myWriterMgr->Stop();
292
293     if(myUseSkippedFrames)
294       AddSkippedFrames();
295
296     myFrameIndexes.clear();
297
298     MakeFileAVI();
299   }
300   myState = SVTK_Recorder_Stop;
301   myPaused = 0;
302
303   QApplication::restoreOverrideCursor();
304 }
305
306
307 //----------------------------------------------------------------------------
308 void
309 SVTK_Recorder
310 ::Pause()
311 {
312   myPaused = myPaused ? 0 : 1;
313   if(myPaused && !myFrameIndexes.empty()){
314     myFrameIndexes.back() *= -1;
315     if(MYDEBUG) cout<<"SVTK_Recorder::Pause - myFrameIndexes.back() = "<<myFrameIndexes.back()<<endl;
316   }
317 }
318
319
320 //----------------------------------------------------------------------------
321 inline 
322 int
323 GetFrameIndex(double theStartTime,
324               double theFPS)
325 {
326   double aTimeNow = vtkTimerLog::GetUniversalTime();
327   double aDelta = aTimeNow - theStartTime;
328   return int(aDelta*theFPS);
329 }
330
331 void
332 SVTK_Recorder
333 ::DoRecord()
334 {
335   if(myPaused)
336     return;
337
338   if(myFrameIndex < 0){
339     myFrameIndex = 0;
340     myTimeStart = vtkTimerLog::GetUniversalTime();
341   }else{
342     int aFrameIndex = GetFrameIndex(myTimeStart,myNbFPS);
343     if(aFrameIndex <= myFrameIndex)
344       return;
345
346     // If there was a "pause" we correct the myTimeStart
347     int aLastFrameIndex = myFrameIndexes.back();
348     if(aLastFrameIndex < 0){
349       myFrameIndexes.back() = abs(myFrameIndexes.back());
350       double aPauseTime = fabs((double)(aFrameIndex - myFrameIndex - 1)) / myNbFPS;
351       if(MYDEBUG) 
352         cout<<"SVTK_Recorder::DoRecord - aFrameIndex = "<<aFrameIndex<<
353           "; aPauseTime = "<<aPauseTime<<endl;
354       myTimeStart += aPauseTime;
355     }
356
357     aFrameIndex = GetFrameIndex(myTimeStart,myNbFPS);
358     if(aFrameIndex <= myFrameIndex)
359       return;
360
361     myFrameIndex = aFrameIndex;
362   }
363
364   myFrameIndexes.push_back(myFrameIndex);
365   if(MYDEBUG) cout<<"SVTK_Recorder::DoRecord - myFrameIndex = "<<myFrameIndex<<endl;
366
367   myRenderWindow->RemoveObserver(myCommand);
368   myFilter->Modified();
369
370   std::string aName;
371   GetNameJPEG(myName,myFrameIndex,aName);
372
373   PreWrite();
374
375   vtkImageData *anImageData = vtkImageData::New(); 
376   anImageData->DeepCopy(myFilter->GetOutput());
377
378   myWriterMgr->StartImageWriter(myFilter,anImageData,aName,myProgressiveMode,myQuality);
379   myNbWrittenFrames++;
380
381   myRenderWindow->AddObserver(vtkCommand::EndEvent,
382                               myCommand,
383                               myPriority);
384 }
385
386
387 //----------------------------------------------------------------------------
388 void
389 SVTK_Recorder
390 ::PreWrite()
391 {
392   vtkImageData *anImageData = myFilter->GetOutput();
393   //
394   if(!anImageData){
395     myErrorStatus = 20;
396     return;
397   }
398   myFilter->UpdateInformation();
399   myFilter->UpdateWholeExtent();
400 }
401
402
403 //----------------------------------------------------------------------------
404 void
405 SVTK_Recorder
406 ::AddSkippedFrames()
407 {
408   myErrorStatus = 0;
409
410   if(myFrameIndexes.size() < 2)
411     return;
412
413   size_t anId = 0, anEnd = myFrameIndexes.size() - 1;
414   for(; anId < anEnd; anId++){
415     int aStartIndex = myFrameIndexes[anId];
416     if(aStartIndex < 0)
417       continue;
418
419     int aFinishIndex = abs(myFrameIndexes[anId + 1]);
420     if(aStartIndex + 1 == aFinishIndex)
421       continue;
422
423     std::string anInitialName;
424     std::ostringstream aStream;
425     GetNameJPEG(myName,aStartIndex,anInitialName);
426     for(int anIndex = aStartIndex + 1; anIndex < aFinishIndex; anIndex++){
427       myNbWrittenFrames++;
428       std::string anCurrentName;
429       GetNameJPEG(myName,anIndex,anCurrentName);
430   #ifndef WIN32
431       aStream<<"ln -s "<< anInitialName<<" "<<anCurrentName<<";";
432   #else
433       aStream<<"COPY /Y "<<QString::fromStdString(anInitialName).replace("/","\\\\").toStdString()<<
434                   " "<<QString::fromStdString(anCurrentName).replace("/","\\\\").toStdString()<<" > NUL";
435   #endif
436       if(anIndex + 1 < aFinishIndex)
437   #ifndef WIN32
438         aStream<<" \\";
439         aStream<<endl;
440   #else
441         aStream<<" & ";
442   #endif
443     }
444     std::string aString(aStream.str());
445     system(aString.c_str());
446     if(MYDEBUG) cout<<"SVTK_Recorder::AddSkippedFrames - "<<aString<<endl;
447   }
448 }
449
450
451 //----------------------------------------------------------------------------
452 void
453 SVTK_Recorder
454 ::MakeFileAVI()
455 {
456   myErrorStatus = 0;
457   std::ostringstream aStream;
458   aStream<<myNameAVIMaker<<
459     " -I p"<<
460     " -v 0"<<
461     //" -f "<<int(myNbFPS)<<" "<<
462     " -f "<<myNbFPS<<" "<<
463     " -n "<<myNbWrittenFrames<<" "<<
464     " -j \""<<myName<<"_\%06d.jpeg\" "<<
465     "| yuv2lav"<<" -o \""<<myName<<"\"";
466 #ifdef WIN32
467   aStream<<" -f aA";   
468 #endif
469   std::string aString(aStream.str());
470   myErrorStatus = system(aString.c_str());
471
472   if(MYDEBUG) cout<<"SVTK_Recorder::MakeFileAVI - "<<aString<<endl;
473
474   QFileInfo aFileInfo(myName.c_str());
475   QString aDirPath = aFileInfo.absoluteDir().path();
476   QString aBaseName = aFileInfo.fileName();
477   QString aCommand;
478 #ifndef WIN32
479   aCommand = QString("(cd ") + aDirPath + 
480     "; ls " +
481     " | egrep '" + aBaseName + "_[0-9]*.jpeg'" +
482     " | xargs rm " +
483     ")";
484 #else
485   QString tmpFile = QString("_") + aBaseName + "_tempfile";
486   QString diskName = aDirPath.split("/")[0];
487   aCommand = diskName + " && (cd " + aDirPath.replace("/","\\\\") + 
488         " && ((dir /b | findstr " + aBaseName + "_[0-9]*.jpeg > " + tmpFile + 
489         ") & (for /f %i in (" + tmpFile + ") do (del \"%i\")) & (del " + tmpFile + "))) > NUL";
490 #endif
491
492   if(MYDEBUG) cout<<"SVTK_Recorder::MakeFileAVI - "<<(const char*)aCommand.toUtf8()<<endl;
493   system((const char*)aCommand.toUtf8());
494 }