svcore  1.9
QuickTimeFileReader.cpp
Go to the documentation of this file.
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