📄 oggtremor.cpp
字号:
// Set buffering values and allocate or free buffers
TInt err = BufferingModeChanged();
if (err != KErrNone)
{
// Reset back to the old mode
iSharedData.iBufferingMode = oldMode;
// Free any buffers that got allocated
BufferingModeChanged();
// Restart the stream
if (streamStopped)
iRestartAudioStreamingTimer->Wait(KStreamStartDelay);
return err;
}
// Set the new buffering mode values in the streaming thread
iStreamingThreadCommandHandler->SetBufferingMode();
// Restart the stream
if (streamStopped)
iRestartAudioStreamingTimer->Wait(KStreamStartDelay);
return KErrNone;
}
void COggPlayback::SetThreadPriority(TStreamingThreadPriority aNewThreadPriority)
{
// Set the thread priorities (Normal or High)
iSharedData.iStreamingThreadPriority = aNewThreadPriority;
iStreamingThreadCommandHandler->SetThreadPriority();
}
TBool COggPlayback::FlushBuffers(TFlushBufferEvent aFlushBufferEvent)
{
__ASSERT_DEBUG((aFlushBufferEvent == EBufferingModeChanged) || (aFlushBufferEvent == EPlaybackPaused), User::Panic(_L("COggPlayback::FB"), 0));
// Set the flush buffer event
iSharedData.iFlushBufferEvent = aFlushBufferEvent;
iStreamingThreadCommandHandler->PrepareToFlushBuffers();
iBufferingThreadAO->Cancel();
return iStreamingThreadCommandHandler->FlushBuffers();
}
TBool COggPlayback::FlushBuffers(TInt64 aNewPosition)
{
// The position has been changed so flush the buffers (and set the new position)
iSharedData.iFlushBufferEvent = EPositionChanged;
iSharedData.iNewPosition = aNewPosition;
iStreamingThreadCommandHandler->PrepareToFlushBuffers();
iBufferingThreadAO->Cancel();
return iStreamingThreadCommandHandler->FlushBuffers();
}
void COggPlayback::FlushBuffers(TGainType aNewGain)
{
// The volume gain has been changed so flush the buffers (and set the new gain)
iSharedData.iFlushBufferEvent = EVolumeGainChanged;
iSharedData.iNewGain = aNewGain;
TBool streaming = iStreamingThreadCommandHandler->PrepareToFlushBuffers();
iBufferingThreadAO->Cancel();
if (streaming && (iSharedData.iBufferingMode == EBufferThread))
iBufferingThreadAO->StartListening();
iStreamingThreadCommandHandler->FlushBuffers();
}
void COggPlayback::SetDecoderPosition(TInt64 aPos)
{
// Set the deocder position (aPos is in milliseconds)
iDecoder->Setposition(aPos);
}
void COggPlayback::SetSampleRateConverterVolumeGain(TGainType aGain)
{
// Set the volume gain (called by the streaming thread after the buffers have been flushed)
iOggSampleRateConverter->SetVolumeGain(aGain);
}
TInt COggPlayback::StreamingThread(TAny* aThreadData)
{
// Access the shared data / command handler
TStreamingThreadData& sharedData = *((TStreamingThreadData *) aThreadData);
CStreamingThreadCommandHandler& streamingThreadCommandHandler = *sharedData.iStreamingThreadCommandHandler;
// Create a cleanup stack
CTrapCleanup* cleanupStack = CTrapCleanup::New();
if (!cleanupStack)
{
// Inform the UI thread that starting the streaming thread failed
streamingThreadCommandHandler.ResumeComplete(KErrNoMemory);
return KErrNoMemory;
}
// Set the thread priority
RThread streamingThread;
streamingThread.SetPriority(EPriorityMore);
streamingThread.Close();
// Allocate resources and start the active scheduler
TRAPD(err, StreamingThreadL(sharedData));
// Cleanup
COggLog::Exit();
delete cleanupStack;
// Complete the shutdown request
streamingThreadCommandHandler.ShutdownComplete(err);
return err;
}
void COggPlayback::StreamingThreadL(TStreamingThreadData& aSharedData)
{
// Attach to the file session
User::LeaveIfError(aSharedData.iOggPlayback.AttachToFs());
// Create a scheduler
CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
CleanupStack::PushL(scheduler);
// Install the scheduler
CActiveScheduler::Install(scheduler);
// Add the streaming command handler to the scheduler
CStreamingThreadCommandHandler& streamingThreadCommandHandler = *aSharedData.iStreamingThreadCommandHandler;
CActiveScheduler::Add(&streamingThreadCommandHandler);
// Create the audio playback engine
CStreamingThreadPlaybackEngine* playbackEngine = CStreamingThreadPlaybackEngine::NewLC(aSharedData);
// Listen for commands and dispatch them to the playback engine
streamingThreadCommandHandler.ListenForCommands(*playbackEngine);
// Inform the UI thread that the streaming thread has started
streamingThreadCommandHandler.ResumeComplete(KErrNone);
// Start the scheduler
CActiveScheduler::Start();
// Cancel the command handler
streamingThreadCommandHandler.Cancel();
// Uninstall the scheduler
CActiveScheduler::Install(NULL);
// Delete the scheduler and audio engine
CleanupStack::PopAndDestroy(2, scheduler);
}
TBool COggPlayback::PrimeBuffer(HBufC8& buf)
{
// Read and decode the next buffer
TPtr8 bufPtr(buf.Des());
iOggSampleRateConverter->FillBuffer(bufPtr);
return iEof;
}
void COggPlayback::NotifyOpenComplete(TInt aErr)
{
// Called by the streaming thread listener when CMdaAudioOutputStream::Open() completes
if (aErr == KErrNone)
iState = EStreamOpen;
iObserver->NotifyStreamOpen(aErr);
}
TInt COggPlayback::BufferingModeChanged()
{
// The buffering mode has changed so allocate or free buffers
TInt i;
TInt allocError = KErrNone;
switch (iSharedData.iBufferingMode)
{
case ENoBuffering:
iSharedData.iBuffersToUse = KNoBuffers;
iSharedData.iMaxBuffers = KNoBuffers;
// Delete all the buffers
for (i = 1 ; i<KMultiThreadBuffers ; i++)
{
delete iBuffer[i];
iBuffer[i] = NULL;
}
break;
case EBufferStream:
iSharedData.iBuffersToUse = KSingleThreadBuffersToUse;
iSharedData.iMaxBuffers = KSingleThreadBuffers;
if (iBuffer[KNoBuffers])
{
// The previous mode was EBufferThread so free some buffers
for (i = KSingleThreadBuffers ; i<KMultiThreadBuffers ; i++)
{
delete iBuffer[i];
iBuffer[i] = NULL;
}
}
else
{
// Allocate the buffers
for (i = 1 ; i<KSingleThreadBuffers ; i++)
{
iBuffer[i] = HBufC8::New(KBufferSize48K);
if (!iBuffer[i])
{
allocError = KErrNoMemory;
break;
}
}
}
break;
case EBufferThread:
iSharedData.iBuffersToUse = KMultiThreadBuffersToUse;
iSharedData.iMaxBuffers = KMultiThreadBuffers;
if (iBuffer[KNoBuffers])
{
// The previous mode was EBufferStream so allocate some more buffers
for (i = KSingleThreadBuffers ; i<KMultiThreadBuffers ; i++)
{
iBuffer[i] = HBufC8::New(KBufferSize48K);
if (!iBuffer[i])
{
allocError = KErrNoMemory;
break;
}
}
}
else
{
// Allocate the buffers
for (i = 1 ; i<KMultiThreadBuffers ; i++)
{
iBuffer[i] = HBufC8::New(KBufferSize48K);
if (!iBuffer[i])
{
allocError = KErrNoMemory;
break;
}
}
}
break;
}
return allocError;
}
void COggPlayback::NotifyStreamingStatus(TInt aErr)
{
// Called by the streaming thread listener when the last buffer has been copied or play has been interrupted
// Theoretically it's possible for a restart to be in progress, so make sure it doesn't complete
// (i.e. when the streaming thread generates an event at the same time as the UI thread has started processing a RW\FW key press)
iRestartAudioStreamingTimer->Cancel();
// Ignore streaming status if we are already handling an error
if (iStreamingErrorDetected)
return;
// Check for an error
if (aErr < 0)
{
// Pause the stream
FlushBuffers(EPlaybackPaused);
iStreamingErrorDetected = ETrue;
// Notify the user
TBuf<256> buf, tbuf;
if (aErr == KErrInUse)
{
CEikonEnv::Static()->ReadResource(tbuf, R_OGG_ERROR_18);
CEikonEnv::Static()->ReadResource(buf, R_OGG_ERROR_30);
}
else
{
CEikonEnv::Static()->ReadResource(tbuf, R_OGG_ERROR_18);
CEikonEnv::Static()->ReadResource(buf, R_OGG_ERROR_16);
buf.AppendNum(aErr);
}
// Display the message and wait for the user
iEnv->OggErrorMsgL(tbuf, buf);
// Notify the UI, try to restart
iObserver->NotifyPlayInterrupted();
iStreamingErrorDetected = EFalse;
return;
}
// Handle stream events
TStreamingThreadStatus status = (TStreamingThreadStatus) aErr;
switch (status)
{
case ELastBufferCopied:
// Not all phones call MaoscPlayComplete,
// so start a timer that will stop playback afer a delay
iStopAudioStreamingTimer->Wait(KStreamStopDelay);
break;
case EPlayInterrupted:
// Playback has been interrupted so notify the observer
iObserver->NotifyPlayInterrupted();
break;
case EPlayUnderflow:
// Flush buffers (and pause the stream)
FlushBuffers(EPlaybackPaused);
iRestartAudioStreamingTimer->Wait(KStreamRestartDelay);
break;
default:
User::Panic(_L("COggPlayback:NSS"), 0);
break;
}
}
void COggPlayback::HandleThreadPanic(RThread& /* aPanicThread */, TInt /* aErr */)
{
// Great, the streaming thread has panic'd, now what do we do!?
iStreamingThreadRunning = EFalse;
iState = EClosed;
// Try to exit cleanly
iObserver->NotifyFatalPlayError();
}
#else
void COggPlayback::SendBuffer(HBufC8& buf)
{
if (iEof) return;
if (iState==EPaused) return;
long ret=0;
TInt cnt = 0;
TPtr8 bufPtr(buf.Des());
if (iFirstBuffers) {
cnt = 4000;
bufPtr.FillZ(cnt);
ret = cnt;
iLastPlayBufferBytes+= cnt;
iFirstBuffers--;
} else {
ret = iOggSampleRateConverter->FillBuffer(bufPtr);
}
if (ret < 0) {
// Error in the stream. Bad luck, we will continue anyway.
} else {
TRAPD( err, iStream->WriteL(buf) );
if (err!=KErrNone) {
// This should never ever happen.
TBuf<256> cbuf,tbuf;
CEikonEnv::Static()->ReadResource(cbuf,R_OGG_ERROR_16);
CEikonEnv::Static()->ReadResource(tbuf,R_OGG_ERROR_17);
cbuf.AppendNum(err);
iEnv->OggErrorMsgL(tbuf,cbuf);
User::Leave(err);
}
iSentIdx++;
if (iSentIdx>=KBuffers) iSentIdx=0;
iSent[iSentIdx]= &buf;
}
}
void COggPlayback::MaoscPlayComplete(TInt aErr)
{
// Error codes:
// KErrCancel -3
// KErrUnderflow -10
// KErrDied -13 (interrupted by higher priority)
// KErrInUse -14
TRACEF(COggLog::VA(_L("MaoscPlayComplete:%d"), aErr ));
if (iState==EPaused || iState==EStopped) return;
if (aErr == KErrUnderflow)
return;
if (aErr == KErrInUse) aErr= KErrNone;
if (aErr == KErrCancel)
return;
if (aErr == KErrDied) {
iObserver->NotifyPlayInterrupted();
return;
}
if (aErr != KErrNone) {
TBuf<256> buf,tbuf;
CEikonEnv::Static()->ReadResource(tbuf,R_OGG_ERROR_18);
CEikonEnv::Static()->ReadResource(buf,R_OGG_ERROR_16);
buf.AppendNum(aErr);
iEnv->OggErrorMsgL(tbuf,buf);
if (iObserver )
{
if (!iEof )
iObserver->NotifyPlayInterrupted();
}
}
}
void COggPlayback::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)
if (aErr != KErrNone)
{ TRACEF(COggLog::VA(_L("MaoscBufferCopied:%d"), aErr )); }
if (aErr == KErrCancel) aErr= KErrNone;
if (aErr == KErrAbort ||
aErr == KErrInUse ||
aErr == KErrDied ||
aErr == KErrCancel) return;
if (iState != EPlaying) return;
TInt b;
for (b=0; b<KBuffers; b++) if (&aBuffer == iBuffer[b]) break;
if ( (iEof) && (iSent[iSentIdx] == &aBuffer) )
{
// All the buffers have been sent, stop the playback
// We cannot rely on a MaoscPlayComplete from the CMdaAudioOutputStream
// since not all phone supports that.
iUnderflowing = EFalse;
iRestartAudioStreamingTimer->Cancel();
iStopAudioStreamingTimer->Wait(KStreamStopDelay);
return;
}
else if (aErr == KErrUnderflow)
{
#ifdef DELAY_AUDIO_STREAMING_START
if (!iUnderflowing)
{
iFirstUnderflowBuffer = b;
iLastUnderflowBuffer = b;
iUnderflowing = ETrue;
}
else
iLastUnderflowBuffer = b;
iRestartAudioStreamingTimer->Wait(KStreamRestartDelay);
return;
#else
aErr = KErrNone;
#endif
}
if (aErr == KErrNone)
{
if (iUnderflowing)
{
iRestartAudioStreamingTimer->Cancel();
RestartAudioStreamingCallBack(this);
}
SendBuffer(*iBuffer[b]);
}
else {
// An unknown error condition. This should never ever happen.
TBuf<256> buf,tbuf;
CEikonEnv::Static()->ReadResource(tbuf, R_OGG_ERROR_18);
CEikonEnv::Static()->ReadResource(buf, R_OGG_ERROR_16);
buf.AppendNum(aErr);
iEnv->OggErrorMsgL(tbuf,buf);
}
}
void COggPlayback::MaoscOpenComplete(TInt aErr)
{
if (aErr == KErrNone)
{
iState = EStreamOpen;
iMaxVolume=iStream->MaxVolume();
iStream->SetPriority(KAudioPriority, EMdaPriorityPreferenceTimeAndQuality);
}
iObserver->NotifyStreamOpen(aErr);
}
void COggPlayback::RestartAudioStreamingCallBack()
{
iUnderflowing = EFalse;
TInt firstUnderflowBuffer = iFirstUnderflowBuffer;
TInt lastUnderflowBuffer = iLastUnderflowBuffer;
TInt i;
if (lastUnderflowBuffer>=firstUnderflowBuffer)
{
for (i = firstUnderflowBuffer ; i<=lastUnderflowBuffer ; i++)
SendBuffer(*(iBuffer[i]));
}
else
{
for (i = firstUnderflowBuffer ; i<KBuffers ; i++)
SendBuffer(*(iBuffer[i]));
for (i = 0 ; i<=lastUnderflowBuffer ; i++)
SendBuffer(*(iBuffer[i]));
}
}
#endif
#endif /* !PLUGIN_SYSTEM */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -