annotate data/fileio/QuickTimeFileReader.cpp @ 308:14e0f60435b8

* Make it possible to drop audio files, layer files, session files and images onto SV panes. Need to do a bit more work on where we expect the dropped file to go, particularly in the case of audio files -- at the moment they're always opened in new panes, but it may be better to by default replace whatever is in the target pane.
author Chris Cannam
date Wed, 10 Oct 2007 15:18:02 +0000
parents c022976d18e8
children bbf87199e6e1
rev   line source
Chris@281 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@281 2
Chris@281 3 /*
Chris@281 4 Sonic Visualiser
Chris@281 5 An audio file viewer and annotation editor.
Chris@281 6 Centre for Digital Music, Queen Mary, University of London.
Chris@281 7 This file copyright 2006-2007 Chris Cannam and QMUL.
Chris@281 8
Chris@281 9 Based on QTAudioFile.cpp from SoundBite, copyright 2006
Chris@281 10 Chris Sutton and Mark Levy.
Chris@281 11
Chris@281 12 This program is free software; you can redistribute it and/or
Chris@281 13 modify it under the terms of the GNU General Public License as
Chris@281 14 published by the Free Software Foundation; either version 2 of the
Chris@281 15 License, or (at your option) any later version. See the file
Chris@281 16 COPYING included with this distribution for more information.
Chris@281 17 */
Chris@281 18
Chris@281 19 #ifdef HAVE_QUICKTIME
Chris@281 20
Chris@281 21 #include "QuickTimeFileReader.h"
Chris@281 22 #include "base/Profiler.h"
Chris@281 23 #include "system/System.h"
Chris@281 24
Chris@281 25 #include <QApplication>
Chris@281 26 #include <QFileInfo>
Chris@281 27 #include <QProgressDialog>
Chris@281 28
Chris@281 29 #ifdef WIN32
Chris@281 30 #include <QTML.h>
Chris@281 31 #include <Movies.h>
Chris@281 32 #else
Chris@281 33 #include <QuickTime/QuickTime.h>
Chris@281 34 #endif
Chris@281 35
Chris@281 36 class QuickTimeFileReader::D
Chris@281 37 {
Chris@281 38 public:
Chris@281 39 D() : data(0), blockSize(1024) { }
Chris@281 40
Chris@281 41 MovieAudioExtractionRef extractionSessionRef;
Chris@281 42 AudioBufferList buffer;
Chris@282 43 double *data;
Chris@281 44 OSErr err;
Chris@281 45 AudioStreamBasicDescription asbd;
Chris@281 46 Movie movie;
Chris@281 47 size_t blockSize;
Chris@281 48 };
Chris@281 49
Chris@281 50
Chris@290 51 QuickTimeFileReader::QuickTimeFileReader(QString path,
Chris@281 52 DecodeMode decodeMode,
Chris@297 53 CacheMode mode,
Chris@297 54 size_t targetRate) :
Chris@297 55 CodedAudioFileReader(mode, targetRate),
Chris@281 56 m_path(path),
Chris@281 57 m_d(new D),
Chris@281 58 m_progress(0),
Chris@281 59 m_cancelled(false),
Chris@281 60 m_completion(0),
Chris@281 61 m_decodeThread(0)
Chris@281 62 {
Chris@281 63 m_channelCount = 0;
Chris@297 64 m_fileRate = 0;
Chris@281 65
Chris@281 66 Profiler profiler("QuickTimeFileReader::QuickTimeFileReader", true);
Chris@281 67
Chris@282 68 std::cerr << "QuickTimeFileReader: path is \"" << path.toStdString() << "\"" << std::endl;
Chris@282 69
Chris@281 70 long QTversion;
Chris@281 71
Chris@281 72 #ifdef WIN32
Chris@281 73 InitializeQTML(0); // FIXME should check QT version
Chris@281 74 #else
Chris@281 75 m_d->err = Gestalt(gestaltQuickTime,&QTversion);
Chris@281 76 if ((m_d->err != noErr) || (QTversion < 0x07000000)) {
Chris@290 77 m_error = QString("Failed to find compatible version of QuickTime (version 7 or above required)");
Chris@281 78 return;
Chris@281 79 }
Chris@281 80 #endif
Chris@281 81
Chris@281 82 EnterMovies();
Chris@281 83
Chris@281 84 Handle dataRef;
Chris@281 85 OSType dataRefType;
Chris@281 86
Chris@282 87 // CFStringRef URLString = CFStringCreateWithCString
Chris@282 88 // (0, m_path.toLocal8Bit().data(), 0);
Chris@281 89
Chris@282 90
Chris@282 91 CFURLRef url = CFURLCreateFromFileSystemRepresentation
Chris@282 92 (kCFAllocatorDefault,
Chris@282 93 (const UInt8 *)path.toLocal8Bit().data(),
Chris@282 94 (CFIndex)path.length(),
Chris@282 95 false);
Chris@282 96
Chris@282 97
Chris@282 98 // m_d->err = QTNewDataReferenceFromURLCFString
Chris@282 99 m_d->err = QTNewDataReferenceFromCFURL
Chris@282 100 (url, 0, &dataRef, &dataRefType);
Chris@281 101
Chris@281 102 if (m_d->err) {
Chris@290 103 m_error = QString("Error creating data reference for QuickTime decoder: code %1").arg(m_d->err);
Chris@281 104 return;
Chris@281 105 }
Chris@281 106
Chris@281 107 short fileID = movieInDataForkResID;
Chris@281 108 short flags = 0;
Chris@281 109 m_d->err = NewMovieFromDataRef
Chris@281 110 (&m_d->movie, flags, &fileID, dataRef, dataRefType);
Chris@281 111
Chris@281 112 DisposeHandle(dataRef);
Chris@281 113 if (m_d->err) {
Chris@290 114 m_error = QString("Error creating new movie for QuickTime decoder: code %1").arg(m_d->err);
Chris@281 115 return;
Chris@281 116 }
Chris@281 117
Chris@281 118 Boolean isProtected = 0;
Chris@281 119 Track aTrack = GetMovieIndTrackType
Chris@281 120 (m_d->movie, 1, SoundMediaType,
Chris@281 121 movieTrackMediaType | movieTrackEnabledOnly);
Chris@281 122
Chris@281 123 if (aTrack) {
Chris@281 124 Media aMedia = GetTrackMedia(aTrack); // get the track media
Chris@281 125 if (aMedia) {
Chris@281 126 MediaHandler mh = GetMediaHandler(aMedia); // get the media handler we can query
Chris@281 127 if (mh) {
Chris@281 128 m_d->err = QTGetComponentProperty(mh,
Chris@281 129 kQTPropertyClass_DRM,
Chris@281 130 kQTDRMPropertyID_IsProtected,
Chris@281 131 sizeof(Boolean), &isProtected,nil);
Chris@281 132 } else {
Chris@281 133 m_d->err = 1;
Chris@281 134 }
Chris@281 135 } else {
Chris@281 136 m_d->err = 1;
Chris@281 137 }
Chris@281 138 } else {
Chris@281 139 m_d->err = 1;
Chris@281 140 }
Chris@281 141
Chris@281 142 if (m_d->err && m_d->err != kQTPropertyNotSupportedErr) {
Chris@290 143 m_error = QString("Error checking for DRM in QuickTime decoder: code %1").arg(m_d->err);
Chris@281 144 return;
Chris@281 145 } else if (!m_d->err && isProtected) {
Chris@290 146 m_error = QString("File is protected with DRM");
Chris@281 147 return;
Chris@281 148 } else if (m_d->err == kQTPropertyNotSupportedErr && !isProtected) {
Chris@281 149 std::cerr << "QuickTime: File is not protected with DRM" << std::endl;
Chris@281 150 }
Chris@281 151
Chris@281 152 if (m_d->movie) {
Chris@281 153 SetMovieActive(m_d->movie, TRUE);
Chris@281 154 m_d->err = GetMoviesError();
Chris@281 155 if (m_d->err) {
Chris@290 156 m_error = QString("Error in QuickTime decoder activation: code %1").arg(m_d->err);
Chris@281 157 return;
Chris@281 158 }
Chris@282 159 } else {
Chris@290 160 m_error = QString("Error in QuickTime decoder: Movie object not valid");
Chris@282 161 return;
Chris@281 162 }
Chris@281 163
Chris@281 164 m_d->err = MovieAudioExtractionBegin
Chris@281 165 (m_d->movie, 0, &m_d->extractionSessionRef);
Chris@281 166 if (m_d->err) {
Chris@290 167 m_error = QString("Error in QuickTime decoder extraction init: code %1").arg(m_d->err);
Chris@281 168 return;
Chris@281 169 }
Chris@281 170
Chris@281 171 m_d->err = MovieAudioExtractionGetProperty
Chris@281 172 (m_d->extractionSessionRef,
Chris@281 173 kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
Chris@281 174 sizeof(m_d->asbd),
Chris@281 175 &m_d->asbd,
Chris@281 176 nil);
Chris@281 177
Chris@281 178 if (m_d->err) {
Chris@290 179 m_error = QString("Error in QuickTime decoder property get: code %1").arg(m_d->err);
Chris@281 180 return;
Chris@281 181 }
Chris@281 182
Chris@281 183 m_channelCount = m_d->asbd.mChannelsPerFrame;
Chris@297 184 m_fileRate = m_d->asbd.mSampleRate;
Chris@281 185
Chris@297 186 std::cerr << "QuickTime: " << m_channelCount << " channels, " << m_fileRate << " kHz" << std::endl;
Chris@281 187
Chris@281 188 m_d->asbd.mFormatFlags =
Chris@281 189 kAudioFormatFlagIsFloat |
Chris@281 190 kAudioFormatFlagIsPacked |
Chris@281 191 kAudioFormatFlagsNativeEndian;
Chris@282 192 m_d->asbd.mBitsPerChannel = sizeof(double) * 8;
Chris@282 193 m_d->asbd.mBytesPerFrame = sizeof(double) * m_d->asbd.mChannelsPerFrame;
Chris@281 194 m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame;
Chris@281 195
Chris@281 196 m_d->err = MovieAudioExtractionSetProperty
Chris@281 197 (m_d->extractionSessionRef,
Chris@281 198 kQTPropertyClass_MovieAudioExtraction_Audio,
Chris@281 199 kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
Chris@281 200 sizeof(m_d->asbd),
Chris@281 201 &m_d->asbd);
Chris@281 202
Chris@281 203 if (m_d->err) {
Chris@290 204 m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err);
Chris@281 205 return;
Chris@281 206 }
Chris@281 207
Chris@281 208 m_d->buffer.mNumberBuffers = 1;
Chris@281 209 m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount;
Chris@281 210 m_d->buffer.mBuffers[0].mDataByteSize =
Chris@282 211 sizeof(double) * m_channelCount * m_d->blockSize;
Chris@282 212 m_d->data = new double[m_channelCount * m_d->blockSize];
Chris@282 213 m_d->buffer.mBuffers[0].mData = m_d->data;
Chris@281 214
Chris@281 215 initialiseDecodeCache();
Chris@281 216
Chris@281 217 if (decodeMode == DecodeAtOnce) {
Chris@281 218
Chris@281 219 m_progress = new QProgressDialog
Chris@290 220 (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()),
Chris@281 221 QObject::tr("Stop"), 0, 100);
Chris@281 222 m_progress->hide();
Chris@281 223
Chris@281 224 while (1) {
Chris@281 225
Chris@282 226 UInt32 framesRead = m_d->blockSize;
Chris@281 227 UInt32 extractionFlags = 0;
Chris@281 228 m_d->err = MovieAudioExtractionFillBuffer
Chris@281 229 (m_d->extractionSessionRef, &framesRead, &m_d->buffer,
Chris@281 230 &extractionFlags);
Chris@281 231 if (m_d->err) {
Chris@290 232 m_error = QString("Error in QuickTime decoding: code %1")
Chris@290 233 .arg(m_d->err);
Chris@281 234 break;
Chris@281 235 }
Chris@281 236
Chris@282 237 // std::cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << std::endl;
Chris@282 238
Chris@281 239 // QuickTime buffers are interleaved unless specified otherwise
Chris@297 240 addSamplesToDecodeCache(m_d->data, framesRead);
Chris@281 241
Chris@281 242 if (framesRead < m_d->blockSize) break;
Chris@281 243 }
Chris@281 244
Chris@281 245 finishDecodeCache();
Chris@281 246
Chris@281 247 m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef);
Chris@281 248 if (m_d->err) {
Chris@290 249 m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err);
Chris@281 250 }
Chris@281 251
Chris@281 252 m_completion = 100;
Chris@281 253
Chris@281 254 delete m_progress;
Chris@281 255 m_progress = 0;
Chris@281 256
Chris@281 257 } else {
Chris@281 258 if (m_channelCount > 0) {
Chris@281 259 m_decodeThread = new DecodeThread(this);
Chris@281 260 m_decodeThread->start();
Chris@281 261 }
Chris@281 262 }
Chris@282 263
Chris@290 264 std::cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error.toStdString() << "\"" << std::endl;
Chris@281 265 }
Chris@281 266
Chris@281 267 QuickTimeFileReader::~QuickTimeFileReader()
Chris@281 268 {
Chris@282 269 std::cerr << "QuickTimeFileReader::~QuickTimeFileReader" << std::endl;
Chris@282 270
Chris@281 271 if (m_decodeThread) {
Chris@281 272 m_cancelled = true;
Chris@281 273 m_decodeThread->wait();
Chris@281 274 delete m_decodeThread;
Chris@281 275 }
Chris@281 276
Chris@282 277 SetMovieActive(m_d->movie, FALSE);
Chris@281 278 DisposeMovie(m_d->movie);
Chris@281 279
Chris@281 280 delete[] m_d->data;
Chris@281 281 delete m_d;
Chris@281 282 }
Chris@281 283
Chris@281 284 void
Chris@281 285 QuickTimeFileReader::DecodeThread::run()
Chris@281 286 {
Chris@297 287 if (m_reader->m_cacheMode == CacheInTemporaryFile) {
Chris@297 288 m_reader->m_completion = 1;
Chris@297 289 m_reader->startSerialised("QuickTimeFileReader::Decode");
Chris@297 290 }
Chris@297 291
Chris@281 292 while (1) {
Chris@281 293
Chris@282 294 UInt32 framesRead = m_reader->m_d->blockSize;
Chris@281 295 UInt32 extractionFlags = 0;
Chris@281 296 m_reader->m_d->err = MovieAudioExtractionFillBuffer
Chris@281 297 (m_reader->m_d->extractionSessionRef, &framesRead,
Chris@281 298 &m_reader->m_d->buffer, &extractionFlags);
Chris@281 299 if (m_reader->m_d->err) {
Chris@290 300 m_reader->m_error = QString("Error in QuickTime decoding: code %1")
Chris@290 301 .arg(m_reader->m_d->err);
Chris@281 302 break;
Chris@281 303 }
Chris@282 304
Chris@281 305 // QuickTime buffers are interleaved unless specified otherwise
Chris@297 306 addSamplesToDecodeCache(m_d->data, framesRead);
Chris@281 307
Chris@281 308 if (framesRead < m_reader->m_d->blockSize) break;
Chris@281 309 }
Chris@281 310
Chris@281 311 m_reader->finishDecodeCache();
Chris@281 312
Chris@281 313 m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef);
Chris@281 314 if (m_reader->m_d->err) {
Chris@290 315 m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err);
Chris@281 316 }
Chris@281 317
Chris@281 318 m_reader->m_completion = 100;
Chris@297 319 m_reader->endSerialised();
Chris@281 320 }
Chris@281 321
Chris@281 322 void
Chris@290 323 QuickTimeFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@281 324 {
Chris@281 325 extensions.insert("aiff");
Chris@291 326 extensions.insert("aif");
Chris@281 327 extensions.insert("au");
Chris@281 328 extensions.insert("avi");
Chris@281 329 extensions.insert("m4a");
Chris@281 330 extensions.insert("m4b");
Chris@281 331 extensions.insert("m4p");
Chris@281 332 extensions.insert("m4v");
Chris@281 333 extensions.insert("mov");
Chris@281 334 extensions.insert("mp3");
Chris@281 335 extensions.insert("mp4");
Chris@281 336 extensions.insert("wav");
Chris@281 337 }
Chris@281 338
Chris@281 339 #endif
Chris@297 340