svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006-2007 Chris Cannam and QMUL. 00008 00009 Based on QTAudioFile.cpp from SoundBite, copyright 2006 00010 Chris Sutton and Mark Levy. 00011 00012 This program is free software; you can redistribute it and/or 00013 modify it under the terms of the GNU General Public License as 00014 published by the Free Software Foundation; either version 2 of the 00015 License, or (at your option) any later version. See the file 00016 COPYING included with this distribution for more information. 00017 */ 00018 00019 #ifdef HAVE_QUICKTIME 00020 00021 #include "QuickTimeFileReader.h" 00022 #include "base/Profiler.h" 00023 #include "base/ProgressReporter.h" 00024 #include "system/System.h" 00025 00026 #include <QFileInfo> 00027 00028 #ifdef _WIN32 00029 #include <QTML.h> 00030 #include <Movies.h> 00031 #else 00032 #include <QuickTime/QuickTime.h> 00033 #endif 00034 00035 class QuickTimeFileReader::D 00036 { 00037 public: 00038 D() : data(0), blockSize(1024) { } 00039 00040 MovieAudioExtractionRef extractionSessionRef; 00041 AudioBufferList buffer; 00042 float *data; 00043 OSErr err; 00044 AudioStreamBasicDescription asbd; 00045 Movie movie; 00046 int blockSize; 00047 }; 00048 00049 00050 QuickTimeFileReader::QuickTimeFileReader(FileSource source, 00051 DecodeMode decodeMode, 00052 CacheMode mode, 00053 int targetRate, 00054 bool normalised, 00055 ProgressReporter *reporter) : 00056 CodedAudioFileReader(mode, targetRate, normalised), 00057 m_source(source), 00058 m_path(source.getLocalFilename()), 00059 m_d(new D), 00060 m_reporter(reporter), 00061 m_cancelled(false), 00062 m_completion(0), 00063 m_decodeThread(0) 00064 { 00065 m_channelCount = 0; 00066 m_fileRate = 0; 00067 00068 Profiler profiler("QuickTimeFileReader::QuickTimeFileReader", true); 00069 00070 SVDEBUG << "QuickTimeFileReader: path is \"" << m_path << "\"" << endl; 00071 00072 long QTversion; 00073 00074 #ifdef WIN32 00075 InitializeQTML(0); // FIXME should check QT version 00076 #else 00077 m_d->err = Gestalt(gestaltQuickTime,&QTversion); 00078 if ((m_d->err != noErr) || (QTversion < 0x07000000)) { 00079 m_error = QString("Failed to find compatible version of QuickTime (version 7 or above required)"); 00080 return; 00081 } 00082 #endif 00083 00084 EnterMovies(); 00085 00086 Handle dataRef; 00087 OSType dataRefType; 00088 00089 // CFStringRef URLString = CFStringCreateWithCString 00090 // (0, m_path.toLocal8Bit().data(), 0); 00091 00092 00093 QByteArray ba = m_path.toLocal8Bit(); 00094 00095 CFURLRef url = CFURLCreateFromFileSystemRepresentation 00096 (kCFAllocatorDefault, 00097 (const UInt8 *)ba.data(), 00098 (CFIndex)ba.length(), 00099 false); 00100 00101 00102 // m_d->err = QTNewDataReferenceFromURLCFString 00103 m_d->err = QTNewDataReferenceFromCFURL 00104 (url, 0, &dataRef, &dataRefType); 00105 00106 if (m_d->err) { 00107 m_error = QString("Error creating data reference for QuickTime decoder: code %1").arg(m_d->err); 00108 return; 00109 } 00110 00111 short fileID = movieInDataForkResID; 00112 short flags = 0; 00113 m_d->err = NewMovieFromDataRef 00114 (&m_d->movie, flags, &fileID, dataRef, dataRefType); 00115 00116 DisposeHandle(dataRef); 00117 if (m_d->err) { 00118 m_error = QString("Error creating new movie for QuickTime decoder: code %1").arg(m_d->err); 00119 return; 00120 } 00121 00122 Boolean isProtected = 0; 00123 Track aTrack = GetMovieIndTrackType 00124 (m_d->movie, 1, SoundMediaType, 00125 movieTrackMediaType | movieTrackEnabledOnly); 00126 00127 if (aTrack) { 00128 Media aMedia = GetTrackMedia(aTrack); // get the track media 00129 if (aMedia) { 00130 MediaHandler mh = GetMediaHandler(aMedia); // get the media handler we can query 00131 if (mh) { 00132 m_d->err = QTGetComponentProperty(mh, 00133 kQTPropertyClass_DRM, 00134 kQTDRMPropertyID_IsProtected, 00135 sizeof(Boolean), &isProtected,nil); 00136 } else { 00137 m_d->err = 1; 00138 } 00139 } else { 00140 m_d->err = 1; 00141 } 00142 } else { 00143 m_d->err = 1; 00144 } 00145 00146 if (m_d->err && m_d->err != kQTPropertyNotSupportedErr) { 00147 m_error = QString("Error checking for DRM in QuickTime decoder: code %1").arg(m_d->err); 00148 return; 00149 } else if (!m_d->err && isProtected) { 00150 m_error = QString("File is protected with DRM"); 00151 return; 00152 } else if (m_d->err == kQTPropertyNotSupportedErr && !isProtected) { 00153 cerr << "QuickTime: File is not protected with DRM" << endl; 00154 } 00155 00156 if (m_d->movie) { 00157 SetMovieActive(m_d->movie, TRUE); 00158 m_d->err = GetMoviesError(); 00159 if (m_d->err) { 00160 m_error = QString("Error in QuickTime decoder activation: code %1").arg(m_d->err); 00161 return; 00162 } 00163 } else { 00164 m_error = QString("Error in QuickTime decoder: Movie object not valid"); 00165 return; 00166 } 00167 00168 m_d->err = MovieAudioExtractionBegin 00169 (m_d->movie, 0, &m_d->extractionSessionRef); 00170 if (m_d->err) { 00171 m_error = QString("Error in QuickTime decoder extraction init: code %1").arg(m_d->err); 00172 return; 00173 } 00174 00175 m_d->err = MovieAudioExtractionGetProperty 00176 (m_d->extractionSessionRef, 00177 kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, 00178 sizeof(m_d->asbd), 00179 &m_d->asbd, 00180 nil); 00181 00182 if (m_d->err) { 00183 m_error = QString("Error in QuickTime decoder property get: code %1").arg(m_d->err); 00184 return; 00185 } 00186 00187 m_channelCount = m_d->asbd.mChannelsPerFrame; 00188 m_fileRate = m_d->asbd.mSampleRate; 00189 00190 cerr << "QuickTime: " << m_channelCount << " channels, " << m_fileRate << " kHz" << endl; 00191 00192 m_d->asbd.mFormatFlags = 00193 kAudioFormatFlagIsFloat | 00194 kAudioFormatFlagIsPacked | 00195 kAudioFormatFlagsNativeEndian; 00196 m_d->asbd.mBitsPerChannel = sizeof(float) * 8; 00197 m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame; 00198 m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame; 00199 00200 m_d->err = MovieAudioExtractionSetProperty 00201 (m_d->extractionSessionRef, 00202 kQTPropertyClass_MovieAudioExtraction_Audio, 00203 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, 00204 sizeof(m_d->asbd), 00205 &m_d->asbd); 00206 00207 if (m_d->err) { 00208 m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err); 00209 m_channelCount = 0; 00210 return; 00211 } 00212 m_d->buffer.mNumberBuffers = 1; 00213 m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount; 00214 m_d->buffer.mBuffers[0].mDataByteSize = 00215 sizeof(float) * m_channelCount * m_d->blockSize; 00216 m_d->data = new float[m_channelCount * m_d->blockSize]; 00217 m_d->buffer.mBuffers[0].mData = m_d->data; 00218 00219 initialiseDecodeCache(); 00220 00221 if (decodeMode == DecodeAtOnce) { 00222 00223 if (m_reporter) { 00224 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); 00225 m_reporter->setMessage 00226 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); 00227 } 00228 00229 while (1) { 00230 00231 UInt32 framesRead = m_d->blockSize; 00232 UInt32 extractionFlags = 0; 00233 m_d->err = MovieAudioExtractionFillBuffer 00234 (m_d->extractionSessionRef, &framesRead, &m_d->buffer, 00235 &extractionFlags); 00236 if (m_d->err) { 00237 m_error = QString("Error in QuickTime decoding: code %1") 00238 .arg(m_d->err); 00239 break; 00240 } 00241 00243 00244 // cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << endl; 00245 00246 // QuickTime buffers are interleaved unless specified otherwise 00247 addSamplesToDecodeCache(m_d->data, framesRead); 00248 00249 if (framesRead < m_d->blockSize) break; 00250 } 00251 00252 finishDecodeCache(); 00253 endSerialised(); 00254 00255 m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef); 00256 if (m_d->err) { 00257 m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err); 00258 } 00259 00260 m_completion = 100; 00261 00262 } else { 00263 if (m_reporter) m_reporter->setProgress(100); 00264 00265 if (m_channelCount > 0) { 00266 m_decodeThread = new DecodeThread(this); 00267 m_decodeThread->start(); 00268 } 00269 } 00270 00271 cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error << "\"" << endl; 00272 } 00273 00274 QuickTimeFileReader::~QuickTimeFileReader() 00275 { 00276 SVDEBUG << "QuickTimeFileReader::~QuickTimeFileReader" << endl; 00277 00278 if (m_decodeThread) { 00279 m_cancelled = true; 00280 m_decodeThread->wait(); 00281 delete m_decodeThread; 00282 } 00283 00284 SetMovieActive(m_d->movie, FALSE); 00285 DisposeMovie(m_d->movie); 00286 00287 delete[] m_d->data; 00288 delete m_d; 00289 } 00290 00291 void 00292 QuickTimeFileReader::cancelled() 00293 { 00294 m_cancelled = true; 00295 } 00296 00297 void 00298 QuickTimeFileReader::DecodeThread::run() 00299 { 00300 if (m_reader->m_cacheMode == CacheInTemporaryFile) { 00301 m_reader->m_completion = 1; 00302 m_reader->startSerialised("QuickTimeFileReader::Decode"); 00303 } 00304 00305 while (1) { 00306 00307 UInt32 framesRead = m_reader->m_d->blockSize; 00308 UInt32 extractionFlags = 0; 00309 m_reader->m_d->err = MovieAudioExtractionFillBuffer 00310 (m_reader->m_d->extractionSessionRef, &framesRead, 00311 &m_reader->m_d->buffer, &extractionFlags); 00312 if (m_reader->m_d->err) { 00313 m_reader->m_error = QString("Error in QuickTime decoding: code %1") 00314 .arg(m_reader->m_d->err); 00315 break; 00316 } 00317 00318 // QuickTime buffers are interleaved unless specified otherwise 00319 m_reader->addSamplesToDecodeCache(m_reader->m_d->data, framesRead); 00320 00321 if (framesRead < m_reader->m_d->blockSize) break; 00322 } 00323 00324 m_reader->finishDecodeCache(); 00325 00326 m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef); 00327 if (m_reader->m_d->err) { 00328 m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err); 00329 } 00330 00331 m_reader->m_completion = 100; 00332 m_reader->endSerialised(); 00333 } 00334 00335 void 00336 QuickTimeFileReader::getSupportedExtensions(std::set<QString> &extensions) 00337 { 00338 extensions.insert("aiff"); 00339 extensions.insert("aif"); 00340 extensions.insert("au"); 00341 extensions.insert("avi"); 00342 extensions.insert("m4a"); 00343 extensions.insert("m4b"); 00344 extensions.insert("m4p"); 00345 extensions.insert("m4v"); 00346 extensions.insert("mov"); 00347 extensions.insert("mp3"); 00348 extensions.insert("mp4"); 00349 extensions.insert("wav"); 00350 } 00351 00352 bool 00353 QuickTimeFileReader::supportsExtension(QString extension) 00354 { 00355 std::set<QString> extensions; 00356 getSupportedExtensions(extensions); 00357 return (extensions.find(extension.toLower()) != extensions.end()); 00358 } 00359 00360 bool 00361 QuickTimeFileReader::supportsContentType(QString type) 00362 { 00363 return (type == "audio/x-aiff" || 00364 type == "audio/x-wav" || 00365 type == "audio/mpeg" || 00366 type == "audio/basic" || 00367 type == "audio/x-aac" || 00368 type == "video/mp4" || 00369 type == "video/quicktime"); 00370 } 00371 00372 bool 00373 QuickTimeFileReader::supports(FileSource &source) 00374 { 00375 return (supportsExtension(source.getExtension()) || 00376 supportsContentType(source.getContentType())); 00377 } 00378 00379 #endif 00380