tomwalters@397
|
1 // Copyright 2006, Willem van Engen
|
tomwalters@397
|
2 //
|
tomwalters@397
|
3 // AIM-C: A C++ implementation of the Auditory Image Model
|
tomwalters@397
|
4 // http://www.acousticscale.org/AIMC
|
tomwalters@397
|
5 //
|
tomwalters@397
|
6 // Licensed under the Apache License, Version 2.0 (the "License");
|
tomwalters@397
|
7 // you may not use this file except in compliance with the License.
|
tomwalters@397
|
8 // You may obtain a copy of the License at
|
tomwalters@397
|
9 //
|
tomwalters@397
|
10 // http://www.apache.org/licenses/LICENSE-2.0
|
tomwalters@397
|
11 //
|
tomwalters@397
|
12 // Unless required by applicable law or agreed to in writing, software
|
tomwalters@397
|
13 // distributed under the License is distributed on an "AS IS" BASIS,
|
tomwalters@397
|
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
tomwalters@397
|
15 // See the License for the specific language governing permissions and
|
tomwalters@397
|
16 // limitations under the License.
|
tomwalters@397
|
17
|
tomwalters@397
|
18 /*!
|
tomwalters@397
|
19 * \file
|
tomwalters@397
|
20 * \brief Output device for output to a movie
|
tomwalters@397
|
21 *
|
tomwalters@397
|
22 * \author Willem van Engen <cnbh@willem.engen.nl>
|
tomwalters@397
|
23 * \date created 2006/10/16
|
tomwalters@397
|
24 * \version \$Id: GraphicsOutputDeviceMovie.cpp 633 2008-09-11 04:20:16Z tom $
|
tomwalters@397
|
25 */
|
tomwalters@397
|
26
|
tomwalters@397
|
27 /*! \todo
|
tomwalters@397
|
28 * A recent experiment showed that the video was about an audioframe
|
tomwalters@397
|
29 * (something like 30 ms) behind the audio in rdct.wav. It seems odd
|
tomwalters@397
|
30 * to me, since I already output a frame at the beginning to compensate
|
tomwalters@397
|
31 * for the missed buffer.
|
tomwalters@397
|
32 * A solution that would solve this and be a broader improvement, is to
|
tomwalters@397
|
33 * include the source's time when Fire()ing. The outputdevice can then
|
tomwalters@397
|
34 * do audio/video synchronization. In the case of the movie, the very
|
tomwalters@397
|
35 * first gGrab() looks at the time and emits as much empty output frames
|
tomwalters@397
|
36 * as needed until the correct signal time is reached.
|
tomwalters@397
|
37 */
|
tomwalters@397
|
38 #include "Support/Common.h"
|
tomwalters@397
|
39
|
tomwalters@397
|
40 #ifdef _WINDOWS
|
tomwalters@398
|
41 # include <direct.h> // for _mkdir&_rmdir
|
tomwalters@397
|
42 #else
|
tomwalters@398
|
43 # include <sys/types.h>
|
tomwalters@398
|
44 # include <dirent.h> // for opendir&friends
|
tomwalters@397
|
45 #endif
|
tomwalters@397
|
46 #include <stdio.h>
|
tomwalters@397
|
47 #include <string.h>
|
tomwalters@397
|
48
|
tom@400
|
49 #include "Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovie.h"
|
tomwalters@397
|
50
|
tom@400
|
51 namespace aimc {
|
tomwalters@397
|
52
|
tomwalters@397
|
53 GraphicsOutputDeviceMovie::GraphicsOutputDeviceMovie(Parameters *pParam)
|
tomwalters@398
|
54 : GraphicsOutputDeviceCairo(pParam) { // or GraphicsOutputDevicePlotutils
|
tomwalters@398
|
55 m_sMovieFile[0] = '\0';
|
tomwalters@398
|
56 m_sSoundFile[0] = '\0';
|
tomwalters@397
|
57 }
|
tomwalters@397
|
58
|
tomwalters@397
|
59 bool GraphicsOutputDeviceMovie::Initialize(const char *sSoundFile,
|
tomwalters@397
|
60 const char *sMovieFile) {
|
tomwalters@398
|
61 FILE *f;
|
tomwalters@398
|
62 AIM_ASSERT(sSoundFile);
|
tomwalters@398
|
63 AIM_ASSERT(sMovieFile);
|
tomwalters@397
|
64
|
tomwalters@398
|
65 // Check sound file exists
|
tomwalters@398
|
66 if ((f = fopen(sSoundFile, "r")) == NULL) {
|
tom@400
|
67 LOG_ERROR(_T("Couldn't open sound file '%s' for movie creation."),
|
tomwalters@397
|
68 sSoundFile);
|
tomwalters@398
|
69 return false;
|
tomwalters@398
|
70 }
|
tomwalters@398
|
71 fclose(f);
|
tomwalters@398
|
72 strcpy(m_sSoundFile, sSoundFile);
|
tomwalters@397
|
73
|
tomwalters@398
|
74 // Check movie output file can be made
|
tomwalters@398
|
75 if ( (f=fopen(sMovieFile, "w"))==NULL ) {
|
tom@400
|
76 LOG_ERROR(_T("Couldn't open movie file '%s' to write to."),
|
tomwalters@397
|
77 sMovieFile);
|
tomwalters@398
|
78 return false;
|
tomwalters@398
|
79 }
|
tomwalters@398
|
80 fclose(f);
|
tomwalters@398
|
81 strcpy(m_sMovieFile, sMovieFile);
|
tomwalters@397
|
82
|
tomwalters@398
|
83 // Get a temporary image output directory
|
tomwalters@398
|
84 //! \warning Not really safe ... but windows has no mkdtemp()
|
tomwalters@398
|
85 //! \todo Make build system check for mkdtemp() to use it when available. See TODO.txt.
|
tomwalters@398
|
86 char *sTmpDir = NULL;
|
tomwalters@397
|
87 #ifdef _WINDOWS
|
tomwalters@398
|
88 if ((sTmpDir = _tempnam(NULL, AIM_NAME))
|
tomwalters@397
|
89 && _mkdir(sTmpDir) >= 0) {
|
tomwalters@398
|
90 strcpy(m_sDir, sTmpDir);
|
tomwalters@398
|
91 strcat(m_sDir, "\\"); // Make sure to end with trailing slash
|
tomwalters@398
|
92 } else
|
tomwalters@397
|
93 #else
|
tomwalters@398
|
94 strcpy(m_sDir, "/tmp/"AIM_NAME"-movie.XXXXXX");
|
tomwalters@398
|
95 if (mkdtemp(m_sDir)) {
|
tomwalters@398
|
96 strcat(m_sDir, "/"); // Make sure to end with trailing slash
|
tomwalters@398
|
97 } else
|
tomwalters@397
|
98 #endif
|
tomwalters@398
|
99 {
|
tom@400
|
100 LOG_ERROR(_T("Couldn't create a temporary directory for movie output."));
|
tomwalters@398
|
101 if (sTmpDir) free(sTmpDir);
|
tomwalters@398
|
102 return false;
|
tomwalters@398
|
103 }
|
tomwalters@398
|
104 if (sTmpDir) {
|
tomwalters@397
|
105 free(sTmpDir);
|
tomwalters@397
|
106 }
|
tomwalters@397
|
107
|
tomwalters@398
|
108 // We want png for movie conversion
|
tomwalters@398
|
109 //! \bug This may change the user preference in GUI, hmm what to do? See TODO.txt
|
tomwalters@398
|
110 m_pParam->SetString("output.img.format", "png");
|
tomwalters@398
|
111 //if ( !GraphicsOutputDevicePlotutils::Initialize(m_sDir) ) {
|
tomwalters@398
|
112 if ( !GraphicsOutputDeviceCairo::Initialize(m_sDir) ) {
|
tomwalters@398
|
113 return false;
|
tomwalters@397
|
114 }
|
tomwalters@397
|
115
|
tomwalters@398
|
116 return true;
|
tomwalters@397
|
117 }
|
tomwalters@397
|
118
|
tomwalters@397
|
119 void GraphicsOutputDeviceMovie::Start() {
|
tomwalters@398
|
120 //GraphicsOutputDevicePlotutils::Start();
|
tomwalters@398
|
121 GraphicsOutputDeviceCairo::Start();
|
tomwalters@398
|
122 // Just output a single frame to get audio/video in sync, put params in there
|
tomwalters@398
|
123 gGrab();
|
tomwalters@398
|
124 PlotParameterScreen();
|
tomwalters@398
|
125 gRelease();
|
tomwalters@397
|
126 }
|
tomwalters@397
|
127
|
tomwalters@397
|
128 void GraphicsOutputDeviceMovie::Stop() {
|
tomwalters@398
|
129 // Make sure Plotutils is really done writing.
|
tomwalters@398
|
130 //GraphicsOutputDevicePlotutils::Stop();
|
tomwalters@398
|
131 GraphicsOutputDeviceCairo::Stop();
|
tomwalters@398
|
132 CloseFile();
|
tomwalters@397
|
133
|
tomwalters@397
|
134 #ifdef __WX__
|
tomwalters@398
|
135 // GUI only: popup dialog
|
tomwalters@397
|
136 #else
|
tomwalters@398
|
137 printf("Generating movie ... \n");
|
tomwalters@397
|
138 #endif
|
tomwalters@398
|
139 AIM_ASSERT(m_pParam);
|
tomwalters@398
|
140 // Convert images and sound file to a movie
|
tomwalters@398
|
141 //! \warning Movie files are overwritten without warning
|
tomwalters@398
|
142 //! \bug ffmpeg only works with colour images, not with bw. So make sure to not use bw only in drawing..
|
tomwalters@398
|
143 // Always convert to audio stream of 44.1kHz or problems may occur in playing or conversio.
|
tomwalters@398
|
144 float fFps = 1000.0 / m_pParam->GetFloat("output.frameperiod");
|
tomwalters@398
|
145 char sffmpegPath[1024];
|
tomwalters@398
|
146 if (!m_pParam->IsSet("output.ffmpeg_path")) {
|
tomwalters@397
|
147 strcpy(sffmpegPath,"ffmpeg");
|
tomwalters@397
|
148 } else {
|
tomwalters@398
|
149 strcpy(sffmpegPath, m_pParam->GetString("output.ffmpeg_path"));
|
tomwalters@397
|
150 }
|
tomwalters@397
|
151 char sCodecOptions[1024];
|
tomwalters@398
|
152 if (!m_pParam->IsSet("output.ffmpeg_codec_options")) {
|
tomwalters@397
|
153 strcpy(sCodecOptions,"");
|
tomwalters@397
|
154 } else {
|
tomwalters@398
|
155 strcpy(sCodecOptions, m_pParam->GetString("output.ffmpeg_codec_options"));
|
tomwalters@397
|
156 }
|
tomwalters@397
|
157
|
tomwalters@398
|
158 char sCmdLine[1024]; //!\todo check that snprintf does not want a larger buffer
|
tomwalters@398
|
159 snprintf(sCmdLine, sizeof(sCmdLine)/sizeof(sCmdLine[0]),
|
tomwalters@398
|
160 "%s -r %.2f -y -i \"%s\" -i \"%s%%06d.png\" "
|
tomwalters@398
|
161 "-title \"%s\" -comment \"Generated by "AIM_NAME" "AIM_VERSION_STRING"\" "
|
tomwalters@398
|
162 "-sameq -r %.2f -ar 44100 -acodec pcm_s16le %s \"%s\"",
|
tomwalters@398
|
163 sffmpegPath, fFps, m_sSoundFile, m_sDir,
|
tomwalters@398
|
164 m_pParam->GetString("output.movie.title"),
|
tomwalters@398
|
165 fFps, sCodecOptions, m_sMovieFile);
|
tomwalters@397
|
166 printf(sCmdLine);
|
tomwalters@397
|
167 printf("\n");
|
tomwalters@398
|
168 if (system(sCmdLine)) {
|
tom@400
|
169 LOG_ERROR(_T("Couldn't create movie output."));
|
tomwalters@398
|
170 }
|
tomwalters@397
|
171
|
tomwalters@397
|
172 #ifdef __WX__
|
tomwalters@398
|
173 // GUI only: close dialog again
|
tomwalters@397
|
174 #endif
|
tomwalters@398
|
175 // Remove files in temporary directory and the dir itself
|
tomwalters@398
|
176 //! \todo make portable function, possibly decided on by build system
|
tomwalters@397
|
177 #ifdef _WINDOWS
|
tomwalters@398
|
178 HANDLE hList;
|
tomwalters@398
|
179 WIN32_FIND_DATA FileData;
|
tomwalters@398
|
180 snprintf(sCmdLine, sizeof(sCmdLine)/sizeof(sCmdLine[0]), "%s/*.*", m_sDir);
|
tomwalters@398
|
181 if ((hList = FindFirstFile(sCmdLine, &FileData)) == INVALID_HANDLE_VALUE) {
|
tom@400
|
182 LOG_ERROR(_T("Couldn't remove files from temporary directory."));
|
tomwalters@398
|
183 return;
|
tomwalters@398
|
184 }
|
tomwalters@398
|
185 bool bRMfinished = false;
|
tomwalters@398
|
186 while (!bRMfinished) {
|
tomwalters@398
|
187 snprintf(sCmdLine,
|
tomwalters@397
|
188 sizeof(sCmdLine)/sizeof(sCmdLine[0]),
|
tomwalters@398
|
189 "%s%s",
|
tomwalters@397
|
190 m_sDir,
|
tomwalters@397
|
191 FileData.cFileName);
|
tomwalters@398
|
192 remove(sCmdLine);
|
tomwalters@398
|
193 if (!FindNextFile(hList, &FileData) && GetLastError() == ERROR_NO_MORE_FILES) {
|
tomwalters@398
|
194 bRMfinished = true;
|
tomwalters@397
|
195 }
|
tomwalters@398
|
196 }
|
tomwalters@398
|
197 FindClose(hList);
|
tomwalters@398
|
198 _rmdir(m_sDir);
|
tomwalters@397
|
199 #else
|
tomwalters@398
|
200 DIR *dir;
|
tomwalters@398
|
201 struct dirent *dirent;
|
tomwalters@398
|
202 if (!(dir = opendir(m_sDir))) {
|
tom@400
|
203 LOG_ERROR(_T("Couldn't remove files in temporary directory."));
|
tomwalters@398
|
204 return;
|
tomwalters@398
|
205 }
|
tomwalters@402
|
206 while ((dirent = readdir(dir))) {
|
tomwalters@398
|
207 snprintf(sCmdLine,
|
tomwalters@397
|
208 sizeof(sCmdLine)/sizeof(sCmdLine[0]),
|
tomwalters@398
|
209 "%s%s",
|
tomwalters@397
|
210 m_sDir,
|
tomwalters@397
|
211 dirent->d_name);
|
tomwalters@398
|
212 unlink(sCmdLine);
|
tomwalters@398
|
213 }
|
tomwalters@398
|
214 closedir(dir);
|
tomwalters@398
|
215 rmdir(m_sDir);
|
tomwalters@397
|
216 #endif
|
tomwalters@397
|
217 }
|
tomwalters@397
|
218
|
tomwalters@397
|
219 void GraphicsOutputDeviceMovie::PlotParameterScreen() {
|
tomwalters@398
|
220 AIM_ASSERT(m_pParam);
|
tomwalters@398
|
221 char sStr[50];
|
tomwalters@398
|
222 int lineno = 1;
|
tomwalters@397
|
223
|
tomwalters@398
|
224 float fMarL = m_pParam->GetFloat(_S("graph.margin.left"));
|
tomwalters@398
|
225 float fMarT = m_pParam->GetFloat(_S("graph.margin.top"));
|
tomwalters@398
|
226 float fTextHeight = 1.0f / 50.0f * 1.2; // change this when fontsizing is there!
|
tomwalters@397
|
227
|
tomwalters@398
|
228 gText2f(fMarL, 1-(fMarT+fTextHeight*lineno++),
|
tomwalters@397
|
229 _S("AIM-C"));
|
tomwalters@398
|
230 gText2f(fMarL,
|
tomwalters@397
|
231 1-(fMarT+fTextHeight*lineno++),
|
tomwalters@397
|
232 _S("(c) 2006-2010, Thomas Walters, Willem van Engen"));
|
tomwalters@398
|
233 gText2f(fMarL,
|
tomwalters@397
|
234 1-(fMarT+fTextHeight*lineno++),
|
tomwalters@397
|
235 _S("http://aimc.acousticscale.org/"));
|
tomwalters@398
|
236 lineno++;
|
tomwalters@397
|
237
|
tomwalters@398
|
238 static const char *pPlotParams[] = {
|
tomwalters@398
|
239 _S("input.buffersize"),
|
tomwalters@398
|
240 _S("input.samplerate"),
|
tomwalters@398
|
241 _S("bmm.freqstart"),
|
tomwalters@398
|
242 _S("bmm.freqend"),
|
tomwalters@398
|
243 _S("bmm.numchannels"),
|
tomwalters@398
|
244 _S("preset.name"),
|
tomwalters@398
|
245 _S("preset.title"),
|
tomwalters@398
|
246 NULL
|
tomwalters@398
|
247 };
|
tomwalters@398
|
248 for (int i = 0; pPlotParams[i]; i++) {
|
tomwalters@398
|
249 snprintf(sStr,
|
tomwalters@397
|
250 sizeof(sStr)/sizeof(sStr[0]), _S("%s=%s"),
|
tomwalters@398
|
251 pPlotParams[i],
|
tomwalters@397
|
252 m_pParam->GetString(pPlotParams[i]));
|
tomwalters@398
|
253 gText2f(fMarL,
|
tomwalters@397
|
254 1-(fMarT+fTextHeight*lineno++),
|
tomwalters@397
|
255 sStr);
|
tomwalters@398
|
256 }
|
tomwalters@397
|
257 }
|
tom@400
|
258 } // namespace aimc
|