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