📄 oggmultithread.cpp
字号:
}
// Start the streaming thread AO
if (iStreaming && !iStreamingThreadAO->IsActive() && (iSharedData.iBufferingMode != ENoBuffering))
{
// Reset the buffering thread priority
if ((iBufferingThreadPriority != EPriorityNormal) && (iBufferingThreadPriority != EPriorityAbsoluteForeground))
{
iBufferingThreadPriority = (iBufferingThreadPriority == EPriorityMore) ? EPriorityNormal : EPriorityAbsoluteForeground;
iSharedData.iBufferingThread.SetPriority(iBufferingThreadPriority);
}
iStreamingThreadAO->ResumeBuffering();
}
return ret;
}
void CStreamingThreadPlaybackEngine::Shutdown()
{
// We can't shutdown if we are streaming, so panic
if (iStreaming)
User::Panic(_L("STPE: Shutdown"), 0);
}
// Send the next audio buffer to the stream
// iBufNum holds the index of the next buffer
void CStreamingThreadPlaybackEngine::SendNextBuffer()
{
TRAPD(err, iStream->WriteL(*iSharedData.iOggPlayback.iBuffer[iBufNum]));
if (err != KErrNone)
{
// If an error occurs notify the UI thread
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
iSharedData.iUIThread.RequestComplete(status, err);
}
// Increment the number of stream buffers (we have one more now)
iStreamBuffers++;
// Move on to the next buffer
iBufNum++;
if (iBufNum == iSharedData.iMaxBuffers)
iBufNum = 0;
}
void CStreamingThreadPlaybackEngine::MaoscOpenComplete(TInt aErr)
{
TRACEF(COggLog::VA(_L("MaoscOpenComplete: %d"), aErr));
if (aErr != KErrNone)
{
// Notify the UI thread
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
iSharedData.iUIThread.RequestComplete(status, aErr);
return;
}
// Determine the maximum volume
iMaxVolume = iStream->MaxVolume();
// Set our audio priority
iStream->SetPriority(KAudioPriority, EMdaPriorityPreferenceTimeAndQuality);
// Notify the UI thread
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
iSharedData.iUIThread.RequestComplete(status, KErrNone);
}
// MaoscBufferCopied does all of the work to manage the buffering
void CStreamingThreadPlaybackEngine::MaoscBufferCopied(TInt aErr, const TDesC8& aBuffer)
{
// Error codes:
// KErrCancel -3 (not sure when this happens, but ignore for now)
// KErrUnderflow -10 (ignore this, do as if nothing has happend, and live happily ever after)
// KErrDied -13 (interrupted by higher priority)
// KErrInUse -14 (the sound device was stolen from us)
// KErrAbort -39 (stream was stopped before this buffer was copied)
// Debug trace, if error
if (aErr != KErrNone)
{ TRACEF(COggLog::VA(_L("MaoscBufferCopied:%d"), aErr)); }
// Decrement the stream buffers (we have one less now)
iStreamBuffers--;
// Adjust the number of bytes we have in buffers
iSharedData.iBufferBytesPlayed += aBuffer.Length();
// Increment the total number of buffers played
iSharedData.iNumBuffersPlayed++;
// Ignore most of the error codes
if ((aErr == KErrAbort) || (aErr == KErrInUse) || (aErr == KErrDied) || (aErr == KErrCancel))
return;
if ((aErr != KErrNone) && (aErr != KErrUnderflow))
{
// Notify the UI thread (unknown error)
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
if (status->Int() == KRequestPending)
iSharedData.iUIThread.RequestComplete(status, aErr);
return;
}
// If we have reached the last buffer, notify the UI thread
if (iSharedData.iLastBuffer == &aBuffer)
{
// Notify the UI thread that we have copied the last buffer to the stream
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
iSharedData.iUIThread.RequestComplete(status, ELastBufferCopied);
return;
}
// Dynamically adjust the buffering thread priority
TBool streamingThreadActive = iStreamingThreadAO->IsActive();
if (iSharedData.iBufferingMode == EBufferThread)
{
TInt numBuffers = iSharedData.NumBuffers();
if (numBuffers < KBufferThreadLowThreshold)
{
if ((iBufferingThreadPriority != EPriorityMore) && (iBufferingThreadPriority != EPriorityAbsoluteHigh))
{
if (!streamingThreadActive && !iSharedData.iLastBuffer)
{
iBufferingThreadPriority = (iBufferingThreadPriority == EPriorityNormal) ? EPriorityMore : EPriorityAbsoluteHigh;
iSharedData.iBufferingThread.SetPriority(iBufferingThreadPriority);
}
}
}
else if (numBuffers > KBufferThreadHighThreshold)
{
if ((iBufferingThreadPriority != EPriorityNormal) && (iBufferingThreadPriority != EPriorityAbsoluteForeground))
{
iBufferingThreadPriority = (iBufferingThreadPriority == EPriorityMore) ? EPriorityNormal : EPriorityAbsoluteForeground;
iSharedData.iBufferingThread.SetPriority(iBufferingThreadPriority);
}
}
}
// Ignore underflow if there are stream buffers left
if ((aErr == KErrUnderflow) && iStreamBuffers)
return;
// Check if we have available buffers
TInt availBuffers = iSharedData.NumBuffers() - iStreamBuffers;
if (!availBuffers)
{
// If the streaming thread is doing the buffering, prime another buffer now
if ((streamingThreadActive || (iSharedData.iBufferingMode == ENoBuffering)) && (aErr != KErrUnderflow))
iStreamingThreadAO->PrimeNextBuffer();
else
{
// If we still have stream buffers, ignore the fact there are no buffers left
if (iStreamBuffers)
return;
// Otherwise try to re-start playback
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
iSharedData.iUIThread.RequestComplete(status, EPlayUnderflow);
return;
}
}
// Stream the next buffer
SendNextBuffer();
availBuffers = iSharedData.NumBuffers() - iStreamBuffers;
if ((iStreamBuffers<iMaxStreamBuffers) && availBuffers)
{
// Try to catch up by writing another buffer
SendNextBuffer();
}
// If the number of buffers has dropped below a certain threshold, request the buffering thread to start buffering
if (iSharedData.NumBuffers()<iBufferLowThreshold)
{
// Nothing to do if the streaming thread is already buffering
// If a buffer flush is pending we don't want to start any more buffering
if (streamingThreadActive || iBufferFlushPending)
{
if (streamingThreadActive)
{
// Deschedule the streaming thread for a little while (1ms)
// This gives other threads a chance to run and really helps
// on phones where the thread scheduling doesn't work properly
#if !defined(__VC32__)
User::After(1000);
#endif
}
return;
}
switch (iSharedData.iBufferingMode)
{
case ENoBuffering:
break;
case EBufferStream:
if (!iSharedData.iLastBuffer)
iStreamingThreadAO->ResumeBuffering();
break;
case EBufferThread:
{
// Nothing to do if the buffering thread is already buffering
if (iSharedData.iBufRequestInProgress)
break;
// Nothing to do if we have reached eof
if (iSharedData.iLastBuffer)
break;
// Issue a buffering request
iSharedData.iBufRequestInProgress = ETrue;
TRequestStatus* status = &iSharedData.iBufferingThreadAO->iStatus;
iSharedData.iBufferingThread.RequestComplete(status, KErrNone);
}
break;
default:
User::Panic(_L("STPE: MaoscBC"), 0);
break;
}
}
if (streamingThreadActive)
{
// Deschedule the streaming thread for a little while (1ms)
// This gives other threads a chance to run and really helps
// on phones where the thread scheduling doesn't work properly
#if !defined(__VC32__)
User::After(1000);
#endif
}
}
// Handle play complete
// There are five possibilities possibilities:
// 1. Playback is stopped by the user
// In this case there is nothing to do, the UI thread will handle everything
// 2. KErrUnderflow
// MaoscBufferCopied will have scheduled the restart timer, so we ignore KErrUnderflow
// 3. KErrDied
// I'm not sure what causes this, but we simply inform the UI thread
// The UI thread will make a number of attempts to restart playback
// (This behaviour is the same as OggPlay 1.07 and earlier)
// 4. KErrInUse
// This is caused when another application takes the sound device from us (real player does this)
// OggPlay will stop playback and display a message informing the user.
// OggPlay will attempt to restart playback when the user presses "back"
// 5. Unknown error
// This is handled in the same way as KErrInUse, except that the UI thread will display the error code
void CStreamingThreadPlaybackEngine::MaoscPlayComplete(TInt aErr)
{
// Error codes:
// KErrNone 0 (stopped by user)
// KErrCancel -3 (also stopped by user)
// KErrUnderflow -10 (ran out of audio, EOF or lack of cpu power)
// KErrDied -13 (interrupted by higher priority)
// KErrInUse -14 (the sound device was stolen from us)
// Trace the reason for the play complete
TRACEF(COggLog::VA(_L("MaoscPlayComplete:%d"), aErr));
// Stopped by the user
if ((aErr == KErrNone) || (aErr == KErrCancel))
return;
// Ignore underflow, underflow is handled by MaoscBufferCopied
if (aErr == KErrUnderflow)
return;
// Expect KErrDied if interrupted
if (aErr == KErrDied)
{
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
if (status->Int() == KRequestPending)
iSharedData.iUIThread.RequestComplete(status, EPlayInterrupted);
return;
}
// Unknown error (or KErrInUse)
TRequestStatus* status = &iSharedData.iStreamingThreadListener->iStatus;
if (status->Int() == KRequestPending)
iSharedData.iUIThread.RequestComplete(status, aErr);
}
// Streaming thread listener AO
// This AO is owned by the COggPlayback object and runs in the UI thread
// It handles events from the audio stream (CMdaAudioOutputStream events from the streaming thread)
CStreamingThreadListener::CStreamingThreadListener(COggPlayback& aOggPlayback, TStreamingThreadData& aSharedData)
: CActive(EPriorityHigh), iOggPlayback(aOggPlayback), iSharedData(aSharedData), iListeningState(EListeningForOpenComplete)
{
CActiveScheduler::Add(this);
// Start listening
iStatus = KRequestPending;
SetActive();
}
CStreamingThreadListener::~CStreamingThreadListener()
{
}
void CStreamingThreadListener::RunL()
{
// Get the status
TInt status = iStatus.Int();
// Start listening again
iStatus = KRequestPending;
SetActive();
// Handle the message
switch (iListeningState)
{
case EListeningForOpenComplete:
iOggPlayback.NotifyOpenComplete(status);
iListeningState = EListeningForStreamingStatus;
break;
case EListeningForStreamingStatus:
iOggPlayback.NotifyStreamingStatus(status);
break;
}
}
void CStreamingThreadListener::DoCancel()
{
// Stop listening
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrCancel);
}
#endif /* MULTI_THREAD_PLAYBACK */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -