OSDN Git Service

c7aace93b97bb816aeb1c9bbc6a087ec7cf5a1cd
[lamexp/LameXP.git] / src / Thread_Process.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2010 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "Thread_Process.h"
23
24 #include "Global.h"
25 #include "Model_AudioFile.h"
26 #include "Model_Progress.h"
27 #include "Encoder_Abstract.h"
28 #include "Decoder_Abstract.h"
29 #include "Registry_Decoder.h"
30 #include "Model_Settings.h"
31
32 #include <QUuid>
33 #include <QFileInfo>
34 #include <QDir>
35 #include <QMutex>
36 #include <QMutexLocker>
37
38 #include <limits.h>
39 #include <time.h>
40
41 QMutex *ProcessThread::m_mutex_genFileName = NULL;
42
43 ////////////////////////////////////////////////////////////
44 // Constructor
45 ////////////////////////////////////////////////////////////
46
47 ProcessThread::ProcessThread(const AudioFileModel &audioFile, const QString &outputDirectory, AbstractEncoder *encoder, const bool prependRelativeSourcePath)
48 :
49         m_audioFile(audioFile),
50         m_outputDirectory(outputDirectory),
51         m_encoder(encoder),
52         m_jobId(QUuid::createUuid()),
53         m_prependRelativeSourcePath(prependRelativeSourcePath),
54         m_aborted(false)
55 {
56         if(m_mutex_genFileName)
57         {
58                 m_mutex_genFileName = new QMutex;
59         }
60
61         connect(m_encoder, SIGNAL(statusUpdated(int)), this, SLOT(handleUpdate(int)), Qt::DirectConnection);
62         connect(m_encoder, SIGNAL(messageLogged(QString)), this, SLOT(handleMessage(QString)), Qt::DirectConnection);
63
64         m_currentStep = UnknownStep;
65 }
66
67 ProcessThread::~ProcessThread(void)
68 {
69         while(!m_tempFiles.isEmpty())
70         {
71                 QFile::remove(m_tempFiles.takeFirst());
72         }
73
74         LAMEXP_DELETE(m_encoder);
75 }
76
77 void ProcessThread::run()
78 {
79         try
80         {
81                 processFile();
82         }
83         catch(...)
84         {
85                 fflush(stdout);
86                 fflush(stderr);
87                 fprintf(stderr, "\nEXCEPTION ERROR !!!\n");
88                 FatalAppExit(0, L"Unhandeled exception error, application will exit!");
89                 TerminateProcess(GetCurrentProcess(), -1);
90         }
91 }
92
93 void ProcessThread::processFile()
94 {
95         m_aborted = false;
96         bool bSuccess = true;
97
98         qDebug("Process thread %s has started.", m_jobId.toString().toLatin1().constData());
99         emit processStateInitialized(m_jobId, QFileInfo(m_audioFile.filePath()).fileName(), "Starting...", ProgressModel::JobRunning);
100
101         //Generate output file name
102         QString outFileName = generateOutFileName();
103         if(outFileName.isEmpty())
104         {
105                 emit processStateChanged(m_jobId, "Not found!", ProgressModel::JobFailed);
106                 emit processStateFinished(m_jobId, outFileName, false);
107                 return;
108         }
109
110         QString sourceFile = m_audioFile.filePath();
111
112         //Decode source file
113         if(!m_encoder->isFormatSupported(m_audioFile.formatContainerType(), m_audioFile.formatContainerProfile(), m_audioFile.formatAudioType(), m_audioFile.formatAudioProfile(), m_audioFile.formatAudioVersion()))
114         {
115                 m_currentStep = DecodingStep;
116                 AbstractDecoder *decoder = DecoderRegistry::lookup(m_audioFile.formatContainerType(), m_audioFile.formatContainerProfile(), m_audioFile.formatAudioType(), m_audioFile.formatAudioProfile(), m_audioFile.formatAudioVersion());
117                 
118                 if(decoder)
119                 {
120                         QString tempFile = generateTempFileName();
121
122                         connect(decoder, SIGNAL(statusUpdated(int)), this, SLOT(handleUpdate(int)), Qt::DirectConnection);
123                         connect(decoder, SIGNAL(messageLogged(QString)), this, SLOT(handleMessage(QString)), Qt::DirectConnection);
124
125                         bSuccess = decoder->decode(sourceFile, tempFile, &m_aborted);
126
127                         if(bSuccess)
128                         {
129                                 sourceFile = tempFile;
130                                 handleMessage("\n-------------------------------\n");
131                         }
132                 }
133                 else
134                 {
135                         handleMessage(QString("The format of this file is NOT supported:\n%1\n\nContainer Format:\t%2\nAudio Format:\t%3").arg(m_audioFile.filePath(), m_audioFile.formatContainerInfo(), m_audioFile.formatAudioCompressInfo()));
136                         emit processStateChanged(m_jobId, "Unsupported!", ProgressModel::JobFailed);
137                         emit processStateFinished(m_jobId, outFileName, false);
138                         return;
139                 }
140         }
141
142         //Encode audio file
143         if(bSuccess)
144         {
145                 m_currentStep = EncodingStep;
146                 bSuccess = m_encoder->encode(sourceFile, m_audioFile, outFileName, &m_aborted);
147         }
148
149         //Make sure output file exists
150         if(bSuccess)
151         {
152                 QFileInfo fileInfo(outFileName);
153                 bSuccess = fileInfo.exists() && fileInfo.isFile() && (fileInfo.size() > 0);
154         }
155
156         //Report result
157         emit processStateChanged(m_jobId, (bSuccess ? "Done." : (m_aborted ? "Aborted!" : "Failed!")), (bSuccess ? ProgressModel::JobComplete : ProgressModel::JobFailed));
158         emit processStateFinished(m_jobId, outFileName, bSuccess);
159
160         qDebug("Process thread is done.");
161 }
162
163 ////////////////////////////////////////////////////////////
164 // SLOTS
165 ////////////////////////////////////////////////////////////
166
167 void ProcessThread::handleUpdate(int progress)
168 {
169         switch(m_currentStep)
170         {
171         case EncodingStep:
172                 emit processStateChanged(m_jobId, QString("Encoding (%1%)").arg(QString::number(progress)), ProgressModel::JobRunning);
173                 break;
174         case DecodingStep:
175                 emit processStateChanged(m_jobId, QString("Decoding (%1%)").arg(QString::number(progress)), ProgressModel::JobRunning);
176                 break;
177         }
178 }
179
180 void ProcessThread::handleMessage(const QString &line)
181 {
182         emit processMessageLogged(m_jobId, line);
183 }
184
185 ////////////////////////////////////////////////////////////
186 // PRIVAE FUNCTIONS
187 ////////////////////////////////////////////////////////////
188
189 QString ProcessThread::generateOutFileName(void)
190 {
191         QMutexLocker lock(m_mutex_genFileName);
192         
193         int n = 1;
194
195         QFileInfo sourceFile(m_audioFile.filePath());
196         if(!sourceFile.exists() || !sourceFile.isFile())
197         {
198                 handleMessage(QString("The source audio file could not be found:\n%1").arg(sourceFile.absoluteFilePath()));
199                 return QString();
200         }
201
202         QFile readTest(sourceFile.canonicalFilePath());
203         if(!readTest.open(QIODevice::ReadOnly))
204         {
205                 handleMessage(QString("The source audio file could not be opened for reading:\n%1").arg(readTest.fileName()));
206                 return QString();
207         }
208         else
209         {
210                 readTest.close();
211         }
212
213         QString baseName = sourceFile.completeBaseName();
214         QDir targetDir(m_outputDirectory.isEmpty() ? sourceFile.canonicalPath() : m_outputDirectory);
215
216         if(m_prependRelativeSourcePath && !m_outputDirectory.isEmpty())
217         {
218                 QDir rootDir = sourceFile.dir();
219                 while(!rootDir.isRoot())
220                 {
221                         if(!rootDir.cdUp()) break;
222                 }
223                 targetDir.setPath(QString("%1/%2").arg(targetDir.absolutePath(), QFileInfo(rootDir.relativeFilePath(sourceFile.canonicalFilePath())).path()));
224         }
225         
226         if(!targetDir.exists())
227         {
228                 targetDir.mkpath(".");
229                 if(!targetDir.exists())
230                 {
231                         handleMessage(QString("The target output directory doesn't exist and could NOT be created:\n%1").arg(targetDir.absolutePath()));
232                         return QString();
233                 }
234         }
235         
236         QFile writeTest(QString("%1/.%2").arg(targetDir.canonicalPath(), lamexp_rand_str()));
237         if(!writeTest.open(QIODevice::ReadWrite))
238         {
239                 handleMessage(QString("The target output directory is NOT writable:\n%1").arg(targetDir.absolutePath()));
240                 return QString();
241         }
242         else
243         {
244                 writeTest.close();
245                 writeTest.remove();
246         }
247
248         QString outFileName = QString("%1/%2.%3").arg(targetDir.canonicalPath(), baseName, m_encoder->extension());
249         while(QFileInfo(outFileName).exists())
250         {
251                 outFileName = QString("%1/%2 (%3).%4").arg(targetDir.canonicalPath(), baseName, QString::number(++n), m_encoder->extension());
252         }
253
254         QFile placeholder(outFileName);
255         if(placeholder.open(QIODevice::WriteOnly))
256         {
257                 placeholder.close();
258         }
259
260         return outFileName;
261 }
262
263 QString ProcessThread::generateTempFileName(void)
264 {
265         QMutexLocker lock(m_mutex_genFileName);
266         QString tempFileName = QString("%1/%2.wav").arg(lamexp_temp_folder(), lamexp_rand_str());
267
268         while(QFileInfo(tempFileName).exists())
269         {
270                 tempFileName = QString("%1/%2.wav").arg(lamexp_temp_folder(), lamexp_rand_str());
271         }
272
273         QFile file(tempFileName);
274         if(file.open(QFile::ReadWrite))
275         {
276                 file.close();
277         }
278
279         m_tempFiles << tempFileName;
280         return tempFileName;
281 }
282
283 ////////////////////////////////////////////////////////////
284 // EVENTS
285 ////////////////////////////////////////////////////////////
286
287 /*NONE*/