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