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