diff -r 000000000000 -r f0cf47e981f9 mmsharing/mmshengine/src/musengreceivesession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmsharing/mmshengine/src/musengreceivesession.cpp Thu Dec 17 08:44:37 2009 +0200 @@ -0,0 +1,756 @@ +/* +* Copyright (c) 2006 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: +* +*/ + + + +// USER INCLUDES + +#include "musengreceivesession.h" +#include "musengsessionobserver.h" +#include "musengreceivesessionobserver.h" +#include "musengmceutils.h" +#include "musenglogger.h" +#include "mussettings.h" +#include "mussipprofilehandler.h" + +// SYSTEM INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +const TUint8 KMusEngRtpKeepAliveTimer = 5; // this should be 30 sec, this a temporary fix +const TUint8 KMusEngKeepAlivePayloadTypeVideoH263 = 96; +const TUint8 KMusEngKeepAlivePayloadTypeAudio = 97; +const TUint8 KMusEngKeepAlivePayloadTypeVideoAvc = 98; + +const TInt KMusEngJitterBufferLength = 51; //Must be bigger than treshold +// Using following value increases treshold buffer to 1 second from +// default 100 ms +const TInt KMusEngJitterBufferTreshold = 50; +const TInt KMusEngTresholdToSecondsFactor = 20; +const TInt KMusEngOneSecondInMilliSeconds = 1000; +// Use inactivity timer value that is a little bigger than treshold in seconds +const TUint KMusEngInactivityTimer = KMusEngTresholdToSecondsFactor * + KMusEngJitterBufferTreshold + + KMusEngOneSecondInMilliSeconds; + +_LIT8( KMusEngSwisIdentifier, "Require: precondition" ); +_LIT8( KMusEngAssertedIdentity, "P-Asserted-Identity" ); +_LIT8( KMusEngBiggerSign, ">" ); + +const TInt KMusEngSipReasonCodeBusyHere = 486; +_LIT8( KMusEngSipReasonPhraseBusy, "Busy" ); + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +CMusEngReceiveSession::~CMusEngReceiveSession() + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::~CMusEngReceiveSession()" ) + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::~CMusEngReceiveSession()" ) + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +EXPORT_C CMusEngReceiveSession* CMusEngReceiveSession::NewL( + const TRect& aRect, + MMusEngSessionObserver& aSessionObserver, + MMusEngReceiveSessionObserver& aReceiveSessionObserver ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::NewL(...)" ) + + CMusEngReceiveSession* self = new( ELeave ) CMusEngReceiveSession( + aSessionObserver, + aReceiveSessionObserver, + aRect ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::NewL(...)" ) + return self; + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +EXPORT_C void CMusEngReceiveSession::AcceptInvitationL( + const TBool& aAccept ) + { + MUS_LOG1( "mus: [ENGINE] -> CMusEngReceiveSession::\ + AcceptInvitationL( %d )", aAccept ) + + __ASSERT_ALWAYS( iSession && iSession->State() == CMceSession::EProceeding, + User::Leave( KErrNotReady ) ); + + // Accept or reject + if ( aAccept ) + { + InSession()->AcceptL(); + } + else + { + if ( iOperatorVariant ) + { + // In operator variant, session is rejected with 486 instead of 603. + // Also the reason phrase is supposed to be "Busy". + InSession()->RejectL( KMusEngSipReasonPhraseBusy(), + KMusEngSipReasonCodeBusyHere ); + } + else + { + InSession()->RejectL(); + } + } + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::\ + AcceptInvitationL(...)" ) + } + + +// ----------------------------------------------------------------------------- +// When reserving resources is ready, this function reports observer about +// incoming session and signals this also to other end terminal ( 180 ringing ). +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::HandleSessionStateChanged( + CMceSession& aSession, + TInt aStatusCode, + const TDesC8& aReasonPhrase ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::\ + HandleSessionStateChanged()" ) + + MUS_ENG_LOG_SESSION_STATE_AND_STATUS( aSession, aStatusCode, aReasonPhrase ) + + if ( iSession && iSession == &aSession && + aSession.State() == CMceSession::EProceeding ) + { + // Indicates that resource reservation is ready, user can be notified + // if we do not have to wait for PRACK + + if ( !iRingLCalled ) + { + // Notify other end terminal + TRAP_IGNORE( InSession()->RingL() ) + iRingLCalled = ETrue; + + // Notify user + TBuf16 originator; + CnvUtfConverter::ConvertToUnicodeFromUtf8( originator, + iSession->Originator() ); + + TBuf16 identity; + CnvUtfConverter::ConvertToUnicodeFromUtf8( identity, + iIdentity ); + iReceiveSessionObserver.IncomingSession( originator, identity ); + } + else + { + MUS_LOG( "mus: [ENGINE] User and remote end already notified." ) + } + + } + else + { + // Forward all other session state changes to base class + CMusEngMceSession::HandleSessionStateChanged( aSession, + aStatusCode, + aReasonPhrase ); + } + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::\ + HandleSessionStateChanged()" ) + } + + +// ----------------------------------------------------------------------------- +// Sets keepalive timer for video and calls base class variant of this function +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::AdjustVideoCodecL( CMceVideoCodec& aVideoCodec ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::AdjustVideoCodecL()" ) + + CMusEngMceSession::AdjustVideoCodecL( aVideoCodec ); + + aVideoCodec.SetKeepAliveTimerL( KMusEngRtpKeepAliveTimer ); + if ( aVideoCodec.SdpName() == KMceSDPNameH263() || + aVideoCodec.SdpName() == KMceSDPNameH2632000() ) + { + aVideoCodec.SetKeepAlivePayloadTypeL( KMusEngKeepAlivePayloadTypeVideoH263 ); + } + else if ( aVideoCodec.SdpName() == KMceSDPNameH264() ) + { + aVideoCodec.SetKeepAlivePayloadTypeL( KMusEngKeepAlivePayloadTypeVideoAvc ); + } + else + { + // NOP + } + + aVideoCodec.SetKeepAliveDataL( KNullDesC8() ); + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::AdjustVideoCodecL()" ) + } + + +// ----------------------------------------------------------------------------- +// Sets keepalive timer for audio and calls base class variant of this function +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::AdjustAudioCodecL( CMceAudioCodec& aAudioCodec ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::AdjustAudioCodecL()" ) + + CMusEngMceSession::AdjustAudioCodecL( aAudioCodec ); + + aAudioCodec.SetKeepAliveTimerL( KMusEngRtpKeepAliveTimer ); + aAudioCodec.SetKeepAlivePayloadTypeL( KMusEngKeepAlivePayloadTypeAudio ); + aAudioCodec.SetKeepAliveDataL( KNullDesC8() ); + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::AdjustAudioCodecL()" ) + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::DoCodecSelectionL( CMceVideoStream& aVideoStream ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::DoCodecSelectionL()" ) + + const RPointerArray& codecs = aVideoStream.Codecs(); + + const CMceVideoCodec* avcSingleNal = NULL; + const CMceVideoCodec* avcNonInterleaved = NULL; + + for ( TInt codecIndex = 0; codecIndex < codecs.Count(); ++codecIndex ) + { + if ( codecs[codecIndex]->SdpName() == KMceSDPNameH264() && + !MultimediaSharingSettings::IsAvcDisabled() ) + { + if ( codecs[codecIndex]->CodecMode() == KMceAvcModeSingleNal ) + { + // Store a pointer to the Single NAL codec with best bitrate + if ( !avcSingleNal || + ( avcSingleNal && codecs[codecIndex]->MaxBitRate() > + avcSingleNal->MaxBitRate() ) ) + { + avcSingleNal = codecs[codecIndex]; + } + } + else if ( codecs[codecIndex]->CodecMode() == + KMceAvcModeNonInterleaved ) + { + // Store a pointer to the Non-Interleaved codec with best bitrate + if ( !avcNonInterleaved || + ( avcNonInterleaved && codecs[codecIndex]->MaxBitRate() > + avcNonInterleaved->MaxBitRate() ) ) + { + avcNonInterleaved = codecs[codecIndex]; + } + } + else + { + // NOP, we do not care about interleaved AVC + } + } + } + + const CMceVideoCodec* selectedCodec = NULL; + + if ( avcNonInterleaved ) + { + selectedCodec = avcNonInterleaved; + } + else + { + selectedCodec = avcSingleNal; + } + + if ( selectedCodec ) + { + // Remove all other codecs + for ( TInt codecIndex = 0; codecIndex < codecs.Count(); ++codecIndex ) + { + if ( codecs[codecIndex] != selectedCodec ) + { + aVideoStream.RemoveCodecL( *codecs[codecIndex] ); + // Since succesfull removal of a codec has decreased the amount + // of codecs in array by one, we have to reset the index + codecIndex = 0; + } + } + } + else + { + // Let the base class do H.263 selection + CMusEngMceSession::DoCodecSelectionL( aVideoStream ); + } + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::DoCodecSelectionL()" ) + } + + +// ----------------------------------------------------------------------------- +// If incoming session does not have valid structure and cannot be reconstructed +// as such, session is rejected automatically. +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::IncomingSession( + CMceInSession* aSession, + TMceTransactionDataContainer* aContainer ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::IncomingSession(...)" ) + + if ( !aContainer || + ( iSession && + iSession->State() != CMceSession::ETerminated ) ) + { + // We already have one session ongoing, or do not have container. + // Reject new session. + Reject( *aSession ); + delete aSession; + aSession = NULL; + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::IncomingSession( \ + session already exists. New session rejected and deleted. )" ) + return; + } + + iReceiveSessionObserver.IncomingSessionPreNotification(); + + delete iSession; // possibly existing terminated session + iSession = aSession; + + if ( iOperatorVariant ) + { + ParseAssertedIdentity( aContainer ); + } + + // Reset variable defined for preparing for multiple state changes to + // EProceeding. This might happen if we set 100rel to be required. + iRingLCalled = EFalse; + + // If preparing fails, session setup with VS2.2 will fail later + TRAP_IGNORE( PrepareToRequire100RelL( aContainer ) ) + + TRAPD( error, CompleteSessionStructureL() ); + + if ( error != KErrNone ) + { + Reject( *InSession() ); + } + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::IncomingSession()" ) + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::IncomingUpdate( + CMceSession& aOrigSession, + CMceInSession* aUpdatedSession, + TMceTransactionDataContainer* aContainer ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::IncomingUpdate(...)" ) + + if ( iSession && + iSession == &aOrigSession ) + { + // Old session is useless from now on + delete iSession; + iSession = aUpdatedSession; + + TRAPD( error, CompleteSessionStructureL() ); + + if ( error != KErrNone ) + { + MUS_LOG( "mus: [ENGINE] Cannot handle update, reject" ) + Reject( *aUpdatedSession ); + } + } + else + { + // Cannot handle, forward to a base class + CMusEngMceSession::IncomingUpdate( aOrigSession, + aUpdatedSession, + aContainer ); + } + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::IncomingUpdate(...)" ) + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::StreamStateChanged( CMceMediaStream& aStream ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::StreamStateChanged()" ) + + MUS_ENG_LOG_STREAM_STATE( aStream ) + + if ( !iSession ) + { + return; + } + + if ( aStream.Type() == KMceVideo && + aStream.Source() && + aStream.Source()->Type() == KMceRTPSource ) + { + + if ( aStream.State() == CMceMediaStream::EBuffering ) + { + iReceiveSessionObserver.StreamBuffering(); + } + else if ( aStream.State() == CMceMediaStream::EStreaming ) + { + CMceRtpSource* rtpSource = + static_cast( aStream.Source() ); + TRAPD( err , + rtpSource->EnableInactivityTimerL( KMusEngInactivityTimer ) ); + if ( err != KErrNone ) + { + MUS_LOG1("mus: [ENGINE] EnableInactivityTimerL Fails %d",err) + iSessionObserver.SessionFailed(); + } + else + { + iSessionObserver.StreamStreaming(); + } + } + else + { + // Cannot handle, forward to CMusEngMceSession + CMusEngMceSession::StreamStateChanged( aStream ); + } + + } + else + { + // Cannot handle, forward to CMusEngMceSession + CMusEngMceSession::StreamStateChanged( aStream ); + } + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::StreamStateChanged()" ) + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +CMusEngReceiveSession::CMusEngReceiveSession( + MMusEngSessionObserver& aSessionObserver, + MMusEngReceiveSessionObserver& aReceiveSessionObserver, + const TRect& aRect ) + : CMusEngMceSession( aRect, aSessionObserver ), + iReceiveSessionObserver( aReceiveSessionObserver ) + { + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::ConstructL() + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::ConstructL()" ) + + CMusEngMceSession::ConstructL(); + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::ConstructL()" ) + } + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +// +CMceInSession* CMusEngReceiveSession::InSession() + { + return static_cast(iSession); + } + + +// ----------------------------------------------------------------------------- +// Check if incoming session is originated from VideoSharing 2.2 client, +// which sends PRACK for 180 Ringing whether it is required or not. In +// order to let MCE handle the PRACK correctly, force 180 Ringing to be +// sent reliably. +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::PrepareToRequire100RelL( + TMceTransactionDataContainer* aContainer) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::PrepareToRequire100RelL()" ) + + __ASSERT_ALWAYS( aContainer, User::Leave( KErrArgument ) ); + + CDesC8Array* headers = aContainer->GetHeaders(); + CleanupStack::PushL( headers ); + + if ( headers ) + { + for ( TInt i = 0; i < headers->Count(); ++i ) + { + if ( headers->MdcaPoint(i).FindF( KMusEngSwisIdentifier() ) != + KErrNotFound ) + { + iSession->SetModifierL( KMce100Rel, KMce100RelRequired ); + MUS_LOG( "mus: [ENGINE] Forced to require 100rel" ) + } + } + + } + + CleanupStack::PopAndDestroy( headers ); + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::PrepareToRequire100RelL()" ) + } + + +// ----------------------------------------------------------------------------- +// Check that incoming session contains only incoming video and audio +// streams. All the other streams will be removed. If there is no +// display sinks and speakers, they will be created. +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::CompleteSessionStructureL() + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::CompleteSessionStructureL()" ) + + __ASSERT_ALWAYS( iSession, User::Leave( KErrNotReady ) ); + + // Force bandwidth line usage in sdp as it is mandatory + // at MT side based on GSMA VS specification IR.74. Bandwidth is set to + // session or to media level based on sender's way of usage. If other end + // is not using bandwidth attribute at all, media level is preferred. + SetSessionSdpLinesL( *iSession, ETrue ); + + CMceStreamBundle* localBundle = + CMceStreamBundle::NewLC( CMceStreamBundle::ELS ); + + const RPointerArray& streams = iSession->Streams(); + + CMceMediaStream* videoInStream = NULL; + + for( TInt i = 0; i < streams.Count(); ++i ) + { + if ( MusEngMceUtils::IsVideoInStream( *streams[i] ) && + !videoInStream ) + { + videoInStream = streams[i]; + + // Disable possible opposite stream to indicate that sendrecv is + // not allowed. + if ( streams[i]->BoundStream() ) + { + MusEngMceUtils::DisableStreamL( streams[i]->BoundStreamL() ); + } + } + else if ( streams[i]->BoundStream() && + MusEngMceUtils::IsVideoInStream( streams[i]->BoundStreamL() ) && + !videoInStream ) + { + videoInStream = &streams[i]->BoundStreamL(); + + // Disable opposite stream to indicate that sendrecv is not allowed. + MusEngMceUtils::DisableStreamL( *streams[i] ); + } + else if ( MusEngMceUtils::IsAudioInStream( *streams[i] ) && + !iOperatorVariant ) + { + MusEngMceUtils::AddSpeakerL( *streams[i] ); + + localBundle->AddStreamL( *streams[i] ); + + // Disable possible opposite stream to indicate that sendrecv is + // not allowed. + if ( streams[i]->BoundStream() ) + { + MusEngMceUtils::DisableStreamL( streams[i]->BoundStreamL() ); + } + } + else if ( streams[i]->BoundStream() && + MusEngMceUtils::IsAudioInStream( streams[i]->BoundStreamL() ) && + !iOperatorVariant ) + { + MusEngMceUtils::AddSpeakerL( streams[i]->BoundStreamL() ); + + localBundle->AddStreamL( streams[i]->BoundStreamL() ); + + // Disable opposite stream to indicate that sendrecv is not allowed. + MusEngMceUtils::DisableStreamL( *streams[i] ); + } + else + { + // We remove stream because either it + // 1. Is not and does not contain incoming video or audio, or + // 2. We already have one incoming video stream or + // 3. Stream is audio and we run operator variant where audio is + // not allowed. + iSession->RemoveStreamL( *streams[i] ); + + // Since succesfull removal of a stream has decreased the amount + // of streams in array by one, we have to modify the index + --i; + } + } + + if ( videoInStream ) + { + SetMediaSdpLinesL( *videoInStream, ETrue ); + + // Complete stream + MusEngMceUtils::AddDisplayL( *videoInStream, *iManager, Rect() ); + + static_cast(videoInStream->Source())->UpdateL( + KMusEngJitterBufferLength, + KMusEngJitterBufferTreshold ); + + localBundle->AddStreamL( *videoInStream ); + } + else + { + User::Leave( KErrCorrupt ); + } + + // Next line is to cause a leave if there were no incoming video streams + MusEngMceUtils::GetVideoInStreamL( *iSession ); + + // Destroy bundle if it is not needed or transfer ownership + if ( localBundle->Streams().Count() > 1 ) + { + iSession->AddBundleL( localBundle ); + CleanupStack::Pop( localBundle ); + } + else + { + CleanupStack::PopAndDestroy( localBundle ); + } + + AdjustStreamsAndCodecsL(); + + iSession->UpdateL(); + + // Now session state is right to adjust volume + SetSpeakerVolumeL( VolumeL() ); + + iSipProfileHandler->CreateProfileL( iSession->Profile() ); + + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::CompleteSessionStructureL()" ) + } + + +// ----------------------------------------------------------------------------- +// Parse P-Asserted-Identity Header +// ----------------------------------------------------------------------------- +// +void CMusEngReceiveSession::ParseAssertedIdentity( + TMceTransactionDataContainer* aContainer ) + { + MUS_LOG( "mus: [ENGINE] -> CMusEngReceiveSession::AssertedIdentity()" ) + + iIdentity.Zero(); + + if ( !aContainer ) + { + MUS_LOG( "mus: [ENGINE] Data container is NULL" ) + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::AssertedIdentity()" ) + return; + } + + CDesC8Array* headers = aContainer->GetHeaders(); + + if ( !headers ) + { + MUS_LOG( "mus: [ENGINE] No headers" ) + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::AssertedIdentity()" ) + return; + } + + TBool found = EFalse; + TInt length = 0; + for( TInt i = 0; i < headers->Count() && !found; ++i ) + { + TPtrC8 header = (*headers)[i]; + if ( header.FindF( KMusEngAssertedIdentity ) != KErrNotFound ) + { + TInt pos = 0; + // check for sip url, continue if found because maybe tel url exists + if ( ( pos = header.FindF( KMusEngSipPrefix ) ) != KErrNotFound ) + { + TInt atPos = header.FindF( KMusEngAtSign ); + length = atPos - pos - KMusEngSipPrefix().Length(); + if ( length > 0 && length <= KMaxUriLength ) + { + iIdentity.Copy( + header.Mid( pos + KMusEngSipPrefix().Length(), length ) ); + } + } + // check for tel url, exit if found + else if ( ( pos = header.FindF( KMusEngTelPrefix ) ) != KErrNotFound ) + { + length = header.Length() - pos - KMusEngTelPrefix().Length(); + if ( length > 0 && length <= KMaxUriLength ) + { + iIdentity.Copy( header.Right( length ) ); + } + found = ETrue; + } + } + } + + // remove ending '>' if exists and whitespaces + if ( iIdentity.Length() > 0 ) + { + iIdentity.Trim(); + if ( iIdentity.Find( KMusEngBiggerSign ) == iIdentity.Length() - 1 ) + { + iIdentity = iIdentity.Left( iIdentity.Length() - 1 ); + iIdentity.Trim(); + } + } + + delete headers; + MUS_LOG( "mus: [ENGINE] <- CMusEngReceiveSession::AssertedIdentity()" ) + }