⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shoutcaststream.cpp

📁 这是一个使用 ShortCast的IP网络流式播放的symbian的软件源码.里面有ShortCast短播协议的实现以及AAC音频,MP3音频流式播放的实现.
💻 CPP
📖 第 1 页 / 共 4 页
字号:


// -----------------------------------------------------------------------------
// 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 + -