annotate data/fileio/QuickTimeFileReader.cpp @ 294:2c1e57ad86e7

* Show colour swatch next to layer name in pane (if available) * Fix for incorrect layer name prefix handling (was making some layers appear to have the same model name in cases where the model names differed by the final character only)
author Chris Cannam
date Wed, 05 Sep 2007 15:17:15 +0000
parents 42e6c8360df1
children c022976d18e8
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@281 53 CacheMode mode) :
Chris@281 54 CodedAudioFileReader(mode),
Chris@281 55 m_path(path),
Chris@281 56 m_d(new D),
Chris@281 57 m_progress(0),
Chris@281 58 m_cancelled(false),
Chris@281 59 m_completion(0),
Chris@281 60 m_decodeThread(0)
Chris@281 61 {
Chris@281 62 m_frameCount = 0;
Chris@281 63 m_channelCount = 0;
Chris@281 64 m_sampleRate = 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@281 184 m_sampleRate = m_d->asbd.mSampleRate;
Chris@281 185
Chris@281 186 std::cerr << "QuickTime: " << m_channelCount << " channels, " << m_sampleRate << " 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@282 239 m_frameCount += framesRead;
Chris@282 240
Chris@281 241 // QuickTime buffers are interleaved unless specified otherwise
Chris@281 242 for (UInt32 i = 0; i < framesRead * m_channelCount; ++i) {
Chris@281 243 addSampleToDecodeCache(m_d->data[i]);
Chris@281 244 }
Chris@281 245
Chris@281 246 if (framesRead < m_d->blockSize) break;
Chris@281 247 }
Chris@281 248
Chris@281 249 finishDecodeCache();
Chris@281 250
Chris@281 251 m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef);
Chris@281 252 if (m_d->err) {
Chris@290 253 m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err);
Chris@281 254 }
Chris@281 255
Chris@281 256 m_completion = 100;
Chris@281 257
Chris@281 258 delete m_progress;
Chris@281 259 m_progress = 0;
Chris@281 260
Chris@281 261 } else {
Chris@281 262 if (m_channelCount > 0) {
Chris@281 263 m_decodeThread = new DecodeThread(this);
Chris@281 264 m_decodeThread->start();
Chris@281 265 }
Chris@281 266 }
Chris@282 267
Chris@290 268 std::cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error.toStdString() << "\"" << std::endl;
Chris@281 269 }
Chris@281 270
Chris@281 271 QuickTimeFileReader::~QuickTimeFileReader()
Chris@281 272 {
Chris@282 273 std::cerr << "QuickTimeFileReader::~QuickTimeFileReader" << std::endl;
Chris@282 274
Chris@281 275 if (m_decodeThread) {
Chris@281 276 m_cancelled = true;
Chris@281 277 m_decodeThread->wait();
Chris@281 278 delete m_decodeThread;
Chris@281 279 }
Chris@281 280
Chris@282 281 SetMovieActive(m_d->movie, FALSE);
Chris@281 282 DisposeMovie(m_d->movie);
Chris@281 283
Chris@281 284 delete[] m_d->data;
Chris@281 285 delete m_d;
Chris@281 286 }
Chris@281 287
Chris@281 288 void
Chris@281 289 QuickTimeFileReader::DecodeThread::run()
Chris@281 290 {
Chris@281 291 while (1) {
Chris@281 292
Chris@282 293 UInt32 framesRead = m_reader->m_d->blockSize;
Chris@281 294 UInt32 extractionFlags = 0;
Chris@281 295 m_reader->m_d->err = MovieAudioExtractionFillBuffer
Chris@281 296 (m_reader->m_d->extractionSessionRef, &framesRead,
Chris@281 297 &m_reader->m_d->buffer, &extractionFlags);
Chris@281 298 if (m_reader->m_d->err) {
Chris@290 299 m_reader->m_error = QString("Error in QuickTime decoding: code %1")
Chris@290 300 .arg(m_reader->m_d->err);
Chris@281 301 break;
Chris@281 302 }
Chris@282 303
Chris@282 304 m_reader->m_frameCount += framesRead;
Chris@282 305
Chris@281 306 // QuickTime buffers are interleaved unless specified otherwise
Chris@282 307 for (UInt32 i = 0; i < framesRead * m_reader->m_channelCount; ++i) {
Chris@282 308 m_reader->addSampleToDecodeCache(m_reader->m_d->data[i]);
Chris@281 309 }
Chris@281 310
Chris@281 311 if (framesRead < m_reader->m_d->blockSize) break;
Chris@281 312 }
Chris@281 313
Chris@281 314 m_reader->finishDecodeCache();
Chris@281 315
Chris@281 316 m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef);
Chris@281 317 if (m_reader->m_d->err) {
Chris@290 318 m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err);
Chris@281 319 }
Chris@281 320
Chris@281 321 m_reader->m_completion = 100;
Chris@281 322 }
Chris@281 323
Chris@281 324 void
Chris@290 325 QuickTimeFileReader::getSupportedExtensions(std::set<QString> &extensions)
Chris@281 326 {
Chris@281 327 extensions.insert("aiff");
Chris@291 328 extensions.insert("aif");
Chris@281 329 extensions.insert("au");
Chris@281 330 extensions.insert("avi");
Chris@281 331 extensions.insert("m4a");
Chris@281 332 extensions.insert("m4b");
Chris@281 333 extensions.insert("m4p");
Chris@281 334 extensions.insert("m4v");
Chris@281 335 extensions.insert("mov");
Chris@281 336 extensions.insert("mp3");
Chris@281 337 extensions.insert("mp4");
Chris@281 338 extensions.insert("wav");
Chris@281 339 }
Chris@281 340
Chris@281 341 #endif