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

📄 shoutcaststream.cpp

📁 这是一个使用 ShortCast的IP网络流式播放的symbian的软件源码.里面有ShortCast短播协议的实现以及AAC音频,MP3音频流式播放的实现.
💻 CPP
📖 第 1 页 / 共 4 页
字号:
/*
* ==============================================================================
*  Name        : ShoutcastStream.cpp
*  Part of     : Shoutcast Engine
*  Interface   :
*  Description : Implementation of shoutcast stream class
*  Version     : 2
*
*  Copyright (c) 2006, Nokia Corporation All rights reserved. Redistribution
*  and use in source and binary forms, with or without modification, are
*  permitted provided that the following conditions are met: Redistributions
*  of source code must retain the above copyright notice, this list of
*  conditions and the following disclaimer. Redistributions in binary form
*  must reproduce the above copyright notice, this list of conditions and the
*  following disclaimer in the documentation and/or other materials provided
*  with the distribution. Neither the name of the Nokia Corporation nor the
*  names of its contributors may be used to endorse or promote products
*  derived from this software without specific prior written permission. THIS
*  SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
*  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
*  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
* ==============================================================================
*/

// INCLUDE FILES
#include <e32base.h>
#include <f32file.h>

#include <libc/stdlib.h>
#include <libc/string.h>
#include <libc/sys/unistd.h> //sleep
#include <errno.h>
#include <assert.h>

#include "ShoutcastDefs.h"
#include "ShoutcastErrors.h"
#include "ShoutcastUIDs.hrh"
#include "ShoutcastStream.h"
#include "ShoutcastEventDispatcher.h"

// CONSTANTS

// Formating strings
_LIT(KSCBufFormat, "Buf:%3d");
_LIT(KSCTechDataFormat, "%dkb/s %dkHz ");
_LIT(KSCDataRcvdFormat, "%3.1fMB");
_LIT(KSCMetaPriceFormat, "price: %3.1f");
_LIT(KSCMetaBitrateFormat, "Link:%d/%d");

// Audio formats
_LIT(KSCMono, "Mono");
_LIT(KSCStereo, "Stereo");
_LIT(KSCMp3, " MP3");
_LIT(KSCAacp, " AAC+");

// HTTP Request
_LIT8(KSCGet, "GET ");
_LIT8(KSCHttp10, " HTTP/1.0\r\n");
_LIT8(KSCHost, "Host: ");
_LIT8(KSCUserAgent, "User-Agent:S60InternetRadio/2.0\r\n");
_LIT8(KSCAccept, "Accept:*/*\r\n");
_LIT8(KSCIcyMetadata, "icy-metadata:1\r\n");
_LIT8(KSCConnectionClose, "Connection:close\r\n\r\n");

// HTTP Response
_LIT8(KSCOKHeader, "200 OK");
_LIT8(KSCContentTypeTag, "content-type");
_LIT(KSCMimeTypeAudioMpeg, "audio/mpeg");
_LIT(KSCMimeTypeAudioAacp, "audio/aacp");
_LIT(KSCMimeTypeAudioAac, "audio/aac");
_LIT8(KSCIcyMetaintTag, "icy-metaint");
_LIT8(KSCIcyNameTag, "icy-name");
_LIT8(KSCIcyGenreTag, "icy-genre");
_LIT8(KSCIcyBrTag, "icy-br");

// Internal metadata names
_LIT(KSCMetaServer, "Server");
_LIT(KSCMetaTechDetails, "TechDetails");
_LIT(KSCMetaArtistTitle, "ArtistTitle");
_LIT(KSCMetaGenre, "Genre");
_LIT(KSCMetaBytes, "Bytes");
_LIT(KSCMetaPrice, "Price");
_LIT(KSCMetaBuffer, "Buffer");
_LIT(KSCMetaBitrate, "Bitrate");

_LIT8(KSCStreamTitle, "StreamTitle='");

const TInt K100kBFactor = 104857;
const TReal K1MBFactor = 1048576.0;

// Bit rates in bits/sec supported by MPEG2, MPEG1 and MPEG2.5 respectively
const TInt16 KMp3BitRateTable[3][16] =
	{
		{-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
		{-1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0},
		{-1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
	};

// Sampling frequencies supported by MPEG2, MPEG1 and MPEG2.5 respectively
const TUint16 KMp3SamplingFrequencyTable[3][4] =
	{
		{22050,24000,16000,0},
		{44100,48000,32000,0},
		{11025,12000,8000,0},
	};

const TUint KAacSamplingFrequencyTable[16] =
	{
	96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,0,0,0,0
	};

// Maximum MP3 frame size
const TInt KMp3MaxFrameSize = 1440; // 320kbit/s @ 32kHz

// The size of MP3 header, header must include bits for determining frame length
const TInt KMp3FrameHeaderSize = 5;
const TInt KAacFrameHeaderSize = 7;

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CShoutcastStream::CShoutcastStream
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CShoutcastStream::CShoutcastStream()
	: CActive(EPriorityStandard),
	  iState(ENotConnected),
	  iDispatcher(NULL),
	  iStreamOutput(NULL),
	  iPtrBuffer2Decode(NULL,0,0),
	  iPtrBuffer2Play(NULL,0,0),
	  iInstantBitrate(0),
	  iMaxFrameSize(0),
	  iVolume(0),
	  iMetadata(NR_METADATA_FIELDS)
    {
    }

// -----------------------------------------------------------------------------
// CShoutcastStream::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CShoutcastStream* CShoutcastStream::NewL(
	const TDesC8& aUrl,
	MShoutcastStreamObserver& aObserver )
    {
    CShoutcastStream* self = new (ELeave) CShoutcastStream();
    CleanupStack::PushL(self);
    self->ConstructL(aUrl, aObserver);
	CleanupStack::Pop(self);
    return self;
    }

// -----------------------------------------------------------------------------
// CShoutcastStream::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ConstructL(
	const TDesC8& aUrl,
	MShoutcastStreamObserver& aObserver )
	{
	LOG_START;

	LOG("CShoutcastStream::ConstructL");

	iURL.Copy(aUrl);
	iTempMetadata.Copy(iURL);
	LOG1("CShoutcastStream::OpenL: %S",&iTempMetadata);

	//here gotUrl should be true and iURL should be filled!
	GetIPFromURL();

	iDispatcher = CEventDispatcher::NewL(aObserver);
	// Defaults to MP3 encoded stream
	iDataType.Set(KMMFFourCCCodeMP3);

	CActiveScheduler::Add(this);
	LOG("CShoutcastStream::ConstructL OK");

	}


// Destructor
CShoutcastStream::~CShoutcastStream()
    {
	LOG("CShoutcastStream::~CShoutcastStream");
	Cancel();
	CloseAndClean();

	delete iAddr;iAddr=NULL;

	if ( iStreamOutput )
		{
		delete iStreamOutput;
		iStreamOutput = NULL;
		}

	delete iDispatcher;

	LOG("CShoutcastStream Destructor OK");
	LOG_END;

	}


// -----------------------------------------------------------------------------
// CShoutcastStream::CloseAndClean
// Close connections and clean up resources
// -----------------------------------------------------------------------------
//

void CShoutcastStream::CloseAndClean()
	{

	if ( iState == ENotConnected )
		return;

	iState = EDisconnecting;

	//buffering stuff
	iPtrBuffer2Decode.Set(NULL,0,0);
	iPtrBuffer2Play.Set(NULL,0,0);
	delete iBuffer2Decode;
	iBuffer2Decode = NULL;
	iLenBuffer2Decode = iPosBuffer2Decode=0;
	iLenBuffer2Play = 0;
	delete iBuffer2Play;
	iBuffer2Play = NULL;

	//close connection
	iSock.Close();
	iSocksvr.Close();

	//metadata
	iMetadata.ResetAndDestroy();
	delete iMetadataBuffer;
	iMetadataBuffer = NULL;

	iState = ENotConnected;
	}




// -----------------------------------------------------------------------------
// CShoutcastStream::MaoscBufferCopied
// The descriptor buffer has been copied, fill it again with encoded data.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::MaoscBufferCopied(
	TInt aError,
	const TDesC8& /*aBuffer*/)
	{
	//LOG1("MaoscBufferCopied: iState = %d ",iState);
	ASSERT(iState == EData);
	iWritingToStream = EFalse;
	// if no error, write more data to the output stream
	if ( !aError )
		{

		TRAPD(err, FillBufferL());
		if ( err )
			{
			iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),err);
			}
		}
	}


// -----------------------------------------------------------------------------
// CShoutcastStream::FillBuffer
// Fill the play buffer with encoded data and send it to the stream output.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::FillBufferL()
	{
	//LOG1("FillBufferL: iState = %d", iState);
	ASSERT(iState == EData);

	if ( iWritingToStream )
		{
		// Still busy writing to stream. We have not received MaoscBufferCopied.
		// Bailing out...
		return;
		}
	else
		{
		iWritingToStream = ETrue;
		}

	TInt ldTotal;

	iLenBuffer2Play=iPtrBuffer2Play.MaxLength();
	if ( iLenBuffer2Decode+iReadData < iLenBuffer2Play )
		{
		//LOG("FillBufferL: Not enough data to play. Rebuffering");
		//not enough data to play
		iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),KShoutcastEvent_BufferEmpty);
		iLenBuffer2Play=0;
		iPausedForBuffering = ETrue;
		iWritingToStream = EFalse;
		}
	else
		{
		//there is enough data to be played!
		if ( iLenBuffer2Decode >= iLenBuffer2Play )
			{
			//copy all the data into the buffer
			iPtrBuffer2Play.Copy(iBuffer2Decode+iPosBuffer2Decode,iLenBuffer2Play);
			iPosBuffer2Decode+=iLenBuffer2Play;
			iLenBuffer2Decode-=iLenBuffer2Play;
			}
		else
			{
			//we have to copy the entire end of the buffer, then also the beginning of the buffer
			//copy the end of the buffer
			iPtrBuffer2Play.Copy(iBuffer2Decode+iPosBuffer2Decode, iLenBuffer2Decode);
			TInt dataLeft = iLenBuffer2Play-iLenBuffer2Decode;
			//we need to go from case 2 to case 1 (when there is no buffer to copy)
			ASSERT(iReadData > 0);
			iPosBuffer2Decode=0;
			iLenBuffer2Decode=iReadData;
			iReadData=-1;//going from case 2 to case 1
			//append the remaining data
			ASSERT(dataLeft <= iLenBuffer2Decode);//due to the first if from the HwDevice else
			iPtrBuffer2Play.Append(iBuffer2Decode+iPosBuffer2Decode,dataLeft);
			iPosBuffer2Decode+=dataLeft;
			iLenBuffer2Decode-=dataLeft;
			}
		}
	ASSERT(iLenBuffer2Play >= 0);
	ldTotal = iLenBuffer2Play;

	if ( iLenBuffer2Play > 0 )
		{
		//LOG1("FillBufferL: Calling WriteL for %d bytes",iLenBuffer2Play);
		iStreamOutput->WriteL(iPtrBuffer2Play);
		}


	//buffering stuff
	if(iPosBuffer2Decode>BUFFER2DECODE_SIZE-iMaxFrameSize-READ_EPSILON && iLenBuffer2Decode<iMaxFrameSize)
	{
		//we passed the upper safety limit!
		//LOG2("FillBufferL: Upper limit passed (pos=%d, len=%d). ",iPosBuffer2Decode,iLenBuffer2Decode);
		ASSERT(iLenBuffer2Decode < iMaxFrameSize);
		ASSERT(iReadData >= 0);

		//copy the upper part somewhere down
		memmove(iBuffer2Decode+iMaxFrameSize-iLenBuffer2Decode,iBuffer2Decode+iPosBuffer2Decode,iLenBuffer2Decode);
		iPosBuffer2Decode=iMaxFrameSize-iLenBuffer2Decode;
		iLenBuffer2Decode+=iReadData;
		iReadData=-1;//going from case 2 to case 1
	};

	//buffering fill
	TInt bufferFill;
	if(iReadData==-1)
		{
		bufferFill=(TInt)(100.0*iLenBuffer2Decode/(BUFFER2DECODE_SIZE-iMaxFrameSize));
		}
	else
		{
		bufferFill=(TInt)(100.0*(iLenBuffer2Decode+iReadData)/(BUFFER2DECODE_SIZE-iMaxFrameSize));
		}

	//fill buffering metadata
	iTempMetadata.Format(KSCBufFormat, bufferFill);
	iMetadata[6]->SetValueL(iTempMetadata);
	//update metadata on the client/controller
	if ( !iPausedForBuffering )
		{
		iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),0x0140);
		}

	//restarting buffering, if needed!
	if(!iReadingActive && ldTotal>0)
	{
		//reading is stopped and we need to restart it!
		ReadRequest();
	};

	//LOG2("FillBufferL OK (pos=%d, len=%d)",iPosBuffer2Decode, iLenBuffer2Decode);

	}

// -----------------------------------------------------------------------------
// CShoutcastStream::ResetBufferVars
// Reset buffer variables
// -----------------------------------------------------------------------------
//
void CShoutcastStream::ResetBufferVars()
	{
    iPosBuffer2Decode=0;
	iLenBuffer2Decode=0;
	iReadData=-1;
	iReadingActive=EFalse;
	iPtrBuffer2Decode.Set(iBuffer2Decode,0,BUFFER2DECODE_SIZE);
	iPtrBuffer2Play.Set(iBuffer2Play,0,BUFFER2PLAY_SIZE);
	iBitsRead.Reset();
	iTimeIntervals.Reset();
	iLastReading=0;
	}


// -----------------------------------------------------------------------------
// CShoutcastStream::InitStreamOutputL
// Create and initialize the output stream  This must be done before playing can
// began.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::InitStreamOutputL()
	{
	LOG("CShoutcastStream::InitStreamOutputL");
	ASSERT(iState == EInitStreamOutput);
	iStreamOutput = CMdaAudioOutputStream::NewL(*this);
//		iStreamOutput->SetPriority(EMdaPriorityMin , EMdaPriorityPreferenceTimeAndQuality);
	iStreamOutput->SetPriority(80 , EMdaPriorityPreferenceTimeAndQuality);
	iStreamOutput->Open(&iSettings);
	LOG("CShoutcastStream::InitStreamOutputL OK");
	}

// -----------------------------------------------------------------------------
// CShoutcastStream::MaoscOpenComplete
// Once the stream is opened, we can set audio properties, volume, and priority.
// -----------------------------------------------------------------------------
//
void CShoutcastStream::MaoscOpenComplete(
	TInt aError )
	{

	LOG("MaoscOpenComplete");

	TInt err;
	if ( aError == KErrNone )
		{
		// If this leaves with KErrNotSupported, no decoder was found that support the
		// specified encoded data stream.
		TRAP(err, iStreamOutput->SetDataTypeL(iDataType.FourCC()));
		if ( err )
			{
			if ( err == KErrNotSupported )
				{
				iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), KErrShoutcast_FormatNotSupported);
				}
			else
				{
				iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), err);
				}
			LOG1("SetDataType: Leaving with %d err",err);
			return;
			}

        LOG2("Setting audio properties: sampleRate=%d channels=%d",iAudioSettings.iSampleRate,iAudioSettings.iChannels);

		TRAP(err, iStreamOutput->SetAudioPropertiesL(iAudioSettings.iSampleRate, iAudioSettings.iChannels));
		if ( err )
			{
			iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), err);
			LOG1("SetAudioPropertiesL: Leaving with %d err", err);
			return;
			}

		LOG1("SetAudioPropertiesL: Max Volume = %d", iStreamOutput->MaxVolume());
		iStreamOutput->SetVolume(iVolume);

		//announce the client that we are connected
		iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid),KShoutcastEvent_Connected);
		iState = EData;//this should be the only place where iState gets assigned the EData value!
		}
	else
		{
		iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), aError);
		}

	LOG("MaoscOpenComplete OK");
	}

// -----------------------------------------------------------------------------
// CShoutcastStream::MaoscPlayComplete
// Called when the end of the sound data has been reached or the
// playing has stopped for some other reason.
// KErrUnderflow = "end of the sound data has been reached"
// -----------------------------------------------------------------------------
//
void CShoutcastStream::MaoscPlayComplete(
	TInt aError )
	{
	LOG1("MaoscPlayComplete: err=%d",aError);

	if ( aError == KErrUnderflow )
		{
		// This case where we run out of data to play and needs to rebuffer.
		iPausedForBuffering = ETrue;
		}
	if ( aError == KErrDied || aError == KErrAccessDenied )
		{
		Stop();
		iDispatcher->SendEvent(TUid::Uid(KShoutcastStreamUid), aError);
		}

	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -