| 1 |
/* This file is part of the KDE project. |
| 2 |
|
| 3 |
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
| 4 |
|
| 5 |
This library is free software: you can redistribute it and/or modify |
| 6 |
it under the terms of the GNU Lesser General Public License as published by |
| 7 |
the Free Software Foundation, either version 2.1 or 3 of the License. |
| 8 |
|
| 9 |
This library is distributed in the hope that it will be useful, |
| 10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 |
GNU Lesser General Public License for more details. |
| 13 |
|
| 14 |
You should have received a copy of the GNU Lesser General Public License |
| 15 |
along with this library. If not, see <http://www.gnu.org/licenses/>. |
| 16 |
*/ |
| 17 |
|
| 18 |
#import <QTKit/QTMovie.h> |
| 19 |
|
| 20 |
#include "quicktimeaudioplayer.h" |
| 21 |
#include "quicktimevideoplayer.h" |
| 22 |
#include "audiograph.h" |
| 23 |
#include "medianodeevent.h" |
| 24 |
#include "medianode.h" |
| 25 |
|
| 26 |
QT_BEGIN_NAMESPACE |
| 27 |
|
| 28 |
namespace Phonon |
| 29 |
{ |
| 30 |
namespace QT7 |
| 31 |
{ |
| 32 |
|
| 33 |
QuickTimeAudioPlayer::QuickTimeAudioPlayer() : AudioNode(0, 1) |
| 34 |
{ |
| 35 |
m_state = NoMedia; |
| 36 |
m_videoPlayer = 0; |
| 37 |
m_audioChannelLayout = 0; |
| 38 |
m_sliceList = 0; |
| 39 |
m_sliceCount = 30; |
| 40 |
m_maxExtractionPacketCount = 4096; |
| 41 |
m_audioExtractionComplete = false; |
| 42 |
m_audioEnabled = true; |
| 43 |
m_samplesRemaining = -1; |
| 44 |
m_startTime = 0; |
| 45 |
m_sampleTimeStamp = 0; |
| 46 |
m_audioUnitIsReset = true; |
| 47 |
|
| 48 |
#ifdef QUICKTIME_C_API_AVAILABLE |
| 49 |
m_audioExtractionRef = 0; |
| 50 |
#endif |
| 51 |
} |
| 52 |
|
| 53 |
QuickTimeAudioPlayer::~QuickTimeAudioPlayer() |
| 54 |
{ |
| 55 |
unsetVideoPlayer(); |
| 56 |
} |
| 57 |
|
| 58 |
void QuickTimeAudioPlayer::unsetVideoPlayer() |
| 59 |
{ |
| 60 |
if (m_audioUnit){ |
| 61 |
OSStatus err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); |
| 62 |
BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit when unsetting movie", FATAL_ERROR) |
| 63 |
} |
| 64 |
|
| 65 |
#ifdef QUICKTIME_C_API_AVAILABLE |
| 66 |
if (m_audioExtractionRef && m_videoPlayer && m_videoPlayer->hasMovie()) |
| 67 |
MovieAudioExtractionEnd(m_audioExtractionRef); |
| 68 |
m_audioExtractionRef = 0; |
| 69 |
#endif |
| 70 |
|
| 71 |
if (m_audioChannelLayout){ |
| 72 |
free(m_audioChannelLayout); |
| 73 |
m_audioChannelLayout = 0; |
| 74 |
} |
| 75 |
|
| 76 |
if (m_sliceList){ |
| 77 |
for (int i=0; i<m_sliceCount; i++) |
| 78 |
free(m_sliceList[i].mBufferList); |
| 79 |
free(m_sliceList); |
| 80 |
m_sliceList = 0; |
| 81 |
} |
| 82 |
|
| 83 |
m_videoPlayer = 0; |
| 84 |
m_audioExtractionComplete = false; |
| 85 |
m_samplesRemaining = -1; |
| 86 |
m_sampleTimeStamp = 0; |
| 87 |
m_state = NoMedia; |
| 88 |
} |
| 89 |
|
| 90 |
void QuickTimeAudioPlayer::enableAudio(bool enable) |
| 91 |
{ |
| 92 |
// Remember to seek after enabling audio. |
| 93 |
if (enable == m_audioEnabled) |
| 94 |
return; |
| 95 |
|
| 96 |
m_audioEnabled = enable; |
| 97 |
if (!enable) |
| 98 |
flush(); |
| 99 |
} |
| 100 |
|
| 101 |
bool QuickTimeAudioPlayer::audioEnabled() |
| 102 |
{ |
| 103 |
return m_audioEnabled; |
| 104 |
} |
| 105 |
|
| 106 |
void QuickTimeAudioPlayer::setVideoPlayer(QuickTimeVideoPlayer *videoPlayer) |
| 107 |
{ |
| 108 |
unsetVideoPlayer(); |
| 109 |
if (videoPlayer && videoPlayer->hasMovie()){ |
| 110 |
m_videoPlayer = videoPlayer; |
| 111 |
initSoundExtraction(); |
| 112 |
allocateSoundSlices(); |
| 113 |
m_state = Paused; |
| 114 |
seek(0); |
| 115 |
} |
| 116 |
} |
| 117 |
|
| 118 |
QuickTimeVideoPlayer *QuickTimeAudioPlayer::videoPlayer() |
| 119 |
{ |
| 120 |
return m_videoPlayer; |
| 121 |
} |
| 122 |
|
| 123 |
void QuickTimeAudioPlayer::scheduleAudioToGraph() |
| 124 |
{ |
| 125 |
if (!m_videoPlayer || !m_audioEnabled || m_audioExtractionComplete || m_state != Playing) |
| 126 |
return; |
| 127 |
|
| 128 |
// Schedule audio slices, and detect if everything went OK. |
| 129 |
// If not, flag the need for another audio system, but let |
| 130 |
// the end app know about it: |
| 131 |
gClearError(); |
| 132 |
scheduleSoundSlices(); |
| 133 |
if (gGetErrorType() != NO_ERROR){ |
| 134 |
gClearError(); |
| 135 |
if (m_audioGraph) |
| 136 |
m_audioGraph->setStatusCannotPlay(); |
| 137 |
} |
| 138 |
} |
| 139 |
|
| 140 |
void QuickTimeAudioPlayer::flush() |
| 141 |
{ |
| 142 |
// Empty scheduled audio data, so playback |
| 143 |
// will stop. Call seek to refill data again. |
| 144 |
if (m_audioUnit){ |
| 145 |
m_startTime = currentTime(); |
| 146 |
OSStatus err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); |
| 147 |
BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit on pause", FATAL_ERROR) |
| 148 |
m_audioUnitIsReset = true; |
| 149 |
} |
| 150 |
} |
| 151 |
|
| 152 |
void QuickTimeAudioPlayer::pause() |
| 153 |
{ |
| 154 |
m_state = Paused; |
| 155 |
flush(); |
| 156 |
} |
| 157 |
|
| 158 |
void QuickTimeAudioPlayer::play() |
| 159 |
{ |
| 160 |
m_state = Playing; |
| 161 |
if (!m_audioEnabled) |
| 162 |
return; |
| 163 |
if (m_audioUnitIsReset) |
| 164 |
seek(m_startTime); |
| 165 |
else |
| 166 |
scheduleAudioToGraph(); |
| 167 |
} |
| 168 |
|
| 169 |
bool QuickTimeAudioPlayer::isPlaying() |
| 170 |
{ |
| 171 |
return m_videoPlayer && m_state == Playing; |
| 172 |
} |
| 173 |
|
| 174 |
void QuickTimeAudioPlayer::seek(quint64 milliseconds) |
| 175 |
{ |
| 176 |
if (!m_videoPlayer || !m_videoPlayer->hasMovie()) |
| 177 |
return; |
| 178 |
if (milliseconds > m_videoPlayer->duration()) |
| 179 |
milliseconds = m_videoPlayer->duration(); |
| 180 |
if (!m_audioUnitIsReset && milliseconds == currentTime()) |
| 181 |
return; |
| 182 |
|
| 183 |
m_startTime = milliseconds; |
| 184 |
|
| 185 |
// Since the graph may be running (advancing time), there is |
| 186 |
// no point in seeking if were not going to play immidiatly: |
| 187 |
if (m_state != Playing) |
| 188 |
return; |
| 189 |
if (!m_audioUnit) |
| 190 |
return; |
| 191 |
if (!m_audioEnabled || !m_videoPlayer->isSeekable()) |
| 192 |
return; |
| 193 |
|
| 194 |
// Reset (and stop playing): |
| 195 |
OSStatus err; |
| 196 |
if (!m_audioUnitIsReset){ |
| 197 |
err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); |
| 198 |
BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit before seek", FATAL_ERROR) |
| 199 |
} |
| 200 |
m_sampleTimeStamp = 0; |
| 201 |
for (int i = 0; i < m_sliceCount; i++) |
| 202 |
m_sliceList[i].mFlags = kScheduledAudioSliceFlag_Complete; |
| 203 |
|
| 204 |
// Start to play again immidiatly: |
| 205 |
AudioTimeStamp timeStamp; |
| 206 |
memset(&timeStamp, 0, sizeof(timeStamp)); |
| 207 |
timeStamp.mFlags = kAudioTimeStampSampleTimeValid; |
| 208 |
timeStamp.mSampleTime = -1; |
| 209 |
err = AudioUnitSetProperty(m_audioUnit, |
| 210 |
kAudioUnitProperty_ScheduleStartTimeStamp, kAudioUnitScope_Global, |
| 211 |
0, &timeStamp, sizeof(timeStamp)); |
| 212 |
BACKEND_ASSERT2(err == noErr, "Could not set schedule start time stamp on audio player unit", FATAL_ERROR) |
| 213 |
|
| 214 |
// Seek back to 'now' in the movie: |
| 215 |
TimeRecord timeRec; |
| 216 |
timeRec.scale = m_videoPlayer->timeScale(); |
| 217 |
timeRec.base = 0; |
| 218 |
timeRec.value.hi = 0; |
| 219 |
timeRec.value.lo = (milliseconds / 1000.0f) * timeRec.scale; |
| 220 |
|
| 221 |
#ifdef QUICKTIME_C_API_AVAILABLE |
| 222 |
err = MovieAudioExtractionSetProperty(m_audioExtractionRef, |
| 223 |
kQTPropertyClass_MovieAudioExtraction_Movie, |
| 224 |
kQTMovieAudioExtractionMoviePropertyID_CurrentTime, |
| 225 |
sizeof(TimeRecord), &timeRec); |
| 226 |
BACKEND_ASSERT2(err == noErr, "Could not set current time on audio player unit", FATAL_ERROR) |
| 227 |
#endif |
| 228 |
|
| 229 |
float durationLeftSec = float(m_videoPlayer->duration() - milliseconds) / 1000.0f; |
| 230 |
m_samplesRemaining = (durationLeftSec > 0) ? (durationLeftSec * m_audioStreamDescription.mSampleRate) : -1; |
| 231 |
m_audioExtractionComplete = false; |
| 232 |
m_audioUnitIsReset = false; |
| 233 |
scheduleAudioToGraph(); |
| 234 |
|
| 235 |
} |
| 236 |
|
| 237 |
quint64 QuickTimeAudioPlayer::currentTime() |
| 238 |
{ |
| 239 |
if (!m_audioUnit){ |
| 240 |
if (m_videoPlayer) |
| 241 |
return m_videoPlayer->currentTime(); |
| 242 |
else |
| 243 |
return m_startTime; |
| 244 |
} |
| 245 |
|
| 246 |
Float64 currentUnitTime = getTimeInSamples(kAudioUnitProperty_CurrentPlayTime); |
| 247 |
if (currentUnitTime == -1) |
| 248 |
currentUnitTime = 0; |
| 249 |
|
| 250 |
quint64 cTime = quint64(m_startTime + |
| 251 |
float(currentUnitTime / float(m_audioStreamDescription.mSampleRate)) * 1000.0f); |
| 252 |
return (m_videoPlayer && cTime > m_videoPlayer->duration()) ? m_videoPlayer->duration() : cTime; |
| 253 |
} |
| 254 |
|
| 255 |
QString QuickTimeAudioPlayer::currentTimeString() |
| 256 |
{ |
| 257 |
return QuickTimeVideoPlayer::timeToString(currentTime()); |
| 258 |
} |
| 259 |
|
| 260 |
bool QuickTimeAudioPlayer::hasAudio() |
| 261 |
{ |
| 262 |
if (!m_videoPlayer) |
| 263 |
return false; |
| 264 |
|
| 265 |
return m_videoPlayer->hasAudio(); |
| 266 |
} |
| 267 |
|
| 268 |
bool QuickTimeAudioPlayer::soundPlayerIsAwailable() |
| 269 |
{ |
| 270 |
QuickTimeAudioPlayer player; |
| 271 |
ComponentDescription d = player.getAudioNodeDescription(); |
| 272 |
return FindNextComponent(0, &d); |
| 273 |
} |
| 274 |
|
| 275 |
ComponentDescription QuickTimeAudioPlayer::getAudioNodeDescription() const |
| 276 |
{ |
| 277 |
ComponentDescription description; |
| 278 |
description.componentType = kAudioUnitType_Generator; |
| 279 |
description.componentSubType = kAudioUnitSubType_ScheduledSoundPlayer; |
| 280 |
description.componentManufacturer = kAudioUnitManufacturer_Apple; |
| 281 |
description.componentFlags = 0; |
| 282 |
description.componentFlagsMask = 0; |
| 283 |
return description; |
| 284 |
} |
| 285 |
|
| 286 |
void QuickTimeAudioPlayer::initializeAudioUnit() |
| 287 |
{ |
| 288 |
} |
| 289 |
|
| 290 |
bool QuickTimeAudioPlayer::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) |
| 291 |
{ |
| 292 |
if (!m_videoPlayer){ |
| 293 |
if (side == Source) |
| 294 |
DEBUG_AUDIO_STREAM("QuickTimeAudioPlayer" << int(this) << "is source, but has no movie to use for stream spec fill.") |
| 295 |
return true; |
| 296 |
} |
| 297 |
|
| 298 |
if (side == Source){ |
| 299 |
DEBUG_AUDIO_STREAM("QuickTimeAudioPlayer" << int(this) << "is source, and fills in stream spec from movie.") |
| 300 |
connection->m_sourceStreamDescription = m_audioStreamDescription; |
| 301 |
connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_audioChannelLayoutSize); |
| 302 |
memcpy(connection->m_sourceChannelLayout, m_audioChannelLayout, m_audioChannelLayoutSize); |
| 303 |
connection->m_sourceChannelLayoutSize = m_audioChannelLayoutSize; |
| 304 |
connection->m_hasSourceSpecification = true; |
| 305 |
} |
| 306 |
return true; |
| 307 |
} |
| 308 |
|
| 309 |
long QuickTimeAudioPlayer::regularTaskFrequency(){ |
| 310 |
if (!m_audioEnabled || !m_audioUnit || (m_audioGraph && m_audioGraph->graphCannotPlay())) |
| 311 |
return INT_MAX; |
| 312 |
|
| 313 |
// Calculate how much audio in |
| 314 |
// milliseconds our slices can hold: |
| 315 |
int packetNeedPerSecond = m_audioStreamDescription.mSampleRate / m_maxExtractionPacketCount; |
| 316 |
long bufferTimeLengthSec = float(m_sliceCount) / float(packetNeedPerSecond); |
| 317 |
// Make sure we also get some time to fill the |
| 318 |
// buffer, so divide the time by two: |
| 319 |
return (bufferTimeLengthSec * (1000 / 2)); |
| 320 |
} |
| 321 |
|
| 322 |
void QuickTimeAudioPlayer::initSoundExtraction() |
| 323 |
{ |
| 324 |
#ifdef QUICKTIME_C_API_AVAILABLE |
| 325 |
|
| 326 |
// Initilize the extraction: |
| 327 |
OSStatus err = noErr; |
| 328 |
err = MovieAudioExtractionBegin([m_videoPlayer->qtMovie() quickTimeMovie], 0, &m_audioExtractionRef); |
| 329 |
BACKEND_ASSERT2(err == noErr, "Could not start audio extraction on audio player unit", FATAL_ERROR) |
| 330 |
m_discrete = false; |
| 331 |
#if 0 |
| 332 |
// Extract all channels as descrete: |
| 333 |
err = MovieAudioExtractionSetProperty(audioExtractionRef, |
| 334 |
kQTPropertyClass_MovieAudioExtraction_Movie, |
| 335 |
kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, |
| 336 |
sizeof (discrete), |
| 337 |
&discrete); |
| 338 |
BACKEND_ASSERT2(err == noErr, "Could not set channels discrete on audio player unit", FATAL_ERROR) |
| 339 |
#endif |
| 340 |
|
| 341 |
// Get the size of the audio channel layout (may include offset): |
| 342 |
err = MovieAudioExtractionGetPropertyInfo(m_audioExtractionRef, |
| 343 |
kQTPropertyClass_MovieAudioExtraction_Audio, |
| 344 |
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, |
| 345 |
0, &m_audioChannelLayoutSize, 0); |
| 346 |
BACKEND_ASSERT2(err == noErr, "Could not get channel layout size from audio extraction", FATAL_ERROR) |
| 347 |
|
| 348 |
// Allocate memory for the layout |
| 349 |
m_audioChannelLayout = (AudioChannelLayout *) calloc(1, m_audioChannelLayoutSize); |
| 350 |
BACKEND_ASSERT2(m_audioChannelLayout, "Could not allocate memory for channel layout on audio player unit", FATAL_ERROR) |
| 351 |
|
| 352 |
// Get the layout: |
| 353 |
err = MovieAudioExtractionGetProperty(m_audioExtractionRef, |
| 354 |
kQTPropertyClass_MovieAudioExtraction_Audio, |
| 355 |
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, |
| 356 |
m_audioChannelLayoutSize, m_audioChannelLayout, 0); |
| 357 |
BACKEND_ASSERT2(err == noErr, "Could not get channel layout from audio extraction", FATAL_ERROR) |
| 358 |
|
| 359 |
// Get audio stream description: |
| 360 |
err = MovieAudioExtractionGetProperty(m_audioExtractionRef, |
| 361 |
kQTPropertyClass_MovieAudioExtraction_Audio, |
| 362 |
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, |
| 363 |
sizeof(m_audioStreamDescription), &m_audioStreamDescription, 0); |
| 364 |
BACKEND_ASSERT2(err == noErr, "Could not get audio stream description from audio extraction", FATAL_ERROR) |
| 365 |
|
| 366 |
#endif // QUICKTIME_C_API_AVAILABLE |
| 367 |
} |
| 368 |
|
| 369 |
void QuickTimeAudioPlayer::allocateSoundSlices() |
| 370 |
{ |
| 371 |
#ifdef QUICKTIME_C_API_AVAILABLE |
| 372 |
|
| 373 |
// m_sliceList will contain a specified number of ScheduledAudioSlice-s that each can |
| 374 |
// carry audio from extraction, and be scheduled for playback at an audio unit. |
| 375 |
// Each ScheduledAudioSlice will contain several audio buffers, one for each sound channel. |
| 376 |
// Each buffer will carry (at most) a specified number of sound packets, and each packet can |
| 377 |
// contain one or more frames. |
| 378 |
|
| 379 |
// Create a list of ScheduledAudioSlices: |
| 380 |
m_sliceList = (ScheduledAudioSlice *) calloc(m_sliceCount, sizeof(ScheduledAudioSlice)); |
| 381 |
BACKEND_ASSERT2(m_sliceList, "Could not allocate memory for audio slices", FATAL_ERROR) |
| 382 |
bzero(m_sliceList, m_sliceCount * sizeof(ScheduledAudioSlice)); |
| 383 |
|
| 384 |
// Calculate the size of the different structures needed: |
| 385 |
int packetsBufferSize = m_maxExtractionPacketCount * m_audioStreamDescription.mBytesPerPacket; |
| 386 |
int channels = m_audioStreamDescription.mChannelsPerFrame; |
| 387 |
int audioBufferListSize = int(sizeof(AudioBufferList) + (channels-1) * sizeof(AudioBuffer)); |
| 388 |
int mallocSize = audioBufferListSize + (packetsBufferSize * m_audioStreamDescription.mChannelsPerFrame); |
| 389 |
|
| 390 |
// Round off to Altivec sizes: |
| 391 |
packetsBufferSize = int(((packetsBufferSize + 15) / 16) * 16); |
| 392 |
audioBufferListSize = int(((audioBufferListSize + 15) / 16) * 16); |
| 393 |
|
| 394 |
for (int sliceIndex = 0; sliceIndex < m_sliceCount; ++sliceIndex){ |
| 395 |
// Create the memory chunk for this audio slice: |
| 396 |
AudioBufferList *audioBufferList = (AudioBufferList*) calloc(1, mallocSize); |
| 397 |
BACKEND_ASSERT2(audioBufferList, "Could not allocate memory for audio buffer list", FATAL_ERROR) |
| 398 |
|
| 399 |
// The AudioBufferList contains an AudioBuffer for each channel in the audio stream: |
| 400 |
audioBufferList->mNumberBuffers = m_audioStreamDescription.mChannelsPerFrame; |
| 401 |
for (uint i = 0; i < audioBufferList->mNumberBuffers; ++i){ |
| 402 |
audioBufferList->mBuffers[i].mNumberChannels = 1; |
| 403 |
audioBufferList->mBuffers[i].mData = (char *) audioBufferList + audioBufferListSize + (i * packetsBufferSize); |
| 404 |
audioBufferList->mBuffers[i].mDataByteSize = packetsBufferSize; |
| 405 |
} |
| 406 |
|
| 407 |
m_sliceList[sliceIndex].mBufferList = audioBufferList; |
| 408 |
m_sliceList[sliceIndex].mNumberFrames = m_maxExtractionPacketCount; |
| 409 |
m_sliceList[sliceIndex].mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; |
| 410 |
m_sliceList[sliceIndex].mCompletionProcUserData = 0; |
| 411 |
m_sliceList[sliceIndex].mCompletionProc = 0; |
| 412 |
m_sliceList[sliceIndex].mFlags = kScheduledAudioSliceFlag_Complete; |
| 413 |
m_sliceList[sliceIndex].mReserved = 0; |
| 414 |
} |
| 415 |
|
| 416 |
#endif // QUICKTIME_C_API_AVAILABLE |
| 417 |
} |
| 418 |
|
| 419 |
void QuickTimeAudioPlayer::scheduleSoundSlices() |
| 420 |
{ |
| 421 |
#ifdef QUICKTIME_C_API_AVAILABLE |
| 422 |
|
| 423 |
PhononAutoReleasePool pool; |
| 424 |
// For each completed (or never used) slice, fill and schedule it. |
| 425 |
for (int sliceIndex = 0; sliceIndex < m_sliceCount; ++sliceIndex){ |
| 426 |
if (m_sliceList[sliceIndex].mFlags & kScheduledAudioSliceFlag_Complete){ |
| 427 |
if (m_samplesRemaining == 0) |
| 428 |
m_audioExtractionComplete = true; |
| 429 |
|
| 430 |
if (!m_audioExtractionComplete){ |
| 431 |
// Determine how many samples to read: |
| 432 |
int samplesCount = m_samplesRemaining; |
| 433 |
if ((samplesCount > m_maxExtractionPacketCount) || (samplesCount == -1)) |
| 434 |
samplesCount = m_maxExtractionPacketCount; |
| 435 |
m_sliceList[sliceIndex].mTimeStamp.mSampleTime = m_sampleTimeStamp; |
| 436 |
|
| 437 |
// Reset buffer sizes: |
| 438 |
int byteSize = samplesCount * m_audioStreamDescription.mBytesPerPacket; |
| 439 |
for (uint i = 0; i < m_sliceList[sliceIndex].mBufferList->mNumberBuffers; ++i) |
| 440 |
m_sliceList[sliceIndex].mBufferList->mBuffers[i].mDataByteSize = byteSize; |
| 441 |
|
| 442 |
// Do the extraction: |
| 443 |
UInt32 flags = 0; |
| 444 |
UInt32 samplesRead = samplesCount; |
| 445 |
OSStatus err = MovieAudioExtractionFillBuffer( |
| 446 |
m_audioExtractionRef, &samplesRead, m_sliceList[sliceIndex].mBufferList, &flags); |
| 447 |
BACKEND_ASSERT2(err == noErr, "Could not fill audio buffers from audio extraction", FATAL_ERROR) |
| 448 |
m_audioExtractionComplete = (flags & kQTMovieAudioExtractionComplete); |
| 449 |
|
| 450 |
// Play the slice: |
| 451 |
if (samplesRead != 0 && m_audioUnit != 0){ |
| 452 |
m_sliceList[sliceIndex].mNumberFrames = samplesRead; |
| 453 |
err = AudioUnitSetProperty(m_audioUnit, |
| 454 |
kAudioUnitProperty_ScheduleAudioSlice, kAudioUnitScope_Global, |
| 455 |
0, &m_sliceList[sliceIndex], sizeof(ScheduledAudioSlice)); |
| 456 |
BACKEND_ASSERT2(err == noErr, "Could not schedule audio buffers on audio unit", FATAL_ERROR) |
| 457 |
} else |
| 458 |
m_sliceList[sliceIndex].mFlags = kScheduledAudioSliceFlag_Complete; |
| 459 |
|
| 460 |
// Move the window: |
| 461 |
m_sampleTimeStamp += samplesRead; |
| 462 |
if (m_samplesRemaining != -1) |
| 463 |
m_samplesRemaining -= samplesRead; |
| 464 |
} |
| 465 |
} |
| 466 |
} |
| 467 |
|
| 468 |
#endif // QUICKTIME_C_API_AVAILABLE |
| 469 |
} |
| 470 |
|
| 471 |
void QuickTimeAudioPlayer::mediaNodeEvent(const MediaNodeEvent *event) |
| 472 |
{ |
| 473 |
switch (event->type()){ |
| 474 |
case MediaNodeEvent::AudioGraphAboutToBeDeleted: |
| 475 |
case MediaNodeEvent::AboutToRestartAudioStream: |
| 476 |
case MediaNodeEvent::StartConnectionChange: |
| 477 |
m_startTime = currentTime(); |
| 478 |
break; |
| 479 |
case MediaNodeEvent::AudioGraphInitialized: |
| 480 |
case MediaNodeEvent::RestartAudioStreamRequest: |
| 481 |
case MediaNodeEvent::EndConnectionChange: |
| 482 |
if (m_state == Playing) |
| 483 |
seek(m_startTime); |
| 484 |
break; |
| 485 |
default: |
| 486 |
break; |
| 487 |
} |
| 488 |
} |
| 489 |
|
| 490 |
}} |
| 491 |
|
| 492 |
QT_END_NAMESPACE |