📄 shoutcaststream.cpp
字号:
// -----------------------------------------------------------------------------
// CShoutcastStream::ConnectToServerL
// Attempt to open and establish connection with the server.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ConnectToServerL(
TBool aReconnecting )
{
LOG("CShoutcastStream::ConnectToServerL");
TInt err;
//initialize data related to networking
ASSERT(iState == ENotConnected);
if ( !aReconnecting )
{
//retrieving the address and connecting to Socket Server can be done only once!
err = iSocksvr.Connect();
if ( err != KErrNone )
{
User::Leave(KErrShoutcast_ConnectToSockServer+err);
}
LOG("ConnectToServerL: Connected to ESOCK server");
//opening the socket can also be done only once
err = iSock.Open(iSocksvr, KAfInet, KSockStream, KProtocolInetTcp);
LOG1("ConnectToServerL: Client sock opened, err=%d",err);
if( err != KErrNone )
{
User::Leave(KErrShoutcast_OpenSocket+err);
}
}
//announce to the client that there will be a lengthly operation
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),
aReconnecting?KShoutcastEvent_Reconnecting:KShoutcastEvent_Connecting);
//connecting
iState = EConnecting;
iSock.Connect(*iAddr, iStatus);
SetActive();
LOG("CShoutcastStream::ConnectToServerL OK");
}
// -----------------------------------------------------------------------------
// CShoutcastStream::SendRequestToServerL
// Sends HTTP requests to the server
// -----------------------------------------------------------------------------
//
void CShoutcastStream::SendRequestToServerL()
{
//LOG1("SendRequestToServerL: Status: %d", iStatus.Int());
if (iStatus != KErrNone)
{
User::Leave(KErrShoutcast_Connecting2Server+iStatus.Int());
}
//We use iPtrBuffer2Decode to build the HTTP request
ResetBufferVars();
LOG1("SendRequestToServerL: No error, sending HTTP request (path has %d bytes)", iPath.Length());
//first line of the request
iPtrBuffer2Decode.Copy(KSCGet);
iPtrBuffer2Decode.Append(iPath);
iPtrBuffer2Decode.Append(KSCHttp10);
//next lines of the request
iPtrBuffer2Decode.Append(KSCUserAgent);
iPtrBuffer2Decode.Append(KSCAccept);
//Indicate to the server we want to receive metadata
iPtrBuffer2Decode.Append(KSCIcyMetadata);
iPtrBuffer2Decode.Append(KSCConnectionClose);
//send it off
iState = ESendingRequest;
iSock.Write(iPtrBuffer2Decode, iStatus);
SetActive();
TBuf16<1000> zz;
zz.Copy(iPtrBuffer2Decode);
LOG1("SendRequestToServerL OK (HTTP request sent:\n%S)->end of HTTP request",&zz);
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ReceiveResponseFromServerL
// Read the response from the server.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ReceiveResponseFromServerL()
{
LOG("CShoutcastStream::ReceiveResponseFromServerL: We sent the HTTP request");
if (iStatus != KErrNone)
{
User::Leave(KErrShoutcast_SendingHTTPRequest+iStatus.Int());
}
LOG("CShoutcastStream::ReceiveResponseFromServerL: HTTP request sent successfully");
//receive the HTTP answer
iState = EReceivingResponse;
iPtrBuffer2Decode.SetLength(0);
iSock.Read(iPtrBuffer2Decode,iStatus);
SetActive();
LOG("CShoutcastStream::ReceiveResponseFromServerL OK");
}
// -----------------------------------------------------------------------------
// CShoutcastStream::ParseResponseFromServerL
// Parse the response message.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ParseResponseFromServerL()
{
TInt pos,len,extraByteNL;
len = iPtrBuffer2Decode.Size();
TBool haveError=EFalse;
//LOG2("ParseResponseFromServerL: HTTP answer: we got %d bytes. err = %d",len, iStatus.Int());
if (iStatus != KErrNone)
{
if(len==0)
{
User::Leave(KErrShoutcast_ReceivingHTTPResponse+iStatus.Int());
}
else
{
haveError=ETrue;
}
}
//Get and parse the HTTP answer
if(len < 500 && iStatus != KErrNone)
{
TBuf16<500> zz;
zz.Copy(iPtrBuffer2Decode);
LOG1("HTTP answer:\n%S",&zz);
};
//...but first, initialize some data
ASSERT(iPosBuffer2Decode == 0);
iTotalBytesReceived+=len;//so, we received some data in the iPtrBuffer2Decode
iState = EAnswerError;//if we don't get the OK HTTP response, we asume there was an error
for(;;) // Parse the response header
{
iPtrBuffer2Decode.Set(iBuffer2Decode+iPosBuffer2Decode, len-iPosBuffer2Decode, len-iPosBuffer2Decode);
pos = iPtrBuffer2Decode.Locate(10);
if(pos==KErrNotFound)
{
User::Leave(KErrShoutcast_NoHTTPServer);
}
if(pos > 0 && iPtrBuffer2Decode[pos-1] == 13)
{
extraByteNL=1;
}
else
{
extraByteNL=0;
}
iPtrBuffer2Decode.Set(iBuffer2Decode+iPosBuffer2Decode,pos-extraByteNL,pos-extraByteNL);
iPosBuffer2Decode += (pos+1);
if( pos-extraByteNL == 0 )
{
break;//we got to the end of the answer
}
//if we are here, we got a line to parse
//check our line against some usefull header
if( iPtrBuffer2Decode.Find(KSCOKHeader) != KErrNotFound )
{
//we found the OK line
iState = EAnswerOK;
LOG("200 OK");
}
else if( iPtrBuffer2Decode.Find(KSCContentTypeTag) != KErrNotFound )
{
//This tell us which mime type the stream is.
TInt pos2 = iPtrBuffer2Decode.LocateReverse(':');
if( pos2 > 0 )
{
TPtrC8 contentPtr = iPtrBuffer2Decode.Right(iPtrBuffer2Decode.Length()-pos2-1);
TBuf<20> contentType;
contentType.Copy(contentPtr);
contentType.Trim();
if ( !contentType.Compare(KSCMimeTypeAudioMpeg) )
{
iDataType.Set(KMMFFourCCCodeMP3);
}
else if ( !contentType.Compare(KSCMimeTypeAudioAacp) )
{
iDataType.Set(KMMFFourCCCodeAAC);
}
else if ( !contentType.Compare(KSCMimeTypeAudioAac) )
{
iDataType.Set(KMMFFourCCCodeAAC);
}
}
}
else if( iPtrBuffer2Decode.Find(KSCIcyMetaintTag) != KErrNotFound )
{
//we will get MetaData, at some interval. Fill the iMetaint
TInt pos2 = iPtrBuffer2Decode.LocateReverse(':');
if( pos2 == KErrNotFound )
{
User::Leave(KErrShoutcast_BadIcyMetadataFormat);
}
TPtrC8 metaintPtr = iPtrBuffer2Decode.Right(iPtrBuffer2Decode.Length()-pos2-1);
TBuf8<10> metaint;
metaint.Copy(metaintPtr);
metaint.Trim();
iMetaint = atoi((const char *)metaint.PtrZ());
iCnt = iMetaint;
LOG1("We have metadata at %d bytes interval",iMetaint);
}
else if( iPtrBuffer2Decode.Find(KSCIcyNameTag) != KErrNotFound )
{
LOG("icy-name");
//this is the server name. Fill the metadataServer
TInt pos2 = iPtrBuffer2Decode.LocateReverse(':');
if( pos2 > 0 )
{
pos2 = iPtrBuffer2Decode.Length()-pos2-1;
if( pos2 > iTempMetadata.MaxLength() )
{
pos2 = iTempMetadata.MaxLength();
}
iTempMetadata.Copy(iPtrBuffer2Decode.Right(pos2));
iMetadata[0]->SetValueL(iTempMetadata);
} //if pos2==KErrNotFound or simmilar, no big deal, we will not have server name
}
else if( iPtrBuffer2Decode.Find(KSCIcyGenreTag) != KErrNotFound )
{
LOG("icy-genre");
//this is the server genre. Fill the metadataGenre
TInt pos2 = iPtrBuffer2Decode.LocateReverse(':');
if( pos2 > 0 )
{
LOG("icy-genre present");
//use pos2 as the new length
pos2 = iPtrBuffer2Decode.Length()-pos2-1;
if( pos2 > iTempMetadata.MaxLength() )
{
pos2 = iTempMetadata.MaxLength();
}
iTempMetadata.Copy(iPtrBuffer2Decode.Right(pos2));
iMetadata[1]->SetValueL(iTempMetadata);
} //if pos2==KErrNotFound or simmilar, no big deal, we will not have server genre
}
else if( iPtrBuffer2Decode.Find(KSCIcyBrTag) != KErrNotFound )
{
LOG("icy-br");
//this is the bitrate
TInt pos2 = iPtrBuffer2Decode.LocateReverse(':');
if(pos2 == KErrNotFound)
{
User::Leave(KErrShoutcast_BadIcyMetadataFormat);
}
TPtrC8 brPtr = iPtrBuffer2Decode.Right(iPtrBuffer2Decode.Length()-pos2-1);
TBuf8<10> br;
br.Copy(brPtr);
br.Trim();
iBitRate = atoi((const char *)br.PtrZ()) * 1000;
}
}
//check if the answer is EAnswerOK
if( iState != EAnswerOK || haveError )
{
User::Leave(KErrShoutcast_NoShoutcastServer);
}
//get the metadata out of the remaining buffer/data
iLenBuffer2Decode = len-iPosBuffer2Decode;
GetAndRemoveMetadata(iBuffer2Decode+iPosBuffer2Decode, iLenBuffer2Decode);
GetAudioSettings();
//some more metadata
iTempMetadata.Format(KSCTechDataFormat, iBitRate/1000, iSamplingRate/1000);
if ( iAudioSettings.iChannels == TMdaAudioDataSettings::EChannelsMono )
{
iTempMetadata.Append(KSCMono);
}
else
{
iTempMetadata.Append(KSCStereo);
}
if ( iDataType.FourCC() == KMMFFourCCCodeMP3 )
{
iTempMetadata.Append(KSCMp3);
}
else if ( iDataType.FourCC() == KMMFFourCCCodeAAC )
{
iTempMetadata.Append(KSCAacp);
}
iMetadata[2]->SetValueL(iTempMetadata);
iState = EInitStreamOutput;
// Notify the client that metadata is available
iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),0x01FF);
// Initialize the output stream
InitStreamOutputL();
//done
LOG("ParseResponseFromServerL OK");
}
// -----------------------------------------------------------------------------
// CShoutcastStream::GetAudioSettings
// -----------------------------------------------------------------------------
//
void CShoutcastStream::GetAudioSettings()
{
TInt err = ScanHeader(iBuffer2Decode, iLenBuffer2Decode);
if ( err )
{
LOG2("ScanHeader: sampling_rate=%d channels=%d",iSamplingRate,iChannels);
switch ( iSamplingRate )
{
case 96000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate96000Hz;
break;
case 64000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate64000Hz;
break;
case 48000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate48000Hz;
break;
case 44100:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz;
break;
case 32000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate32000Hz;
break;
case 24000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate24000Hz;
break;
case 22050:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate22050Hz;
break;
case 16000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz;
break;
case 12000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate12000Hz;
break;
case 11025:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate11025Hz;
break;
case 8000:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;
break;
default:
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;
}
if ( iChannels >= 2 )
{
iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsStereo;
}
else
{
iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsMono;
}
}
else
{
// In the event that scan header didn't find any valid encoding data we have to derive them
// from the bitrate received from the server.
LOG1("ScanHeader returned an error: %d",err);
DeriveAudioSettings();
}
}
// -----------------------------------------------------------------------------
// CShoutcastStream::DeriveAudioSettings
// This is only needed if we are unable to extract audio settings from the stream.
// These assumptions are not always accurate.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::DeriveAudioSettings()
{
if ( iBitRate >= 64000)
{
// Typical sampling rate for these high bitrates
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz;
iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsStereo;
iSamplingRate = 44100;
}
else // lower bitrates
{
if ( iDataType.FourCC() == KMMFFourCCCodeAAC )
{
// Typical sampling rate for AAC+ stream
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate24000Hz;
iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsStereo;
iSamplingRate = 24000;
}
else
{
// Typical sampling rate for low bitrates mp3 stream
iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate16000Hz;
iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsMono;
iSamplingRate = 16000;
}
}
}
// -----------------------------------------------------------------------------
// CShoutcastStream::PrimeL
//
// -allocates the iBuffer2Decode buffer
// -allocate metadata entries
// -opens some stuff and gets some data in iBuffer2Decode buffer
// -instantiates the mp3 codec,
// -gets the audio parameter
// -allocates the iBuffer2Play buffer
// -negotiates with the sink
// -initializes some of the metadata entries
// -----------------------------------------------------------------------------
//
void CShoutcastStream::PrimeL()
{
LOG("CShoutcastStream::PrimeL");
//state should be ENotConnected
if ( iState != ENotConnected )
{
User::Leave(KErrNotSupported);
}
//-allocate the iBuffer2Decode buffer
iBuffer2Decode = new (ELeave) TUint8[BUFFER2DECODE_SIZE];
// allocate the play buffer
iBuffer2Play = new (ELeave) TUint8[BUFFER2PLAY_SIZE];
iMetadataBuffer = new (ELeave) TUint8[METADATA_BUFFER_SIZE];
ResetBufferVars();
//-add metadata fields. Intended: server name, server genre, artist, song, bitrate (constant) + kHz + stereo, bytes by now + cost (total: 6)
CMetaDataEntry *mmfMetadata;
iTempMetadata.Copy(_L(""));
//add metadata - server name
mmfMetadata = CMetaDataEntry::NewL(KSCMetaServer, iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - server genre
mmfMetadata = CMetaDataEntry::NewL(KSCMetaGenre, iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - technical details
mmfMetadata = CMetaDataEntry::NewL(KSCMetaTechDetails,iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - artist + song
mmfMetadata = CMetaDataEntry::NewL(KSCMetaArtistTitle, iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - bytes
iTempMetadata.Format(_L("%dMB"),0);
mmfMetadata = CMetaDataEntry::NewL(KSCMetaBytes, iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - price
iTempMetadata.Format(_L("price: %d"),0);
mmfMetadata = CMetaDataEntry::NewL(KSCMetaPrice, iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - buffer fill
iTempMetadata.Format(_L("%d"),0);
mmfMetadata = CMetaDataEntry::NewL(KSCMetaBuffer, iTempMetadata);
iMetadata.Append(mmfMetadata);
//add metadata - bitrate
iTempMetadata.Format(_L("%d/%d"),0);
mmfMetadata = CMetaDataEntry::NewL(KSCMetaBitrate, iTempMetadata);
iMetadata.Append(mmfMetadata);
// try to establish connection with the server
ConnectToServerL();
}
// -----------------------------------------------------------------------------
// CShoutcastStream::PlayL
// Start playing the stream by filling the buffer to be played and write it to
// the stream.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::PlayL()
{
LOG("PlayL");
iPausedForBuffering = EFalse;
//update metadata on the client
FillBufferL();
//reset the bitrate stuff
iLastReading = 0;
iBitsRead.Reset();
iTimeIntervals.Reset();
LOG("PlayL OK");
}
// -----------------------------------------------------------------------------
// CShoutcastStream::Pause
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -