Mercurial > hg > svcore
diff data/fileio/QuickTimeFileReader.cpp @ 281:9c447d664275
* Add QuickTime file reader class -- totally untested, shouldn't even compile
author | Chris Cannam |
---|---|
date | Mon, 06 Aug 2007 14:37:59 +0000 |
parents | |
children | e2fdcf9d35c5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/QuickTimeFileReader.cpp Mon Aug 06 14:37:59 2007 +0000 @@ -0,0 +1,328 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2007 Chris Cannam and QMUL. + + Based on QTAudioFile.cpp from SoundBite, copyright 2006 + Chris Sutton and Mark Levy. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifdef HAVE_QUICKTIME + +#include "QuickTimeFileReader.h" +#include "base/Profiler.h" +#include "system/System.h" + +#include <QApplication> +#include <QFileInfo> +#include <QProgressDialog> + +#ifdef WIN32 +#include <QTML.h> +#include <Movies.h> +#else +#include <QuickTime/QuickTime.h> +#endif + +class QuickTimeFileReader::D +{ +public: + D() : data(0), blockSize(1024) { } + + MovieAudioExtractionRef extractionSessionRef; + AudioBufferList buffer; + float *data; + OSErr err; + AudioStreamBasicDescription asbd; + Movie movie; + size_t blockSize; +}; + + +QuickTimeFileReader::QuickTimeFileReader(QString path, + DecodeMode decodeMode, + CacheMode mode) : + CodedAudioFileReader(mode), + m_path(path), + m_d(new D), + m_progress(0), + m_cancelled(false), + m_completion(0), + m_decodeThread(0) +{ + m_frameCount = 0; + m_channelCount = 0; + m_sampleRate = 0; + + Profiler profiler("QuickTimeFileReader::QuickTimeFileReader", true); + + long QTversion; + +#ifdef WIN32 + InitializeQTML(0); // FIXME should check QT version +#else + m_d->err = Gestalt(gestaltQuickTime,&QTversion); + if ((m_d->err != noErr) || (QTversion < 0x07000000)) { + m_error = QString("Failed to find compatible version of QuickTime (version 7 or above required)"); + return; + } +#endif + + EnterMovies(); + + Handle dataRef; + OSType dataRefType; + + CFStringRef URLString = CFStringCreateWithCString + (0, m_path.toLocal8Bit().data(), 0); + + m_d->err = QTNewDataReferenceFromURLCFString + (URLString, 0, &dataRef, &dataRefType); + + if (m_d->err) { + m_error = QString("Error creating data reference for QuickTime decoder: code %1").arg(m_d->err); + return; + } + + short fileID = movieInDataForkResID; + short flags = 0; + m_d->err = NewMovieFromDataRef + (&m_d->movie, flags, &fileID, dataRef, dataRefType); + + DisposeHandle(dataRef); + if (m_d->err) { + m_error = QString("Error creating new movie for QuickTime decoder: code %1").arg(m_d->err); + return; + } + + Boolean isProtected = 0; + Track aTrack = GetMovieIndTrackType + (m_d->movie, 1, SoundMediaType, + movieTrackMediaType | movieTrackEnabledOnly); + + if (aTrack) { + Media aMedia = GetTrackMedia(aTrack); // get the track media + if (aMedia) { + MediaHandler mh = GetMediaHandler(aMedia); // get the media handler we can query + if (mh) { + m_d->err = QTGetComponentProperty(mh, + kQTPropertyClass_DRM, + kQTDRMPropertyID_IsProtected, + sizeof(Boolean), &isProtected,nil); + } else { + m_d->err = 1; + } + } else { + m_d->err = 1; + } + } else { + m_d->err = 1; + } + + if (m_d->err && m_d->err != kQTPropertyNotSupportedErr) { + m_error = QString("Error checking for DRM in QuickTime decoder: code %1").arg(m_d->err); + return; + } else if (!m_d->err && isProtected) { + m_error = QString("File is protected with DRM"); + return; + } else if (m_d->err == kQTPropertyNotSupportedErr && !isProtected) { + std::cerr << "QuickTime: File is not protected with DRM" << std::endl; + } + + if (m_d->movie) { + SetMovieActive(m_d->movie, TRUE); + m_d->err = GetMoviesError(); + if (m_d->err) { + m_error = QString("Error in QuickTime decoder activation: code %1").arg(m_d->err); + return; + } + } + + m_d->err = MovieAudioExtractionBegin + (m_d->movie, 0, &m_d->extractionSessionRef); + if (m_d->err) { + m_error = QString("Error in QuickTime decoder extraction init: code %1").arg(m_d->err); + return; + } + /* + AudioChannelLayout monoLayout = {0}; + monoLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono; + m_d->err = MovieAudioExtractionSetProperty(m_d->extractionSessionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + sizeof(monoLayout), &monoLayout); + if (m_d->err) { + m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err); + return; + } + */ + + m_d->err = MovieAudioExtractionGetProperty + (m_d->extractionSessionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof(m_d->asbd), + &m_d->asbd, + nil); + + if (m_d->err) { + m_error = QString("Error in QuickTime decoder property get: code %1").arg(m_d->err); + return; + } + + m_channelCount = m_d->asbd.mChannelsPerFrame; + m_sampleRate = m_d->asbd.mSampleRate; + + std::cerr << "QuickTime: " << m_channelCount << " channels, " << m_sampleRate << " kHz" << std::endl; + + m_d->asbd.mFormatFlags = + kAudioFormatFlagIsFloat | + kAudioFormatFlagIsPacked | + kAudioFormatFlagsNativeEndian; + m_d->asbd.mBitsPerChannel = sizeof(float) * 8; + m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame; + m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame; + + m_d->err = MovieAudioExtractionSetProperty + (m_d->extractionSessionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof(m_d->asbd), + &m_d->asbd); + + if (m_d->err) { + m_error = QString("Error in QuickTime decoder ASBD set: code %1").arg(m_d->err); + return; + } + + m_d->buffer.mNumberBuffers = 1; + m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount; + m_d->buffer.mBuffers[0].mDataByteSize = + sizeof(float) * m_channelCount * m_d->blockSize; + m_d->data = new float[m_d->blockSize]; + m_d->buffer[0].mData = m_d->data; + + initialiseDecodeCache(); + + if (decodeMode == DecodeAtOnce) { + + m_progress = new QProgressDialog + (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + + while (1) { + + UInt32 framesRead = 0; + UInt32 extractionFlags = 0; + m_d->err = MovieAudioExtractionFillBuffer + (m_d->extractionSessionRef, &framesRead, &m_d->buffer, + &extractionFlags); + if (m_d->err) { + m_error = QString("Error in QuickTime decoding: code %1") + .arg(m_d->err); + break; + } + + // QuickTime buffers are interleaved unless specified otherwise + for (UInt32 i = 0; i < framesRead * m_channelCount; ++i) { + addSampleToDecodeCache(m_d->data[i]); + } + + if (framesRead < m_d->blockSize) break; + } + + finishDecodeCache(); + + m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef); + if (m_d->err) { + m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err); + } + + m_completion = 100; + + delete m_progress; + m_progress = 0; + + } else { + if (m_channelCount > 0) { + m_decodeThread = new DecodeThread(this); + m_decodeThread->start(); + } + } +} + +QuickTimeFileReader::~QuickTimeFileReader() +{ + if (m_decodeThread) { + m_cancelled = true; + m_decodeThread->wait(); + delete m_decodeThread; + } + + SetMovieActive(m_d->movie); + DisposeMovie(m_d->movie); + + delete[] m_d->data; + delete m_d; +} + +void +QuickTimeFileReader::DecodeThread::run() +{ + while (1) { + + UInt32 framesRead = 0; + UInt32 extractionFlags = 0; + m_reader->m_d->err = MovieAudioExtractionFillBuffer + (m_reader->m_d->extractionSessionRef, &framesRead, + &m_reader->m_d->buffer, &extractionFlags); + if (m_reader->m_d->err) { + m_error = QString("Error in QuickTime decoding: code %1") + .arg(m_reader->m_d->err); + break; + } + + // QuickTime buffers are interleaved unless specified otherwise + for (UInt32 i = 0; i < framesRead * m_channelCount; ++i) { + addSampleToDecodeCache(m_reader->m_d->data[i]); + } + + if (framesRead < m_reader->m_d->blockSize) break; + } + + m_reader->finishDecodeCache(); + + m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef); + if (m_reader->m_d->err) { + m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err); + } + + m_reader->m_completion = 100; +} + +void +QuickTimeFileReader::getSupportedExtensions(std::set<QString> &extensions) +{ + extensions.insert("aiff"); + extensions.insert("au"); + extensions.insert("avi"); + extensions.insert("m4a"); + extensions.insert("m4b"); + extensions.insert("m4p"); + extensions.insert("m4v"); + extensions.insert("mov"); + extensions.insert("mp3"); + extensions.insert("mp4"); + extensions.insert("wav"); +} + +#endif