📄 shoutcaststream.cpp
字号:
// Pauses the playback but the engine continues to receive data from the
// network. The stream output is stopped.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::Pause()
{
iStreamOutput->Stop();
LOG("PauseL, OK");
}
// -----------------------------------------------------------------------------
// CShoutcastStream::Stop
// Stops the playback. The engine stops receiving data from the
// network. The stream output is stopped.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::Stop()
{
LOG("StopL");
if ( iState == EData )
{
iStreamOutput->Stop();
}
//we have to undo all that the PrimeL function did
Cancel();//make sure we do not have any request pending
CloseAndClean();
//we expect that Prime & Play to be called in order to play again
LOG("StopL OK");
}
// -----------------------------------------------------------------------------
// CShoutcastStream::DoCancel
// Cancels any outstanding requests
// -----------------------------------------------------------------------------
//
void CShoutcastStream::DoCancel()
{
LOG("DoCancel was called!!!");
iSock.CancelAll();
}
// -----------------------------------------------------------------------------
// CShoutcastStream::RunL
// Invoked when one of these requests completes.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::RunL()
{
switch ( iState )
{
case EConnecting:
SendRequestToServerL();
break;
case ESendingRequest:
ReceiveResponseFromServerL();
break;
case EReceivingResponse:
ParseResponseFromServerL();
break;
case EData:
{
if ( iStatus != KErrNone )
{
CloseAndClean();
Disconnect(iStatus.Int());
return;
}
if ( ReadRequestHelperDone() != KErrNone )
{
CloseAndClean();
Disconnect(iStatus.Int());
return;
}
};
break;
default:
ASSERT(0);
}
}
// -----------------------------------------------------------------------------
// CShoutcastStream::RunError
// Called when the RunL leaves.
// If we are here, it means that we failed to connect somehow!
// -----------------------------------------------------------------------------
//
TInt CShoutcastStream::RunError(
TInt aError )
{
//send the error as an event to the client
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), aError);
return KErrNone;
}
// -----------------------------------------------------------------------------
// CShoutcastStream::GetNumberOfMetaDataEntries
// Get number of metadata entries
// -----------------------------------------------------------------------------
//
TInt CShoutcastStream::GetNumberOfMetaDataEntries()
{
return NR_METADATA_FIELDS;
}
// -----------------------------------------------------------------------------
// CShoutcastStream::GetNumberOfMetaDataEntries
// Get a particular metadata entry.
// -----------------------------------------------------------------------------
//
CMetaDataEntry* CShoutcastStream::GetMetaDataEntryL(
TInt aIndex )
{
//check if this is "special" metadata
//LOG("GetMetaDataEntryL");
if( aIndex == KShoutcastMetaData_URL )
{
CMetaDataEntry *mmfMetadata;
iTempMetadata.Copy(iURL);
mmfMetadata = CMetaDataEntry::NewL(KSCMetaServer, iTempMetadata);
return mmfMetadata;
};
//this is normal metadata
if ( aIndex >= iMetadata.Count() )
{
return NULL;
}
else
{
CMetaDataEntry* md = iMetadata[aIndex];
md = CMetaDataEntry::NewL(*md);
return md;
}
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ReadRequest
// The read request is done when all the requested data has been read.
// The read request is made intelligently:
// -first, we read until the metadata count byte.
// -if we have metadata, then we read metadata (in a separate buffer),
// -then we read what is left
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ReadRequest()
{
TInt data2read = DATA2READ;
TUint8 *buffer2read = NULL;
if ( iReadData == -1)
{
//case 1: the buffer is partially filled (data is in the middle)
//we have to read from iBuffer2Decode+iPosBuffer2Decode+iLenBuffer2Decode
//... 8k (DATA2READ)
//... or until the end of the buffer
if ( data2read > BUFFER2DECODE_SIZE-iPosBuffer2Decode-iLenBuffer2Decode )
{
data2read = BUFFER2DECODE_SIZE-iPosBuffer2Decode-iLenBuffer2Decode;
}
//check for transition to case 2
if ( data2read < READ_EPSILON && iPosBuffer2Decode>iMaxFrameSize )
{
//go to case 2
iReadData = 0;
//LOG2("RR transition case 1->case 2 (pos=%d, len=%d)",iPosBuffer2Decode,iLenBuffer2Decode);
ReadRequest();
return;
}
else
{
//stick to case 1
buffer2read = iBuffer2Decode+iPosBuffer2Decode+iLenBuffer2Decode;
//LOG1("RR case 1: have to read %d bytes",data2read);
}
}
else
{
//case 2: we fill the buffer from the beginning (or from the middle)
//we have to read from iBuffer2Decode+iMaxFrameSize+iReadData
//... 8k (DATA2READ)
//... or until the data (iPosBuffer2Decode)
ASSERT(iReadData >= 0);
ASSERT(iMaxFrameSize >= 0);
ASSERT(iMaxFrameSize < 3000);
ASSERT(iPosBuffer2Decode >= iMaxFrameSize);
ASSERT(iMaxFrameSize + iReadData <= iPosBuffer2Decode);
if ( iMaxFrameSize+iReadData+data2read > iPosBuffer2Decode )
{
data2read = iPosBuffer2Decode-iMaxFrameSize-iReadData;
}
buffer2read = iBuffer2Decode+iMaxFrameSize+iReadData;
//LOG1("RR case 2: have to read %d bytes",data2read);
}
if ( data2read < READ_EPSILON )
{
//the buffer looks completely full!
// suspend the read
iReadingActive = EFalse;
//LOG("RR OK (buffer full, no reading)");
if ( iPausedForBuffering ) // in case we have paused playback for buffering...
{
TRAPD(err, PlayL());
if ( err )
{
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), err);
}
else
{
// send buffering complete event!!
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),KShoutcastEvent_BufferingComplete);
}
}
}
else
{
//we need to read data!
ASSERT(data2read > 0);
iPtrBuffer2Decode.Set(buffer2read,0,data2read);
iReadingActive = ETrue;
iRRreq = data2read;
iRRread = 0;
//LOG1("RR OK (will read %d bytes)",data2read);
ReadRequestHelper();
}
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ReadRequestHelper
// Read the requested amount of data from the socket. When the asynchronous read
// completes, our RunL will be called.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ReadRequestHelper()
{
//read from the socket
iSock.Read(iPtrBuffer2Decode,iStatus);
SetActive();
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ReadRequestHelperDone
// The read request is completed. Extract metadata from the buffer if there's
// any and request additional reads if the buffer wasn't full.
// -----------------------------------------------------------------------------
//
TInt CShoutcastStream::ReadRequestHelperDone()
{
TInt readSize = iPtrBuffer2Decode.Size();
TInt maxSize = iPtrBuffer2Decode.MaxSize();
//LOG2("RRHD read %d (status: %d)",readSize,iStatus.Int());
GetAndRemoveMetadata(const_cast<TUint8*>(iPtrBuffer2Decode.Ptr()),readSize);
iRRread+=readSize;
//check if we have read the full length
if ( maxSize-readSize<5 || (readSize==0 && iStatus.Int()==0) )
{
//this reading is done!
return ReadRequestDone(iRRreq,iRRread);
}
//LOG2("RRHD: Partial read of %d, requesting again %d bytes",readSize,maxSize-readSize);
iPtrBuffer2Decode.Set(const_cast<TUint8*>(iPtrBuffer2Decode.Ptr())+readSize,0,maxSize-readSize);
ReadRequestHelper();
return KErrNone;
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ReadRequestDone
// The entire read request buffer is full. Now process the contents and re-new
// the read request.
// -----------------------------------------------------------------------------
//
TInt CShoutcastStream::ReadRequestDone(
TInt aRequested,
TInt aRead )
{
TInt bufferFill;
TInt avgBitrate = 0;
TInt metadataInd = 0;
//if we are here, it means that we read normal data
//LOG2("RRD: We have read normal data (req=%d, read=%d)",aRequested,aRead);
iTotalBytesReceived += aRead;
// 100kb: 1024*100 = 102400 (decimal)
// 1MB: 1048576 binary bytes
// In order to get exactly 0.1 MB, and for easy calculation, the constant 104857 is used here.
//every 100kB we signal metadata modifications
if( (iNrHKB+1) * K100kBFactor < iTotalBytesReceived )
{
iNrHKB++;
//some more metadata
iTempMetadata.Format(KSCDataRcvdFormat, iTotalBytesReceived/K1MBFactor);
// It's ok if we can't set the value.
TRAPD(err,
{
iMetadata[4]->SetValueL(iTempMetadata);
iTempMetadata.Format(KSCMetaPriceFormat, iTotalBytesReceived/K1MBFactor * PRICE_PER_MB);
iMetadata[5]->SetValueL(iTempMetadata);
});
if ( !err )
{
// Indicate which metadata was updated
metadataInd = 0x0130;
}
}
//do something with the data we just read!
if( iReadData == -1)
{
//case 1: we should increase the data length
iLenBuffer2Decode+=aRead;
bufferFill=(TInt)(100.0*iLenBuffer2Decode/(BUFFER2DECODE_SIZE-iMaxFrameSize));
}
else
{
//case 2: we need to increase the iReadData
iReadData += aRead;
bufferFill = (TInt)(100.0*(iLenBuffer2Decode+iReadData)/(BUFFER2DECODE_SIZE-iMaxFrameSize));
}
//compute bitrate
if( iLastReading.Int64() == 0)
{
iLastReading.UniversalTime();
}
else
{
TInt i,tbits;
TTime now;
TInt64 avg;
now.UniversalTime();
TInt64 diff=now.Int64()-iLastReading.Int64();
TInt bits=(TInt)(aRead*7812.5);//7812.5=8*1000*1000/1024 (8=bits, 1024=kbits, 100000 from microseconds)
if(diff>0) //this is to avoid division by zero on EKA1 kernels
{
#ifdef __SERIES60_3X__
iInstantBitrate=(TInt)(bits/diff);
#else //2.x
iInstantBitrate=bits/diff.GetTInt();
#endif
};//if diff is zero, we keep the old value
//compute now the averaged bitrate
iBitsRead.Append(bits);
iTimeIntervals.Append(diff);
ASSERT(iBitsRead.Count()==iTimeIntervals.Count());
tbits=iBitsRead[0];
avg=iTimeIntervals[0];
for( i = 1; i < iBitsRead.Count(); i++)
{
tbits += iBitsRead[i];
avg += iTimeIntervals[i];
};
#ifdef __SERIES60_3X__
avgBitrate=(TInt)(tbits/avg);
#else //2.x
avgBitrate=tbits/avg.GetTInt();
#endif
if( iBitsRead.Count() > BITRATE_SAVE_COUNT )
{
iBitsRead.Remove(0);
iTimeIntervals.Remove(0);
}
iLastReading=now;
}
//fill buffering metadata
if ( bufferFill > 100)
{
bufferFill=100;//this can happen when a part of iMaxFrameSize is also filled
}
TRAPD(err,
{
iTempMetadata.Format(KSCBufFormat, bufferFill);
iMetadata[6]->SetValueL(iTempMetadata);
iTempMetadata.Format(KSCMetaBitrateFormat, avgBitrate, iInstantBitrate);
iMetadata[7]->SetValueL(iTempMetadata);
});
if ( !err )
{
// Indicate which metadata was updated
metadataInd = metadataInd | 0x01C0;
}
// Send metadata update event to client
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), metadataInd);
//if we are here, it means that we have finished reading our data!!
//LOG1("RRD: Done reading %d bytes",aRead);
//Read again!
ReadRequest();
return KErrNone;
}
// -----------------------------------------------------------------------------
// CShoutcastStream::RemoveMetadata
// Remove metadata from the buffer
// -----------------------------------------------------------------------------
//
void CShoutcastStream::RemoveMetadata(
TDes8& ptrMetadata )
{
ASSERT(iMetaint > 0);//we have metadata
TInt pos1,pos2;
pos1 = ptrMetadata.Find(KSCStreamTitle);
pos2 = ptrMetadata.Find(_L8("';"));
if ( pos1 != KErrNotFound && pos2 != KErrNotFound )
{
//use pos2 as length
pos2 = pos2-pos1-13;//13 is the number of characters in "StreamTitle='"
if ( pos2 > iTempMetadata.MaxLength() )
{
pos2=iTempMetadata.MaxLength();
}
iTempMetadata.Copy(ptrMetadata.Mid(pos1+13,pos2));
TRAPD(err, iMetadata[3]->SetValueL(iTempMetadata));
//LOG2("MD: Metadata(%d):(%S)", pos2, &iTempMetadata);
// Send metadata event to the client
if ( !err )
{
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),0x0108);
}
}
else
{
//LOG("MD: Metadata found but Title NOT FOUND !!! ");
}
}
// -----------------------------------------------------------------------------
// CShoutcastStream::GetAndRemoveMetadata
// Get the metadata and remove it from the data stream
// -----------------------------------------------------------------------------
//
void CShoutcastStream::GetAndRemoveMetadata(
TUint8 *aBuf,
TInt &aLength )
{
if ( iMetaint == 0 )
{
return; //we do not have metadata!
}
TInt dataLength = aLength;
TInt pos = 0;
while ( iCnt < dataLength )
{
dataLength -= iCnt;
pos += iCnt;
//we have metadata here!
iDataSeen = 16 * aBuf[pos];
//LOG1("MD: Found Metadata %d bytes",iDataSeen);
if ( iDataSeen )
if ( iDataSeen < METADATA_BUFFER_SIZE )
{
//we really have metadata!
TPtr8 ptrMetadata(aBuf+pos+1,iDataSeen,iDataSeen);
RemoveMetadata(ptrMetadata);
}
else
{
//we lost the sync
ASSERT(0);
}
//remove the metadata
iDataSeen++;
//LOG2("Removing metadata: pos=%d, removed=%d",pos,iDataSeen);
memmove(aBuf+pos,aBuf+pos+iDataSeen,aLength-pos-iDataSeen);
iCnt = iMetaint;
aLength -= iDataSeen;
dataLength -= iDataSeen;
//LOG1("After memmove: aLength=%d",aLength);
}
iCnt -= dataLength;
//LOG2("MD: we exit with %d bytes (cnt is %d)",aLength,iCnt);
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ScanHeader
// -----------------------------------------------------------------------------
//
TInt CShoutcastStream::ScanHeader(
TUint8 *aBuf,
TInt aLength )
{
TInt length = 0;
TInt readLen = aLength;
TInt seekLen;
const TUint8* seekPtr;
TInt seekOffset;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -