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

Generated on Wed Feb 20 15:45:27 2008 for SonicVisualiser by  doxygen 1.5.1