/*
* Copyright (c) 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: Telephony Multimedia Service
*
*/
#include <AudioPreference.h>
#include <AudioOutput.h>
#include <mmcccodecinformation.h>
#include <IlbcDecoderIntfc.h>
#include <G711DecoderIntfc.h>
#include <G729DecoderIntfc.h>
#include <ErrorConcealmentIntfc.h>
#include "tmsutility.h"
#include "ipcallstream.h"
using namespace TMS;
// -----------------------------------------------------------------------------
// TMSIPDownlink::TMSIPDownlink
// Standard Constructor
// -----------------------------------------------------------------------------
//
TMSIPDownlink::TMSIPDownlink()
{
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::~TMSIPDownlink
// Standard Constructor
// -----------------------------------------------------------------------------
//
TMSIPDownlink::~TMSIPDownlink()
{
TRACE_PRN_FN_ENT;
Stop();
delete iAudioOutput;
delete iErrConcealmentIntfc;
delete iG711DecoderIntfc;
delete iG729DecoderIntfc;
delete iIlbcDecoderIntfc;
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::NewL
// Symbian two-phase constructor
// -----------------------------------------------------------------------------
//
TMSIPDownlink* TMSIPDownlink::NewL(const guint32 codecID,
const TMMFPrioritySettings priority)
{
TMSIPDownlink* self = new (ELeave) TMSIPDownlink();
CleanupStack::PushL(self);
self->ConstructL(codecID, priority);
CleanupStack::Pop(self);
return self;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::ConstructL
// Part two of Symbian two phase construction
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::ConstructL(const guint32 codecID,
const TMMFPrioritySettings priority)
{
TRACE_PRN_FN_ENT;
iCodecID = codecID;
iPriority = priority;
// Client must set these before querying!
iG711DecodeMode = TMS_G711_CODEC_MODE_ALAW;
iILBCDecodeMode = TMS_ILBC_CODEC_MODE_20MS_FRAME;
TRAPD(err, InitDevSoundL(EMMFStatePlaying, priority));
if (err != TMS_RESULT_SUCCESS)
{
User::Leave(err);
}
iMaxBufLen = ConfigureMedia(iCodecID);
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::Start
//
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::Start()
{
TRACE_PRN_FN_ENT;
gint err = TMS_RESULT_ILLEGAL_OPERATION;
if (iStatus == EReady && iDevSound)
{
TRAP(err, iDevSound->PlayInitL());
TRACE_PRN_IF_ERR(err);
#ifdef _DEBUG
iSamplesPlayedCount = 0;
#endif
if (err != TMS_RESULT_SUCCESS)
{
iStatus = EReady;
}
}
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::Stop
//
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::Stop()
{
TRACE_PRN_FN_ENT;
if (iStatus == EStreaming && iDevSound)
{
iDevSound->Stop();
iStatus = EReady;
}
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::BufferToBeFilled
// A reference to the buffer delivered from the DevSound is stored locally
// for later use. It will be filled with the data passed from the client
// when it calls BufferFilled.
//
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::BufferToBeFilled(CMMFBuffer* aBuffer)
{
// Store pointer to the received buffer
iDevSoundBufPtr = static_cast<CMMFDataBuffer*> (aBuffer);
iBufLen = iDevSoundBufPtr->RequestSize();
TRACE_PRN_N1(_L("TMS->DNL->BTBF: LEN[%d]"), iBufLen);
#ifndef __WINSCW__
//TODO: revisit this!
// The first AMR buffer returns 1 for no data frame.
/*if (iCodecID == KMccFourCCIdAMRNB)
{
iBufLen = iMaxBufLen;
}*/
#endif //__WINSCW__
// Create or adjust the chunk
gint err = DoChunk(iBufLen, iMsgBuffer);
if (err != TMS_RESULT_SUCCESS)
{
Stop();
iMsgBuffer.iStatus = err;
}
else
{
// Notify client there is buffer ready to be filled
iMsgBuffer.iStatus = err;
iMsgBuffer.iInt = iBufLen;
iStatus = EStreaming;
// If chunk is opened, we will expect a call from the client to
// get chunk handle. When we get a call to copy chunk handle,
// check these variables and see if they match. This is not
// completely secure, but will provide some level of security
if (iMsgBuffer.iBool == TRUE)
{
iWriteDataXferHndlToClient = TRUE;
iKey = iMsgBuffer.iUint32;
}
}
iMsgBuffer.iRequest = ECmdFillBuffer;
err = iMsgQueue.Send(iMsgBuffer);
TRACE_PRN_IF_ERR(err);
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::BufferFilled
//
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::BufferFilled(const guint buflen)
{
TRACE_PRN_N1(_L("TMS->DNL->BF: LEN[%d]"), buflen);
// Copy data over from chunk
TPtr8 dataPtr(iChunk.Base(), buflen, iMaxBufLen);
// RDebug::RawPrint(dataPtr);
if (iStatus == EStreaming && iDevSound && iDevSoundBufPtr)
{
// Fill D/S buffer and send it for playback
iDevSoundBufPtr->Data() = dataPtr;
TRAP_IGNORE(iDevSoundBufPtr->SetRequestSizeL(buflen));
//try playing anyway
iDevSound->PlayData();
}
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetCodecCi
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetCodecCi()
{
TRAPD(err, SetCodecCiL());
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetCodecCiL
//
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::SetCodecCiL()
{
TRACE_PRN_FN_ENT;
switch (iCodecID)
{
case KMccFourCCIdG711:
{
if (!iG711DecoderIntfc)
{
iG711DecoderIntfc = CG711DecoderIntfc::NewL(*iDevSound);
}
break;
}
case KMccFourCCIdG729:
{
if (!iG729DecoderIntfc)
{
iG729DecoderIntfc = CG729DecoderIntfc::NewL(*iDevSound);
}
break;
}
case KMccFourCCIdILBC:
{
if (!iIlbcDecoderIntfc)
{
iIlbcDecoderIntfc = CIlbcDecoderIntfc::NewL(*iDevSound);
}
break;
}
case KMccFourCCIdAMRNB:
case KMMFFourCCCodePCM16:
{
break;
}
default:
{
User::Leave(TMS_RESULT_INVALID_ARGUMENT);
break;
}
}
if (!iErrConcealmentIntfc && iCodecID != KMMFFourCCCodePCM16)
{
iErrConcealmentIntfc = CErrorConcealmentIntfc::NewL(*iDevSound);
}
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetVolume
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetVolume(const guint volume)
{
gint ret(TMS_RESULT_UNINITIALIZED_OBJECT);
if (iDevSound)
{
iDevSound->SetVolume(volume);
ret = TMS_RESULT_SUCCESS;
}
TRACE_PRN_N1(_L("TMS->DNL: SetVolume [%d]"), volume);
return ret;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetVolume
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetVolume(guint& volume)
{
gint ret(TMS_RESULT_UNINITIALIZED_OBJECT);
if (iDevSound)
{
volume = iDevSound->Volume();
ret = TMS_RESULT_SUCCESS;
}
TRACE_PRN_N1(_L("TMS->DNL: GetVolume [%d]"), volume);
return ret;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetMaxVolume
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetMaxVolume(guint& volume)
{
gint ret(TMS_RESULT_UNINITIALIZED_OBJECT);
if (iDevSound)
{
volume = iDevSound->MaxVolume();
ret = TMS_RESULT_SUCCESS;
}
TRACE_PRN_N1(_L("TMS->DNL: MaxVolume [%d]"), volume);
return ret;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetDataXferChunkHndl
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetDataXferChunkHndl(const TUint32 key, RChunk& chunk)
{
gint status = TMS_RESULT_DOES_NOT_EXIST;
if (iChunk.Handle())
{
if (iWriteDataXferHndlToClient && (iKey == key))
{
chunk = iChunk;
iWriteDataXferHndlToClient = FALSE;
iKey = 0;
status = TMS_RESULT_SUCCESS;
}
else
{
status = TMS_RESULT_ILLEGAL_OPERATION;
}
}
TRACE_PRN_IF_ERR(status);
return status;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetIlbcCodecMode
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetIlbcCodecMode(gint mode)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
iILBCDecodeMode = mode;
if (iIlbcDecoderIntfc && iCodecID == KMccFourCCIdILBC)
{
if (mode == TMS_ILBC_CODEC_MODE_20MS_FRAME)
{
err = iIlbcDecoderIntfc->SetDecoderMode(
CIlbcDecoderIntfc::E20msFrame);
TRACE_PRN_N(_L("TMS->DNL: iLBC Mode Set [20ms]"));
}
else if (mode == TMS_ILBC_CODEC_MODE_30MS_FRAME)
{
err = iIlbcDecoderIntfc->SetDecoderMode(
CIlbcDecoderIntfc::E30msFrame);
TRACE_PRN_N(_L("TMS->DNL: iLBC Mode Set [30ms]"));
}
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetIlbcCodecMode
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetIlbcCodecMode(gint& mode)
{
// not available through CIs -> return cashed value
mode = iILBCDecodeMode;
TRACE_PRN_N1(_L("TMS->DNL: GetIlbcCodecMode [%d]"), mode);
return TMS_RESULT_SUCCESS;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetG711CodecMode
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetG711CodecMode(gint mode)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
iG711DecodeMode = mode;
if (iG711DecoderIntfc && iCodecID == KMccFourCCIdG711)
{
if (mode == TMS_G711_CODEC_MODE_ALAW)
{
err = iG711DecoderIntfc->SetDecoderMode(
CG711DecoderIntfc::EDecALaw);
TRACE_PRN_N(_L("TMS->DNL: G711 Mode Set [ALaw]"));
}
else if (mode == TMS_G711_CODEC_MODE_MULAW)
{
err = iG711DecoderIntfc->SetDecoderMode(
CG711DecoderIntfc::EDecULaw);
TRACE_PRN_N(_L("TMS->DNL: G711 Mode Set [uLaw]"));
}
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetG711CodecMode
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetG711CodecMode(gint& mode)
{
// not available through CIs -> return cached value
mode = iG711DecodeMode;
TRACE_PRN_N1(_L("TMS->DNL: GetG711CodecMode [%d]"), mode);
return TMS_RESULT_SUCCESS;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::FrameModeRqrdForEC
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::FrameModeRqrdForEC(gboolean& frmodereq)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
if (iErrConcealmentIntfc)
{
err = iErrConcealmentIntfc->FrameModeRqrdForEC(frmodereq);
TRACE_PRN_N1(_L("TMS->DNL: FrameModeRqrdForEC [%d]"), frmodereq);
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetFrameMode
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetFrameMode(const gboolean frmode)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
iFrameMode = frmode;
if (iErrConcealmentIntfc)
{
err = iErrConcealmentIntfc->SetFrameMode(frmode);
TRACE_PRN_N1(_L("TMS->DNL: SetFrameMode [%d]"), frmode);
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetFrameMode
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetFrameMode(gboolean& frmode)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iErrConcealmentIntfc)
{
// not available through CIs -> return cached value
frmode = iFrameMode;
TRACE_PRN_N1(_L("TMS->DNL: GetFrameMode [%d]"), frmode);
err = TMS_RESULT_SUCCESS;
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::ConcealErrorForNextBuffer
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::ConcealErrorForNextBuffer()
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iErrConcealmentIntfc)
{
err = iErrConcealmentIntfc->ConcealErrorForNextBuffer();
TRACE_PRN_N(_L("TMS->DNL: ConcealErrorForNextBuffer"));
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetCng
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetCng(const TMSFormatType fmttype, const gboolean cng)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
if (fmttype == TMS_FORMAT_G711 && iG711DecoderIntfc)
{
err = iG711DecoderIntfc->SetCng(cng);
TRACE_PRN_N1(_L("TMS->DNL: SetCng [%d]"), cng);
}
else if (fmttype == TMS_FORMAT_ILBC && iIlbcDecoderIntfc)
{
err = iIlbcDecoderIntfc->SetCng(cng);
TRACE_PRN_N1(_L("TMS->DNL: SetCng [%d]"), cng);
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetCng
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetCng(const TMSFormatType fmttype, gboolean& cng)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
if (fmttype == TMS_FORMAT_G711 && iG711DecoderIntfc)
{
err = iG711DecoderIntfc->GetCng(cng);
TRACE_PRN_N1(_L("TMS->DNL: GetCng [%d]"), cng);
}
else if (fmttype == TMS_FORMAT_ILBC && iIlbcDecoderIntfc)
{
err = iIlbcDecoderIntfc->GetCng(cng);
TRACE_PRN_N1(_L("TMS->DNL: GetCng [%d]"), cng);
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetPlc
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::SetPlc(const TMSFormatType fmttype, const gboolean plc)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EReady)
{
if (fmttype == TMS_FORMAT_G711 && iG711DecoderIntfc)
{
iPlc = plc;
err = iG711DecoderIntfc->SetPlc(iPlc);
TRACE_PRN_N1(_L("TMS->DNL: SetPlc [%d]"), plc);
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetPlc
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::GetPlc(const TMSFormatType fmttype, gboolean& plc)
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (fmttype == TMS_FORMAT_G711 && iG711DecoderIntfc)
{
// not available through CIs -> return cached value
plc = iPlc;
err = TMS_RESULT_SUCCESS;
TRACE_PRN_N1(_L("TMS->DNL: GetPlc [%d]"), plc);
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::BadLsfNextBuffer
//
// -----------------------------------------------------------------------------
//
gint TMSIPDownlink::BadLsfNextBuffer()
{
gint err = TMS_RESULT_DOES_NOT_EXIST;
if (iStatus == EStreaming)
{
if (iCodecID == KMccFourCCIdG729 && iG729DecoderIntfc)
{
err = iG729DecoderIntfc->BadLsfNextBuffer();
TRACE_PRN_N(_L("TMS->DNL: BadLsfNextBuffer"));
}
}
TRACE_PRN_IF_ERR(err);
return err;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::SetAudioDeviceL
//
// -----------------------------------------------------------------------------
void TMSIPDownlink::SetAudioDeviceL(TMSAudioOutput output)
{
TRACE_PRN_FN_ENT;
if (!iAudioOutput)
{
iAudioOutput = CAudioOutput::NewL(*iDevSound);
}
if (iAudioOutput)
{
// ENoPreference=0, EAll=1, ENoOutput=2, EPrivate=3, EPublic=4
CAudioOutput::TAudioOutputPreference outputDev;
switch (output)
{
case TMS_AUDIO_OUTPUT_PRIVATE:
case TMS_AUDIO_OUTPUT_HANDSET:
outputDev = CAudioOutput::EPrivate;
break;
case TMS_AUDIO_OUTPUT_PUBLIC:
case TMS_AUDIO_OUTPUT_LOUDSPEAKER:
outputDev = CAudioOutput::EPublic;
break;
default: // Use default device routing
outputDev = CAudioOutput::ENoPreference;
break;
}
iAudioOutput->SetAudioOutputL(outputDev);
TRACE_PRN_N1(_L("TMS->DNL: SetAudioDeviceL [%d]"), outputDev);
}
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::GetAudioDeviceL
//
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::GetAudioDeviceL(TMSAudioOutput& output)
{
TRACE_PRN_FN_ENT;
if (!iAudioOutput)
{
iAudioOutput = CAudioOutput::NewL(*iDevSound);
}
if (iAudioOutput)
{
CAudioOutput::TAudioOutputPreference outputDev =
iAudioOutput->AudioOutput();
switch (outputDev)
{
case CAudioOutput::ENoPreference:
case CAudioOutput::EAll:
case CAudioOutput::ENoOutput:
case CAudioOutput::EPrivate:
output = TMS_AUDIO_OUTPUT_PRIVATE;
break;
case CAudioOutput::EPublic:
output = TMS_AUDIO_OUTPUT_PUBLIC;
break;
default:
output = TMS_AUDIO_OUTPUT_PRIVATE;
break;
}
}
TRACE_PRN_FN_EXT;
}
// CALLBACKS
// -----------------------------------------------------------------------------
// TMSIPDownlink::InitializeComplete
// A callback from the DevSound indicating completion of the initialization.
// It will send config data to the D/S and configure the encoder via CI.
// If everything goes well, the state of the thread is set EReady.
// The initialization completion message is sent back to the main thread.
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::InitializeComplete(TInt aError)
{
TRACE_PRN_FN_ENT;
gint err = aError;
if (err == TMS_RESULT_SUCCESS && iDevSound)
{
TMMFCapabilities conf;
conf = iDevSound->Config();
conf.iRate = EMMFSampleRate8000Hz;
conf.iChannels = EMMFMono;
TRAP(err, iDevSound->SetConfigL(conf));
if (err == TMS_RESULT_SUCCESS)
{
// We are ready to stream even in case of later CI setting failure
iStatus = EReady;
iMaxVolume = iDevSound->MaxVolume();
}
// Init Custom Interface API to the decoder
err = SetCodecCi();
if (err != TMS_RESULT_SUCCESS)
{
// DEBUG only
// Can ignore error - although decoder is not fully configured but
// it can still run in the default mode.
TRACE_PRN_IF_ERR(err);
}
}
// TODO: Notify client
TRACE_PRN_IF_ERR(err);
TRACE_PRN_FN_EXT;
}
// -----------------------------------------------------------------------------
// TMSIPDownlink::PlayError
// From MDevSoundObserver
// Record error is send to client over comm channel.
// The state of recorder is rolled back to EReady.
// -----------------------------------------------------------------------------
//
void TMSIPDownlink::PlayError(TInt /*aError*/)
{
//TRACE_PRN_IF_ERR(aError);
#ifdef _DEBUG
iSamplesPlayedCount = 0;
#endif
iStatus = EReady;
// TODO: Notify client
}
// End of file