diff -r 000000000000 -r 29b1cd4cb562 bluetooth/gavdp/test/tavsrcStreamer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetooth/gavdp/test/tavsrcStreamer.cpp Fri Jan 15 08:13:17 2010 +0200 @@ -0,0 +1,830 @@ +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "tavsrcStreamer.h" +#include "tavsrcUI.h" +#include "tavsrcUtils.h" + +#include + +static const TSize KStreamerConsole(55,12); + +using namespace SymbianBluetoothAV; +using namespace SymbianSBC; + +// +// class CSbcTrackInfo +// +CSbcTrackInfo::~CSbcTrackInfo() + { + iFrameInfo.Close(); + } + +TInt CSbcTrackInfo::GetLastFrameSize() + { + TInt frameSize = KErrNotFound; + TInt count = iFrameInfo.Count(); + + if (count > 0) + { + frameSize = iFrameInfo[count - 1].iFrameSize; + } + return frameSize; + } + +TInt CSbcTrackInfo::AddNewFrame(TInt aFrameSize) + { + TInt rerr = KErrNone; + TInt count = iFrameInfo.Count(); + + if ((count > 0) && (iFrameInfo[count - 1].iFrameSize == aFrameSize)) + { + // another frame of the same size + iFrameInfo[count - 1].iFrameCount++; + } + else + { + // add new frame info + rerr = iFrameInfo.Append(TSbcTrackFrameInfo()); + if (rerr == KErrNone) + { + iFrameInfo[count].iFrameSize = aFrameSize; + iFrameInfo[count].iFrameCount = 1; + } + } + return rerr; + } + +TInt CSbcTrackInfo::RemoveLastFrame() + { + TInt rerr = KErrNotFound; + TInt count = iFrameInfo.Count(); + + if (count > 0) + { + if (iFrameInfo[count - 1].iFrameCount > 1) + { + // remove one of the instances of the last frame size + iFrameInfo[count - 1].iFrameCount--; + } + else + { + // remove the last frame info + iFrameInfo.Remove(count - 1); + } + rerr = KErrNone; + } + return rerr; + } + +void CSbcTrackInfo::Reset() + { + iFrameInfo.Reset(); + } + +// +// class CActiveStreamer +// +CActiveStreamer* CActiveStreamer::NewL(RSocketArray aSockets, + CConsoleBase& aConsole, + MActiveStreamerUser& aUser, + TUint aDisplayMode, + TBool aPreloadFile) + { + CActiveStreamer* self = new (ELeave) CActiveStreamer (aConsole, aUser, aDisplayMode, aPreloadFile); + CleanupStack::PushL(self); + self->ConstructL(aSockets); + CleanupStack::Pop(); + return self; + } + +CActiveStreamer::CActiveStreamer(CConsoleBase& aConsole, MActiveStreamerUser& aUser, TUint aDisplayMode, TBool aPreloadFile) +: iConsole(aConsole), iUser(aUser), iDisplayMode(aDisplayMode), iPreloadFile(aPreloadFile), iSbcFrameRate(1), iDirectionForward(ETrue) + { + iRTPCanSend = ETrue; + } + +CActiveStreamer::~CActiveStreamer() + { + delete iTimer; + + iSendSource.Close(); + iSession.Close(); + iSockets[0].Close(); + delete iFiles; + iFile.Close(); + iRFs.Close(); + + delete iProgressBar; + DestroyBucket(); + delete iStreamingInfoConsole; + + delete iFileBuf; + iFileBuf = NULL; + + delete iStreamerUI; + iStreamerUI = NULL; + } + +void CActiveStreamer::Stream(TBool aIsSink) + { + + if (!aIsSink) + { + iStartedTime.UniversalTime(); + iTimer->Start(iNominalSendClockInterval); + Drip(); + } + else + { + //as sink, we do want to start up rtp and await its notification of a NewSource + //Nothing to do at the moment, because when INT is a SNK, + //it just needs to get the RTP packets running and wait for notification. + //This has already been done in constructor of the class. + } + iStreamerUI->Play(); + } + +void CActiveStreamer::Suspend() + { + iTimer->Cancel(); // stop callbacks to send + iStreamerUI->Pause(); + } + +void CActiveStreamer::ReStream() + { + + + iTimer->Start(iNominalSendClockInterval); + Drip(); + iStreamerUI->Play(); + } + + +void CActiveStreamer::Stop() + { + iTimer->Cancel(); + iStreamerUI->Stop(); + + iFillLevel = 0; + iPos = 0; + } + +void CActiveStreamer::Pause() + { + iTimer->Cancel(); + iStreamerUI->Pause(); + } + + +void CActiveStreamer::NextTrack() + { + iPos=0; + if (iCurrentFile < iFiles->Count() - 1) + { + iCurrentFile++; + iStreamerUI->Next(); + } + else + { + iCurrentFile = 0; + iStreamerUI->First(); + } + TRAPD(err, InitL()); + if (err) + { + iConsole.Printf(_L("InitL failed with error: %d"),err); + } + } + +void CActiveStreamer::PrevTrack() + { + iPos=0; + if (iCurrentFile>0) + { + iCurrentFile--; + iStreamerUI->Prev(); + } + TRAPD(err, InitL()); + if (err) + { + iConsole.Printf(_L("InitL failed with error: %d"),err); + } + } + +void CActiveStreamer::InitL() + { + // close current file + iFile.Close(); + delete iProgressBar; + iProgressBar = NULL; + + // get file details + RBuf filename; + filename.Create(100); + filename.Append(KSBCFileRoot); + filename.Append(iFiles->operator[](iCurrentFile).iName); + + User::LeaveIfError(iFile.Open(iRFs,filename,EFileRead | EFileShareReadersOnly)); + + TInt numChannels, chMode, numSubbands, blkLen, bitPool, freq, allocMethod; + User::LeaveIfError(TTavsrcUtils::GetCodecSettingsFromSBCFile(filename, chMode, numChannels, numSubbands, blkLen, bitPool, freq, allocMethod)); + + if (iStreamingInfoConsole) + { + iStreamingInfoConsole->Printf(_L("\nFirst SBC frame info for: %S...\n"), &filename); + } + + filename.Close(); + + // determine if a re-configuration is required + if ((iNumChannels != numChannels) || (iFreq != freq) || + (iChMode != chMode) || (iBitPool != bitPool) || + (iBlkLen != blkLen) || (iNumSubbands != numSubbands) || + (iAllocMethod != allocMethod)) + { + iNumChannels = numChannels; + iChMode = chMode; + iNumSubbands = numSubbands; + iBlkLen = blkLen; + iBitPool = bitPool; + iFreq = freq; + iAllocMethod = allocMethod; + + // configuration is set first time around + if (iSBCFrameSize != 0) + { + TSBCCodecCapabilities cfg; + + TSBCSubbandsBitmask subbands = numSubbands == 8 ? EEightSubbands : EFourSubbands; + TSBCAllocationMethodBitmask alloc = allocMethod == 0 ? ELoudness : ESNR; + + TSBCSamplingFrequencyBitmask freqs(0); + if (freq == 48000) freqs = E48kHz; + else if (freq == 44100) freqs = E44100Hz; // note else if now as only select one + else if (freq == 32000) freqs = E32kHz; + else if (freq == 16000) freqs = E16kHz; + + TSBCChannelModeBitmask chs(0); // set it to anything to prevent warning + if (chMode == 0) chs=EMono; + else if (chMode == 1) chs=EDualChannel; + else if (chMode == 2) chs=EStereo; + else if (chMode == 3) chs=EJointStereo; + + TSBCBlockLengthBitmask blkLens(0); // set it to anything to prevent warning + if (blkLen == 4) blkLens = EBlockLenFour; + else if (blkLen == 8) blkLens = EBlockLenEight; + else if (blkLen == 12) blkLens = EBlockLenTwelve; + else if (blkLen == 16) blkLens = EBlockLenSixteen; + + cfg.SetSamplingFrequencies(freqs); + cfg.SetChannelModes(chs); + cfg.SetBlockLengths(blkLens); + cfg.SetSubbands(subbands); + cfg.SetAllocationMethods(alloc); + + // reconfig required + iUser.MediaCodecConfigurationRequired(cfg); + + // ensure no more timer events until we have finished reconfiguring + iTimer->Cancel(); + } + + if (chMode == 0 || chMode == 1) + { + iSBCFrameSize = 4+TReal((4*numSubbands*numChannels))/8+TTavsrcUtils::CEIL(TReal(blkLen*numChannels*bitPool)/8); + } + else + { + TBool join = chMode == 0x03; + + iSBCFrameSize = 4+TReal((4*numSubbands*numChannels))/8+TTavsrcUtils::CEIL(TReal((join*numSubbands+blkLen*bitPool))/8); + } + + iSBCBitrate = 8*iSBCFrameSize*freq/(numSubbands*blkLen); + TUint64 numerator = TUint64(8000000)*iSBCFrameSize; + iSBCFrameInterval = (numerator)/iSBCBitrate; //microsecs + } + + iSbcTrackInfo.Reset(); + iSbcTrackInfo.AddNewFrame(iSBCFrameSize); + + iDirectionForward = ETrue; + iSbcFrameRate = 1; + + User::LeaveIfError(iFile.Size(iFileSize)); + + if (iStreamingInfoConsole) + { + iStreamingInfoConsole->Printf(_L("Sampling Frequency: %d Hz\n"), iFreq); + iStreamingInfoConsole->Printf(_L("Subbands: %d\n"), iNumSubbands); + iStreamingInfoConsole->Printf(_L("BlkLen: %d\n"), iBlkLen); + iStreamingInfoConsole->Printf(_L("ChannelMode: %d\n"), iChMode); + iStreamingInfoConsole->Printf(_L("AllocMethod: %d\n"), iAllocMethod); + iStreamingInfoConsole->Printf(_L("Bitpool: %d\n"), iBitPool); + iStreamingInfoConsole->Printf(_L("SBC Frame size: %d bytes\n"), iSBCFrameSize); + iStreamingInfoConsole->Printf(_L("Bitrate: %d bps\n"), iSBCBitrate); + } + + TInt err = LoadFile(); + if(err==KErrNone) + { + if (iDisplayMode & EProgressBarWindow) + { + iProgressBar = CProgressBar::NewL(iFileSize); + } + FillBucket(); + } + else + { + User::Leave(err); + } + // start the timer for this file + iStartTime.UniversalTime(); + } + +void CActiveStreamer::UpdateFrameInfo() + { + TInt numChannels, chMode, numSubbands, blkLen, bitPool, freq, allocMethod; + User::LeaveIfError(TTavsrcUtils::GetCodecSettingsFromSBCFile(iFile, iPos, chMode, numChannels, numSubbands, blkLen, bitPool, freq, allocMethod)); + + // determine if a re-configuration is required + if ((iNumChannels != numChannels)|| (iFreq != freq) || + (iChMode != chMode) || (iBitPool != bitPool) || + (iBlkLen != blkLen) || (iNumSubbands != numSubbands) || + (iAllocMethod != allocMethod)) + { + iNumChannels = numChannels; + iChMode = chMode; + iNumSubbands = numSubbands; + iBlkLen = blkLen; + iBitPool = bitPool; + iFreq = freq; + iAllocMethod = allocMethod; + + TInt newFrameSize = 0; + if (chMode == 0 || chMode == 1) + { + newFrameSize = 4+TReal((4*numSubbands*numChannels))/8+TTavsrcUtils::CEIL(TReal(blkLen*numChannels*bitPool)/8); + } + else + { + TBool join = chMode == 0x03; + + newFrameSize = 4+TReal((4*numSubbands*numChannels))/8+TTavsrcUtils::CEIL(TReal((join*numSubbands+blkLen*bitPool))/8); + } + + if (newFrameSize != iSBCFrameSize) + { + // work out timer for SBC frame + iSBCBitrate = 8*newFrameSize*freq/(numSubbands*blkLen); + + TUint64 numerator = TUint64(8000000)*iSBCFrameSize; + iSBCFrameInterval = (numerator)/iSBCBitrate; //microsecs + } + iSBCFrameSize = newFrameSize; + } + } + +void CActiveStreamer::TimerEvent(CAdaptiveHighResPeriodic& /*aTimer*/) + { + if (iRTPCanSend) + { + iSent++; + } + else + { + // move iPos on anyway? + iFailedSend++; + } + DoTimerEvent(); + } + +void CActiveStreamer::TimerError(CAdaptiveHighResPeriodic& /*aTimer*/, TInt aError) + { + iConsole.Printf(_L("*ERROR %d*\n"), aError); + __DEBUGGER(); + } + + +void CActiveStreamer::DoTimerEvent() + { + FillBucket(); + Drip(); + CheckJammed(); + } + +void CActiveStreamer::FillBucket() + { + // fill up bucket - it may be fully empty or partially full, but top it up in all cases + if (iFillLevel < KLowTidemark) + { + for (/*iFillLevel*/; iFillLevel < KSendBucketSize; iFillLevel++) + { + // get the next RTP packet to send + RRtpSendPacket& sendPacket = iSendPackets[iFillLevel]; + TDes8& payload = sendPacket.WritePayload(); + payload.Zero(); + payload.Append(0); // update this later with number of frames in packet. + + TInt spaceInRtpPacket = iSBCFrameBytesPerRTP; + TInt framesAdded = 0; + TInt packetInterval = 0; + + TInt nextFrameSize = iDirectionForward ? iSBCFrameSize : iSbcTrackInfo.GetLastFrameSize(); + + TBool moreFrames = ETrue; + while ((nextFrameSize > 0) && (nextFrameSize <= spaceInRtpPacket) && (framesAdded <= 15) && moreFrames) + { + // add frame + if (iPreloadFile) + { + TPtrC8 ptr(iFileBuf->Des().Ptr()+iPos, nextFrameSize); + payload.Append(ptr); + } + else + { + TPtr8 ptr(const_cast(iFileBuf->Des().Ptr()), 0, nextFrameSize); + iFile.Read(iPos, ptr); + payload.Append(ptr); + } + framesAdded++; + packetInterval+=iSBCFrameInterval; + spaceInRtpPacket-=nextFrameSize; + + // get next frame information + for (TInt count = 0; (count != iSbcFrameRate) && moreFrames; count++) + { + iPos = iDirectionForward ? iPos + nextFrameSize : iPos - nextFrameSize; + + // determine if we are done with the current file + if ((iPos >= iFileSize) || (iPos < 0)) + { + moreFrames = EFalse; + } + else + { + TInt err = KErrNone; + if (iDirectionForward) + { + // keep track of the frame sizes as we go, this is used to rewind, + // i.e. iDirectionForward = EFalse + if ((err = iSbcTrackInfo.AddNewFrame(iSBCFrameSize)) != KErrNone) + { + iConsole.Printf(_L("Error adding SBC frame information: %d\n"), err); + __DEBUGGER(); + } + } + else + { + if ((err = iSbcTrackInfo.RemoveLastFrame()) != KErrNone) + { + // this should never happen as we always check the length first + iConsole.Printf(_L("Error removing SBC frame information: %d\n"), err); + __DEBUGGER(); + } + } + UpdateFrameInfo(); + nextFrameSize = iDirectionForward ? iSBCFrameSize : iSbcTrackInfo.GetLastFrameSize(); + } + } + } + + // has the interval changed since last time we set the timer + if ((iNominalSendClockInterval != packetInterval) && moreFrames) + { + // adjust timer + if (iStreamingInfoConsole) + { + iStreamingInfoConsole->Printf(_L("Interval change from: %d to %d\n"), iNominalSendClockInterval, packetInterval); + } + iNominalSendClockInterval = packetInterval; + iTimer->SetInterval(iNominalSendClockInterval); + } + + // write number of SBC frames into packet + payload[0] = TChar(framesAdded); + + //DrawBucket(); //<--- to animate the display + + if (iPos > iFileSize) + { + // time to do some metrics, and loop back to beginning + TTime finTime; + finTime.UniversalTime(); + + TInt64 secs; + secs = (finTime.MicroSecondsFrom(iStartTime)).Int64(); + + TInt bps = (iFileSize*8LL*1000000LL)/secs; + iConsole.Printf(_L("Looping. fail=%d, sent=%d, bytes=%d, secs=%Ld"), iFailedSend, iSent, iFileSize, secs); + iConsole.Printf(_L(" bps=%d\n"), bps); + + iFailedSend=0; + iSent=0; + iPos=0; // loop + + RDebug::Printf("Looping"); + NextTrack(); + + // restart the timer + iTimer->Start(iNominalSendClockInterval); + } + else if (iPos <= 0) + { + PrevTrack(); + } + else if (iProgressBar) + { + iProgressBar->Increment(iPos-iProgressBarPos); + iProgressBarPos = iPos; + } + } + iPreviousFillLevel = iFillLevel; + } + } + +void CActiveStreamer::DrawBucket() + { + if (iStreamingInfoConsole) + { + iStreamingInfoConsole->SetPos(1,1); + + TBuf bar; + bar.AppendFill('#',iFillLevel); + bar.AppendFill('.',KSendBucketSize-iFillLevel); + + iStreamingInfoConsole->Printf(bar); + } + } + +void CActiveStreamer::Drip() + { + // take head packet + RRtpSendPacket& packet = iSendPackets[0]; //packet=oldpacket + + // move previous packet to back - it is reusable, so we don't close + // instead just move to back so that the packet to send is at head + iSendPackets.Remove(0); + iSendPackets.Append(packet); + + if (iRTPCanSend && iFillLevel > 0) + { + // take oldest packet and give to RTP + packet.Send(); + iRTPCanSend = EFalse; + iFillLevel--; + //DrawBucket(); //<--- to animate the display + } + else + { + // not ready to send yet but when we are send straight away + iBonusDrip = ETrue; + + // remember this as a fail to measure + // let code beneath recycle packet + iFailedSend++; + } + } + +void CActiveStreamer::ConstructL(RSocketArray aSockets) + { + iTimer = CAdaptiveHighResPeriodic::NewL(*this); + + if (iDisplayMode & EStreamerInfoWindow) + { + iStreamingInfoConsole = Console::NewL(_L("Streamer"), KStreamerConsole); + } + + User::LeaveIfError(iRFs.Connect()); + + iSockets = aSockets; + + TPckgBuf mruBuf; + iSockets[0].GetOpt(EAvdtpMediaGetMaximumReceivePacketSize, KSolBtAVDTPMedia, mruBuf); + + // donate media socket to rtp + iSession.OpenL(iSockets[0], mruBuf()); + + // we get all RTP events in one place (could have them separately) + iSession.RegisterEventCallbackL(ERtpAnyEvent, + RTPCallbackL, + this); + + iSendSource = iSession.NewSendSourceL(); + + iSendSource.RegisterEventCallbackL(ERtpAnyEvent, + RTPCallbackL, + this); + + iStreamerUI = CStreamerUI::NewL((iDisplayMode & EPlaylistWindow), (iDisplayMode & EChunkyIconWindow)); + + TInt err = iRFs.GetDir(KSBCFiles, KEntryAttNormal, ESortByName, iFiles); + + // set playlist + for (TInt i=0; iCount(); i++) + { + iStreamerUI->AddTitle(iFiles->operator[](i).iName); + } + + err = aSockets[0].GetOpt(EAvdtpMediaGetMaximumPacketSize, KSolBtAVDTPMedia, iMTU); + + iSBCFrameBytesPerRTP = iMTU - 12 - 1; + iSendSource.SetDefaultPayloadSize(iSBCFrameBytesPerRTP+1); + + CreateBucketL(); + InitL(); + } + +void CActiveStreamer::CreateBucketL() + { + if (iSendPackets.Count() == KSendBucketSize) + { + RDebug::Printf("Bucket already created"); + } + else + { + RDebug::Printf("Creating bucket"); + // create all the RTP send packets now + for (TInt i=0; iiStreamingInfoConsole) + { + aStreamer->iStreamingInfoConsole->Printf(_L("\n**RTP SEND FAILURE**")); + } + break; + + case ERtpSendSucceeded: + aStreamer->iRTPCanSend = ETrue; + if (aStreamer->iBonusDrip) + { + aStreamer->Drip(); + aStreamer->iBonusDrip = EFalse; + } + break; + + case ERtpSourceFail: + if (aStreamer->iStreamingInfoConsole) + { + aStreamer->iStreamingInfoConsole->Printf(_L("\n**RTP SOURCE FAILURE**")); + } + break; + + + case ERtpNewSource: + if (aStreamer->iStreamingInfoConsole) + { + aStreamer->iStreamingInfoConsole->Printf(_L("\n**NEW SOURCE!\n")); + } + aStreamer->StartSinkL(); + break; + + case ERtpPacketReceived: + RRtpPacket packet = aStreamer->iReceiveSource.Packet(); + if (aStreamer->iStreamingInfoConsole) + { + aStreamer->iStreamingInfoConsole->Printf(_L("SNK Rxd packet ")); + aStreamer->iStreamingInfoConsole->Printf(_L("SeqNo %d\n"),packet.SequenceNumber()); + } + break; + } + } + +void CActiveStreamer::StartSinkL() + { + iReceiveSource = iSession.NewReceiveSourceL(); + iReceiveSource.RegisterEventCallbackL(ERtpAnyEvent, + RTPCallbackL, + this); + } + +TInt CActiveStreamer::LoadFile() + { + RDebug::Printf("Loading file"); + delete iFileBuf; + iFileBuf = NULL; + + TInt err = KErrNone; + + if (iPreloadFile) + { + if (iStreamingInfoConsole) + { + iStreamingInfoConsole->Printf(_L("Preloading SBC file\n")); + } + + // max heap is something or other + const TInt KMaxHeap = 4000000; + TInt size = iFileSize; + size = Min(iFileSize, KMaxHeap); + TRAP(err, iFileBuf = HBufC8::NewL(size)); + if (err) + { + return err; + } + iFileSize = Min(KMaxHeap, iFileSize); + + TPtr8 ptr(const_cast(iFileBuf->Des().Ptr()), 0, iFileSize); + + const TEntry& entry = iFiles->operator[](iCurrentFile); + RFile test; + test.Open(iRFs, entry.iName, EFileRead); + + ptr.Zero(); + err=iFile.Read(ptr); + } + else + { + if (iStreamingInfoConsole) + { + iStreamingInfoConsole->Printf(_L("Streaming from file\n")); + } + + // read from file to be more "streaming"-like + TRAP(err, iFileBuf = HBufC8::NewL(Min(iFileSize, iSBCFrameBytesPerRTP))); + } + return err; + } + +void CActiveStreamer::Faster() + { + // limit the speed + if (iSbcFrameRate < 5) + { + iSbcFrameRate++; + } + } + +void CActiveStreamer::Slower() + { + if (iSbcFrameRate > 1) + { + iSbcFrameRate--; + } + } + +void CActiveStreamer::Backward() + { + iDirectionForward=EFalse; + } + +void CActiveStreamer::Forward() + { + iDirectionForward=ETrue; + } + +void CActiveStreamer::CheckJammed() + { + if (iFillLevel==iPreviousFillLevel) + { + if ((iBucketAppearsJammed++ > 500) && iStreamingInfoConsole) + { + iStreamingInfoConsole->Printf(_L("BUCKET JAMMED\n")); + iStreamingInfoConsole->Printf(_L("iFillLevel %d "),iFillLevel); + iStreamingInfoConsole->Printf(_L("iRTPCanSend %d "),iRTPCanSend); + iStreamingInfoConsole->Printf(_L("iFailedSend %d "),iFailedSend); + iStreamingInfoConsole->Printf(_L("iPos %d "),iPos); + + TTime now; + now.UniversalTime(); + + TInt millisecs = now.MicroSecondsFrom(iLastPacketSentTime).Int64()/1000; + iStreamingInfoConsole->Printf(_L("time since last send %d ms "),millisecs); + } + } + else + { + iBucketAppearsJammed = 0; + } + }